aboutsummaryrefslogtreecommitdiff
path: root/core/metaprogramming.nom
diff options
context:
space:
mode:
authorBruce Hill <bitbucket@bruce-hill.com>2018-02-02 15:48:28 -0800
committerBruce Hill <bitbucket@bruce-hill.com>2018-02-02 15:49:42 -0800
commit505fec2a61d2571317cc4bbc36ec0f4822a63f9d (patch)
treec2b37e9db8e2f958fbca0caa0a9c4924912a37a9 /core/metaprogramming.nom
parent513c721198b2256235a95c98d161ab1bb51e6671 (diff)
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.
Diffstat (limited to 'core/metaprogramming.nom')
-rw-r--r--core/metaprogramming.nom200
1 files changed, 200 insertions, 0 deletions
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 "<unknown source code>");
+ 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"}
+