nomsu/lib/metaprogramming.nom

153 lines
6.1 KiB
Plaintext

#..
This File contains rules for making rules and macros and some helper functions to make
that easier.
# Rule to make rules:
lua> ".."
nomsu:defmacro("rule %signature = %body", (function(nomsu, vars)
nomsu:assert(\%signature.type == "List",
"Invalid type for rule definition signature. Expected List, but got: "..tostring(\%signature.type));
nomsu:assert(\%body.type == "Thunk",
"Invalid type for rule definition body. Expected Thunk, but got: "..tostring(\%body.type));
local signature = {};
for i, alias in ipairs(\%signature.value) do
signature[i] = alias.src;
end
local src = nomsu:source_code(0);
return nil, ([[
nomsu:def(%s, %s, %s)
]]):format(nomsu:repr(signature), nomsu:tree_to_lua(\%body), nomsu:repr(nomsu:dedent(src)));
end), \(__src__ 1));
# Rule to make lua macros:
rule [compile \%macro_def to \%body] =:
lua> ".."
nomsu:assert(\%macro_def.type == "List",
"Invalid type for compile definition signature. Expected List, but got: "..tostring(\%macro_def.type));
nomsu:assert(\%body.type == "Thunk",
"Invalid type for compile definition body. Expected Thunk, but got: "..tostring(\%body.type));
local signature = {};
for i, alias in ipairs(\%macro_def.value) do
signature[i] = alias.src;
end
local thunk = nomsu:tree_to_value(\%body);
nomsu:defmacro(signature, thunk, ("compile %s\\n..to %s"):format(\%macro_def.src, \%body.src));
rule [compile \%macro_def to code \%body] =:
lua> ".."
nomsu:assert(\%macro_def.type == "List",
"Invalid type for compile definition signature. Expected List, but got: "..tostring(\%macro_def.type));
nomsu:assert(\%body.type == "Thunk",
"Invalid type for compile definition body. Expected Thunk, but got: "..tostring(\%body.type));
local signature = {};
for i, alias in ipairs(\%macro_def.value) do
signature[i] = alias.src;
end
local thunk = nomsu:tree_to_value(\%body);
local thunk_wrapper = function(...) return nil, thunk(...); end
nomsu:defmacro(signature, thunk_wrapper, ("compile %s\\n..to code %s"):format(\%macro_def.src, \%body.src));
# Rule to make nomsu macros:
lua> ".."
nomsu:defmacro("parse %shorthand as %longhand", (function(nomsu, vars)
nomsu:assert(\%shorthand.type == "List",
"Invalid type for parse definition signature. Expected List, but got: "..tostring(\%shorthand.type));
nomsu:assert(\%longhand.type == "Thunk",
"Invalid type for parse definition body. Expected Thunk, but got: "..tostring(\%longhand.type));
local signature = {};
for i, alias in ipairs(\%shorthand.value) do
signature[i] = alias.src;
end
local template = {};
for i, line in ipairs(\%longhand.value) do
template[i] = nomsu:dedent(line.src);
end
signature, template = nomsu:repr(signature), nomsu:repr(table.concat(template, "\\n"));
return nil, ([[
nomsu:defmacro(%s, (function(nomsu, vars)
local template = nomsu:parse(%s, %s);
if #template.value == 1 then template = template.value[1]; end
local replacement = nomsu:replaced_vars(template, vars);
return nomsu:tree_to_lua(replacement);
end), %s)]]):format(signature, template, nomsu:repr(\%shorthand.line_no), nomsu:repr(nomsu:source_code(0)));
end), \(__src__ 1));
rule [remove rule %stub] =:
lua> ".."
local def = nomsu.defs[\%stub];
for _, alias in ipairs(def.aliases) do
nomsu.defs[alias] = false;
end
rule [%tree as lua] =:
=lua "nomsu:tree_to_lua(\%tree)"
rule [%tree as value] =:
=lua "nomsu:tree_to_value(\%tree, vars)"
compile [repr %obj] to:
"nomsu:repr(\(%obj as lua))"
compile [indented %obj] to:
"nomsu:indent(\(%obj as lua))"
compile [dedented %obj] to:
"nomsu:dedent(\(%obj as lua))"
compile [type %obj, type of %obj] to:
"type(\(%obj as lua))"
parse [lua do> %block] as:
lua> "do"
lua> %block
lua> "end"
rule [%tree as lua statement] =:
lua do> ".."
local _,statement = nomsu:tree_to_lua(\%tree);
return statement;
rule [%tree as lua statements] =:
lua do> ".."
local lua_bits = {};
nomsu:assert(type(\%tree) == 'table' and \%tree.type == "Thunk",
"Invalid type. Expected Thunk.");
for _,bit in ipairs(\%tree.value) do
local expr, statement = nomsu:tree_to_lua(bit);
if statement then table.insert(lua_bits, statement); end
if expr then table.insert(lua_bits, "ret = "..expr..";"); end
end
return table.concat(lua_bits, "\\n");
compile [nomsu] to: "nomsu"
compile [nomsu's %key] to: "nomsu[\(%key as lua)]"
compile [nomsu %method %args] to: "nomsu[\(%method as lua)](nomsu, unpack(\(%args as lua)))"
compile [tree %tree with %replacements] to: ".."
nomsu:replaced_vars(\(%tree as lua), \(%replacements as lua))
parse [rule %signature] as:
(nomsu's "defs")->(nomsu "get_stub" [\%signature])
# Get the source code for a function
rule [help %rule] =:
lua do> ".."
local fn_def = nomsu.defs[nomsu:get_stub(\%rule)]
if not fn_def then
nomsu:writeln("Rule not found: "..nomsu:repr(\%rule));
else
nomsu:writeln(fn_def.src or "<unknown source code>");
end
# Compiler tools
parse [eval %code, run %code] as: nomsu "run" [%code]
rule [source code from tree %tree] =:
lua do> ".."
local _,_,leading_space = \%tree.src:find("\\n(%s*)%S");
if leading_space then
local chunk1, chunk2 = \%tree.src:match(":%s*([^\\n]*)(\\n.*)");
chunk2 = chunk2:gsub("\\n"..leading_space, "\\n");
return chunk1..chunk2.."\\n";
else
return \%tree.src:match(":%s*(%S.*)").."\\n";
end
parse [source code %body] as: source code from tree \%body
parse [parse tree %code] as: nomsu "tree_to_str" [\%code]
parse [enable debugging] as: lua> "nomsu.debug = true"
parse [disable debugging] as: lua> "nomsu.debug = false"