1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#![feature(plugin_registrar, rustc_private, slice_patterns)]

extern crate syntax;
extern crate rustc;
extern crate rustc_plugin;

use std::rc::Rc;
use rustc_plugin::Registry;
use syntax::ast::LitKind::ByteStr;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, MacResult, DummyResult, MacEager};
use syntax::ext::build::AstBuilder;
use syntax::parse::token;
use syntax::tokenstream::TokenTree;

fn decode_hex_str(hex: &str) -> Result<Vec<u8>, String> {
    let mut vec = Vec::with_capacity(hex.len() / 2);
    let mut filtered = hex.chars().filter(|&c| !c.is_whitespace());
    // eat hex characters two at a time
    while let Some(first) = filtered.next() {
        let hi = try!(decode_hex_char(first));
        if let Some(second) = filtered.next() {
            let lo = try!(decode_hex_char(second));
            // finally, combine them
            vec.push((hi << 4) | lo)
        } else {
            return Err("odd number of hex characters".into());
        }
    }
    Ok(vec)
}

fn decode_hex_char(c: char) -> Result<u8, String> {
    if let Some(digit) = c.to_digit(16) {
        Ok(digit as u8)
    } else {
        Err(format!("non-hexadecimal non-whitespace character {:?}", c))
    }
}

fn expand_hex(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree]) -> Box<MacResult + 'static> {
    match *args {
        [TokenTree::Token(tok_span, token::Literal(token::Str_(name), _))] => {
            match decode_hex_str(&name.as_str()) {
                Ok(bytes) => {
                    // success!
                    let byte_str = ByteStr(Rc::new(bytes));
                    return MacEager::expr(cx.expr_lit(sp, byte_str));
                }
                Err(reason) => cx.span_err(tok_span, &reason),
            }
        }
        [ref tt] => cx.span_err(tt.span(), "expected string literal"),
        [_, ref tt, ..] => cx.span_err(tt.span(), "too many arguments"),
        _ => cx.span_err(sp, "expected string literal"),
    }
    // error path
    DummyResult::any(sp)
}

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("hex", expand_hex);
}