aboutsummaryrefslogtreecommitdiff
path: root/lib/metaprogramming.nom
blob: e60a235c7c7a671a4c08a30699214debf07a5312 (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
#..
    This File contains rules for making rules and macros and some helper functions to make
    that easier.

# Nil
lua code ".."
    |nomsu:defmacro("nil", function(nomsu, vars) return "nil", nil end)

# Macros:
lua code ".."
    |local function parse_as(nomsu, vars)
    |    if vars.shorthand.type ~= "Block" then
    |        nomsu:error("Expected shorthand to be Block, but got "..vars.shorthand.type)
    |    end
    |    if vars.longhand.type ~= "Block" then
    |        nomsu:error("Expected longhand to be Block, but got "..vars.longhand.type)
    |    end
    |    local template = vars.longhand
    |    local function parsing_as(nomsu, vars)
    |        local expanded = nomsu:replaced_vars(template, vars)
    |        local expr,statement = nomsu:tree_to_lua(expanded)
    |        return expr, statement
    |    end
    |    for _,call in ipairs(vars.shorthand.value) do
    |        nomsu:defmacro(call, parsing_as)
    |    end
    |end
    |nomsu:def("parse nomsu %shorthand as nomsu %longhand", parse_as)
parse nomsu \(parse %shorthand as %longhand) as nomsu \(parse nomsu \%shorthand as nomsu \%longhand)
parse (foo %x) as (baz %x)

lua code ".."
    |nomsu:defmacro("lua expr %code", function(nomsu, vars)
    |    return nomsu:tree_to_value(vars.code, vars), nil
    |end)


# Rule that lets you make new rules
lua code ".."
    |nomsu:defmacro("rule %rule_def = %body", function(nomsu, vars)
    |    if vars.rule_def.type ~= "Block" then
    |        nomsu:error("Wrong type for rule definition, expected Block, but got "..vars.rule_def.type)
    |    end
    |    local thunk = nomsu:tree_to_lua({type="Thunk", value=vars.body, src=vars.body.src})
    |    local fn_name = "fn_"..nomsu:var_to_lua_identifier(nomsu:get_stub(vars.rule_def.value[1]))
    |    local lua = ([[
    |do
    |    local %s = %s
    |    for _,alias in ipairs(%s) do
    |        nomsu:def(alias, %s)
    |    end
    |end]]):format(fn_name, thunk, nomsu:repr(vars.rule_def.value), fn_name)
    |    return nil, lua
    |end)
parse (rule %rule_name) as: lua expr "nomsu.defs[(nomsu:get_stub(\(%rule_name).value[1]))]"


# Macro helper functions
rule (%tree as value) =:
    lua expr "nomsu:tree_to_value(vars.tree, vars)"

rule (%tree as lua) =:
    lua expr "nomsu:tree_to_lua(vars.tree)"


parse (lua block %block) as:
    lua code ".."
        |do
        |    \(%block)
        |end

parse (nomsu) as: lua expr "nomsu"
parse (nomsu's %key) as:
    lua expr "nomsu[\(%key as lua)]"
parse (nomsu %method %args) as:
    lua block ".."
        |local args = {"nomsu"}
        |for _,arg in ipairs(vars.args.value) do
        |    table.insert(args, nomsu:tree_to_lua(arg))
        |end
        |local method_name = nomsu:repr(nomsu:tree_to_value(vars.method, vars))
    ..with value ".."
        |("nomsu[%s](%s)"):format(method_name, table.concat(args, ", "))
parse (repr %) as: nomsu "repr" [%]

# Get the source code for a function
rule (help %rule) =:
    lua block ".."
        |local fn_def = nomsu:get_fn_def(vars.rule)
        |if not fn_def then
        |    nomsu:writeln("Rule not found: "..nomsu:repr(vars.rule))
        |else
        |    nomsu:writeln("rule "..nomsu:repr(nomsu.utils.keys(fn_def.invocation))
        |        .." ="..(fn_def.src or ":\\n    <unknown source code>"))
        |end

# Compiler tools
rule (eval %code; run %code) =: nomsu "run" [%code]
rule (source code from tree %tree) =:
    lua block ".."
        |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: repr (nomsu "tree_to_str" [\%code])