diff options
Diffstat (limited to 'syntax_tree.moon')
| -rw-r--r-- | syntax_tree.moon | 164 |
1 files changed, 89 insertions, 75 deletions
diff --git a/syntax_tree.moon b/syntax_tree.moon index 305a1ee..03596f8 100644 --- a/syntax_tree.moon +++ b/syntax_tree.moon @@ -1,87 +1,101 @@ -- 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) +as_lua = => + if type(@) == 'number' + return tostring(@) + if mt = getmetatable(@) + if _as_lua = mt.as_lua + return _as_lua(@) + error("Not supported: #{@}") -types = {"Number", "Var", "Block", "EscapedNomsu", "Text", "List", "Dict", "DictEntry", - "IndexChain", "Action", "FileChunks", "Error", "Comment"} -for name in *types - cls = {} - with cls - .__class = cls - .__index = cls - .__name = name - .type = name - .is_instance = (x)=> getmetatable(x) == @ - .__tostring = => "#{@type}#{repr @, (->)}" - .__repr = => "#{@type}#{repr @, (->)}" - .source_code_for_tree = setmetatable({}, {__index:(t)=> - s = t.source - Files = require 'files' - f = Files.read(s.filename) - return f - }) - .get_source_code = => @source_code_for_tree[@] - .map = (fn)=> - replacement = fn(@) - if replacement == false then return nil - if replacement - -- Clone the replacement, so we can give it a proper source/comments - if AST.is_syntax_tree(replacement) - replacement = setmetatable {k,v for k,v in pairs replacement}, getmetatable(replacement) - replacement.source = @source - replacement.comments = {unpack(@comments)} if @comments - if init = replacement.__init then init(replacement) - else - replacement = {source:@source, comments:@comments and {unpack(@comments)}} - changes = false - for k,v in pairs(@) - replacement[k] = v - if AST.is_syntax_tree(v) - r = v\map(fn) - continue if r == v or r == nil - changes = true - replacement[k] = r - return @ unless changes - replacement = setmetatable replacement, getmetatable(@) - if init = replacement.__init then init(replacement) - 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 false if @target != other.target - return true +--types = {"Number", "Var", "Block", "EscapedNomsu", "Text", "List", "Dict", "DictEntry", +-- "IndexChain", "Action", "FileChunks", "Error", "Comment"} +class SyntaxTree + @__type: "Syntax Tree" + + __tostring: => + bits = [tostring(b) for b in *@] + for k,v in pairs(@) + unless bits[k] + table.insert(bits, "[ #{tostring(k)}]=#{tostring(v)}") + return "SyntaxTree{#{table.concat(bits, ", ")}}" - AST[name] = setmetatable cls, - __tostring: => @__name - __call: (t)=> - if type(t.source) == 'string' - t.source = Source\from_string(t.source) - else - assert(Source\is_instance(t.source)) - setmetatable(t, @) - if init = t.__init then init(t) - return t + __eq: (other)=> + return false if type(@) != type(other) or #@ != #other or getmetatable(@) != getmetatable(other) + for i=1,#@ + return false if @[i] != other[i] + return false if @target != other.target + return true -AST.Action.__init = => - stub_bits = {} - arg_i = 1 - for a in *@ - if type(a) == 'string' - stub_bits[#stub_bits+1] = a + as_lua: => + bits = [as_lua(b) for b in *@] + for k,v in pairs(@) + unless bits[k] + table.insert(bits, "[ #{as_lua(k)}]=#{as_lua(v)}") + return "SyntaxTree{#{table.concat(bits, ", ")}}" + + @source_code_for_tree: setmetatable({}, {__index:(t)=> + s = t.source + Files = require 'files' + f = Files.read(s.filename) + return f + }) + get_source_code: => @@source_code_for_tree[@] + map: (fn)=> + replacement = fn(@) + if replacement == false then return nil + if replacement + -- Clone the replacement, so we can give it a proper source/comments + if SyntaxTree\is_instance(replacement) + replacement = {k,v for k,v in pairs replacement} + replacement.source = @source + replacement.comments = {unpack(@comments)} if @comments + replacement = SyntaxTree(replacement) else - stub_bits[#stub_bits+1] = tostring(arg_i) - arg_i += 1 - @stub = concat stub_bits, " " + replacement = {source:@source, comments:@comments and {unpack(@comments)}} + changes = false + for k,v in pairs(@) + replacement[k] = v + if SyntaxTree\is_instance(v) + r = v\map(fn) + continue if r == v or r == nil + changes = true + replacement[k] = r + return @ unless changes + replacement = SyntaxTree(replacement) + return replacement + + get_args: => + assert(@type == "Action", "Only actions have arguments") + return [tok for tok in *@ when type(tok) != 'string'] + + get_stub: => + stub_bits = {} + arg_i = 1 + for a in *@ + if type(a) == 'string' + stub_bits[#stub_bits+1] = a + else + stub_bits[#stub_bits+1] = arg_i + arg_i += 1 + while type(stub_bits[#stub_bits]) == 'number' + stub_bits[#stub_bits] = nil + return concat stub_bits, " " + + @is_instance: (t)=> + type(t) == 'table' and getmetatable(t) == @__base + -AST.Action.get_args = => - [tok for tok in *@ when type(tok) != 'string'] +getmetatable(SyntaxTree).__call = (t)=> + if type(t.source) == 'string' + t.source = Source\from_string(t.source) + setmetatable(t, @__base) + if t.type == 'Action' + t.stub = t\get_stub! + return t -return AST +return SyntaxTree |
