aboutsummaryrefslogtreecommitdiff
path: root/nomsu_tree.moon
blob: 885892f8799b062f693cb47c523eee12611402cf (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
-- This file contains the datastructures used to represent parsed Nomsu syntax trees,
-- as well as the logic for converting them to Lua code.
{:repr} = require 'utils'
{:insert, :remove, :concat} = table
{:Source} = require "code_obj"
unpack or= table.unpack

AST = {}
AST.is_syntax_tree = (n, t=nil)->
    type(n) == 'table' and getmetatable(n) and AST[n.type] == getmetatable(n) and (t == nil or n.type == t)

types = {"Number", "Var", "Block", "EscapedNomsu", "Text", "List", "Dict", "DictEntry",
    "IndexChain", "Action", "FileChunks"}
for name in *types
    cls = {}
    with cls
        .__class = cls
        .__index = cls
        .__name = name
        .type = name
        .is_instance = (x)=> getmetatable(x) == @
        .__tostring = => "#{@type}(#{repr tostring(@source)}, #{concat([repr(v) for v in *@], ', ')})"
        .map = (fn)=>
            replacement = fn(@)
            if replacement == false then return nil
            if replacement
                -- Clone the replacement, but give it a proper source
                replacement = (replacement.__class)(@source, unpack(replacement))
            else
                replacements = {}
                changes = false
                for i,v in ipairs(@)
                    replacements[#replacements+1] = v
                    if AST.is_syntax_tree(v)
                        r = v\map(fn)
                        continue if r == v or r == nil
                        changes = true
                        replacements[#replacements] = r
                return @ unless changes
                replacement = (@__class)(@source, unpack(replacements))
            replacement.comments = [c for c in *@comments] if @comments
            return replacement
        .__eq = (other)=>
            return false if type(@) != type(other) or #@ != #other or getmetatable(@) != getmetatable(other)
            for i=1,#@
                return false if @[i] != other[i]
            return true

    AST[name] = setmetatable cls,
        __tostring: => @name
        __call: (source, ...)=>
            if type(source) == 'string'
                source = Source\from_string(source)
            for i=1,select('#', ...) do assert(select(i,...))
            assert(Source\is_instance(source))
            inst = {:source, ...}
            setmetatable(inst, @)
            if inst.__init then inst\__init!
            return inst

AST.Action.__init = =>
    stub_bits = [type(a) == 'string' and a or '%' for a in *@]
    @stub = concat stub_bits, " "

AST.Action.get_args = =>
    [tok for tok in *@ when type(tok) != 'string']

return AST