diff --git a/src/core_macro_forms.rs b/src/core_macro_forms.rs index cec9b5e..7b51cc5 100644 --- a/src/core_macro_forms.rs +++ b/src/core_macro_forms.rs @@ -716,7 +716,6 @@ fn macro_definitions() { {named => ["part_name"] : val {call_with_type : Expr T}}; {named => ["part_name"] : binding {call_with_type : Pat T}}] } - {Type Nat :} // "unused_type". In practice, this is `trivial_type_form` some_macro ie }), diff --git a/src/expand.rs b/src/expand.rs index 9fccf1a..4f5f8a4 100644 --- a/src/expand.rs +++ b/src/expand.rs @@ -72,27 +72,107 @@ pub fn expand(ast: &Ast) -> Result { #[test] fn expand_basic_macros() { // Quasiquotation doesn't work with `u!`, so we have to use `ast!`: - let make_expr = ast!({"Expr" "quote_expr" : "nt" => (vr "Expr"), + let macro_body_0_args = ast!({"Expr" "quote_expr" : "nt" => (vr "Expr"), "body" => (++ true (,u!({apply : plus [one ; two]})))}); - let macro_def = u!({Syntax scope : + let uqef = ::core_qq_forms::unquote_form(n("Expr"), true, 1); + let uqpf = ::core_qq_forms::unquote_form(n("Pat"), true, 1); + + let macro_def_0_args = u!({Syntax scope : [] {literal => [] : {call : DefaultToken} just_add_1_and_2} - {Type Nat :} // "unused_type" just_add_1_and_2_macro - (,make_expr.clone()) + (,macro_body_0_args.clone()) }); - assert_m!(::runtime::eval::eval_top(¯o_def), Ok(_)); + // Full of closures, so hard to compare: + assert_m!(::runtime::eval::eval_top(¯o_def_0_args), Ok(_)); assert_eq!( expand(&u!({ ::core_macro_forms::macro_invocation( form_pat!((lit "just_add_1_and_2")), n("just_add_1_and_2_macro"), - ::runtime::eval::Closure { body: make_expr, params: vec![], env: Assoc::new() }, + ::runtime::eval::Closure { + body: macro_body_0_args, + params: vec![], + env: Assoc::new(), + }, vec![], ); })), Ok(u!({apply : plus [one ; two]})) ); + + // A macro that generates a one-adding expression: + + let macro_body_1_arg = ast!({"Expr" "quote_expr" : "nt" => (vr "Expr"), + "body" => (++ true (,u!({apply : plus [one ; { uqef.clone(); (~) e}]})))}); + + let macro_def_1_arg = u!({Syntax scope : + [] {seq : [{literal => [] : {call : DefaultToken} add_1} ; + {named => ["part_name"] : e {call : Expr}}] } + add_1_macro + (,macro_body_1_arg.clone()) + }); + + // Full of closures, so hard to compare: + assert_m!(::runtime::eval::eval_top(¯o_def_1_arg), Ok(_)); + + assert_eq!( + expand(&u!({ + ::core_macro_forms::macro_invocation( + // duplicates the syntax syntax above + form_pat!([(lit "add_1"), (named "e", (call "Expr"))]), + n("add_1_macro"), + ::runtime::eval::Closure { + body: macro_body_1_arg, + params: vec![n("e")], + env: Assoc::new(), + }, + vec![], + ); + five // syntax argument for e + })), + Ok(u!({apply : plus [one ; five]})) + ); + + // A let macro: + + let macro_body_let = ast!({"Expr" "quote_expr" : "nt" => (vr "Expr"), + "body" => (++ true (,u!( + {match : { uqef.clone(); (~) let_val} + [{ uqpf.clone(); (~) let_pat } {uqef.clone(); (~) let_body}]})))}); + + let macro_def_let = u!({Syntax scope : + [T; S] {seq : [{literal => [] : {call : DefaultToken} let} ; + {named => ["part_name"] : let_pat {call : Pat}} ; + {named => ["part_name"] : let_val {call : Expr}} ; + {named => ["part_name"] : let_body {call : Expr}}] } + let_macro + (,macro_body_let.clone()) + }); + + // Full of closures, so hard to compare: + assert_m!(::runtime::eval::eval_top(¯o_def_let), Ok(_)); + + assert_eq!( + expand(&u!({ + ::core_macro_forms::macro_invocation( + // duplicates the syntax syntax above + form_pat!([(lit "let"), (named "let_pat", (call "Pat")), + (named "let_val", (call "Expr")), (named "let_body", (call "Expr"))]), + n("let_macro"), + ::runtime::eval::Closure { + body: macro_body_let, + params: vec![n("let_val"), n("let_pat"), n("let_body")], + env: Assoc::new(), + }, + vec![], + ); + x // let_pat + five // let_val + {apply : times [x ; eight]} // let_body + })), + Ok(u!({match : five [x {apply : times [x ; eight]}]})) + ); } diff --git a/src/macros/flimsy_syntax.rs b/src/macros/flimsy_syntax.rs index 4ebd1ee..99decdb 100644 --- a/src/macros/flimsy_syntax.rs +++ b/src/macros/flimsy_syntax.rs @@ -190,7 +190,17 @@ where I: Iterator { match parse_flimsy_mbe(flimsy, grammar) { None => EnvMBE::new(), Some(res) => { - let _ = flimsy_seq.next(); + // `Anyways`es shouldn't consume anything (and they'll always be `Named`): + let consuming = match grammar { + Named(_, ref body) => match **body { + Anyways(_) => false, + _ => true, + }, + _ => true, + }; + if consuming { + let _ = flimsy_seq.next(); + } res } } @@ -228,6 +238,11 @@ pub fn parse_flimsy_mbe(flimsy: &Ast, grammar: &FormPat) -> Option> } _ => panic!("Needed a REP shape"), }, + Alt(ref subs) => { + // HACK: always pick the first branch of the `Alt` + // (mainly affects unquotation, where it skips the type annotation) + parse_flimsy_mbe(flimsy, &*subs[0]) + } Named(name, ref body) => Some(EnvMBE::new_from_leaves( ::util::assoc::Assoc::new().set(*name, parse_flimsy_ast(flimsy, &*body)), )),