#.. This File contains rules for making rules and macros and some helper functions to make that easier. # Macros: lua block ".." |local function make_fn(wrap_in_block) | return function(compiler, vars, kind) # Do a minimal amount of pre-processing (get the aliases and the source) | local aliases = compiler:repr(compiler:get_aliases(vars.macro_def)) | local src = compiler:repr(vars.user_macro.src) | local user_macro = compiler:tree_to_lua(vars.user_macro) # Then produce a block of code that creates the macro at runtime | local lua = [[ | compiler:defmacro(%s, (function(compiler, vars, kind) | if kind == "Expression" then | compiler:error("Macro "..%s.." was defined to be a block, but is being used as an expression") | end | local user_macro = %s | local lua = user_macro(compiler, vars) | %s | return lua, true | end), %s) | ]] | lua = lua:format(aliases, compiler:repr(aliases), user_macro, | wrap_in_block and [[lua = "do\\n "..lua.."\\nend"]] or "", src) | return lua, true | end |end |compiler:defmacro("macro statement %macro_def = %user_macro", make_fn(false), "see:lib/metaprogramming.nom") |compiler:defmacro("macro block %macro_def = %user_macro", make_fn(true), "see:lib/metaprogramming.nom") macro block [macro %macro_def = %user_macro] =: ".."|compiler:defmacro( | \lua expr "compiler:get_aliases(vars.macro_def)"\, | \lua expr "compiler:tree_to_lua(vars.user_macro)"\, | \lua expr "compiler:repr(vars.user_macro.src)"\) macro [compiler] =: "compiler" macro [compiler's %key] =: ".."|compiler[\%key as lua\] macro [compiler %method %args] =: lua block ".." |local args = {"compiler"} |for _,arg in ipairs(vars.args.value) do | table.insert(args, compiler:tree_to_lua(arg)) |end |return "compiler["..compiler:repr(compiler:tree_to_value(vars.method, vars)).."]("..table.concat(args, ", ")..")" macro [compiler utils %method %args] =: lua block ".." |local args = {} |for i,arg in ipairs(vars.args.value) do | args[i] = compiler:tree_to_lua(arg) |end |return "compiler.utils["..compiler:repr(compiler:tree_to_value(vars.method, vars)).."]("..table.concat(args, ", ")..")" # Macro that lets you make new rules #.. This is a macro instead of a rule because it needs to pre-empt processing the list of function calls and convert it into a list of strings (rather than call a function that is currently in the middle of being defined). Being a macro also allows us to snatch the source code and store that macro block [rule %rule_def = %body] =: ".." |compiler:def( | \compiler "repr" [compiler "get_aliases" [%rule_def]]\, | \compiler "tree_to_lua" [%body]\, | \compiler "repr" [lua expr "vars.body.src"]\) rule [fart]=: lua block "print('poot')" macro block [alias %aliases = %aliased] =: lua block ".." |local aliases = compiler:get_aliases(vars.aliases) |aliases = compiler:repr(aliases) |if vars.aliased.type ~= "Thunk" then | compiler:error("Right hand side of alias % = % should be a Thunk, but got "..vars.aliased.type | ..". Maybe you used = instead of =: by mistake?") |end |local aliased = next(compiler:get_aliases(vars.aliased.value)) |aliased = compiler:repr(aliased) |local lua = ([[ |compiler:add_aliases(%s, compiler.defs[%s]) |]]):format(aliases, aliased) |return lua # Get the source code for a function rule [help %rule] =: lua block ".." |local fn_def = compiler:get_fn_def(vars.rule) |if not fn_def then | compiler:writeln("Rule not found: "..compiler:repr(vars.rule)) |else | compiler:writeln("rule "..compiler:repr(compiler.utils.keys(fn_def.aliases)) | .." ="..(fn_def.src or ":\\n ")) |end # Macro helper functions rule [%tree as value] =: lua expr "compiler:tree_to_value(vars.tree, vars)" rule [%tree as lua] =: lua expr "compiler:tree_to_lua(vars.tree)" rule [test, foobar] =: lua expr "print('yup')" # Compiler tools rule [eval %code, run %code] =: compiler "run" [%code] rule [source code from tree %tree] =: lua block ".." |local _,_,leading_space = vars.tree.src:find("\\n(%s*)%S") |if leading_space then | local chunk1, chunk2 = vars.tree.src:match(":%s*([^\\n]*)(\\n.*)") | chunk2 = chunk2:gsub("\\n"..leading_space, "\\n") | return chunk1..chunk2.."\\n" |else | return vars.tree.src:match(":%s*(%S.*)").."\\n" |end macro [source code %body] =: compiler "repr" [compiler "call" ["source code from tree %", %body]] macro [parse tree %code] =: compiler "repr" [compiler "stringify_tree" [lua expr "vars.code.value"]]