aboutsummaryrefslogtreecommitdiff
path: root/lib/metaprogramming.nom
blob: 65a625d59877b459e5d9ae413aa13a3bb1cfb77b (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(body.src)), nil;
    |end), "<source can be found in lib/metaprogramming.nom>");

# 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, template.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, 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, 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"