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:
parent
ec92b0fccd
commit
d0c3c57f7b
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
224
syntax_tree.lua
224
syntax_tree.lua
@ -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
|
||||
|
166
syntax_tree.moon
166
syntax_tree.moon
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user