aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bitbucket@bruce-hill.com>2018-06-12 15:12:27 -0700
committerBruce Hill <bitbucket@bruce-hill.com>2018-06-12 15:14:07 -0700
commitb5fb8933af283d2b873a03ded085e8b69f374e81 (patch)
tree5cf00c528eb339d5a9f4491652f8a9a0601d943b
parent0c9973ff0363e400d3d284339b77197c40c3f60c (diff)
Removed dependency on 'immutable' library. This lets LuaJIT do more
aggressive optimizations and generally helps performance. Some safety is lost, but I think the performance gains, reduced complexity, and reduced dependencies are worth it.
-rw-r--r--README.md2
-rw-r--r--code_obj.lua111
-rw-r--r--code_obj.moon24
-rw-r--r--core/metaprogramming.nom6
-rw-r--r--nomsu.lua32
-rwxr-xr-xnomsu.moon31
-rw-r--r--nomsu.peg4
-rw-r--r--nomsu_tree.lua197
-rw-r--r--nomsu_tree.moon96
-rw-r--r--tests/metaprogramming.nom4
10 files changed, 245 insertions, 262 deletions
diff --git a/README.md b/README.md
index 40c70aa..f1c615d 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ revolving around natural language rule-making and self modification.
## Dependencies
-Nomsu's dependencies are [Lua 5.2 or later](https://www.lua.org/) (tested with version 5.2.4) (or [Luajit](http://luajit.org/) (tested with version 2.1.0)), [LPEG](http://www.inf.puc-rio.br/~roberto/lpeg/) (`luarocks install lpeg`), and [Lua-Immutable](https://bitbucket.org/spilt/lua-immutable). Nomsu's compiler was written in [Moonscript](http://moonscript.org/), but all of the .moon files have been compiled into lua for convenience, so Moonscript is not a dependency.
+Nomsu's only dependencies are [Lua 5.2 or later](https://www.lua.org/) (tested with version 5.2.4) (or [Luajit](http://luajit.org/) (tested with version 2.1.0)) and [LPEG](http://www.inf.puc-rio.br/~roberto/lpeg/) (`luarocks install lpeg`). Nomsu's compiler was written in [Moonscript](http://moonscript.org/), but all of the .moon files have been compiled into lua for convenience, so Moonscript is not a dependency.
## Usage
diff --git a/code_obj.lua b/code_obj.lua
index fa36234..47e08cb 100644
--- a/code_obj.lua
+++ b/code_obj.lua
@@ -3,55 +3,76 @@ do
local _obj_0 = table
insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat
end
-local immutable = require('immutable')
local Lua, Source
-Source = immutable({
- "filename",
- "start",
- "stop"
-}, {
- name = "Source",
- from_string = function(self, str)
+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
+ 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
+ end,
+ __lt = function(self, other)
+ assert(self.filename == other.filename, "Cannot compare sources from different files")
+ if self.start == other.start then
+ return (self.stop or self.start) < (other.stop or other.start)
+ else
+ return self.start < other.start
+ end
+ end,
+ __le = function(self, other)
+ assert(self.filename == other.filename, "Cannot compare sources from different files")
+ if self.start == other.start then
+ return (self.stop or self.start) <= (other.stop or other.start)
+ else
+ return self.start <= other.start
+ end
+ end,
+ __add = function(self, offset)
+ if type(self) == 'number' then
+ offset, self = self, offset
+ else
+ if type(offset) ~= 'number' then
+ error("Cannot add Source and " .. tostring(type(offset)))
+ end
+ end
+ return Source(self.filename, self.start + offset, self.stop)
+ end
+ }
+ _base_0.__index = _base_0
+ _class_0 = setmetatable({
+ __init = function(self, filename, start, stop)
+ self.filename, self.start, self.stop = filename, start, stop
+ end,
+ __base = _base_0,
+ __name = "Source"
+ }, {
+ __index = _base_0,
+ __call = function(cls, ...)
+ local _self_0 = setmetatable({}, _base_0)
+ cls.__init(_self_0, ...)
+ return _self_0
+ end
+ })
+ _base_0.__class = _class_0
+ local self = _class_0
+ self.from_string = function(self, str)
local filename, start, stop = str:match("^@(.-)%[(%d+):(%d+)%]$")
if not (filename) then
filename, start = str:match("^@(.-)%[(%d+)%]$")
end
- return Source(filename or str, tonumber(start or 1), tonumber(stop))
- end,
- __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
- end,
- __lt = function(self, other)
- assert(self.filename == other.filename, "Cannot compare sources from different files")
- if self.start == other.start then
- return (self.stop or self.start) < (other.stop or other.start)
- else
- return self.start < other.start
- end
- end,
- __le = function(self, other)
- assert(self.filename == other.filename, "Cannot compare sources from different files")
- if self.start == other.start then
- return (self.stop or self.start) <= (other.stop or other.start)
- else
- return self.start <= other.start
- end
- end,
- __add = function(self, offset)
- if type(self) == 'number' then
- offset, self = self, offset
- else
- if type(offset) ~= 'number' then
- error("Cannot add Source and " .. tostring(type(offset)))
- end
- end
- return Source(self.filename, self.start + offset, self.stop)
+ return self(filename or str, tonumber(start or 1), tonumber(stop))
end
-})
+ self.is_instance = function(self, x)
+ return type(x) == 'table' and x.__class == self
+ end
+ Source = _class_0
+end
local Code
do
local _class_0
@@ -150,7 +171,7 @@ do
end
for _index_0 = 1, #vars do
local var = vars[_index_0]
- assert(type(var) == 'userdata' and var.type == "Var")
+ assert(var.type == "Var")
if not (seen[var]) then
self.free_vars[#self.free_vars + 1] = var
seen[var] = true
@@ -165,7 +186,7 @@ do
local removals = { }
for _index_0 = 1, #vars do
local var = vars[_index_0]
- assert(type(var) == 'userdata' and var.type == "Var")
+ assert(var.type == "Var")
removals[var.value] = true
end
local stack = {
diff --git a/code_obj.moon b/code_obj.moon
index a810dc7..45df615 100644
--- a/code_obj.moon
+++ b/code_obj.moon
@@ -2,38 +2,46 @@
-- build up generated code, while keeping track of where it came from, and managing
-- indentation levels.
{:insert, :remove, :concat} = table
-immutable = require 'immutable'
local Lua, Source
export LINE_STARTS
-Source = immutable {"filename","start","stop"}, {
- name:"Source"
- from_string: (str)=>
+class Source
+ new: (@filename, @start, @stop)=>
+
+ @from_string: (str)=>
filename,start,stop = str\match("^@(.-)%[(%d+):(%d+)%]$")
unless filename
filename,start = str\match("^@(.-)%[(%d+)%]$")
- return Source(filename or str, tonumber(start or 1), tonumber(stop))
+ return @(filename or str, tonumber(start or 1), tonumber(stop))
+
+ @is_instance: (x)=> type(x) == 'table' and x.__class == @
+
__tostring: =>
if @stop
"@#{@filename}[#{@start}:#{@stop}]"
else
"@#{@filename}[#{@start}]"
+
+ __eq: (other)=>
+ getmetatable(@) == getmetatable(other) and @filename == other.filename and @start == other.start and @stop == other.stop
+
__lt: (other)=>
assert(@filename == other.filename, "Cannot compare sources from different files")
return if @start == other.start
(@stop or @start) < (other.stop or other.start)
else @start < other.start
+
__le: (other)=>
assert(@filename == other.filename, "Cannot compare sources from different files")
return if @start == other.start
(@stop or @start) <= (other.stop or other.start)
else @start <= other.start
+
__add: (offset)=>
if type(self) == 'number'
offset, self = self, offset
else if type(offset) != 'number' then error("Cannot add Source and #{type(offset)}")
return Source(@filename, @start+offset, @stop)
-}
class Code
new: (@source, ...)=>
@@ -90,7 +98,7 @@ class Lua extends Code
return unless #vars > 0
seen = {[v]:true for v in *@free_vars}
for var in *vars
- assert(type(var) == 'userdata' and var.type == "Var")
+ assert(var.type == "Var")
unless seen[var]
@free_vars[#@free_vars+1] = var
seen[var] = true
@@ -100,7 +108,7 @@ class Lua extends Code
return unless #vars > 0
removals = {}
for var in *vars
- assert(type(var) == 'userdata' and var.type == "Var")
+ assert(var.type == "Var")
removals[var.value] = true
stack = {self}
diff --git a/core/metaprogramming.nom b/core/metaprogramming.nom
index 83293c1..ec65f3e 100644
--- a/core/metaprogramming.nom
+++ b/core/metaprogramming.nom
@@ -94,14 +94,14 @@ immediately
return replacements[t.value]
elseif t.type == 'Var' then
return t.type.."("..repr(tostring(t.source))..", "..repr(t.value.."#"..tostring(MANGLE_INDEX))..")"
- elseif t.is_multi then
+ elseif t.value then
+ return t.type.."("..repr(tostring(t.source))..", "..repr(t.value)..")"
+ else
local bits = {repr(tostring(t.source))}
for i, entry in ipairs(t) do
bits[#bits+1] = make_tree(entry)
end
return t.type.."("..table.concat(bits, ", ")..")"
- else
- return t.type.."("..repr(tostring(t.source))..", "..repr(t.value)..")"
end
end
lua:append(")\n local tree = ", make_tree(\%longhand), "\n return nomsu:tree_to_lua(tree)\nend);")
diff --git a/nomsu.lua b/nomsu.lua
index 646b9af..e9d394f 100644
--- a/nomsu.lua
+++ b/nomsu.lua
@@ -34,10 +34,6 @@ local P, R, V, S, Cg, C, Cp, B, Cmt, Carg
P, R, V, S, Cg, C, Cp, B, Cmt, Carg = lpeg.P, lpeg.R, lpeg.V, lpeg.S, lpeg.Cg, lpeg.C, lpeg.Cp, lpeg.B, lpeg.Cmt, lpeg.Carg
local utils = require('utils')
local new_uuid = require('uuid')
-local immutable = require('immutable')
-Tuple = immutable(nil, {
- name = "Tuple"
-})
local repr, stringify, min, max, equivalent, set, is_list, sum
repr, stringify, min, max, equivalent, set, is_list, sum = utils.repr, utils.stringify, utils.min, utils.max, utils.equivalent, utils.set, utils.is_list, utils.sum
local colors = setmetatable({ }, {
@@ -163,7 +159,7 @@ do
end
end
end
-local Types = require("nomsu_tree")
+local AST = require("nomsu_tree")
local NOMSU_DEFS
do
local _with_0 = { }
@@ -249,13 +245,12 @@ setmetatable(NOMSU_DEFS, {
local _with_0 = userdata.source
source = Source(_with_0.filename, _with_0.start + start - 1, _with_0.start + stop - 1)
end
- local tree
- if Types[key].is_multi then
- tree = Types[key](source, unpack(value))
- else
- tree = Types[key](source, value)
+ value.source = source
+ setmetatable(value, AST[key])
+ if value.__init then
+ value:__init()
end
- return tree
+ return value
end
self[key] = make_node
return make_node
@@ -444,7 +439,7 @@ do
run_lua = function(self, lua)
assert(type(lua) ~= 'string', "Attempt to run lua string instead of Lua (object)")
local lua_string = tostring(lua)
- local run_lua_fn, err = load(lua_string, tostring(lua.source), "t", self.environment)
+ local run_lua_fn, err = load(lua_string, nil and tostring(lua.source), "t", self.environment)
if not run_lua_fn then
local n = 1
local fn
@@ -592,10 +587,12 @@ do
elseif "EscapedNomsu" == _exp_0 then
local make_tree
make_tree = function(t)
- if type(t) ~= 'userdata' then
+ if not (AST.is_syntax_tree(t)) then
return repr(t)
end
- if t.is_multi then
+ if t.value then
+ return t.type .. "(" .. repr(tostring(t.source)) .. ", " .. repr(t.value) .. ")"
+ else
local bits
do
local _accum_0 = { }
@@ -608,8 +605,6 @@ do
bits = _accum_0
end
return t.type .. "(" .. repr(tostring(t.source)) .. ", " .. table.concat(bits, ", ") .. ")"
- else
- return t.type .. "(" .. repr(tostring(t.source)) .. ", " .. repr(t.value) .. ")"
end
end
return Lua.Value(tree.source, make_tree(tree[1]))
@@ -1327,10 +1322,9 @@ do
end
return _pairs(x)
end
- for k, v in pairs(Types) do
+ for k, v in pairs(AST) do
self.environment[k] = v
end
- self.environment.Tuple = Tuple
self.environment.Lua = Lua
self.environment.Nomsu = Nomsu
self.environment.Source = Source
@@ -1346,7 +1340,7 @@ do
__mode = "k"
})
self.environment.LOADED = { }
- self.environment.Types = Types
+ self.environment.AST = AST
return self:initialize_core()
end,
__base = _base_0,
diff --git a/nomsu.moon b/nomsu.moon
index 1196b37..bef479b 100755
--- a/nomsu.moon
+++ b/nomsu.moon
@@ -40,9 +40,6 @@ lpeg.setmaxstack 10000
{:P,:R,:V,:S,:Cg,:C,:Cp,:B,:Cmt,:Carg} = lpeg
utils = require 'utils'
new_uuid = require 'uuid'
-immutable = require 'immutable'
-export Tuple
-Tuple = immutable(nil, {name:"Tuple"})
{:repr, :stringify, :min, :max, :equivalent, :set, :is_list, :sum} = utils
colors = setmetatable({}, {__index:->""})
export colored
@@ -134,7 +131,7 @@ do
if type(i) == 'number' then return string.sub(@, i, i)
elseif type(i) == 'table' then return string.sub(@, i[1], i[2])
-Types = require "nomsu_tree"
+AST = require "nomsu_tree"
NOMSU_DEFS = with {}
-- Newline supports either windows-style CR+LF or unix-style LF
@@ -205,10 +202,11 @@ setmetatable(NOMSU_DEFS, {__index:(key)=>
local source
with userdata.source
source = Source(.filename, .start + start-1, .start + stop-1)
- tree = if Types[key].is_multi
- Types[key](source, unpack(value))
- else Types[key](source, value)
- return tree
+ value.source = source
+ setmetatable(value, AST[key])
+ if value.__init then value\__init!
+ return value
+
self[key] = make_node
return make_node
})
@@ -254,6 +252,8 @@ class NomsuCompiler
@[key] = id
return id
})
+ -- Mapping from source string (e.g. "@core/metaprogramming.nom[1:100]") to a mapping
+ -- from lua line number to nomsu line number
@source_map = {}
_list_mt =
@@ -304,8 +304,7 @@ class NomsuCompiler
if mt.__pairs
return mt.__pairs(x)
return _pairs(x)
- for k,v in pairs(Types) do @environment[k] = v
- @environment.Tuple = Tuple
+ for k,v in pairs(AST) do @environment[k] = v
@environment.Lua = Lua
@environment.Nomsu = Nomsu
@environment.Source = Source
@@ -316,7 +315,7 @@ class NomsuCompiler
@environment.COMPILE_ACTIONS = {}
@environment.ARG_ORDERS = setmetatable({}, {__mode:"k"})
@environment.LOADED = {}
- @environment.Types = Types
+ @environment.AST = AST
@initialize_core!
local stub_defs
@@ -427,7 +426,7 @@ class NomsuCompiler
run_lua: (lua)=>
assert(type(lua) != 'string', "Attempt to run lua string instead of Lua (object)")
lua_string = tostring(lua)
- run_lua_fn, err = load(lua_string, tostring(lua.source), "t", @environment)
+ run_lua_fn, err = load(lua_string, nil and tostring(lua.source), "t", @environment)
if not run_lua_fn
n = 1
fn = ->
@@ -525,13 +524,13 @@ class NomsuCompiler
when "EscapedNomsu"
make_tree = (t)->
- if type(t) != 'userdata'
+ unless AST.is_syntax_tree(t)
return repr(t)
- if t.is_multi
+ if t.value
+ return t.type.."("..repr(tostring t.source)..", "..repr(t.value)..")"
+ else
bits = [make_tree(bit) for bit in *t]
return t.type.."("..repr(tostring t.source)..", "..table.concat(bits, ", ")..")"
- else
- return t.type.."("..repr(tostring t.source)..", "..repr(t.value)..")"
Lua.Value tree.source, make_tree(tree[1])
when "Block"
diff --git a/nomsu.peg b/nomsu.peg
index ec40211..2de5292 100644
--- a/nomsu.peg
+++ b/nomsu.peg
@@ -98,11 +98,11 @@ text_interpolation:
inline_text_interpolation /
("\" indented_expression nodent "..")
-number (Number): (("-"? (([0-9]+ "." [0-9]+) / ("." [0-9]+) / ([0-9]+)))-> tonumber)
+number (Number): {| {:value: (("-"? (([0-9]+ "." [0-9]+) / ("." [0-9]+) / ([0-9]+)))-> tonumber) :} |}
-- Variables can be nameless (i.e. just %) and can't contain operators like apostrophe
-- which is a hack to allow %'s to parse as "%" and "' s" separately
-variable (Var): "%" { (%ident_char+ ((!"'" %operator_char+) / %ident_char+)*)? }
+variable (Var): "%" {| {:value: (%ident_char+ ((!"'" %operator_char+) / %ident_char+)*)? :} |}
inline_list (List):
!('[..]')
diff --git a/nomsu_tree.lua b/nomsu_tree.lua
index f1b6489..5aeefe8 100644
--- a/nomsu_tree.lua
+++ b/nomsu_tree.lua
@@ -1,130 +1,105 @@
-local utils = require('utils')
-local repr, stringify, min, max, equivalent, set, is_list, sum
-repr, stringify, min, max, equivalent, set, is_list, sum = utils.repr, utils.stringify, utils.min, utils.max, utils.equivalent, utils.set, utils.is_list, utils.sum
-local immutable = require('immutable')
+local repr
+repr = require('utils').repr
local insert, remove, concat
do
local _obj_0 = table
insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat
end
-local Lua, Nomsu, Source
-do
- local _obj_0 = require("code_obj")
- Lua, Nomsu, Source = _obj_0.Lua, _obj_0.Nomsu, _obj_0.Source
-end
-local MAX_LINE = 80
-local Types = { }
-Types.is_node = function(n)
- return type(n) == 'userdata' and getmetatable(n) and Types[n.type] == getmetatable(n)
+local Source
+Source = require("code_obj").Source
+local AST = { }
+AST.is_syntax_tree = function(n)
+ return type(n) == 'table' and getmetatable(n) and AST[n.type] == getmetatable(n)
end
local Tree
-Tree = function(name, fields, methods)
- methods = methods or { }
- local is_multi = true
- for _index_0 = 1, #fields do
- local f = fields[_index_0]
- is_multi = is_multi and (f ~= "value")
- end
+Tree = function(name, leaf_or_branch, methods)
+ local cls = methods or { }
+ local is_multi = leaf_or_branch == 'branch'
do
- methods.type = name
- methods.name = name
- methods.__new = methods.__new or function(self, source, ...)
- assert(source)
- if type(source) == 'string' then
- source = Source:from_string(source)
- end
- return source, ...
+ cls.type = name
+ cls.is_instance = function(self, x)
+ return getmetatable(x) == self
end
- methods.is_multi = is_multi
- if is_multi then
- methods.__tostring = function(self)
- return tostring(self.name) .. "(" .. tostring(table.concat((function()
- local _accum_0 = { }
- local _len_0 = 1
- for _index_0 = 1, #self do
- local v = self[_index_0]
- _accum_0[_len_0] = repr(v)
- _len_0 = _len_0 + 1
- end
- return _accum_0
- end)(), ', ')) .. ")"
- end
- methods.map = function(self, fn)
- do
- local replacement = fn(self)
- if replacement then
- return replacement
- end
- end
- local new_vals
- do
- local _accum_0 = { }
- local _len_0 = 1
- for _index_0 = 1, #self do
- local v = self[_index_0]
- _accum_0[_len_0] = v.map and v:map(fn) or v
- _len_0 = _len_0 + 1
- end
- new_vals = _accum_0
+ cls.__index = cls
+ cls.__tostring = function(self)
+ return tostring(self.name) .. "(#{@value and repr(@value) or table.concat([repr(v) for v in *@]), ', '})"
+ end
+ cls.map = function(self, fn)
+ do
+ local replacement = fn(self)
+ if replacement then
+ return replacement
end
- return getmetatable(self)(self.source, unpack(new_vals))
end
- else
- methods.__tostring = function(self)
- return tostring(self.name) .. "(" .. tostring(repr(self.value)) .. ")"
+ if self.value then
+ return self
end
- methods.map = function(self, fn)
- return fn(self) or self
+ local new_vals
+ do
+ local _accum_0 = { }
+ local _len_0 = 1
+ for _index_0 = 1, #self do
+ local v = self[_index_0]
+ _accum_0[_len_0] = v.map and v:map(fn) or v
+ _len_0 = _len_0 + 1
+ end
+ new_vals = _accum_0
end
+ return getmetatable(self)(self.source, unpack(new_vals))
end
end
- Types[name] = immutable(fields, methods)
-end
-Tree("Block", {
- "source"
-})
-Tree("EscapedNomsu", {
- "source"
-})
-Tree("Text", {
- "source"
-})
-Tree("List", {
- "source"
-})
-Tree("Dict", {
- "source"
-})
-Tree("DictEntry", {
- "source"
-})
-Tree("IndexChain", {
- "source"
-})
-Tree("Number", {
- "source",
- "value"
-})
-Tree("Var", {
- "source",
- "value"
-})
-Tree("Action", {
- "source",
- "stub"
-}, {
- __new = function(self, source, ...)
- assert(source)
- if type(source) == 'string' then
- source = Source:from_string(source)
+ AST[name] = setmetatable(cls, {
+ __tostring = function(self)
+ return self.name
+ end,
+ __call = function(self, source, ...)
+ if type(source) == 'string' then
+ source = Source:from_string(source)
+ end
+ assert(Source:is_instance(source))
+ local inst
+ if is_multi then
+ inst = {
+ source = source,
+ ...
+ }
+ else
+ inst = {
+ source = source,
+ value = ...
+ }
+ end
+ setmetatable(inst, self)
+ if inst.__init then
+ inst:__init()
+ end
+ return inst
end
- local stub_bits = { }
- for i = 1, select("#", ...) do
- local a = select(i, ...)
- stub_bits[i] = type(a) == 'string' and a or "%"
+ })
+end
+Tree("Number", 'leaf')
+Tree("Var", 'leaf')
+Tree("Block", 'branch')
+Tree("EscapedNomsu", 'branch')
+Tree("Text", 'branch')
+Tree("List", 'branch')
+Tree("Dict", 'branch')
+Tree("DictEntry", 'branch')
+Tree("IndexChain", 'branch')
+Tree("Action", 'branch', {
+ __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
+ end
+ stub_bits = _accum_0
end
- local stub = concat(stub_bits, " ")
- return source, stub, ...
+ self.stub = concat(stub_bits, " ")
end,
get_spec = function(self)
return concat((function()
@@ -139,4 +114,4 @@ Tree("Action", {
end)(), " ")
end
})
-return Types
+return AST
diff --git a/nomsu_tree.moon b/nomsu_tree.moon
index 3339606..fe1c4f4 100644
--- a/nomsu_tree.moon
+++ b/nomsu_tree.moon
@@ -1,69 +1,53 @@
-- This file contains the datastructures used to represent parsed Nomsu syntax trees,
-- as well as the logic for converting them to Lua code.
-utils = require 'utils'
-{:repr, :stringify, :min, :max, :equivalent, :set, :is_list, :sum} = utils
-immutable = require 'immutable'
+{:repr} = require 'utils'
{:insert, :remove, :concat} = table
-{:Lua, :Nomsu, :Source} = require "code_obj"
+{:Source} = require "code_obj"
-MAX_LINE = 80 -- For beautification purposes, try not to make lines much longer than this value
-
-Types = {}
-Types.is_node = (n)->
- type(n) == 'userdata' and getmetatable(n) and Types[n.type] == getmetatable(n)
+AST = {}
+AST.is_syntax_tree = (n)->
+ type(n) == 'table' and getmetatable(n) and AST[n.type] == getmetatable(n)
-- Helper method:
-Tree = (name, fields, methods)->
- methods or= {}
- is_multi = true
- for f in *fields do is_multi and= (f != "value")
- with methods
+Tree = (name, leaf_or_branch, methods)->
+ cls = methods or {}
+ is_multi = leaf_or_branch == 'branch'
+ with cls
.type = name
- .name = name
- .__new or= (source, ...)=>
- assert source
+ .is_instance = (x)=> getmetatable(x) == @
+ .__index = cls
+ .__tostring = => "#{@name}(#{@value and repr(@value) or table.concat([repr(v) for v in *@]), ', '})"
+ .map = (fn)=>
+ if replacement = fn(@) then return replacement
+ if @value then return @
+ new_vals = [v.map and v\map(fn) or v for v in *@]
+ return getmetatable(self)(@source, unpack(new_vals))
+
+ AST[name] = setmetatable cls,
+ __tostring: => @name
+ __call: (source, ...)=>
if type(source) == 'string'
source = Source\from_string(source)
- --assert Source\is_instance(source)
- return source, ...
- .is_multi = is_multi
- if is_multi
- .__tostring = => "#{@name}(#{table.concat [repr(v) for v in *@], ', '})"
- .map = (fn)=>
- if replacement = fn(@)
- return replacement
- new_vals = [v.map and v\map(fn) or v for v in *@]
- return getmetatable(self)(@source, unpack(new_vals))
- else
- .__tostring = => "#{@name}(#{repr(@value)})"
- .map = (fn)=>
- fn(@) or @
-
- Types[name] = immutable fields, methods
-
-Tree "Block", {"source"}
-Tree "EscapedNomsu", {"source"}
-Tree "Text", {"source"}
-Tree "List", {"source"}
-Tree "Dict", {"source"}
-Tree "DictEntry", {"source"}
-Tree "IndexChain", {"source"}
-Tree "Number", {"source", "value"}
-Tree "Var", {"source", "value"}
+ assert(Source\is_instance(source))
+ inst = if is_multi then {:source, ...} else {:source, value:...}
+ setmetatable(inst, @)
+ if inst.__init then inst\__init!
+ return inst
-Tree "Action", {"source", "stub"},
- __new: (source, ...)=>
- assert source
- if type(source) == 'string'
- source = Source\from_string(source)
- --assert Source\is_instance(source)
- stub_bits = {}
- for i=1,select("#",...)
- a = select(i, ...)
- stub_bits[i] = type(a) == 'string' and a or "%"
- stub = concat stub_bits, " "
- return source, stub, ...
+Tree "Number", 'leaf'
+Tree "Var", 'leaf'
+Tree "Block", 'branch'
+Tree "EscapedNomsu", 'branch'
+Tree "Text", 'branch'
+Tree "List", 'branch'
+Tree "Dict", 'branch'
+Tree "DictEntry", 'branch'
+Tree "IndexChain", 'branch'
+Tree "Action", 'branch',
+ __init: =>
+ stub_bits = [type(a) == 'string' and a or '%' for a in *@]
+ @stub = concat stub_bits, " "
get_spec: =>
concat [type(a) == "string" and a or "%#{a.value}" for a in *@], " "
-return Types
+return AST
diff --git a/tests/metaprogramming.nom b/tests/metaprogramming.nom
index b9b97af..a31d030 100644
--- a/tests/metaprogramming.nom
+++ b/tests/metaprogramming.nom
@@ -55,7 +55,9 @@ try: foo 99
assume ((\(5 + 5) as value) = 10) or barf "%tree as value failed."
-assume ("\(\(foo %x) as nomsu)" = "foo %x") or barf "source code failed."
+assume ("\(\(foo %x) as nomsu)" = "foo %x") or barf "action source code failed."
+
+assume ("\(\%x as nomsu)" = "%x") or barf "var source code failed."
assume ((type of {}) = "table") or barf "type of failed."