Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add syntax extension fourcc!() #12034

Merged
merged 4 commits into from
Feb 9, 2014
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add new syntax extension fourcc!()
fourcc!() allows you to embed FourCC (or OSType) values that are
evaluated as u32 literals. It takes a 4-byte ASCII string and produces
the u32 resulting in interpreting those 4 bytes as a u32, using either
the platform-native endianness, or explicitly as big or little endian.
  • Loading branch information
lilyball authored and dguenther committed Feb 9, 2014
commit c1cc7e5f164b0119fcd60d6c9ade31fbfcff4b55
3 changes: 3 additions & 0 deletions src/libsyntax/ext/base.rs
Original file line number Diff line number Diff line change
@@ -194,6 +194,9 @@ pub fn syntax_expander_table() -> SyntaxEnv {
syntax_expanders.insert(intern("bytes"),
builtin_normal_expander(
ext::bytes::expand_syntax_ext));
syntax_expanders.insert(intern("fourcc"),
builtin_normal_tt_no_ctxt(
ext::fourcc::expand_syntax_ext));
syntax_expanders.insert(intern("concat_idents"),
builtin_normal_expander(
ext::concat_idents::expand_syntax_ext));
106 changes: 106 additions & 0 deletions src/libsyntax/ext/fourcc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/* The compiler code necessary to support the fourcc! extension. */

// fourcc!() is called with a single 4-character string, and an optional ident
// that is either `big` or `little`. If the ident is omitted it is assumed to
// be the platform-native value. It returns a u32.

use ast;
use attr::contains;
use codemap::{Span, mk_sp};
use ext::base::*;
use ext::base;
use ext::build::AstBuilder;
use parse;
use parse::token;

use std::ascii::AsciiCast;

pub fn expand_syntax_ext(cx: @ExtCtxt, sp: Span, tts: &[ast::token_tree]) -> base::MacResult {
let (expr, endian) = parse_tts(cx, tts);

let little = match endian {
None => target_endian_little(cx, sp),
Some(Ident{ident, span}) => match cx.str_of(ident).as_slice() {
"little" => true,
"big" => false,
_ => {
cx.span_err(span, "invalid endian directive in fourcc!");
target_endian_little(cx, sp)
}
}
};

let s = match expr.node {
// expression is a literal
ast::ExprLit(lit) => match lit.node {
// string literal
ast::lit_str(s) => {
if !s.is_ascii() {
cx.span_err(expr.span, "non-ascii string literal in fourcc!");
} else if s.len() != 4 {
cx.span_err(expr.span, "string literal with len != 4 in fourcc!");
}
s
}
_ => {
cx.span_err(expr.span, "unsupported literal in fourcc!");
return MRExpr(cx.expr_lit(sp, ast::lit_uint(0u64, ast::ty_u32)));
}
},
_ => {
cx.span_err(expr.span, "non-literal in fourcc!");
return MRExpr(cx.expr_lit(sp, ast::lit_uint(0u64, ast::ty_u32)));
}
};

let mut val = 0u32;
if little {
for byte in s.byte_rev_iter().take(4) {
val = (val << 8) | (byte as u32);
}
} else {
for byte in s.byte_iter().take(4) {
val = (val << 8) | (byte as u32);
}
}
let e = cx.expr_lit(sp, ast::lit_uint(val as u64, ast::ty_u32));
MRExpr(e)
}

struct Ident {
ident: ast::Ident,
span: Span
}

fn parse_tts(cx: @ExtCtxt, tts: &[ast::token_tree]) -> (@ast::Expr, Option<Ident>) {
let p = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), tts.to_owned());
let ex = p.parse_expr();
let id = if *p.token == token::EOF {
None
} else {
p.expect(&token::COMMA);
let lo = p.span.lo;
let ident = p.parse_ident();
let hi = p.last_span.hi;
Some(Ident{ident: ident, span: mk_sp(lo, hi)})
};
if *p.token != token::EOF {
p.unexpected();
}
(ex, id)
}

fn target_endian_little(cx: @ExtCtxt, sp: Span) -> bool {
let meta = cx.meta_name_value(sp, @"target_endian", ast::lit_str(@"little"));
contains(cx.cfg(), meta)
}
1 change: 1 addition & 0 deletions src/libsyntax/lib.rs
Original file line number Diff line number Diff line change
@@ -95,6 +95,7 @@ pub mod ext {
pub mod bytes;
pub mod concat;
pub mod concat_idents;
pub mod fourcc;
pub mod log_syntax;
pub mod source_util;

14 changes: 14 additions & 0 deletions src/test/compile-fail/syntax-extension-fourcc-bad-len.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

fn main() {
let val = fourcc!("foo"); //~ ERROR string literal with len != 4 in fourcc!
let val2 = fourcc!("fooba"); //~ ERROR string literal with len != 4 in fourcc!
}
13 changes: 13 additions & 0 deletions src/test/compile-fail/syntax-extension-fourcc-invalid-endian.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

fn main() {
let val = fourcc!("foo ", bork); //~ ERROR invalid endian directive in fourcc!
}
13 changes: 13 additions & 0 deletions src/test/compile-fail/syntax-extension-fourcc-non-ascii-str.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

fn main() {
let v = fourcc!("fooλ"); //~ ERROR non-ascii string literal in fourcc!
}
13 changes: 13 additions & 0 deletions src/test/compile-fail/syntax-extension-fourcc-non-literal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

fn main() {
let val = fourcc!(foo); //~ ERROR non-literal in fourcc!
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

fn main() {
let val = fourcc!(45f); //~ ERROR unsupported literal in fourcc!
}
30 changes: 30 additions & 0 deletions src/test/run-pass/syntax-extension-fourcc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

static static_val: u32 = fourcc!("foo ");
static static_val_le: u32 = fourcc!("foo ", little);
static static_val_be: u32 = fourcc!("foo ", big);

fn main() {
let val = fourcc!("foo ");
let exp = if cfg!(target_endian = "big") { 0x666f6f20u32 } else { 0x206f6f66u32 };
assert_eq!(val, exp);

let val = fourcc!("foo ", big);
assert_eq!(val, 0x666f6f20u32);

let val = fourcc!("foo ", little);
assert_eq!(val, 0x206f6f66u32);

let exp = if cfg!(target_endian = "big") { 0x666f6f20u32 } else { 0x206f6f66u32 };
assert_eq!(static_val, exp);
assert_eq!(static_val_le, 0x206f6f66u32);
assert_eq!(static_val_be, 0x666f6f20u32);
}