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

# Macros:

lua block ".."
    |local function make_fn(wrap_in_block)
    |    return function(nomsu, vars, kind)
    # Do a minimal amount of pre-processing (get the aliases and the source)
    |        local aliases = nomsu:repr(nomsu:get_aliases(vars.macro_def))
    |        local src = nomsu:repr(vars.user_macro.src)
    |        local user_macro = nomsu:tree_to_lua(vars.user_macro)
    # Then produce a block of code that creates the macro at runtime
    |        local lua = [[
    |nomsu:defmacro(%s, (function(nomsu, vars, kind)
    |    if kind == "Expression" then
    |        nomsu:error("Macro "..%s.." was defined to be a block, but is being used as an expression")
    |    end
    |    local user_macro = %s
    |    local lua = user_macro(nomsu, vars)
    |    %s
    |    return lua, true
    |end), %s)]]
    |        lua = lua:format(aliases, nomsu:repr(aliases), user_macro,
    |            wrap_in_block and [[lua = "do\\n    "..lua.."\\nend"]] or "", src)
    |        return lua, true
    |    end
    |end
    |nomsu:defmacro("macro statement %macro_def = %user_macro", make_fn(false), "see:lib/metaprogramming.nom")
    |nomsu:defmacro("macro block %macro_def = %user_macro", make_fn(true), "see:lib/metaprogramming.nom")

macro statement [macro %macro_def = %user_macro] =:
    ".."|nomsu:defmacro(
        |    \lua expr "nomsu:get_aliases(vars.macro_def)"\,
        |    \lua expr "nomsu:tree_to_lua(vars.user_macro)"\,
        |    \lua expr "nomsu:repr(vars.user_macro.src)"\)

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

# Macro that lets you make new rules
#..
    This is a macro instead of a rule because it needs to pre-empt processing the list of
    function calls and convert it into a list of strings (rather than call a function that
    is currently in the middle of being defined). Being a macro also allows us to snatch
    the source code and store that
macro statement [rule %rule_def = %body] =: ".."
    |nomsu:def(
    |    \nomsu "repr" [nomsu "get_aliases" [%rule_def]]\,
    |    \nomsu "tree_to_lua" [%body]\,
    |    \nomsu "repr" [lua expr "vars.body.src"]\)

rule [fart]=: lua block "print('poot')"

macro block [alias %aliases = %aliased] =:
    lua block ".."
        |local aliases = nomsu:get_aliases(vars.aliases)
        |aliases = nomsu:repr(aliases)
        |if vars.aliased.type ~= "Thunk" then
        |    nomsu:error("Right hand side of alias % = % should be a Thunk, but got "..vars.aliased.type
        |       ..". Maybe you used = instead of =: by mistake?")
        |end
        |local aliased = next(nomsu:get_aliases(vars.aliased.value))
        |aliased = nomsu:repr(aliased)
        |local lua = ([[
        |nomsu:add_aliases(%s, nomsu.defs[%s])
        |]]):format(aliases, aliased)
        |return lua

# 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.aliases))
        |        .." ="..(fn_def.src or ":\\n    <unknown source code>"))
        |end


# 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)"

rule [test, foobar] =: lua expr "print('yup')"

# 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

macro [source code %body] =:
    nomsu "repr" [nomsu "call" ["source code from tree %", %body]]

macro [parse tree %code] =:
    nomsu "repr" [nomsu "stringify_tree" [lua expr "vars.code.value"]]