aboutsummaryrefslogtreecommitdiff
path: root/lib/metaprogramming.nom
blob: ac4557489c0829ec454b13eb838a99a6a009a618 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#..
    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)
    |    local signature = nomsu:get_stubs(nomsu:typecheck(vars, "signature", "List").value);
    |    local body = nomsu:typecheck(vars, "body", "Thunk");
    |    return ([[
    |nomsu:def(%s, %s, %s)
    |]]):format(nomsu:repr(signature), nomsu:tree_to_lua(body), nomsu:repr(("rule %s\\n..=%s"):format(vars.signature.src, vars.body.src))), nil;
    |end));

# Rule to make nomsu macros:
rule [escaped parse %shorthand as %longhand] =:
    lua> ".."
        |local aliases = nomsu:get_stubs(nomsu:typecheck(vars, "shorthand", "List").value);
        |local template = nomsu:typecheck(vars, "longhand", "Thunk").value;
        |local function parsing_as(nomsu, vars)
        # Single expression/statement
        |    if #template == 1 then
        |        local replacement = nomsu:replaced_vars(template[1], vars);
        |        return nomsu:tree_to_lua(replacement);
        |    end
        # Multiple statements
        |   local lua_bits = {};
        |   for _,bit in ipairs(template) do
        |       bit = nomsu:replaced_vars(bit, vars);
        |       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 nil, table.concat(lua_bits, "\\n");
        |end
        |nomsu:defmacro(aliases, parsing_as, ("parse %s\\n..as %s"):format(vars.shorthand.src, vars.longhand.src));
escaped parse \[parse %shorthand as %longhand] as \: escaped parse \%shorthand as \%longhand

# Rule to make lua macros:
rule [escaped compile %macro_def to %body] =:
    lua> ".."
        |local aliases = nomsu:get_stubs(nomsu:typecheck(vars, "macro_def", "List").value);
        |local body = nomsu:typecheck(vars, "body", "Thunk");
        |local thunk = nomsu:tree_to_value(body);
        |nomsu:defmacro(aliases, thunk, ("compile %s\\n..to %s"):format(vars.macro_def.src, body.src));
rule [escaped compile %macro_def to code %body] =:
    lua> ".."
        |local aliases = nomsu:get_stubs(nomsu:typecheck(vars, "macro_def", "List").value);
        |local body = nomsu:typecheck(vars, "body", "Thunk");
        |local thunk = nomsu:tree_to_value(body);
        |local thunk_wrapper = function(nomsu, vars) return nil, thunk(nomsu, vars); end
        |nomsu:defmacro(aliases, thunk_wrapper, ("compile %s\\n..to code %s"):format(vars.macro_def.src, body.src));
parse [compile %macro_def to %body] as: escaped compile \%macro_def to \%body
parse [compile %macro_def to code %body] as: escaped compile \%macro_def to code \%body

rule [do %] =:
    =lua "\(%)(nomsu, vars)"

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 [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 = {};
        |local statements = nomsu:typecheck(vars, "tree", "Thunk").value;
        |for _,bit in ipairs(statements) 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)))"

# Get the source code for a function
rule [help %rule] =:
    lua do> ".."
        |local fn_def = nomsu.defs[nomsu:get_stub(vars.rule)]
        |if not fn_def then
        |    nomsu:writeln("Rule not found: "..nomsu:repr(vars.rule));
        |else
        |    local template = fn_def.is_macro and "compile %s to%s" or "rule %s =%s";
        |    local src = fn_def.src or ":\\n    <unknown source code>";
        |    if src:sub(1,1) ~= ":" and fn_def.is_macro then template = "parse %s as: %s"; end
        |    nomsu:writeln(template:format(nomsu:repr(fn_def.stub), src));
        |end

# Compiler tools
parse [eval %code, run %code] as: nomsu "run" [%code]
rule [source code from tree %tree] =:
    lua do> ".."
        |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
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"