use crate::lexer::{Delimiter, Keyword, LiteralKind, Span, TokenKind};
use super::{
ExpansionElement, MacroContext, MacroDef, MacroExpansion, MacroId, MacroPattern, MacroRule,
MetaVarKind, PatternElement, RepetitionKind,
};
pub fn register_builtins(ctx: &mut MacroContext) {
register_println(ctx);
register_print(ctx);
register_eprintln(ctx);
register_eprint(ctx);
register_format(ctx);
register_format_args(ctx);
register_assert(ctx);
register_assert_eq(ctx);
register_assert_ne(ctx);
register_debug_assert(ctx);
register_vec(ctx);
register_dbg(ctx);
register_todo(ctx);
register_unimplemented(ctx);
register_unreachable(ctx);
register_stringify(ctx);
register_concat(ctx);
register_include(ctx);
register_include_str(ctx);
register_include_bytes(ctx);
register_env(ctx);
register_option_env(ctx);
register_cfg(ctx);
register_compile_error(ctx);
register_line(ctx);
register_column(ctx);
register_file(ctx);
register_module_path(ctx);
}
fn simple_macro(
name: &str,
pattern: Vec<PatternElement>,
expansion: Vec<ExpansionElement>,
) -> MacroDef {
MacroDef {
id: MacroId::fresh(),
name: name.into(),
rules: vec![MacroRule {
pattern: MacroPattern { elements: pattern },
expansion: MacroExpansion {
elements: expansion,
},
span: Span::dummy(),
}],
is_exported: true,
span: Span::dummy(),
}
}
fn variadic_tt_pattern() -> Vec<PatternElement> {
vec![PatternElement::Repetition {
elements: vec![PatternElement::MetaVar {
name: "arg".into(),
kind: MetaVarKind::TokenTree,
}],
separator: None,
repetition: RepetitionKind::ZeroOrMore,
}]
}
fn variadic_tt_expansion() -> Vec<ExpansionElement> {
vec![ExpansionElement::Repetition {
elements: vec![ExpansionElement::MetaVar("arg".into())],
separator: None,
repetition: RepetitionKind::ZeroOrMore,
}]
}
fn register_println(ctx: &mut MacroContext) {
let def = MacroDef {
id: MacroId::fresh(),
name: "println".into(),
rules: vec![
MacroRule {
pattern: MacroPattern { elements: vec![] },
expansion: MacroExpansion {
elements: vec![
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ExpansionElement::Token(TokenKind::ColonColon, Span::dummy()),
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ExpansionElement::Token(TokenKind::ColonColon, Span::dummy()),
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ExpansionElement::Token(TokenKind::Not, Span::dummy()),
ExpansionElement::Delimited {
delimiter: Delimiter::Paren,
elements: vec![
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ],
span: Span::dummy(),
},
],
},
span: Span::dummy(),
},
MacroRule {
pattern: MacroPattern {
elements: variadic_tt_pattern(),
},
expansion: MacroExpansion {
elements: vec![
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ExpansionElement::Token(TokenKind::Not, Span::dummy()),
ExpansionElement::Delimited {
delimiter: Delimiter::Paren,
elements: variadic_tt_expansion(),
span: Span::dummy(),
},
],
},
span: Span::dummy(),
},
],
is_exported: true,
span: Span::dummy(),
};
ctx.register_macro(def);
}
fn register_print(ctx: &mut MacroContext) {
let def = simple_macro(
"print",
variadic_tt_pattern(),
vec![
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ExpansionElement::Token(TokenKind::Not, Span::dummy()),
ExpansionElement::Delimited {
delimiter: Delimiter::Paren,
elements: variadic_tt_expansion(),
span: Span::dummy(),
},
],
);
ctx.register_macro(def);
}
fn register_eprintln(ctx: &mut MacroContext) {
let def = simple_macro(
"eprintln",
variadic_tt_pattern(),
vec![
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ExpansionElement::Token(TokenKind::Not, Span::dummy()),
ExpansionElement::Delimited {
delimiter: Delimiter::Paren,
elements: variadic_tt_expansion(),
span: Span::dummy(),
},
],
);
ctx.register_macro(def);
}
fn register_eprint(ctx: &mut MacroContext) {
let def = simple_macro(
"eprint",
variadic_tt_pattern(),
vec![
ExpansionElement::Token(TokenKind::Ident, Span::dummy()),
ExpansionElement::Token(TokenKind::Not, Span::dummy()),
ExpansionElement::Delimited {
delimiter: Delimiter::Paren,
elements: variadic_tt_expansion(),
span: Span::dummy(),
},
],
);
ctx.register_macro(def);
}
fn register_format(ctx: &mut MacroContext) {
let def = simple_macro(
"format",
variadic_tt_pattern(),
vec![
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ExpansionElement::Token(TokenKind::ColonColon, Span::dummy()),
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ExpansionElement::Delimited {
delimiter: Delimiter::Paren,
elements: vec![
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ExpansionElement::Token(TokenKind::Not, Span::dummy()),
ExpansionElement::Delimited {
delimiter: Delimiter::Paren,
elements: variadic_tt_expansion(),
span: Span::dummy(),
},
],
span: Span::dummy(),
},
],
);
ctx.register_macro(def);
}
fn register_format_args(ctx: &mut MacroContext) {
let def = simple_macro(
"format_args",
variadic_tt_pattern(),
variadic_tt_expansion(),
);
ctx.register_macro(def);
}
fn register_assert(ctx: &mut MacroContext) {
let def = MacroDef {
id: MacroId::fresh(),
name: "assert".into(),
rules: vec![
MacroRule {
pattern: MacroPattern {
elements: vec![PatternElement::MetaVar {
name: "cond".into(),
kind: MetaVarKind::Expr,
}],
},
expansion: MacroExpansion {
elements: vec![
ExpansionElement::Token(TokenKind::Keyword(Keyword::If), Span::dummy()),
ExpansionElement::Token(TokenKind::Not, Span::dummy()),
ExpansionElement::Delimited {
delimiter: Delimiter::Paren,
elements: vec![ExpansionElement::MetaVar("cond".into())],
span: Span::dummy(),
},
ExpansionElement::Delimited {
delimiter: Delimiter::Brace,
elements: vec![
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ExpansionElement::Token(TokenKind::Not, Span::dummy()),
ExpansionElement::Delimited {
delimiter: Delimiter::Paren,
elements: vec![ExpansionElement::Token(
TokenKind::Literal {
kind: LiteralKind::Str { terminated: true },
suffix: None,
},
Span::dummy(),
)],
span: Span::dummy(),
},
],
span: Span::dummy(),
},
],
},
span: Span::dummy(),
},
MacroRule {
pattern: MacroPattern {
elements: vec![
PatternElement::MetaVar {
name: "cond".into(),
kind: MetaVarKind::Expr,
},
PatternElement::Token(TokenKind::Comma),
PatternElement::Repetition {
elements: vec![PatternElement::MetaVar {
name: "arg".into(),
kind: MetaVarKind::TokenTree,
}],
separator: None,
repetition: RepetitionKind::ZeroOrMore,
},
],
},
expansion: MacroExpansion {
elements: vec![
ExpansionElement::Token(TokenKind::Keyword(Keyword::If), Span::dummy()),
ExpansionElement::Token(TokenKind::Not, Span::dummy()),
ExpansionElement::Delimited {
delimiter: Delimiter::Paren,
elements: vec![ExpansionElement::MetaVar("cond".into())],
span: Span::dummy(),
},
ExpansionElement::Delimited {
delimiter: Delimiter::Brace,
elements: vec![
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ExpansionElement::Token(TokenKind::Not, Span::dummy()),
ExpansionElement::Delimited {
delimiter: Delimiter::Paren,
elements: vec![ExpansionElement::Repetition {
elements: vec![ExpansionElement::MetaVar("arg".into())],
separator: None,
repetition: RepetitionKind::ZeroOrMore,
}],
span: Span::dummy(),
},
],
span: Span::dummy(),
},
],
},
span: Span::dummy(),
},
],
is_exported: true,
span: Span::dummy(),
};
ctx.register_macro(def);
}
fn register_assert_eq(ctx: &mut MacroContext) {
let def = simple_macro(
"assert_eq",
vec![
PatternElement::MetaVar {
name: "left".into(),
kind: MetaVarKind::Expr,
},
PatternElement::Token(TokenKind::Comma),
PatternElement::MetaVar {
name: "right".into(),
kind: MetaVarKind::Expr,
},
],
vec![
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ExpansionElement::Token(TokenKind::Not, Span::dummy()),
ExpansionElement::Delimited {
delimiter: Delimiter::Paren,
elements: vec![
ExpansionElement::MetaVar("left".into()),
ExpansionElement::Token(TokenKind::Comma, Span::dummy()),
ExpansionElement::MetaVar("right".into()),
],
span: Span::dummy(),
},
],
);
ctx.register_macro(def);
}
fn register_assert_ne(ctx: &mut MacroContext) {
let def = simple_macro(
"assert_ne",
vec![
PatternElement::MetaVar {
name: "left".into(),
kind: MetaVarKind::Expr,
},
PatternElement::Token(TokenKind::Comma),
PatternElement::MetaVar {
name: "right".into(),
kind: MetaVarKind::Expr,
},
],
vec![
ExpansionElement::Token(TokenKind::Ident, Span::dummy()),
ExpansionElement::Token(TokenKind::Not, Span::dummy()),
ExpansionElement::Delimited {
delimiter: Delimiter::Paren,
elements: vec![
ExpansionElement::MetaVar("left".into()),
ExpansionElement::Token(TokenKind::Comma, Span::dummy()),
ExpansionElement::MetaVar("right".into()),
],
span: Span::dummy(),
},
],
);
ctx.register_macro(def);
}
fn register_debug_assert(ctx: &mut MacroContext) {
let def = simple_macro(
"debug_assert",
variadic_tt_pattern(),
vec![
ExpansionElement::Token(TokenKind::Keyword(Keyword::If), Span::dummy()),
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ExpansionElement::Token(TokenKind::Not, Span::dummy()),
ExpansionElement::Delimited {
delimiter: Delimiter::Paren,
elements: vec![ExpansionElement::Token(TokenKind::Ident, Span::dummy())], span: Span::dummy(),
},
ExpansionElement::Delimited {
delimiter: Delimiter::Brace,
elements: vec![
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ExpansionElement::Token(TokenKind::Not, Span::dummy()),
ExpansionElement::Delimited {
delimiter: Delimiter::Paren,
elements: variadic_tt_expansion(),
span: Span::dummy(),
},
],
span: Span::dummy(),
},
],
);
ctx.register_macro(def);
}
fn register_vec(ctx: &mut MacroContext) {
let def = MacroDef {
id: MacroId::fresh(),
name: "vec".into(),
rules: vec![
MacroRule {
pattern: MacroPattern { elements: vec![] },
expansion: MacroExpansion {
elements: vec![
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ExpansionElement::Token(TokenKind::ColonColon, Span::dummy()),
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ExpansionElement::Delimited {
delimiter: Delimiter::Paren,
elements: vec![],
span: Span::dummy(),
},
],
},
span: Span::dummy(),
},
MacroRule {
pattern: MacroPattern {
elements: vec![
PatternElement::MetaVar {
name: "elem".into(),
kind: MetaVarKind::Expr,
},
PatternElement::Token(TokenKind::Semi),
PatternElement::MetaVar {
name: "n".into(),
kind: MetaVarKind::Expr,
},
],
},
expansion: MacroExpansion {
elements: vec![
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ExpansionElement::Token(TokenKind::ColonColon, Span::dummy()),
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ExpansionElement::Delimited {
delimiter: Delimiter::Paren,
elements: vec![
ExpansionElement::MetaVar("elem".into()),
ExpansionElement::Token(TokenKind::Comma, Span::dummy()),
ExpansionElement::MetaVar("n".into()),
],
span: Span::dummy(),
},
],
},
span: Span::dummy(),
},
MacroRule {
pattern: MacroPattern {
elements: vec![
PatternElement::Repetition {
elements: vec![PatternElement::MetaVar {
name: "elem".into(),
kind: MetaVarKind::Expr,
}],
separator: Some(TokenKind::Comma),
repetition: RepetitionKind::ZeroOrMore,
},
PatternElement::Repetition {
elements: vec![PatternElement::Token(TokenKind::Comma)],
separator: None,
repetition: RepetitionKind::ZeroOrOne,
},
],
},
expansion: MacroExpansion {
elements: vec![ExpansionElement::Delimited {
delimiter: Delimiter::Brace,
elements: vec![
ExpansionElement::Token(
TokenKind::Keyword(Keyword::Let),
Span::dummy(),
),
ExpansionElement::Token(
TokenKind::Keyword(Keyword::Mut),
Span::dummy(),
),
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ExpansionElement::Token(TokenKind::Eq, Span::dummy()),
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ExpansionElement::Token(TokenKind::ColonColon, Span::dummy()),
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ExpansionElement::Delimited {
delimiter: Delimiter::Paren,
elements: vec![],
span: Span::dummy(),
},
ExpansionElement::Token(TokenKind::Semi, Span::dummy()),
ExpansionElement::Repetition {
elements: vec![
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ExpansionElement::Token(TokenKind::Dot, Span::dummy()),
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ExpansionElement::Delimited {
delimiter: Delimiter::Paren,
elements: vec![ExpansionElement::MetaVar("elem".into())],
span: Span::dummy(),
},
ExpansionElement::Token(TokenKind::Semi, Span::dummy()),
],
separator: None,
repetition: RepetitionKind::ZeroOrMore,
},
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ],
span: Span::dummy(),
}],
},
span: Span::dummy(),
},
],
is_exported: true,
span: Span::dummy(),
};
ctx.register_macro(def);
}
fn register_dbg(ctx: &mut MacroContext) {
let def = simple_macro("dbg", variadic_tt_pattern(), variadic_tt_expansion());
ctx.register_macro(def);
}
fn register_todo(ctx: &mut MacroContext) {
let def = simple_macro(
"todo",
variadic_tt_pattern(),
vec![
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ExpansionElement::Token(TokenKind::Not, Span::dummy()),
ExpansionElement::Delimited {
delimiter: Delimiter::Paren,
elements: variadic_tt_expansion(),
span: Span::dummy(),
},
],
);
ctx.register_macro(def);
}
fn register_unimplemented(ctx: &mut MacroContext) {
let def = simple_macro(
"unimplemented",
variadic_tt_pattern(),
vec![
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ExpansionElement::Token(TokenKind::Not, Span::dummy()),
ExpansionElement::Delimited {
delimiter: Delimiter::Paren,
elements: variadic_tt_expansion(),
span: Span::dummy(),
},
],
);
ctx.register_macro(def);
}
fn register_unreachable(ctx: &mut MacroContext) {
let def = simple_macro(
"unreachable",
variadic_tt_pattern(),
vec![
ExpansionElement::Token(TokenKind::Ident, Span::dummy()), ExpansionElement::Token(TokenKind::Not, Span::dummy()),
ExpansionElement::Delimited {
delimiter: Delimiter::Paren,
elements: variadic_tt_expansion(),
span: Span::dummy(),
},
],
);
ctx.register_macro(def);
}
fn register_stringify(ctx: &mut MacroContext) {
let def = simple_macro("stringify", variadic_tt_pattern(), variadic_tt_expansion());
ctx.register_macro(def);
}
fn register_concat(ctx: &mut MacroContext) {
let def = simple_macro("concat", variadic_tt_pattern(), variadic_tt_expansion());
ctx.register_macro(def);
}
fn register_include(ctx: &mut MacroContext) {
let def = simple_macro(
"include",
vec![PatternElement::MetaVar {
name: "file".into(),
kind: MetaVarKind::Literal,
}],
vec![ExpansionElement::MetaVar("file".into())],
);
ctx.register_macro(def);
}
fn register_include_str(ctx: &mut MacroContext) {
let def = simple_macro(
"include_str",
vec![PatternElement::MetaVar {
name: "file".into(),
kind: MetaVarKind::Literal,
}],
vec![ExpansionElement::MetaVar("file".into())],
);
ctx.register_macro(def);
}
fn register_include_bytes(ctx: &mut MacroContext) {
let def = simple_macro(
"include_bytes",
vec![PatternElement::MetaVar {
name: "file".into(),
kind: MetaVarKind::Literal,
}],
vec![ExpansionElement::MetaVar("file".into())],
);
ctx.register_macro(def);
}
fn register_env(ctx: &mut MacroContext) {
let def = simple_macro(
"env",
vec![PatternElement::MetaVar {
name: "name".into(),
kind: MetaVarKind::Literal,
}],
vec![ExpansionElement::MetaVar("name".into())],
);
ctx.register_macro(def);
}
fn register_option_env(ctx: &mut MacroContext) {
let def = simple_macro(
"option_env",
vec![PatternElement::MetaVar {
name: "name".into(),
kind: MetaVarKind::Literal,
}],
vec![ExpansionElement::MetaVar("name".into())],
);
ctx.register_macro(def);
}
fn register_cfg(ctx: &mut MacroContext) {
let def = simple_macro(
"cfg",
variadic_tt_pattern(),
vec![ExpansionElement::Token(
TokenKind::Literal {
kind: LiteralKind::Bool(true),
suffix: None,
},
Span::dummy(),
)],
);
ctx.register_macro(def);
}
fn register_compile_error(ctx: &mut MacroContext) {
let def = simple_macro(
"compile_error",
vec![PatternElement::MetaVar {
name: "msg".into(),
kind: MetaVarKind::Literal,
}],
vec![ExpansionElement::MetaVar("msg".into())],
);
ctx.register_macro(def);
}
fn register_line(ctx: &mut MacroContext) {
let def = simple_macro(
"line",
vec![],
vec![ExpansionElement::Token(
TokenKind::Literal {
kind: LiteralKind::Int {
base: crate::lexer::IntBase::Decimal,
empty_int: false,
},
suffix: None,
},
Span::dummy(),
)],
);
ctx.register_macro(def);
}
fn register_column(ctx: &mut MacroContext) {
let def = simple_macro(
"column",
vec![],
vec![ExpansionElement::Token(
TokenKind::Literal {
kind: LiteralKind::Int {
base: crate::lexer::IntBase::Decimal,
empty_int: false,
},
suffix: None,
},
Span::dummy(),
)],
);
ctx.register_macro(def);
}
fn register_file(ctx: &mut MacroContext) {
let def = simple_macro(
"file",
vec![],
vec![ExpansionElement::Token(
TokenKind::Literal {
kind: LiteralKind::Str { terminated: true },
suffix: None,
},
Span::dummy(),
)],
);
ctx.register_macro(def);
}
fn register_module_path(ctx: &mut MacroContext) {
let def = simple_macro(
"module_path",
vec![],
vec![ExpansionElement::Token(
TokenKind::Literal {
kind: LiteralKind::Str { terminated: true },
suffix: None,
},
Span::dummy(),
)],
);
ctx.register_macro(def);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_register_builtins() {
let mut ctx = MacroContext::new();
register_builtins(&mut ctx);
assert!(ctx.lookup_macro("println").is_some());
assert!(ctx.lookup_macro("print").is_some());
assert!(ctx.lookup_macro("format").is_some());
assert!(ctx.lookup_macro("vec").is_some());
assert!(ctx.lookup_macro("assert").is_some());
assert!(ctx.lookup_macro("assert_eq").is_some());
assert!(ctx.lookup_macro("dbg").is_some());
assert!(ctx.lookup_macro("todo").is_some());
}
}