aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md8
-rw-r--r--code_obj.lua54
-rw-r--r--code_obj.moon50
-rw-r--r--compatibility/2.3.nom2
-rw-r--r--compatibility/2.4.nom2
-rw-r--r--compatibility/2.5.5.5.nom2
-rw-r--r--compatibility/2.5.nom2
-rw-r--r--compatibility/2.nom2
-rw-r--r--compatibility/3.5.5.6.nom2
-rw-r--r--compatibility/3.6.nom2
-rw-r--r--compatibility/3.7.nom2
-rw-r--r--compatibility/3.8.nom2
-rw-r--r--compatibility/3.nom2
-rw-r--r--compatibility/4.8.10.nom60
-rw-r--r--compatibility/compatibility.nom44
-rw-r--r--containers.lua179
-rw-r--r--containers.moon87
-rw-r--r--core/collections.nom57
-rw-r--r--core/control_flow.nom358
-rw-r--r--core/coroutines.nom10
-rw-r--r--core/errors.nom43
-rw-r--r--core/id.nom6
-rw-r--r--core/io.nom6
-rw-r--r--core/math.nom154
-rw-r--r--core/metaprogramming.nom233
-rw-r--r--core/operators.nom102
-rw-r--r--core/scopes.nom12
-rw-r--r--core/text.nom6
-rw-r--r--examples/how_do_i.nom40
-rw-r--r--files.lua24
-rw-r--r--files.moon12
-rw-r--r--lib/base64.nom8
-rw-r--r--lib/consolecolor.nom15
-rw-r--r--lib/file_hash.nom10
-rw-r--r--lib/object.nom73
-rw-r--r--lib/os.nom28
-rw-r--r--lib/training_wheels.nom46
-rw-r--r--lib/version.nom2
-rw-r--r--nomnom/ast.nom88
-rw-r--r--nomnom/code_obj.nom210
-rw-r--r--nomnom/compile.nom276
-rw-r--r--nomnom/decompile.nom347
-rw-r--r--nomnom/files.nom108
-rw-r--r--nomnom/parser.nom91
-rw-r--r--nomnom/pretty_errors.nom85
-rw-r--r--nomnom/source.nom54
-rw-r--r--nomsu.4.peg22
-rw-r--r--nomsu.lua23
-rwxr-xr-xnomsu.moon22
-rw-r--r--nomsu_compiler.lua218
-rw-r--r--nomsu_compiler.moon141
-rw-r--r--parser.lua2
-rw-r--r--parser.moon1
-rw-r--r--string2.lua123
-rw-r--r--string2.moon63
-rw-r--r--syntax_tree.lua260
-rw-r--r--syntax_tree.moon164
-rwxr-xr-xtools/autoformat.nom4
-rwxr-xr-xtools/find_action.nom22
-rwxr-xr-xtools/parse.nom6
-rwxr-xr-xtools/replace.nom4
-rwxr-xr-xtools/test.nom5
-rwxr-xr-xtools/upgrade.nom41
63 files changed, 2945 insertions, 1182 deletions
diff --git a/README.md b/README.md
index 6744001..1de0910 100644
--- a/README.md
+++ b/README.md
@@ -25,12 +25,12 @@ say "Hello"
for %num in %my_nums:
say "\%num is one of my nums"
-action [sing %n bottles of beer]:
+(sing %n bottles of beer) means:
for %i in %n to 1 by -1:
- say ".."
- \%i bottle\("s" if (%i > 1) else "") of beer on the wall,
+ say "\
+ ..\%i bottle\("s" if (%i > 1) else "") of beer on the wall,
\%i bottle\("s" if (%i > 1) else "") of beer!
- Take one down, pass it around...
+ Take one down, pass it around..."
say "No bottles of beer on the wall. Go to the store, buy some more..."
sing 99 bottles of beer
diff --git a/code_obj.lua b/code_obj.lua
index d51d63f..b5213c9 100644
--- a/code_obj.lua
+++ b/code_obj.lua
@@ -3,8 +3,6 @@ do
local _obj_0 = table
insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat
end
-local repr
-repr = require('utils').repr
local unpack = unpack or table.unpack
local LuaCode, NomsuCode, Source
do
@@ -13,8 +11,8 @@ do
__tostring = function(self)
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 '') .. ")"
+ as_lua = function(self)
+ return "Source(" .. tostring(self.filename:as_lua()) .. ", " .. 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
@@ -80,7 +78,7 @@ do
local _class_0
local _base_0 = {
is_code = true,
- __tostring = function(self)
+ text = function(self)
if self.__str == nil then
local buff, indent = { }, 0
local match, gsub, rep
@@ -97,7 +95,7 @@ do
end
end
else
- b = tostring(b)
+ b = b:text()
if indent > 0 then
b = gsub(b, "\n", "\n" .. rep(" ", indent))
end
@@ -108,16 +106,19 @@ do
end
return self.__str
end,
- __repr = function(self)
+ __tostring = function(self)
+ return self:text()
+ end,
+ as_lua = function(self)
return tostring(self.__class.__name) .. "(" .. tostring(concat({
- repr(tostring(self.source)),
+ tostring(self.source):as_lua(),
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)
+ _accum_0[_len_0] = b:as_lua()
_len_0 = _len_0 + 1
end
return _accum_0
@@ -125,13 +126,13 @@ do
}, ", ")) .. ")"
end,
__len = function(self)
- return #tostring(self)
+ return #self:text()
end,
match = function(self, ...)
- return tostring(self):match(...)
+ return self:text():match(...)
end,
gmatch = function(self, ...)
- return tostring(self):gmatch(...)
+ return self:text():gmatch(...)
end,
dirty = function(self)
self.__str = nil
@@ -157,9 +158,6 @@ do
if b.is_code then
b.dirty = error
end
- if type(b) ~= 'string' and not (type(b) == 'table' and b.is_code) then
- b = repr(b)
- end
bits[#bits + 1] = b
_continue_0 = true
until true
@@ -171,7 +169,7 @@ do
end,
trailing_line_len = function(self)
if self._trailing_line_len == nil then
- self._trailing_line_len = #tostring(self):match("[^\n]*$")
+ self._trailing_line_len = #self:text():match("[^\n]*$")
end
return self._trailing_line_len
end,
@@ -214,7 +212,9 @@ do
if b.is_code then
b.dirty = error
end
- b = tostring(b)
+ if not (type(b) == 'string') then
+ b = b:text()
+ end
local line = match(b, "\n([^\n]*)$")
if line then
line_len = #line
@@ -235,9 +235,6 @@ do
if b.is_code then
b.dirty = error
end
- if type(b) ~= 'string' and not (type(b) == 'table' and b.is_code) then
- b = repr(b)
- end
bits[i] = b
end
return self:dirty()
@@ -255,7 +252,6 @@ do
if type(self.source) == 'string' then
self.source = Source:from_string(self.source)
end
- assert(self.source and Source:is_instance(self.source), "Source has the wrong type")
return self:append(...)
end,
__base = _base_0,
@@ -276,7 +272,7 @@ do
local _parent_0 = Code
local _base_0 = {
__tostring = Code.__tostring,
- __repr = Code.__repr,
+ as_lua = Code.as_lua,
__len = Code.__len,
add_free_vars = function(self, vars)
if not (#vars > 0) then
@@ -357,7 +353,7 @@ do
local _list_1 = self.bits
for _index_0 = 1, #_list_1 do
local bit = _list_1[_index_0]
- if bit.__class == LuaCode then
+ if not (type(bit) == 'string') then
gather_from(bit)
end
end
@@ -404,8 +400,9 @@ do
end
else
walk(b, pos)
+ b = b:text()
end
- pos = pos + #tostring(b)
+ pos = pos + #b
end
end
walk(self, 1)
@@ -469,7 +466,7 @@ do
local _parent_0 = Code
local _base_0 = {
__tostring = Code.__tostring,
- __repr = Code.__repr,
+ as_lua = Code.as_lua,
__len = Code.__len
}
_base_0.__index = _base_0
@@ -505,12 +502,7 @@ do
end
NomsuCode = _class_0
end
-Code.__base.append_1 = assert(Code.__base.append)
-Code.__base.append_1_joined_by_2 = assert(Code.__base.concat_append)
-Code.__base.prepend_1 = assert(Code.__base.prepend)
-LuaCode.__base.declare_locals_1 = assert(LuaCode.__base.declare_locals)
-LuaCode.__base.remove_free_vars_1 = assert(LuaCode.__base.remove_free_vars)
-LuaCode.__base.add_free_vars_1 = assert(LuaCode.__base.add_free_vars)
+Code.__base.add_1_joined_with = assert(Code.__base.concat_append)
return {
Code = Code,
NomsuCode = NomsuCode,
diff --git a/code_obj.moon b/code_obj.moon
index bd53c5b..22969a4 100644
--- a/code_obj.moon
+++ b/code_obj.moon
@@ -2,7 +2,6 @@
-- build up generated code, while keeping track of where it came from, and managing
-- indentation levels.
{:insert, :remove, :concat} = table
-{:repr} = require 'utils'
unpack or= table.unpack
local LuaCode, NomsuCode, Source
@@ -19,7 +18,7 @@ class Source
__tostring: => "@#{@filename}[#{@start}#{@stop and ':'..@stop or ''}]"
- __repr: => "Source(#{repr @filename}, #{@start}#{@stop and ', '..@stop or ''})"
+ as_lua: => "Source(#{@filename\as_lua!}, #{@start}#{@stop and ', '..@stop or ''})"
__eq: (other)=>
getmetatable(@) == getmetatable(other) and @filename == other.filename and @start == other.start and @stop == other.stop
@@ -48,10 +47,10 @@ class Code
@bits = {}
if type(@source) == 'string'
@source = Source\from_string(@source)
- assert(@source and Source\is_instance(@source), "Source has the wrong type")
+ --assert(@source and Source\is_instance(@source), "Source has the wrong type")
@append(...)
- __tostring: =>
+ text: =>
if @__str == nil
buff, indent = {}, 0
{:match, :gsub, :rep} = string
@@ -60,21 +59,23 @@ class Code
if spaces = match(b, "\n([ ]*)[^\n]*$")
indent = #spaces
else
- b = tostring(b)
+ b = b\text!
if indent > 0
b = gsub(b, "\n", "\n"..rep(" ", indent))
buff[#buff+1] = b
@__str = concat(buff, "")
return @__str
- __repr: =>
- "#{@__class.__name}(#{concat {repr(tostring(@source)), unpack([repr(b) for b in *@bits])}, ", "})"
+ __tostring: => @text!
- __len: => #tostring(@)
+ as_lua: =>
+ "#{@__class.__name}(#{concat {tostring(@source)\as_lua!, unpack([b\as_lua! for b in *@bits])}, ", "})"
+
+ __len: => #@text!
- match: (...)=> tostring(@)\match(...)
+ match: (...)=> @text!\match(...)
- gmatch: (...)=> tostring(@)\gmatch(...)
+ gmatch: (...)=> @text!\gmatch(...)
dirty: =>
@__str = nil
@@ -92,14 +93,14 @@ class Code
assert(not Source\is_instance(b), "code bit is a Source")
if b == '' then continue
b.dirty = error if b.is_code
- if type(b) != 'string' and not (type(b) == 'table' and b.is_code)
- b = repr(b)
+ --if type(b) != 'string' and not (type(b) == 'table' and b.is_code)
+ -- b = b\as_lua!
bits[#bits+1] = b
@dirty!
trailing_line_len: =>
if @_trailing_line_len == nil
- @_trailing_line_len = #tostring(@)\match("[^\n]*$")
+ @_trailing_line_len = #@text!\match("[^\n]*$")
return @_trailing_line_len
is_multiline: =>
@@ -131,7 +132,8 @@ class Code
bits[#bits+1] = joiner
bits[#bits+1] = b
b.dirty = error if b.is_code
- b = tostring(b)
+ unless type(b) == 'string'
+ b = b\text!
line = match(b, "\n([^\n]*)$")
if line
line_len = #line
@@ -147,8 +149,8 @@ class Code
for i=1,n
b = select(i, ...)
b.dirty = error if b.is_code
- if type(b) != 'string' and not (type(b) == 'table' and b.is_code)
- b = repr(b)
+ --if type(b) != 'string' and not (type(b) == 'table' and b.is_code)
+ -- b = b\as_lua!
bits[i] = b
@dirty!
@@ -158,7 +160,7 @@ class Code
class LuaCode extends Code
__tostring: Code.__tostring
- __repr: Code.__repr
+ as_lua: Code.as_lua
__len: Code.__len
new: (...)=>
super ...
@@ -208,7 +210,7 @@ class LuaCode extends Code
seen[var] = true
to_declare[#to_declare+1] = var
for bit in *@bits
- if bit.__class == LuaCode
+ unless type(bit) == 'string'
gather_from bit
gather_from self
if #to_declare > 0
@@ -238,7 +240,8 @@ class LuaCode extends Code
nomsu_to_lua[lua.source.start] = pos
else
walk b, pos
- pos += #tostring(b)
+ b = b\text!
+ pos += #b
walk self, 1
return {
nomsu_filename:@source.filename
@@ -253,14 +256,9 @@ class LuaCode extends Code
class NomsuCode extends Code
__tostring: Code.__tostring
- __repr: Code.__repr
+ as_lua: Code.as_lua
__len: Code.__len
-Code.__base.append_1 = assert Code.__base.append
-Code.__base.append_1_joined_by_2 = assert Code.__base.concat_append
-Code.__base.prepend_1 = assert Code.__base.prepend
-LuaCode.__base.declare_locals_1 = assert LuaCode.__base.declare_locals
-LuaCode.__base.remove_free_vars_1 = assert LuaCode.__base.remove_free_vars
-LuaCode.__base.add_free_vars_1 = assert LuaCode.__base.add_free_vars
+Code.__base.add_1_joined_with = assert Code.__base.concat_append
return {:Code, :NomsuCode, :LuaCode, :Source}
diff --git a/compatibility/2.3.nom b/compatibility/2.3.nom
index fb137b8..7de353e 100644
--- a/compatibility/2.3.nom
+++ b/compatibility/2.3.nom
@@ -1,4 +1,4 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This file defines upgrades from Nomsu <2.3 to Nomsu 2.3
diff --git a/compatibility/2.4.nom b/compatibility/2.4.nom
index b770840..1a7c9c3 100644
--- a/compatibility/2.4.nom
+++ b/compatibility/2.4.nom
@@ -1,4 +1,4 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This file defines upgrades from Nomsu <2.4 to Nomsu 2.4
diff --git a/compatibility/2.5.5.5.nom b/compatibility/2.5.5.5.nom
index a3f8b27..efed78d 100644
--- a/compatibility/2.5.5.5.nom
+++ b/compatibility/2.5.5.5.nom
@@ -1,4 +1,4 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This file defines upgrades from Nomsu <2.5.5.5 to Nomsu 2.5.5.5
diff --git a/compatibility/2.5.nom b/compatibility/2.5.nom
index fe17dfb..b1e01b0 100644
--- a/compatibility/2.5.nom
+++ b/compatibility/2.5.nom
@@ -1,4 +1,4 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This file defines upgrades from Nomsu <2.5 to Nomsu 2.5
diff --git a/compatibility/2.nom b/compatibility/2.nom
index 2364f56..d7a1489 100644
--- a/compatibility/2.nom
+++ b/compatibility/2.nom
@@ -1,4 +1,4 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This file defines upgrades from Nomsu 1 to Nomsu 2
diff --git a/compatibility/3.5.5.6.nom b/compatibility/3.5.5.6.nom
index 068e65d..89fbc8a 100644
--- a/compatibility/3.5.5.6.nom
+++ b/compatibility/3.5.5.6.nom
@@ -1,4 +1,4 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This file defines upgrades from Nomsu <3.5.5.6 to Nomsu 3.5.5.6
diff --git a/compatibility/3.6.nom b/compatibility/3.6.nom
index eec97eb..7cd5c64 100644
--- a/compatibility/3.6.nom
+++ b/compatibility/3.6.nom
@@ -1,4 +1,4 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This file defines upgrades from Nomsu <3.6 to 3.6
diff --git a/compatibility/3.7.nom b/compatibility/3.7.nom
index 5b458de..599e0d5 100644
--- a/compatibility/3.7.nom
+++ b/compatibility/3.7.nom
@@ -1,4 +1,4 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This file defines upgrades from Nomsu <3.7 to 3.7
diff --git a/compatibility/3.8.nom b/compatibility/3.8.nom
index 9538770..0709688 100644
--- a/compatibility/3.8.nom
+++ b/compatibility/3.8.nom
@@ -1,4 +1,4 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This file defines upgrades from Nomsu <3.8 to 3.8 (Text method changes)
diff --git a/compatibility/3.nom b/compatibility/3.nom
index 36df17d..5156aa8 100644
--- a/compatibility/3.nom
+++ b/compatibility/3.nom
@@ -1,4 +1,4 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This file defines upgrades from Nomsu <=2 to Nomsu 3
diff --git a/compatibility/4.8.10.nom b/compatibility/4.8.10.nom
new file mode 100644
index 0000000..66152b5
--- /dev/null
+++ b/compatibility/4.8.10.nom
@@ -0,0 +1,60 @@
+#!/usr/bin/env nomsu -V4.8.10.6
+#
+ This file defines upgrades from Nomsu <4.8.10 to 4.8.10 (renaming "action" -> "means")
+use "compatibility/compatibility.nom"
+
+upgrade action "local action 1 2" to "4.8.10" via (..)
+ [%tree, %end_version] ->:
+ %spec = %tree.3
+ %body = %tree.4
+ if %spec.type is:
+ "List":
+ if ((size of %spec) == 1):
+ return \(%spec.1 means %body)
+ ..else:
+ return \(%spec all mean %body)
+ else:
+ return \(%spec means %body)
+
+upgrade action "action 1 2" to "4.8.10" via (..)
+ [%tree, %end_version] ->:
+ %spec = %tree.2
+ %body = %tree.3
+ if %spec.type is:
+ "List":
+ if ((size of %spec) == 1):
+ return \(externally %spec.1 means %body)
+ ..else:
+ return \(externally %spec all mean %body)
+ else:
+ return \(externally %spec means %body)
+
+upgrade action "compile 1 to 2" to "4.8.10" via (..)
+ [%tree, %end_version] ->:
+ %spec = %tree.2
+ %body = %tree.4
+ if %spec.type is:
+ "List":
+ if ((size of %spec) == 1):
+ return \(%spec.1 compiles to %body)
+ ..else:
+ return \(%spec all compile to %body)
+ else:
+ return \(%spec compiles to %body)
+
+upgrade action "parse 1 as 2" to "4.8.10" via (..)
+ [%tree, %end_version] ->:
+ %spec = %tree.2
+ %body = %tree.4
+ if %spec.type is:
+ "List":
+ if ((size of %spec) == 1):
+ return \(%spec.1 parses as %body)
+ ..else:
+ return \(%spec all parse as %body)
+ else:
+ return \(%spec parse as %body)
+
+upgrade action (compile as %) to "4.8.10" as (what % compiles to)
+upgrade action (action %) to "4.8.10" as (%'s meaning)
+upgrade action (remove action %) to "4.8.10" as ((%'s meaning) = (nil))
diff --git a/compatibility/compatibility.nom b/compatibility/compatibility.nom
index acd1d11..1153ab4 100644
--- a/compatibility/compatibility.nom
+++ b/compatibility/compatibility.nom
@@ -1,4 +1,4 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This file contains code for defining ways to upgrade code between different versions
of Nomsu.
@@ -6,17 +6,17 @@
use "lib/os.nom"
%UPGRADES = {}
-action [upgrade to %version via %upgrade_fn]:
+externally (upgrade to %version via %upgrade_fn) means:
%UPGRADES.%version = %upgrade_fn
%ACTION_UPGRADES = ({} with fallback % -> {})
-action [upgrade action %stub to %version via %upgrade_fn]:
+externally (upgrade action %stub to %version via %upgrade_fn) means:
%ACTION_UPGRADES.%version.%stub = %upgrade_fn
-parse [upgrade %tree to %version as %body] as (..)
- upgrade to %version via ([%] -> (% with %tree -> %body))
+(upgrade %tree to %version as %body) parses as (..)
+ upgrade to %version via ([%, %end_version] -> (% with %tree -> %body))
-compile [upgrade action %actions to %version as %body] to:
+(upgrade action %actions to %version as %body) compiles to:
if (%actions is "Action" syntax tree):
%actions = \[%actions]
%lua = (Lua "")
@@ -27,7 +27,7 @@ compile [upgrade action %actions to %version as %body] to:
%replacements.(%action.%i.1) = "\(\%tree as lua id)[\%i]"
define mangler
- local action [make tree %t]:
+ (make tree %t) means:
when:
(%t is "Var" syntax tree):
if %replacements.(%t.1):
@@ -52,7 +52,7 @@ compile [upgrade action %actions to %version as %body] to:
%retval = (make tree %body)
%lua::append (..)
Lua "\
- ..upgrade_action_1_to_2_via_3(\(quote %action.stub), \(%version as lua expr), function(\(..)
+ ..upgrade_action_1_to_2_via(\(quote %action.stub), \(%version as lua expr), function(\(..)
\%tree as lua id
..)
return \%retval
@@ -60,11 +60,12 @@ compile [upgrade action %actions to %version as %body] to:
return %lua
-action [..]
+externally [..]
%tree upgraded from %start_version to %end_version
%tree upgraded to %end_version from %start_version
-..:
- local action [%ver as list] ((% as number) for % in %ver matching "[0-9]+")
+..all mean:
+ unless (%tree is syntax tree): return %tree
+ (%ver as list) means ((% as number) for % in %ver matching "[0-9]+")
%versions = {}
for %v = % in %UPGRADES:
%versions.%v = (yes)
@@ -78,25 +79,30 @@ action [..]
%tree = (..)
%tree with % -> (..)
if ((% is "Action" syntax tree) and %ACTION_UPGRADES.%ver.(%.stub)):
- return (call %ACTION_UPGRADES.%ver.(%.stub) with [%])
+ %with_upgraded_args = (..)
+ %k = (%v upgraded from %start_version to %end_version) for %k = %v in %
+ set %with_upgraded_args 's metatable to (% 's metatable)
+ return (..)
+ call %ACTION_UPGRADES.%ver.(%.stub) with [%with_upgraded_args, %end_version]
if %UPGRADES.%ver:
- %tree = (call %UPGRADES.%ver with [%tree])
+ %with_upgraded_args = (..)
+ %k = (%v upgraded from %start_version to %end_version) for %k = %v in %tree
+ set %with_upgraded_args 's metatable to (%tree 's metatable)
+ %tree = (call %UPGRADES.%ver with [%with_upgraded_args, %end_version])
return %tree
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-parse [%tree upgraded from %start_version] as (..)
+externally (%tree upgraded from %start_version) means (..)
%tree upgraded from %start_version to (Nomsu version)
-parse [%tree upgraded to %end_version] as (..)
+externally (%tree upgraded to %end_version) means (..)
%tree upgraded from (%tree.version or (Nomsu version)) to %end_version
-parse [%tree upgraded] as (..)
+externally (%tree upgraded) means (..)
%tree upgraded from (%tree.version or (Nomsu version)) to (Nomsu version)
-action [use %path from version %version]:
+externally (use %path from version %version) means:
for file %filename in %path:
if (=lua "LOADED[\%filename]"): do next %filename
%file = (read file %filename)
diff --git a/containers.lua b/containers.lua
index 1b3fd94..3148dcf 100644
--- a/containers.lua
+++ b/containers.lua
@@ -3,15 +3,52 @@ do
local _obj_0 = table
insert, remove, concat = _obj_0.insert, _obj_0.remove, _obj_0.concat
end
-local repr, stringify, equivalent, nth_to_last, size
+local equivalent, nth_to_last, size
do
local _obj_0 = require('utils')
- repr, stringify, equivalent, nth_to_last, size = _obj_0.repr, _obj_0.stringify, _obj_0.equivalent, _obj_0.nth_to_last, _obj_0.size
+ equivalent, nth_to_last, size = _obj_0.equivalent, _obj_0.nth_to_last, _obj_0.size
end
local lpeg = require('lpeg')
local re = require('re')
local List, Dict
+local as_nomsu
+as_nomsu = function(self)
+ if type(self) == 'number' then
+ return tostring(self)
+ end
+ do
+ local mt = getmetatable(self)
+ if mt then
+ do
+ local _as_nomsu = mt.as_nomsu
+ if _as_nomsu then
+ return _as_nomsu(self)
+ end
+ end
+ end
+ end
+ return tostring(self)
+end
+local as_lua
+as_lua = function(self)
+ if type(self) == 'number' then
+ return tostring(self)
+ end
+ do
+ local mt = getmetatable(self)
+ if mt then
+ do
+ local _as_lua = mt.as_lua
+ if _as_lua then
+ return _as_lua(self)
+ end
+ end
+ end
+ end
+ return tostring(self)
+end
local _list_mt = {
+ __type = "List",
__eq = equivalent,
__tostring = function(self)
return "[" .. concat((function()
@@ -19,12 +56,36 @@ local _list_mt = {
local _len_0 = 1
for _index_0 = 1, #self do
local b = self[_index_0]
- _accum_0[_len_0] = repr(b)
+ _accum_0[_len_0] = as_nomsu(b)
_len_0 = _len_0 + 1
end
return _accum_0
end)(), ", ") .. "]"
end,
+ as_nomsu = function(self)
+ return "[" .. concat((function()
+ local _accum_0 = { }
+ local _len_0 = 1
+ for _index_0 = 1, #self do
+ local b = self[_index_0]
+ _accum_0[_len_0] = as_nomsu(b)
+ _len_0 = _len_0 + 1
+ end
+ return _accum_0
+ end)(), ", ") .. "]"
+ end,
+ as_lua = function(self)
+ return "_List{" .. concat((function()
+ local _accum_0 = { }
+ local _len_0 = 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
+ return _accum_0
+ end)(), ", ") .. "}"
+ end,
__lt = function(self, other)
assert(type(self) == 'table' and type(other) == 'table', "Incompatible types for comparison")
for i = 1, math.max(#self, #other) do
@@ -73,15 +134,15 @@ local _list_mt = {
return ret
end,
__index = {
- add_1 = insert,
- append_1 = insert,
- add_1_at_index_2 = function(t, x, i)
+ add = insert,
+ append = insert,
+ add_1_at_index = function(t, x, i)
return insert(t, i, x)
end,
- at_index_1_add_2 = insert,
+ at_index_1_add = insert,
pop = remove,
remove_last = remove,
- remove_index_1 = remove,
+ remove_index = remove,
last = (function(self)
return self[#self]
end),
@@ -104,7 +165,7 @@ local _list_mt = {
return _accum_0
end)())
end,
- joined_with_1 = function(self, glue)
+ joined_with = function(self, glue)
return table.concat((function()
local _accum_0 = { }
local _len_0 = 1
@@ -116,7 +177,7 @@ local _list_mt = {
return _accum_0
end)(), glue)
end,
- has_1 = function(self, item)
+ has = function(self, item)
for _index_0 = 1, #self do
local x = self[_index_0]
if x == item then
@@ -125,13 +186,36 @@ local _list_mt = {
end
return false
end,
- index_of_1 = function(self, item)
+ remove = function(self, item)
+ for i, x in ipairs(self) do
+ if x == item then
+ remove(self, i)
+ end
+ end
+ end,
+ index_of = function(self, item)
for i, x in ipairs(self) do
if x == item then
return i
end
end
return nil
+ end,
+ from_1_to = function(self, start, stop)
+ local n = #self
+ if n < 0 then
+ start = (n + 1 - start)
+ end
+ if n < 0 then
+ stop = (n + 1 - stop)
+ end
+ local _accum_0 = { }
+ local _len_0 = 1
+ for i = start, stop do
+ _accum_0[_len_0] = self[i]
+ _len_0 = _len_0 + 1
+ end
+ return _accum_0
end
},
__newindex = function(self, k, v)
@@ -139,6 +223,8 @@ local _list_mt = {
return rawset(self, k, v)
end
}
+_list_mt.__index.as_lua = _list_mt.as_lua
+_list_mt.__index.as_nomsu = _list_mt.as_nomsu
List = function(t)
return setmetatable(t, _list_mt)
end
@@ -155,6 +241,7 @@ walk_items = function(self, i)
end
end
local _dict_mt = {
+ __type = "Dict",
__eq = equivalent,
__len = size,
__tostring = function(self)
@@ -162,7 +249,29 @@ local _dict_mt = {
local _accum_0 = { }
local _len_0 = 1
for k, v in pairs(self) do
- _accum_0[_len_0] = tostring(repr(k)) .. ": " .. tostring(repr(v))
+ _accum_0[_len_0] = tostring(as_nomsu(k)) .. ": " .. tostring(as_nomsu(v))
+ _len_0 = _len_0 + 1
+ end
+ return _accum_0
+ end)(), ", ") .. "}"
+ end,
+ as_nomsu = function(self)
+ return "{" .. concat((function()
+ local _accum_0 = { }
+ local _len_0 = 1
+ for k, v in pairs(self) do
+ _accum_0[_len_0] = tostring(as_nomsu(k)) .. ": " .. tostring(as_nomsu(v))
+ _len_0 = _len_0 + 1
+ end
+ return _accum_0
+ end)(), ", ") .. "}"
+ end,
+ as_lua = function(self)
+ return "_Dict{" .. concat((function()
+ local _accum_0 = { }
+ local _len_0 = 1
+ for k, v in pairs(self) do
+ _accum_0[_len_0] = "[ " .. tostring(as_lua(k)) .. "]= " .. tostring(as_lua(v))
_len_0 = _len_0 + 1
end
return _accum_0
@@ -271,19 +380,23 @@ do
reverse, upper, lower, find, byte, match, gmatch, gsub, sub, format, rep = _obj_0.reverse, _obj_0.upper, _obj_0.lower, _obj_0.find, _obj_0.byte, _obj_0.match, _obj_0.gmatch, _obj_0.gsub, _obj_0.sub, _obj_0.format, _obj_0.rep
end
local string2 = require('string2')
- local lines, line, line_at, as_lua_id
- lines, line, line_at, as_lua_id = string2.lines, string2.line, string2.line_at, string2.as_lua_id
+ local lines, line, line_at, as_lua_id, is_lua_id
+ lines, line, line_at, as_lua_id, is_lua_id = string2.lines, string2.line, string2.line_at, string2.as_lua_id, string2.is_lua_id
local text_methods = {
- formatted_with_1 = format,
- byte_1 = byte,
- position_of_1 = find,
- position_of_1_after_2 = find,
- bytes_1_to_2 = function(self, start, stop)
+ formatted_with = format,
+ byte = byte,
+ position_of = find,
+ position_of_1_after = find,
+ as_a_lua_identifier = as_lua_id,
+ is_a_lua_identifier = is_lua_id,
+ as_a_lua_id = as_lua_id,
+ is_a_lua_id = is_lua_id,
+ bytes_1_to = function(self, start, stop)
return List({
byte(tostring(self), start, stop)
})
end,
- [as_lua_id("with 1 -> 2")] = gsub,
+ [as_lua_id("with 1 ->")] = gsub,
bytes = function(self)
return List({
byte(tostring(self), 1, -1)
@@ -292,8 +405,8 @@ do
lines = function(self)
return List(lines(self))
end,
- line_1 = line,
- wrap_to_1 = function(self, maxlen)
+ line = line,
+ wrapped_to = function(self, maxlen)
local _lines = { }
local _list_0 = self:lines()
for _index_0 = 1, #_list_0 do
@@ -309,22 +422,30 @@ do
end
return table.concat(_lines, "\n")
end,
- line_at_1 = function(self, i)
+ line_at = function(self, i)
return (line_at(self, i))
end,
- line_number_of_1 = function(self, i)
+ line_number_at = function(self, i)
return select(2, line_at(self, i))
end,
- line_position_of_1 = function(self, i)
+ line_position_at = function(self, i)
return select(3, line_at(self, i))
end,
- matches_1 = function(self, patt)
+ matches = function(self, patt)
return match(self, patt) and true or false
end,
+ matching = function(self, patt)
+ return (match(self, patt))
+ end,
+ matching_groups = function(self, patt)
+ return {
+ match(self, patt)
+ }
+ end,
[as_lua_id("* 1")] = function(self, n)
return rep(self, n)
end,
- matching_1 = function(self, patt)
+ all_matches_of = function(self, patt)
local result = { }
local stepper, x, i = gmatch(self, patt)
while true do
@@ -343,6 +464,7 @@ do
setmetatable(text_methods, {
__index = string2
})
+ getmetatable("").__methods = text_methods
getmetatable("").__index = function(self, i)
if type(i) == 'number' then
return sub(self, i, i)
@@ -352,6 +474,9 @@ do
return text_methods[i]
end
end
+ getmetatable("").__add = function(self, x)
+ return tostring(self) .. tostring(x)
+ end
end
return {
List = List,
diff --git a/containers.moon b/containers.moon
index eaf007a..4671f6b 100644
--- a/containers.moon
+++ b/containers.moon
@@ -1,19 +1,41 @@
-- This file contains container classes, i.e. Lists, Dicts, and Sets
{:insert,:remove,:concat} = table
-{:repr, :stringify, :equivalent, :nth_to_last, :size} = require 'utils'
+{:equivalent, :nth_to_last, :size} = require 'utils'
lpeg = require 'lpeg'
re = require 're'
local List, Dict
+as_nomsu = =>
+ if type(@) == 'number'
+ return tostring(@)
+ if mt = getmetatable(@)
+ if _as_nomsu = mt.as_nomsu
+ return _as_nomsu(@)
+ return tostring(@)
+
+as_lua = =>
+ if type(@) == 'number'
+ return tostring(@)
+ if mt = getmetatable(@)
+ if _as_lua = mt.as_lua
+ return _as_lua(@)
+ return tostring(@)
+
-- List and Dict classes to provide basic equality/tostring functionality for the tables
-- used in Nomsu. This way, they retain a notion of whether they were originally lists or dicts.
+
_list_mt =
+ __type: "List"
__eq:equivalent
-- Could consider adding a __newindex to enforce list-ness, but would hurt performance
__tostring: =>
- "["..concat([repr(b) for b in *@], ", ").."]"
+ "["..concat([as_nomsu(b) for b in *@], ", ").."]"
+ as_nomsu: =>
+ "["..concat([as_nomsu(b) for b in *@], ", ").."]"
+ as_lua: =>
+ "_List{"..concat([as_lua(b) for b in *@], ", ").."}"
__lt: (other)=>
assert type(@) == 'table' and type(other) == 'table', "Incompatible types for comparison"
for i=1,math.max(#@, #other)
@@ -36,30 +58,41 @@ _list_mt =
insert(ret, x)
return ret
__index:
- add_1: insert, append_1: insert
- add_1_at_index_2: (t,x,i)-> insert(t,i,x)
- at_index_1_add_2: insert
- pop: remove, remove_last: remove, remove_index_1: remove
+ add: insert, append: insert
+ add_1_at_index: (t,x,i)-> insert(t,i,x)
+ at_index_1_add: insert
+ pop: remove, remove_last: remove, remove_index: remove
last: (=> @[#@]), first: (=> @[1])
_1_st_to_last:nth_to_last, _1_nd_to_last:nth_to_last
_1_rd_to_last:nth_to_last, _1_th_to_last:nth_to_last
-- TODO: use stringify() to allow joining misc. objects?
joined: => table.concat([tostring(x) for x in *@]),
- joined_with_1: (glue)=> table.concat([tostring(x) for x in *@], glue),
- has_1: (item)=>
+ joined_with: (glue)=> table.concat([tostring(x) for x in *@], glue),
+ has: (item)=>
for x in *@
if x == item
return true
return false
- index_of_1: (item)=>
+ remove: (item)=>
+ for i,x in ipairs @
+ if x == item
+ remove(@, i)
+ index_of: (item)=>
for i,x in ipairs @
if x == item
return i
return nil
+ from_1_to: (start, stop)=>
+ n = #@
+ start = (n+1-start) if n < 0
+ stop = (n+1-stop) if n < 0
+ return [@[i] for i=start,stop]
-- TODO: remove this safety check to get better performance?
__newindex: (k,v)=>
assert type(k) == 'number', "List indices must be numbers"
rawset(@, k, v)
+_list_mt.__index.as_lua = _list_mt.as_lua
+_list_mt.__index.as_nomsu = _list_mt.as_nomsu
List = (t)-> setmetatable(t, _list_mt)
@@ -71,10 +104,15 @@ walk_items = (i)=>
return i, Dict{key:k, value:v}
_dict_mt =
+ __type: "Dict"
__eq:equivalent
__len:size
__tostring: =>
- "{"..concat(["#{repr(k)}: #{repr(v)}" for k,v in pairs @], ", ").."}"
+ "{"..concat(["#{as_nomsu(k)}: #{as_nomsu(v)}" for k,v in pairs @], ", ").."}"
+ as_nomsu: =>
+ "{"..concat(["#{as_nomsu(k)}: #{as_nomsu(v)}" for k,v in pairs @], ", ").."}"
+ as_lua: =>
+ "_Dict{"..concat(["[ #{as_lua(k)}]= #{as_lua(v)}" for k,v in pairs @], ", ").."}"
__ipairs: => walk_items, {table:@, key:nil}, 0
__band: (other)=>
Dict{k,v for k,v in pairs(@) when other[k] != nil}
@@ -109,15 +147,17 @@ for i,entry in ipairs(Dict({x:99}))
do
{:reverse, :upper, :lower, :find, :byte, :match, :gmatch, :gsub, :sub, :format, :rep} = string
string2 = require 'string2'
- {:lines, :line, :line_at, :as_lua_id} = string2
+ {:lines, :line, :line_at, :as_lua_id, :is_lua_id} = string2
text_methods =
- formatted_with_1:format, byte_1:byte, position_of_1:find, position_of_1_after_2:find,
- bytes_1_to_2: (start, stop)=> List{byte(tostring(@), start, stop)}
- [as_lua_id "with 1 -> 2"]: gsub
+ formatted_with:format, byte:byte, position_of:find, position_of_1_after:find,
+ as_a_lua_identifier: as_lua_id, is_a_lua_identifier: is_lua_id,
+ as_a_lua_id: as_lua_id, is_a_lua_id: is_lua_id,
+ bytes_1_to: (start, stop)=> List{byte(tostring(@), start, stop)}
+ [as_lua_id "with 1 ->"]: gsub
bytes: => List{byte(tostring(@), 1, -1)},
lines: => List(lines(@))
- line_1: line
- wrap_to_1: (maxlen)=>
+ line: line
+ wrapped_to: (maxlen)=>
_lines = {}
for line in *@lines!
while #line > maxlen
@@ -129,12 +169,14 @@ do
_lines[#_lines+1] = line
return table.concat(_lines, "\n")
- line_at_1: (i)=> (line_at(@, i))
- line_number_of_1: (i)=> select(2, line_at(@, i))
- line_position_of_1: (i)=> select(3, line_at(@, i))
- matches_1: (patt)=> match(@, patt) and true or false
+ line_at: (i)=> (line_at(@, i))
+ line_number_at: (i)=> select(2, line_at(@, i))
+ line_position_at: (i)=> select(3, line_at(@, i))
+ matches: (patt)=> match(@, patt) and true or false
+ matching: (patt)=> (match(@, patt))
+ matching_groups: (patt)=> {match(@, patt)}
[as_lua_id "* 1"]: (n)=> rep(@, n)
- matching_1: (patt)=>
+ all_matches_of: (patt)=>
result = {}
stepper,x,i = gmatch(@, patt)
while true
@@ -146,10 +188,13 @@ do
setmetatable(text_methods, {__index:string2})
+ getmetatable("").__methods = text_methods
getmetatable("").__index = (i)=>
-- Use [] for accessing text characters, or s[{3,4}] for s:sub(3,4)
if type(i) == 'number' then return sub(@, i, i)
elseif type(i) == 'table' then return sub(@, i[1], i[2])
else return text_methods[i]
+ getmetatable("").__add = (x)=> tostring(@)..tostring(x)
+
return {:List, :Dict}
diff --git a/core/collections.nom b/core/collections.nom
index 8dbc79d..ae7f5f1 100644
--- a/core/collections.nom
+++ b/core/collections.nom
@@ -1,4 +1,4 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This file contains code that supports manipulating and using collections like lists
and dictionaries.
@@ -45,17 +45,17 @@ test:
# List Comprehension
test:
assume (((% * %) for % in [1, 2, 3]) == [1, 4, 9])
-parse [%expression for %item in %iterable] as (..)
+(%expression for %item in %iterable) parses as (..)
result of:
%comprehension = []
for %item in %iterable:
%comprehension::add %expression
return %comprehension
-parse [..]
+[..]
%expression for %index in %start to %stop via %step
%expression for %index in %start to %stop by %step
-..as (..)
+..all parse as (..)
result of:
%comprehension = []
for %index in %start to %stop via %step:
@@ -66,15 +66,15 @@ parse [..]
test:
assume (((% * %) for % in 1 to 3) == [1, 4, 9])
-parse [%expression for %var in %start to %stop] as (..)
+(%expression for %var in %start to %stop) parses as (..)
%expression for %var in %start to %stop via 1
test:
assume (("\%k,\%v" for %k = %v in {x:1}) == ["x,1"])
-parse [..]
+[..]
%expression for %key = %value in %iterable
%expression for %key %value in %iterable
-..as (..)
+..all parse as (..)
result of:
%comprehension = []
for %key = %value in %iterable:
@@ -84,7 +84,9 @@ parse [..]
# Dict comprehensions
test:
assume (((% * %) = % for % in [1, 2, 3]) == {1:1, 4:2, 9:3})
-parse [%key = %value for %item in %iterable, %key %value for %item in %iterable] as (..)
+[..]
+ %key = %value for %item in %iterable, %key %value for %item in %iterable
+..all parse as (..)
result of:
%comprehension = {}
for %item in %iterable:
@@ -93,20 +95,20 @@ parse [%key = %value for %item in %iterable, %key %value for %item in %iterable]
test:
assume ((%k = (%v * %v) for %k = %v in {x:1, y:2, z:3}) == {x:1, y:4, z:9})
-parse [..]
+[..]
%key = %value for %src_key = %src_value in %iterable
%key %value for %src_key %src_value in %iterable
-..as (..)
+..all parse as (..)
result of:
%comprehension = {}
for %src_key = %src_value in %iterable:
%comprehension.%key = %value
return %comprehension
-parse [..]
+[..]
%key = %value for %item in %start to %stop via %step
%key %value for %item in %start to %stop via %step
-..as (..)
+..all parse as (..)
result of:
%comprehension = {}
for %item in %start to %stop via %step:
@@ -117,14 +119,14 @@ parse [..]
test:
assume (((% * %) = % for % in 1 to 3) == {1:1, 4:2, 9:3})
-parse [..]
+[..]
%key = %value for %item in %start to %stop
%key %value for %item in %start to %stop
-..as (%key = %value for %item in %start to %stop via 1)
+..all parse as (%key = %value for %item in %start to %stop via 1)
test:
assume (([[1, 2], [3, 4]] flattened) == [1, 2, 3, 4])
-action [%lists flattened]:
+externally (%lists flattened) means:
%flat = []
for %list in %lists:
for %item in %list: %flat::add %item
@@ -132,27 +134,30 @@ action [%lists flattened]:
test:
assume ((entries in {x:1}) == [{key:"x", value:1}])
-parse [entries in %dict] as ({key:%k, value:%v} for %k = %v in %dict)
+(entries in %dict) parses as ({key:%k, value:%v} for %k = %v in %dict)
test:
assume ((keys in {x:1}) == ["x"])
-parse [keys in %dict, keys of %dict] as (%k for %k = %v in %dict)
+[keys in %dict, keys of %dict] all parse as (%k for %k = %v in %dict)
test:
assume ((values in {x:1}) == [1])
-parse [values in %dict, values of %dict] as (%v for %k = %v in %dict)
+[values in %dict, values of %dict] all parse as (%v for %k = %v in %dict)
# Metatable stuff
test:
%t = {}
set %t 's metatable to {__tostring:[%] -> "XXX"}
assume ("\%t" == "XXX")
-compile [set %dict 's metatable to %metatable] to (..)
+(set %dict 's metatable to %metatable) compiles to (..)
Lua "setmetatable(\(%dict as lua expr), \(%metatable as lua expr));"
+[% 's metatable, % 'metatable] all compile to (..)
+ Lua value "getmetatable(\(% as lua expr))"
+
test:
assume (({} with fallback % -> (% + 1)).10 == 11)
-compile [%dict with fallback %key -> %value] to (..)
+(%dict with fallback %key -> %value) compiles to (..)
Lua value "\
..(function(d)
local mt = {}
@@ -175,8 +180,10 @@ test:
%keys = {1:999, 2:0, 3:50}
sort %x by % = %keys.%
assume (%x == [2, 3, 1])
-compile [sort %items] to (Lua "table.sort(\(%items as lua expr));")
-parse [sort %items by %item = %key_expr, sort %items by %item -> %key_expr] as (..)
+(sort %items) compiles to (Lua "table.sort(\(%items as lua expr));")
+[..]
+ sort %items by %item = %key_expr, sort %items by %item -> %key_expr
+..all parse as (..)
do:
%keys = ({} with fallback %item -> %key_expr)
lua> "table.sort(\%items, function(x,y) return \%keys[x] < \%keys[y] end)"
@@ -185,12 +192,12 @@ parse [sort %items by %item = %key_expr, sort %items by %item -> %key_expr] as (
test:
assume ((sorted [3, 1, 2]) == [1, 2, 3])
-action [%items sorted, sorted %items]:
+externally [%items sorted, sorted %items] all mean:
%copy = (% for % in %items)
sort %copy
return %copy
-parse [%items sorted by %item = %key, %items sorted by %item -> %key] as (..)
+[%items sorted by %item = %key, %items sorted by %item -> %key] all parse as (..)
result of:
%copy = (% for % in %items)
sort %copy by %item = %key
@@ -198,7 +205,7 @@ parse [%items sorted by %item = %key, %items sorted by %item -> %key] as (..)
test:
assume ((unique [1, 2, 1, 3, 2, 3]) == [1, 2, 3])
-action [unique %items]:
+externally (unique %items) means:
%unique = []
%seen = {}
for % in %items:
diff --git a/core/control_flow.nom b/core/control_flow.nom
index a9e0ae0..bbc98f1 100644
--- a/core/control_flow.nom
+++ b/core/control_flow.nom
@@ -1,4 +1,4 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This file contains compile-time actions that define basic control flow structures
like "if" statements and loops.
@@ -10,31 +10,35 @@ use "core/errors.nom"
# No-Op
test: do nothing
-compile [do nothing] to (Lua "")
+(do nothing) compiles to (Lua "")
# Conditionals
test:
if (no):
barf "conditional fail"
-compile [if %condition %if_body] to (..)
- Lua "\
- ..if \(%condition as lua expr) then
- \(%if_body as lua statements)
- end"
+(if %condition %if_body) compiles to:
+ %lua = (Lua "if ")
+ %lua::append (%condition as lua expr)
+ %lua::append " then\n "
+ %lua::append (%if_body as lua statements)
+ %lua::append "\nend"
+ return %lua
test:
unless (yes):
barf "conditional fail"
-parse [unless %condition %unless_body] as (if (not %condition) %unless_body)
-compile [..]
+(unless %condition %unless_body) parses as (if (not %condition) %unless_body)
+[..]
if %condition %if_body else %else_body, unless %condition %else_body else %if_body
-..to (..)
- Lua "\
- ..if \(%condition as lua expr) then
- \(%if_body as lua statements)
- else
- \(%else_body as lua statements)
- end"
+..all compile to:
+ %lua = (Lua "if ")
+ %lua::append (%condition as lua expr)
+ %lua::append " then\n "
+ %lua::append (%if_body as lua statements)
+ %lua::append "\nelse\n "
+ %lua::append (%else_body as lua statements)
+ %lua::append "\nend"
+ return %lua
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -44,12 +48,12 @@ compile [..]
test:
assume ((1 if (yes) else 2) == 1)
assume ((1 if (no) else 2) == 2)
-compile [..]
+[..]
%when_true_expr if %condition else %when_false_expr
%when_true_expr if %condition otherwise %when_false_expr
%when_false_expr unless %condition else %when_true_expr
%when_false_expr unless %condition then %when_true_expr
-..to (..)
+..all compile to:
# If %when_true_expr is guaranteed to be truthy, we can use Lua's idiomatic
equivalent of a conditional expression: (cond and if_true or if_false)
if {Text:yes, List:yes, Dict:yes, Number:yes}.(%when_true_expr.type):
@@ -72,6 +76,8 @@ compile [..]
end
end)())"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
# GOTOs
test:
%i = 0
@@ -79,14 +85,37 @@ test:
%i += 1
unless (%i == 10): go to %loop
assume (%i == 10)
-compile [=== %label ===, --- %label ---, *** %label ***] to (..)
- Lua "::label_\(%label as lua identifier)::"
+ === (Loop) ===
+ %i -= 1
+ unless (%i == 0): go to (Loop)
+ assume (%i == 0)
+[=== %label ===, --- %label ---, *** %label ***] all compile to (..)
+ Lua "\
+ ..::label_\(..)
+ (%label.stub if (%label.type == "Action") else %label) as lua identifier
+ ..::"
-compile [go to %label] to (Lua "goto label_\(%label as lua identifier)")
+(go to %label) compiles to (..)
+ Lua "\
+ ..goto label_\(..)
+ (%label.stub if (%label.type == "Action") else %label) as lua identifier
+ .."
# Basic loop control
-compile [do next] to (Lua "goto continue")
-compile [stop] to (Lua "break")
+(stop %var) compiles to:
+ if %var:
+ return (..)
+ Lua "goto stop_\((%var.stub if (%var.type == "action") else %var) as lua identifier)"
+ ..else: return (Lua "break")
+(do next %var) compiles to:
+ if %var:
+ return (..)
+ Lua "goto continue_\((%var.stub if (%var.type == "action") else %var) as lua identifier)"
+ ..else: return (Lua "goto continue")
+[===stop %var ===, ---stop %var ---, ***stop %var ***] all compile to (..)
+ Lua "::stop_\((%var.stub if (%var.type == "action") else %var) as lua identifier)::"
+[===next %var ===, ---next %var ---, ***next %var ***] all compile to (..)
+ Lua "::continue_\((%var.stub if (%var.type == "action") else %var) as lua identifier)::"
# While loops
test:
@@ -108,9 +137,9 @@ test:
barf "Failed to 'do next repeat'"
assume (%x == 30)
-compile [do next repeat] to (Lua "goto continue_repeat")
-compile [stop repeating] to (Lua "goto stop_repeat")
-compile [repeat while %condition %body] to:
+(do next repeat) compiles to (Lua "goto continue_repeat")
+(stop repeating) compiles to (Lua "goto stop_repeat")
+(repeat while %condition %body) compiles to:
%lua = (..)
Lua "\
..while \(%condition as lua expr) do
@@ -122,53 +151,44 @@ compile [repeat while %condition %body] to:
%lua::append "\n ::continue_repeat::"
%lua::append "\nend --while-loop"
if (%body has subtree \(stop repeating)):
- %lua = (..)
- Lua "\
- ..do -- scope of "stop repeating" label
- \%lua
- ::stop_repeat::
- end -- end of "stop repeating" label scope"
+ %inner_lua = %lua
+ %lua = (Lua "do -- scope of 'stop repeating' label\n ")
+ %lua::append %inner_lua
+ %lua::append "\
+ ..
+ ::stop_repeat::
+ end -- end of 'stop repeating' label scope"
return %lua
-parse [repeat %body] as (repeat while (yes) %body)
-parse [repeat until %condition %body] as (repeat while (not %condition) %body)
+(repeat %body) parses as (repeat while (yes) %body)
+(repeat until %condition %body) parses as (repeat while (not %condition) %body)
test:
%x = 0
repeat 10 times: %x += 1
assume (%x == 10)
-compile [repeat %n times %body] to:
+(repeat %n times %body) compiles to:
define mangler
%lua = (..)
- Lua "\
- ..for \(mangle "i")=1,\(%n as lua expr) do
- \(%body as lua statements)"
-
+ Lua "for \(mangle "i")=1,\(%n as lua expr) do\n "
+ %lua::append (%body as lua statements)
if (%body has subtree \(do next)):
%lua::append "\n ::continue::"
if (%body has subtree \(do next repeat)):
%lua::append "\n ::continue_repeat::"
%lua::append "\nend --numeric for-loop"
if (%body has subtree \(stop repeating)):
- %lua = (..)
- Lua "\
- ..do -- scope of "stop repeating" label
- \%lua
- ::stop_repeat::
- end -- end of "stop repeating" label scope"
+ %inner_lua = %lua
+ %lua = (Lua "do -- scope of 'stop repeating' label\n ")
+ %lua::append %inner_lua
+ %lua::append "\
+ ..
+ ::stop_repeat::
+ end -- end of 'stop repeating' label scope"
return %lua
-# For loop control flow
-compile [stop %var] to (Lua "goto stop_\(%var as lua identifier)")
-compile [do next %var] to (Lua "goto continue_\(%var as lua identifier)")
-compile [===stop %var ===, ---stop %var ---, ***stop %var ***] to (..)
- Lua "::stop_\(%var as lua identifier)::"
-
-compile [===next %var ===, ---next %var ---, ***next %var ***] to (..)
- Lua "::continue_\(%var as lua identifier)::"
-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test:
@@ -191,38 +211,41 @@ test:
assume (%nums == [1, -2, 3, -2, 3, 4, 3, 4, 5])
# Numeric range for loops
-compile [..]
+[..]
for %var in %start to %stop by %step %body
for %var in %start to %stop via %step %body
-..to:
+..all compile to:
# This uses Lua's approach of only allowing loop-scoped variables in a loop
unless (%var.type is "Var"):
- compile error at %var "Expected a variable here, not a \(%var.type)."
+ compile error at %var "Expected a variable here, not a \(%var.type)"
%lua = (..)
Lua "\
..for \(%var as lua expr)=\(%start as lua expr),\(%stop as lua expr),\(..)
%step as lua expr
- .. do
- \(%body as lua statements)"
+ .. do"
+ %lua::append "\n "
+ %lua::append (%body as lua statements)
if (%body has subtree \(do next)):
%lua::append "\n ::continue::"
if (%body has subtree \(do next %var)):
- %lua::append "\n \(compile as (===next %var ===))"
+ %lua::append "\n "
+ %lua::append (what (===next %var ===) compiles to)
+
%lua::append "\nend --numeric for-loop"
if (%body has subtree \(stop %var)):
- %lua = (..)
- Lua "\
- ..do -- scope for stopping for-loop
- \%lua
- \(compile as (===stop %var ===))
- end -- end of scope for stopping for-loop"
+ %inner_lua = %lua
+ %lua = (Lua "do -- scope for stopping for-loop\n ")
+ %lua::append %inner_lua
+ %lua::append "\n "
+ %lua::append (what (===stop %var ===) compiles to)
+ %lua::append "\nend -- end of scope for stopping for-loop"
return %lua
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-parse [for %var in %start to %stop %body] as (..)
+(for %var in %start to %stop %body) parses as (..)
for %var in %start to %stop via 1 %body
test:
@@ -239,28 +262,49 @@ test:
assume (%b == [20, 30, 40])
# For-each loop (lua's "ipairs()")
-compile [for %var in %iterable %body] to:
- # This uses Lua's approach of only allowing loop-scoped variables in a loop
- unless (%var.type is "Var"):
- compile error at %var "Expected a variable here, not a \(%var.type)."
+(for %var in %iterable %body) compiles to:
define mangler
+ # This uses Lua's approach of only allowing loop-scoped variables in a loop
%lua = (..)
- Lua "\
- ..for \(mangle "i"),\(%var as lua identifier) in ipairs(\(%iterable as lua expr)) do
- \(%body as lua statements)"
+ Lua "for \(mangle "i"),\(%var as lua identifier) in ipairs(\(%iterable as lua expr)) do\n "
+ %lua::append (%body as lua statements)
+ if (%body has subtree \(do next)):
+ %lua::append "\n ::continue::"
+ if (%body has subtree \(do next %var)):
+ %lua::append "\n "
+ %lua::append (what (===next %var ===) compiles to)
+ %lua::append "\nend --foreach-loop"
+ if (%body has subtree \(stop %var)):
+ %inner_lua = %lua
+ %lua = (Lua "do -- scope for stopping for-loop\n ")
+ %lua::append %inner_lua
+ %lua::append "\n "
+ %lua::append (what (===stop %var ===) compiles to)
+ %lua::append "\nend -- end of scope for stopping for-loop"
+
+ return %lua
+
+# TODO: reduce code duplication
+(for %var in %iterable at %i %body) compiles to:
+ # This uses Lua's approach of only allowing loop-scoped variables in a loop
+ %lua = (..)
+ Lua "for \(%i as lua identifier),\(%var as lua identifier) in ipairs(\(%iterable as lua expr)) do\n "
+ %lua::append (%body as lua statements)
if (%body has subtree \(do next)):
%lua::append "\n ::continue::"
if (%body has subtree \(do next %var)):
- %lua::append (Lua "\n\(compile as (===next %var ===))")
+ %lua::append "\n "
+ %lua::append (what (===next %var ===) compiles to)
+
%lua::append "\nend --foreach-loop"
if (%body has subtree \(stop %var)):
- %lua = (..)
- Lua "\
- ..do -- scope for stopping for-loop
- \%lua
- \(compile as (===stop %var ===))
- end -- end of scope for stopping for-loop"
+ %inner_lua = %lua
+ %lua = (Lua "do -- scope for stopping for-loop\n ")
+ %lua::append %inner_lua
+ %lua::append "\n "
+ %lua::append (what (===stop %var ===) compiles to)
+ %lua::append "\nend -- end of scope for stopping for-loop"
return %lua
@@ -275,46 +319,55 @@ test:
assume ((%result sorted) == ["c = 30", "d = 40", "e = 50"])
# Dict iteration (lua's "pairs()")
-compile [..]
+[..]
for %key = %value in %iterable %body, for %key %value in %iterable %body
-..to:
+..all compile to:
# This uses Lua's approach of only allowing loop-scoped variables in a loop
unless (%key.type is "Var"):
- compile error at %key "Expected a variable here, not a \(%key.type)."
+ compile error at %key "Expected a variable here, not a \(%key.type)"
unless (%value.type is "Var"):
- compile error at %value "Expected a variable here, not a \(%value.type)."
+ compile error at %value "Expected a variable here, not a \(%value.type)"
%lua = (..)
Lua "\
..for \(%key as lua identifier),\(%value as lua identifier) in pairs(\(..)
%iterable as lua expr
- ..) do
- \(%body as lua statements)"
+ ..) do"
+ %lua::append "\n "
+ %lua::append (%body as lua statements)
if (%body has subtree \(do next)):
%lua::append "\n ::continue::"
if (%body has subtree \(do next %key)):
- %lua::append (Lua "\n\(compile as (===next %key ===))")
+ %lua::append "\n "
+ %lua::append (what (===next %key ===) compiles to)
+
if (%body has subtree \(do next %value)):
- %lua::append (Lua "\n\(compile as (===next %value ===))")
+ %lua::append "\n "
+ %lua::append (what (===next %value ===) compiles to)
+
%lua::append "\nend --foreach-loop"
%stop_labels = (Lua "")
if (%body has subtree \(stop %key)):
- %stop_labels::append "\n\(compile as (===stop %key ===))"
+ %stop_labels::append "\n"
+ %stop_labels::append (what (===stop %key ===) compiles to)
+
if (%body has subtree \(stop %value)):
- %stop_labels::append "\n\(compile as (===stop %value ===))"
+ %stop_labels::append "\n"
+ %stop_labels::append (what (===stop %value ===) compiles to)
+
if ((size of "\%stop_labels") > 0):
- %lua = (..)
- Lua "\
- ..do -- scope for stopping for % = % loop
- \%lua\%stop_labels
- end"
+ %inner_lua = %lua
+ %lua = (Lua "do -- scope for stopping for % = % loop\n ")
+ %lua::append %inner_lua
+ %lua::append %stop_labels
+ %lua::append "\nend"
return %lua
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test:
- if:
+ when:
(1 == 2) (100 < 0):
barf "bad conditional"
(1 == 0) (1 == 1) %not_a_variable.x: do nothing
@@ -326,13 +379,14 @@ test:
barf "bad conditional"
# Multi-branch conditional (if..elseif..else)
-compile [if %body, when %body] to:
+(when %body) compiles to:
%code = (Lua "")
%clause = "if"
%else_allowed = (yes)
unless (%body.type is "Block"):
compile error at %body "'if' expected a Block, but got a \(%body.type)."
..hint "Perhaps you forgot to put a ':' after 'if'?"
+
for %line in %body:
unless (..)
((%line.type is "Action") and ((size of %line) >= 2)) and (..)
@@ -347,6 +401,7 @@ compile [if %body, when %body] to:
unless %else_allowed:
compile error at %line "You can't have two 'else' blocks."
..hint "Merge all of the 'else' blocks together."
+
unless ((size of "\%code") > 0):
compile error at %line "\
..You can't have an 'else' block without a preceeding condition"
@@ -354,23 +409,19 @@ compile [if %body, when %body] to:
..need a conditional block around it. Otherwise, make sure the 'else' \
..block comes last."
- %code::append "\
- ..
- else
- \(%action as lua statements)"
-
+ %code::append "\nelse\n "
+ %code::append (%action as lua statements)
%else_allowed = (no)
..else:
- %code::append "\%clause "
+ %code::append %clause
+ %code::append " "
for %i in 1 to ((size of %line) - 1):
if (%i > 1):
%code::append " or "
%code::append (%line.%i as lua expr)
- %code::append "\
- .. then
- \(%action as lua statements)"
-
+ %code::append " then\n "
+ %code::append (%action as lua statements)
%clause = "\nelseif"
if ((size of "\%code") == 0):
@@ -390,7 +441,7 @@ test:
barf "bad switch statement"
# Switch statement
-compile [if %branch_value is %body, when %branch_value is %body] to:
+[if %branch_value is %body, when %branch_value is %body] all compile to:
%code = (Lua "")
%clause = "if"
%else_allowed = (yes)
@@ -419,42 +470,42 @@ compile [if %branch_value is %body, when %branch_value is %body] to:
..need a conditional block around it. Otherwise, make sure the 'else' \
..block comes last."
- %code::append "\
- ..
- else
- \(%action as lua statements)"
-
+ %code::append "\nelse\n "
+ %code::append (%action as lua statements)
%else_allowed = (no)
..else:
- %code::append "\%clause "
+ %code::append %clause
+ %code::append " "
for %i in 1 to ((size of %line) - 1):
if (%i > 1):
%code::append " or "
- %code::append "\(mangle "branch value") == \(%line.%i as lua expr)"
-
- %code::append "\
- .. then
- \(%action as lua statements)"
+ %code::append "\(mangle "branch value") == "
+ %code::append (%line.%i as lua expr)
+ %code::append " then\n "
+ %code::append (%action as lua statements)
%clause = "\nelseif"
if ((size of "\%code") == 0):
compile error at %body "'if' block has an empty body."
..hint "This means nothing would happen, so the 'if' block should be deleted."
%code::append "\nend --when"
- return (..)
+ %lua = (..)
Lua "\
- ..do --if % is
- local \(mangle "branch value") = \(%branch_value as lua expr)
- \%code
- end --if % is"
+ ..do --if % is...
+ local \(mangle "branch value") = "
+ %lua::append (%branch_value as lua expr)
+ %lua::append "\n "
+ %lua::append %code
+ %lua::append "\nend --if % is..."
+ return %lua
# Do/finally
-compile [do %action] to (..)
- Lua "\
- ..do
- \(%action as lua statements)
- end --do"
+(do %action) compiles to:
+ %lua = (Lua "do\n ")
+ %lua::append (%action as lua statements)
+ %lua::append "\nend -- do"
+ return %lua
test:
%d = {}
@@ -463,60 +514,63 @@ test:
%d.x = "bad"
barf
..then always: %d.x = "good"
- ..and if it barfs: do nothing
-
assume (%d.x == "good")
-compile [do %action then always %final_action] to:
+(do %action then always %final_action) compiles to:
define mangler
- return (..)
+ %lua = (..)
Lua "\
..do
local \(mangle "fell_through") = false
- local \(mangle "ok"), \(mangle "ret") = pcall(function()
- \(%action as lua statements)
- \(mangle "fell_through") = true
- end)
- \(%final_action as lua statements)
- if not \(mangle "ok") then error(ret, 0) end
- if not \(mangle "fell_through") then return ret end
- end"
+ local \(mangle "ok"), \(mangle "ret") = pcall(function()"
+ %lua::append "\n "
+ %lua::append (%action as lua statements)
+ %lua::append "\
+ .. \(mangle "fell_through") = true
+ end)"
+ %lua::append "\n "
+ %lua::append (%final_action as lua statements)
+ %lua::append "\
+ .. if not \(mangle "ok") then error(ret, 0) end
+ if not \(mangle "fell_through") then return ret end
+ end"
+ return %lua
test:
assume ((result of (: return 99)) == 99)
# Inline thunk:
-compile [result of %body] to (Lua value "\(compile as ([] -> %body))()")
+(result of %body) compiles to (Lua value "\(what ([] -> %body) compiles to)()")
test:
%t = [1, [2, [[3], 4], 5, [[[6]]]]]
%flat = []
for % in recursive %t:
- if ((type of %) is "table"):
+ if ((lua type of %) is "table"):
for %2 in %: recurse % on %2
..else: %flat::add %
- assume ((sorted %flat) == [1, 2, 3, 4, 5, 6])
+ assume (sorted %flat) == [1, 2, 3, 4, 5, 6]
# Recurion control flow
-compile [for %var in recursive %structure %body] to (..)
+(for %var in recursive %structure %body) compiles to:
with local compile actions:
define mangler
- compile [recurse %v on %x] to (..)
+ (recurse %v on %x) compiles to (..)
Lua "table.insert(\(mangle "stack \(%v.1)"), \(%x as lua expr))"
%lua = (..)
Lua "\
..do
local \(mangle "stack \(%var.1)") = _List{\(%structure as lua expr)}
while #\(mangle "stack \(%var.1)") > 0 do
- \(%var as lua expr) = table.remove(\(mangle "stack \(%var.1)"), 1)
- \(%body as lua statements)"
-
+ \(%var as lua expr) = table.remove(\(mangle "stack \(%var.1)"), 1)"
+ %lua::append "\n "
+ %lua::append (%body as lua statements)
if (%body has subtree \(do next)):
%lua::append "\n ::continue::"
if (%body has subtree \(do next %var)):
- %lua::append "\n \(compile as (===next %var ===))"
+ %lua::append "\n \(what (===next %var ===) compiles to)"
%lua::append "\n end -- Recursive loop"
if (%body has subtree \(stop %var)):
- %lua::append "\n \(compile as (===stop %var ===))"
+ %lua::append "\n \(what (===stop %var ===) compiles to)"
%lua::append "\nend -- Recursive scope"
return %lua
diff --git a/core/coroutines.nom b/core/coroutines.nom
index e7ec41e..246a2ef 100644
--- a/core/coroutines.nom
+++ b/core/coroutines.nom
@@ -1,4 +1,4 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This file defines the code that creates and manipulates coroutines
@@ -14,15 +14,15 @@ test:
for % in coroutine %co: %nums::add %
assume (%nums == [4, 5, 6, 6, 6]) or barf "Coroutine iteration failed"
-compile [coroutine %body, generator %body] to (..)
+[coroutine %body, generator %body] all compile to (..)
Lua value "\
..(function()
\(%body as lua statements)
end)"
-compile [->] to (Lua value "coroutine.yield(true)")
-compile [-> %] to (Lua value "coroutine.yield(true, \(% as lua expr))")
-compile [for % in coroutine %co %body] to (..)
+(->) compiles to (Lua value "coroutine.yield(true)")
+(-> %) compiles to (Lua value "coroutine.yield(true, \(% as lua expr))")
+(for % in coroutine %co %body) compiles to (..)
Lua "\
..for junk,\(% as lua expr) in coroutine.wrap(\(%co as lua expr)) do
\(%body as lua statements)
diff --git a/core/errors.nom b/core/errors.nom
index 1157cb1..0b0a6a3 100644
--- a/core/errors.nom
+++ b/core/errors.nom
@@ -1,17 +1,16 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This file contains basic error reporting code
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 %tree %msg] to (..)
+(barf %msg) compiles to (Lua "error(\(=lua "\%msg and \(%msg as lua expr) or 'nil'"), 0);")
+(compile error at %tree %msg) compiles to (..)
Lua "nomsu:compile_error(\(%tree as lua expr), \(%msg as lua expr))"
-compile [compile error at %tree %msg hint %hint] to (..)
+(compile error at %tree %msg hint %hint) compiles to (..)
Lua "nomsu:compile_error(\(%tree as lua expr), \(%msg as lua expr), \(%hint as lua expr))"
-compile [assume %condition] to:
+(assume %condition) compiles to:
lua> "\
..local \%assumption = 'Assumption failed: '..tostring(nomsu:tree_to_nomsu(\%condition))"
return (..)
@@ -20,7 +19,7 @@ compile [assume %condition] to:
error(\(quote "\%assumption"), 0)
end"
-compile [assume %a == %b] to:
+(assume %a == %b) compiles to:
lua> "\
..local \%assumption = 'Assumption failed: '..tostring(nomsu:tree_to_nomsu(\(\(%a == %b))))"
define mangler
@@ -35,7 +34,7 @@ compile [assume %a == %b] to:
end
end"
-compile [assume %condition or barf %message] to (..)
+(assume %condition or barf %message) compiles to (..)
Lua "\
..if not \(%condition as lua expr) then
error(\(%message as lua expr), 0)
@@ -55,10 +54,10 @@ test:
assume (%x == 3) or barf "do/then always failed"
# Try/except
-compile [..]
+[..]
try %action and if it succeeds %success or if it barfs %msg %fallback
try %action and if it barfs %msg %fallback or if it succeeds %success
-..to (..)
+..all compile to (..)
Lua "\
..do
local fell_through = false
@@ -66,9 +65,9 @@ compile [..]
local ok, ret = xpcall(function()
\(%action as lua statements)
fell_through = true
- end, function(\(%msg as lua expr))
+ end, function(\(=lua "\%fallback and \(%msg as lua expr) or ''"))
local ok, ret = pcall(function()
- \(%fallback as lua statements)
+ \((=lua "\%fallback or \%msg") as lua statements)
end)
if not ok then err, erred = ret, true end
end)
@@ -84,21 +83,23 @@ compile [..]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-parse [..]
- try %action and if it succeeds %success or if it barfs %fallback
- try %action and if it barfs %fallback or if it succeeds %success
-..as (try %action and if it succeeds %success or if it barfs (=lua "") %fallback)
+#
+ [..]
+ try %action and if it succeeds %success or if it barfs %fallback
+ try %action and if it barfs %fallback or if it succeeds %success
+ ..all parse as (..)
+ try %action and if it succeeds %success or if it barfs (=lua "") %fallback
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-parse [try %action] as (..)
+(try %action) parses as (..)
try %action and if it succeeds (do nothing) or if it barfs (do nothing)
-parse [try %action and if it barfs %fallback] as (..)
+#(try %action and if it barfs %fallback) parses as (..)
try %action and if it succeeds (do nothing) or if it barfs %fallback
-parse [try %action and if it barfs %msg %fallback] as (..)
+(try %action and if it barfs %msg %fallback) parses as (..)
try %action and if it succeeds (do nothing) or if it barfs %msg %fallback
-parse [try %action and if it succeeds %success] as (..)
+(try %action and if it succeeds %success) parses as (..)
try %action and if it succeeds %success or if it barfs (do nothing)
diff --git a/core/id.nom b/core/id.nom
index d3b2b71..a9231b9 100644
--- a/core/id.nom
+++ b/core/id.nom
@@ -1,4 +1,4 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
A simple UUID function based on RFC 4122: http://www.ietf.org/rfc/rfc4122.txt
@@ -27,7 +27,7 @@ set %id_by_obj 's metatable to {..}
%obj_by_id.%id = %key
return %id
-action [uuid]:
+externally (uuid) means:
# Set all the other bits to randomly (or pseudo-randomly) chosen values.
%bytes = [..]
# time-low, time-mid, time-high-and-version
@@ -56,4 +56,4 @@ test:
seed random with 0
assume ((id of %x) != (id of []))
seed random
-action [id of %, %'s id, %' id] %id_by_obj.%
+externally [id of %, %'s id, %'id] all mean %id_by_obj.%
diff --git a/core/io.nom b/core/io.nom
index 1e87209..6b67b50 100644
--- a/core/io.nom
+++ b/core/io.nom
@@ -1,10 +1,10 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This file contains basic input/output code
use "core/metaprogramming.nom"
-compile [say %message] to (..)
+(say %message) compiles to (..)
lua> "\
..if \%message.type == "Text" then
return LuaCode(tree.source, "print(", \(%message as lua expr), ");");
@@ -12,7 +12,7 @@ compile [say %message] to (..)
return LuaCode(tree.source, "print(tostring(", \(%message as lua expr), "));");
end"
-compile [ask %prompt] to (..)
+(ask %prompt) compiles to (..)
lua> "\
..if \%prompt.type == "Text" then
return LuaCode.Value(tree.source, "(io.write(", \(%prompt as lua expr), ") and io.read())");
diff --git a/core/math.nom b/core/math.nom
index d6ddbce..66f5aba 100644
--- a/core/math.nom
+++ b/core/math.nom
@@ -1,4 +1,4 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This file defines some common math literals and functions
@@ -14,17 +14,18 @@ test:
..math constants failed"
%nan = (NaN)
assume (%nan != %nan) or barf "NaN failed"
-compile [infinity, inf] to (Lua value "math.huge")
-compile [not a number, NaN, nan] to (Lua value "(0/0)")
-compile [pi, Pi, PI] to (Lua value "math.pi")
-compile [tau, Tau, TAU] to (Lua value "(2*math.pi)")
-compile [golden ratio] to (Lua value "((1+math.sqrt(5))/2)")
-compile [e] to (Lua value "math.exp(1)")
+[infinity, inf] all compile to (Lua value "math.huge")
+[not a number, NaN, nan] all compile to (Lua value "(0/0)")
+[pi, Pi, PI] all compile to (Lua value "math.pi")
+[tau, Tau, TAU] all compile to (Lua value "(2*math.pi)")
+(golden ratio) compiles to (Lua value "((1+math.sqrt(5))/2)")
+(e) compiles to (Lua value "math.exp(1)")
# Functions:
test:
assume (("5" as a number) == 5)
-compile [% as a number, % as number] to (Lua value "tonumber(\(% as lua expr))")
+[% as a number, % as number] all compile to (..)
+ Lua value "tonumber(\(% as lua expr))"
test:
assume (..)
@@ -33,79 +34,127 @@ test:
arc tangent 5, arc tangent 5 / 10, hyperbolic sine 5, hyperbolic cosine 5
hyperbolic tangent 5, e^ 5, ln 5, log base 2 of 5, floor 5, ceiling 5, round 5
..or barf "math functions failed"
-compile [absolute value %, | % |, abs %] to (..)
+[absolute value %, | % |, abs %] all compile to (..)
Lua value "math.abs(\(% as lua expr))"
-compile [square root %, square root of %, √ %, sqrt %] to (..)
+[square root %, square root of %, √ %, sqrt %] all compile to (..)
Lua value "math.sqrt(\(% as lua expr))"
-compile [sine %, sin %] to (Lua value "math.sin(\(% as lua expr))")
-compile [cosine %, cos %] to (Lua value "math.cos(\(% as lua expr))")
-compile [tangent %, tan %] to (Lua value "math.tan(\(% as lua expr))")
-compile [arc sine %, asin %] to (Lua value "math.asin(\(% as lua expr))")
-compile [arc cosine %, acos %] to (Lua value "math.acos(\(% as lua expr))")
-compile [arc tangent %, atan %] to (Lua value "math.atan(\(% as lua expr))")
-compile [arc tangent %y / %x, atan2 %y %x] to (..)
+[sine %, sin %] all compile to (Lua value "math.sin(\(% as lua expr))")
+[cosine %, cos %] all compile to (Lua value "math.cos(\(% as lua expr))")
+[tangent %, tan %] all compile to (Lua value "math.tan(\(% as lua expr))")
+[arc sine %, asin %] all compile to (Lua value "math.asin(\(% as lua expr))")
+[arc cosine %, acos %] all compile to (Lua value "math.acos(\(% as lua expr))")
+[arc tangent %, atan %] all compile to (Lua value "math.atan(\(% as lua expr))")
+[arc tangent %y / %x, atan2 %y %x] all compile to (..)
Lua value "math.atan2(\(%y as lua expr), \(%x as lua expr))"
-compile [hyperbolic sine %, sinh %] to (Lua value "math.sinh(\(% as lua expr))")
-compile [hyperbolic cosine %, cosh %] to (Lua value "math.cosh(\(% as lua expr))")
-compile [hyperbolic tangent %, tanh %] to (..)
+[hyperbolic sine %, sinh %] all compile to (..)
+ Lua value "math.sinh(\(% as lua expr))"
+
+[hyperbolic cosine %, cosh %] all compile to (..)
+ Lua value "math.cosh(\(% as lua expr))"
+
+[hyperbolic tangent %, tanh %] all compile to (..)
Lua value "math.tanh(\(% as lua expr))"
-compile [e^ %, exp %] to (Lua value "math.exp(\(% as lua expr))")
-compile [natural log %, ln %, log %] to (Lua value "math.log(\(% as lua expr))")
-compile [log % base %base, log base %base of %] to (..)
+[e^ %, exp %] all compile to (Lua value "math.exp(\(% as lua expr))")
+[natural log %, ln %, log %] all compile to (..)
+ Lua value "math.log(\(% as lua expr))"
+
+[log % base %base, log base %base of %] all compile to (..)
Lua value "math.log(\(% as lua expr), \(%base as lua expr))"
-compile [floor %] to (Lua value "math.floor(\(% as lua expr))")
-compile [ceiling %, ceil %] to (Lua value "math.ceil(\(% as lua expr))")
-compile [round %, % rounded] to (Lua value "math.floor(\(% as lua expr) + .5)")
+(floor %) compiles to (Lua value "math.floor(\(% as lua expr))")
+[ceiling %, ceil %] all compile to (Lua value "math.ceil(\(% as lua expr))")
+[round %, % rounded] all compile to (..)
+ Lua value "math.floor(\(% as lua expr) + .5)"
test:
assume ((463 to the nearest 100) == 500) or barf "rounding failed"
assume ((2.6 to the nearest 0.25) == 2.5) or barf "rounding failed"
-action [%n to the nearest %rounder] (..)
+externally (%n to the nearest %rounder) means (..)
=lua "(\%rounder)*math.floor((\%n / \%rounder) + .5)"
# Any/all/none
-compile [all of %items, all %items] to:
+[all of %items, all %items] all compile to:
unless (%items.type is "List"):
return (Lua value "utils.all(\(%items as lua expr))")
- %clauses = ((% as lua expr) for % in %items)
+ %clauses = (((% as lua expr)::text) for % in %items)
return (Lua value "(\(%clauses::joined with " and "))")
-parse [not all of %items, not all %items] as (not (all of %items))
-compile [any of %items, any %items] to:
+[not all of %items, not all %items] all parse as (not (all of %items))
+[any of %items, any %items] all compile to:
unless (%items.type is "List"):
return (Lua value "utils.any(\(%items as lua expr))")
- %clauses = ((% as lua expr) for % in %items)
+ %clauses = (((% as lua expr)::text) for % in %items)
return (Lua value "(\(%clauses::joined with " or "))")
-parse [none of %items, none %items] as (not (any of %items))
-compile [sum of %items, sum %items] to:
+[none of %items, none %items] all parse as (not (any of %items))
+[sum of %items, sum %items] all compile to:
unless (%items.type is "List"):
return (Lua value "utils.sum(\(%items as lua expr))")
- %clauses = ((% as lua expr) for % in %items)
+ %clauses = (((% as lua expr)::text) for % in %items)
return (Lua value "(\(%clauses::joined with " + "))")
-compile [product of %items, product %items] to:
+[if all of %items %body, if all of %items then %body] all parse as (..)
+ if (all of %items) %body
+
+[unless all of %items %body, unless all of %items then %body] all parse as (..)
+ if (not (all of %items)) %body
+
+[if any of %items %body, if any of %items then %body] all parse as (..)
+ if (any of %items) %body
+
+[unless any of %items %body, unless any of %items then %body] all parse as (..)
+ if (not (any of %items)) %body
+
+[if none of %items %body, if none of %items then %body] all parse as (..)
+ if (not (any of %items)) %body
+
+[unless none of %items %body, unless none of %items then %body] all parse as (..)
+ if (any of %items) %body
+
+[if all of %items %body else %else, if all of %items then %body else %else] all parse \
+..as (if (all of %items) %body else %else)
+
+[..]
+ unless all of %items %body else %else, unless all of %items then %body else %else
+..all parse as (if (not (all of %items)) %body else %else)
+
+[if any of %items %body else %else, if any of %items then %body else %else] all parse \
+..as (if (any of %items) %body else %else)
+
+[..]
+ unless any of %items %body else %else, unless any of %items then %body else %else
+..all parse as (if (not (any of %items)) %body else %else)
+
+[if none of %items %body else %else, if none of %items then %body else %else] all \
+..parse as (if (not (any of %items)) %body else %else)
+
+[..]
+ unless none of %items %body else %else, unless none of %items then %body else %else
+..all parse as (if (any of %items) %body else %else)
+
+[product of %items, product %items] all compile to:
unless (%items.type is "List"):
return (Lua value "utils.product(\(%items as lua expr))")
- %clauses = ((% as lua expr) for % in %items)
+ %clauses = (((% as lua expr)::text) for % in %items)
return (Lua value "(\(%clauses::joined with " * "))")
-action [avg of %items, average of %items] (=lua "(utils.sum(\%items)/#\%items)")
-compile [min of %items, smallest of %items, lowest of %items] to (..)
+externally [avg of %items, average of %items] all mean (..)
+ =lua "(utils.sum(\%items)/#\%items)"
+
+[min of %items, smallest of %items, lowest of %items] all compile to (..)
Lua value "utils.min(\(%items as lua expr))"
-compile [max of %items, biggest of %items, largest of %items, highest of %items] to (..)
- Lua value "utils.max(\(%items as lua expr))"
+[max of %items, biggest of %items, largest of %items, highest of %items] all compile \
+..to (Lua value "utils.max(\(%items as lua expr))")
test:
assume ((min of [3, -4, 1, 2] by % = (% * %)) == 1)
assume ((max of [3, -4, 1, 2] by % = (% * %)) == -4)
-parse [min of %items by %item = %value_expr] as (..)
+(min of %items by %item = %value_expr) parses as (..)
result of:
set {%best:nil, %best_key:nil}
for %item in %items:
@@ -115,7 +164,7 @@ parse [min of %items by %item = %value_expr] as (..)
return %best
-parse [max of %items by %item = %value_expr] as (..)
+(max of %items by %item = %value_expr) parses as (..)
result of:
set {%best:nil, %best_key:nil}
for %item in %items:
@@ -126,20 +175,19 @@ parse [max of %items by %item = %value_expr] as (..)
return %best
# Random functions
-action [seed random with %] (..)
+externally (seed random with %) means (..)
lua> "\
..math.randomseed(\%);
for i=1,20 do math.random(); end"
-parse [seed random] as (seed random with (=lua "os.time()"))
-compile [random number, random, rand] to (Lua value "math.random()")
-compile [random int %n, random integer %n, randint %n] to (..)
+(seed random) parses as (seed random with (=lua "os.time()"))
+[random number, random, rand] all compile to (Lua value "math.random()")
+[random int %n, random integer %n, randint %n] all compile to (..)
Lua value "math.random(\(%n as lua expr))"
-compile [..]
- random from %low to %high, random number from %low to %high
- rand %low %high
-..to (Lua value "math.random(\(%low as lua expr), \(%high as lua expr))")
+[random from %low to %high, random number from %low to %high, rand %low %high] all \
+..compile to (Lua value "math.random(\(%low as lua expr), \(%high as lua expr))")
-action [random choice from %elements, random choice %elements, random %elements] (..)
- =lua "\%elements[math.random(#\%elements)]"
+externally [..]
+ random choice from %elements, random choice %elements, random %elements
+..all mean (=lua "\%elements[math.random(#\%elements)]")
diff --git a/core/metaprogramming.nom b/core/metaprogramming.nom
index c50f783..61c877f 100644
--- a/core/metaprogramming.nom
+++ b/core/metaprogramming.nom
@@ -1,9 +1,9 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This File contains actions for making actions and compile-time actions and some helper
functions to make that easier.
-lua> "NOMSU_CORE_VERSION = 8"
+lua> "NOMSU_CORE_VERSION = 9"
lua> "\
..do
local mangle_index = 0
@@ -16,16 +16,16 @@ lua> "\
end
end
COMPILE_ACTIONS["define mangler"] = function(nomsu, tree)
- return LuaCode(tree.source, "local mangle_1 = mangler()")
+ return LuaCode(tree.source, "local mangle = mangler()")
end"
lua> "\
- ..COMPILE_ACTIONS["1 -> 2"] = function(nomsu, tree, \%args, \%body)
+ ..COMPILE_ACTIONS["1 ->"] = 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(nomsu:compile(a)) 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):text() 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)")
@@ -33,8 +33,8 @@ lua> "\
end"
lua> "\
- ..COMPILE_ACTIONS["compile as 1"] = function(nomsu, tree, \%action)
- local lua = LuaCode.Value(tree.source, "COMPILE_ACTIONS[", repr(\%action.stub), "](")
+ ..COMPILE_ACTIONS["what 1 compiles to"] = function(nomsu, tree, \%action)
+ local lua = LuaCode.Value(tree.source, "COMPILE_ACTIONS[", \%action.stub:as_lua(), "](")
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")
@@ -46,78 +46,84 @@ lua> "\
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test:
- compile [five] to (Lua value "5")
+ (five) compiles to (Lua value "5")
test:
assume ((five) == 5) or barf "Compile to expression failed."
- compile [loc x] to (Lua "local x = 99;")
+ (loc x) compiles to (Lua "local x = 99;")
test:
lua> "do"
loc x
assume (%x is 99) or barf "Compile to statements with locals failed."
lua> "end"
assume (%x is (nil)) or barf "Failed to properly localize a variable."
- compile [asdf] to:
+ (asdf) compiles to:
%tmp = ""
return (Lua %tmp)
test:
asdf
assume (%tmp is (nil)) or barf "compile to is leaking variables"
lua> "\
- ..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)))
+ ..COMPILE_ACTIONS["1 compiles to"] = function(nomsu, tree, \%actions, \%body)
+ if \%actions.type ~= "List" then \%actions = {\%actions, type="List"} end
+ local \%args = {"nomsu", "tree", unpack(table.map(\%actions[1]:get_args(), function(a) return nomsu:compile(a):text() end))}
+ local lua = LuaCode(tree.source, "COMPILE_ACTIONS[", \%actions[1].stub:as_lua(),
+ "] = ", \(what (%args -> %body) compiles to))
for i=2,#\%actions do
local alias = \%actions[i]
- 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), "] = ")
+ local \%alias_args = {"nomsu", "tree", unpack(table.map(alias:get_args(), function(a) return nomsu:compile(a):text() \
+ ..end))}
+ lua:append("\\nCOMPILE_ACTIONS[", alias.stub:as_lua(), "] = ")
if utils.equivalent(\%args, \%alias_args) then
- lua:append("COMPILE_ACTIONS[", repr(\%actions[1].stub), "]")
+ lua:append("COMPILE_ACTIONS[", \%actions[1].stub:as_lua(), "]")
else
lua:append("function(")
lua:concat_append(\%alias_args, ", ")
- lua:append(")\\n return COMPILE_ACTIONS[", repr(\%actions[1].stub), "](")
+ lua:append(")\\n return COMPILE_ACTIONS[", \%actions[1].stub:as_lua(), "](")
lua:concat_append(\%args, ", ")
lua:append(")\\nend")
end
end
return lua
- end"
+ end
+ COMPILE_ACTIONS["1 all compile to"] = COMPILE_ACTIONS["1 compiles to"]"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-compile [call %fn with %args] to:
+(call %fn with %args) compiles to:
lua> "\
..local lua = LuaCode.Value(tree.source, nomsu:compile(\%fn), "(")
- lua:concat_append(table.map(\%args, function(a) return nomsu:compile(a) end), ", ")
+ if \%args.type == 'List' then
+ lua:concat_append(table.map(\%args, function(a) return nomsu:compile(a) end), ", ")
+ else
+ lua:append('unpack(', nomsu:compile(\%args), ')')
+ end
lua:append(")")
return lua"
test:
- local action [foo %x]: return "outer"
- with local [action (foo %)]:
- local action [foo %x]:
+ (foo %x) means (return "outer")
+ with local [(foo %)'s meaning]:
+ (foo %x) means:
%y = (%x + 1)
return %y
assume ((foo 10) == 11) or barf "Action didn't work."
assume (%y is (nil)) or barf "Action leaked a local into globals."
- parse [baz %] as (foo %)
+ (baz %) parses as (foo %)
assume ((foo 1) == "outer")
-compile [local action %actions %body] to:
+[%actions means %body, %actions all mean %body] all compile to:
lua> "\
- ..local fn_name = \%actions[1].stub:as_lua_id()
- 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)))
+ ..if \%actions.type ~= "List" then \%actions = {\%actions, type="List"} end
+ local fn_name = \%actions[1].stub:as_lua_id()
+ local \%args = table.map(\%actions[1]:get_args(), function(a) return nomsu:compile(a):text() end)
+ local lua = LuaCode(tree.source, fn_name, " = ", \(what (%args -> %body) compiles to))
lua:add_free_vars({fn_name})
for i=2,#\%actions do
local alias = \%actions[i]
local alias_name = alias.stub:as_lua_id()
lua:add_free_vars({alias_name})
- local \%alias_args = table.map(alias:get_args(), function(a) return tostring(nomsu:compile(a)) end)
+ local \%alias_args = table.map(alias:get_args(), function(a) return nomsu:compile(a):text() end)
lua:append("\\n", alias_name, " = ")
if utils.equivalent(\%args, \%alias_args) then
lua:append(fn_name)
@@ -132,23 +138,24 @@ compile [local action %actions %body] to:
return lua"
test:
- action [baz1]: return "baz1"
- action [baz2] "baz2"
+ externally (baz1) means: return "baz1"
+ externally (baz2) means "baz2"
test:
assume ((baz1) == "baz1")
assume ((baz2) == "baz2")
-compile [action %actions %body] to (..)
+[externally %actions means %body, externally %actions all mean %body] all compile to:
lua> "\
- ..local lua = \(compile as (local action %actions %body))
+ ..local lua = \(what (%actions means %body) compiles to)
+ if \%actions.type ~= "List" then \%actions = {\%actions, type="List"} end
lua:remove_free_vars(table.map(\%actions, function(a) return a.stub:as_lua_id() end))
return lua"
test:
- assume ((action (say %)) == (=lua "say_1"))
-compile [action %action] to (Lua value (%action as lua id))
+ assume (((say %)'s meaning) == (=lua "say"))
+(%action's meaning) compiles to (Lua value (%action.stub as lua id))
test:
- parse [swap %x and %y] as (..)
+ (swap %x and %y) parses as (..)
do:
%tmp = %x
%x = %y
@@ -162,20 +169,21 @@ test:
swap %tmp and %tmp2
assume ((%tmp == 2) and (%tmp2 == 1)) or barf "\
..'parse % as %' variable mangling failed."
-compile [parse %actions as %body] to (..)
+[%actions parses as %body, %actions all parse as %body] all compile to:
lua> "\
..local replacements = {}
+ if \%actions.type ~= "List" then \%actions = {\%actions, type="List"} end
for i,arg in ipairs(\%actions[1]:get_args()) do
- replacements[arg[1]] = tostring(nomsu:compile(arg))
+ replacements[arg[1]] = nomsu:compile(arg):text()
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("..repr(t[1]).."), source="..repr(tostring(t.source)).."}"
+ 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
@@ -183,71 +191,76 @@ compile [parse %actions as %body] to (..)
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.."= "..tostring(v):as_lua()
+ elseif lua_type_of(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, ", ").."}"
+ return "SyntaxTree{"..table.concat(ret, ", ").."}"
+ elseif lua_type_of(t) == 'number' then
+ return tostring(t)
else
- return repr(t)
+ return t:as_lua()
end
end
local \%new_body = LuaCode(\%body.source,
"local mangle = mangler()",
"\\nreturn ", make_tree(\%body))
- local ret = \(compile as (compile %actions to %new_body))
+ local ret = \(what (%actions compiles to %new_body) compiles to)
return ret"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# TODO: add check for .is_value
-compile [%tree as lua expr] to (Lua value "nomsu:compile(\(=lua "nomsu:compile(\%tree, nil, true)"), nil, true)")
+(%tree as lua expr) compiles to (..)
+ Lua value "nomsu:compile(\(=lua "nomsu:compile(\%tree, nil, true)"), nil, true)"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-compile [%tree as lua] to (Lua value "nomsu:compile(\(%tree as lua expr))")
-compile [%tree as lua statements] to (..)
+(%tree as lua) compiles to (Lua value "nomsu:compile(\(%tree as lua expr))")
+(%tree as lua statements) compiles to (..)
Lua value "nomsu:compile(\(%tree as lua expr)):as_statements()"
-compile [%tree as lua return] to (..)
+(%tree as lua return) compiles to (..)
Lua value "nomsu:compile(\(%tree as lua expr)):as_statements('return ')"
-compile [remove action %action] to (..)
- Lua "\(=lua "(\(%action.stub)):as_lua_id()") = nil"
-
test:
assume ("\(\(foo \%x) as nomsu)" == "foo %x") or barf "\
..action source code failed."
-compile [%tree as nomsu] to (..)
+(%tree as nomsu) compiles to (..)
Lua value "nomsu:tree_to_nomsu(\(%tree as lua expr))"
-compile [%tree as inline nomsu] to (..)
- Lua value "nomsu:tree_to_nomsu(\(%tree as lua expr), true)"
+(%tree as inline nomsu) compiles to (..)
+ Lua value "nomsu:tree_to_inline_nomsu(\(%tree as lua expr), true)"
-action [%var as lua identifier, %var as lua id] (..)
+externally [%var as lua identifier, %var as lua id] all mean:
lua> "\
- ..if type(\%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, 'Action') then return \%var.stub:as_lua_id()
+ ..if lua_type_of(\%var) == 'string' then return \%var:as_lua_id()
+ 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:text():match("^[_a-zA-Z][_a-zA-Z0-9]*$") then
+ nomsu:compile_error(\%var, "This is not a valid Lua identifier.")
+ end
+ return lua
else error("Unknown type: "..tostring(\%var))
end"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-compile [% is syntax tree] to (Lua value "AST.is_syntax_tree(\(% as lua expr))")
-compile [% is %kind syntax tree] to (..)
- Lua value "AST.is_syntax_tree(\(% as lua expr), \(%kind as lua expr))"
+(% is syntax tree) compiles to (Lua value "SyntaxTree:is_instance(\(% as lua expr))")
+(% is %kind syntax tree) compiles to (..)
+ Lua value "SyntaxTree:is_instance(\(% as lua expr), \(%kind as lua expr))"
-compile [%tree with %t -> %replacement] to (..)
+(%tree with %t -> %replacement) compiles to (..)
Lua value "\
..\(%tree as lua expr):map(function(\(%t as lua expr))
\(%replacement as lua return)
end)"
-action [%tree with vars %replacements] (..)
+externally (%tree with vars %replacements) means (..)
=lua "\
..\%tree:map(function(\%t)
if \%t.type == "Var" then
@@ -255,15 +268,15 @@ action [%tree with vars %replacements] (..)
end
end)"
-compile [tree %tree with vars %replacements] to (..)
+(tree %tree with vars %replacements) compiles to (..)
Lua value "\
- ..\(=lua "repr(\%tree)"):map(function(t)
+ ..\(=lua "(\%tree):as_lua()"):map(function(t)
if t.type == "Var" then
return \(%replacements as lua expr)[t[1]]
end
end)"
-compile [%tree has subtree %match_tree] to (..)
+(%tree has subtree %match_tree) compiles to (..)
Lua value "\
..(function()
local match_tree = \(%match_tree as lua expr)
@@ -272,14 +285,14 @@ compile [%tree has subtree %match_tree] to (..)
end
end)()"
-action [match %tree with %patt]:
+externally (match %tree with %patt) means:
lua> "\
..if \%patt.type == "Var" then return _Dict{[\%patt[1]]=\%tree} end
if \%patt.type == "Action" and \%patt.stub ~= \%tree.stub then return nil end
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
@@ -290,7 +303,7 @@ action [match %tree with %patt]:
end
return matches"
-action [%tree with %patt ~> %replacement]:
+externally (%tree with %patt ~> %replacement) means:
lua> "\
..return \%tree:map(function(\%t)
local \%vars = \(match %t with %patt)
@@ -312,11 +325,31 @@ test:
..one
"two""
..== "\"one\\n\\\"two\\\"\""
-compile [quote %s] to (Lua value "repr(\(%s as lua expr))")
+(quote %s) compiles to (Lua value "tostring(\(%s as lua expr)):as_lua()")
test:
- assume ((type of {}) == "table") or barf "type of failed."
-compile [type of %obj] to (Lua value "type(\(%obj as lua expr))")
+ assume (lua type of {}) == "table"
+ assume (type of {}) == "Dict"
+ assume ({} is a "Dict")
+ assume ("" is text)
+ assume ("" isn't a "Dict")
+externally (% is text) means (=lua "\(lua type of %) == 'string'")
+externally [% is not text, % isn't text] all mean (..)
+ =lua "\(lua type of %) ~= 'string'"
+
+externally (type of %) means:
+ lua> "\
+ ..local lua_type = \(lua type of %)
+ if lua_type == 'string' then return 'Text'
+ elseif lua_type == 'table' then
+ local mt = getmetatable(\%)
+ if mt and mt.__type then return mt.__type end
+ return 'Lua table'
+ else return lua_type end"
+
+[% is a %type, % is an %type] all parse as ((type of %) == %type)
+[% isn't a %type, % isn't an %type, % is not a %type, % is not an %type] all parse as (..)
+ (type of %) != %type
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -325,8 +358,8 @@ test:
%a = (parse "\\1")
%b = \(\(1))
assume ((parse "\\1") == \(\(1)))
-compile [parse %text] to (Lua value "nomsu:parse(\(%text as lua expr))")
-compile [parse %text from %filename] to (..)
+(parse %text) compiles to (Lua value "nomsu:parse(\(%text as lua expr))")
+(parse %text from %filename) compiles to (..)
Lua value "\
..nomsu:parse(NomsuCode(Source(\(%filename as lua expr), 1, #\(%text as lua expr)), \(..)
%text as lua expr
@@ -337,37 +370,39 @@ test:
external %passed = (no)
run "external %passed = (yes)"
assume %passed
-compile [run %nomsu_code] to (..)
+(run %nomsu_code) compiles to (..)
Lua value "\
- ..nomsu:run(NomsuCode(\(..)
- =lua "repr(tostring(\(%nomsu_code.source)))"
- .., \(%nomsu_code as lua expr)))"
+ ..nomsu:run(NomsuCode(\(=lua "tostring(\(%nomsu_code.source)):as_lua()"), \(..)
+ %nomsu_code as lua expr
+ ..))"
test:
assume ((\(\(5) + \(5)) as value) == 10) or barf "%tree as value failed."
-compile [run tree %tree, %tree as value] to (Lua value "nomsu:run(\(%tree as lua expr))")
-compile [compile %block, compiled %block, %block compiled] to (..)
+[run tree %tree, %tree as value] all compile to (..)
+ Lua value "nomsu:run(\(%tree as lua expr))"
+
+[compile %block, compiled %block, %block compiled] all compile to (..)
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.
-compile [return] to (Lua "do return; end")
-compile [return %return_value] to (..)
+(return) compiles to (Lua "do return; end")
+(return %return_value) compiles to (..)
Lua "do return \(%return_value as lua expr) end"
# Literals
-compile [yes] to (Lua value "true")
-compile [no] to (Lua value "false")
-compile [nothing, nil, null] to (Lua value "nil")
-compile [Nomsu syntax version] to (Lua value "NOMSU_SYNTAX_VERSION")
-compile [Nomsu compiler version] to (Lua value "NOMSU_COMPILER_VERSION")
-compile [core version] to (Lua value "NOMSU_CORE_VERSION")
-compile [lib version] to (Lua value "NOMSU_LIB_VERSION")
-compile [command line args] to (Lua value "arg")
+(yes) compiles to (Lua value "true")
+(no) compiles to (Lua value "false")
+[nothing, nil, null] all compile to (Lua value "nil")
+(Nomsu syntax version) compiles to (Lua value "NOMSU_SYNTAX_VERSION")
+(Nomsu compiler version) compiles to (Lua value "NOMSU_COMPILER_VERSION")
+(core version) compiles to (Lua value "NOMSU_CORE_VERSION")
+(lib version) compiles to (Lua value "NOMSU_LIB_VERSION")
+(command line args) compiles to (Lua value "arg")
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-compile [with local compile actions %body] to (..)
+(with local compile actions %body) compiles to (..)
Lua "\
..do
local nomsu = nomsu:fork()
@@ -375,7 +410,7 @@ compile [with local compile actions %body] to (..)
\(%body as lua statements)
end"
-action [Nomsu version]:
+externally (Nomsu version) means:
use "lib/version.nom"
return "\
..\(Nomsu syntax version).\(core version).\(Nomsu compiler version).\(lib version)"
diff --git a/core/operators.nom b/core/operators.nom
index 3ae7c2f..6d574b7 100644
--- a/core/operators.nom
+++ b/core/operators.nom
@@ -1,4 +1,4 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This file contains definitions of operators like "+" and "and".
@@ -9,14 +9,14 @@ test:
assume (all [1 < 2, 2 > 1, 1 <= 2, 2 >= 1, 1 == 1, 1 != 2])
# Comparison Operators
-compile [%x < %y] to (Lua value "(\(%x as lua expr) < \(%y as lua expr))")
-compile [%x > %y] to (Lua value "(\(%x as lua expr) > \(%y as lua expr))")
-compile [%x <= %y] to (Lua value "(\(%x as lua expr) <= \(%y as lua expr))")
-compile [%x >= %y] to (Lua value "(\(%x as lua expr) >= \(%y as lua expr))")
-compile [%a is %b, %a == %b] to (..)
+(%x < %y) compiles to (Lua value "(\(%x as lua expr) < \(%y as lua expr))")
+(%x > %y) compiles to (Lua value "(\(%x as lua expr) > \(%y as lua expr))")
+(%x <= %y) compiles to (Lua value "(\(%x as lua expr) <= \(%y as lua expr))")
+(%x >= %y) compiles to (Lua value "(\(%x as lua expr) >= \(%y as lua expr))")
+[%a is %b, %a == %b] all compile to (..)
Lua value "(\(%a as lua expr) == \(%b as lua expr))"
-compile [%a isn't %b, %a is not %b, %a not= %b, %a != %b] to (..)
+[%a isn't %b, %a is not %b, %a not= %b, %a != %b] all compile to (..)
Lua value "(\(%a as lua expr) ~= \(%b as lua expr))"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -26,7 +26,7 @@ test:
assume (%x == 10)
# Variable assignment operator
-compile [%var = %value] to:
+(%var = %value) compiles to:
lua> "local \%var_lua = \(%var as lua expr)"
assume %var_lua.is_value or barf "Invalid target for assignment: \%var"
lua> "local \%value_lua = \(%value as lua expr)"
@@ -34,7 +34,7 @@ compile [%var = %value] to:
lua> "\
..local lua = LuaCode(tree.source, \%var_lua, ' = ', \%value_lua, ';')
if \%var.type == 'Var' then
- lua:add_free_vars({tostring(nomsu:compile(\%var))})
+ lua:add_free_vars({nomsu:compile(\%var):text()})
end
return lua"
@@ -45,7 +45,7 @@ test:
assume ((%y == 10) and (%x == 20)) or barf "swapping vars failed."
# Simultaneous mutli-assignments like: x,y,z = 1,x,3;
-compile [set %assignments] to:
+(set %assignments) compiles to:
assume (%assignments.type is "Dict") or barf "\
..Expected a Dict for the assignments part of '<- %' statement, not \%assignments"
lua> "\
@@ -53,7 +53,7 @@ compile [set %assignments] to:
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)
@@ -66,7 +66,7 @@ compile [set %assignments] to:
%value as text
..) end
if \%target.type == "Var" then
- lhs:add_free_vars({tostring(target_lua)})
+ lhs:add_free_vars({target_lua:text()})
end
if i > 1 then
lhs:append(", ")
@@ -81,13 +81,13 @@ compile [set %assignments] to:
test:
set {%foozle:"outer", %y:"outer"}
- action [set global x local y]:
+ externally (set global x local y) means:
external %foozle = "inner"
%y = "inner"
set global x local y
assume ((%foozle == "inner") and (%y == "outer")) or barf "external failed."
-compile [external %var = %value] to:
+(external %var = %value) compiles to:
%var_lua = (%var as lua)
assume %var_lua.is_value or barf "Invalid target for assignment: \%var"
%value_lua = (%value as lua)
@@ -96,7 +96,7 @@ compile [external %var = %value] to:
test:
set {%foozle:"outer", %y:"outer"}
- action [set global x local y] (..)
+ externally (set global x local y) means (..)
with external [%foozle]:
%foozle = "inner"
%y = "inner"
@@ -104,10 +104,10 @@ test:
set global x local y
assume ((%foozle == "inner") and (%y == "outer")) or barf "\
..'with external' failed."
-compile [with external %externs %body] to:
+(with external %externs %body) compiles to:
%body_lua = (%body as lua statements)
lua> "\
- ..\%body_lua:remove_free_vars(table.map(\%externs, function(v) return tostring(nomsu:compile(v)) end))"
+ ..\%body_lua:remove_free_vars(table.map(\%externs, function(v) return nomsu:compile(v):text() end))"
return %body_lua
test:
@@ -119,7 +119,7 @@ test:
assume (%x == 1) or barf "'with' scoping failed"
assume (%z == (nil)) or barf "'with' scoping failed"
-compile [with %assignments %body] to:
+(with %assignments %body) compiles to:
%lua = (%body as lua statements)
lua> "\
..local lhs, rhs = LuaCode(tree.source), LuaCode(tree.source)
@@ -141,7 +141,7 @@ compile [with %assignments %body] to:
lhs:append(target_lua)
rhs:append(value_lua)
if \%target.type == "Var" then
- vars[i] = tostring(target_lua)
+ vars[i] = target_lua:text()
end
end
\%lua:remove_free_vars(vars)
@@ -156,53 +156,53 @@ compile [with %assignments %body] to:
# Math Operators
test:
assume ((5 wrapped around 2) == 1) or barf "mod not working"
-compile [%x wrapped around %y, %x mod %y] to (..)
- Lua value "(\(%x as lua expr) % \(%y as lua expr))"
+[%x wrapped around %y, %x mod %y] all compile to (..)
+ Lua value "((\(%x as lua expr)) % (\(%y as lua expr)))"
# 3-part chained comparisons
# (uses a lambda to avoid re-evaluating middle value, while still being an expression)
test:
%calls = 0
- local action [one]:
+ (one) means:
external %calls = (%calls + 1)
return 1
assume (0 <= (one) <= 2) or barf "Three-way chained comparison failed."
assume (%calls == 1) or barf "\
..Three-way comparison evaluated middle value multiple times"
-parse [%x < %y < %z] as (..)
+(%x < %y < %z) parses as (..)
call ([%a, %b, %c] -> ((%a < %b) and (%b < %c))) with [%x, %y, %z]
-parse [%x <= %y < %z] as (..)
+(%x <= %y < %z) parses as (..)
call ([%a, %b, %c] -> ((%a <= %b) and (%b < %c))) with [%x, %y, %z]
-parse [%x < %y <= %z] as (..)
+(%x < %y <= %z) parses as (..)
call ([%a, %b, %c] -> ((%a < %b) and (%b <= %c))) with [%x, %y, %z]
-parse [%x <= %y <= %z] as (..)
+(%x <= %y <= %z) parses as (..)
call ([%a, %b, %c] -> ((%a <= %b) and (%b <= %c))) with [%x, %y, %z]
-parse [%x > %y > %z] as (..)
+(%x > %y > %z) parses as (..)
call ([%a, %b, %c] -> ((%a > %b) and (%b > %c))) with [%x, %y, %z]
-parse [%x >= %y > %z] as (..)
+(%x >= %y > %z) parses as (..)
call ([%a, %b, %c] -> ((%a >= %b) and (%b > %c))) with [%x, %y, %z]
-parse [%x > %y >= %z] as (..)
+(%x > %y >= %z) parses as (..)
call ([%a, %b, %c] -> ((%a > %b) and (%b >= %c))) with [%x, %y, %z]
-parse [%x >= %y >= %z] as (..)
+(%x >= %y >= %z) parses as (..)
call ([%a, %b, %c] -> ((%a >= %b) and (%b >= %c))) with [%x, %y, %z]
# TODO: optimize for common case where x,y,z are all either variables or number literals
# Boolean Operators
test:
- local action [barfer] (barf "short circuiting failed")
+ (barfer) means (barf "short circuiting failed")
assume (((no) and (barfer)) == (no))
assume ((no) or (yes))
assume ((yes) or (barfer))
-compile [%x and %y] to (Lua value "(\(%x as lua expr) and \(%y as lua expr))")
-compile [%x or %y] to (Lua value "(\(%x as lua expr) or \(%y as lua expr))")
+(%x and %y) compiles to (Lua value "(\(%x as lua expr) and \(%y as lua expr))")
+(%x or %y) compiles to (Lua value "(\(%x as lua expr) or \(%y as lua expr))")
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -219,31 +219,31 @@ test:
# Lua 5.3 introduced bit operators like | and &. Use them when possible, otherwise
fall back to bit.bor(), bit.band(), etc.
%use_bitops = ((is jit) or ((Lua version) == "Lua 5.2"))
-compile [NOT %, ~ %] to (..)
+[NOT %, ~ %] all compile to (..)
Lua value (..)
(%use_bitops and "bit.bnot(\(% as lua expr))") or "~(\(% as lua expr))"
-compile [%a OR %b, %a | %b] to (..)
+[%a OR %b, %a | %b] all compile to (..)
Lua value (..)
(%use_bitops and "bit.bor(\(%a as lua expr), \(%b as lua expr))") or "\
..(\(%a as lua expr) | \(%b as lua expr))"
-compile [%a XOR %b, %a ~ %b] to (..)
+[%a XOR %b, %a ~ %b] all compile to (..)
Lua value (..)
(%use_bitops and "bit.bxor(\(%a as lua expr), \(%b as lua expr))") or "\
..(\(%a as lua expr) ~ \(%b as lua expr))"
-compile [%a AND %b, %a & %b] to (..)
+[%a AND %b, %a & %b] all compile to (..)
Lua value (..)
(%use_bitops and "bit.band(\(%a as lua expr), \(%b as lua expr))") or "\
..(\(%a as lua expr) & \(%b as lua expr))"
-compile [%x LSHIFT %shift, %x << %shift] to (..)
+[%x LSHIFT %shift, %x << %shift] all compile to (..)
Lua value (..)
(%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 (..)
+[%x RSHIFT %shift, %x >> %shift] all compile 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))"
@@ -252,15 +252,15 @@ compile [%x RSHIFT %shift, %x >> %shift] to (..)
test:
assume ((- 5) == -5)
assume ((not (yes)) == (no))
-compile [- %] to (Lua value "(- \(% as lua expr))")
-compile [not %] to (Lua value "(not \(% as lua expr))")
+(- %) compiles to (Lua value "(- \(% as lua expr))")
+(not %) compiles to (Lua value "(not \(% as lua expr))")
test:
assume ((size of [1, 2, 3]) == 3)
-compile [size of %list, size of %list, size of %list, size of %list] to (..)
+[size of %list, size of %list, size of %list, size of %list] all compile to (..)
Lua value "(#\(%list as lua expr))"
-compile [%list is empty] to (Lua value "(#\(%list as lua expr) == 0)")
+(%list is empty) compiles to (Lua value "(#\(%list as lua expr) == 0)")
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -273,11 +273,11 @@ test:
assume (%x == 4) or barf "*= failed"
wrap %x around 3
assume (%x == 1) or barf "wrap around failed"
-parse [%var += %] as (%var = (%var + %))
-parse [%var -= %] as (%var = (%var - %))
-parse [%var *= %] as (%var = (%var * %))
-parse [%var /= %] as (%var = (%var / %))
-parse [%var ^= %] as (%var = (%var ^ %))
-parse [%var and= %] as (%var = (%var and %))
-parse [%var or= %] as (%var = (%var or %))
-parse [wrap %var around %] as (%var = (%var wrapped around %))
+(%var += %) parses as (%var = (%var + %))
+(%var -= %) parses as (%var = (%var - %))
+(%var *= %) parses as (%var = (%var * %))
+(%var /= %) parses as (%var = (%var / %))
+(%var ^= %) parses as (%var = (%var ^ %))
+(%var and= %) parses as (%var = (%var and %))
+(%var or= %) parses as (%var = (%var or %))
+(wrap %var around %) parses as (%var = (%var wrapped around %))
diff --git a/core/scopes.nom b/core/scopes.nom
index c01a361..2d86ca4 100644
--- a/core/scopes.nom
+++ b/core/scopes.nom
@@ -1,4 +1,4 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This file contains definitions pertaining to variable scoping
@@ -14,19 +14,19 @@ test:
assume (%x == "inner")
assume (%x == "outer")
- action [foo] "outer foo"
- with local [action (foo)]:
- action [foo] "inner foo"
+ externally (foo) means "outer foo"
+ with local [(foo)'s meaning]:
+ externally (foo) means "inner foo"
assume ((foo) == "inner foo")
assume ((foo) == "outer foo")
-compile [with local %locals %body, with local %locals do %body] to:
+[with local %locals %body, with local %locals do %body] all compile to:
%body_lua = (%body as lua statements)
if %locals.type is:
"Dict":
%body_lua = (..)
Lua "\
- ..\(compile as (<- %locals))
+ ..\(what (<- %locals) compiles to)
\%body_lua"
%body_lua::declare locals ("\(%.1 as lua)" for % in %locals)
diff --git a/core/text.nom b/core/text.nom
index 25e9c3c..05ec409 100644
--- a/core/text.nom
+++ b/core/text.nom
@@ -1,4 +1,4 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This file contains some definitions of text escape sequences, including ANSI console
color codes.
@@ -22,12 +22,12 @@ test:
assume ("asdf"::uppercase) == "ASDF"
assume ("asdf"::with "s" -> "X") == "aXdf"
assume ("one\ntwo\n"::lines) == ["one", "two", ""]
- parse [アクション %spec %body] as (action %spec %body)
+ (アクション %spec %body) parses as (externally %spec means %body)
test:
%こんにちは = "こんにちは"
アクション [% と言う] "\(%)世界"
assume (%こんにちは と言う) == "こんにちは世界"
-compile [%expr for %match in %text matching %patt] to (..)
+(%expr for %match in %text matching %patt) compiles to (..)
Lua value "\
..(function()
local ret = _List{}
diff --git a/examples/how_do_i.nom b/examples/how_do_i.nom
index 1d43982..ec75596 100644
--- a/examples/how_do_i.nom
+++ b/examples/how_do_i.nom
@@ -1,10 +1,10 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
# How do I...
# Write a comment? Put a # and go till the end of the line
# How do I write a multi-line comment?
After a comment line, any indented text
is considered part of the comment
- (including any deeper-level indented text)
+ (including any deeper-level indented text)
The comment ends when the indentation ends
# How do I import a file?
use "lib/os.nom"
@@ -167,14 +167,14 @@ do:
# How do I define a function/method/procedure?
# In nomsu, they're called "action"s, and they can be declared like this:
-action [say both %first and also %second]:
+(say both %first and also %second) means:
say %first
# Function arguments are accessed just like variables
say %second
# Actions can use "return" to return a value early
-action [first fibonacci above %n]:
+(first fibonacci above %n) means:
%f1 = 0
%f2 = 1
repeat:
@@ -186,11 +186,11 @@ action [first fibonacci above %n]:
say (first fibonacci above 10)
# Actions can have aliases, which may or may not have the arguments in different order
-action [..]
+[..]
I hate %worse_things more than %better_things
I think %worse_things are worse than %better_things
I like %better_things more than %worse_things
-..:
+..all mean:
say "\(%better_things::capitalized) rule and \%worse_things drool!"
I like "dogs" more than "cats"
@@ -201,7 +201,7 @@ I think "chihuahuas" are worse than "corgis"
say both "Hello" and also "again!"
# Actions can even start with a parameter
-action [%what_she_said is what she said]:
+(%what_she_said is what she said) means:
say %what_she_said
say "-- she said"
@@ -209,7 +209,7 @@ action [%what_she_said is what she said]:
# The language only reserves []{}().,:;% as special characters, so actions
can have really funky names!
-action [>> %foo_bar $$$^ --> % @&_~-^-~_~-^ %1 !]:
+(>> %foo_bar $$$^ --> % @&_~-^-~_~-^ %1 !) means:
say %foo_bar
say %
say %1
@@ -218,7 +218,7 @@ action [>> %foo_bar $$$^ --> % @&_~-^-~_~-^ %1 !]:
# There's also full unicode support
%こんにちは = "こんにちは"
-action [% と言う] "\%世界"
+(% と言う) means "\%世界"
say (%こんにちは と言う)
# Math and logic operations are just treated the same as actions in the syntax
@@ -226,7 +226,7 @@ say (2 + 3)
# So you can define your own operators, although they will need to be parenthesized to
play nicely with other operators
-action [%a ++ %b] (2 * (%a + %b))
+(%a ++ %b) means (2 * (%a + %b))
say (1 ++ (2 * 3))
# How do I do grouping?
@@ -240,36 +240,38 @@ say (2 + 3)
say both "Very long first argument that needs its own line" and also "\
..short second arg"
-action [my favorite number] (21 + 2)
+(my favorite number) means (21 + 2)
# This can be nested:
say both (my favorite number) and also "foo"
# Macros:
# The "lua> %" and "=lua %" macros can be used to write raw lua code:
-action [say the time] (lua> "io.write(\"The OS time is: \", os.time(), \"\\n\");")
+(say the time) means:
+ lua> "io.write(\"The OS time is: \", os.time(), \"\\n\");"
+
say the time
say "Math expression result is: \(=lua "(1 + 2*3 + 3*4)^2")"
# Variables can be accessed via \%varname
-action [square root of %n] (=lua "math.sqrt(\%n)")
+(square root of %n) means (=lua "math.sqrt(\%n)")
say "The square root of 2 is \(square root of 2)"
# Macros can be defined to transform one bit of nomsu code into another using "parse % as %":
-parse [if %condition is untrue %body] as (if (not %condition) %body)
+(if %condition is untrue %body) parses as (if (not %condition) %body)
# Or to transform nomsu code into custom lua code using "compile % to %"
-compile [if %condition on opposite day %body] to (..)
+(if %condition on opposite day %body) compiles to (..)
Lua "\
..if not \(%condition as lua expr) then
\(%body as lua statements)
end"
# Constants can be defined as macros
-parse [TWENTY] as 20
+(TWENTY) parses as 20
# When they're invoked, they'll need parentheses just like a function call
-parse [TWENTY ONE] as 21
+(TWENTY ONE) parses as 21
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -286,7 +288,7 @@ if (1 > (TWENTY)) on opposite day:
# How do I use an action as a value?
# Well... it's always *possible* to fall back to Lua behavior for something like this:
-action [best of %items according to %key_fn]:
+(best of %items according to %key_fn) means:
set {%best:nil, %best_key:nil}
for %item in %items:
%key = (=lua "\%key_fn(\%item)")
@@ -301,7 +303,7 @@ say (best of [2, -3, 4, -8] according to ([%x] -> (%x * %x)))
one-off function to pass to another function and get called a bunch of times, you
could use a macro to generate a single block of code that inlines the expression you
want to use:
-parse [best of %items where %item_var has score %key_expr] as (..)
+(best of %items where %item_var has score %key_expr) parses as (..)
result of:
set {%best:nil, %best_key:nil}
for %item_var in %items:
diff --git a/files.lua b/files.lua
index c04f703..13679e3 100644
--- a/files.lua
+++ b/files.lua
@@ -181,6 +181,8 @@ Files.walk = function(path, flush_cache)
files = {
path
}
+ elseif path:match("^[~/]") or path:match("^%./") or path:match("^%.%./") then
+ files = browse(path)
else
for nomsupath in package.nomsupath:gmatch("[^;]+") do
do
@@ -191,20 +193,18 @@ Files.walk = function(path, flush_cache)
end
end
end
- local iter
- iter = function(files, i)
- if not (files) then
- return
- end
- i = i + 1
- do
- local f = files[i]
- if f then
- return i, f
- end
+ files = files or { }
+ do
+ local _accum_0 = { }
+ local _len_0 = 1
+ for _index_0 = 1, #files do
+ local f = files[_index_0]
+ _accum_0[_len_0] = gsub(f, "^%./", "")
+ _len_0 = _len_0 + 1
end
+ files = _accum_0
end
- return iter, files, 0
+ return ipairs(files)
end
local line_counter = re.compile([[ lines <- {| line (%nl line)* |}
line <- {} (!%nl .)*
diff --git a/files.moon b/files.moon
index becc3fb..738c5f8 100644
--- a/files.moon
+++ b/files.moon
@@ -33,7 +33,6 @@ Files.read = (filename)->
_FILE_CACHE[filename] = contents
return contents
--- `walk` returns an iterator over all files matching a path.
{:match, :gsub} = string
-- TODO: improve sanitization
@@ -105,15 +104,14 @@ Files.walk = (path, flush_cache=false)->
local files
if path == 'stdin' or _SPOOFED_FILES[path]
files = {path}
+ elseif path\match("^[~/]") or path\match("^%./") or path\match("^%.%./")
+ files = browse(path)
else
for nomsupath in package.nomsupath\gmatch("[^;]+")
if files = browse(nomsupath.."/"..path) then break
- iter = (files, i)->
- return unless files
- i += 1
- if f = files[i]
- return i, f
- return iter, files, 0
+ files or= {}
+ files = [gsub(f, "^%./", "") for f in *files]
+ return ipairs(files)
line_counter = re.compile([[
lines <- {| line (%nl line)* |}
diff --git a/lib/base64.nom b/lib/base64.nom
index 8fc4e6b..2091615 100644
--- a/lib/base64.nom
+++ b/lib/base64.nom
@@ -1,4 +1,4 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This file defines actions for encoding/decoding base 64, as specified in:
https://tools.ietf.org/html/rfc4648
@@ -13,7 +13,7 @@ test:
%plain = "foobar".[1, %len - 1]
assume (base64 %plain) == %encoded
assume (base64 decode %encoded) == %plain
-action [base64 %str, base64 encode %str, %str base64]:
+externally [base64 %str, base64 encode %str, %str base64] all mean:
%chars = []
for %i in 1 to (size of %str) via 3:
%bytes = [=lua "\%str:byte(\%i, \(%i + 2))"]
@@ -36,8 +36,8 @@ action [base64 %str, base64 encode %str, %str base64]:
return (%chars::joined)
-action [chr %] (=lua "string.char(\%)")
-action [decode base64 %str, %str base64 decoded, base64 decode %str]:
+externally (chr %) means (=lua "string.char(\%)")
+externally [decode base64 %str, %str base64 decoded, base64 decode %str] all mean:
%chars = []
for %i in 1 to (size of %str) via 4:
%indices = (%reverse_b64.(%str.%) for % in %i to (%i + 3))
diff --git a/lib/consolecolor.nom b/lib/consolecolor.nom
index f4c4df9..fe7da4c 100644
--- a/lib/consolecolor.nom
+++ b/lib/consolecolor.nom
@@ -1,4 +1,4 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This file defines actions for ANSI console color escape codes.
@@ -14,9 +14,10 @@ test:
for %name = %colornum in %colors:
%colornum = "\%colornum"
- (=lua "COMPILE_ACTIONS").%name = (..)
- [%nomsu, %tree] -> (..)
- Lua value "'\\027[\(%colornum)m'"
- (=lua "COMPILE_ACTIONS")."\%name 1" = (..)
- [%nomsu, %tree, %text] -> (..)
- Lua value "('\\027[\(%colornum)m'..\(%text as lua expr)..'\\027[0m')"
+ #(=lua "COMPILE_ACTIONS").%name = (..)
+ [%nomsu, %tree] -> (Lua value "'\\027[\(%colornum)m'")
+ (=lua "COMPILE_ACTIONS")."\%name" = (..)
+ [%nomsu, %tree, %text] ->:
+ if %text:
+ return (Lua value "('\\027[\(%colornum)m'..\(%text as lua expr)..'\\027[0m')")
+ ..else: return (Lua value "'\\027[\(%colornum)m'")
diff --git a/lib/file_hash.nom b/lib/file_hash.nom
index 6fdb2f4..6c815f5 100644
--- a/lib/file_hash.nom
+++ b/lib/file_hash.nom
@@ -1,4 +1,4 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This file defines some actions for hashing files and looking up files by hash.
@@ -26,14 +26,14 @@ test:
if %use_sha1:
assume ((hash "hello world") == "Kq5sNclPz7QV2+lfQIuc6R7oRu0=")
if %use_sha1:
- action [hash %]:
+ externally (hash %) means:
%hash = (=lua "\%hashlib.new('sha1'):final(\%)")
return (base64 %hash)
..else:
# TODO: remove warning?
say "\
..\027[31;1mWARNING: OpenSSL module not found. Defaulting to a non-cryptographically secure hash function.\027[0m"
- action [hash %]:
+ externally (hash %) means:
%bytes = (%::bytes)
%hash = (%bytes.1 << 7)
for %i in 2 to (size of %bytes):
@@ -41,10 +41,10 @@ if %use_sha1:
%hash = (%hash ~ (size of %bytes))
return "\%hash"
-action [file with hash %hash]:
+externally (file with hash %hash) means:
for file %filename in ".":
%contents = (read file %filename)
%file_hash = (hash %contents)
if (%file_hash == %hash): return %filename
-parse [hash of file %filename] as (hash (read file %filename))
+(hash of file %filename) parses as (hash (read file %filename))
diff --git a/lib/object.nom b/lib/object.nom
index b49b8f2..d5555df 100644
--- a/lib/object.nom
+++ b/lib/object.nom
@@ -1,9 +1,16 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This file contains the implementation of an Object-Oriented programming system.
+%globals.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", size:"__len", iterate:"__ipairs", "iterate all":"__pairs"
+
test:
- object "Dog":
+ object (Dog):
(Dog).genus = "Canus"
my action [set up]: %me.barks or= 0
my action [bark, woof]:
@@ -12,8 +19,10 @@ test:
my action [get pissed off]: %me.barks += 1
- %d = (new Dog {barks:2})
- assume (%d.barks == 2)
+ %d = (Dog {barks:2})
+ assume (type of %d) == "Dog"
+ assume (%d is a "Dog")
+ assume %d.barks == 2
assume ((%d::bark) == "Bark! Bark!")
assume ((%d::woof) == "Bark! Bark!")
%d::get pissed off
@@ -23,38 +32,38 @@ test:
assume ("\(%d.class)" == "Dog")
assume (%d.genus == "Canus")
assume (%d.barks == 3)
- %d2 = (new Dog)
+ %d2 = (Dog {})
assume (%d2.barks == 0) or barf "Default initializer failed"
- with {%d:new Dog {barks:1}}:
+ with {%d:Dog {barks:1}}:
assume ((%d::bark) == "Bark!")
- object "Corgi" extends (Dog):
+ 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)
+ %corg = (Corgi {})
assume (%corg.barks == 0)
- with {%d:new Corgi {barks:1}}:
+ with {%d:Corgi {barks:1}}:
assume ((%d::sploot) == "splooted") or barf "subclass method failed"
assume ((%d::bark) == "Yip!") or barf "inheritance failed"
assume ((%d::woof) == "Yip!")
- with {%d:new Dog {barks:2}}:
+ with {%d:Dog {barks:2}}:
assume ((%d::bark) == "Bark! Bark!")
-compile [my action %actions %body] to:
+(my action %actions %body) compiles to:
lua> "\
..local fn_name = \%actions[1].stub:as_lua_id()
local \%args = table.map(\%actions[1]:get_args(), function(a) return tostring(nomsu:compile(a)) end)
- table.insert(\%args, \(\%me as lua id))
+ table.insert(\%args, 1, \(\%me as lua id))
local lua = LuaCode(tree.source, "class.", fn_name, " = ", \(..)
- compile as (%args -> %body)
+ what (%args -> %body) compiles to
..)
for i=2,#\%actions do
local alias = \%actions[i]
local alias_name = alias.stub:as_lua_id()
local \%alias_args = table.map(alias:get_args(), function(a) return tostring(nomsu:compile(a)) end)
- table.insert(\%alias_args, \(\%me as lua id))
+ table.insert(\%alias_args, 1, \(\%me as lua id))
lua:append("\\nclass.", alias_name, " = ")
if utils.equivalent(\%args, \%alias_args) then
lua:append("class.", fn_name)
@@ -68,45 +77,39 @@ compile [my action %actions %body] to:
end
return lua"
-compile [object %classname extends %parent %class_body] to:
+(object %classname extends %parent %class_body) compiles to:
+ unless (%classname.type == "Action"):
+ compile error at %classname "Expected this to be an action, not a \(%classname.type)"
+ for % in %classname:
+ unless (% is text):
+ compile error at % "Class names should not have arguments."
+
return (..)
Lua "\
..do
- local class = {name=\(%classname as lua expr)}
+ local class = {name=\(quote %classname.stub)}
+ class.__type = class.name
setmetatable(class, {
__index=\(%parent as lua expr),
__tostring=function(cls) return cls.name end,
__call=function(cls, inst)
- inst = setmetatable(inst or {}, cls)
- if inst.set_up then
- inst:set_up()
- end
+ if inst == nil then return cls end
+ inst = setmetatable(inst, cls)
+ if inst.set_up then inst:set_up() end
return inst
end,
})
- nomsu.environment[("new "..class.name):as_lua_id()] = class
- nomsu.environment[("new "..class.name.." 1"):as_lua_id()] = class
- nomsu.environment[class.name:as_lua_id()] = function() return class end
+ nomsu.environment[class.name:as_lua_id()] = class
class.__index = class
class.class = class
class.__tostring = function(inst)
return inst.name..getmetatable(_Dict{}).__tostring(inst)
end
-
\(%class_body as lua statements)
-
- 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
+ for stub,metamethod in pairs(globals.METAMETHOD_MAP) do
class[metamethod] = class[stub:as_lua_id()]
end
end"
-parse [object %classname %class_body] as (..)
+(object %classname %class_body) parses as (..)
object %classname extends (nil) %class_body
diff --git a/lib/os.nom b/lib/os.nom
index e6faa18..2bd91ad 100644
--- a/lib/os.nom
+++ b/lib/os.nom
@@ -1,14 +1,14 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This file defines some actions that interact with the operating system and filesystem.
test:
path of Nomsu file "lib/os.nom"
-action [path of Nomsu file %filename]:
+externally (path of Nomsu file %filename) means:
lua> "for i,f in Files.walk(\%filename) do return f end"
barf "Could not find file: \%filename"
-action [sh> %cmd]:
+externally (sh> %cmd) means:
lua> "\
..local result = io.popen(\%cmd)
local contents = result:read("*a")
@@ -17,19 +17,19 @@ action [sh> %cmd]:
test:
read file "lib/os.nom"
-action [read file %filename] (=lua "Files.read(\%filename)")
+externally (read file %filename) means (=lua "Files.read(\%filename)")
test:
for file %f in "core": do nothing
-compile [for file %f in %path %body] to (..)
+(for file %f in %path %body) compiles to (..)
Lua "\
..for i,\(%f as lua expr) in Files.walk(\(%path as lua expr)) do
\(%body as lua statements)
- \(compile as (===next %f ===))
+ \(what (===next %f ===) compiles to)
end
- \(compile as (===stop %f ===))"
+ \(what (===stop %f ===) compiles to)"
-compile [%expr for file %f in %path] to (..)
+(%expr for file %f in %path) compiles to (..)
Lua value "\
..(function()
local ret = _List{}
@@ -39,10 +39,10 @@ compile [%expr for file %f in %path] to (..)
return ret
end)()"
-action [..]
+externally [..]
write to file %filename %text, to file %filename write %text
write %text to file %filename
-..:
+..all mean:
assume (%filename != "stdin") or barf "Cannot write to stdin"
lua> "\
..local file = io.open(\%filename, 'w')
@@ -51,15 +51,17 @@ action [..]
test:
assume (line number of 3 in "x\ny") == 2
-action [line number of %pos in %str] (=lua "Files.get_line_number(\%str, \%pos)")
+externally (line number of %pos in %str) means (..)
+ =lua "Files.get_line_number(\%str, \%pos)"
test:
assume (line 2 in "one\ntwo\nthree") == "two"
-action [line %line_num in %str] (=lua "Files.get_line(\%str, \%line_num)")
+externally (line %line_num in %str) means (..)
+ =lua "Files.get_line(\%str, \%line_num)"
test:
assume (source lines of \(this))
-action [source lines of %tree]:
+externally (source lines of %tree) means:
%source = (%tree.source if (%tree is syntax tree) else %tree)
%file = (read file %source.filename)
return (..)
diff --git a/lib/training_wheels.nom b/lib/training_wheels.nom
index 6155069..f16b700 100644
--- a/lib/training_wheels.nom
+++ b/lib/training_wheels.nom
@@ -1,26 +1,28 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
This file contains a set of definitions that bring some familiar language features
from other languages into nomsu (e.g. "||" and "continue")
-parse [%a === %b] as ((%a 's id) is (%b 's id))
-parse [%a !== %b] as ((%a 's id) is not (%b 's id))
-parse [function %names %body, def %names %body] as (action %names %body)
-parse [switch %branch_value %body] as (if %branch_value is %body)
-parse [None, Null] as (nil)
-parse [True, true] as (yes)
-parse [False, false] as (no)
-parse [pass] as (do nothing)
-parse [%a || %b] as (%a or %b)
-parse [%a && %b] as (%a and %b)
-parse [continue] as (do next)
-parse [break] as (stop)
-parse [let %thing = %value in %action] as (with local {%thing:%value})
-parse [print %, println %] as (say %)
-parse [error!, panic!, fail!, abort!] as (barf!)
-parse [error %, panic %, fail %, abort %] as (barf %)
-parse [assert %condition] as (assume %condition)
-parse [assert %condition %message] as (assume %condition or barf %message)
-parse [%cond ? %if_true %if_false] as (%if_true if %cond else %if_false)
-parse [lambda %args %body] as (%args -> %body)
-parse [function %name %args %body] as (%name = (%args -> %body))
+(%a === %b) parses as ((%a 's id) is (%b 's id))
+(%a !== %b) parses as ((%a 's id) is not (%b 's id))
+[function %names %body, def %names %body] all parse as (..)
+ externally %names means %body
+
+(switch %branch_value %body) parses as (if %branch_value is %body)
+[None, Null] all parse as (nil)
+[True, true] all parse as (yes)
+[False, false] all parse as (no)
+(pass) parses as (do nothing)
+(%a || %b) parses as (%a or %b)
+(%a && %b) parses as (%a and %b)
+(continue) parses as (do next)
+(break) parses as (stop)
+(let %thing = %value in %action) parses as (with local {%thing:%value})
+[print %, println %] all parse as (say %)
+[error!, panic!, fail!, abort!] all parse as (barf!)
+[error %, panic %, fail %, abort %] all parse as (barf %)
+(assert %condition) parses as (assume %condition)
+(assert %condition %message) parses as (assume %condition or barf %message)
+(%cond ? %if_true %if_false) parses as (%if_true if %cond else %if_false)
+(lambda %args %body) parses as (%args -> %body)
+(function %name %args %body) parses as (%name = (%args -> %body))
diff --git a/lib/version.nom b/lib/version.nom
index afb9e14..2f4003f 100644
--- a/lib/version.nom
+++ b/lib/version.nom
@@ -1,3 +1,3 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
# This file sets the current library version.
lua> "NOMSU_LIB_VERSION = 6"
diff --git a/nomnom/ast.nom b/nomnom/ast.nom
new file mode 100644
index 0000000..ef41b26
--- /dev/null
+++ b/nomnom/ast.nom
@@ -0,0 +1,88 @@
+#!/usr/bin/env nomsu -V4.8.10
+use "lib/object.nom"
+
+# The types are [..]
+ "Number", "Var", "Block", "EscapedNomsu", "Text", "List", "Dict", "DictEntry",
+ "IndexChain", "Action", "FileChunks", "Error", "Comment"
+object (Syntax Tree):
+ my action [set up]:
+ if (%me.type == "Action"):
+ %stub_bits = []
+ %argnum = 1
+ for %bit in %me:
+ if:
+ (%bit is text):
+ %stub_bits::add %bit
+ (%bit.type != "Comment"):
+ %stub_bits::add "\%argnum"
+ %argnum += 1
+
+ %me.stub = (%stub_bits::joined with " ")
+ if (%me.stub == "Lua Code 1 2"):
+ lua> "require('ldt').breakpoint()"
+
+ (Syntax Tree).source_code_for_tree = (..)
+ {} with fallback % -> (read file %.source.filename)
+ my action [children]:
+ %children = []
+ for % in %me:
+ if ((% is a "Syntax Tree") and (%.type != "Comment")): %children::add %
+ if ((%me.type == "Action") and %me.target):
+ %children::add %me.target
+ return %children
+
+ my action [as lua] "\
+ ..a_Syntax_Tree_with(\(call ({} 's metatable).as_lua with [%me]))"
+ my action [as nomsu] "\
+ ..(a Syntax Tree with \(call ({} 's metatable).as_nomsu with [%me]))"
+ my action [as text] "\
+ ..(Syntax Tree \(call ({} 's metatable).__tostring with [%me]))"
+ my action [get source code] (Syntax Tree).source_code_for_tree.%me
+ my action [map %fn]:
+ %replacement = (call %fn with [%me])
+ if %replacement:
+ if (%replacement is a "Syntax Tree"):
+ %replacement = (%k = %v for %k = %v in %replacement)
+ %replacement.source = %me.source
+ return (Syntax Tree %replacement)
+ return %replacement
+ ..else:
+ %replacement = {}
+ %changes = (no)
+ for %k = %v in %me:
+ %replacement.%k = %v
+ if (%v is a "Syntax Tree"):
+ %r = (%v::map %fn)
+ if ((%r == %v) or (%r == (nil))): do next %k
+ %changes = (yes)
+ %replacement.%k = %r
+
+ unless %changes: return %me
+ return (Syntax Tree %replacement)
+
+ my action [with %overrides]:
+ %new = (%k = %v for %k = %v in %me)
+ for %k = %v in %overrides: %new.%k = %v
+ return (Syntax Tree %new)
+
+ my action [== %other]:
+ unless (..)
+ all of [..]
+ (type of %me) == (type of %other), (%me 's metatable) == (%other 's metatable)
+ (size of %me) == (size of %other), %me.type == %other.type
+ ..: return (no)
+
+ for %item in %me at %i:
+ if (%other.%i != %item): return (no)
+ if (%me.type == "Action"):
+ if (%me.target != %other.target): return (no)
+ return (yes)
+
+ my action [get args]:
+ assume (%me.type == "Action") or barf "\
+ ..Only actions have arguments, not \(%me.type)"
+ %args = []
+ for % in %me:
+ unless ((% is text) or (%.type == "Comment")): %args::add %
+ return %args
+
diff --git a/nomnom/code_obj.nom b/nomnom/code_obj.nom
new file mode 100644
index 0000000..c8d2784
--- /dev/null
+++ b/nomnom/code_obj.nom
@@ -0,0 +1,210 @@
+#!/usr/bin/env nomsu -V4.8.10
+# This file contains objects that are used to track code positions and incrementally
+ build up generated code, while keeping track of where it came from, and managing
+ indentation levels.
+use "lib/things.nom"
+
+a (Code Buffer) is a thing:
+ that can (set up) by:
+ assume %its.source
+ %old_bits = (%its.bits if (%its.bits is a "List") else [%its.bits])
+ %its.bits = []
+ if (type of %its.source) is:
+ "Text":
+ %its.source = (Source from text %its.source)
+ "Syntax Tree":
+ %its.source = %its.source.source
+
+ for % in %old_bits: %its::add %
+
+ whose (text) means:
+ if (%its._text == (nil)):
+ %buff = []
+ %indent = 0
+ for %bit in %its.bits:
+ if (%bit is text):
+ %spaces = (%bit::matching "\n([ ]*)[^\n]*$")
+ if %spaces: %indent = (size of %spaces.1)
+ ..else:
+ %bit = (%bit::text)
+ if (%indent > 0):
+ %bit = (%bit::with "\n" -> "\n\(" "::* %indent)")
+ %buff::add %bit
+ %its._text = (%buff::joined)
+ return %its._text
+
+ whose (lua code) means "\
+ ..a_\(%its.class.name::as lua id)_with{source=\(..)
+ (%its.source::as lua) if %its.source else "nil"
+ .., \(%its.bits::as lua)}"
+
+ whose (nomsu code) means "\
+ ..(a \(%its.class.name) with {source: \((%its.source::as nomsu) if %its.source else "(nil)"), bits: \(..)
+ %its.bits::as nomsu
+ ..})"
+
+ whose (size) means (size of (%its::text))
+
+ that can (mark as dirty) by:
+ %its._text = (nil)
+ %its._trailing_line_len = (nil)
+ %its._num_lines = (nil)
+
+ that can (add %new_bits) by:
+ unless (%new_bits is a "List"):
+ %new_bits = [%new_bits]
+ for % in %new_bits:
+ if (% == ""): do next %
+ #if ((% isn't text) and (% isn't a (Code))):
+ % = (%::as lua)
+ %its.bits::add %
+ %its::mark as dirty
+
+ whose (trailing line length) means:
+ if (%its._trailing_line_len == (nil)):
+ %its._trailing_line_len = (size of ((%its::text)::matching "[^\n]*$"))
+ return %its._trailing_line_len
+
+ whose (number of lines) means:
+ unless %its._num_lines:
+ %num_lines = 1
+ for % in %its:
+ if (% is text):
+ %num_lines += (size of (%::all matches of "\n"))
+ ..else:
+ %num_lines += ((%::number of lines) - 1)
+
+ %its._num_lines = %num_lines
+
+ return %its._num_lines
+
+ whose [is multiline, is multi-line] all mean ((%its::number of lines) > 1)
+ whose [is one line, is single line] all mean ((%its::number of lines) == 1)
+ that can (add %values joined with %joiner) by:
+ %its::add %values joined with %joiner or %joiner
+ that can [add %values joined with %joiner or %wrapping_joiner] by:
+ %line_len = 0
+ %bits = %its.bits
+ for %value in %values at %i:
+ if (%i > 1):
+ if (%line_len > 80):
+ %bits::add %wrapping_joiner
+ %line_len = 0
+ ..else: %bits::add %joiner
+
+ %bits::add %value
+ unless (%value is text):
+ %value = (%value::text)
+ %line = (%value::matching "\n([^\n]*)$")
+ if %line:
+ %line_len = (size of %line)
+ ..else:
+ %line_len += (size of %value)
+ %its::mark as dirty
+
+ that can (prepend %) by:
+ #if ((% isn't text) and (% isn't a %its.__type)):
+ % = (%::as lua)
+ %its.bits::add % at index 1
+ %its::mark as dirty
+
+ that can (parenthesize) by:
+ %its.bits::add "(" at index 1
+ %its.bits::add ")"
+ %its::mark as dirty
+
+a (Lua Buffer) is a thing:
+ that has [..]
+ text, lua code, nomsu code, trailing line length, size, number of lines,
+ is multiline, is multi-line, is one line, is single line,
+ ..like a (Code Buffer)
+ that can [..]
+ set up, mark as dirty, add %, prepend %, parenthesize,
+ add % joined with %, add % joined with % or %,
+ ..like a (Code Buffer)
+
+ that can (add free vars %vars) by:
+ if ((size of %vars) == 0): return
+ %seen = (%v = (yes) for %v in %its.free_vars)
+ for %var in %vars:
+ assume (%var is text)
+ unless %seen.%var:
+ %its.free_vars::add %var
+ %seen.%var = (yes)
+
+ %its::mark as dirty
+
+ that can (remove free vars %vars) by:
+ if ((size of %vars) == 0): return
+ %removals = {}
+ for %var in %vars:
+ assume (%var is text)
+ %removals.%var = (yes)
+
+ %stack = [%its]
+ repeat while ((size of %stack) > 0):
+ %lua = (%stack::pop)
+ for %i in (size of %lua.free_vars) to 1 by -1:
+ if %removals.(%lua.free_vars.%i):
+ %lua.free_vars::remove at index %i
+
+ for % in %lua.bits:
+ unless (% is text): %stack::add %
+
+ %its::mark as dirty
+
+ that can (declare locals) by (%its::declare locals (nil))
+ that can (declare locals %to_declare) by:
+ unless %to_declare:
+ %to_declare = []
+ %seen = {}
+ for %lua in recursive %its:
+ for %var in %lua.free_vars:
+ unless %seen.%var:
+ %seen.%var = (yes)
+ %to_declare::add %var
+
+ for % in %lua.bits:
+ unless (% is text): recurse %lua on %
+
+ if ((size of %to_declare) > 0):
+ %its::remove free vars %to_declare
+ %its::prepend "local \(%to_declare::joined with ", ");\n"
+ return %to_declare
+
+ whose (as statements) means (%its::as statements with "")
+ whose (as statements with %prefix) means:
+ unless %its.is_value: return %its
+ %statements = (a Lua Buffer with {source:%its.source})
+ if ((%prefix or "") != ""):
+ %statements::add %prefix
+ %statements::add %its
+ %statements::add ";"
+ return %statements
+
+ that can (mark as value) by:
+ %its.is_value = (yes)
+
+ that can (mark as variable) by:
+ %its.is_variable = (yes)
+ %its.is_value = (yes)
+
+ that can (variables) by:
+ %vars = []
+ for %code in recursive %its:
+ if %code.is_variable:
+ %vars::add (%code::text)
+ for % in %code.bits:
+ unless (% is text): recurse %code on %
+
+ return %vars
+
+a (Nomsu Buffer) is a thing:
+ that has [..]
+ text, lua code, nomsu code, trailing line length, size, number of lines,
+ is multiline, is multi-line, is one line, is single line,
+ ..like a (Code Buffer)
+ that can [..]
+ set up, mark as dirty, add %, prepend %, parenthesize,
+ add % joined with %, add % joined with % or %,
+ ..like a (Code Buffer)
diff --git a/nomnom/compile.nom b/nomnom/compile.nom
new file mode 100644
index 0000000..8241ef2
--- /dev/null
+++ b/nomnom/compile.nom
@@ -0,0 +1,276 @@
+#!/usr/bin/env nomsu -V4.8.10
+# This file contains the code to convert syntax trees to Lua code
+use "nomnom/code_obj.nom"
+use "nomnom/parser.nom"
+use "nomnom/pretty_errors.nom"
+
+externally (report compile error at %tree %err) means:
+ barf (pretty "Compile Error" error at %tree %err)
+
+externally (report compile error at %pos %err hint %hint) means:
+ barf (pretty "Compile Error" error at %tree %err hint %hint)
+
+externally (barf any errors in %t) means:
+ assume (%t is a "Syntax Tree")
+ %errs = []
+ for % in recursive %t:
+ if (%.type == "Error"): %errs::add %
+ for %k = %v in %:
+ if (%v is a "Syntax Tree"): recurse % on %v
+
+ sort %errs by % -> %.source
+ %errs = ((% as a pretty error) for % in %errs)
+ if ((size of %errs) > 0):
+ if ((size of %errs) > 3):
+ %n = ((size of %errs) - 3)
+ for %i in 4 to (size of %errs): %errs.%i = (nil)
+ %errs::add "\027[31;1m +\%n additional errors...\027[0m\n"
+ barf (%errs::joined with "\n\n")
+
+externally (%tree compiled with %compile_actions) means:
+ assume (%tree is a "Syntax Tree")
+ if all of [..]
+ %tree.version, ((Nomsu version)'s meaning) != (nil), %tree.version != (Nomsu version)
+ ((1 upgraded from 2 to 3)'s meaning) != (nil)
+ ..then:
+ %tree = (upgrade %tree from %tree.version to (Nomsu version))
+
+ if %tree.type is:
+ "Action":
+ %stub = %tree.stub
+ %compile_action = %compile_actions.%stub
+
+ # Don't apply compiler actions to methods
+ if (%compile_action and (not %tree.target)):
+ # TODO: restore this:
+ #%args = [%tree, %compile_actions]
+ %args = [%nomsu, %tree]
+ for % in (%tree::arguments): %args::add %
+ %result = (call %compile_action with %args)
+ if (%result == (nil)):
+ report compile error at %tree "\
+ ..The compile-time action here (\(%tree.stub)) failed to return any value."
+ ..hint "\
+ ..Look at the implementation of (\(%tree.stub)) and make sure it's returning something."
+
+ if (%result is a "Syntax Tree"):
+ if (%result == %tree):
+ report compile error at %tree "\
+ ..The compile-time action here (\(%tree.stub)) is producing an endless loop."
+ ..hint "\
+ ..Look at the implementation of (\(%tree.stub)) and make sure it's not just returning the original tree."
+
+ return (%result compiled with %compile_actions)
+
+ return %result
+
+ %lua = (a Lua Buffer with {source:%tree})
+ if %tree.target:
+ # Method call
+ %target_lua = (%tree.target compiled with %compile_actions)
+ if (..)
+ ((%target_lua::text)::matches "^%(.*%)$") or (..)
+ (%target_lua::text)::matches "^[_a-zA-Z][_a-zA-Z0-9]*$"
+ ..:
+ %lua::add [%target_lua, ":"]
+ ..else:
+ %lua::add ["(", %target_lua, "):"]
+
+ %lua::add [%stub as lua id, "("]
+ %args = []
+ for %tok in %tree at %i:
+ if (%tok is text): do next %tok
+
+ # TODO: maybe don't translate Lua comments
+ #if (%tok.type == "Comment"): do next %tok
+ if (%tok.type == "Block"):
+ %values = []
+ for %line in %tok:
+ #unless (%line.type == "Comment"):
+ %values::add (%line compiled with %compile_actions)
+
+ if all of (%.is_value for % in %values):
+ if ((size of %values) == 1):
+ %arg_lua = %values.1
+ ..else:
+ %arg_lua = (a Lua Buffer with {source:%tok, is_value:yes, bits:["("]})
+ %arg_lua::add %values joined with " and nil or "
+ %arg_lua::add ")"
+ ..else:
+ %arg_lua = (a Lua Buffer with {source:%tok, is_value:yes, bits:["((function()"]})
+ for %v in %values at %i:
+ if %v.is_value:
+ %v = (%v::as statements with ("return " if (%i == (size of %values) else "")))
+ %arg_lua::add ["\n ", %v]
+
+ %arg_lua::add "\nend)())"
+ ..else:
+ %arg_lua = (%tok compiled with %compile_actions)
+ unless %arg_lua.is_value:
+ if (%tok.type == "Action"):
+ report compile error at %tok "\
+ ..Can't use this as an argument to (\%stub), since it's not an expression, it produces: \%arg_lua"
+ ..hint "\
+ ..Check the implementation of (\(%tok.stub)) to see if it is actually meant to produce an expression."
+ ..else:
+ report compile error at %tok "\
+ ..Can't use this as an argument to (\%stub), since it's not an expression, it produces: \%arg_lua"
+
+ assume (%arg_lua != %lua) or barf "Huh? \%tree .\%i = \%tok -> \%arg_lua"
+ %args::add %arg_lua
+
+ %lua::add %args joined with ", "
+ %lua::add ")"
+ return %lua
+
+ "EscapedNomsu":
+ %lua = (a Lua Buffer with {source:%tree, is_value:yes, bits:["a_Syntax_Tree_with{type=", quote %tree.(1).type]})
+ set {%needs_comma:no, %i:1}
+ (% as shmua) means:
+ if (% is a "Lua number"): return "\%"
+ if (% is a "Syntax Tree"):
+ return (% compiled with %compile_actions)
+ if (% is text): return (quote %)
+ return (%::as lua)
+
+ for %k = %v in (((%tree.(1).type == "EscapedNomsu") and %tree) or %tree.1):
+ %lua::add ", "
+ if:
+ (%k == %i): %i += 1
+ ((%k is text) and (%k::is a lua identifier)):
+ %lua::add [%k, "= "]
+ else:
+ %lua::add ["[", % as shmua, "]= "]
+
+ if (%k == "source"):
+ %lua::add (quote "\%v")
+ ..else:
+ %lua::add (%v as shmua)
+
+ %lua::add "}"
+ return %lua
+
+ "Block":
+ %lua = (a Lua Buffer with {source:%tree})
+ %lua::add (..)
+ ((%line compiled with %compile_actions)::as statements) for %line in %tree
+ ..joined with "\n"
+
+ return %lua
+
+ "Text":
+ %lua = (a Lua Buffer with {source:%tree})
+ %lua_bits = []
+ %string_buffer = ""
+ for % in %tree:
+ if (% is text):
+ %string_buffer = "\%string_buffer\%"
+ do next %
+
+ if (%string_buffer != ""):
+ %lua_bits::add (%string_buffer::as lua)
+ %string_buffer = ""
+
+ %bit_lua = (% compiled with %compile_actions)
+ unless %bit_lua.is_value:
+ report compile error at % "\
+ ..Can't use this as a string interpolation value, since it doesn't have a value."
+
+ if (%.type != "Text"):
+ %bit_lua = (a Lua Buffer with {source:%, is_value:yes, bits:["tostring(", %bit_lua, ")"]})
+ %lua_bits::add %bit_lua
+
+ if ((%string_buffer != "") or ((size of %lua_bits) == 0)):
+ %lua_bits::add (%string_buffer::as lua)
+ %lua::add %lua_bits joined with ".."
+ if ((size of %lua_bits) > 1): %lua::parenthesize
+ return %lua
+
+ "List":
+ %lua = (a Lua Buffer with {source:%tree, is_value:yes, bits:["List{"]})
+ %lua::add ((% compiled with %compile_actions) for % in %tree) joined with ", " or ",\n "
+ %lua::add "}"
+ return %lua
+
+ "Dict":
+ %lua = (a Lua Buffer with {source:%tree, is_value:yes, bits:["Dict{"]})
+ %lua::add ((% compiled with %compile_actions) for % in %tree) joined with ", " or ",\n "
+ %lua::add "}"
+ return %lua
+
+ "DictEntry":
+ set {%key:%tree.1, %value:%tree.2}
+ %key_lua = (%key compiled with %compile_actions)
+ unless %key_lua.is_value:
+ report compile error at %tree.1 "\
+ ..Can't use this as a dict key, since it's not an expression."
+
+ %value_lua = (..)
+ (%value compiled with %compile_actions) if %value else (..)
+ a Lua Buffer with {source:%key, is_value:yes, bits:["true"]}
+
+ unless %value_lua.is_value:
+ report compile error at %tree.2 "\
+ ..Can't use this as a dict value, since it's not an expression."
+
+ %key_str = ((%key_lua::text)::matching "^[\"']([a-zA-Z_][a-zA-Z0-9_]*)['\"]$")
+ if:
+ %key_str:
+ return (a Lua Buffer with {source:%tree, bits:[%key_str, "=", %value_lua]})
+ ((%key_lua::text).1 == "["):
+ # NOTE: this *must* use a space after the [ to avoid freaking out
+ Lua's parser if the inner expression is a long string. Lua
+ parses x[[[y]]] as x("[y]"), not as x["y"]
+ return (a Lua Buffer with {source:%tree, bits:["[ ", %key_lua, "]=", %value_lua]})
+
+ else:
+ return (a Lua Buffer with {source:%tree, bits:["[", %key_lua, "]=", %value_lua]})
+
+ "IndexChain":
+ %lua = (%tree.1 compiled with %compile_actions)
+ unless %lua.is_value:
+ report compile error at %tree.1 "\
+ ..Can't index into this, since it's not an expression."
+
+ %first_char = (%lua::text).1
+ if (any of [%first_char == "{", %first_char == "\"", %first_char == "["]):
+ %lua::parenthesize
+ for %i in 2 to (size of %tree):
+ %key = %tree.%i
+ %key_lua = (%key compiled with %compile_actions)
+ unless %key_lua.is_value:
+ report compile error at %key "\
+ ..Can't use this as an index, since it's not an expression."
+
+ %key_lua_str = (%key_lua::text)
+ %lua_id = (%key_lua_str::matching "^['\"]([a-zA-Z_][a-zA-Z0-9_]*)['\"]$")
+ if:
+ %lua_id:
+ %lua::add [".", %lua_id]
+ (%key_lua_str.1 == "["):
+ # NOTE: this *must* use a space after the [ to avoid freaking out
+ Lua's parser if the inner expression is a long string. Lua
+ parses x[[[y]]] as x("[y]"), not as x["y"]
+ %lua::add ["[ ", %key_lua, " ]"]
+
+ else:
+ %lua::add ["[", %key_lua, "]"]
+
+ return %lua
+
+ "Number":
+ return (a Lua Buffer with {source:%tree, is_value:yes, bits:["\(%tree.1)"]})
+ "Var":
+ return (a Lua Buffer with {source:%tree, is_value:yes, is_variable:yes, bits:[%tree.1::as lua id]})
+ "FileChunks":
+ barf "\
+ ..Can't convert FileChunks to a single block of lua, since each chunk's compilation depends on the earlier chunks"
+
+ "Comment":
+ # TODO: de-implement?
+ return (a Lua Buffer with {source:%tree, bits:["-- \(%tree.1::with "\n" -> "\n-- ")"]})
+
+ "Error":
+ barf (%tree as a pretty error)
+ else:
+ barf "Unknown type: \(%tree.type)"
diff --git a/nomnom/decompile.nom b/nomnom/decompile.nom
new file mode 100644
index 0000000..11ec233
--- /dev/null
+++ b/nomnom/decompile.nom
@@ -0,0 +1,347 @@
+#!/usr/bin/env nomsu -V4.8.10
+# This file contains the code to convert syntax trees to Nomsu code
+use "nomnom/code_obj.nom"
+use "nomnom/parser.nom"
+
+# TODO: maybe re-implement the fancy coroutine checker that aborts early if nomsu gets too long
+externally (%tree decompiled inline) means:
+ assume (%tree is a "Syntax Tree")
+ if %tree.type is:
+ "Action":
+ %nomsu = (a Nomsu Buffer with {source: %tree})
+ if %tree.target:
+ %target_nomsu = (%tree.target decompiled inline)
+ if %tree.target.type is:
+ "Action" "Block":
+ %target_nomsu::parenthesize
+
+ %nomsu::add [%target_nomsu, "::"]
+
+ for %bit in %tree at %i:
+ if (%bit is text):
+ unless (..)
+ any of [..]
+ %i == 1, %tree.(%i - 1) isn't text, (%bit is a nomsu operator) == (%tree.(%i - 1) is a nomsu operator)
+ ..: %nomsu::add " "
+
+ %nomsu::add %bit
+ ..else:
+ %arg_nomsu = (%bit decompiled inline)
+ unless ((%i == (size of %tree)) and (%bit.type == "Block")):
+ %nomsu::add " "
+ if ((%bit.type == "Action") or (%bit.type == "Block")):
+ %arg_nomsu::parenthesize
+
+ %nomsu::add %arg_nomsu
+
+ return %nomsu
+
+ "EscapedNomsu":
+ %inner_nomsu = (%tree.1 decompiled inline)
+ unless (..)
+ any of [..]
+ %tree.(1).type == "List", %tree.(1).type == "Dict", %tree.(1).type == "Var"
+ ..:
+ %inner_nomsu::parenthesize
+
+ %nomsu = (a Nomsu Buffer with {source: %tree, bits: ["\\", %inner_nomsu]})
+ return %nomsu
+
+ "Block":
+ %nomsu = (a Nomsu Buffer with {source: %tree, bits: [":"]})
+ for %line in %tree at %i:
+ %nomsu::add [" " if (%i == 1) else "; ", %line decompiled inline]
+ return %nomsu
+
+ "Text":
+ %nomsu = (a Nomsu Buffer with {source: %tree, bits: []})
+ for %text in recursive %tree:
+ for %bit in %text at %i:
+ if (%bit is text): %nomsu::add %bit
+ ..else:
+ if %bit.type is:
+ "Text":
+ recurse %text on %bit
+ "Var":
+ %interp_nomsu = (%bit decompiled inline)
+
+ # Make sure "...\(%x)y..." isn't confused with "...\(%xy)..."
+ # TODO: make this more robust against "...\%x\("y").."
+ if (..)
+ (%tree.(%i + 1) is text) and (..)
+ not (%tree.(%i + 1)::matches "^[ \n\t,.:;#(){}%[%]]")
+ ..: %interp_nomsu::parenthesize
+ %nomsu::add ["\\", %interp_nomsu]
+
+ "List" "Dict":
+ %nomsu::add ["\\", %bit decompiled inline]
+ else:
+ %nomsu::add ["\\(", %bit decompiled inline, ")"]
+
+ return (a Nomsu Buffer with {source: %tree, bits: ["\"", %nomsu, "\""]})
+
+ "List" "Dict":
+ %nomsu = (a Nomsu Buffer with {source: %tree, bits: ["[" if (%tree.type == "List") else "{"]})
+ for %item in %tree at %i:
+ if (%i > 1): %nomsu::add ", "
+ %nomsu::add (%item decompiled inline)
+
+ %nomsu::add ("]" if (%tree.type == "List") else "}")
+ return %nomsu
+
+ "DictEntry":
+ set {%key:%tree.1, %value:%tree.2}
+ if (..)
+ all of [%key.type == "Text", (size of %key) == 1, %key.1 is a nomsu identifier]
+ ..:
+ %nomsu = (a Nomsu Buffer with {source: %key, bits: [%key.1]})
+ ..else:
+ %nomsu = (%key decompiled inline)
+
+ if (%key.type == "Action"):
+ %nomsu::parenthesize
+ if %value:
+ %nomsu::add ":"
+ %nomsu::add (%value decompiled inline)
+
+ return %nomsu
+
+ "IndexChain":
+ %nomsu = (a Nomsu Buffer with {source: %tree})
+ for %bit in %tree at %i:
+ if (%i > 1): %nomsu::add "."
+ if (..)
+ all of [..]
+ %i > 1, %bit.type == "Text", (size of %bit) == 1, %bit.1 is text, %bit.1 is a nomsu identifier
+ ..: %nomsu::add %bit.1
+ ..else:
+ %bit_nomsu = (%bit decompiled inline)
+ if (..)
+ any of [..]
+ %bit.type == "Action", %bit.type == "Block", %bit.type == "IndexChain"
+ (%bit.type == "Number") and (%i < (size of %tree))
+ ..:
+ %bit_nomsu::parenthesize
+
+ %nomsu::add %bit_nomsu
+
+ return %nomsu
+
+ "Number":
+ return (a Nomsu Buffer with {source: %tree, bits: [(%tree.1 as hex) if %tree.hex else "\(%tree.1)"]})
+ "Var":
+ return (a Nomsu Buffer with {source: %tree, bits: ["%\(%tree.1)"]})
+ "Comment": return (nil)
+ "FileChunks":
+ barf "Can't inline a FileChunks"
+ "Error":
+ barf "Can't compile errors"
+ else:
+ barf "Unknown type: \(%tree.type)"
+
+%MAX_LINE = 90
+externally (%tree decompiled) means:
+ %nomsu = (a Nomsu Buffer with {source: %tree})
+
+ # For concision:
+ (recurse on %t) means:
+ %space = (%MAX_LINE - (%nomsu::trailing line length))
+ if (%space <= 0):
+ go to (Use Indented)
+ for %subtree in recursive %tree:
+ if %subtree.type is:
+ "Block":
+ if ((size of %subtree) > 1):
+ go to (Use Indented)
+ if ((size of "\(%subtree decompiled inline)") > 20):
+ go to (Use Indented)
+
+ for %k = %v in %subtree:
+ if (%v is a "Syntax Tree"):
+ recurse %subtree on %v
+
+ %inline_nomsu = (%t decompiled inline)
+ if (%inline_nomsu and ((size of "\%inline_nomsu") <= %space)):
+ return %inline_nomsu
+ === (Use Indented) ===
+ %indented = (%t decompiled)
+ if (%t.type == "Action"):
+ %indented = (..)
+ a Nomsu Buffer with {source: %t, bits: ["(..)\n ", %indented]}
+
+ return %indented
+
+ if %tree.type is:
+ "FileChunks":
+ (%1 and %2 should clump) means:
+ if ((%1.type == "Action") and (%2.type == "Action")):
+ if (%1.stub == "use 1"):
+ return (%2.stub == "use 1")
+ if (%1.stub == "test 1"): return (yes)
+ if (%2.stub == "test 1"): return (no)
+
+ return (not ((recurse on %1)::is multi-line))
+
+ for %chunk in %tree at %chunk_no:
+ if (%chunk_no > 1):
+ %nomsu::add "\n\n\("~"::* 80)\n\n"
+ if (%chunk.type == "Block"):
+ for %line in %chunk at %line_no:
+ if (%line_no > 1):
+ if (%chunk.(%line_no - 1) and %line should clump): %nomsu::add "\n"
+ ..else: %nomsu::add "\n\n"
+
+ %nomsu::add (%line decompiled)
+ ..else:
+ %nomsu::add (%chunk decompiled)
+
+ unless ("\%nomsu"::matches "\n$"): %nomsu::add "\n"
+ return %nomsu
+
+ "Action":
+ %pos = %tree.source.start
+ %next_space = ""
+ if %tree.target:
+ %target_nomsu = (recurse on %tree.target)
+ if ((%tree.target.type == "Action") and (%target_nomsu::is one line)):
+ %target_nomsu::parenthesize
+ %nomsu::add %target_nomsu
+ %pos = %tree.target.source.stop
+ %next_space = ("\n..::" if (%target_nomsu::is multi-line) else "::")
+
+ for %bit in %tree at %i:
+ if ((%next_space == " ") and ((%nomsu::trailing line length) > %MAX_LINE)):
+ %next_space = " \\\n"
+
+ %nomsu::add %next_space
+ if (%bit is text):
+ unless (..)
+ all of [..]
+ %tree.(%i - 1) is text, (%tree.(%i - 1) is a nomsu operator) != (%bit is a nomsu operator)
+ ..:
+ %nomsu::add %next_space
+
+ %nomsu::add %bit
+ %next_space = " "
+ do next %bit
+
+ %bit_nomsu = (recurse on %bit)
+ if (%bit.type == "Comment"): %next_space = "\n"
+ ..else:
+ %next_space = (" " if (%bit_nomsu::is one line) else "\n..")
+ if (%bit.type == "Action"):
+ %bit_nomsu::parenthesize
+
+ return %nomsu
+
+ "EscapedNomsu":
+ %nomsu::add "\\"
+ %val_nomsu = (recurse on %tree.1)
+ if ((%tree.(1).type == "Action") and (%val_nomsu::is one line)):
+ %val_nomsu::parenthesize
+ %nomsu::add %val_nomsu
+ return %nomsu
+
+ "Block":
+ for %line in %tree at %i:
+ if ((%i > 1) and (%line.type == "Comment")): %nomsu::add "\n"
+ %line_nomsu = (recurse on %line)
+ %nomsu::add
+ if (%i < (size of %tree)):
+ if ((%line_nomsu::number of lines) > 2): %nomsu::add "\n\n"
+ ..else: %nomsu::add "\n"
+
+ return (..)
+ a Nomsu Buffer with {source: %tree, bits: [":\n ", %nomsu]}
+
+ "Text":
+ # Multi-line text has more generous wrap margins
+ %max_line = ((1.5 * %MAX_LINE) rounded down)
+ %nomsu = (a Nomsu Buffer with {source: %tree})
+ (add text from %tree) means:
+ for %bit in %tree at %i:
+ if (%bit is text):
+ # TODO: escape properly?
+ %bit = (escape text %bit)
+ for %line in (%bit::lines) at %j:
+ if:
+ (%j > 1): %nomsu::add "\n"
+ (((size of %line) > 10) and ((%nomsu::trailing line length) > %max_line)):
+ %nomsu::add "\\\n.."
+
+ repeat while ((size of %line) > 0):
+ %space = (%max_line - (%nomsu::trailing line length))
+ %split = (%line::position of "[%p%s]" after %space)
+ if ((not %split) or (%split > %space + 10)):
+ %split = (%space + 10)
+ if ((%line - %split) < 10):
+ %split = (size of %line)
+ set {%bite:%line.[1, %split], %line:%line.[%split + 1, -1]}
+ %nomsu::add %bite
+ if ((size of %line) > 0):
+ %nomsu::add "\\\n.."
+ if (%bit.type == "Text"):
+ add text from %bit
+ ..else:
+ %nomsu::add "\\"
+ %interp_nomsu = (recurse on %bit)
+ unless (%interp_nomsu::is multi-line):
+ if %bit.type is:
+ "Var":
+ if ((%tree.(%i+1) is text) and (not (%tree.(%i+1)::matches "^[ \n\t,.:#(){}[%]]"))):
+ %interp_nomsu::parenthesize
+
+ "List" "Dict":
+ %interp_nomsu::parenthesize
+
+ %nomsu::add %interp_nomsu
+ if (%interp_nomsu::is multi-line): %nomsu::add "\n.."
+
+ add text from %tree
+ return (..)
+ a Nomsu Buffer with {source: %tree, bits: ["\"\\\n ..", %nomsu, "\""]}
+
+ "List" "Dict":
+ if ((size of %tree) == 0):
+ %nomsu::add ("[]" if (%tree.type == "List") else "{}")
+ return %nomsu
+
+ for %item in %tree at %i:
+ %item_nomsu = (%item decompiled inline)
+ if ((not %item_nomsu) or ((size of "\%item_nomsu") > %MAX_LINE)):
+ %item_nomsu = (recurse on %item_nomsu)
+ %nomsu::add %item_nomsu
+ if (%i < (size of %tree)):
+ if any of [..]
+ %item_nomsu::is multi-line, ((%nomsu::trailing line length) + (size of "\%item_nomsu")) >= %MAX_LINE
+ ..: %nomsu::add "\n"
+ ..else: %nomsu::add ", "
+
+ return (..)
+ a Nomsu Buffer with {..}
+ source: %tree, bits: [..]
+ "[..]\n " if (%tree.type == "List") else "{..}\n ", %nomsu
+
+ "DictEntry":
+ set {%key:%tree.1, %value:%tree.2}
+ if (..)
+ all of [%key.type == "Text", (size of %key) == 1, %key.1 is a nomsu identifier]
+ ..: %nomsu::add %key.1
+ ..else:
+ %nomsu::add (%key decompiled inline)
+
+ if ((%key.type == "Action") or (%key.type == "Block")):
+ %nomsu::parenthesize
+ %nomsu::add [": ", recurse on %value]
+ return %nomsu
+
+ "Comment":
+ %nomsu::add ["#", %tree.1::with "\n" -> "\n "]
+ return %nomsu
+
+ "IndexChain" "Number" "Var":
+ return (%tree decompiled inline)
+ "Error":
+ barf "Cannot decompile an error"
+ else:
+ barf "Unknown type: \(%tree.type)"
diff --git a/nomnom/files.nom b/nomnom/files.nom
new file mode 100644
index 0000000..352ddfa
--- /dev/null
+++ b/nomnom/files.nom
@@ -0,0 +1,108 @@
+#!/usr/bin/env nomsu -V4.8.10
+# Some file utilities for searching for files recursively and using package.nomsupath
+use "lib/os.nom"
+
+%_SPOOFED_FILES = {}
+%_FILE_CACHE = ({} with fallback % -> %_SPOOFED_FILES.%)
+%_BROWSE_CACHE = {}
+
+# Create a fake file and put it in the cache
+externally (spoof file %filename %contents) means:
+ %_SPOOFED_FILES.%filename = %contents
+ return %contents
+
+# Read a file's contents
+externally (read file %filename) means:
+ %contents = %_FILE_CACHE.%filename
+ if %contents: return %contents
+ if (%filename == "stdin"):
+ return (spoof file "stdin" (=lua "io.read('*a')"))
+ %file = (=lua "io.open(\%filename)")
+ unless %file: return (nil)
+ %contents = (call %file.read with [%file, "*a"])
+ %file::close
+ %_FILE_CACHE.%filename = %contents
+ return %contents
+
+externally (%path sanitized) means:
+ %path = (%path::with "\\" -> "\\\\")
+ %path = (%path::with "`" -> "")
+ %path = (%path::with "\"" -> "\\\"")
+ %path = (%path::with "$" -> "")
+ %path = (%path::with "%.%." -> "\\..")
+ return %path
+
+try:
+ %lfs = (=lua "require('lfs')")
+..and if it succeeds:
+ (filesystem has %filename) means:
+ %mode = (call %lfs.attributes with [%filename, "mode"])
+ if %mode is:
+ "file" "directory" "link" "char device": return (yes)
+ else: return (no)
+
+ externally (file %path exists) means:
+ if (any of [%_SPOOFED_FILES.%path, %path == "stdin", filesystem has %path]):
+ return (yes)
+ for %nomsupath in (%package.nomsupath::all matches of "[^;]+"):
+ if (filesystem has "\(%nomsupath)/\%path"): return (yes)
+ return (no)
+
+ externally (files in %path) means:
+ unless %_BROWSE_CACHE.%path:
+ if (%_SPOOFED_FILES.%path or (%filename == "stdin")):
+ %_BROWSE_CACHE.%path = [%path]
+ ..else:
+ if (call %lfs.attributes with [%filename, "mode"]) is:
+ "file" "char device":
+ %_BROWSE_CACHE.%path = [%filename]
+ "directory" "link":
+ for %nomsupath in (%package.nomsupath::all matches of "[^;]+"):
+ %files = []
+ for %member in (call %lfs.dir with ["\(%nomsupath)/\%filename"]):
+ if ((%member == ".") or (%member == "..")): do next %member
+ for % in (files in %member): %files::add %
+
+ if ((size of %files) > 0):
+ %_BROWSE_CACHE.%path = %files
+ go to (Found Files)
+
+ %_BROWSE_CACHE.%path = []
+
+ else:
+ %_BROWSE_CACHE.%path = []
+
+ === (Found Files) ===
+ return %_BROWSE_CACHE.%filename
+..or if it barfs:
+ # LFS not found! Fall back to shell commands, if available.
+ unless (sh> "find . -maxdepth 0"):
+ barf "\
+ ..Could not find 'luafilesystem' module and couldn't run system command 'find' (this might happen on Windows). Please install \
+ ..'luafilesystem' (which can be found at \(..)
+ "https://github.com/spacewander/luafilesystem" if %jit else "\
+ ..https://github.com/keplerproject/luafilesystem"
+ .. or obtained through `luarocks install luafilesystem`)"
+
+ externally (file %path exists) means:
+ if (any of [%_SPOOFED_FILES.%path, %path == "stdin", sh> "ls \(%path sanitized)"]) \
+ ..: return (yes)
+ for %nomsupath in (%package.nomsupath::all matches of "[^;]+"):
+ if (sh> "ls \(%nomsupath)/\%path"): return (yes)
+ return (no)
+
+ externally (files in %path) means:
+ unless %_BROWSE_CACHE.%path:
+ if %_SPOOFED_FILES.%path:
+ %_BROWSE_CACHE.%path = [%_SPOOFED_FILES.%path]
+ ..else:
+ for %nomsupath in (%package.nomsupath::all matches of "[^;]+"):
+ %files = (sh> "find -L '\(%path)' -not -path '*/\\.*' -type f'")
+ if %files:
+ %_BROWSE_CACHE.%path = (%files::lines)
+ go to (Found Files)
+
+ %_BROWSE_CACHE.%path = []
+
+ === (Found Files) ===
+ return %_BROWSE_CACHE.%path
diff --git a/nomnom/parser.nom b/nomnom/parser.nom
new file mode 100644
index 0000000..fe237e8
--- /dev/null
+++ b/nomnom/parser.nom
@@ -0,0 +1,91 @@
+#!/usr/bin/env nomsu -V4.8.10
+# This file contains the parser, which converts text into abstract syntax trees
+#use "nomonom/ast.nom"
+%lpeg = (=lua "require('lpeg')")
+%re = (=lua "require('re')")
+call %lpeg.setmaxstack with [20000]
+set {..}
+ ((P 1)'s meaning):%lpeg.P, ((R 1)'s meaning):%lpeg.R
+ ((Carg 1)'s meaning):%lpeg.Carg, ((S 1)'s meaning):%lpeg.S
+ ((Cc 1)'s meaning):%lpeg.Cc, ((lpeg re pattern 1)'s meaning):%re.compile
+ ((lpeg re pattern 1 using 2)'s meaning):%re.compile
+ ((lpeg pattern 1's match of 2)'s meaning):%lpeg.match
+ ((lpeg pattern 1's match of 2 with 3)'s meaning): (..)
+ [%1, %2, %3] -> (call %lpeg.match with [%1, %2, nil, %3])
+
+%source_code_for_tree = {}
+%defs = (..)
+ {..}
+ nl: (P "\r")^(-1) * (P "\n")
+ tab: P "\t"
+ tonumber: %tonumber
+ tochar: %string.char
+ unpack: %unpack
+ nil: Cc (nil)
+ userdata: Carg 1
+ utf8_char: (..)
+ (R "\194\223")*(R "\128\191") +
+ ..(R "\224\239")*(R "\128\191")*(R "\128\191") +
+ ..(R "\240\244")*(R "\128\191")*(R "\128\191")*(R "\128\191")
+
+ Tree: [%t, %userdata] ->:
+ %source = (..)
+ Source {filename:%userdata.filename, start:%t.start, stop:%t.stop}
+ set {%t.start: nil, %t.stop: nil, %t.source: %source}
+ %t = (a Syntax Tree with %t)
+ (Syntax Tree).source_code_for_tree.%t = %userdata.source
+ return %t
+ ..with fallback %key ->:
+ if:
+ (%key::matches "^ascii_(%d+)$"):
+ %i = (%key::matching "^ascii_(%d+)$")
+ return (call %string.char with [%i as a number])
+
+ (%key::matches "^number_(%d+)$"):
+ %i = (%key::matching "^number_(%d+)$")
+ return (Cc (%i as a number))
+
+%id_patt = (..)
+ ((P "") - (R "09")) * (..)
+ (%defs.utf8_char + (R "az") + (R "AZ") + (P "_") + (R "09")) ^ 1 * -1
+
+%operator_patt = ((S "'`~!@$^&*+=|<>?/-") ^ 1 * -1)
+externally [%text is a nomsu id, %text is a nomsu identifier] all mean (..)
+ lpeg pattern %id_patt's match of %text
+
+externally (%text is a nomsu operator) means (..)
+ lpeg pattern %operator_patt's match of %text
+
+%peg_tidier = (..)
+ lpeg re pattern "\
+ ..file <- %nl* {~ (def/comment) (%nl+ (def/comment))* %nl* ~}
+ def <- anon_def / captured_def
+ anon_def <-
+ ({ident} (" "*) ":" {[^%nl]* (%nl+ " "+ [^%nl]*)*})
+ -> "%1 <- %2"
+ captured_def <-
+ ({ident} (" "*) "(" {ident} ")" (" "*) ":" {[^%nl]* (%nl+ " "+ [^%nl]*)*})
+ -> "%1 <- ({| {:start:{}:} %3 {:stop:{}:} {:type: (''->'%2') :} |} %%userdata) -> Tree"
+ ident <- [a-zA-Z_][a-zA-Z0-9_]*
+ comment <- "--" [^%nl]*
+ "
+
+externally (make parser from %peg) means (make parser from %peg using (nil))
+externally (make parser from %peg using %make_tree) means:
+ %peg = (lpeg pattern %peg_tidier's match of %peg)
+ %peg = (lpeg re pattern %peg using %defs)
+ (%input from %filename parsed) means:
+ %input = "\%input"
+ %tree_mt = {__index:{source:%input, filename:%filename}}
+ %userdata = {..}
+ make_tree:%make_tree or ([%] -> (: set %'s metatable to %tree_mt; return %))
+ filename:%filename, source:%input
+
+ %tree = (lpeg pattern %peg's match of %input with %userdata)
+ assume %tree or barf "\
+ ..File \%filename failed to parse:
+ \%input"
+
+ return %tree
+
+ return ((1 from 2 parsed)'s meaning)
diff --git a/nomnom/pretty_errors.nom b/nomnom/pretty_errors.nom
new file mode 100644
index 0000000..db4be5a
--- /dev/null
+++ b/nomnom/pretty_errors.nom
@@ -0,0 +1,85 @@
+#!/usr/bin/env nomsu -V4.8.10
+# This file has code for converting errors to user-friendly format, with colors,
+ line numbers, code excerpts, and so on.
+(visible size of %text) means:
+ return (size of (%text::with "\027%[[0-9;]*m" -> ""))
+
+(%text boxed) means:
+ %max_line = (max of ((visible size of %line) for %line in (%text::lines)))
+ %ret = (..)
+ "\n\%text"::with "\n([^\n]*)" -> (..)
+ [%] -> (..)
+ "\n\%\(" "::* (%max_line - (visible size of %))) \027[0m"
+ return %ret.[2,-1]
+
+%CONTEXT = 2
+externally (pretty %title error at %tree %err hint %hint) means:
+ %source_code = (%tree::get source code)
+ %start = %tree.source.start
+ %stop = %tree.source.stop
+ %filename = (%tree.source.filename or "???")
+ %err_line = (%source_code::line at %start)
+ %err_linenum = (%source_code::line number at %start)
+ %err_linepos = (%source_code::line position at %start)
+
+ # TODO: better handle multi-line errors
+ %err_size = (min of [%stop - %start, (size of %err_line) - %err_linepos + 1])
+ %nl_indicator = (" " if (%err_linepos > (size of %err_line)) else "")
+ %fmt_str = " %\(size of "\(%err_linenum + %CONTEXT)")d|"
+ (num %i) means (%fmt_str::formatted with %i)
+ %linenum_size = (size of (num 0))
+ %pointer = "\(" "::* (%err_linepos + %linenum_size - 1))"
+ if (%err_size >= 2):
+ %pointer += "╚\("═"::* (%err_size - 2))╝"
+ ..else: %pointer += "⬆"
+
+ %err_msg = "\
+ ..\027[33;41;1m\(%title or "Error") at \%filename:\%err_linenum\027[0m"
+ for %i in (%err_linenum - %CONTEXT) to (%err_linenum - 1):
+ %line = (%source_code::line %i)
+ if %line:
+ %err_msg += "\n\027[2m\(num %i)\027[0m\(%line)\027[0m"
+
+ if %err_line:
+ %before = %err_line.[1, %err_linepos - 1]
+ %during = %err_line.[%err_linepos, %err_linepos + %err_size - 1]
+ %after = %err_line.[%err_linepos + %err_size, -1]
+ %err_line = "\027[0m\(%before)\027[41;30m\%during\(%nl_indicator)\027[0m\%after"
+ %err_msg += "\n\027[2m\(num %err_linenum)\(%err_line)\027[0m"
+
+ %err_linenum_end = (%source_code::line number at %stop)
+ %err_linepos_end = (%source_code::line position at %stop)
+ %err_linenum_end or= %err_linenum
+ if (%err_linenum_end == %err_linenum):
+ %err_msg += "\n\%pointer"
+ ..else:
+ for %i in (%err_linenum + 1) to %err_linenum_end:
+ %line = (%source_code::line %i)
+ if %line:
+ if (%i == %err_linenum_end):
+ %during = %line.[1, %err_linepos_end - 1]
+ %after = %line.[%err_linepos_end, -1]
+ %err_msg += "\n\027[2m\(num %i)\027[0;41;30m\(%during)\027[0m\%after"
+ ..else:
+ %err_msg += "\n\027[2m\(num %i)\027[0;41;30m\(%line)\027[0m"
+
+ %box_width = 70
+ %err_text = "\
+ ..\027[47;31;1m\((" \%err"::wrapped to %box_width)::with "\n" -> "\n\027[47;31;1m ")"
+ if %hint:
+ %err_text += "\n\027[47;30m\((" Suggestion: \(%hint)"::wrapped to %box_width)::with "\n" -> "\n\027[47;30m ")"
+ %err_msg += "\n\027[33;1m \((%err_text boxed)::with "\n" -> "\n ")"
+
+ %err_msg += "\n\027[33;1m \((%err_text boxed)::with "\n" -> "\n ")"
+ for %i in (%err_linenum_end + 1) to (%err_linenum_end + %CONTEXT):
+ %line = (%source_code::line %i)
+ if %line:
+ %err_msg += "\n\027[2m\(num %i)\027[0m\(%line)\027[0m"
+
+ return %err_msg
+
+externally (pretty %title error at %tree %err) means (..)
+ pretty %title error at %tree %err (nil)
+
+externally (%err_tree as a pretty error) means (..)
+ pretty %err_tree.title error at %err_tree %err_tree.error hint %err_tree.hint
diff --git a/nomnom/source.nom b/nomnom/source.nom
new file mode 100644
index 0000000..c36216f
--- /dev/null
+++ b/nomnom/source.nom
@@ -0,0 +1,54 @@
+#!/usr/bin/env nomsu -V4.8.10
+use "lib/object.nom"
+
+object (Source):
+ externally (Source from text %text) means:
+ %match = (%text::matching groups "^@(.-)%[(%d+):(%d+)%]$")
+ set {%filename:%match.1, %start:%match.2, %stop:%match.3}
+ unless %filename:
+ %match = (%text::matching groups "^@(.-)%[(%d+)%]$")
+ set {%filename:%match.1, %start:%match.2}
+
+ return (..)
+ Source {filename:%filename, start:(%start or 1) as number, stop:%stop as number}
+
+ my action [as text] "\
+ ..@\(%me.filename)[\(%me.start)\(":\(%me.stop)" if %me.stop else "")]"
+ my action [as lua] "\
+ ..Source{filename=\(%me.filename::as lua), start=\(%me.start)\(..)
+ ", stop=\(%me.stop)" if %stop else ""
+ ..}"
+
+ my action [as nomsu] "\
+ ..(Source {filename:\(%me.filename::as nomsu), start:\(%me.start)\(..)
+ ", stop:\(%me.stop)" if %stop else ""
+ ..})"
+
+ my action [== %other] (..)
+ all of [..]
+ (%me's metatable) == (%other's metatable)
+ %me.filename == %other.filename
+ %me.start == %other.start
+ %me.stop == %other.stop
+
+ my action [< %other]:
+ assume %me.filename == %other.filename
+ if (%start == %other.start):
+ return ((%me.stop or %me.start) < (%other.stop or %other.start))
+ ..else:
+ return (%me.start < %other.start)
+
+ my action [<= %other]:
+ assume %me.filename == %other.filename
+ if (%start == %other.start):
+ return ((%me.stop or %me.start) <= (%other.stop or %other.start))
+ ..else:
+ return (%me.start <= %other.start)
+
+ my action [+ %offset]:
+ if ((type of %me) == "number"):
+ set {%me:%offset, %offset:%me}
+ ..else:
+ assume (type of %offset) == "number"
+
+ return (Source {filename:%me.filename, start:%me.start + %offset, stop:%me.stop})
diff --git a/nomsu.4.peg b/nomsu.4.peg
index 32a170f..a3677c6 100644
--- a/nomsu.4.peg
+++ b/nomsu.4.peg
@@ -8,6 +8,8 @@ file:
shebang: "#!" (!"nomsu" [^%nl])* "nomsu" ws+ "-V" ws* {:version: [0-9.]+ :} [^%nl]*
+eof: !.
+
file_chunks (FileChunks):
{:curr_indent: ' '* :}
shebang? comment? blank_lines?
@@ -71,7 +73,7 @@ tab_error (Error):
section_division: ("~")^+3 eol
inline_block:
- "(" ws* inline_block ws* ")" / raw_inline_block
+ "(" ws* inline_block ws* (eof / ")") / raw_inline_block
raw_inline_block (Block):
(!"::") ":" ws* ((inline_statement (ws* ";" ws* inline_statement)*) / !(eol nl_indent))
indented_block (Block):
@@ -89,7 +91,7 @@ noindex_inline_expression:
/ ( "("
ws* (inline_action / inline_expression) ws*
(ws* ',' ws* (inline_action / inline_expression) ws*)*
- (")" / missing_paren_err / unexpected_code)
+ (")" / eof / missing_paren_err / unexpected_code)
)
inline_expression: index_chain / noindex_inline_expression
indented_expression:
@@ -119,9 +121,9 @@ inline_action (Action):
inline_arg: inline_expression / inline_block
action (Action):
!section_division
- ({:target: arg :} (eol nl_nodent "..")? ws* "::" (eol nl_nodent "..")? ws*)?
- ( (arg ((eol nl_nodent "..")? ws* (arg / word))+)
- / (word ((eol nl_nodent "..")? ws* (arg / word))*))
+ ({:target: arg :} ((ws* "\")? eol nl_nodent "..")? ws* "::" ((ws* "\")? eol nl_nodent "..")? ws*)?
+ ( (arg (((ws* "\")? eol nl_nodent "..")? ws* (arg / word))+)
+ / (word (((ws* "\")? eol nl_nodent "..")? ws* (arg / word))*))
arg: expression / inline_block / indented_block
word: !number { operator_char+ / ident_char+ }
@@ -130,7 +132,7 @@ text_word (Text): word
inline_text (Text):
!(indented_text)
- '"' _inline_text* ('"' / missing_quote_err / unexpected_code)
+ '"' _inline_text* ('"' / eof / missing_quote_err / unexpected_code)
_inline_text:
{~ (('\"' -> '"') / ('\\' -> '\') / escaped_char / text_char+)+ ~}
/ inline_text_interpolation / illegal_char
@@ -140,7 +142,7 @@ inline_text_interpolation:
/ ("("
ws* (inline_action / inline_expression) ws*
(ws* ',' ws* (inline_action / inline_expression) ws*)*
- (")" / missing_paren_err / unexpected_code))
+ (")" / eof / missing_paren_err / unexpected_code))
)
text_char: %utf8_char / !["\] %print / %tab
@@ -156,7 +158,7 @@ indented_text (Text):
(('\' %nl+ {:curr_indent: indent :} ('..')?)
/ disallowed_interpolation? {%nl+} {:curr_indent: indent :})
(indented_plain_text / text_interpolation / illegal_char / {~ %nl+ (=curr_indent -> "") ~})*
- ('"' eol / missing_quote_err)
+ ('"' eol / eof / missing_quote_err)
{:curr_indent: %nil :}
-- Tracking text-lines-within-indented-text as separate objects allows for better debugging line info
indented_plain_text (Text):
@@ -180,7 +182,7 @@ inline_list (List):
!('[..]')
"[" ws*
(inline_list_item (ws* ',' ws* inline_list_item)* (ws* ',')?)? ws*
- ("]" / (","? (missing_bracket_error / unexpected_code)))
+ ("]" / eof / (","? (missing_bracket_error / unexpected_code)))
indented_list (List):
"[..]" eol nl_indent
list_line (nl_nodent list_line)*
@@ -195,7 +197,7 @@ inline_dict (Dict):
!('{..}')
"{" ws*
(inline_dict_entry (ws* ',' ws* inline_dict_entry)*)? ws*
- ("}" / (","? (missing_brace_error / unexpected_code)))
+ ("}" / eof / (","? (missing_brace_error / unexpected_code)))
indented_dict (Dict):
"{..}" eol nl_indent
dict_line (nl_nodent dict_line)*
diff --git a/nomsu.lua b/nomsu.lua
index 3d78dd1..bd1ee85 100644
--- a/nomsu.lua
+++ b/nomsu.lua
@@ -91,8 +91,6 @@ do
local _obj_0 = require("code_obj")
NomsuCode, LuaCode, Source = _obj_0.NomsuCode, _obj_0.LuaCode, _obj_0.Source
end
-local repr
-repr = require("utils").repr
if not arg or debug.getinfo(2).func == require then
return NomsuCompiler
end
@@ -135,8 +133,7 @@ end
local nomsu = NomsuCompiler
nomsu.environment.arg = NomsuCompiler.environment._List(args.nomsu_args)
if args.version then
- nomsu:run([[use "core"
-say (Nomsu version)]])
+ nomsu:run([[(: use "core"; say (Nomsu version))]])
os.exit(EXIT_SUCCESS)
end
FILE_CACHE = setmetatable({ }, {
@@ -185,11 +182,7 @@ run = function()
return true
end
if not (args.no_core) then
- for _, filename in Files.walk('core') do
- if filename:match("%.nom$") then
- nomsu:import(nomsu:run_file(filename))
- end
- end
+ nomsu:import_file('core')
end
local get_file_and_source
get_file_and_source = function(filename)
@@ -282,8 +275,8 @@ run = function()
if not (args.primary_file or args.exec_strings) then
nomsu:run([[#!/usr/bin/env nomsu -V4
use "lib/consolecolor.nom"
-action [quit, exit]: lua> "os.exit(0)"
-action [help]:
+[quit, exit] all mean: lua> "os.exit(0)"
+(help) means:
say "\
..This is the Nomsu v\(Nomsu version) interactive console.
You can type in Nomsu code here and hit 'enter' twice to run it.
@@ -322,9 +315,13 @@ say "\
return Errhand.print_error(error_message)
end
local ret
- ok, ret = xpcall(nomsu.run, err_hand, nomsu, buff, Source(pseudo_filename, 1, #buff))
+ ok, ret = xpcall(nomsu.run, err_hand, nomsu, NomsuCode(Source(pseudo_filename, 1, #buff), buff))
if ok and ret ~= nil then
- print("= " .. repr(ret))
+ if type(ret) == 'number' then
+ print("= " .. tostring(ret))
+ else
+ print("= " .. tostring(ret:as_nomsu()))
+ end
elseif not ok then
Errhand.print_error(ret)
end
diff --git a/nomsu.moon b/nomsu.moon
index 382a5b5..2c4d21f 100755
--- a/nomsu.moon
+++ b/nomsu.moon
@@ -48,7 +48,6 @@ Files = require "files"
Errhand = require "error_handling"
NomsuCompiler = require "nomsu_compiler"
{:NomsuCode, :LuaCode, :Source} = require "code_obj"
-{:repr} = require "utils"
-- If this file was reached via require(), then just return the Nomsu compiler
if not arg or debug.getinfo(2).func == require
@@ -90,9 +89,7 @@ nomsu = NomsuCompiler
nomsu.environment.arg = NomsuCompiler.environment._List(args.nomsu_args)
if args.version
- nomsu\run [[
-use "core"
-say (Nomsu version)]]
+ nomsu\run [[(: use "core"; say (Nomsu version))]]
os.exit(EXIT_SUCCESS)
export FILE_CACHE
@@ -124,9 +121,7 @@ run = ->
return true
unless args.no_core
- for _,filename in Files.walk('core')
- if filename\match "%.nom$"
- nomsu\import(nomsu\run_file(filename))
+ nomsu\import_file('core')
get_file_and_source = (filename)->
local file, source
@@ -186,8 +181,8 @@ run = ->
nomsu\run [[
#!/usr/bin/env nomsu -V4
use "lib/consolecolor.nom"
-action [quit, exit]: lua> "os.exit(0)"
-action [help]:
+[quit, exit] all mean: lua> "os.exit(0)"
+(help) means:
say "\
..This is the Nomsu v\(Nomsu version) interactive console.
You can type in Nomsu code here and hit 'enter' twice to run it.
@@ -216,13 +211,18 @@ say "\
break -- Exit
buff = table.concat(buff)
+
+ -- TODO: support local variables
pseudo_filename = "user input #"..repl_line
Files.spoof(pseudo_filename, buff)
err_hand = (error_message)->
Errhand.print_error error_message
- ok, ret = xpcall(nomsu.run, err_hand, nomsu, buff, Source(pseudo_filename, 1, #buff))
+ ok, ret = xpcall nomsu.run, err_hand, nomsu, NomsuCode(Source(pseudo_filename,1,#buff), buff)
if ok and ret != nil
- print "= "..repr(ret)
+ if type(ret) == 'number'
+ print "= #{ret}"
+ else
+ print "= #{ret\as_nomsu!}"
elseif not ok
Errhand.print_error ret
diff --git a/nomsu_compiler.lua b/nomsu_compiler.lua
index c3fc802..67e9621 100644
--- a/nomsu_compiler.lua
+++ b/nomsu_compiler.lua
@@ -4,8 +4,8 @@ R, P, S = lpeg.R, lpeg.P, lpeg.S
local re = require('re')
local utils = require('utils')
local Files = require('files')
-local repr, stringify, equivalent
-repr, stringify, equivalent = utils.repr, utils.stringify, utils.equivalent
+local stringify, equivalent
+stringify, equivalent = utils.stringify, utils.equivalent
local List, Dict, Text
do
local _obj_0 = require('containers')
@@ -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,34 +115,29 @@ 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 = { }
-local max_parser_version = 0
-for version = 1, 999 do
- local found_version = false
- for _, full_path in Files.walk("nomsu." .. tostring(version) .. ".peg") do
- do
- local peg_contents = Files.read(full_path)
- if peg_contents then
- found_version = true
- max_parser_version = version
- Parsers[version] = make_parser(peg_contents, make_tree)
+local max_parser_version = 4
+for version = 1, max_parser_version do
+ local peg_file = io.open("nomsu." .. tostring(version) .. ".peg")
+ if not peg_file and package.nomsupath then
+ for path in package.nomsupath:gmatch("[^;]+") do
+ peg_file = io.open(path .. "/nomsu." .. tostring(version) .. ".peg")
+ if peg_file then
+ break
end
end
end
- if not (found_version) then
- break
- end
+ assert(peg_file, "could not find nomsu .peg file")
+ local peg_contents = peg_file:read('*a')
+ peg_file:close()
+ Parsers[version] = make_parser(peg_contents, make_tree)
end
local MAX_LINE = 80
local NomsuCompiler = setmetatable({ }, {
@@ -154,13 +147,11 @@ local NomsuCompiler = setmetatable({ }, {
})
local _anon_chunk = 0
do
- NomsuCompiler.NOMSU_COMPILER_VERSION = 9
- NomsuCompiler.NOMSU_SYNTAX_VERSION = max_parser_version
NomsuCompiler.can_optimize = function()
return false
end
NomsuCompiler.environment = {
- NOMSU_COMPILER_VERSION = 8,
+ NOMSU_COMPILER_VERSION = 11,
NOMSU_SYNTAX_VERSION = max_parser_version,
next = next,
unpack = unpack,
@@ -189,7 +180,7 @@ do
assert = assert,
dofile = dofile,
loadstring = loadstring,
- type = type,
+ lua_type_of = type,
select = select,
math = math,
io = io,
@@ -198,16 +189,14 @@ do
ipairs = ipairs,
_List = List,
_Dict = Dict,
- repr = repr,
stringify = stringify,
utils = utils,
lpeg = lpeg,
re = re,
Files = Files,
- AST = AST,
- TESTS = Dict({ }, {
- globals = Dict({ })
- }),
+ SyntaxTree = SyntaxTree,
+ TESTS = Dict({ }),
+ globals = Dict({ }),
LuaCode = LuaCode,
NomsuCode = NomsuCode,
Source = Source,
@@ -250,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
@@ -293,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
@@ -390,7 +376,7 @@ do
add_lua_string_bits = function(self, val_or_stmt, code)
local cls_str = val_or_stmt == "value" and "LuaCode.Value(" or "LuaCode("
if code.type ~= "Text" then
- return LuaCode.Value(code.source, cls_str, repr(tostring(code.source)), ", ", self:compile(code), ")")
+ return LuaCode.Value(code.source, cls_str, tostring(code.source):as_lua(), ", ", self:compile(code), ")")
end
local add_bit_lua
add_bit_lua = function(lua, bit_lua)
@@ -400,11 +386,11 @@ do
end
local operate_on_text
operate_on_text = function(text)
- local lua = LuaCode.Value(text.source, cls_str, repr(tostring(text.source)))
+ local lua = LuaCode.Value(text.source, cls_str, tostring(text.source):as_lua())
for _index_0 = 1, #text do
local bit = text[_index_0]
if type(bit) == "string" then
- add_bit_lua(lua, repr(bit))
+ add_bit_lua(lua, bit:as_lua())
elseif bit.type == "Text" then
add_bit_lua(lua, operate_on_text(bit))
else
@@ -420,7 +406,7 @@ do
end
return operate_on_text(code)
end
- local math_expression = re.compile([[ ([+-] " ")* [0-9]+ (" " [*/^+-] (" " [+-])* " " [0-9]+)+ !. ]])
+ local math_expression = re.compile([[ (([*/^+-] / [0-9]+) " ")* [*/^+-] !. ]])
local compile_math_expression
compile_math_expression = function(self, tree, ...)
local lua = LuaCode.Value(tree.source)
@@ -445,36 +431,36 @@ do
end
NomsuCompiler.environment.COMPILE_ACTIONS = setmetatable({
__imported = Dict({ }),
- ["Lua 1"] = function(self, tree, code)
+ ["Lua"] = function(self, tree, code)
return add_lua_string_bits(self, 'statements', code)
end,
- ["Lua value 1"] = function(self, tree, code)
+ ["Lua value"] = function(self, tree, code)
return add_lua_string_bits(self, 'value', code)
end,
- ["lua > 1"] = function(self, tree, code)
+ ["lua >"] = function(self, tree, code)
if code.type ~= "Text" then
return LuaCode(tree.source, "nomsu:run_lua(", self:compile(code), ", nomsu);")
end
return add_lua_bits(self, "statements", code)
end,
- ["= lua 1"] = function(self, tree, code)
+ ["= lua"] = function(self, tree, code)
if code.type ~= "Text" then
return LuaCode.Value(tree.source, "nomsu:run_lua(", self:compile(code), ":as_statements('return '), nomsu)")
end
return add_lua_bits(self, "value", code)
end,
- ["use 1"] = function(self, tree, path)
+ ["use"] = function(self, tree, path)
if path.type == 'Text' and #path == 1 and type(path[1]) == 'string' then
- for _, f in Files.walk(path[1]) do
- self:import(self:run_file(f))
+ if not (self:import_file(path[1])) then
+ self:compile_error(tree, "Could not find anything to import for " .. tostring(path))
end
end
- return LuaCode(tree.source, "for i,f in Files.walk(", self:compile(path), ") do nomsu:import(nomsu:run_file(f)) end")
+ return LuaCode(tree.source, "nomsu:import_file(" .. tostring(self:compile(path)) .. ")")
end,
["tests"] = function(self, tree)
return LuaCode.Value(tree.source, "TESTS")
end,
- ["test 1"] = function(self, tree, body)
+ ["test"] = function(self, tree, body)
local test_str = table.concat((function()
local _accum_0 = { }
local _len_0 = 1
@@ -485,13 +471,13 @@ do
end
return _accum_0
end)(), "\n")
- return LuaCode(tree.source, "TESTS[" .. tostring(repr(tostring(tree.source))) .. "] = ", repr(test_str))
+ return LuaCode(tree.source, "TESTS[" .. tostring(tostring(tree.source):as_lua()) .. "] = ", test_str:as_lua())
end,
["is jit"] = function(self, tree, code)
return LuaCode.Value(tree.source, jit and "true" or "false")
end,
["Lua version"] = function(self, tree, code)
- return LuaCode.Value(tree.source, repr(_VERSION))
+ return LuaCode.Value(tree.source, _VERSION:as_lua())
end,
__parent = setmetatable({ }, {
__index = function(self, key)
@@ -531,6 +517,16 @@ do
end
end
end
+ NomsuCompiler.import_file = function(self, path)
+ local found = false
+ for _, f in Files.walk(path) do
+ if match(f, "%.lua$") or match(f, "%.nom$") or match(f, "^/dev/fd/[012]$") then
+ found = true
+ self:import(self:run_file(f))
+ end
+ end
+ return found
+ end
NomsuCompiler.run = function(self, to_run, compile_actions)
local source = to_run.source or Source(to_run, 1, #to_run)
if type(source) == 'string' then
@@ -540,7 +536,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)
@@ -682,7 +678,7 @@ do
local get_version = self[("Nomsu version"):as_lua_id()]
if get_version then
do
- local upgrade = self[("1 upgraded from 2 to 3"):as_lua_id()]
+ local upgrade = self[("1 upgraded from 2 to"):as_lua_id()]
if upgrade then
tree = upgrade(tree, tree.version, get_version())
end
@@ -693,38 +689,36 @@ do
local _exp_0 = tree.type
if "Action" == _exp_0 then
local stub = tree.stub
- do
- local compile_action = compile_actions[stub]
- if compile_action then
- local args
- do
- local _accum_0 = { }
- local _len_0 = 1
- for _index_0 = 1, #tree do
- local arg = tree[_index_0]
- if type(arg) ~= "string" then
- _accum_0[_len_0] = arg
- _len_0 = _len_0 + 1
- end
+ local compile_action = compile_actions[stub]
+ if compile_action and not tree.target then
+ local args
+ do
+ local _accum_0 = { }
+ local _len_0 = 1
+ for _index_0 = 1, #tree do
+ local arg = tree[_index_0]
+ if type(arg) ~= "string" then
+ _accum_0[_len_0] = arg
+ _len_0 = _len_0 + 1
end
- args = _accum_0
end
- local ret = compile_action(self, tree, unpack(args))
- if ret == nil then
+ args = _accum_0
+ end
+ local ret = compile_action(self, tree, unpack(args))
+ if ret == nil then
+ local info = debug.getinfo(compile_action, "S")
+ 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 SyntaxTree:is_instance(ret) then
+ if ret == tree then
local info = debug.getinfo(compile_action, "S")
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.")
+ self:compile_error(tree, "The compile-time action here (" .. tostring(stub) .. ") is producing an endless loop.", "Look at the implementation of (" .. tostring(stub) .. ") in " .. tostring(filename) .. ":" .. tostring(info.linedefined) .. " and make sure it's not just returning the original tree.")
end
- if AST.is_syntax_tree(ret) then
- if ret == tree then
- local info = debug.getinfo(compile_action, "S")
- local filename = Source:from_string(info.source).filename
- self:compile_error(tree, "The compile-time action here (" .. tostring(stub) .. ") is producing an endless loop.", "Look at the implementation of (" .. tostring(stub) .. ") in " .. tostring(filename) .. ":" .. tostring(info.linedefined) .. " and make sure it's not just returning the original tree.")
- end
- return self:compile(ret, compile_actions)
- end
- return ret
+ return self:compile(ret, compile_actions)
end
+ return ret
end
local lua = LuaCode.Value(tree.source)
if tree.target then
@@ -749,9 +743,9 @@ do
if tok.type == "Block" then
self:compile_error(tok, "Can't compile action (" .. tostring(stub) .. ") with a Block as an argument.", "Maybe there should be a compile-time action with that name that isn't being found?")
elseif tok.type == "Action" then
- self:compile_error(tok, "Can't use this as an argument to (" .. tostring(stub) .. "), since it's not an expression, it produces: " .. tostring(repr(arg_lua)), "Check the implementation of (" .. tostring(tok.stub) .. ") to see if it is actually meant to produce an expression.")
+ self:compile_error(tok, "Can't use this as an argument to (" .. tostring(stub) .. "), since it's not an expression, it produces: " .. tostring(tostring(arg_lua)), "Check the implementation of (" .. tostring(tok.stub) .. ") to see if it is actually meant to produce an expression.")
else
- self:compile_error(tok, "Can't use this as an argument to (" .. tostring(stub) .. "), since it's not an expression, it produces: " .. tostring(repr(arg_lua)))
+ self:compile_error(tok, "Can't use this as an argument to (" .. tostring(stub) .. "), since it's not an expression, it produces: " .. tostring(tostring(arg_lua)))
end
end
insert(args, arg_lua)
@@ -765,9 +759,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
- for k, v in pairs(AST.is_syntax_tree(tree[1], "EscapedNomsu") and tree or tree[1]) do
+ local as_lua
+ as_lua = function(x)
+ if type(x) == 'number' then
+ return tostring(x)
+ elseif SyntaxTree:is_instance(x) then
+ return self:compile(x, compile_actions)
+ else
+ return x:as_lua()
+ end
+ end
+ 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
@@ -778,12 +782,12 @@ do
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, compile_actions) or repr(k)), "]= ")
+ lua:append("[", as_lua(k), "]= ")
end
if k == "source" then
- lua:append(repr(tostring(v)))
+ lua:append(tostring(v):as_lua())
else
- lua:append(AST.is_syntax_tree(v) and self:compile(v, compile_actions) or repr(v))
+ lua:append(as_lua(v))
end
end
lua:append("}")
@@ -854,7 +858,7 @@ do
if #lua.bits > 0 then
lua:append("..")
end
- lua:append(repr(string_buffer))
+ lua:append(string_buffer:as_lua())
string_buffer = ""
end
local bit_lua = self:compile(bit, compile_actions)
@@ -880,7 +884,7 @@ do
if #lua.bits > 0 then
lua:append("..")
end
- lua:append(repr(string_buffer))
+ lua:append(string_buffer:as_lua())
end
if #lua.bits > 1 then
lua:parenthesize()
@@ -925,7 +929,7 @@ do
self:compile_error(tree[2], "Can't use this as a dict value, since it's not an expression.")
end
local key_str = match(tostring(key_lua), [=[^["']([a-zA-Z_][a-zA-Z0-9_]*)['"]$]=])
- if key_str then
+ if key_str and key_str:is_lua_id() then
return LuaCode(tree.source, key_str, "=", value_lua)
elseif sub(tostring(key_lua), 1, 1) == "[" then
return LuaCode(tree.source, "[ ", key_lua, "]=", value_lua)
@@ -948,15 +952,13 @@ do
self:compile_error(key, "Can't use this as an index, since it's not an expression.")
end
local key_lua_str = tostring(key_lua)
- do
- local lua_id = match(key_lua_str, "^['\"]([a-zA-Z_][a-zA-Z0-9_]*)['\"]$")
- if lua_id then
- lua:append("." .. tostring(lua_id))
- elseif sub(key_lua_str, 1, 1) == '[' then
- lua:append("[ ", key_lua, " ]")
- else
- lua:append("[", key_lua, "]")
- end
+ local lua_id = match(key_lua_str, "^['\"]([a-zA-Z_][a-zA-Z0-9_]*)['\"]$")
+ if lua_id and lua_id:is_lua_id() then
+ lua:append("." .. tostring(lua_id))
+ elseif sub(key_lua_str, 1, 1) == '[' then
+ lua:append("[ ", key_lua, " ]")
+ else
+ lua:append("[", key_lua, "]")
end
end
return lua
@@ -1004,7 +1006,11 @@ do
elseif "Action" == _exp_0 then
local nomsu = NomsuCode(tree.source)
if tree.target then
- nomsu:append(self:tree_to_inline_nomsu(tree.target), "::")
+ local inline_target = self:tree_to_inline_nomsu(tree.target)
+ if tree.target.type == "Action" then
+ inline_target:parenthesize()
+ end
+ nomsu:append(inline_target, "::")
end
for i, bit in ipairs(tree) do
if type(bit) == "string" then
@@ -1165,7 +1171,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
@@ -1272,13 +1278,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 1" then
- return line.stub == "use 1"
+ if prev_line.stub == "use" then
+ return line.stub == "use"
end
- if prev_line.stub == "test 1" then
+ if prev_line.stub == "test" then
return true
end
- if line.stub == "test 1" then
+ if line.stub == "test" then
return false
end
end
diff --git a/nomsu_compiler.moon b/nomsu_compiler.moon
index f5cc7d2..a1194d9 100644
--- a/nomsu_compiler.moon
+++ b/nomsu_compiler.moon
@@ -14,7 +14,7 @@ lpeg = require 'lpeg'
re = require 're'
utils = require 'utils'
Files = require 'files'
-{:repr, :stringify, :equivalent} = utils
+{:stringify, :equivalent} = utils
{:List, :Dict, :Text} = require 'containers'
export colors, colored
colors = require 'consolecolors'
@@ -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,53 +62,49 @@ 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 = {}
-max_parser_version = 0
-for version=1,999
- found_version = false
- for _, full_path in Files.walk("nomsu.#{version}.peg")
- if peg_contents = Files.read(full_path)
- found_version = true
- max_parser_version = version
- Parsers[version] = make_parser(peg_contents, make_tree)
- break unless found_version
+max_parser_version = 4
+for version=1,max_parser_version
+ peg_file = io.open("nomsu.#{version}.peg")
+ if not peg_file and package.nomsupath
+ for path in package.nomsupath\gmatch("[^;]+")
+ peg_file = io.open(path.."/nomsu.#{version}.peg")
+ break if peg_file
+ assert(peg_file, "could not find nomsu .peg file")
+ peg_contents = peg_file\read('*a')
+ peg_file\close!
+ Parsers[version] = make_parser(peg_contents, make_tree)
MAX_LINE = 80 -- For beautification purposes, try not to make lines much longer than this value
NomsuCompiler = setmetatable {}, {__tostring: => "Nomsu"}
_anon_chunk = 0
with NomsuCompiler
- .NOMSU_COMPILER_VERSION = 9
- .NOMSU_SYNTAX_VERSION = max_parser_version
.can_optimize = -> false
-- Discretionary/convenience stuff
.environment = {
- NOMSU_COMPILER_VERSION: 8, NOMSU_SYNTAX_VERSION: max_parser_version
+ NOMSU_COMPILER_VERSION: 11, NOMSU_SYNTAX_VERSION: max_parser_version
-- Lua stuff:
:next, :unpack, :setmetatable, :coroutine, :rawequal, :getmetatable, :pcall,
:error, :package, :os, :require, :tonumber, :tostring, :string, :xpcall, :module,
:print, :loadfile, :rawset, :_VERSION, :collectgarbage, :rawget, :rawlen,
- :table, :assert, :dofile, :loadstring, :type, :select, :math, :io, :load,
+ :table, :assert, :dofile, :loadstring, lua_type_of:type, :select, :math, :io, :load,
:pairs, :ipairs,
-- Nomsu types:
_List:List, _Dict:Dict,
-- Utilities and misc.
- repr:repr, stringify:stringify, utils:utils, lpeg:lpeg, re:re, Files:Files,
- :AST, TESTS: Dict{}, globals: Dict{}
+ stringify:stringify, utils:utils, lpeg:lpeg, re:re, Files:Files,
+ :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))]
@@ -205,16 +200,16 @@ with NomsuCompiler
add_lua_string_bits = (val_or_stmt, code)=>
cls_str = val_or_stmt == "value" and "LuaCode.Value(" or "LuaCode("
if code.type != "Text"
- return LuaCode.Value(code.source, cls_str, repr(tostring(code.source)), ", ", @compile(code), ")")
+ return LuaCode.Value(code.source, cls_str, tostring(code.source)\as_lua!, ", ", @compile(code), ")")
add_bit_lua = (lua, bit_lua)->
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)->
- lua = LuaCode.Value(text.source, cls_str, repr(tostring(text.source)))
+ lua = LuaCode.Value(text.source, cls_str, tostring(text.source)\as_lua!)
for bit in *text
if type(bit) == "string"
- add_bit_lua(lua, repr(bit))
+ add_bit_lua(lua, bit\as_lua!)
elseif bit.type == "Text"
add_bit_lua(lua, operate_on_text(bit))
else
@@ -230,7 +225,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 [[ ([+-] " ")* [0-9]+ (" " [*/^+-] (" " [+-])* " " [0-9]+)+ !. ]]
+ math_expression = re.compile [[ (([*/^+-] / [0-9]+) " ")* [*/^+-] !. ]]
compile_math_expression = (tree, ...)=>
lua = LuaCode.Value(tree.source)
for i,tok in ipairs tree
@@ -247,39 +242,38 @@ with NomsuCompiler
return lua
.environment.COMPILE_ACTIONS = setmetatable({
__imported: Dict{}
- ["Lua 1"]: (tree, code)=>
+ ["Lua"]: (tree, code)=>
return add_lua_string_bits(@, 'statements', code)
- ["Lua value 1"]: (tree, code)=>
+ ["Lua value"]: (tree, code)=>
return add_lua_string_bits(@, 'value', code)
- ["lua > 1"]: (tree, code)=>
+ ["lua >"]: (tree, code)=>
if code.type != "Text"
return LuaCode tree.source, "nomsu:run_lua(", @compile(code), ", nomsu);"
return add_lua_bits(@, "statements", code)
- ["= lua 1"]: (tree, code)=>
+ ["= lua"]: (tree, code)=>
if code.type != "Text"
return LuaCode.Value tree.source, "nomsu:run_lua(", @compile(code), ":as_statements('return '), nomsu)"
return add_lua_bits(@, "value", code)
- ["use 1"]: (tree, path)=>
+ ["use"]: (tree, path)=>
if path.type == 'Text' and #path == 1 and type(path[1]) == 'string'
- for _,f in Files.walk(path[1])
- @import(@run_file(f))
-
- return LuaCode(tree.source, "for i,f in Files.walk(", @compile(path), ") do nomsu:import(nomsu:run_file(f)) end")
+ unless @import_file(path[1])
+ @compile_error tree, "Could not find anything to import for #{path}"
+ return LuaCode(tree.source, "nomsu:import_file(#{@compile(path)})")
["tests"]: (tree)=> LuaCode.Value(tree.source, "TESTS")
- ["test 1"]: (tree, body)=>
+ ["test"]: (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)
+ LuaCode tree.source, "TESTS[#{tostring(tree.source)\as_lua!}] = ", test_str\as_lua!
["is jit"]: (tree, code)=>
return LuaCode.Value(tree.source, jit and "true" or "false")
["Lua version"]: (tree, code)=>
- return LuaCode.Value(tree.source, repr(_VERSION))
+ return LuaCode.Value(tree.source, _VERSION\as_lua!)
__parent: setmetatable({}, {
__index: (key)=>
@@ -296,11 +290,19 @@ with NomsuCompiler
continue if k == "__imported" or k == "__parent"
@environment.COMPILE_ACTIONS.__imported[k] or= v
+ .import_file = (path)=>
+ found = false
+ for _,f in Files.walk(path)
+ if match(f, "%.lua$") or match(f, "%.nom$") or match(f, "^/dev/fd/[012]$")
+ found = true
+ @import(@run_file(f))
+ return found
+
.run = (to_run, compile_actions)=>
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"
@@ -396,12 +398,13 @@ with NomsuCompiler
compile_actions or= @environment.COMPILE_ACTIONS
if tree.version
if get_version = @[("Nomsu version")\as_lua_id!]
- if upgrade = @[("1 upgraded from 2 to 3")\as_lua_id!]
+ if upgrade = @[("1 upgraded from 2 to")\as_lua_id!]
tree = upgrade(tree, tree.version, get_version!)
switch tree.type
when "Action"
stub = tree.stub
- if compile_action = compile_actions[stub]
+ compile_action = compile_actions[stub]
+ if compile_action and not tree.target
args = [arg for arg in *tree when type(arg) != "string"]
-- Force Lua to avoid tail call optimization for debugging purposes
-- TODO: use tail call?
@@ -412,7 +415,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
@@ -442,20 +445,27 @@ with NomsuCompiler
elseif tok.type == "Action"
@compile_error tok,
- "Can't use this as an argument to (#{stub}), since it's not an expression, it produces: #{repr arg_lua}",
+ "Can't use this as an argument to (#{stub}), since it's not an expression, it produces: #{tostring(arg_lua)}",
"Check the implementation of (#{tok.stub}) to see if it is actually meant to produce an expression."
else
@compile_error tok,
- "Can't use this as an argument to (#{stub}), since it's not an expression, it produces: #{repr arg_lua}"
+ "Can't use this as an argument to (#{stub}), since it's not an expression, it produces: #{tostring(arg_lua)}"
insert args, arg_lua
lua\concat_append args, ", "
lua\append ")"
return lua
when "EscapedNomsu"
- lua = LuaCode.Value tree.source, tree[1].type, "{"
+ lua = LuaCode.Value tree.source, "SyntaxTree{"
needs_comma, i = false, 1
- for k,v in pairs(AST.is_syntax_tree(tree[1], "EscapedNomsu") and tree or tree[1])
+ as_lua = (x)->
+ if type(x) == 'number'
+ tostring(x)
+ elseif SyntaxTree\is_instance(x)
+ @compile(x, compile_actions)
+ else x\as_lua!
+
+ 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
@@ -463,11 +473,11 @@ with NomsuCompiler
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, compile_actions) or repr(k)), "]= ")
+ lua\append("[", as_lua(k), "]= ")
if k == "source"
- lua\append repr(tostring(v))
+ lua\append tostring(v)\as_lua!
else
- lua\append(AST.is_syntax_tree(v) and @compile(v, compile_actions) or repr(v))
+ lua\append as_lua(v)
lua\append "}"
return lua
@@ -502,9 +512,9 @@ with NomsuCompiler
if type(bit) == "string"
string_buffer ..= bit
continue
- if string_buffer ~= ""
+ if string_buffer != ""
if #lua.bits > 0 then lua\append ".."
- lua\append repr(string_buffer)
+ lua\append string_buffer\as_lua!
string_buffer = ""
bit_lua = @compile(bit, compile_actions)
unless bit_lua.is_value
@@ -519,7 +529,7 @@ with NomsuCompiler
if string_buffer ~= "" or #lua.bits == 0
if #lua.bits > 0 then lua\append ".."
- lua\append repr(string_buffer)
+ lua\append string_buffer\as_lua!
if #lua.bits > 1
lua\parenthesize!
@@ -547,9 +557,8 @@ with NomsuCompiler
unless value_lua.is_value
@compile_error tree[2],
"Can't use this as 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_]*)['"]$]=])
- return if key_str
+ return if key_str and key_str\is_lua_id!
LuaCode tree.source, key_str,"=",value_lua
elseif sub(tostring(key_lua),1,1) == "["
-- NOTE: this *must* use a space after the [ to avoid freaking out
@@ -575,7 +584,8 @@ with NomsuCompiler
@compile_error key,
"Can't use this as an index, since it's not an expression."
key_lua_str = tostring(key_lua)
- if lua_id = match(key_lua_str, "^['\"]([a-zA-Z_][a-zA-Z0-9_]*)['\"]$")
+ lua_id = match(key_lua_str, "^['\"]([a-zA-Z_][a-zA-Z0-9_]*)['\"]$")
+ if lua_id and lua_id\is_lua_id!
lua\append ".#{lua_id}"
elseif sub(key_lua_str,1,1) == '['
-- NOTE: this *must* use a space after the [ to avoid freaking out
@@ -623,7 +633,10 @@ with NomsuCompiler
when "Action"
nomsu = NomsuCode(tree.source)
if tree.target
- nomsu\append @tree_to_inline_nomsu(tree.target), "::"
+ inline_target = @tree_to_inline_nomsu(tree.target)
+ if tree.target.type == "Action"
+ inline_target\parenthesize!
+ nomsu\append inline_target, "::"
for i,bit in ipairs tree
if type(bit) == "string"
clump_words = (type(tree[i-1]) == 'string' and is_operator(bit) != is_operator(tree[i-1]))
@@ -729,7 +742,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]
@@ -778,9 +791,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 1" then return line.stub == "use 1"
- if prev_line.stub == "test 1" then return true
- if line.stub == "test 1" then return false
+ 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
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
diff --git a/parser.lua b/parser.lua
index 0694e3e..5eee8ca 100644
--- a/parser.lua
+++ b/parser.lua
@@ -3,8 +3,6 @@ local re = require('re')
lpeg.setmaxstack(20000)
local P, R, S, C, Cmt, Carg, Cc
P, R, S, C, Cmt, Carg, Cc = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cmt, lpeg.Carg, lpeg.Cc
-local repr
-repr = require('utils').repr
local DEFS
do
local _with_0 = { }
diff --git a/parser.moon b/parser.moon
index 9a2e2ff..78e6291 100644
--- a/parser.moon
+++ b/parser.moon
@@ -3,7 +3,6 @@ lpeg = require 'lpeg'
re = require 're'
lpeg.setmaxstack 20000
{:P,:R,:S,:C,:Cmt,:Carg,:Cc} = lpeg
-{:repr} = require 'utils'
DEFS = with {}
-- Newline supports either windows-style CR+LF or unix-style LF
diff --git a/string2.lua b/string2.lua
index 5995de2..09cb5ea 100644
--- a/string2.lua
+++ b/string2.lua
@@ -27,34 +27,39 @@ isplit = function(self, sep)
}, 0
end
local lua_keywords = {
- "and",
- "break",
- "do",
- "else",
- "elseif",
- "end",
- "false",
- "for",
- "function",
- "goto",
- "if",
- "in",
- "local",
- "nil",
- "not",
- "or",
- "repeat",
- "return",
- "then",
- "true",
- "until",
- "while"
+ ["and"] = true,
+ ["break"] = true,
+ ["do"] = true,
+ ["else"] = true,
+ ["elseif"] = true,
+ ["end"] = true,
+ ["false"] = true,
+ ["for"] = true,
+ ["function"] = true,
+ ["goto"] = true,
+ ["if"] = true,
+ ["in"] = true,
+ ["local"] = true,
+ ["nil"] = true,
+ ["not"] = true,
+ ["or"] = true,
+ ["repeat"] = true,
+ ["return"] = true,
+ ["then"] = true,
+ ["true"] = true,
+ ["until"] = true,
+ ["while"] = true
}
+local is_lua_id
+is_lua_id = function(str)
+ return match(str, "^[_a-zA-Z][_a-zA-Z0-9]*$") and not lua_keywords[str]
+end
local string2 = {
isplit = isplit,
uppercase = upper,
lowercase = lower,
reversed = reverse,
+ is_lua_id = is_lua_id,
capitalized = function(self)
return gsub(self, '%l', upper, 1)
end,
@@ -73,6 +78,12 @@ local string2 = {
end
return _accum_0
end,
+ starts_with = function(self, s)
+ return sub(self, 1, #s) == s
+ end,
+ ends_with = function(self, s)
+ return #self >= #s and sub(self, #self - #s, -1) == s
+ end,
lines = function(self)
local _accum_0 = { }
local _len_0 = 1
@@ -109,19 +120,35 @@ local string2 = {
for _index_0 = 1, #_list_0 do
local line = _list_0[_index_0]
while #line > maxlen do
- local chunk = line:sub(1, maxlen)
- local split = chunk:find(' ', maxlen - buffer, true) or maxlen
- chunk = line:sub(1, split)
- line = line:sub(split + 1, -1)
+ local chunk = sub(line, 1, maxlen)
+ local split = find(chunk, ' ', maxlen - buffer, true) or maxlen
+ chunk = sub(line, 1, split)
+ line = sub(line, split + 1, -1)
lines[#lines + 1] = chunk
end
lines[#lines + 1] = line
end
return table.concat(lines, "\n")
end,
+ as_lua = function(self)
+ local escaped = gsub(self, "\\", "\\\\")
+ escaped = gsub(escaped, "\n", "\\n")
+ escaped = gsub(escaped, '"', '\\"')
+ escaped = gsub(escaped, "[^ %g]", function(c)
+ return format("\\%03d", byte(c, 1))
+ end)
+ return '"' .. escaped .. '"'
+ end,
+ as_nomsu = function(self)
+ local escaped = gsub(self, "\\", "\\\\")
+ escaped = gsub(escaped, "\n", "\\n")
+ escaped = gsub(escaped, '"', '\\"')
+ escaped = gsub(escaped, "[^ %g]", function(c)
+ return format("\\%03d", byte(c, 1))
+ end)
+ return '"' .. escaped .. '"'
+ end,
as_lua_id = function(str)
- local orig = str
- str = gsub(str, "^ *$", "%1 ")
str = gsub(str, "x([0-9A-F][0-9A-F])", "x78%1")
str = gsub(str, "%W", function(c)
if c == ' ' then
@@ -130,36 +157,42 @@ local string2 = {
return format("x%02X", byte(c))
end
end)
- str = gsub(str, "^_*%d", "_%1")
- if match(str, "^_*[abdefgilnortuw][aefhilnoru][acdefiklnoprstu]*$") then
- for _index_0 = 1, #lua_keywords do
- local kw = lua_keywords[_index_0]
- if match(str, ("^_*" .. kw .. "$")) then
- str = "_" .. str
- end
- end
+ if not (is_lua_id(match(str, "^_*(.*)$"))) then
+ str = "_" .. str
end
return str
end,
from_lua_id = function(str)
- if match(str, "^_+[abdefgilnortuw][aefhilnoru][acdefiklnoprstu]*$") then
- for _index_0 = 1, #lua_keywords do
- local kw = lua_keywords[_index_0]
- if match(str, ("^_+" .. kw .. "$")) then
- str = str:sub(2, -1)
- end
- end
+ if not (is_lua_id(match(str, "^_*(.*)$"))) then
+ str = sub(str, 2, -1)
end
- str = gsub(str, "^_(_*%d.*)", "%1")
str = gsub(str, "_", " ")
str = gsub(str, "x([0-9A-F][0-9A-F])", function(hex)
return char(tonumber(hex, 16))
end)
- str = gsub(str, "^ ([ ]*)$", "%1")
return str
end
}
for k, v in pairs(string) do
string2[k] = string2[k] or v
end
+local _list_0 = {
+ "",
+ "_",
+ " ",
+ "return",
+ "asdf",
+ "one two",
+ "one_two",
+ "Hex2Dec",
+ "He-ec",
+ "\3"
+}
+for _index_0 = 1, #_list_0 do
+ local test = _list_0[_index_0]
+ local lua_id = string2.as_lua_id(test)
+ assert(is_lua_id(lua_id), "failed to convert '" .. tostring(test) .. "' to a valid Lua identifier (got '" .. tostring(lua_id) .. "')")
+ local roundtrip = string2.from_lua_id(lua_id)
+ assert(roundtrip == test, "Failed lua_id roundtrip: '" .. tostring(test) .. "' -> '" .. tostring(lua_id) .. "' -> '" .. tostring(roundtrip) .. "'")
+end
return string2
diff --git a/string2.moon b/string2.moon
index 2259272..e6db628 100644
--- a/string2.moon
+++ b/string2.moon
@@ -13,15 +13,21 @@ isplit = (sep='%s+')=>
return step, {str:@, pos:1, :sep}, 0
lua_keywords = {
- "and", "break", "do", "else", "elseif", "end", "false", "for", "function", "goto", "if",
- "in", "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while"
+ ["and"]:true, ["break"]:true, ["do"]:true, ["else"]:true, ["elseif"]:true, ["end"]:true,
+ ["false"]:true, ["for"]:true, ["function"]:true, ["goto"]:true, ["if"]:true,
+ ["in"]:true, ["local"]:true, ["nil"]:true, ["not"]:true, ["or"]:true, ["repeat"]:true,
+ ["return"]:true, ["then"]:true, ["true"]:true, ["until"]:true, ["while"]:true
}
+is_lua_id = (str)->
+ match(str, "^[_a-zA-Z][_a-zA-Z0-9]*$") and not lua_keywords[str]
string2 = {
- :isplit, uppercase:upper, lowercase:lower, reversed:reverse
+ :isplit, uppercase:upper, lowercase:lower, reversed:reverse, :is_lua_id
capitalized: => gsub(@, '%l', upper, 1)
byte: byte, bytes: (i, j)=> {byte(@, i or 1, j or -1)}
split: (sep)=> [chunk for i,chunk in isplit(@, sep)]
+ starts_with: (s)=> sub(@, 1, #s) == s
+ ends_with: (s)=> #@ >= #s and sub(@, #@-#s, -1) == s
lines: => [line for i,line in isplit(@, '\n')]
line: (line_num)=>
for i, line, start in isplit(@, '\n')
@@ -37,22 +43,32 @@ string2 = {
lines = {}
for line in *@lines!
while #line > maxlen
- chunk = line\sub(1, maxlen)
- split = chunk\find(' ', maxlen-buffer, true) or maxlen
- chunk = line\sub(1, split)
- line = line\sub(split+1, -1)
+ chunk = sub(line, 1, maxlen)
+ split = find(chunk, ' ', maxlen-buffer, true) or maxlen
+ chunk = sub(line, 1, split)
+ line = sub(line, split+1, -1)
lines[#lines+1] = chunk
lines[#lines+1] = line
return table.concat(lines, "\n")
+ as_lua: =>
+ escaped = gsub(@, "\\", "\\\\")
+ escaped = gsub(escaped, "\n", "\\n")
+ escaped = gsub(escaped, '"', '\\"')
+ escaped = gsub(escaped, "[^ %g]", (c)-> format("\\%03d", byte(c, 1)))
+ return '"'..escaped..'"'
+
+ as_nomsu: =>
+ escaped = gsub(@, "\\", "\\\\")
+ escaped = gsub(escaped, "\n", "\\n")
+ escaped = gsub(escaped, '"', '\\"')
+ escaped = gsub(escaped, "[^ %g]", (c)-> format("\\%03d", byte(c, 1)))
+ return '"'..escaped..'"'
+
-- Convert an arbitrary text into a valid Lua identifier. This function is injective,
-- but not idempotent. In logic terms: (x != y) => (as_lua_id(x) != as_lua_id(y)),
-- but not (as_lua_id(a) == b) => (as_lua_id(b) == b).
as_lua_id: (str)->
- orig = str
- -- Empty strings are not valid lua identifiers, so treat them like " ",
- -- and treat " " as " ", etc. to preserve injectivity.
- str = gsub str, "^ *$", "%1 "
-- Escape 'x' (\x78) when it precedes something that looks like an uppercase hex sequence.
-- This way, all Lua IDs can be unambiguously reverse-engineered, but normal usage
-- of 'x' won't produce ugly Lua IDs.
@@ -62,29 +78,26 @@ string2 = {
str = gsub str, "%W", (c)->
if c == ' ' then '_'
else format("x%02X", byte(c))
- -- Lua IDs can't start with numbers, so map "1" -> "_1", "_1" -> "__1", etc.
- str = gsub str, "^_*%d", "_%1"
- -- This pattern is guaranteed to match all keywords, but also matches some other stuff.
- if match str, "^_*[abdefgilnortuw][aefhilnoru][acdefiklnoprstu]*$"
- for kw in *lua_keywords
- if match str, ("^_*"..kw.."$")
- str = "_"..str
+
+ unless is_lua_id(match(str, "^_*(.*)$"))
+ str = "_"..str
return str
-- from_lua_id(as_lua_id(str)) == str, but behavior is unspecified for inputs that
-- did not come from as_lua_id()
from_lua_id: (str)->
- -- This pattern is guaranteed to match all keywords, but also matches some other stuff.
- if match str, "^_+[abdefgilnortuw][aefhilnoru][acdefiklnoprstu]*$"
- for kw in *lua_keywords
- if match str, ("^_+"..kw.."$")
- str = str\sub(2,-1)
- str = gsub(str, "^_(_*%d.*)", "%1")
+ unless is_lua_id(match(str, "^_*(.*)$"))
+ str = sub(str,2,-1)
str = gsub(str, "_", " ")
str = gsub(str, "x([0-9A-F][0-9A-F])", (hex)-> char(tonumber(hex, 16)))
- str = gsub(str, "^ ([ ]*)$", "%1")
return str
}
for k,v in pairs(string) do string2[k] or= v
+for test in *{"", "_", " ", "return", "asdf", "one two", "one_two", "Hex2Dec", "He-ec", "\3"}
+ lua_id = string2.as_lua_id(test)
+ assert is_lua_id(lua_id), "failed to convert '#{test}' to a valid Lua identifier (got '#{lua_id}')"
+ roundtrip = string2.from_lua_id(lua_id)
+ assert roundtrip == test, "Failed lua_id roundtrip: '#{test}' -> '#{lua_id}' -> '#{roundtrip}'"
+
return string2
diff --git a/syntax_tree.lua b/syntax_tree.lua
index 72b510e..fe4f6dc 100644
--- a/syntax_tree.lua
+++ b/syntax_tree.lua
@@ -1,5 +1,3 @@
-local repr
-repr = require('utils').repr
local insert, remove, concat
do
local _obj_0 = table
@@ -8,82 +6,104 @@ 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
+local as_lua
+as_lua = function(self)
+ if type(self) == 'number' then
+ return tostring(self)
end
- return type(n) == 'table' and getmetatable(n) and AST[n.type] == getmetatable(n) and (t == nil or n.type == t)
-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.is_instance = function(self, x)
- return getmetatable(x) == self
- end
- cls.__tostring = function(self)
- return tostring(self.type) .. tostring(repr(self, (function() end)))
- end
- cls.__repr = function(self)
- return tostring(self.type) .. tostring(repr(self, (function() end)))
- 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
+ local mt = getmetatable(self)
+ if mt then
+ do
+ local _as_lua = mt.as_lua
+ if _as_lua then
+ return _as_lua(self)
+ end
end
- })
- cls.get_source_code = function(self)
- return self.source_code_for_tree[self]
end
- cls.map = function(self, fn)
+ end
+ return error("Not supported: " .. tostring(self))
+end
+local SyntaxTree
+do
+ local _class_0
+ local _base_0 = {
+ __tostring = function(self)
+ local bits
+ do
+ local _accum_0 = { }
+ local _len_0 = 1
+ for _index_0 = 1, #self do
+ local b = self[_index_0]
+ _accum_0[_len_0] = tostring(b)
+ _len_0 = _len_0 + 1
+ end
+ bits = _accum_0
+ end
+ for k, v in pairs(self) do
+ if not (bits[k]) then
+ table.insert(bits, "[ " .. tostring(tostring(k)) .. "]=" .. tostring(tostring(v)))
+ end
+ end
+ 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_0 = 1, #self do
+ local b = self[_index_0]
+ _accum_0[_len_0] = as_lua(b)
+ _len_0 = _len_0 + 1
+ end
+ bits = _accum_0
+ end
+ for k, v in pairs(self) do
+ if not (bits[k]) then
+ table.insert(bits, "[ " .. tostring(as_lua(k)) .. "]=" .. tostring(as_lua(v)))
+ end
+ end
+ 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 = {
@@ -97,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
@@ -115,76 +135,78 @@ 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,
+ 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
- if self.target ~= other.target then
- return false
- end
- return true
- end
- end
- AST[name] = setmetatable(cls, {
- __tostring = function(self)
- return self.__name
+ return _accum_0
end,
- __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
- setmetatable(t, self)
- do
- local init = t.__init
- if init then
- init(t)
+ 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] = arg_i
+ arg_i = arg_i + 1
end
end
- return t
+ while type(stub_bits[#stub_bits]) == 'number' do
+ stub_bits[#stub_bits] = nil
+ 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
+ setmetatable(t, self.__base)
+ if t.type == 'Action' then
+ t.stub = t:get_stub()
end
- return _accum_0
+ return t
end
-return AST
+return SyntaxTree
diff --git a/syntax_tree.moon b/syntax_tree.moon
index 305a1ee..03596f8 100644
--- a/syntax_tree.moon
+++ b/syntax_tree.moon
@@ -1,87 +1,101 @@
-- This file contains the datastructures used to represent parsed Nomsu syntax trees,
-- as well as the logic for converting them to Lua code.
-{:repr} = require 'utils'
{:insert, :remove, :concat} = table
{:Source} = require "code_obj"
unpack or= table.unpack
-AST = {}
-AST.is_syntax_tree = (n, t=nil)->
- type(n) == 'table' and getmetatable(n) and AST[n.type] == getmetatable(n) and (t == nil or n.type == t)
+as_lua = =>
+ if type(@) == 'number'
+ return tostring(@)
+ if mt = getmetatable(@)
+ if _as_lua = mt.as_lua
+ return _as_lua(@)
+ error("Not supported: #{@}")
-types = {"Number", "Var", "Block", "EscapedNomsu", "Text", "List", "Dict", "DictEntry",
- "IndexChain", "Action", "FileChunks", "Error", "Comment"}
-for name in *types
- cls = {}
- with cls
- .__class = cls
- .__index = cls
- .__name = name
- .type = name
- .is_instance = (x)=> getmetatable(x) == @
- .__tostring = => "#{@type}#{repr @, (->)}"
- .__repr = => "#{@type}#{repr @, (->)}"
- .source_code_for_tree = setmetatable({}, {__index:(t)=>
- s = t.source
- Files = require 'files'
- f = Files.read(s.filename)
- return f
- })
- .get_source_code = => @source_code_for_tree[@]
- .map = (fn)=>
- replacement = fn(@)
- if replacement == false then return nil
- if replacement
- -- Clone the replacement, so we can give it a proper source/comments
- if AST.is_syntax_tree(replacement)
- replacement = setmetatable {k,v for k,v in pairs replacement}, getmetatable(replacement)
- replacement.source = @source
- replacement.comments = {unpack(@comments)} if @comments
- if init = replacement.__init then init(replacement)
- else
- replacement = {source:@source, comments:@comments and {unpack(@comments)}}
- changes = false
- for k,v in pairs(@)
- replacement[k] = v
- if AST.is_syntax_tree(v)
- r = v\map(fn)
- continue if r == v or r == nil
- changes = true
- replacement[k] = r
- return @ unless changes
- replacement = setmetatable replacement, getmetatable(@)
- if init = replacement.__init then init(replacement)
- return replacement
- .__eq = (other)=>
- return false if type(@) != type(other) or #@ != #other or getmetatable(@) != getmetatable(other)
- for i=1,#@
- return false if @[i] != other[i]
- return false if @target != other.target
- return true
+--types = {"Number", "Var", "Block", "EscapedNomsu", "Text", "List", "Dict", "DictEntry",
+-- "IndexChain", "Action", "FileChunks", "Error", "Comment"}
+class SyntaxTree
+ @__type: "Syntax Tree"
+
+ __tostring: =>
+ bits = [tostring(b) for b in *@]
+ for k,v in pairs(@)
+ unless bits[k]
+ table.insert(bits, "[ #{tostring(k)}]=#{tostring(v)}")
+ return "SyntaxTree{#{table.concat(bits, ", ")}}"
- AST[name] = setmetatable cls,
- __tostring: => @__name
- __call: (t)=>
- if type(t.source) == 'string'
- t.source = Source\from_string(t.source)
- else
- assert(Source\is_instance(t.source))
- setmetatable(t, @)
- if init = t.__init then init(t)
- return t
+ __eq: (other)=>
+ return false if type(@) != type(other) or #@ != #other or getmetatable(@) != getmetatable(other)
+ for i=1,#@
+ return false if @[i] != other[i]
+ return false if @target != other.target
+ return true
-AST.Action.__init = =>
- stub_bits = {}
- arg_i = 1
- for a in *@
- if type(a) == 'string'
- stub_bits[#stub_bits+1] = a
+ as_lua: =>
+ bits = [as_lua(b) for b in *@]
+ for k,v in pairs(@)
+ unless bits[k]
+ table.insert(bits, "[ #{as_lua(k)}]=#{as_lua(v)}")
+ return "SyntaxTree{#{table.concat(bits, ", ")}}"
+
+ @source_code_for_tree: setmetatable({}, {__index:(t)=>
+ s = t.source
+ Files = require 'files'
+ f = Files.read(s.filename)
+ return f
+ })
+ get_source_code: => @@source_code_for_tree[@]
+ map: (fn)=>
+ replacement = fn(@)
+ if replacement == false then return nil
+ if replacement
+ -- Clone the replacement, so we can give it a proper source/comments
+ if SyntaxTree\is_instance(replacement)
+ replacement = {k,v for k,v in pairs replacement}
+ replacement.source = @source
+ replacement.comments = {unpack(@comments)} if @comments
+ replacement = SyntaxTree(replacement)
else
- stub_bits[#stub_bits+1] = tostring(arg_i)
- arg_i += 1
- @stub = concat stub_bits, " "
+ replacement = {source:@source, comments:@comments and {unpack(@comments)}}
+ changes = false
+ for k,v in pairs(@)
+ replacement[k] = v
+ if SyntaxTree\is_instance(v)
+ r = v\map(fn)
+ continue if r == v or r == nil
+ changes = true
+ replacement[k] = r
+ return @ unless changes
+ replacement = SyntaxTree(replacement)
+ return replacement
+
+ get_args: =>
+ assert(@type == "Action", "Only actions have arguments")
+ return [tok for tok in *@ when type(tok) != 'string']
+
+ get_stub: =>
+ stub_bits = {}
+ arg_i = 1
+ for a in *@
+ if type(a) == 'string'
+ stub_bits[#stub_bits+1] = a
+ else
+ stub_bits[#stub_bits+1] = arg_i
+ arg_i += 1
+ while type(stub_bits[#stub_bits]) == 'number'
+ stub_bits[#stub_bits] = nil
+ return concat stub_bits, " "
+
+ @is_instance: (t)=>
+ type(t) == 'table' and getmetatable(t) == @__base
+
-AST.Action.get_args = =>
- [tok for tok in *@ when type(tok) != 'string']
+getmetatable(SyntaxTree).__call = (t)=>
+ if type(t.source) == 'string'
+ t.source = Source\from_string(t.source)
+ setmetatable(t, @__base)
+ if t.type == 'Action'
+ t.stub = t\get_stub!
+ return t
-return AST
+return SyntaxTree
diff --git a/tools/autoformat.nom b/tools/autoformat.nom
index a915fa4..13f54ef 100755
--- a/tools/autoformat.nom
+++ b/tools/autoformat.nom
@@ -1,7 +1,7 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
Auto-format Nomsu code. Usage:
- nomsu tools/autoformat.nom [-i] file1 file2 directory1 ...
+ nomsu tools/autoformat.nom [-i] file1 file2 directory1 ...
If the first argument is "-i", modifications will be performed in-place. Otherwise,
the formatted code will be printed.
diff --git a/tools/find_action.nom b/tools/find_action.nom
index e6fe90a..02abdbc 100755
--- a/tools/find_action.nom
+++ b/tools/find_action.nom
@@ -1,7 +1,7 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
Find an action by its stub. Usage:
- nomsu tools/find_action.nom "foo %" file1 file2 directory1 ...
+ nomsu tools/find_action.nom "foo %" file1 file2 directory1 ...
Will print all the code locations and code that uses the stub.
use "lib/os.nom"
@@ -14,13 +14,25 @@ for %path in %files:
for file %filename in %path:
unless (%filename::matches "%.nom$") (do next %filename)
%file = (read file %filename)
- %tree = (parse %file from %filename)
+ try:
+ %tree = (parse %file from %filename)
+ ..and if it barfs:
+ say (red "\%filename failed to parse")
+ %tree = (nil)
+ unless %tree: do next %filename
+ %results = []
for %t in recursive %tree:
if (%t is "Action" syntax tree):
if (%t.stub is %stub):
%line_num = (line number of %t.source.start in %file)
- say (blue "\%filename:\%line_num:")
- say (yellow (source lines of %t))
+ %results::add {..}
+ line: %line_num
+ text: "\
+ ..\(blue "\%filename:\%line_num:")
+ \(yellow (source lines of %t))"
if (%t is syntax tree):
for %sub in %t: recurse %t on %sub
+
+ sort %results by % -> %.line
+ for % in %results: say %.text
diff --git a/tools/parse.nom b/tools/parse.nom
index 9d728e8..de6aff4 100755
--- a/tools/parse.nom
+++ b/tools/parse.nom
@@ -1,11 +1,11 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
Tool to print out a parse tree of files in an easy-to-read format. Usage:
- nomsu tools/parse.nom file1 file2 directory1 ...
+ nomsu tools/parse.nom file1 file2 directory1 ...
use "lib/os.nom"
-action [print tree %t at indent %indent]:
+externally (print tree %t at indent %indent) means:
if %t.type is:
"Action":
say "\(%indent)Action (\(%t.stub)):"
diff --git a/tools/replace.nom b/tools/replace.nom
index c55793a..25ae0ae 100755
--- a/tools/replace.nom
+++ b/tools/replace.nom
@@ -1,7 +1,7 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
Tool to find and replace one tree with another.
- nomsu tools/replace.nom [-i] tree_to_replace replacement file1 file2 directory1 ...
+ nomsu tools/replace.nom [-i] tree_to_replace replacement file1 file2 directory1 ...
If "-i" is the first argument, replacements will be performed in-place. Otherwise, the
upgraded code will be printed.
diff --git a/tools/test.nom b/tools/test.nom
index 6d557ea..fb70d1c 100755
--- a/tools/test.nom
+++ b/tools/test.nom
@@ -1,7 +1,7 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
Tool to run all tests in a file (i.e. the code block inside a call to 'test %'). Usage:
- nomsu tools/test.nom file1 file2 directory1 ...
+ nomsu tools/test.nom file1 file2 directory1 ...
use "lib/os.nom"
use "lib/consolecolor.nom"
@@ -17,7 +17,6 @@ for %path in (command line args):
if (%filename::matches "%.nom$"): use %filename
for %path in (command line args): use %path
-
%tests = ((=lua "Source:from_string(\%s)") = %t for %s = %t in (tests))
for %path in (command line args):
for file %filename in %path:
diff --git a/tools/upgrade.nom b/tools/upgrade.nom
index cd3fbbb..575646e 100755
--- a/tools/upgrade.nom
+++ b/tools/upgrade.nom
@@ -1,7 +1,7 @@
-#!/usr/bin/env nomsu -V4.8.8.6
+#!/usr/bin/env nomsu -V4.8.10
#
Tool to automatically update code from old versions of Nomsu. Usage:
- nomsu tools/upgrade.nom [-i] file1 file2 directory1 ...
+ nomsu tools/upgrade.nom [-i] file1 file2 directory1 ...
If "-i" is the first argument, upgrades will be performed in-place. Otherwise, the
upgraded code will be printed.
@@ -10,22 +10,39 @@ use "lib/os.nom"
%args = (command line args)
%inplace = (no)
-if (%args.1 is "-i"):
- %inplace = (yes)
- %args::remove index 1
-
-if (%args.1 is "-t"):
- use "lib/consolecolor.nom"
- %test = (yes)
- %args::remove index 1
+%start_version = (nil)
+%version = (Nomsu version)
+repeat:
+ if %args.1 is:
+ "-i":
+ %inplace = (yes)
+ %args::remove index 1
+
+ "-t":
+ use "lib/consolecolor.nom"
+ %test = (yes)
+ %args::remove index 1
+
+ "-V":
+ %version = %args.2
+ %args::remove index 1
+ %args::remove index 1
+
+ "-S":
+ %start_version = %args.2
+ %args::remove index 1
+ %args::remove index 1
+
+ else: stop
for %path in %args:
for file %filename in %path:
unless (%filename::matches "%.nom$"): do next %filename
%tree = (parse (read file %filename) from %filename)
- %uptree = (%tree upgraded)
+ %uptree = (..)
+ %tree upgraded from (%start_version or (%tree.version or (Nomsu version))) to %version
%text = "\
- ..#!/usr/bin/env nomsu -V\(Nomsu version)
+ ..#!/usr/bin/env nomsu -V\%version
\(%uptree as nomsu)"
if: