Lots of overhaul, supporting a new Object Oriented approach (e.g.
%obj::action 1 2) and syntax.
This commit is contained in:
parent
930d522fbc
commit
e44acbf338
38
code_obj.lua
38
code_obj.lua
@ -5,16 +5,16 @@ do
|
||||
end
|
||||
local repr
|
||||
repr = require('utils').repr
|
||||
local unpack = unpack or table.unpack
|
||||
local LuaCode, NomsuCode, Source
|
||||
do
|
||||
local _class_0
|
||||
local _base_0 = {
|
||||
__tostring = function(self)
|
||||
if self.stop then
|
||||
return "@" .. tostring(self.filename) .. "[" .. tostring(self.start) .. ":" .. tostring(self.stop) .. "]"
|
||||
else
|
||||
return "@" .. tostring(self.filename) .. "[" .. tostring(self.start) .. "]"
|
||||
end
|
||||
return "@" .. tostring(self.filename) .. "[" .. tostring(self.start) .. tostring(self.stop and ':' .. self.stop or '') .. "]"
|
||||
end,
|
||||
__repr = function(self)
|
||||
return "Source(" .. tostring(repr(self.filename)) .. ", " .. tostring(self.start) .. tostring(self.stop and ', ' .. self.stop or '') .. ")"
|
||||
end,
|
||||
__eq = function(self, other)
|
||||
return getmetatable(self) == getmetatable(other) and self.filename == other.filename and self.start == other.start and self.stop == other.stop
|
||||
@ -108,9 +108,31 @@ do
|
||||
end
|
||||
return self.__str
|
||||
end,
|
||||
__repr = function(self)
|
||||
return tostring(self.__class.__name) .. "(" .. tostring(concat({
|
||||
repr(tostring(self.source)),
|
||||
unpack((function()
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
local _list_0 = self.bits
|
||||
for _index_0 = 1, #_list_0 do
|
||||
local b = _list_0[_index_0]
|
||||
_accum_0[_len_0] = repr(b)
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
return _accum_0
|
||||
end)())
|
||||
}, ", ")) .. ")"
|
||||
end,
|
||||
__len = function(self)
|
||||
return #tostring(self)
|
||||
end,
|
||||
match = function(self, ...)
|
||||
return tostring(self):match(...)
|
||||
end,
|
||||
gmatch = function(self, ...)
|
||||
return tostring(self):gmatch(...)
|
||||
end,
|
||||
dirty = function(self)
|
||||
self.__str = nil
|
||||
self._trailing_line_len = nil
|
||||
@ -192,8 +214,8 @@ do
|
||||
if b.is_code then
|
||||
b.dirty = error
|
||||
end
|
||||
local b_str = tostring(b)
|
||||
local line = match(b_str, "\n([^\n]*)$")
|
||||
b = tostring(b)
|
||||
local line = match(b, "\n([^\n]*)$")
|
||||
if line then
|
||||
line_len = #line
|
||||
else
|
||||
@ -254,6 +276,7 @@ do
|
||||
local _parent_0 = Code
|
||||
local _base_0 = {
|
||||
__tostring = Code.__tostring,
|
||||
__repr = Code.__repr,
|
||||
__len = Code.__len,
|
||||
add_free_vars = function(self, vars)
|
||||
if not (#vars > 0) then
|
||||
@ -446,6 +469,7 @@ do
|
||||
local _parent_0 = Code
|
||||
local _base_0 = {
|
||||
__tostring = Code.__tostring,
|
||||
__repr = Code.__repr,
|
||||
__len = Code.__len
|
||||
}
|
||||
_base_0.__index = _base_0
|
||||
|
@ -3,6 +3,7 @@
|
||||
-- indentation levels.
|
||||
{:insert, :remove, :concat} = table
|
||||
{:repr} = require 'utils'
|
||||
unpack or= table.unpack
|
||||
local LuaCode, NomsuCode, Source
|
||||
|
||||
class Source
|
||||
@ -16,11 +17,9 @@ class Source
|
||||
|
||||
@is_instance: (x)=> type(x) == 'table' and x.__class == @
|
||||
|
||||
__tostring: =>
|
||||
if @stop
|
||||
"@#{@filename}[#{@start}:#{@stop}]"
|
||||
else
|
||||
"@#{@filename}[#{@start}]"
|
||||
__tostring: => "@#{@filename}[#{@start}#{@stop and ':'..@stop or ''}]"
|
||||
|
||||
__repr: => "Source(#{repr @filename}, #{@start}#{@stop and ', '..@stop or ''})"
|
||||
|
||||
__eq: (other)=>
|
||||
getmetatable(@) == getmetatable(other) and @filename == other.filename and @start == other.start and @stop == other.stop
|
||||
@ -68,8 +67,14 @@ class Code
|
||||
@__str = concat(buff, "")
|
||||
return @__str
|
||||
|
||||
__len: =>
|
||||
#tostring(self)
|
||||
__repr: =>
|
||||
"#{@__class.__name}(#{concat {repr(tostring(@source)), unpack([repr(b) for b in *@bits])}, ", "})"
|
||||
|
||||
__len: => #tostring(@)
|
||||
|
||||
match: (...)=> tostring(@)\match(...)
|
||||
|
||||
gmatch: (...)=> tostring(@)\gmatch(...)
|
||||
|
||||
dirty: =>
|
||||
@__str = nil
|
||||
@ -126,8 +131,8 @@ class Code
|
||||
bits[#bits+1] = joiner
|
||||
bits[#bits+1] = b
|
||||
b.dirty = error if b.is_code
|
||||
b_str = tostring(b)
|
||||
line = match(b_str, "\n([^\n]*)$")
|
||||
b = tostring(b)
|
||||
line = match(b, "\n([^\n]*)$")
|
||||
if line
|
||||
line_len = #line
|
||||
else
|
||||
@ -153,6 +158,7 @@ class Code
|
||||
|
||||
class LuaCode extends Code
|
||||
__tostring: Code.__tostring
|
||||
__repr: Code.__repr
|
||||
__len: Code.__len
|
||||
new: (...)=>
|
||||
super ...
|
||||
@ -247,6 +253,7 @@ class LuaCode extends Code
|
||||
|
||||
class NomsuCode extends Code
|
||||
__tostring: Code.__tostring
|
||||
__repr: Code.__repr
|
||||
__len: Code.__len
|
||||
|
||||
return {:Code, :NomsuCode, :LuaCode, :Source}
|
||||
|
@ -7,11 +7,11 @@ use "core/metaprogramming.nom"
|
||||
compile [barf] to (Lua "error(nil, 0);")
|
||||
compile [barf %msg] to (Lua "error(\(%msg as lua expr), 0);")
|
||||
compile [compile error at %source %msg] to (..)
|
||||
Lua "_ENV:compile_error(\(%source as lua expr), \(%msg as lua expr))"
|
||||
Lua "nomsu:compile_error(\(%source as lua expr), \(%msg as lua expr))"
|
||||
|
||||
compile [assume %condition] to:
|
||||
lua> ".."
|
||||
local \%assumption = 'Assumption failed: '..tostring(_ENV:tree_to_nomsu(\%condition))
|
||||
local \%assumption = 'Assumption failed: '..tostring(nomsu:tree_to_nomsu(\%condition))
|
||||
return (..)
|
||||
Lua ".."
|
||||
if not \(%condition as lua expr) then
|
||||
|
@ -5,12 +5,12 @@
|
||||
|
||||
lua> "NOMSU_CORE_VERSION = 5"
|
||||
lua> ".."
|
||||
COMPILE_ACTIONS["% -> %"] = function(nomsu, tree, \%args, \%body)
|
||||
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 tostring(_ENV:compile(a)) or a end)
|
||||
local lua_args = table.map(\%args, function(a) return AST.is_syntax_tree(a) and tostring(nomsu:compile(a)) or a end)
|
||||
lua:concat_append(lua_args, ", ")
|
||||
local body_lua = AST.is_syntax_tree(\%body) and _ENV:compile(\%body):as_statements("return ") or \%body
|
||||
local body_lua = AST.is_syntax_tree(\%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)")
|
||||
@ -18,9 +18,9 @@ lua> ".."
|
||||
end
|
||||
|
||||
lua> ".."
|
||||
COMPILE_ACTIONS["compile as %"] = function(nomsu, tree, \%action)
|
||||
COMPILE_ACTIONS["compile as 1"] = function(nomsu, tree, \%action)
|
||||
local lua = LuaCode.Value(tree.source, "COMPILE_ACTIONS[", repr(\%action.stub), "](")
|
||||
local lua_args = table.map(\%action:get_args(), function(a) return _ENV:compile(a) end)
|
||||
local lua_args = table.map(\%action:get_args(), function(a) return nomsu:compile(a) end)
|
||||
table.insert(lua_args, 1, "nomsu")
|
||||
table.insert(lua_args, 2, "tree")
|
||||
lua:concat_append(lua_args, ", ")
|
||||
@ -48,14 +48,14 @@ test:
|
||||
asdf
|
||||
assume (%tmp is (nil)) or barf "compile to is leaking variables"
|
||||
lua> ".."
|
||||
COMPILE_ACTIONS["compile % to %"] = function(nomsu, tree, \%actions, \%body)
|
||||
local \%args = {"nomsu", "tree", unpack(table.map(\%actions[1]:get_args(), function(a) return tostring(_ENV:compile(\
|
||||
COMPILE_ACTIONS["compile 1 to 2"] = function(nomsu, tree, \%actions, \%body)
|
||||
local \%args = {"nomsu", "tree", unpack(table.map(\%actions[1]:get_args(), function(a) return tostring(nomsu:compile(\
|
||||
..a)) end))}
|
||||
local lua = LuaCode(tree.source, "COMPILE_ACTIONS[", repr(\%actions[1].stub),
|
||||
"] = ", \(compile as (%args -> %body)))
|
||||
for i=2,#\%actions do
|
||||
local alias = \%actions[i]
|
||||
local \%alias_args = {"nomsu", "tree", unpack(table.map(alias:get_args(), function(a) return tostring(_ENV:compile(\
|
||||
local \%alias_args = {"nomsu", "tree", unpack(table.map(alias:get_args(), function(a) return tostring(nomsu:compile(\
|
||||
..a)) end))}
|
||||
lua:append("\\nCOMPILE_ACTIONS[", repr(alias.stub), "] = ")
|
||||
if utils.equivalent(\%args, \%alias_args) then
|
||||
@ -73,10 +73,10 @@ lua> ".."
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
compile [call %fn with %args] to (..)
|
||||
compile [call %fn with %args] to:
|
||||
lua> ".."
|
||||
local lua = LuaCode.Value(tree.source, _ENV:compile(\%fn), "(")
|
||||
lua:concat_append(table.map(\%args, function(a) return _ENV:compile(a) end), ", ")
|
||||
local lua = LuaCode.Value(tree.source, nomsu:compile(\%fn), "(")
|
||||
lua:concat_append(table.map(\%args, function(a) return nomsu:compile(a) end), ", ")
|
||||
lua:append(")")
|
||||
return lua
|
||||
|
||||
@ -92,17 +92,17 @@ test:
|
||||
parse [baz %] as (foo %)
|
||||
|
||||
assume ((foo 1) == "outer")
|
||||
compile [local action %actions %body] to (..)
|
||||
compile [local action %actions %body] to:
|
||||
lua> ".."
|
||||
local fn_name = "A"..string.as_lua_id(\%actions[1].stub)
|
||||
local \%args = table.map(\%actions[1]:get_args(), function(a) return tostring(_ENV:compile(a)) end)
|
||||
local \%args = table.map(\%actions[1]:get_args(), function(a) return tostring(nomsu:compile(a)) end)
|
||||
local lua = LuaCode(tree.source, fn_name, " = ", \(compile as (%args -> %body)))
|
||||
lua:add_free_vars({fn_name})
|
||||
for i=2,#\%actions do
|
||||
local alias = \%actions[i]
|
||||
local alias_name = "A"..string.as_lua_id(alias.stub)
|
||||
lua:add_free_vars({alias_name})
|
||||
local \%alias_args = table.map(alias:get_args(), function(a) return tostring(_ENV:compile(a)) end)
|
||||
local \%alias_args = table.map(alias:get_args(), function(a) return tostring(nomsu:compile(a)) end)
|
||||
lua:append("\\n", alias_name, " = ")
|
||||
if utils.equivalent(\%args, \%alias_args) then
|
||||
lua:append(fn_name)
|
||||
@ -151,24 +151,40 @@ compile [parse %actions as %body] to (..)
|
||||
lua> ".."
|
||||
local replacements = {}
|
||||
for i,arg in ipairs(\%actions[1]:get_args()) do
|
||||
replacements[arg[1]] = tostring(_ENV:compile(arg))
|
||||
replacements[arg[1]] = tostring(nomsu:compile(arg))
|
||||
end
|
||||
local function make_tree(t)
|
||||
if not AST.is_syntax_tree(t) then
|
||||
return repr(t)
|
||||
elseif t.type ~= 'Var' then
|
||||
local args = {repr(tostring(t.source)), unpack(table.map(t, make_tree))}
|
||||
return t.type.."("..table.concat(args, ", ")..")"
|
||||
elseif replacements[t[1]] then
|
||||
return replacements[t[1]]
|
||||
if AST.is_syntax_tree(t, "Var") then
|
||||
if replacements[t[1]] then
|
||||
return replacements[t[1]]
|
||||
else
|
||||
return t.type.."{"..repr(t[1].." \\0").."..('%X'):format(__MANGLE_INDEX), source="..repr(tostring(t.source)).."}"
|
||||
end
|
||||
elseif AST.is_syntax_tree(t) then
|
||||
local ret = {}
|
||||
local i = 1
|
||||
for k, v in pairs(t) do
|
||||
if k == i then
|
||||
ret[#ret+1] = make_tree(t[i])
|
||||
i = i + 1
|
||||
elseif k == "source" then
|
||||
ret[#ret+1] = k.."= "..repr(tostring(v))
|
||||
elseif type(k) == 'string' and k:match("[_a-zA-Z][_a-zA-Z0-9]*") then
|
||||
ret[#ret+1] = k.."= "..make_tree(v)
|
||||
else
|
||||
ret[#ret+1] = "["..make_tree(k).."]= "..make_tree(v)
|
||||
end
|
||||
end
|
||||
return t.type.."{"..table.concat(ret, ", ").."}"
|
||||
else
|
||||
return t.type.."("..repr(tostring(t.source))..", "..repr(t[1].." \\0").."..string.format('%X', __MANGLE_INDEX))"
|
||||
return repr(t)
|
||||
end
|
||||
end
|
||||
local \%new_body = LuaCode(\%body.source,
|
||||
"__MANGLE_INDEX = (__MANGLE_INDEX or 0) + 1",
|
||||
"\\nlocal tree = ", make_tree(\%body),
|
||||
"\\nlocal lua = _ENV:compile(tree); return lua")
|
||||
"\\nlocal lua = nomsu:compile(tree)",
|
||||
"\\nreturn lua")
|
||||
local ret = \(compile as (compile %actions to %new_body))
|
||||
return ret
|
||||
|
||||
@ -176,21 +192,21 @@ compile [parse %actions as %body] to (..)
|
||||
|
||||
action [%tree as lua expr]:
|
||||
lua> ".."
|
||||
\%tree_lua = _ENV:compile(\%tree)
|
||||
\%tree_lua = nomsu:compile(\%tree)
|
||||
if not \%tree_lua.is_value then
|
||||
_ENV:compile_error(\%tree.source, "Could not convert %s to a Lua expression",
|
||||
_ENV:tree_to_nomsu(\%tree))
|
||||
nomsu:compile_error(\%tree.source, "Could not convert %s to a Lua expression",
|
||||
nomsu:tree_to_nomsu(\%tree))
|
||||
end
|
||||
return \%tree_lua
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
compile [%tree as lua] to (Lua value "_ENV:compile(\(%tree as lua expr))")
|
||||
compile [%tree as lua] to (Lua value "nomsu:compile(\(%tree as lua expr))")
|
||||
compile [%tree as lua statements] to (..)
|
||||
Lua value "_ENV:compile(\(%tree as lua expr)):as_statements()"
|
||||
Lua value "nomsu:compile(\(%tree as lua expr)):as_statements()"
|
||||
|
||||
compile [%tree as lua return] to (..)
|
||||
Lua value "_ENV:compile(\(%tree as lua expr)):as_statements('return ')"
|
||||
Lua value "nomsu:compile(\(%tree as lua expr)):as_statements('return ')"
|
||||
|
||||
compile [remove action %action] to (..)
|
||||
Lua "A\(=lua "string.as_lua_id(\(%action.stub))") = nil"
|
||||
@ -199,10 +215,10 @@ test:
|
||||
assume ("\(\(foo \%x) as nomsu)" == "foo %x") or barf ".."
|
||||
action source code failed.
|
||||
compile [%tree as nomsu] to (..)
|
||||
Lua value "_ENV:tree_to_nomsu(\(%tree as lua expr))"
|
||||
Lua value "nomsu:tree_to_nomsu(\(%tree as lua expr))"
|
||||
|
||||
compile [%tree as inline nomsu] to (..)
|
||||
Lua value "_ENV:tree_to_nomsu(\(%tree as lua expr), true)"
|
||||
Lua value "nomsu:tree_to_nomsu(\(%tree as lua expr), true)"
|
||||
|
||||
action [%var as lua identifier, %var as lua id] (..)
|
||||
lua> ".."
|
||||
@ -317,11 +333,13 @@ compile [type of %obj] to (Lua value "type(\(%obj as lua expr))")
|
||||
|
||||
test:
|
||||
assume ((parse "foo %") == \(foo \%))
|
||||
%a = (parse "\\1")
|
||||
%b = \\1
|
||||
assume ((parse "\\1") == \\1)
|
||||
compile [parse %text] to (Lua value "_ENV:parse(\(%text as lua expr))")
|
||||
compile [parse %text] to (Lua value "nomsu:parse(\(%text as lua expr))")
|
||||
compile [parse %text from %filename] to (..)
|
||||
Lua value ".."
|
||||
_ENV:parse(NomsuCode(Source(\(%filename as lua expr), 1, #\(%text as lua expr)), \(..)
|
||||
nomsu:parse(NomsuCode(Source(\(%filename as lua expr), 1, #\(%text as lua expr)), \(..)
|
||||
%text as lua expr
|
||||
..))
|
||||
|
||||
@ -333,15 +351,15 @@ test:
|
||||
assume %passed
|
||||
compile [run %nomsu_code] to (..)
|
||||
Lua value ".."
|
||||
_ENV:run(\(%nomsu_code as lua expr), \(..)
|
||||
nomsu:run(\(%nomsu_code as lua expr), \(..)
|
||||
=lua "repr(tostring(\(%nomsu_code.source)))"
|
||||
..)
|
||||
|
||||
test:
|
||||
assume ((\(\5 + \5) as value) == 10) or barf "%tree as value failed."
|
||||
action [run tree %tree, %tree as value] (lua> "return _ENV:run(\%tree)")
|
||||
action [run tree %tree, %tree as value] (lua> "return nomsu:run(\%tree)")
|
||||
compile [compile %block, compiled %block, %block compiled] to (..)
|
||||
Lua value "_ENV:compile(\(%block as lua))"
|
||||
Lua value "nomsu:compile(\(%block as lua))"
|
||||
|
||||
# Return statement is wrapped in a do..end block because Lua is unhappy if you
|
||||
put code after a return statement, unless you wrap it in a block.
|
||||
|
@ -238,6 +238,7 @@ compile [%x or %y] to (Lua value "(\(%x as lua expr) or \(%y as lua expr))")
|
||||
# Bitwise Operators
|
||||
# TODO: implement OR, XOR, AND for multiple operands?
|
||||
test:
|
||||
assume ((~5) == -6)
|
||||
assume ((1 | 4) == 5)
|
||||
assume ((1 ~ 3) == 2)
|
||||
assume ((1 & 3) == 1)
|
||||
@ -271,14 +272,9 @@ compile [%x LSHIFT %shift, %x << %shift] to (..)
|
||||
(%use_bitops and "bit.lshift(\(%x as lua expr), \(%shift as lua expr))") or ".."
|
||||
(\(%x as lua expr) << \(%shift as lua expr))
|
||||
|
||||
compile [%x RSHIFT %shift, %x >>> %shift] to (..)
|
||||
compile [%x RSHIFT %shift, %x >> %shift] to (..)
|
||||
Lua value (..)
|
||||
(%use_bitops and "bit.rshift(\(%x as lua expr), \(%shift as lua expr))") or ".."
|
||||
(\(%x as lua expr) >>> \(%shift as lua expr))
|
||||
|
||||
compile [%x ARSHIFT %shift, %x >> %shift] to (..)
|
||||
Lua value (..)
|
||||
(%use_bitops and "bit.arshift(\(%x as lua expr), \(%shift as lua expr))") or ".."
|
||||
(\(%x as lua expr) >> \(%shift as lua expr))
|
||||
|
||||
# Unary operators
|
||||
|
145
lib/object.nom
145
lib/object.nom
@ -1,111 +1,112 @@
|
||||
#!/usr/bin/env nomsu -V2.5.5.5
|
||||
#!/usr/bin/env nomsu -V3
|
||||
#
|
||||
This file contains the implementation of an Object-Oriented programming system.
|
||||
|
||||
test:
|
||||
object "Dog":
|
||||
(class Dog).genus = "Canus"
|
||||
method [initialize %] (%.barks or= 0)
|
||||
method [bark, woof]:
|
||||
%barks = ("Bark!" for % in 1 to (me).barks)
|
||||
(Dog).genus = "Canus"
|
||||
my action [set up]:
|
||||
%me.barks or= 0
|
||||
my action [bark, woof]:
|
||||
%barks = ("Bark!" for % in 1 to %me.barks)
|
||||
return (%barks joined with " ")
|
||||
|
||||
method [get pissed off] ((me).barks += 1)
|
||||
my action [get pissed off]:
|
||||
%me.barks += 1
|
||||
|
||||
%d = (new Dog {barks:2})
|
||||
as %d:
|
||||
assume ((me) == %d)
|
||||
assume ((me).barks == 2)
|
||||
assume ((bark) == "Bark! Bark!")
|
||||
assume ((woof) == "Bark! Bark!")
|
||||
get pissed off
|
||||
assume ((me).barks == 3)
|
||||
assume ((bark) == "Bark! Bark! Bark!")
|
||||
assume ((me).genus == "Canus")
|
||||
|
||||
assume (%d.barks == 2)
|
||||
assume ((%d::bark) == "Bark! Bark!")
|
||||
assume ((%d::woof) == "Bark! Bark!")
|
||||
%d::get pissed off
|
||||
assume (%d.barks == 3)
|
||||
assume ((%d::bark) == "Bark! Bark! Bark!")
|
||||
assume (%d.genus == "Canus")
|
||||
assume ("\(%d.class)" == "Dog")
|
||||
assume (%d.genus == "Canus")
|
||||
assume (%d.barks == 3)
|
||||
as (new Dog) (assume ((me).barks == 0) or barf "Default initializer failed")
|
||||
as (new Dog {barks:1}) (assume ((bark) == "Bark!"))
|
||||
action [foo] (as (new Dog {barks:23}) (return (me).barks))
|
||||
assume ((foo) == 23) or barf "Oops, \(foo) != 23"
|
||||
as (new Dog {barks:101}):
|
||||
try (as (new Dog {barks:8}) (barf)) and if it succeeds (barf)
|
||||
assume ((me).barks == 101) or barf ".."
|
||||
Error in nested 'as % %' failed to properly reset 'self'
|
||||
%d2 = (new Dog)
|
||||
assume (%d2.barks == 0) or barf "Default initializer failed"
|
||||
with {%d:new Dog {barks:1}}: assume ((%d::bark) == "Bark!")
|
||||
|
||||
object "Corgi" extends (class Dog):
|
||||
method [sploot] "splooted"
|
||||
method [bark, woof]:
|
||||
%barks = ("Yip!" for % in 1 to (me).barks)
|
||||
object "Corgi" extends (Dog):
|
||||
my action [sploot] "splooted"
|
||||
my action [bark, woof]:
|
||||
%barks = ("Yip!" for % in 1 to %me.barks)
|
||||
return (%barks joined with " ")
|
||||
|
||||
%corg = (new Corgi)
|
||||
assume (%corg.barks == 0)
|
||||
as (new Corgi {barks:1}):
|
||||
assume ((sploot) == "splooted") or barf "subclass method failed"
|
||||
assume ((bark) == "Yip!") or barf "inheritance failed"
|
||||
assume ((woof) == "Yip!")
|
||||
with {%d:new Corgi {barks:1}}:
|
||||
assume ((%d::sploot) == "splooted") or barf "subclass method failed"
|
||||
assume ((%d::bark) == "Yip!") or barf "inheritance failed"
|
||||
assume ((%d::woof) == "Yip!")
|
||||
|
||||
as (new Dog {barks:2}):
|
||||
assume ((bark) == "Bark! Bark!")
|
||||
with {%d:new Dog {barks:2}}:
|
||||
assume ((%d::bark) == "Bark! Bark!")
|
||||
|
||||
compile [@, me] to (Lua value "self")
|
||||
compile [method %actions %body] to:
|
||||
%lua = (\(local action \[%actions.1] %body) as lua)
|
||||
declare locals in %lua
|
||||
for % in %actions:
|
||||
to %lua write "\n\(\%class as lua id).\(% as lua id) = \(%actions.1 as lua id)"
|
||||
%lua = (..)
|
||||
Lua ".."
|
||||
do -- Method: \(%actions.(1).stub)
|
||||
\%lua
|
||||
compile [my action %actions %body] to:
|
||||
lua> ".."
|
||||
local fn_name = "A"..string.as_lua_id(\%actions[1].stub)
|
||||
local \%args = table.map(\%actions[1]:get_args(), function(a) return tostring(nomsu:compile(a)) end)
|
||||
table.insert(\%args, \(\%me as lua id))
|
||||
local lua = LuaCode(tree.source, "class.", fn_name, " = ", \(compile as (%args -> %body)))
|
||||
for i=2,#\%actions do
|
||||
local alias = \%actions[i]
|
||||
local alias_name = "A"..string.as_lua_id(alias.stub)
|
||||
local \%alias_args = table.map(alias:get_args(), function(a) return tostring(nomsu:compile(a)) end)
|
||||
table.insert(\%alias_args, \(\%me as lua id))
|
||||
lua:append("\\nclass.", alias_name, " = ")
|
||||
if utils.equivalent(\%args, \%alias_args) then
|
||||
lua:append("class.", fn_name)
|
||||
else
|
||||
lua:append("function(")
|
||||
lua:concat_append(\%alias_args, ", ")
|
||||
lua:append(")\\n return class.", fn_name, "(")
|
||||
lua:concat_append(\%args, ", ")
|
||||
lua:append(")\\nend")
|
||||
end
|
||||
return %lua
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
parse [as %instance %body] as (..)
|
||||
result of:
|
||||
%old_self = (me)
|
||||
(me) = %instance
|
||||
try %body and if it barfs %msg:
|
||||
(me) = %old_self
|
||||
barf %msg
|
||||
..or if it succeeds: (me) = %old_self
|
||||
end
|
||||
return lua
|
||||
|
||||
compile [object %classname extends %parent %class_body] to:
|
||||
%class = (\%class as lua id)
|
||||
return (..)
|
||||
Lua ".."
|
||||
do
|
||||
local \%class = {name=\(%classname as lua expr)}
|
||||
setmetatable(\%class, {
|
||||
local class = {name=\(%classname as lua expr)}
|
||||
setmetatable(class, {
|
||||
__index=\(%parent as lua expr),
|
||||
__tostring=function(cls) return cls.name end,
|
||||
__call=function(cls, inst)
|
||||
inst = setmetatable(inst or {}, cls)
|
||||
if cls.A_initialize_1 then
|
||||
cls.A_initialize_1(inst)
|
||||
if inst.A_set_up then
|
||||
inst:A_set_up()
|
||||
end
|
||||
return inst
|
||||
end,
|
||||
})
|
||||
_ENV["A"..string.as_lua_id("new "..\%class.name)] = \%class
|
||||
_ENV["A"..string.as_lua_id("new "..\%class.name.." 1")] = \%class
|
||||
_ENV["A"..string.as_lua_id("class "..\%class.name)] = function() return \%class end
|
||||
\%class.__index = \%class
|
||||
\%class.class = \%class
|
||||
nomsu["A"..string.as_lua_id("new "..class.name)] = class
|
||||
nomsu["A"..string.as_lua_id("new "..class.name.." 1")] = class
|
||||
nomsu["A"..string.as_lua_id(class.name)] = function() return class end
|
||||
class.__index = class
|
||||
class.class = class
|
||||
class.__tostring = function(inst)
|
||||
return inst.name..getmetatable(dict{}).__tostring(inst)
|
||||
end
|
||||
|
||||
\(%class_body as lua statements)
|
||||
|
||||
\%class.__tostring = \%class["A"..string.as_lua_id("as text")] or function(inst)
|
||||
return inst.name..getmetatable(dict{}).__tostring(inst)
|
||||
|
||||
local metamethod_map = {["as text"]="__tostring", ["clean up"]="__gc",
|
||||
["+ 1"]="__add", ["- 1"]="__sub", ["* 1"]="__mul", ["/ 1"]="__div",
|
||||
["-"]="__unm", ["// 1"]="__idiv", ["mod 1"]="__mod", ["^ 1"]="__pow",
|
||||
["& 1"]="__band", ["| 1"]="__bor", ["~ 1"]="__bxor", ["~"]="__bnot",
|
||||
["<< 1"]="__bshl", [">> 1"]="__bshr", ["== 1"]="__eq", ["< 1"]="__lt",
|
||||
["<= 1"]="__le", ["set 1 = 2"]="__newindex", ["length"]="__len",
|
||||
["__ipairs"]="__ipairs", ["__pairs"]="__pairs",
|
||||
}
|
||||
for stub,metamethod in pairs(metamethod_map) do
|
||||
class[metamethod] = class["A"..string.as_lua_id(stub)]
|
||||
end
|
||||
end
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
parse [object %classname %class_body] as (..)
|
||||
object %classname extends (nil) %class_body
|
||||
|
171
nomsu.3.peg
Normal file
171
nomsu.3.peg
Normal file
@ -0,0 +1,171 @@
|
||||
-- Nomsu version 3
|
||||
file:
|
||||
{:curr_indent: ' '* :}
|
||||
(((action / expression / inline_block / indented_block) eol !.)
|
||||
/ file_chunks / empty_block)
|
||||
%ws* (!! .+ -> "Parse error" !!)?
|
||||
|
||||
shebang: {:shebang: "#!" (!"nomsu" [^%nl])* "nomsu" %ws+ "-V" %ws* {:version: [0-9.]+ :} [^%nl]* :}
|
||||
|
||||
file_chunks (FileChunks):
|
||||
{:curr_indent: ' '* :}
|
||||
shebang? comment? blank_lines?
|
||||
(top_block (nl_nodent section_division top_block)*)
|
||||
blank_lines?
|
||||
|
||||
top_block (Block):
|
||||
{:curr_indent: ' '* :}
|
||||
comment? blank_lines? statement (nl_nodent statement)*
|
||||
|
||||
empty_block (Block):
|
||||
{:curr_indent: ' '* :}
|
||||
comment? blank_lines?
|
||||
|
||||
nodent: =curr_indent !(" ")
|
||||
indent: =curr_indent " "
|
||||
blank_lines: %nl ((nodent comment / %ws*) %nl)*
|
||||
eol: %ws* eol_comment? (!. / &%nl)
|
||||
|
||||
nl_nodent: blank_lines nodent
|
||||
nl_indent: blank_lines {:curr_indent: indent :} (comment nl_nodent)?
|
||||
|
||||
comment:
|
||||
"#" (({} {~ [^%nl]* (%nl+ (indent -> '') [^%nl]*)* ~} %userdata) => add_comment)
|
||||
eol_comment:
|
||||
"#" (({} {[^%nl]*} %userdata) => add_comment)
|
||||
|
||||
section_division: ("~")^+3 eol
|
||||
|
||||
inline_block:
|
||||
"(" %ws* inline_block %ws* ")" / raw_inline_block
|
||||
raw_inline_block (Block):
|
||||
(!"::") ":" %ws* ((inline_statement (%ws* ";" %ws* inline_statement)*) / !(eol nl_indent))
|
||||
indented_block (Block):
|
||||
":" eol nl_indent statement (nl_nodent statement)* (%nl (%ws* %nl)* nodent comment)*
|
||||
|
||||
statement:
|
||||
(action / expression) (eol / (!! [^%nl]+ -> "Unexpected code while parsing line" !!))
|
||||
|
||||
inline_statement: (inline_action / inline_expression)
|
||||
|
||||
noindex_inline_expression:
|
||||
number / variable / inline_text / inline_list / inline_dict / inline_nomsu
|
||||
/ ( "("
|
||||
%ws* (inline_action / inline_expression) %ws*
|
||||
(%ws* ',' %ws* (inline_action / inline_expression) %ws*)*
|
||||
(")"
|
||||
/ (!! eol -> 'Line ended without finding a closing )-parenthesis' !!)
|
||||
/ (!! [^%nl]+ -> 'Unexpected code while parsing subexpression' !!)
|
||||
)
|
||||
)
|
||||
inline_expression: index_chain / noindex_inline_expression
|
||||
indented_expression:
|
||||
indented_text / indented_nomsu / indented_list / indented_dict / ({|
|
||||
"(..)" nl_indent
|
||||
(action / expression) (nl_nodent comment)*
|
||||
(eol / (!! [^%nl]+ -> "Unexpected code while parsing indented expression" !!))
|
||||
|} -> unpack)
|
||||
/ (nl_indent (!! [^%nl]* -> "Unexpected indentation. Perhaps you meant to put a ':' or '(..)' on the previous line?" !!) (nl_nodent [^%nl]*)*)
|
||||
expression:
|
||||
inline_expression / indented_expression
|
||||
|
||||
inline_nomsu (EscapedNomsu): "\" (inline_expression / inline_block)
|
||||
indented_nomsu (EscapedNomsu):
|
||||
"\" (noindex_inline_expression / inline_block / indented_expression / indented_block)
|
||||
|
||||
index_chain (IndexChain):
|
||||
noindex_inline_expression ("." (text_word / noindex_inline_expression))+
|
||||
|
||||
-- Actions need either at least 1 word, or at least 2 tokens
|
||||
inline_action (Action):
|
||||
!section_division
|
||||
({:target: inline_arg :} %ws* "::" %ws*)?
|
||||
( (inline_arg (%ws* (inline_arg / word))+)
|
||||
/ (word (%ws* (inline_arg / word))*))
|
||||
(%ws* inline_block)?
|
||||
inline_arg: inline_expression / inline_block
|
||||
action (Action):
|
||||
!section_division
|
||||
({:target: arg :} (nl_nodent "..")? %ws* "::" (nl_nodent "..")? %ws*)?
|
||||
( (arg ((nl_nodent "..")? %ws* (arg / word))+)
|
||||
/ (word ((nl_nodent "..")? %ws* (arg / word))*))
|
||||
arg: expression / inline_block / indented_block
|
||||
|
||||
word: !number { %operator_char+ / %ident_char+ }
|
||||
|
||||
text_word (Text): word
|
||||
|
||||
inline_text (Text):
|
||||
!('".."' eol)
|
||||
'"'
|
||||
({~ (('\"' -> '"') / ('\\' -> '\') / %escaped_char / [^%nl\"])+ ~}
|
||||
/ inline_text_interpolation)*
|
||||
('"'
|
||||
/ (!! eol -> 'Line ended before finding a closing double quotation mark' !!)
|
||||
/ (!! [^%nl]+ -> 'Unexpected code while parsing Text' !!))
|
||||
inline_text_interpolation:
|
||||
"\" (
|
||||
variable / inline_list / inline_dict / inline_text
|
||||
/ ("("
|
||||
%ws* (inline_action / inline_expression) %ws*
|
||||
(%ws* ',' %ws* (inline_action / inline_expression) %ws*)*
|
||||
(")"
|
||||
/ (!! eol -> 'Line ended without finding a closing )-parenthesis' !!)
|
||||
/ (!! [^%nl]+ -> 'Unexpected code while parsing Text interpolation' !!)))
|
||||
)
|
||||
|
||||
indented_text (Text):
|
||||
'".."' eol %nl {%nl*} {:curr_indent: indent :}
|
||||
(indented_plain_text / text_interpolation / {~ %nl+ (=curr_indent -> "") ~})*
|
||||
(!! [^%nl]+ -> "Unexpected code while parsing Text" !!)?
|
||||
-- Tracking text-lines-within-indented-text as separate objects allows for better debugging line info
|
||||
indented_plain_text (Text):
|
||||
{~ (("\\" -> "\") / (("\" blank_lines =curr_indent "..") -> "") / (!text_interpolation "\") / [^%nl\]+)+
|
||||
(%nl+ (=curr_indent -> ""))* ~}
|
||||
text_interpolation:
|
||||
inline_text_interpolation / ("\" indented_expression (blank_lines =curr_indent "..")?)
|
||||
|
||||
number (Number): (("-"? (([0-9]+ "." [0-9]+) / ("." [0-9]+) / "0x" [0-9a-fA-F]+ / ([0-9]+)))-> tonumber)
|
||||
|
||||
-- Variables can be nameless (i.e. just %) and can only contain identifier chars.
|
||||
-- This ensures you don't get weird parsings of `%x+%y` or `%'s thing`.
|
||||
variable (Var): "%" {%ident_char*}
|
||||
|
||||
inline_list (List):
|
||||
!('[..]')
|
||||
"[" %ws*
|
||||
(inline_list_item (%ws* ',' %ws* inline_list_item)* (%ws* ',')?)? %ws*
|
||||
("]" / (","? (
|
||||
(!! eol -> "Line ended before finding a closing ]-bracket" !!)
|
||||
/(!! [^%nl]+ -> "Unexpected code while parsing List" !!)
|
||||
)))
|
||||
indented_list (List):
|
||||
"[..]" eol nl_indent
|
||||
list_line (nl_nodent list_line)* (nl_nodent comment)*
|
||||
(","? (!! [^%nl]+ -> "Unexpected code while parsing List" !!))?
|
||||
list_line:
|
||||
(inline_list_item %ws* "," %ws*)+ eol
|
||||
/ (inline_list_item %ws* "," %ws*)* (action / expression) eol
|
||||
inline_list_item: inline_action / inline_expression
|
||||
|
||||
inline_dict (Dict):
|
||||
!('{..}')
|
||||
"{" %ws*
|
||||
(inline_dict_entry (%ws* ',' %ws* inline_dict_entry)*)? %ws*
|
||||
("}" / (","? (
|
||||
(!! eol -> "Line ended before finding a closing }-brace" !!)
|
||||
/ (!! [^%nl]* -> "Unexpected code while parsing Dictionary" !!)
|
||||
)))
|
||||
indented_dict (Dict):
|
||||
"{..}" eol nl_indent
|
||||
dict_line (nl_nodent dict_line)* (nl_nodent comment)*
|
||||
(","? (!! [^%nl]+ -> "Unexpected code while parsing Dictionary" !!))?
|
||||
dict_line:
|
||||
(inline_dict_entry %ws* "," %ws*)+ eol
|
||||
/ (inline_dict_entry %ws* "," %ws*)* dict_entry eol
|
||||
dict_entry(DictEntry):
|
||||
dict_key (%ws* ":" %ws* (action / expression))?
|
||||
inline_dict_entry(DictEntry):
|
||||
dict_key (%ws* ":" %ws* (inline_action / inline_expression)?)?
|
||||
dict_key:
|
||||
text_word / inline_expression
|
@ -32,14 +32,10 @@ local AST = require("syntax_tree")
|
||||
local Parser = require("parser")
|
||||
SOURCE_MAP = { }
|
||||
string.as_lua_id = function(str)
|
||||
local argnum = 0
|
||||
str = gsub(str, "x([0-9A-F][0-9A-F])", "x\0%1")
|
||||
str = gsub(str, "%W", function(c)
|
||||
if c == ' ' then
|
||||
return '_'
|
||||
elseif c == '%' then
|
||||
argnum = argnum + 1
|
||||
return tostring(argnum)
|
||||
else
|
||||
return format("x%02X", byte(c))
|
||||
end
|
||||
@ -60,6 +56,15 @@ table.fork = function(t, values)
|
||||
__index = t
|
||||
})
|
||||
end
|
||||
table.copy = function(t)
|
||||
return setmetatable((function()
|
||||
local _tbl_0 = { }
|
||||
for k, v in pairs(t) do
|
||||
_tbl_0[k] = v
|
||||
end
|
||||
return _tbl_0
|
||||
end)(), getmetatable(t))
|
||||
end
|
||||
do
|
||||
local STRING_METATABLE = getmetatable("")
|
||||
STRING_METATABLE.__add = function(self, other)
|
||||
@ -165,6 +170,7 @@ local NomsuCompiler = setmetatable({
|
||||
do
|
||||
NomsuCompiler.NOMSU_COMPILER_VERSION = 5
|
||||
NomsuCompiler.NOMSU_SYNTAX_VERSION = Parser.version
|
||||
NomsuCompiler.nomsu = NomsuCompiler
|
||||
NomsuCompiler.parse = function(self, ...)
|
||||
return Parser.parse(...)
|
||||
end
|
||||
@ -246,7 +252,7 @@ do
|
||||
local err_msg = err_format_string:format(src, ...)
|
||||
return error(tostring(source.filename) .. ":" .. tostring(line_no) .. ": " .. err_msg, 0)
|
||||
end
|
||||
local math_expression = re.compile([[ ([+-] " ")* "%" (" " [*/^+-] (" " [+-])* " %")+ !. ]])
|
||||
local math_expression = re.compile([[ ([+-] " ")* [0-9]+ (" " [*/^+-] (" " [+-])* " " [0-9]+)+ !. ]])
|
||||
local add_lua_bits
|
||||
add_lua_bits = function(self, val_or_stmt, code)
|
||||
local cls = val_or_stmt == "value" and LuaCode.Value or LuaCode
|
||||
@ -279,7 +285,7 @@ do
|
||||
end
|
||||
local add_bit_lua
|
||||
add_bit_lua = function(lua, bit_lua)
|
||||
local bit_leading_len = #(tostring(bit_lua):match("^[^\n]*"))
|
||||
local bit_leading_len = #(bit_lua:match("^[^\n]*"))
|
||||
lua:append(lua:trailing_line_len() + bit_leading_len > MAX_LINE and ",\n " or ", ")
|
||||
return lua:append(bit_lua)
|
||||
end
|
||||
@ -327,37 +333,37 @@ do
|
||||
end
|
||||
return lua
|
||||
end,
|
||||
["Lua %"] = function(self, tree, _code)
|
||||
["Lua 1"] = function(self, tree, _code)
|
||||
return add_lua_string_bits(self, 'statements', _code)
|
||||
end,
|
||||
["Lua value %"] = function(self, tree, _code)
|
||||
["Lua value 1"] = function(self, tree, _code)
|
||||
return add_lua_string_bits(self, 'value', _code)
|
||||
end,
|
||||
["lua > %"] = function(self, tree, _code)
|
||||
["lua > 1"] = function(self, tree, _code)
|
||||
if _code.type ~= "Text" then
|
||||
return LuaCode(tree.source, "_ENV:run_lua(", self:compile(_code), ");")
|
||||
return LuaCode(tree.source, "nomsu:run_lua(", self:compile(_code), ");")
|
||||
end
|
||||
return add_lua_bits(self, "statements", _code)
|
||||
end,
|
||||
["= lua %"] = function(self, tree, _code)
|
||||
["= lua 1"] = function(self, tree, _code)
|
||||
if _code.type ~= "Text" then
|
||||
return LuaCode.Value(tree.source, "_ENV:run_lua(", self:compile(_code), ":as_statements('return '))")
|
||||
return LuaCode.Value(tree.source, "nomsu:run_lua(", self:compile(_code), ":as_statements('return '))")
|
||||
end
|
||||
return add_lua_bits(self, "value", _code)
|
||||
end,
|
||||
["use %"] = function(self, tree, _path)
|
||||
["use 1"] = function(self, tree, _path)
|
||||
if _path.type == 'Text' and #_path == 1 and type(_path[1]) == 'string' then
|
||||
local path = _path[1]
|
||||
for _, f in Files.walk(path) do
|
||||
self:run_file(f)
|
||||
end
|
||||
end
|
||||
return LuaCode(tree.source, "for i,f in Files.walk(", self:compile(_path), ") do _ENV:run_file(f) end")
|
||||
return LuaCode(tree.source, "for i,f in Files.walk(", self:compile(_path), ") do nomsu:run_file(f) end")
|
||||
end,
|
||||
["tests"] = function(self, tree)
|
||||
return LuaCode.Value(tree.source, "TESTS")
|
||||
end,
|
||||
["test %"] = function(self, tree, _body)
|
||||
["test 1"] = function(self, tree, _body)
|
||||
local test_str = table.concat((function()
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
@ -481,7 +487,7 @@ do
|
||||
source = nil
|
||||
end
|
||||
local lua_string = tostring(lua)
|
||||
local run_lua_fn, err = load(lua_string, tostring(source or lua.source), "t", self)
|
||||
local run_lua_fn, err = load(lua_string, nil and tostring(source or lua.source), "t", self)
|
||||
if not run_lua_fn then
|
||||
local line_numbered_lua = concat((function()
|
||||
local _accum_0 = { }
|
||||
@ -502,7 +508,8 @@ do
|
||||
if not file then
|
||||
error("Failed to find file: " .. tostring(source.filename))
|
||||
end
|
||||
local nomsu_str = tostring(file:sub(source.start, source.stop))
|
||||
local nomsu_str = file:sub(source.start, source.stop)
|
||||
assert(type(nomsu_str) == 'string')
|
||||
local lua_line = 1
|
||||
local nomsu_line = Files.get_line_number(file, source.start)
|
||||
local map_sources
|
||||
@ -596,30 +603,34 @@ do
|
||||
end
|
||||
lua:concat_append(args, ", ")
|
||||
lua:append(")")
|
||||
if tree.target then
|
||||
local target_lua = self:compile(tree.target)
|
||||
lua:prepend(target_lua, ":")
|
||||
end
|
||||
return lua
|
||||
elseif "EscapedNomsu" == _exp_0 then
|
||||
local lua = LuaCode.Value(tree.source, tree[1].type, "(")
|
||||
local bits
|
||||
if tree[1].type == "EscapedNomsu" then
|
||||
bits = {
|
||||
self:compile(tree[1])
|
||||
}
|
||||
else
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
local _list_0 = tree[1]
|
||||
for _index_0 = 1, #_list_0 do
|
||||
local bit = _list_0[_index_0]
|
||||
_accum_0[_len_0] = AST.is_syntax_tree(bit) and self:compile(bit) or repr(bit)
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
bits = _accum_0
|
||||
local lua = LuaCode.Value(tree.source, tree[1].type, "{")
|
||||
local needs_comma, i = false, 1
|
||||
for k, v in pairs(AST.is_syntax_tree(tree[1], "EscapedNomsu") and tree or tree[1]) do
|
||||
if needs_comma then
|
||||
lua:append(", ")
|
||||
else
|
||||
needs_comma = true
|
||||
end
|
||||
if k == i then
|
||||
i = i + 1
|
||||
elseif type(k) == 'string' and match(k, "[_a-zA-Z][_a-zA-Z0-9]*") then
|
||||
lua:append(k, "= ")
|
||||
else
|
||||
lua:append("[", (AST.is_syntax_tree(k) and self:compile(k) or repr(k)), "]= ")
|
||||
end
|
||||
if k == "source" then
|
||||
lua:append(repr(tostring(v)))
|
||||
else
|
||||
lua:append(AST.is_syntax_tree(v) and self:compile(v) or repr(v))
|
||||
end
|
||||
end
|
||||
insert(bits, 1, repr(tostring(tree[1].source)))
|
||||
lua:concat_append(bits, ", ")
|
||||
lua:append(")")
|
||||
lua:append("}")
|
||||
return lua
|
||||
elseif "Block" == _exp_0 then
|
||||
local lua = LuaCode(tree.source)
|
||||
@ -719,7 +730,7 @@ do
|
||||
if not (value_lua.is_value) then
|
||||
self:compile_error(tree[2].source, "Cannot use:\n%s\nas a dict value, since it's not an expression.")
|
||||
end
|
||||
local key_str = match(tostring(key_lua), [=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=])
|
||||
local key_str = match(tostring(key_lua), [=[^["']([a-zA-Z_][a-zA-Z0-9_]*)['"]$]=])
|
||||
if key_str then
|
||||
return LuaCode(tree.source, key_str, "=", value_lua)
|
||||
elseif sub(tostring(key_lua), 1, 1) == "[" then
|
||||
@ -790,6 +801,9 @@ do
|
||||
return error("Cannot inline a FileChunks")
|
||||
elseif "Action" == _exp_0 then
|
||||
local nomsu = NomsuCode(tree.source)
|
||||
if tree.target then
|
||||
nomsu:append(self:tree_to_inline_nomsu(tree.target), "::")
|
||||
end
|
||||
for i, bit in ipairs(tree) do
|
||||
if type(bit) == "string" then
|
||||
local clump_words = (type(tree[i - 1]) == 'string' and Parser.is_operator(bit) ~= Parser.is_operator(tree[i - 1]))
|
||||
@ -799,7 +813,7 @@ do
|
||||
nomsu:append(bit)
|
||||
else
|
||||
local arg_nomsu = recurse(bit, nomsu, parenthesize_blocks or (i == 1 or i < #tree))
|
||||
if not (tostring(arg_nomsu):match("^:") or i == 1) then
|
||||
if not (arg_nomsu:match("^:") or i == 1) then
|
||||
nomsu:append(" ")
|
||||
end
|
||||
if bit.type == "Action" then
|
||||
@ -1056,13 +1070,13 @@ do
|
||||
local should_clump
|
||||
should_clump = function(prev_line, line)
|
||||
if prev_line.type == "Action" and line.type == "Action" then
|
||||
if prev_line.stub == "use %" then
|
||||
return line.stub == "use %"
|
||||
if prev_line.stub == "use 1" then
|
||||
return line.stub == "use 1"
|
||||
end
|
||||
if prev_line.stub == "test %" then
|
||||
if prev_line.stub == "test 1" then
|
||||
return true
|
||||
end
|
||||
if line.stub == "test %" then
|
||||
if line.stub == "test 1" then
|
||||
return false
|
||||
end
|
||||
end
|
||||
@ -1090,13 +1104,16 @@ do
|
||||
end
|
||||
end
|
||||
nomsu:append(pop_comments(tree.source.stop, '\n'))
|
||||
if not (tostring(nomsu):match("\n$")) then
|
||||
if not (nomsu:match("\n$")) then
|
||||
nomsu:append('\n')
|
||||
end
|
||||
return nomsu
|
||||
elseif "Action" == _exp_0 then
|
||||
local pos, next_space = tree.source.start, ''
|
||||
local nomsu = NomsuCode(tree.source, pop_comments(pos))
|
||||
if tree.target then
|
||||
nomsu:append(self:tree_to_nomsu(tree.target), "::")
|
||||
end
|
||||
for i, bit in ipairs(tree) do
|
||||
if next_space == "\n.." or (next_space == " " and nomsu:trailing_line_len() > MAX_LINE) then
|
||||
nomsu:append("\n", pop_comments(pos), '..')
|
||||
@ -1109,12 +1126,12 @@ do
|
||||
nomsu:append(bit)
|
||||
next_space = ' '
|
||||
elseif bit.type == "Block" then
|
||||
nomsu:append(recurse(bit, #tostring(nomsu):match('[^\n]*$')))
|
||||
nomsu:append(recurse(bit, #nomsu:match('[^\n]*$')))
|
||||
pos = bit.source.stop
|
||||
next_space = inline and " " or "\n.."
|
||||
else
|
||||
nomsu:append(next_space)
|
||||
local bit_nomsu = recurse(bit, #tostring(nomsu):match('[^\n]*$'))
|
||||
local bit_nomsu = recurse(bit, #nomsu:match('[^\n]*$'))
|
||||
if bit.type == "Action" and not bit_nomsu:is_multiline() then
|
||||
bit_nomsu:parenthesize()
|
||||
end
|
||||
@ -1134,7 +1151,7 @@ do
|
||||
local line_nomsu = recurse(line)
|
||||
nomsu:append(line_nomsu)
|
||||
if i < #tree then
|
||||
nomsu:append(tostring(line_nomsu):match('\n[^\n]*\n') and "\n\n" or "\n")
|
||||
nomsu:append(line_nomsu:match('\n[^\n]*\n') and "\n\n" or "\n")
|
||||
end
|
||||
end
|
||||
nomsu:append(pop_comments(tree.source.stop, '\n'))
|
||||
@ -1174,7 +1191,7 @@ do
|
||||
add_text(nomsu, bit)
|
||||
else
|
||||
nomsu:append("\\")
|
||||
local interp_nomsu = recurse(bit, #tostring(nomsu):match('[^\n]*$'))
|
||||
local interp_nomsu = recurse(bit, #nomsu:match('[^\n]*$'))
|
||||
if not (interp_nomsu:is_multiline()) then
|
||||
if bit.type == "Var" then
|
||||
if type(tree[i + 1]) == 'string' and not match(tree[i + 1], "^[ \n\t,.:;#(){}[%]]") then
|
||||
@ -1193,7 +1210,7 @@ do
|
||||
end
|
||||
local nomsu = NomsuCode(tree.source)
|
||||
add_text(nomsu, tree)
|
||||
if nomsu:is_multiline() and tostring(nomsu):match("\n$") then
|
||||
if nomsu:is_multiline() and nomsu:match("\n$") then
|
||||
nomsu:append('\\("")')
|
||||
end
|
||||
return NomsuCode(tree.source, '".."\n ', nomsu)
|
||||
@ -1205,7 +1222,7 @@ do
|
||||
nomsu:append(pop_comments(item.source.start))
|
||||
end
|
||||
local inline_nomsu = self:tree_to_inline_nomsu(item)
|
||||
local item_nomsu = #tostring(inline_nomsu) <= MAX_LINE and inline_nomsu or recurse(item, #tostring(nomsu):match('[^\n]*$'))
|
||||
local item_nomsu = #tostring(inline_nomsu) <= MAX_LINE and inline_nomsu or recurse(item, #nomsu:match('[^\n]*$'))
|
||||
nomsu:append(item_nomsu)
|
||||
if i < #tree then
|
||||
nomsu:append((item_nomsu:is_multiline() or nomsu:trailing_line_len() + #tostring(item_nomsu) >= MAX_LINE) and '\n' or ', ')
|
||||
|
@ -29,20 +29,17 @@ export SOURCE_MAP
|
||||
SOURCE_MAP = {}
|
||||
|
||||
string.as_lua_id = (str)->
|
||||
argnum = 0
|
||||
-- Cut up escape-sequence-like chunks
|
||||
str = gsub str, "x([0-9A-F][0-9A-F])", "x\0%1"
|
||||
-- Alphanumeric unchanged, spaces to underscores, and everything else to hex escape sequences
|
||||
str = gsub str, "%W", (c)->
|
||||
if c == ' ' then '_'
|
||||
elseif c == '%' then
|
||||
argnum += 1
|
||||
tostring(argnum)
|
||||
else format("x%02X", byte(c))
|
||||
return '_'..str
|
||||
|
||||
table.map = (fn)=> [fn(v) for _,v in ipairs(@)]
|
||||
table.fork = (t, values)-> setmetatable(values or {}, {__index:t})
|
||||
table.copy = (t)-> setmetatable({k,v for k,v in pairs(t)}, getmetatable(t))
|
||||
|
||||
-- TODO:
|
||||
-- consider non-linear codegen, rather than doing thunks for things like comprehensions
|
||||
@ -99,6 +96,7 @@ NomsuCompiler = setmetatable {name:"Nomsu"},
|
||||
with NomsuCompiler
|
||||
.NOMSU_COMPILER_VERSION = 5
|
||||
.NOMSU_SYNTAX_VERSION = Parser.version
|
||||
.nomsu = NomsuCompiler
|
||||
.parse = (...)=> Parser.parse(...)
|
||||
.can_optimize = -> false
|
||||
|
||||
@ -142,7 +140,7 @@ with NomsuCompiler
|
||||
-- This is a bit of a hack, but this code handles arbitrarily complex
|
||||
-- math expressions like 2*x + 3^2 without having to define a single
|
||||
-- action for every possibility.
|
||||
math_expression = re.compile [[ ([+-] " ")* "%" (" " [*/^+-] (" " [+-])* " %")+ !. ]]
|
||||
math_expression = re.compile [[ ([+-] " ")* [0-9]+ (" " [*/^+-] (" " [+-])* " " [0-9]+)+ !. ]]
|
||||
|
||||
add_lua_bits = (val_or_stmt, code)=>
|
||||
cls = val_or_stmt == "value" and LuaCode.Value or LuaCode
|
||||
@ -167,7 +165,7 @@ with NomsuCompiler
|
||||
if code.type != "Text"
|
||||
return LuaCode.Value(code.source, cls_str, repr(tostring(code.source)), ", ", @compile(code), ")")
|
||||
add_bit_lua = (lua, bit_lua)->
|
||||
bit_leading_len = #(tostring(bit_lua)\match("^[^\n]*"))
|
||||
bit_leading_len = #(bit_lua\match("^[^\n]*"))
|
||||
lua\append(lua\trailing_line_len! + bit_leading_len > MAX_LINE and ",\n " or ", ")
|
||||
lua\append(bit_lua)
|
||||
operate_on_text = (text)->
|
||||
@ -204,31 +202,31 @@ with NomsuCompiler
|
||||
lua\append " "
|
||||
return lua
|
||||
|
||||
["Lua %"]: (tree, _code)=>
|
||||
["Lua 1"]: (tree, _code)=>
|
||||
return add_lua_string_bits(@, 'statements', _code)
|
||||
|
||||
["Lua value %"]: (tree, _code)=>
|
||||
["Lua value 1"]: (tree, _code)=>
|
||||
return add_lua_string_bits(@, 'value', _code)
|
||||
|
||||
["lua > %"]: (tree, _code)=>
|
||||
["lua > 1"]: (tree, _code)=>
|
||||
if _code.type != "Text"
|
||||
return LuaCode tree.source, "_ENV:run_lua(", @compile(_code), ");"
|
||||
return LuaCode tree.source, "nomsu:run_lua(", @compile(_code), ");"
|
||||
return add_lua_bits(@, "statements", _code)
|
||||
|
||||
["= lua %"]: (tree, _code)=>
|
||||
["= lua 1"]: (tree, _code)=>
|
||||
if _code.type != "Text"
|
||||
return LuaCode.Value tree.source, "_ENV:run_lua(", @compile(_code), ":as_statements('return '))"
|
||||
return LuaCode.Value tree.source, "nomsu:run_lua(", @compile(_code), ":as_statements('return '))"
|
||||
return add_lua_bits(@, "value", _code)
|
||||
|
||||
["use %"]: (tree, _path)=>
|
||||
["use 1"]: (tree, _path)=>
|
||||
if _path.type == 'Text' and #_path == 1 and type(_path[1]) == 'string'
|
||||
path = _path[1]
|
||||
for _,f in Files.walk(path)
|
||||
@run_file(f)
|
||||
return LuaCode(tree.source, "for i,f in Files.walk(", @compile(_path), ") do _ENV:run_file(f) end")
|
||||
return LuaCode(tree.source, "for i,f in Files.walk(", @compile(_path), ") do nomsu:run_file(f) end")
|
||||
|
||||
["tests"]: (tree)=> LuaCode.Value(tree.source, "TESTS")
|
||||
["test %"]: (tree, _body)=>
|
||||
["test 1"]: (tree, _body)=>
|
||||
test_str = table.concat [tostring(@tree_to_nomsu(line)) for line in *_body], "\n"
|
||||
LuaCode tree.source, "TESTS[#{repr(tostring(tree.source))}] = ", repr(test_str)
|
||||
|
||||
@ -304,7 +302,7 @@ with NomsuCompiler
|
||||
|
||||
.run_lua = (lua, source=nil)=>
|
||||
lua_string = tostring(lua)
|
||||
run_lua_fn, err = load(lua_string, tostring(source or lua.source), "t", self)
|
||||
run_lua_fn, err = load(lua_string, nil and tostring(source or lua.source), "t", self)
|
||||
if not run_lua_fn
|
||||
line_numbered_lua = concat(
|
||||
[format("%3d|%s",i,line) for i, line in ipairs Files.get_lines(lua_string)],
|
||||
@ -317,7 +315,8 @@ with NomsuCompiler
|
||||
file = Files.read(source.filename)
|
||||
if not file
|
||||
error "Failed to find file: #{source.filename}"
|
||||
nomsu_str = tostring(file\sub(source.start, source.stop))
|
||||
nomsu_str = file\sub(source.start, source.stop)
|
||||
assert type(nomsu_str) == 'string'
|
||||
lua_line = 1
|
||||
nomsu_line = Files.get_line_number(file, source.start)
|
||||
map_sources = (s)->
|
||||
@ -373,15 +372,28 @@ with NomsuCompiler
|
||||
insert args, arg_lua
|
||||
lua\concat_append args, ", "
|
||||
lua\append ")"
|
||||
if tree.target
|
||||
target_lua = @compile(tree.target)
|
||||
lua\prepend(target_lua, ":")
|
||||
return lua
|
||||
|
||||
when "EscapedNomsu"
|
||||
lua = LuaCode.Value tree.source, tree[1].type, "("
|
||||
bits = if tree[1].type == "EscapedNomsu" then {@compile(tree[1])}
|
||||
else [AST.is_syntax_tree(bit) and @compile(bit) or repr(bit) for bit in *tree[1]]
|
||||
insert bits, 1, repr(tostring tree[1].source)
|
||||
lua\concat_append bits, ", "
|
||||
lua\append ")"
|
||||
lua = LuaCode.Value tree.source, tree[1].type, "{"
|
||||
needs_comma, i = false, 1
|
||||
for k,v in pairs(AST.is_syntax_tree(tree[1], "EscapedNomsu") and tree or tree[1])
|
||||
if needs_comma then lua\append ", "
|
||||
else needs_comma = true
|
||||
if k == i
|
||||
i += 1
|
||||
elseif type(k) == 'string' and match(k,"[_a-zA-Z][_a-zA-Z0-9]*")
|
||||
lua\append(k, "= ")
|
||||
else
|
||||
lua\append("[", (AST.is_syntax_tree(k) and @compile(k) or repr(k)), "]= ")
|
||||
if k == "source"
|
||||
lua\append repr(tostring(v))
|
||||
else
|
||||
lua\append(AST.is_syntax_tree(v) and @compile(v) or repr(v))
|
||||
lua\append "}"
|
||||
return lua
|
||||
|
||||
when "Block"
|
||||
@ -442,7 +454,7 @@ with NomsuCompiler
|
||||
@compile_error tree[2].source,
|
||||
"Cannot use:\n%s\nas a dict value, since it's not an expression."
|
||||
-- TODO: support arbitrary words here, like operators and unicode
|
||||
key_str = match(tostring(key_lua), [=[["']([a-zA-Z_][a-zA-Z0-9_]*)['"]]=])
|
||||
key_str = match(tostring(key_lua), [=[^["']([a-zA-Z_][a-zA-Z0-9_]*)['"]$]=])
|
||||
return if key_str
|
||||
LuaCode tree.source, key_str,"=",value_lua
|
||||
elseif sub(tostring(key_lua),1,1) == "["
|
||||
@ -502,6 +514,8 @@ with NomsuCompiler
|
||||
|
||||
when "Action"
|
||||
nomsu = NomsuCode(tree.source)
|
||||
if tree.target
|
||||
nomsu\append @tree_to_inline_nomsu(tree.target), "::"
|
||||
for i,bit in ipairs tree
|
||||
if type(bit) == "string"
|
||||
clump_words = (type(tree[i-1]) == 'string' and Parser.is_operator(bit) != Parser.is_operator(tree[i-1]))
|
||||
@ -509,7 +523,7 @@ with NomsuCompiler
|
||||
nomsu\append bit
|
||||
else
|
||||
arg_nomsu = recurse(bit, nomsu, parenthesize_blocks or (i == 1 or i < #tree))
|
||||
nomsu\append " " unless tostring(arg_nomsu)\match("^:") or i == 1
|
||||
nomsu\append " " unless arg_nomsu\match("^:") or i == 1
|
||||
arg_nomsu\parenthesize! if bit.type == "Action"
|
||||
nomsu\append arg_nomsu
|
||||
check(len, nomsu, tree) if check
|
||||
@ -656,9 +670,9 @@ with NomsuCompiler
|
||||
nomsu = NomsuCode(tree.source, pop_comments(tree.source.start))
|
||||
should_clump = (prev_line, line)->
|
||||
if prev_line.type == "Action" and line.type == "Action"
|
||||
if prev_line.stub == "use %" then return line.stub == "use %"
|
||||
if prev_line.stub == "test %" then return true
|
||||
if line.stub == "test %" then return false
|
||||
if prev_line.stub == "use 1" then return line.stub == "use 1"
|
||||
if prev_line.stub == "test 1" then return true
|
||||
if line.stub == "test 1" then return false
|
||||
return not recurse(prev_line)\is_multiline!
|
||||
for chunk_no, chunk in ipairs tree
|
||||
nomsu\append "\n\n#{("~")\rep(80)}\n\n" if chunk_no > 1
|
||||
@ -675,12 +689,14 @@ with NomsuCompiler
|
||||
else
|
||||
nomsu\append recurse(chunk)
|
||||
nomsu\append pop_comments(tree.source.stop, '\n')
|
||||
nomsu\append('\n') unless tostring(nomsu)\match("\n$")
|
||||
nomsu\append('\n') unless nomsu\match("\n$")
|
||||
return nomsu
|
||||
|
||||
when "Action"
|
||||
pos, next_space = tree.source.start, ''
|
||||
nomsu = NomsuCode(tree.source, pop_comments(pos))
|
||||
if tree.target
|
||||
nomsu\append @tree_to_nomsu(tree.target), "::"
|
||||
for i,bit in ipairs tree
|
||||
if next_space == "\n.." or (next_space == " " and nomsu\trailing_line_len! > MAX_LINE)
|
||||
nomsu\append "\n", pop_comments(pos), '..'
|
||||
@ -692,12 +708,12 @@ with NomsuCompiler
|
||||
nomsu\append bit
|
||||
next_space = ' '
|
||||
elseif bit.type == "Block"
|
||||
nomsu\append(recurse(bit, #tostring(nomsu)\match('[^\n]*$')))
|
||||
nomsu\append(recurse(bit, #nomsu\match('[^\n]*$')))
|
||||
pos = bit.source.stop
|
||||
next_space = inline and " " or "\n.."
|
||||
else
|
||||
nomsu\append next_space
|
||||
bit_nomsu = recurse(bit, #tostring(nomsu)\match('[^\n]*$'))
|
||||
bit_nomsu = recurse(bit, #nomsu\match('[^\n]*$'))
|
||||
if bit.type == "Action" and not bit_nomsu\is_multiline!
|
||||
bit_nomsu\parenthesize!
|
||||
nomsu\append bit_nomsu
|
||||
@ -717,7 +733,7 @@ with NomsuCompiler
|
||||
line_nomsu = recurse(line)
|
||||
nomsu\append line_nomsu
|
||||
if i < #tree
|
||||
nomsu\append(tostring(line_nomsu)\match('\n[^\n]*\n') and "\n\n" or "\n")
|
||||
nomsu\append(line_nomsu\match('\n[^\n]*\n') and "\n\n" or "\n")
|
||||
nomsu\append pop_comments(tree.source.stop, '\n')
|
||||
return NomsuCode(tree.source, ":\n ", nomsu)
|
||||
|
||||
@ -749,7 +765,7 @@ with NomsuCompiler
|
||||
add_text(nomsu, bit)
|
||||
else
|
||||
nomsu\append "\\"
|
||||
interp_nomsu = recurse(bit, #tostring(nomsu)\match('[^\n]*$'))
|
||||
interp_nomsu = recurse(bit, #nomsu\match('[^\n]*$'))
|
||||
unless interp_nomsu\is_multiline!
|
||||
if bit.type == "Var"
|
||||
if type(tree[i+1]) == 'string' and not match(tree[i+1], "^[ \n\t,.:;#(){}[%]]")
|
||||
@ -761,7 +777,7 @@ with NomsuCompiler
|
||||
nomsu\append "\n.."
|
||||
nomsu = NomsuCode(tree.source)
|
||||
add_text(nomsu, tree)
|
||||
if nomsu\is_multiline! and tostring(nomsu)\match("\n$")
|
||||
if nomsu\is_multiline! and nomsu\match("\n$")
|
||||
nomsu\append '\\("")' -- Need to specify where the text ends
|
||||
return NomsuCode(tree.source, '".."\n ', nomsu)
|
||||
|
||||
@ -771,7 +787,7 @@ with NomsuCompiler
|
||||
for i, item in ipairs tree
|
||||
nomsu\append(pop_comments(item.source.start)) if nomsu\trailing_line_len! == 0
|
||||
inline_nomsu = @tree_to_inline_nomsu(item)
|
||||
item_nomsu = #tostring(inline_nomsu) <= MAX_LINE and inline_nomsu or recurse(item, #tostring(nomsu)\match('[^\n]*$'))
|
||||
item_nomsu = #tostring(inline_nomsu) <= MAX_LINE and inline_nomsu or recurse(item, #nomsu\match('[^\n]*$'))
|
||||
nomsu\append item_nomsu
|
||||
if i < #tree
|
||||
nomsu\append((item_nomsu\is_multiline! or nomsu\trailing_line_len! + #tostring(item_nomsu) >= MAX_LINE) and '\n' or ', ')
|
||||
|
@ -113,7 +113,7 @@ setmetatable(NOMSU_DEFS, {
|
||||
end
|
||||
})
|
||||
local Parser = {
|
||||
version = 2,
|
||||
version = 3,
|
||||
patterns = { }
|
||||
}
|
||||
do
|
||||
|
@ -74,7 +74,7 @@ setmetatable(NOMSU_DEFS, {__index:(key)=>
|
||||
return make_node
|
||||
})
|
||||
|
||||
Parser = {version:2, patterns:{}}
|
||||
Parser = {version:3, patterns:{}}
|
||||
do
|
||||
-- Just for cleanliness, I put the language spec in its own file using a slightly modified
|
||||
-- version of the lpeg.re syntax.
|
||||
|
114
syntax_tree.lua
114
syntax_tree.lua
@ -26,7 +26,8 @@ local types = {
|
||||
"DictEntry",
|
||||
"IndexChain",
|
||||
"Action",
|
||||
"FileChunks"
|
||||
"FileChunks",
|
||||
"Method"
|
||||
}
|
||||
for _index_0 = 1, #types do
|
||||
local name = types[_index_0]
|
||||
@ -40,20 +41,14 @@ for _index_0 = 1, #types do
|
||||
return getmetatable(x) == self
|
||||
end
|
||||
cls.__tostring = function(self)
|
||||
local args = {
|
||||
tostring(self.source),
|
||||
unpack(self)
|
||||
}
|
||||
return tostring(self.type) .. "(" .. tostring(concat((function()
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for _index_1 = 1, #args do
|
||||
local v = args[_index_1]
|
||||
_accum_0[_len_0] = repr(v)
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
return _accum_0
|
||||
end)(), ', ')) .. ")"
|
||||
return tostring(self.type) .. tostring(repr(self, (function(x)
|
||||
return Source:is_instance(x) and tostring(x) or nil
|
||||
end)))
|
||||
end
|
||||
cls.__repr = function(self)
|
||||
return tostring(self.type) .. tostring(repr(self, (function(x)
|
||||
return Source:is_instance(x) and tostring(x) or nil
|
||||
end)))
|
||||
end
|
||||
cls.map = function(self, fn)
|
||||
local replacement = fn(self)
|
||||
@ -61,14 +56,33 @@ for _index_0 = 1, #types do
|
||||
return nil
|
||||
end
|
||||
if replacement then
|
||||
replacement = (replacement.__class)(self.source, unpack(replacement))
|
||||
if AST.is_syntax_tree(replacement) then
|
||||
replacement = setmetatable((function()
|
||||
local _tbl_0 = { }
|
||||
for k, v in pairs(replacement) do
|
||||
_tbl_0[k] = v
|
||||
end
|
||||
return _tbl_0
|
||||
end)(), getmetatable(replacement))
|
||||
replacement.source = self.source
|
||||
if self.comments then
|
||||
replacement.comments = {
|
||||
unpack(self.comments)
|
||||
}
|
||||
end
|
||||
end
|
||||
else
|
||||
local replacements = { }
|
||||
replacement = {
|
||||
source = self.source,
|
||||
comments = self.comments and {
|
||||
unpack(self.comments)
|
||||
}
|
||||
}
|
||||
local changes = false
|
||||
for i, v in ipairs(self) do
|
||||
for k, v in pairs(self) do
|
||||
local _continue_0 = false
|
||||
repeat
|
||||
replacements[#replacements + 1] = v
|
||||
replacement[k] = v
|
||||
if AST.is_syntax_tree(v) then
|
||||
local r = v:map(fn)
|
||||
if r == v or r == nil then
|
||||
@ -76,7 +90,7 @@ for _index_0 = 1, #types do
|
||||
break
|
||||
end
|
||||
changes = true
|
||||
replacements[#replacements] = r
|
||||
replacement[k] = r
|
||||
end
|
||||
_continue_0 = true
|
||||
until true
|
||||
@ -87,20 +101,7 @@ for _index_0 = 1, #types do
|
||||
if not (changes) then
|
||||
return self
|
||||
end
|
||||
replacement = (self.__class)(self.source, unpack(replacements))
|
||||
end
|
||||
if self.comments then
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
local _list_0 = self.comments
|
||||
for _index_1 = 1, #_list_0 do
|
||||
local c = _list_0[_index_1]
|
||||
_accum_0[_len_0] = c
|
||||
_len_0 = _len_0 + 1
|
||||
end
|
||||
replacement.comments = _accum_0
|
||||
end
|
||||
replacement = setmetatable(replacement, getmetatable(self))
|
||||
end
|
||||
return replacement
|
||||
end
|
||||
@ -113,6 +114,9 @@ for _index_0 = 1, #types do
|
||||
return false
|
||||
end
|
||||
end
|
||||
if self.target ~= other.target then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
@ -120,37 +124,31 @@ for _index_0 = 1, #types do
|
||||
__tostring = function(self)
|
||||
return self.__name
|
||||
end,
|
||||
__call = function(self, source, ...)
|
||||
if type(source) == 'string' then
|
||||
source = Source:from_string(source)
|
||||
__call = function(self, t)
|
||||
if type(t.source) == 'string' then
|
||||
t.source = Source:from_string(t.source)
|
||||
else
|
||||
assert(Source:is_instance(t.source))
|
||||
end
|
||||
for i = 1, select('#', ...) do
|
||||
assert(select(i, ...))
|
||||
setmetatable(t, self)
|
||||
if t.__init then
|
||||
t:__init()
|
||||
end
|
||||
assert(Source:is_instance(source))
|
||||
local inst = {
|
||||
source = source,
|
||||
...
|
||||
}
|
||||
setmetatable(inst, self)
|
||||
if inst.__init then
|
||||
inst:__init()
|
||||
end
|
||||
return inst
|
||||
return t
|
||||
end
|
||||
})
|
||||
end
|
||||
AST.Action.__init = function(self)
|
||||
local stub_bits
|
||||
do
|
||||
local _accum_0 = { }
|
||||
local _len_0 = 1
|
||||
for _index_0 = 1, #self do
|
||||
local a = self[_index_0]
|
||||
_accum_0[_len_0] = type(a) == 'string' and a or '%'
|
||||
_len_0 = _len_0 + 1
|
||||
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
|
||||
stub_bits = _accum_0
|
||||
end
|
||||
self.stub = concat(stub_bits, " ")
|
||||
end
|
||||
|
@ -10,7 +10,7 @@ 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"}
|
||||
"IndexChain", "Action", "FileChunks", "Method"}
|
||||
for name in *types
|
||||
cls = {}
|
||||
with cls
|
||||
@ -19,49 +19,57 @@ for name in *types
|
||||
.__name = name
|
||||
.type = name
|
||||
.is_instance = (x)=> getmetatable(x) == @
|
||||
.__tostring = =>
|
||||
args = {tostring(@source), unpack(@)}
|
||||
"#{@type}(#{concat([repr(v) for v in *args], ', ')})"
|
||||
.__tostring = => "#{@type}#{repr @, ((x)-> Source\is_instance(x) and tostring(x) or nil)}"
|
||||
.__repr = => "#{@type}#{repr @, ((x)-> Source\is_instance(x) and tostring(x) or nil)}"
|
||||
.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))
|
||||
-- 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
|
||||
else
|
||||
replacements = {}
|
||||
replacement = {source:@source, comments:@comments and {unpack(@comments)}}
|
||||
changes = false
|
||||
for i,v in ipairs(@)
|
||||
replacements[#replacements+1] = v
|
||||
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
|
||||
replacements[#replacements] = r
|
||||
replacement[k] = r
|
||||
return @ unless changes
|
||||
replacement = (@__class)(@source, unpack(replacements))
|
||||
replacement.comments = [c for c in *@comments] if @comments
|
||||
replacement = setmetatable replacement, getmetatable(@)
|
||||
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
|
||||
|
||||
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
|
||||
__call: (t)=>
|
||||
if type(t.source) == 'string'
|
||||
t.source = Source\from_string(t.source)
|
||||
else
|
||||
assert(Source\is_instance(t.source))
|
||||
setmetatable(t, @)
|
||||
if t.__init then t\__init!
|
||||
return t
|
||||
|
||||
AST.Action.__init = =>
|
||||
stub_bits = [type(a) == 'string' and a or '%' for a in *@]
|
||||
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
|
||||
@stub = concat stub_bits, " "
|
||||
|
||||
AST.Action.get_args = =>
|
||||
|
51
utils.lua
51
utils.lua
@ -23,32 +23,34 @@ local function size(t)
|
||||
return n
|
||||
end
|
||||
|
||||
local function repr(x, depth)
|
||||
local repr_behavior = function(x)
|
||||
local mt = getmetatable(x)
|
||||
if mt then
|
||||
local fn = rawget(mt, "__repr")
|
||||
if fn then return fn(x) end
|
||||
end
|
||||
end
|
||||
local function repr(x, mt_behavior)
|
||||
-- Create a string representation of the object that is close to the lua code that will
|
||||
-- reproduce the object (similar to Python's "repr" function)
|
||||
depth = depth or 10
|
||||
if depth == 0 then return "..." end
|
||||
depth = depth - 1
|
||||
mt_behavior = mt_behavior or repr_behavior
|
||||
local x_type = type(x)
|
||||
if x_type == 'table' then
|
||||
if getmetatable(x) then
|
||||
-- If this object has a weird metatable, then don't pretend like it's a regular table
|
||||
return tostring(x)
|
||||
else
|
||||
local ret = {}
|
||||
local i = 1
|
||||
for k, v in pairs(x) do
|
||||
if k == i then
|
||||
ret[#ret+1] = repr(x[i], depth)
|
||||
i = i + 1
|
||||
elseif type(k) == 'string' and match(k,"[_a-zA-Z][_a-zA-Z0-9]*") then
|
||||
ret[#ret+1] = k.."= "..repr(v,depth)
|
||||
else
|
||||
ret[#ret+1] = "["..repr(k,depth).."]= "..repr(v,depth)
|
||||
end
|
||||
local ret = mt_behavior(x)
|
||||
if ret then return ret end
|
||||
local ret = {}
|
||||
local i = 1
|
||||
for k, v in pairs(x) do
|
||||
if k == i then
|
||||
ret[#ret+1] = repr(v, mt_behavior)
|
||||
i = i + 1
|
||||
elseif type(k) == 'string' and match(k,"[_a-zA-Z][_a-zA-Z0-9]*") then
|
||||
ret[#ret+1] = k.."= "..repr(v, mt_behavior)
|
||||
else
|
||||
ret[#ret+1] = "["..repr(k, mt_behavior).."]= "..repr(v, mt_behavior)
|
||||
end
|
||||
return "{"..table.concat(ret, ", ").."}"
|
||||
end
|
||||
return "{"..table.concat(ret, ", ").."}"
|
||||
elseif x_type == 'string' then
|
||||
local escaped = gsub(x, "\\", "\\\\")
|
||||
escaped = gsub(escaped, "\n", "\\n")
|
||||
@ -60,11 +62,18 @@ local function repr(x, depth)
|
||||
end
|
||||
end
|
||||
|
||||
local stringify_behavior = function(x)
|
||||
local mt = getmetatable(x)
|
||||
if mt then
|
||||
local fn = rawget(mt, "__tostring")
|
||||
if fn then return fn(x) end
|
||||
end
|
||||
end
|
||||
local function stringify(x)
|
||||
if type(x) == 'string' then
|
||||
return x
|
||||
else
|
||||
return repr(x)
|
||||
return repr(x, stringify_behavior)
|
||||
end
|
||||
end
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user