From 505fec2a61d2571317cc4bbc36ec0f4822a63f9d Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Fri, 2 Feb 2018 15:48:28 -0800 Subject: Restructured the nomsu files to group all the essentials into core/ and all the optionals into lib/. lib/core.nom and tests/all.nom are no longer needed now. --- core/metaprogramming.nom | 200 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 core/metaprogramming.nom (limited to 'core/metaprogramming.nom') diff --git a/core/metaprogramming.nom b/core/metaprogramming.nom new file mode 100644 index 0000000..544ae59 --- /dev/null +++ b/core/metaprogramming.nom @@ -0,0 +1,200 @@ +#.. + This File contains actions for making actions and compile-time actions and some helper + functions to make that easier. + +# Compile-time action to make compile-time actions: +immediately: + lua> ".." + nomsu:define_compile_action("compile %actions to %lua", \(!! code location !!), function(\%actions, \%lua) + local signature = {}; + for i, action in ipairs(\%actions.value) do signature[i] = action:get_src(); end + local stubs = nomsu:get_stubs_from_signature(signature); + local stub_args = nomsu:get_args_from_signature(signature); + local arg_set = {}; + for i, arg in ipairs(stub_args[1]) do arg_set[arg] = true; end + if \%lua.type == "Text" then + error("Invalid type for 'compile % to %', expected a dict with expr/statements, but got text.", 0); + end + local body_lua = nomsu:tree_to_lua(\%lua); + local body_code = body_lua.statements or ("return "..body_lua.expr..";"); + local undeclared_locals = {}; + for i, body_local in ipairs(body_lua.locals or {}) do + if not arg_set[body_local] then + table.insert(undeclared_locals, body_local); + end + end + if #undeclared_locals > 0 then + body_code = "local "..table.concat(undeclared_locals, ", ")..";\\n"..body_code; + end + local lua_fn_args = table.concat(stub_args[1], ", "); + local def_tree = nomsu.compilestack[#nomsu.compilestack]; + local code_location = ("%s:%s,%s"):format(def_tree.filename, def_tree.start, def_tree.stop); + return {statements=([[ + nomsu:define_compile_action(]]..repr(signature)..[[, ]]..repr(code_location)..[[, function(]]..lua_fn_args..[[) + ]]..body_code.."\\n"..[[ + end); + ]])}; + end); + +# Compile-time action to make actions +immediately: + compile [action %actions %body] to: + lua> ".." + local signature = {}; + for i, action in ipairs(\%actions.value) do signature[i] = action:get_src(); end + local stubs = nomsu:get_stubs_from_signature(signature); + local stub_args = nomsu:get_args_from_signature(signature); + local arg_set = {}; + for i, arg in ipairs(stub_args[1]) do arg_set[arg] = true; end + local body_lua = nomsu:tree_to_lua(\%body); + local body_code = body_lua.statements or ("return "..body_lua.expr..";"); + local undeclared_locals = {}; + for i, body_local in ipairs(body_lua.locals or {}) do + if not arg_set[body_local] then + table.insert(undeclared_locals, body_local); + end + end + if #undeclared_locals > 0 then + body_code = "local "..table.concat(undeclared_locals, ", ")..";\\n"..body_code; + end + local lua_fn_args = table.concat(stub_args[1], ", "); + local def_tree = nomsu.compilestack[#nomsu.compilestack]; + local code_location = ("%s:%s,%s"):format(def_tree.filename, def_tree.start, def_tree.stop); + return {statements=[[ + nomsu:define_action(]]..repr(signature)..[[, ]]..repr(code_location)..[[, function(]]..lua_fn_args..[[) + ]]..body_code.."\\n"..[[ + end); + ]]}; + +# Macro to make nomsu macros: +immediately: + compile [parse %shorthand as %longhand] to: + lua> ".." + local signature = {}; + for i, action in ipairs(\%shorthand.value) do signature[i] = action:get_src(); end + local stubs = nomsu:get_stubs_from_signature(signature); + local stub_args = nomsu:get_args_from_signature(signature); + local lua_fn_args = table.concat(stub_args[1], ", "); + local template; + if \%longhand.type == "Block" then + local lines = {}; + for i, line in ipairs(\%longhand.value) do lines[i] = nomsu:dedent(line:get_src()); end + template = repr(table.concat(lines, "\\n")); + else + template = repr(nomsu:dedent(\%longhand:get_src())); + end + local replacements = {}; + for i, a in ipairs(stub_args[1]) do replacements[i] = a.."="..a; end + replacements = "{"..table.concat(replacements, ", ").."}"; + local def_tree = nomsu.compilestack[#nomsu.compilestack]; + local code_location = ("%s:%s,%s"):format(def_tree.filename, def_tree.start, def_tree.stop); + return {statements=[[ + nomsu:define_compile_action(]]..repr(signature)..[[, ]]..repr(code_location)..[[, function(]]..lua_fn_args..[[) + local template = nomsu:parse(]]..template..[[, ]]..repr(def_tree.filename)..[[); + local replacement = nomsu:tree_with_replaced_vars(template, ]]..replacements..[[); + return nomsu:tree_to_lua(replacement); + end); + ]]}; + +action [remove action %stub]: + lua> ".." + local fn = ACTION[\%stub]; + local metadata = ACTION_METADATA[fn]; + for i=#metadata.aliases,1,-1 do + metadata.arg_orders[metadata.aliases[i]] = nil; + table.remove(metadata.aliases, i); + end + ACTION[\%stub] = nil; + +immediately: + action [%tree as lua]: + =lua "nomsu:tree_to_lua(\%tree)" + + action [%tree as lua expr]: + lua> ".." + local lua = nomsu:tree_to_lua(\%tree); + if lua.locals or not lua.expr then + error("Invalid thing to convert to lua expr: "..\%tree:get_src()); + end + return lua.expr; + + action [%tree as lua statements]: + lua> ".." + local lua = nomsu:tree_to_lua(\%tree); + local code = lua.statements or (lua.expr..";"); + if lua.locals then + code = "local "..table.concat(lua.locals, ", ")..";\\n"..code; + end + return code; + + action [%tree as value]: + =lua "nomsu:tree_to_value(\%tree)" + +immediately: + compile [%tree's source code, %tree' source code] to {expr:"\(%tree as lua expr):get_src()"} + + compile [repr %obj] to {expr:"repr(\(%obj as lua expr))"} + compile [type of %obj] to {expr:"type(\(%obj as lua expr))"} + +immediately: + compile [nomsu] to {expr:"nomsu"} + compile [%var as lua identifier] to {expr:"nomsu:var_to_lua_identifier(\(%var as lua expr))"} + +action [action %names metadata]: + =lua "ACTION_METADATA[ACTION[\%names]]" + +# Get the source code for a function +action [help %action]: + lua> ".." + local metadata = \(action %action metadata); + if not metadata then + print("Action not found: "..repr(\%action)); + else + print(metadata.src or ""); + end + +# Compiler tools +immediately: + compile [run %code] to {expr: "nomsu:run(\(%code as lua expr), '\(!! code location !!)')"} + parse [enable debugging] as: lua> "nomsu.debug = true;" + parse [disable debugging] as: lua> "nomsu.debug = false;" + +immediately: + compile [say %message] to: + lua> ".." + if \%message.type == "Text" then + return {statements="print("..\(%message as lua expr)..");"}; + else + return {statements="print(stringify("..\(%message as lua expr).."));"}; + end + +# Return +immediately: + #.. Return statement is wrapped in a do..end block because Lua is unhappy if you + put code after a return statement, unless you wrap it in a block. + compile [return] to {statements:"do return; end"} + compile [return %return_value] to {statements:"do return \(%return_value as lua expr); end"} + +# Error functions +immediately: + compile [barf] to {statements:"error(nil, 0);"} + compile [barf %msg] to {statements:"error(\(%msg as lua expr), 0);"} + compile [assume %condition] to: + lua> "local \%assumption = 'Assumption failed: '..\%condition:get_src();" + return {..} + statements:".." + if not \(%condition as lua expr) then + error(\(repr %assumption), 0); + end + compile [assume %condition or barf %msg] to {..} + statements:".." + if not \(%condition as lua expr) then + error(\(%msg as lua expr), 0); + end + +# Literals +immediately: + compile [yes] to {expr:"true"} + compile [no] to {expr:"false"} + compile [nothing, nil, null] to {expr:"nil"} + -- cgit v1.2.3