|
| 1 | +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT |
| 2 | +// file at the top-level directory of this distribution and at |
| 3 | +// http://rust-lang.org/COPYRIGHT. |
| 4 | +// |
| 5 | +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| 6 | +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| 7 | +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| 8 | +// option. This file may not be copied, modified, or distributed |
| 9 | +// except according to those terms. |
| 10 | + |
| 11 | +/*! |
| 12 | +Syntax extension to generate FourCCs. |
| 13 | +
|
| 14 | +Once loaded, fourcc!() is called with a single 4-character string, |
| 15 | +and an optional ident that is either `big`, `little`, or `target`. |
| 16 | +The ident represents endianness, and specifies in which direction |
| 17 | +the characters should be read. If the ident is omitted, it is assumed |
| 18 | +to be `big`, i.e. left-to-right order. It returns a u32. |
| 19 | +
|
| 20 | +# Examples |
| 21 | +
|
| 22 | +To load the extension and use it: |
| 23 | +
|
| 24 | +```rust,ignore |
| 25 | +#[phase(syntax)] |
| 26 | +extern mod fourcc; |
| 27 | +
|
| 28 | +fn main() { |
| 29 | + let val = fourcc!("\xC0\xFF\xEE!") |
| 30 | + // val is 0xC0FFEE21 |
| 31 | + let big_val = fourcc!("foo ", big); |
| 32 | + // big_val is 0x21EEFFC0 |
| 33 | +} |
| 34 | + ``` |
| 35 | +
|
| 36 | +# References |
| 37 | +
|
| 38 | +* [Wikipedia: FourCC](http://en.wikipedia.org/wiki/FourCC) |
| 39 | +
|
| 40 | +*/ |
| 41 | + |
| 42 | +#[crate_id = "fourcc#0.10-pre"]; |
| 43 | +#[crate_type = "rlib"]; |
| 44 | +#[crate_type = "dylib"]; |
| 45 | +#[license = "MIT/ASL2"]; |
| 46 | + |
| 47 | +#[feature(macro_registrar, managed_boxes)]; |
| 48 | + |
| 49 | +extern mod syntax; |
| 50 | + |
| 51 | +use syntax::ast; |
| 52 | +use syntax::ast::Name; |
| 53 | +use syntax::attr::contains; |
| 54 | +use syntax::codemap::{Span, mk_sp}; |
| 55 | +use syntax::ext::base; |
| 56 | +use syntax::ext::base::{SyntaxExtension, BasicMacroExpander, NormalTT, ExtCtxt, MRExpr}; |
| 57 | +use syntax::ext::build::AstBuilder; |
| 58 | +use syntax::parse; |
| 59 | +use syntax::parse::token; |
| 60 | +use syntax::parse::token::InternedString; |
| 61 | + |
| 62 | +#[macro_registrar] |
| 63 | +#[cfg(not(test))] |
| 64 | +pub fn macro_registrar(register: |Name, SyntaxExtension|) { |
| 65 | + register(token::intern("fourcc"), |
| 66 | + NormalTT(~BasicMacroExpander { |
| 67 | + expander: expand_syntax_ext, |
| 68 | + span: None, |
| 69 | + }, |
| 70 | + None)); |
| 71 | +} |
| 72 | + |
| 73 | +pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) -> base::MacResult { |
| 74 | + let (expr, endian) = parse_tts(cx, tts); |
| 75 | + |
| 76 | + let little = match endian { |
| 77 | + None => false, |
| 78 | + Some(Ident{ident, span}) => match token::get_ident(ident.name).get() { |
| 79 | + "little" => true, |
| 80 | + "big" => false, |
| 81 | + "target" => target_endian_little(cx, sp), |
| 82 | + _ => { |
| 83 | + cx.span_err(span, "invalid endian directive in fourcc!"); |
| 84 | + target_endian_little(cx, sp) |
| 85 | + } |
| 86 | + } |
| 87 | + }; |
| 88 | + |
| 89 | + let s = match expr.node { |
| 90 | + // expression is a literal |
| 91 | + ast::ExprLit(lit) => match lit.node { |
| 92 | + // string literal |
| 93 | + ast::LitStr(ref s, _) => { |
| 94 | + if s.get().char_len() != 4 { |
| 95 | + cx.span_err(expr.span, "string literal with len != 4 in fourcc!"); |
| 96 | + } |
| 97 | + s |
| 98 | + } |
| 99 | + _ => { |
| 100 | + cx.span_err(expr.span, "unsupported literal in fourcc!"); |
| 101 | + return MRExpr(cx.expr_lit(sp, ast::LitUint(0u64, ast::TyU32))); |
| 102 | + } |
| 103 | + }, |
| 104 | + _ => { |
| 105 | + cx.span_err(expr.span, "non-literal in fourcc!"); |
| 106 | + return MRExpr(cx.expr_lit(sp, ast::LitUint(0u64, ast::TyU32))); |
| 107 | + } |
| 108 | + }; |
| 109 | + |
| 110 | + let mut val = 0u32; |
| 111 | + for codepoint in s.get().chars().take(4) { |
| 112 | + let byte = if codepoint as u32 > 0xFF { |
| 113 | + cx.span_err(expr.span, "fourcc! literal character out of range 0-255"); |
| 114 | + 0u8 |
| 115 | + } else { |
| 116 | + codepoint as u8 |
| 117 | + }; |
| 118 | + |
| 119 | + val = if little { |
| 120 | + (val >> 8) | ((byte as u32) << 24) |
| 121 | + } else { |
| 122 | + (val << 8) | (byte as u32) |
| 123 | + }; |
| 124 | + } |
| 125 | + let e = cx.expr_lit(sp, ast::LitUint(val as u64, ast::TyU32)); |
| 126 | + MRExpr(e) |
| 127 | +} |
| 128 | + |
| 129 | +struct Ident { |
| 130 | + ident: ast::Ident, |
| 131 | + span: Span |
| 132 | +} |
| 133 | + |
| 134 | +fn parse_tts(cx: &ExtCtxt, tts: &[ast::TokenTree]) -> (@ast::Expr, Option<Ident>) { |
| 135 | + let p = &mut parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), tts.to_owned()); |
| 136 | + let ex = p.parse_expr(); |
| 137 | + let id = if p.token == token::EOF { |
| 138 | + None |
| 139 | + } else { |
| 140 | + p.expect(&token::COMMA); |
| 141 | + let lo = p.span.lo; |
| 142 | + let ident = p.parse_ident(); |
| 143 | + let hi = p.last_span.hi; |
| 144 | + Some(Ident{ident: ident, span: mk_sp(lo, hi)}) |
| 145 | + }; |
| 146 | + if p.token != token::EOF { |
| 147 | + p.unexpected(); |
| 148 | + } |
| 149 | + (ex, id) |
| 150 | +} |
| 151 | + |
| 152 | +fn target_endian_little(cx: &ExtCtxt, sp: Span) -> bool { |
| 153 | + let meta = cx.meta_name_value(sp, InternedString::new("target_endian"), |
| 154 | + ast::LitStr(InternedString::new("little"), ast::CookedStr)); |
| 155 | + contains(cx.cfg(), meta) |
| 156 | +} |
| 157 | + |
| 158 | +// Fixes LLVM assert on Windows |
| 159 | +#[test] |
| 160 | +fn dummy_test() { } |
0 commit comments