#.. 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 = ACTIONS[\%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 ACTIONS[\%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[ACTIONS[\%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 [show lua %block] to: lua> ".." local \%lua = nomsu:tree_to_lua(\%block); return {statements = "print("..repr(\%lua.statements or \%lua.expr)..");"}; 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"}