aboutsummaryrefslogtreecommitdiff
path: root/bootstrap.moon
blob: 66c8553b17e1665c4364ec081ee4ad5991bcd0a4 (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 a set of compile actions needed for bootstrapping
{:match, :sub, :gsub, :format, :byte, :find} = string
{:LuaCode, :Source} = require "code_obj"
SyntaxTree = require "syntax_tree"
Files = require "files"

-- TODO: de-duplicate this
pretty_error = require("pretty_errors")
compile_error = (source, err_msg, hint=nil)->
    local file
    if SyntaxTree\is_instance(source)
        file = source\get_source_file!
        source = source.source
    elseif type(source) == 'string'
        source = Source\from_string(source)
    if source and not file
        file = Files.read(source.filename)

    err_str = pretty_error{
        title: "Compile error"
        error:err_msg, hint:hint, source:file
        start:source.start, stop:source.stop, filename:source.filename
    }
    error(err_str, 0)

MAX_LINE = 80 -- For beautification purposes, try not to make lines much longer than this value
--ENVNAME = jit and "getfenv(1)" or "_ENV"
ENVNAME = jit and "_G" or "_ENV"
compile_actions = {
    [""]: (fn, ...)=>
        lua = LuaCode!
        fn_lua = @compile(fn)
        lua\add fn_lua
        unless fn_lua\text!\match("^%(.*%)$") or fn_lua\text!\match("^[_a-zA-Z][_a-zA-Z0-9.]*$")
            lua\parenthesize!
        lua\add "("
        for i=1,select('#',...)
            lua\add(", ") if i > 1
            lua\add @compile((select(i, ...)))
        lua\add ")"
        return lua

    ["Lua"]: (code)=>
        if not code
            return LuaCode("LuaCode()")
        if code.type != "Text"
            return LuaCode("LuaCode:from(", tostring(code.source)\as_lua!, ", ", @compile(code), ")")

        operate_on_text = (text)->
            lua = LuaCode\from(text.source, "LuaCode:from(", tostring(text.source)\as_lua!)
            for bit in *text
                local bit_lua
                if type(bit) == "string"
                    bit_lua = bit\as_lua!
                elseif bit.type == "Text"
                    bit_lua = operate_on_text(bit)
                elseif bit.type == "Block"
                    bit_lua = LuaCode\from bit.source, "(function()",
                        "\n    local _lua = LuaCode:from(", tostring(bit.source)\as_lua!, ")",
                        "\n    local function add(...) _lua:add(...) end",
                        "\n    local function join_with(glue)",
                        "\n        local old_bits = _lua.bits",
                        "\n        _lua = LuaCode:from(_lua.source)",
                        "\n        _lua:concat_add(old_bits, glue)",
                        "\n    end",
                        "\n    ", @compile(bit),
                        "\n    return _lua",
                        "\nend)()"
                else
                    bit_lua = @compile(bit)

                bit_leading_len = #(bit_lua\match("^[^\n]*"))
                lua\add(lua\trailing_line_len! + bit_leading_len > MAX_LINE and ",\n    " or ", ")
                lua\add(bit_lua)
            lua\add ")"
            return lua

        return operate_on_text code

    ["lua >"]: (code)=>
        if code.type != "Text"
            return code
        operate_on_text = (text)->
            lua = LuaCode\from(text.source)
            for bit in *text
                if type(bit) == "string"
                    lua\add bit
                elseif bit.type == "Text"
                    lua\add(operate_on_text(bit))
                else
                    lua\add @compile(bit)
            return lua
        return operate_on_text code

    ["= lua"]: (code)=>
        @compile(SyntaxTree{type:"Action", "lua", ">", code})

    ["1 as lua"]: (code)=>
        LuaCode("#{ENVNAME}:compile(", @compile(code), ")")

    ["use"]: (path)=>
        LuaCode("#{ENVNAME}:use(#{@compile(path)})")

    ["export"]: (path)=>
        LuaCode("#{ENVNAME}:export(#{@compile(path)})")

    ["run"]: (path)=>
        LuaCode("#{ENVNAME}:run(#{@compile(path)})")

    ["test"]: (body)=>
        unless body.type == 'Block'
            compile_error(body, "This should be a Block")
        test_nomsu = body\get_source_code!\match(":[ ]*(.*)")
        if indent = test_nomsu\match("\n([ ]*)")
            test_nomsu = test_nomsu\gsub("\n"..indent, "\n")
        test_text = @compile(SyntaxTree{type:"Text", source:body.source, test_nomsu})
        return LuaCode "TESTS[#{tostring(body.source)\as_lua!}] = ", test_text

    ["is jit"]: (code)=> LuaCode("jit")
    ["Lua version"]: (code)=> LuaCode("_VERSION")
    ["nomsu environment"]: ()=> LuaCode(ENVNAME)
    ["nomsu environment name"]: ()=> LuaCode(ENVNAME\as_lua!)
}

return compile_actions