Simplified AST to just use a single moonscript class called "SyntaxTree"

instead of a different metatable for each type of syntax tree.
This commit is contained in:
Bruce Hill 2018-10-31 15:54:18 -07:00
parent ec92b0fccd
commit d0c3c57f7b
6 changed files with 215 additions and 263 deletions

View File

@ -22,10 +22,10 @@ lua> "\
lua> "\
..COMPILE_ACTIONS["1 -> 2"] = function(nomsu, tree, \%args, \%body)
local lua = LuaCode.Value(tree.source, "(function(")
if AST.is_syntax_tree(\%args, "Action") then \%args = \%args:get_args() end
local lua_args = table.map(\%args, function(a) return AST.is_syntax_tree(a) and nomsu:compile(a):as_smext() or a end)
if SyntaxTree:is_instance(\%args) and \%args.type == "Action" then \%args = \%args:get_args() end
local lua_args = table.map(\%args, function(a) return SyntaxTree:is_instance(a) and nomsu:compile(a):as_smext() or a end)
lua:concat_append(lua_args, ", ")
local body_lua = AST.is_syntax_tree(\%body) and nomsu:compile(\%body):as_statements("return ") or \%body
local body_lua = SyntaxTree:is_instance(\%body) and nomsu:compile(\%body):as_statements("return ") or \%body
body_lua:remove_free_vars(lua_args)
body_lua:declare_locals()
lua:append(")\\n ", body_lua, "\\nend)")
@ -177,13 +177,13 @@ test:
replacements[arg[1]] = nomsu:compile(arg):as_smext()
end
local function make_tree(t)
if AST.is_syntax_tree(t, "Var") then
if SyntaxTree:is_instance(t) and t.type == "Var" then
if replacements[t[1]] then
return replacements[t[1]]
else
return t.type.."{mangle("..t[1]:as_lua().."), source="..tostring(t.source):as_lua().."}"
return "SyntaxTree{mangle("..t[1]:as_lua().."), type="..t.type:as_lua()..", source="..tostring(t.source):as_lua().."}"
end
elseif AST.is_syntax_tree(t) then
elseif SyntaxTree:is_instance(t) then
local ret = {}
local i = 1
for k, v in pairs(t) do
@ -198,7 +198,7 @@ test:
ret[#ret+1] = "["..make_tree(k).."]= "..make_tree(v)
end
end
return t.type.."{"..table.concat(ret, ", ").."}"
return "SyntaxTree{"..table.concat(ret, ", ").."}"
elseif lua_type_of_1(t) == 'number' then
return tostring(t)
else
@ -241,8 +241,8 @@ test:
externally [%var as lua identifier, %var as lua id] all mean:
lua> "\
..if lua_type_of_1(\%var) == 'string' then return \%var:as_lua_id()
elseif AST.is_syntax_tree(\%var, 'Var') then return \%var[1]:as_lua_id()
elseif AST.is_syntax_tree(\%var) then
elseif SyntaxTree:is_instance(\%var, 'Var') then return \%var[1]:as_lua_id()
elseif SyntaxTree:is_instance(\%var) then
local lua = \(%var as lua expr)
if not lua:as_smext():match("^[_a-zA-Z][_a-zA-Z0-9]*$") then
nomsu:compile_error(\%var, "This is not a valid Lua identifier.")
@ -253,9 +253,9 @@ externally [%var as lua identifier, %var as lua id] all mean:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(% is syntax tree) compiles to (Lua value "AST.is_syntax_tree(\(% as lua expr))")
(% is syntax tree) compiles to (Lua value "SyntaxTree:is_instance(\(% as lua expr))")
(% is %kind syntax tree) compiles to (..)
Lua value "AST.is_syntax_tree(\(% as lua expr), \(%kind as lua expr))"
Lua value "SyntaxTree:is_instance(\(% as lua expr), \(%kind as lua expr))"
(%tree with %t -> %replacement) compiles to (..)
Lua value "\
@ -295,7 +295,7 @@ externally (match %tree with %patt) means:
if #\%patt ~= #\%tree then return nil end
local matches = _Dict{}
for \%i=1,#\%patt do
if AST.is_syntax_tree(\%tree[\%i]) then
if SyntaxTree:is_instance(\%tree[\%i]) then
local submatch = \(match %tree.%i with %patt.%i)
if not submatch then return nil end
for k,v in pairs(submatch) do

View File

@ -53,7 +53,7 @@ test:
for i, item in ipairs(\%assignments) do
local \%target, \%value = item[1], item[2]
\%value = \%value:map(function(t)
if Action:is_instance(t) and t.stub == "?" then
if SyntaxTree:is_instance(t) and t.type == "Action" and t.stub == "?" then
return \%target
end
end)

View File

@ -35,7 +35,7 @@ do
local _obj_0 = require("code_obj")
NomsuCode, LuaCode, Source = _obj_0.NomsuCode, _obj_0.LuaCode, _obj_0.Source
end
local AST = require("syntax_tree")
local SyntaxTree = require("syntax_tree")
local make_parser = require("parser")
local pretty_error = require("pretty_errors")
SOURCE_MAP = { }
@ -97,16 +97,14 @@ escape = function(s)
end
local make_tree
make_tree = function(tree, userdata)
local cls = AST[tree.type]
tree.source = Source(userdata.filename, tree.start, tree.stop)
tree.start, tree.stop = nil, nil
tree.type = nil
do
local _accum_0 = { }
local _len_0 = 1
for _index_0 = 1, #tree do
local t = tree[_index_0]
if AST.is_syntax_tree(t, "Comment") then
if SyntaxTree:is_instance(t) and t.type == "Comment" then
_accum_0[_len_0] = t
_len_0 = _len_0 + 1
end
@ -117,15 +115,11 @@ make_tree = function(tree, userdata)
tree.comments = nil
end
for i = #tree, 1, -1 do
if AST.is_syntax_tree(tree[i], "Comment") then
if SyntaxTree:is_instance(tree[i]) and tree[i].type == "Comment" then
table.remove(tree, i)
end
end
tree = setmetatable(tree, cls)
cls.source_code_for_tree[tree] = userdata.source
if tree.__init then
tree:__init()
end
tree = SyntaxTree(tree)
return tree
end
local Parsers = { }
@ -200,7 +194,7 @@ do
lpeg = lpeg,
re = re,
Files = Files,
AST = AST,
SyntaxTree = SyntaxTree,
TESTS = Dict({ }),
globals = Dict({ }),
LuaCode = LuaCode,
@ -245,9 +239,6 @@ do
if jit or _VERSION == "Lua 5.2" then
NomsuCompiler.environment.bit = require("bitops")
end
for k, v in pairs(AST) do
NomsuCompiler.environment[k] = v
end
NomsuCompiler.fork = function(self)
local f = setmetatable({ }, {
__index = self
@ -288,7 +279,7 @@ do
for k, v in pairs(t) do
local _continue_0 = false
repeat
if not (AST.is_syntax_tree(v)) then
if not (SyntaxTree:is_instance(v)) then
_continue_0 = true
break
end
@ -540,7 +531,7 @@ do
Files.spoof(source.filename, to_run)
end
local tree
if AST.is_syntax_tree(to_run) then
if SyntaxTree:is_instance(to_run) then
tree = to_run
else
tree = self:parse(to_run, source)
@ -714,7 +705,7 @@ do
local filename = Source:from_string(info.source).filename
self:compile_error(tree, "The compile-time action here (" .. tostring(stub) .. ") failed to return any value.", "Look at the implementation of (" .. tostring(stub) .. ") in " .. tostring(filename) .. ":" .. tostring(info.linedefined) .. " and make sure it's returning something.")
end
if AST.is_syntax_tree(ret) then
if SyntaxTree:is_instance(ret) then
if ret == tree then
local info = debug.getinfo(compile_action, "S")
local filename = Source:from_string(info.source).filename
@ -763,19 +754,19 @@ do
lua:append(")")
return lua
elseif "EscapedNomsu" == _exp_0 then
local lua = LuaCode.Value(tree.source, tree[1].type, "{")
local lua = LuaCode.Value(tree.source, "SyntaxTree{")
local needs_comma, i = false, 1
local as_lua
as_lua = function(x)
if type(x) == 'number' then
return tostring(x)
elseif AST.is_syntax_tree(x) then
elseif SyntaxTree:is_instance(x) then
return self:compile(x, compile_actions)
else
return x:as_lua()
end
end
for k, v in pairs(AST.is_syntax_tree(tree[1], "EscapedNomsu") and tree or tree[1]) do
for k, v in pairs((SyntaxTree:is_instance(tree[1]) and tree[1].type == "EscapedNomsu" and tree) or tree[1]) do
if needs_comma then
lua:append(", ")
else
@ -1175,7 +1166,7 @@ do
local _list_0 = t
for _index_0 = 1, #_list_0 do
local x = _list_0[_index_0]
if AST.is_syntax_tree(x) then
if SyntaxTree:is_instance(x) then
find_comments(x)
end
end

View File

@ -23,7 +23,7 @@ colored = setmetatable({}, {__index:(_,color)-> ((msg)-> colors[color]..tostring
unpack or= table.unpack
{:match, :sub, :gsub, :format, :byte, :find} = string
{:NomsuCode, :LuaCode, :Source} = require "code_obj"
AST = require "syntax_tree"
SyntaxTree = require "syntax_tree"
make_parser = require("parser")
pretty_error = require("pretty_errors")
-- Mapping from source string (e.g. "@core/metaprogramming.nom[1:100]") to a mapping
@ -62,18 +62,14 @@ escape = (s)->
-- Re-implement nomsu-to-lua comment translation?
make_tree = (tree, userdata)->
cls = AST[tree.type]
tree.source = Source(userdata.filename, tree.start, tree.stop)
tree.start, tree.stop = nil, nil
tree.type = nil
tree.comments = [t for t in *tree when AST.is_syntax_tree(t, "Comment")]
tree.comments = [t for t in *tree when SyntaxTree\is_instance(t) and t.type == "Comment"]
if #tree.comments == 0 then tree.comments = nil
for i=#tree,1,-1
if AST.is_syntax_tree(tree[i], "Comment")
if SyntaxTree\is_instance(tree[i]) and tree[i].type == "Comment"
table.remove(tree, i)
tree = setmetatable(tree, cls)
cls.source_code_for_tree[tree] = userdata.source
if tree.__init then tree\__init!
tree = SyntaxTree(tree)
return tree
Parsers = {}
@ -108,7 +104,7 @@ with NomsuCompiler
_List:List, _Dict:Dict,
-- Utilities and misc.
stringify:stringify, utils:utils, lpeg:lpeg, re:re, Files:Files,
:AST, TESTS: Dict({}), globals: Dict({}),
:SyntaxTree, TESTS: Dict({}), globals: Dict({}),
:LuaCode, :NomsuCode, :Source
nomsu:NomsuCompiler
__imported: Dict{}
@ -130,7 +126,6 @@ with NomsuCompiler
return ipairs(x)
if jit or _VERSION == "Lua 5.2"
.environment.bit = require("bitops")
for k,v in pairs(AST) do .environment[k] = v
.fork = =>
f = setmetatable({}, {__index:@})
@ -160,7 +155,7 @@ with NomsuCompiler
coroutine.yield t
else
for k,v in pairs(t)
continue unless AST.is_syntax_tree(v)
continue unless SyntaxTree\is_instance(v)
find_errors(v)
errs = [err for err in coroutine.wrap(-> find_errors(tree))]
@ -303,7 +298,7 @@ with NomsuCompiler
source = to_run.source or Source(to_run, 1, #to_run)
if type(source) == 'string' then source = Source\from_string(source)
if not Files.read(source.filename) then Files.spoof(source.filename, to_run)
tree = if AST.is_syntax_tree(to_run) then to_run else @parse(to_run, source)
tree = if SyntaxTree\is_instance(to_run) then to_run else @parse(to_run, source)
if tree == nil -- Happens if pattern matches, but there are no captures, e.g. an empty string
return nil
if tree.type != "FileChunks"
@ -416,7 +411,7 @@ with NomsuCompiler
@compile_error tree,
"The compile-time action here (#{stub}) failed to return any value.",
"Look at the implementation of (#{stub}) in #{filename}:#{info.linedefined} and make sure it's returning something."
if AST.is_syntax_tree(ret)
if SyntaxTree\is_instance(ret)
if ret == tree
info = debug.getinfo(compile_action, "S")
filename = Source\from_string(info.source).filename
@ -457,16 +452,16 @@ with NomsuCompiler
return lua
when "EscapedNomsu"
lua = LuaCode.Value tree.source, tree[1].type, "{"
lua = LuaCode.Value tree.source, "SyntaxTree{"
needs_comma, i = false, 1
as_lua = (x)->
if type(x) == 'number'
tostring(x)
elseif AST.is_syntax_tree(x)
elseif SyntaxTree\is_instance(x)
@compile(x, compile_actions)
else x\as_lua!
for k,v in pairs(AST.is_syntax_tree(tree[1], "EscapedNomsu") and tree or tree[1])
for k,v in pairs((SyntaxTree\is_instance(tree[1]) and tree[1].type == "EscapedNomsu" and tree) or tree[1])
if needs_comma then lua\append ", "
else needs_comma = true
if k == i
@ -743,7 +738,7 @@ with NomsuCompiler
find_comments = (t)->
if t.comments and t.source.filename == tree.source.filename
comment_set[c] = true for c in *t.comments
find_comments(x) for x in *t when AST.is_syntax_tree x
find_comments(x) for x in *t when SyntaxTree\is_instance x
find_comments(tree)
-- Sort in reversed order so they can be easily popped
comments = [c for c in pairs comment_set]

View File

@ -6,13 +6,6 @@ end
local Source
Source = require("code_obj").Source
local unpack = unpack or table.unpack
local AST = { }
AST.is_syntax_tree = function(n, t)
if t == nil then
t = nil
end
return type(n) == 'table' and getmetatable(n) and getmetatable(n).__type == "Syntax Tree" and (t == nil or n.type == t)
end
local as_lua
as_lua = function(self)
if type(self) == 'number' then
@ -31,40 +24,17 @@ as_lua = function(self)
end
return error("Not supported: " .. tostring(self))
end
local types = {
"Number",
"Var",
"Block",
"EscapedNomsu",
"Text",
"List",
"Dict",
"DictEntry",
"IndexChain",
"Action",
"FileChunks",
"Error",
"Comment"
}
for _index_0 = 1, #types do
local name = types[_index_0]
local cls = { }
do
cls.__class = cls
cls.__index = cls
cls.__name = name
cls.type = name
cls.__type = "Syntax Tree"
cls.is_instance = function(self, x)
return getmetatable(x) == self
end
cls.__tostring = function(self)
local SyntaxTree
do
local _class_0
local _base_0 = {
__tostring = function(self)
local bits
do
local _accum_0 = { }
local _len_0 = 1
for _index_1 = 1, #self do
local b = self[_index_1]
for _index_0 = 1, #self do
local b = self[_index_0]
_accum_0[_len_0] = tostring(b)
_len_0 = _len_0 + 1
end
@ -75,15 +45,29 @@ for _index_0 = 1, #types do
table.insert(bits, "[ " .. tostring(tostring(k)) .. "]=" .. tostring(tostring(v)))
end
end
return tostring(self.type) .. "{" .. tostring(table.concat(bits, ", ")) .. "}"
end
cls.as_lua = function(self)
return "SyntaxTree{" .. tostring(table.concat(bits, ", ")) .. "}"
end,
__eq = function(self, other)
if type(self) ~= type(other) or #self ~= #other or getmetatable(self) ~= getmetatable(other) then
return false
end
for i = 1, #self do
if self[i] ~= other[i] then
return false
end
end
if self.target ~= other.target then
return false
end
return true
end,
as_lua = function(self)
local bits
do
local _accum_0 = { }
local _len_0 = 1
for _index_1 = 1, #self do
local b = self[_index_1]
for _index_0 = 1, #self do
local b = self[_index_0]
_accum_0[_len_0] = as_lua(b)
_len_0 = _len_0 + 1
end
@ -94,45 +78,32 @@ for _index_0 = 1, #types do
table.insert(bits, "[ " .. tostring(as_lua(k)) .. "]=" .. tostring(as_lua(v)))
end
end
return tostring(self.type) .. "{" .. tostring(table.concat(bits, ", ")) .. "}"
end
cls.source_code_for_tree = setmetatable({ }, {
__index = function(self, t)
local s = t.source
local Files = require('files')
local f = Files.read(s.filename)
return f
end
})
cls.get_source_code = function(self)
return self.source_code_for_tree[self]
end
cls.map = function(self, fn)
return "SyntaxTree{" .. tostring(table.concat(bits, ", ")) .. "}"
end,
get_source_code = function(self)
return self.__class.source_code_for_tree[self]
end,
map = function(self, fn)
local replacement = fn(self)
if replacement == false then
return nil
end
if replacement then
if AST.is_syntax_tree(replacement) then
replacement = setmetatable((function()
if SyntaxTree:is_instance(replacement) then
do
local _tbl_0 = { }
for k, v in pairs(replacement) do
_tbl_0[k] = v
end
return _tbl_0
end)(), getmetatable(replacement))
replacement = _tbl_0
end
replacement.source = self.source
if self.comments then
replacement.comments = {
unpack(self.comments)
}
end
do
local init = replacement.__init
if init then
init(replacement)
end
end
replacement = SyntaxTree(replacement)
end
else
replacement = {
@ -146,7 +117,7 @@ for _index_0 = 1, #types do
local _continue_0 = false
repeat
replacement[k] = v
if AST.is_syntax_tree(v) then
if SyntaxTree:is_instance(v) then
local r = v:map(fn)
if r == v or r == nil then
_continue_0 = true
@ -164,74 +135,75 @@ for _index_0 = 1, #types do
if not (changes) then
return self
end
replacement = setmetatable(replacement, getmetatable(self))
do
local init = replacement.__init
if init then
init(replacement)
end
end
replacement = SyntaxTree(replacement)
end
return replacement
end
cls.__eq = function(self, other)
if type(self) ~= type(other) or #self ~= #other or getmetatable(self) ~= getmetatable(other) then
return false
end
for i = 1, #self do
if self[i] ~= other[i] then
return false
end
end
if self.target ~= other.target then
return false
end
return true
end
end
AST[name] = setmetatable(cls, {
__tostring = function(self)
return self.__name
end,
__call = function(self, t)
if type(t.source) == 'string' then
t.source = Source:from_string(t.source)
end
setmetatable(t, self)
do
local init = t.__init
if init then
init(t)
get_args = function(self)
assert(self.type == "Action", "Only actions have arguments")
local _accum_0 = { }
local _len_0 = 1
for _index_0 = 1, #self do
local tok = self[_index_0]
if type(tok) ~= 'string' then
_accum_0[_len_0] = tok
_len_0 = _len_0 + 1
end
end
return t
return _accum_0
end,
get_stub = function(self)
local stub_bits = { }
local arg_i = 1
for _index_0 = 1, #self do
local a = self[_index_0]
if type(a) == 'string' then
stub_bits[#stub_bits + 1] = a
else
stub_bits[#stub_bits + 1] = tostring(arg_i)
arg_i = arg_i + 1
end
end
return concat(stub_bits, " ")
end
}
_base_0.__index = _base_0
_class_0 = setmetatable({
__init = function() end,
__base = _base_0,
__name = "SyntaxTree"
}, {
__index = _base_0,
__call = function(cls, ...)
local _self_0 = setmetatable({}, _base_0)
cls.__init(_self_0, ...)
return _self_0
end
})
end
AST.Action.__init = function(self)
local stub_bits = { }
local arg_i = 1
for _index_0 = 1, #self do
local a = self[_index_0]
if type(a) == 'string' then
stub_bits[#stub_bits + 1] = a
else
stub_bits[#stub_bits + 1] = tostring(arg_i)
arg_i = arg_i + 1
_base_0.__class = _class_0
local self = _class_0
self.__type = "Syntax Tree"
self.source_code_for_tree = setmetatable({ }, {
__index = function(self, t)
local s = t.source
local Files = require('files')
local f = Files.read(s.filename)
return f
end
})
self.is_instance = function(self, t)
return type(t) == 'table' and getmetatable(t) == self.__base
end
self.stub = concat(stub_bits, " ")
SyntaxTree = _class_0
end
AST.Action.get_args = function(self)
local _accum_0 = { }
local _len_0 = 1
for _index_0 = 1, #self do
local tok = self[_index_0]
if type(tok) ~= 'string' then
_accum_0[_len_0] = tok
_len_0 = _len_0 + 1
end
getmetatable(SyntaxTree).__call = function(self, t)
if type(t.source) == 'string' then
t.source = Source:from_string(t.source)
end
return _accum_0
setmetatable(t, self.__base)
if t.type == 'Action' then
t.stub = t:get_stub()
end
return t
end
return AST
return SyntaxTree

View File

@ -4,10 +4,6 @@
{:Source} = require "code_obj"
unpack or= table.unpack
AST = {}
AST.is_syntax_tree = (n, t=nil)->
type(n) == 'table' and getmetatable(n) and getmetatable(n).__type == "Syntax Tree" and (t == nil or n.type == t)
as_lua = =>
if type(@) == 'number'
return tostring(@)
@ -16,90 +12,88 @@ 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
.__type = "Syntax Tree"
.is_instance = (x)=> getmetatable(x) == @
.__tostring = =>
bits = [tostring(b) for b in *@]
for k,v in pairs(@)
unless bits[k]
table.insert(bits, "[ #{tostring(k)}]=#{tostring(v)}")
return "#{@type}{#{table.concat(bits, ", ")}}"
.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 "#{@type}{#{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 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
AST.Action.get_args = =>
[tok for tok in *@ when type(tok) != 'string']
get_args: =>
assert(@type == "Action", "Only actions have arguments")
return [tok for tok in *@ when type(tok) != 'string']
return AST
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] = tostring(arg_i)
arg_i += 1
return concat stub_bits, " "
@is_instance: (t)=>
type(t) == 'table' and getmetatable(t) == @__base
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 SyntaxTree