For the benefit of the pretty printer we want to keep track of how string literals in the ast were originally represented in the source code. This commit changes parser functions so they don't extract strings from the token stream without at least also returning what style of string literal it was. This is stored in the resulting ast node for string literals, obviously, for the package id in `extern mod = r"package id"` view items, for the inline asm in `asm!()` invocations. For `asm!()`'s other arguments or for `extern "Rust" fn()` items, I just the style of string, because it seemed disproportionally cumbersome to thread that information through the string processing that happens with those string literals, given the limited advantage raw string literals would provide in these positions. The other syntax extensions don't seem to store passed string literals in the ast, so they also discard the style of strings they parse.
193 lines
5.7 KiB
Rust
193 lines
5.7 KiB
Rust
// Copyright 2012-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.
|
|
|
|
/*
|
|
* Inline assembly support.
|
|
*/
|
|
|
|
use ast;
|
|
use codemap::Span;
|
|
use ext::base;
|
|
use ext::base::*;
|
|
use parse;
|
|
use parse::token;
|
|
|
|
enum State {
|
|
Asm,
|
|
Outputs,
|
|
Inputs,
|
|
Clobbers,
|
|
Options
|
|
}
|
|
|
|
fn next_state(s: State) -> Option<State> {
|
|
match s {
|
|
Asm => Some(Outputs),
|
|
Outputs => Some(Inputs),
|
|
Inputs => Some(Clobbers),
|
|
Clobbers => Some(Options),
|
|
Options => None
|
|
}
|
|
}
|
|
|
|
pub fn expand_asm(cx: @ExtCtxt, sp: Span, tts: &[ast::token_tree])
|
|
-> base::MacResult {
|
|
let p = parse::new_parser_from_tts(cx.parse_sess(),
|
|
cx.cfg(),
|
|
tts.to_owned());
|
|
|
|
let mut asm = @"";
|
|
let mut asm_str_style = None;
|
|
let mut outputs = ~[];
|
|
let mut inputs = ~[];
|
|
let mut cons = ~"";
|
|
let mut volatile = false;
|
|
let mut alignstack = false;
|
|
let mut dialect = ast::asm_att;
|
|
|
|
let mut state = Asm;
|
|
|
|
// Not using labeled break to get us through one round of bootstrapping.
|
|
let mut continue_ = true;
|
|
while continue_ {
|
|
match state {
|
|
Asm => {
|
|
let (s, style) =
|
|
expr_to_str(cx, p.parse_expr(),
|
|
"inline assembly must be a string literal.");
|
|
asm = s;
|
|
asm_str_style = Some(style);
|
|
}
|
|
Outputs => {
|
|
while *p.token != token::EOF &&
|
|
*p.token != token::COLON &&
|
|
*p.token != token::MOD_SEP {
|
|
|
|
if outputs.len() != 0 {
|
|
p.eat(&token::COMMA);
|
|
}
|
|
|
|
let (constraint, _str_style) = p.parse_str();
|
|
p.expect(&token::LPAREN);
|
|
let out = p.parse_expr();
|
|
p.expect(&token::RPAREN);
|
|
|
|
let out = @ast::Expr {
|
|
id: ast::DUMMY_NODE_ID,
|
|
span: out.span,
|
|
node: ast::ExprAddrOf(ast::MutMutable, out)
|
|
};
|
|
|
|
outputs.push((constraint, out));
|
|
}
|
|
}
|
|
Inputs => {
|
|
while *p.token != token::EOF &&
|
|
*p.token != token::COLON &&
|
|
*p.token != token::MOD_SEP {
|
|
|
|
if inputs.len() != 0 {
|
|
p.eat(&token::COMMA);
|
|
}
|
|
|
|
let (constraint, _str_style) = p.parse_str();
|
|
p.expect(&token::LPAREN);
|
|
let input = p.parse_expr();
|
|
p.expect(&token::RPAREN);
|
|
|
|
inputs.push((constraint, input));
|
|
}
|
|
}
|
|
Clobbers => {
|
|
let mut clobs = ~[];
|
|
while *p.token != token::EOF &&
|
|
*p.token != token::COLON &&
|
|
*p.token != token::MOD_SEP {
|
|
|
|
if clobs.len() != 0 {
|
|
p.eat(&token::COMMA);
|
|
}
|
|
|
|
let (s, _str_style) = p.parse_str();
|
|
let clob = format!("~\\{{}\\}", s);
|
|
clobs.push(clob);
|
|
}
|
|
|
|
cons = clobs.connect(",");
|
|
}
|
|
Options => {
|
|
let (option, _str_style) = p.parse_str();
|
|
|
|
if "volatile" == option {
|
|
volatile = true;
|
|
} else if "alignstack" == option {
|
|
alignstack = true;
|
|
} else if "intel" == option {
|
|
dialect = ast::asm_intel;
|
|
}
|
|
|
|
if *p.token == token::COMMA {
|
|
p.eat(&token::COMMA);
|
|
}
|
|
}
|
|
}
|
|
|
|
while *p.token == token::COLON ||
|
|
*p.token == token::MOD_SEP ||
|
|
*p.token == token::EOF {
|
|
state = if *p.token == token::COLON {
|
|
p.bump();
|
|
match next_state(state) {
|
|
Some(x) => x,
|
|
None => {
|
|
continue_ = false;
|
|
break
|
|
}
|
|
}
|
|
} else if *p.token == token::MOD_SEP {
|
|
p.bump();
|
|
let s = match next_state(state) {
|
|
Some(x) => x,
|
|
None => {
|
|
continue_ = false;
|
|
break
|
|
}
|
|
};
|
|
match next_state(s) {
|
|
Some(x) => x,
|
|
None => {
|
|
continue_ = false;
|
|
break
|
|
}
|
|
}
|
|
} else if *p.token == token::EOF {
|
|
continue_ = false;
|
|
break;
|
|
} else {
|
|
state
|
|
};
|
|
}
|
|
}
|
|
|
|
MRExpr(@ast::Expr {
|
|
id: ast::DUMMY_NODE_ID,
|
|
node: ast::ExprInlineAsm(ast::inline_asm {
|
|
asm: asm,
|
|
asm_str_style: asm_str_style.unwrap(),
|
|
clobbers: cons.to_managed(),
|
|
inputs: inputs,
|
|
outputs: outputs,
|
|
volatile: volatile,
|
|
alignstack: alignstack,
|
|
dialect: dialect
|
|
}),
|
|
span: sp
|
|
})
|
|
}
|