aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--code_obj.lua14
-rw-r--r--code_obj.moon11
-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.nom2
-rw-r--r--compatibility/4.8.10.nom2
-rw-r--r--compatibility/4.9.nom61
-rw-r--r--compatibility/compatibility.nom2
-rw-r--r--containers.lua4
-rw-r--r--containers.moon2
-rw-r--r--core/collections.nom2
-rw-r--r--core/control_flow.nom2
-rw-r--r--core/coroutines.nom2
-rw-r--r--core/errors.nom6
-rw-r--r--core/id.nom2
-rw-r--r--core/io.nom6
-rw-r--r--core/math.nom4
-rw-r--r--core/metaprogramming.nom197
-rw-r--r--core/operators.nom32
-rw-r--r--core/scopes.nom2
-rw-r--r--core/text.nom6
-rw-r--r--error_handling.lua26
-rw-r--r--error_handling.moon27
-rw-r--r--examples/how_do_i.nom2
-rw-r--r--files.lua11
-rw-r--r--files.moon10
-rw-r--r--importer.lua62
-rw-r--r--importer.moon33
-rw-r--r--lib/consolecolor.nom4
-rw-r--r--lib/file_hash.nom2
-rw-r--r--lib/object.nom2
-rw-r--r--lib/os.nom3
-rw-r--r--lib/things.nom14
-rw-r--r--nomnom/ast.nom2
-rw-r--r--nomnom/code_obj.nom2
-rw-r--r--nomnom/compile.nom2
-rw-r--r--nomnom/decompile.nom15
-rw-r--r--nomnom/files.nom2
-rw-r--r--nomnom/source.nom2
-rw-r--r--nomsu.4.peg17
-rw-r--r--nomsu.lua196
-rwxr-xr-xnomsu.moon156
-rw-r--r--nomsu_compiler.lua1409
-rw-r--r--nomsu_compiler.moon939
-rw-r--r--nomsu_decompiler.lua411
-rw-r--r--nomsu_decompiler.moon319
-rw-r--r--nomsu_environment.lua325
-rw-r--r--nomsu_environment.moon184
-rw-r--r--syntax_tree.lua3
-rw-r--r--syntax_tree.moon1
-rwxr-xr-xtools/autoformat.nom2
-rwxr-xr-xtools/find_action.nom2
-rwxr-xr-xtools/parse.nom2
-rw-r--r--tools/repl.nom61
-rwxr-xr-xtools/replace.nom2
-rwxr-xr-xtools/test.nom2
-rwxr-xr-xtools/upgrade.nom2
64 files changed, 2162 insertions, 2471 deletions
diff --git a/Makefile b/Makefile
index e3db5a3..1c77c8e 100644
--- a/Makefile
+++ b/Makefile
@@ -12,10 +12,10 @@ UNINSTALL_VERSION=
MOON_FILES= code_obj.moon error_handling.moon files.moon nomsu.moon nomsu_compiler.moon \
syntax_tree.moon containers.moon bitops.moon parser.moon pretty_errors.moon \
- string2.moon
+ string2.moon nomsu_decompiler.moon nomsu_environment.moon importer.moon
LUA_FILES= code_obj.lua consolecolors.lua error_handling.lua files.lua nomsu.lua nomsu_compiler.lua \
syntax_tree.lua containers.lua bitops.lua parser.lua pretty_errors.lua \
- string2.lua
+ string2.lua nomsu_decompiler.lua nomsu_environment.lua importer.lua
CORE_NOM_FILES= $(wildcard core/*.nom)
CORE_LUA_FILES= $(patsubst %.nom,%.lua,$(CORE_NOM_FILES))
LIB_NOM_FILES= $(wildcard lib/*.nom)
diff --git a/code_obj.lua b/code_obj.lua
index 7d335e0..124064f 100644
--- a/code_obj.lua
+++ b/code_obj.lua
@@ -265,6 +265,10 @@ do
end
})
_base_0.__class = _class_0
+ local self = _class_0
+ self.is_instance = function(self, x)
+ return type(x) == 'table' and x.__class == self
+ end
Code = _class_0
end
do
@@ -373,7 +377,7 @@ do
if suffix == nil then
suffix = ";"
end
- if not (self.is_value) then
+ if self:text():matches(";$") or self:text() == "" then
return self
end
local statements = LuaCode(self.source)
@@ -415,7 +419,6 @@ do
}
end,
parenthesize = function(self)
- assert(self.is_value, "Cannot parenthesize lua statements")
self:prepend("(")
return self:append(")")
end
@@ -426,7 +429,6 @@ do
__init = function(self, ...)
_class_0.__parent.__init(self, ...)
self.free_vars = { }
- self.is_value = false
end,
__base = _base_0,
__name = "LuaCode",
@@ -450,12 +452,6 @@ do
end
})
_base_0.__class = _class_0
- local self = _class_0
- self.Value = function(...)
- local lua = LuaCode(...)
- lua.is_value = true
- return lua
- end
if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
end
diff --git a/code_obj.moon b/code_obj.moon
index cb8eae2..39a59c6 100644
--- a/code_obj.moon
+++ b/code_obj.moon
@@ -50,6 +50,8 @@ class Code
--assert(@source and Source\is_instance(@source), "Source has the wrong type")
@append(...)
+ @is_instance: (x)=> type(x) == 'table' and x.__class == @
+
text: =>
if @__str == nil
buff, indent = {}, 0
@@ -165,13 +167,7 @@ class LuaCode extends Code
new: (...)=>
super ...
@free_vars = {}
- @is_value = false
- @Value = (...)->
- lua = LuaCode(...)
- lua.is_value = true
- return lua
-
add_free_vars: (vars)=>
return unless #vars > 0
seen = {[v]:true for v in *@free_vars}
@@ -219,7 +215,7 @@ class LuaCode extends Code
return to_declare
as_statements: (prefix="", suffix=";")=>
- unless @is_value
+ if @text!\matches(";$") or @text! == ""
return self
statements = LuaCode(@source)
if prefix != ""
@@ -250,7 +246,6 @@ class LuaCode extends Code
}
parenthesize: =>
- assert @is_value, "Cannot parenthesize lua statements"
@prepend "("
@append ")"
diff --git a/compatibility/2.3.nom b/compatibility/2.3.nom
index 7de353e..df3c619 100644
--- a/compatibility/2.3.nom
+++ b/compatibility/2.3.nom
@@ -4,6 +4,8 @@
use "compatibility/compatibility.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
upgrade action (%a = %b) to "2.3" as (%a == %b)
upgrade action (<- %) to "2.3" as (set %)
upgrade action (assign %) to "2.3" as (set %)
diff --git a/compatibility/2.4.nom b/compatibility/2.4.nom
index 1a7c9c3..f26bf0d 100644
--- a/compatibility/2.4.nom
+++ b/compatibility/2.4.nom
@@ -4,6 +4,8 @@
use "compatibility/compatibility.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
upgrade %tree to "2.4" as:
unless (%tree is "Action" syntax tree): return
if %tree.stub is:
diff --git a/compatibility/2.5.5.5.nom b/compatibility/2.5.5.5.nom
index efed78d..b7a7b21 100644
--- a/compatibility/2.5.5.5.nom
+++ b/compatibility/2.5.5.5.nom
@@ -4,6 +4,8 @@
use "compatibility/compatibility.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
upgrade action [hash %, sha1 %] to "2.5.5.5" as (..)
=lua "\
..\(base64 decode (hash %)):gsub('.', function(c) return ('%x02'):format(c) end)"
diff --git a/compatibility/2.5.nom b/compatibility/2.5.nom
index b1e01b0..8c3f4d0 100644
--- a/compatibility/2.5.nom
+++ b/compatibility/2.5.nom
@@ -4,6 +4,8 @@
use "compatibility/compatibility.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
upgrade action (for %1 where %2 matches %3 %4) to "2.5" as (..)
for %1 in %2 matching %3 %4
diff --git a/compatibility/2.nom b/compatibility/2.nom
index d7a1489..eb4060a 100644
--- a/compatibility/2.nom
+++ b/compatibility/2.nom
@@ -4,6 +4,8 @@
use "compatibility/compatibility.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
upgrade %tree to "2" as:
unless (%tree is "Action" syntax tree): return
if (%tree.stub is "if % % else %"):
diff --git a/compatibility/3.5.5.6.nom b/compatibility/3.5.5.6.nom
index 89fbc8a..ff69614 100644
--- a/compatibility/3.5.5.6.nom
+++ b/compatibility/3.5.5.6.nom
@@ -4,6 +4,8 @@
use "compatibility/compatibility.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
upgrade action "traceback" to "3.5.5.6" via (..)
[%] -> (barf "'traceback' has been deprecated")
diff --git a/compatibility/3.6.nom b/compatibility/3.6.nom
index 7cd5c64..b2fc5b4 100644
--- a/compatibility/3.6.nom
+++ b/compatibility/3.6.nom
@@ -4,6 +4,8 @@
use "compatibility/compatibility.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
upgrade action [..]
append %item to %list, add %item to %list, to %list add %item, to %list append %item
..to "3.6" as (%list::add %item)
diff --git a/compatibility/3.7.nom b/compatibility/3.7.nom
index 599e0d5..76a49e2 100644
--- a/compatibility/3.7.nom
+++ b/compatibility/3.7.nom
@@ -4,6 +4,8 @@
use "compatibility/compatibility.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
upgrade action [%index st to last in %list] to "3.7" as (%list::%index st to last)
upgrade action [%index nd to last in %list] to "3.7" as (%list::%index nd to last)
upgrade action [%index rd to last in %list] to "3.7" as (%list::%index rd to last)
diff --git a/compatibility/3.nom b/compatibility/3.nom
index 5156aa8..a4840ca 100644
--- a/compatibility/3.nom
+++ b/compatibility/3.nom
@@ -4,6 +4,8 @@
use "compatibility/compatibility.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
upgrade action (method %spec %body) to "3" as (my action %spec %body)
upgrade action (me) to "3" as %me
upgrade action (@) to "3" as %me
diff --git a/compatibility/4.8.10.nom b/compatibility/4.8.10.nom
index 66152b5..d62efa1 100644
--- a/compatibility/4.8.10.nom
+++ b/compatibility/4.8.10.nom
@@ -3,6 +3,8 @@
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
diff --git a/compatibility/4.9.nom b/compatibility/4.9.nom
new file mode 100644
index 0000000..f84171a
--- /dev/null
+++ b/compatibility/4.9.nom
@@ -0,0 +1,61 @@
+#!/usr/bin/env nomsu -V4.9.11.6
+#
+ This file defines upgrades from Nomsu <4.9 to 4.9
+use "compatibility/compatibility.nom"
+
+upgrade action "local action 1 2" to "4.9" 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.9" 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.9" 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.9" 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.9" as (what % compiles to)
+upgrade action (action %) to "4.9" as (%'s meaning)
+upgrade action (remove action %) to "4.9" as ((%'s meaning) = (nil))
+upgrade action (if %) to "4.9" as (when %)
diff --git a/compatibility/compatibility.nom b/compatibility/compatibility.nom
index 1153ab4..184ca56 100644
--- a/compatibility/compatibility.nom
+++ b/compatibility/compatibility.nom
@@ -5,6 +5,8 @@
use "lib/os.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
%UPGRADES = {}
externally (upgrade to %version via %upgrade_fn) means:
%UPGRADES.%version = %upgrade_fn
diff --git a/containers.lua b/containers.lua
index 9f8fa46..17d37a2 100644
--- a/containers.lua
+++ b/containers.lua
@@ -424,7 +424,9 @@ do
byte(tostring(self), start, stop)
})
end,
- [as_lua_id("with 1 ->")] = gsub,
+ [as_lua_id("with 1 ->")] = function(...)
+ return (gsub(...))
+ end,
bytes = function(self)
return List({
byte(tostring(self), 1, -1)
diff --git a/containers.moon b/containers.moon
index 9591fa2..4cafdaf 100644
--- a/containers.moon
+++ b/containers.moon
@@ -164,7 +164,7 @@ do
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
+ [as_lua_id "with 1 ->"]: (...)-> (gsub(...))
bytes: => List{byte(tostring(@), 1, -1)},
lines: => List(lines(@))
line: line
diff --git a/core/collections.nom b/core/collections.nom
index ae7f5f1..684f3f6 100644
--- a/core/collections.nom
+++ b/core/collections.nom
@@ -7,6 +7,8 @@ use "core/metaprogramming.nom"
use "core/control_flow.nom"
use "core/operators.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
# List functionality:
test:
%list = [1, 2, 3, 4, 5]
diff --git a/core/control_flow.nom b/core/control_flow.nom
index a692f95..f7a8423 100644
--- a/core/control_flow.nom
+++ b/core/control_flow.nom
@@ -8,6 +8,8 @@ use "core/text.nom"
use "core/operators.nom"
use "core/errors.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
# No-Op
test: do nothing
(do nothing) compiles to (Lua "")
diff --git a/core/coroutines.nom b/core/coroutines.nom
index 246a2ef..f03cfc8 100644
--- a/core/coroutines.nom
+++ b/core/coroutines.nom
@@ -4,6 +4,8 @@
use "core/metaprogramming.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
test:
%nums = []
%co = (..)
diff --git a/core/errors.nom b/core/errors.nom
index 0b0a6a3..74c0a54 100644
--- a/core/errors.nom
+++ b/core/errors.nom
@@ -4,6 +4,8 @@
use "core/metaprogramming.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
(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))"
@@ -12,7 +14,7 @@ use "core/metaprogramming.nom"
(assume %condition) compiles to:
lua> "\
- ..local \%assumption = 'Assumption failed: '..tostring(nomsu:tree_to_nomsu(\%condition))"
+ ..local \%assumption = 'Assumption failed: '..tostring((\%condition):get_source_code())"
return (..)
Lua "\
..if not \(%condition as lua expr) then
@@ -21,7 +23,7 @@ use "core/metaprogramming.nom"
(assume %a == %b) compiles to:
lua> "\
- ..local \%assumption = 'Assumption failed: '..tostring(nomsu:tree_to_nomsu(\(\(%a == %b))))"
+ ..local \%assumption = 'Assumption failed: '..tostring(\(\(%a == %b) as nomsu))"
define mangler
return (..)
Lua "\
diff --git a/core/id.nom b/core/id.nom
index a9231b9..46aeaa9 100644
--- a/core/id.nom
+++ b/core/id.nom
@@ -8,6 +8,8 @@ use "core/math.nom"
use "core/collections.nom"
use "core/control_flow.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
%NaN_surrogate = {}
%nil_surrogate = {}
%obj_by_id = {}
diff --git a/core/io.nom b/core/io.nom
index 6b67b50..e12e4eb 100644
--- a/core/io.nom
+++ b/core/io.nom
@@ -4,7 +4,9 @@
use "core/metaprogramming.nom"
-(say %message) compiles to (..)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+(say %message) compiles to:
lua> "\
..if \%message.type == "Text" then
return LuaCode(tree.source, "print(", \(%message as lua expr), ");");
@@ -12,7 +14,7 @@ use "core/metaprogramming.nom"
return LuaCode(tree.source, "print(tostring(", \(%message as lua expr), "));");
end"
-(ask %prompt) compiles 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 3bad78a..36d1d90 100644
--- a/core/math.nom
+++ b/core/math.nom
@@ -8,6 +8,8 @@ use "core/operators.nom"
use "core/control_flow.nom"
use "core/collections.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
# Literals:
test:
assume (all of [inf, NaN, pi, tau, golden ratio, e]) or barf "\
@@ -204,7 +206,7 @@ test:
return %best
# Random functions
-externally (seed random with %) means (..)
+externally (seed random with %) means:
lua> "\
..math.randomseed(\%);
for i=1,20 do math.random(); end"
diff --git a/core/metaprogramming.nom b/core/metaprogramming.nom
index b104987..be7450a 100644
--- a/core/metaprogramming.nom
+++ b/core/metaprogramming.nom
@@ -3,7 +3,9 @@
This File contains actions for making actions and compile-time actions and some helper
functions to make that easier.
-lua> "NOMSU_CORE_VERSION = 9"
+lua> "\
+ ..NOMSU_CORE_VERSION = 10
+ NOMSU_LIB_VERSION = 7"
lua> "\
..do
local mangle_index = 0
@@ -15,17 +17,18 @@ lua> "\
end
end
end
- COMPILE_ACTIONS["define mangler"] = function(nomsu, tree)
+ compile.action["define mangler"] = function(compile, tree)
return LuaCode(tree.source, "local mangle = mangler()")
end"
lua> "\
- ..COMPILE_ACTIONS["1 ->"] = function(nomsu, tree, \%args, \%body)
- local lua = LuaCode.Value(tree.source, "(function(")
+ ..compile.action["1 ->"] = function(compile, tree, \%args, \%body)
+ local lua = LuaCode(tree.source, "(function(")
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)
+ local lua_args = table.map(\%args, function(a) return SyntaxTree:is_instance(a) and compile(a):text() or a end)
lua:concat_append(lua_args, ", ")
- local body_lua = SyntaxTree:is_instance(\%body) and nomsu:compile(\%body):as_statements("return ") or \%body
+ local body_lua = SyntaxTree:is_instance(\%body) and compile(\%body) or \%body
+ if SyntaxTree:is_instance(\%body) and \%body.type ~= "Block" then body_lua:prepend("return ") end
body_lua:remove_free_vars(lua_args)
body_lua:declare_locals()
lua:append(")\\n ", body_lua, "\\nend)")
@@ -33,10 +36,10 @@ lua> "\
end"
lua> "\
- ..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")
+ ..compile.action["what 1 compiles to"] = function(compile, tree, \%action)
+ local lua = LuaCode(tree.source, "compile.action[", \%action.stub:as_lua(), "](")
+ local lua_args = table.map(\%action:get_args(), function(a) return compile(a) end)
+ table.insert(lua_args, 1, "compile")
table.insert(lua_args, 2, "tree")
lua:concat_append(lua_args, ", ")
lua:append(")")
@@ -46,10 +49,10 @@ lua> "\
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test:
- (five) compiles to (Lua value "5")
+ (five) compiles to "5"
test:
assume ((five) == 5) or barf "Compile to expression failed."
- (loc x) compiles to (Lua "local x = 99;")
+ (loc x) compiles to "local x = 99;"
test:
lua> "do"
loc x
@@ -63,9 +66,12 @@ test:
asdf
assume (%tmp is (nil)) or barf "compile to is leaking variables"
lua> "\
- ..COMPILE_ACTIONS["1 compiles to"] = function(nomsu, tree, \%action, \%body)
- local \%args = List{\(\%nomsu), \(\%tree), unpack(\%action:get_args())}
- local lua = LuaCode(tree.source, "COMPILE_ACTIONS[", \%action.stub:as_lua(),
+ ..compile.action["1 compiles to"] = function(compile, tree, \%action, \%body)
+ local \%args = List{\(\%compile), \(\%tree), unpack(\%action:get_args())}
+ if \%body.type == "Text" then
+ \%body = SyntaxTree{source=\%body.source, type="Action", "Lua", \%body}
+ end
+ local lua = LuaCode(tree.source, "compile.action[", \%action.stub:as_lua(),
"] = ", \(what (%args -> %body) compiles to))
return lua
end"
@@ -75,16 +81,16 @@ lua> "\
(%actions all compile to %body) compiles to:
lua> "\
..if \%actions.type ~= "List" then
- nomsu:compile_error(\%actions, "This should be a list of actions.")
+ compile_error(\%actions, "This should be a list of actions.")
end
local lua = LuaCode(tree.source, \(what (%actions.1 compiles to %body) compiles to))
- local \%args = List{\(\%nomsu), \(\%tree), unpack(\%actions[1]:get_args())}
+ local \%args = List{\(\%compile), \(\%tree), unpack(\%actions[1]:get_args())}
for i=2,#\%actions do
local alias = \%actions[i]
- local \%alias_args = List{\(\%nomsu), \(\%tree), unpack(alias:get_args())}
- lua:append("\\nCOMPILE_ACTIONS[", alias.stub:as_lua(), "] = ")
+ local \%alias_args = List{\(\%compile), \(\%tree), unpack(alias:get_args())}
+ lua:append("\\ncompile.action[", alias.stub:as_lua(), "] = ")
if \%alias_args == \%args then
- lua:append("COMPILE_ACTIONS[", \%actions[1].stub:as_lua(), "]")
+ lua:append("compile.action[", \%actions[1].stub:as_lua(), "]")
else
lua:append(\(what (%alias_args -> \(what %actions.1 compiles to)) compiles to))
end
@@ -95,17 +101,17 @@ lua> "\
(call %fn with %args) compiles to:
lua> "\
- ..local lua = LuaCode.Value(tree.source, nomsu:compile(\%fn), "(")
+ ..local lua = LuaCode(tree.source, compile(\%fn), "(")
if \%args.type == 'List' then
- lua:concat_append(table.map(\%args, function(a) return nomsu:compile(a) end), ", ")
+ lua:concat_append(table.map(\%args, function(a) return compile(a) end), ", ")
else
- lua:append('unpack(', nomsu:compile(\%args), ')')
+ lua:append('unpack(', compile(\%args), ')')
end
lua:append(")")
return lua"
test:
- (foo %x) means (return "outer")
+ (foo %x) means "outer"
with local [(foo %)'s meaning]:
(foo %x) means:
%y = (%x + 1)
@@ -162,7 +168,7 @@ test:
test:
assume (((say %)'s meaning) == (=lua "say"))
-(%action's meaning) compiles to (Lua value (%action.stub as lua id))
+(%action's meaning) compiles to (Lua (%action.stub as lua id))
test:
(swap %x and %y) parses as (..)
@@ -183,10 +189,10 @@ test:
lua> "\
..local replacements = {}
if \%actions.type ~= "List" then
- nomsu:compile_error(\%actions, "This should be a list.")
+ compile_error(\%actions, "This should be a list.")
end
for i,arg in ipairs(\%actions[1]:get_args()) do
- replacements[arg[1]] = nomsu:compile(arg):text()
+ replacements[arg[1]] = compile(arg):text()
end
local function make_tree(t)
if SyntaxTree:is_instance(t) and t.type == "Var" then
@@ -227,27 +233,17 @@ test:
[%action parses as %body] all parse as ([%action] all parse as %body)
-# TODO: add check for .is_value
-(%tree as lua expr) compiles to (..)
- Lua value "nomsu:compile(\(=lua "nomsu:compile(\%tree, nil, true)"), nil, true)"
+(%tree as lua expr) compiles to "\
+ ..compile(\(=lua "compile(\%tree, nil, true)"), nil, true)"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-(%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()"
-
-(%tree as lua return) compiles to (..)
- Lua value "nomsu:compile(\(%tree as lua expr)):as_statements('return ')"
-
-test:
- assume ("\(\(foo \%x) as nomsu)" == "foo %x") or barf "\
- ..action source code failed."
-(%tree as nomsu) compiles to (..)
- Lua value "nomsu:tree_to_nomsu(\(%tree as lua expr))"
+(%tree as lua) compiles to "compile(\(%tree as lua expr))"
+(%tree as lua statements) compiles to "\
+ ..compile(\(%tree as lua expr)):as_statements()"
-(%tree as inline nomsu) compiles to (..)
- Lua value "nomsu:tree_to_inline_nomsu(\(%tree as lua expr), true)"
+(%tree as lua return) compiles to "\
+ ..compile(\(%tree as lua expr)):as_statements('return ')"
externally [%var as lua identifier, %var as lua id] all mean:
lua> "\
@@ -256,7 +252,7 @@ externally [%var as lua identifier, %var as lua id] all mean:
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.")
+ compile_error(\%var, "This is not a valid Lua identifier.")
end
return lua
else error("Unknown type: "..tostring(\%var))
@@ -264,15 +260,14 @@ externally [%var as lua identifier, %var as lua id] all mean:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-(% 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))"
+(% is syntax tree) compiles to "SyntaxTree:is_instance(\(% as lua expr))"
+externally (% is %kind syntax tree) means (..)
+ =lua "SyntaxTree:is_instance(\%) and \%.type == \%kind"
-(%tree with %t -> %replacement) compiles to (..)
- Lua value "\
- ..\(%tree as lua expr):map(function(\(%t as lua expr))
- \(%replacement as lua return)
- end)"
+(%tree with %t -> %replacement) compiles to "\
+ ..\(%tree as lua expr):map(function(\(%t as lua expr))
+ \(%replacement as lua return)
+ end)"
externally (%tree with vars %replacements) means (..)
=lua "\
@@ -282,22 +277,20 @@ externally (%tree with vars %replacements) means (..)
end
end)"
-(tree %tree with vars %replacements) compiles to (..)
- Lua value "\
- ..\(=lua "(\%tree):as_lua()"):map(function(t)
- if t.type == "Var" then
- return \(%replacements as lua expr)[t[1]]
- end
- end)"
+(tree %tree with vars %replacements) compiles to "\
+ ..\(=lua "(\%tree):as_lua()"):map(function(t)
+ if t.type == "Var" then
+ return \(%replacements as lua expr)[t[1]]
+ end
+ end)"
-(%tree has subtree %match_tree) compiles to (..)
- Lua value "\
- ..(function()
- local match_tree = \(%match_tree as lua expr)
- for subtree in coroutine.wrap(function() \(%tree as lua expr):map(coroutine.yield) end) do
- if subtree == match_tree then return true end
- end
- end)()"
+(%tree has subtree %match_tree) compiles to "\
+ ..(function()
+ local match_tree = \(%match_tree as lua expr)
+ for subtree in coroutine.wrap(function() \(%tree as lua expr):map(coroutine.yield) end) do
+ if subtree == match_tree then return true end
+ end
+ end)()"
externally (match %tree with %patt) means:
lua> "\
@@ -339,7 +332,7 @@ test:
..one
"two""
..== "\"one\\n\\\"two\\\"\""
-(quote %s) compiles to (Lua value "tostring(\(%s as lua expr)):as_lua()")
+(quote %s) compiles to (Lua "tostring(\(%s as lua expr)):as_lua()")
test:
assume (lua type of {}) == "table"
@@ -368,63 +361,43 @@ externally (type of %) means:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test:
- assume ((parse "foo %") == \(foo \%))
- %a = (parse "\\1")
- %b = \(\(1))
- assume ((parse "\\1") == \(\(1)))
-(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
- ..))"
-
-test:
assume ((run "return (2 + 99)") == 101)
external %passed = (no)
run "external %passed = (yes)"
assume %passed
-(run %nomsu_code) compiles to (..)
- Lua value "\
- ..nomsu:run(NomsuCode(\(=lua "tostring(\(%nomsu_code.source)):as_lua()"), \(..)
- %nomsu_code as lua expr
- ..))"
+ assume (run \(return \(\(5) + \(5)))) == 10
+(run %nomsu_code) compiles to "\
+ ..run_1_in(\(%nomsu_code as lua expr), _ENV)"
-test:
- assume ((\(\(5) + \(5)) as value) == 10) or barf "%tree as value failed."
-[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))"
+[compile %block, compiled %block, %block compiled] all compile to "\
+ ..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.
-(return) compiles to (Lua "do return; end")
-(return %return_value) compiles to (..)
- Lua "do return \(%return_value as lua expr) end"
+(return) compiles to "do return; end"
+(return %return_value) compiles to "do return \(%return_value as lua expr) end"
# Literals
-(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")
+(yes) compiles to "true"
+(no) compiles to "false"
+[nothing, nil, null] all compile to "nil"
+(Nomsu syntax version) compiles to "NOMSU_SYNTAX_VERSION"
+(Nomsu compiler version) compiles to "NOMSU_COMPILER_VERSION"
+(core version) compiles to "NOMSU_CORE_VERSION"
+(lib version) compiles to "NOMSU_LIB_VERSION"
+(command line args) compiles to "command_line_args"
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-(with local compile actions %body) compiles to (..)
- Lua "\
- ..do
- local nomsu = nomsu:fork()
- local COMPILE_ACTIONS = nomsu.environment.COMPILE_ACTIONS
- \(%body as lua statements)
- end"
+(with local compile actions %body) compiles to "\
+ ..do
+ --local compile = _1_forked(compile)
+ local old_action = compile.action
+ compile.action = _1_forked(old_action)
+ \(%body as lua statements)
+ compile.action = old_action
+ end"
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 6d574b7..1d8e63f 100644
--- a/core/operators.nom
+++ b/core/operators.nom
@@ -5,6 +5,8 @@
use "core/metaprogramming.nom"
use "core/errors.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
test:
assume (all [1 < 2, 2 > 1, 1 <= 2, 2 >= 1, 1 == 1, 1 != 2])
@@ -27,14 +29,12 @@ test:
# Variable assignment operator
(%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)"
- assume %value_lua.is_value or barf "Invalid value for assignment: \%value"
lua> "\
- ..local lua = LuaCode(tree.source, \%var_lua, ' = ', \%value_lua, ';')
+ ..local \%var_lua = \(%var as lua expr)
+ local \%value_lua = \(%value as lua expr)
+ local lua = LuaCode(tree.source, \%var_lua, ' = ', \%value_lua, ';')
if \%var.type == 'Var' then
- lua:add_free_vars({nomsu:compile(\%var):text()})
+ lua:add_free_vars({compile(\%var):text()})
end
return lua"
@@ -58,13 +58,7 @@ test:
end
end)
local target_lua = \(%target as lua)
- if not target_lua.is_value then error("Invalid target for assignment: "..\(..)
- %target as text
- ..) end
local value_lua = \(%value as lua)
- if not value_lua.is_value then error("Invalid value for assignment: "..\(..)
- %value as text
- ..) end
if \%target.type == "Var" then
lhs:add_free_vars({target_lua:text()})
end
@@ -87,16 +81,11 @@ test:
set global x local y
assume ((%foozle == "inner") and (%y == "outer")) or barf "external failed."
-(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)
- assume %value_lua.is_value or barf "Invalid value for assignment: \%value"
- return (Lua "\%var_lua = \%value_lua;")
+(external %var = %value) compiles to "\(%var as lua) = \(%value as lua)"
test:
set {%foozle:"outer", %y:"outer"}
- externally (set global x local y) means (..)
+ externally (set global x local y) means:
with external [%foozle]:
%foozle = "inner"
%y = "inner"
@@ -107,7 +96,7 @@ test:
(with external %externs %body) compiles to:
%body_lua = (%body as lua statements)
lua> "\
- ..\%body_lua:remove_free_vars(table.map(\%externs, function(v) return nomsu:compile(v):text() end))"
+ ..\%body_lua:remove_free_vars(table.map(\%externs, function(v) return compile(v):text() end))"
return %body_lua
test:
@@ -131,9 +120,6 @@ test:
end
local target_lua = \(%target as lua)
local value_lua = \(%value as lua)
- if not value_lua.is_value then
- error("Invalid value for assignment: "..tostring(\%value))
- end
if i > 1 then
lhs:append(", ")
rhs:append(", ")
diff --git a/core/scopes.nom b/core/scopes.nom
index 2d86ca4..47960d8 100644
--- a/core/scopes.nom
+++ b/core/scopes.nom
@@ -7,6 +7,8 @@ use "core/operators.nom"
use "core/collections.nom"
use "core/control_flow.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
test:
%x = "outer"
with local %x:
diff --git a/core/text.nom b/core/text.nom
index 40eb895..38fa08b 100644
--- a/core/text.nom
+++ b/core/text.nom
@@ -5,6 +5,8 @@
use "core/metaprogramming.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
test:
assume "\[1, 2, 3]" == "[1, 2, 3]"
assume "foo = \(1 + 2)!" == "foo = 3!"
@@ -52,8 +54,8 @@ lua> "\
};
for name, e in pairs(escapes) do
local lua = "'"..e.."'"
- COMPILE_ACTIONS[name] = function(nomsu, tree)
- return LuaCode.Value(tree.source, lua)
+ compile.action[name] = function(compile, tree)
+ return LuaCode(tree.source, lua)
end
end
end"
diff --git a/error_handling.lua b/error_handling.lua
index 88ce1a9..33f9764 100644
--- a/error_handling.lua
+++ b/error_handling.lua
@@ -1,5 +1,13 @@
local files = require("files")
local debug_getinfo = debug.getinfo
+local RED = "\027[31m"
+local BRIGHT_RED = "\027[31;1m"
+local RESET = "\027[0m"
+local YELLOW = "\027[33m"
+local CYAN = "\027[36m"
+local GREEN = "\027[32m"
+local BLUE = "\027[34m"
+local DIM = "\027[37;2m"
local ok, to_lua = pcall(function()
return require('moonscript.base').to_lua
end)
@@ -66,7 +74,7 @@ debug.getinfo = function(thread, f, what)
end
local print_error
print_error = function(error_message, start_fn, stop_fn)
- io.stderr:write(tostring(colored.red("ERROR:")) .. " " .. tostring(colored.bright(colored.red((error_message or "")))) .. "\n")
+ io.stderr:write(tostring(RED) .. "ERROR: " .. tostring(BRIGHT_RED) .. tostring(error_message or "") .. tostring(RESET) .. "\n")
io.stderr:write("stack traceback:\n")
local level = 1
local found_start = false
@@ -126,10 +134,10 @@ print_error = function(error_message, start_fn, stop_fn)
do
local err_line = files.get_line(file, calling_fn.currentline)
if err_line then
- local offending_statement = colored.bright(colored.red(err_line:match("^[ ]*(.*)")))
- line = colored.yellow(tostring(filename) .. ":" .. tostring(calling_fn.currentline) .. " in " .. tostring(name) .. "\n " .. tostring(offending_statement))
+ local offending_statement = tostring(BRIGHT_RED) .. tostring(err_line:match("^[ ]*(.*)")) .. tostring(RESET)
+ line = tostring(YELLOW) .. tostring(filename) .. ":" .. tostring(calling_fn.currentline) .. " in " .. tostring(name) .. "\n " .. tostring(offending_statement) .. tostring(RESET)
else
- line = colored.yellow(tostring(filename) .. ":" .. tostring(calling_fn.currentline) .. " in " .. tostring(name))
+ line = tostring(YELLOW) .. tostring(filename) .. ":" .. tostring(calling_fn.currentline) .. " in " .. tostring(name) .. tostring(RESET)
end
end
else
@@ -184,20 +192,20 @@ print_error = function(error_message, start_fn, stop_fn)
for _ in file:sub(1, char):gmatch("\n") do
line_num = line_num + 1
end
- line = colored.cyan(tostring(calling_fn.short_src) .. ":" .. tostring(line_num) .. " in " .. tostring(name or '?'))
+ line = tostring(CYAN) .. tostring(calling_fn.short_src) .. ":" .. tostring(line_num) .. " in " .. tostring(name or '?') .. tostring(RESET)
else
line_num = calling_fn.currentline
if calling_fn.short_src == '[C]' then
- line = colored.green(tostring(calling_fn.short_src) .. " in " .. tostring(name or '?'))
+ line = tostring(GREEN) .. tostring(calling_fn.short_src) .. " in " .. tostring(name or '?') .. tostring(RESET)
else
- line = colored.blue(tostring(calling_fn.short_src) .. ":" .. tostring(calling_fn.currentline) .. " in " .. tostring(name or '?'))
+ line = tostring(BLUE) .. tostring(calling_fn.short_src) .. ":" .. tostring(calling_fn.currentline) .. " in " .. tostring(name or '?') .. tostring(RESET)
end
end
if file then
do
local err_line = files.get_line(file, line_num)
if err_line then
- local offending_statement = colored.bright(colored.red(err_line:match("^[ ]*(.*)$")))
+ local offending_statement = tostring(BRIGHT_RED) .. tostring(err_line:match("^[ ]*(.*)$")) .. tostring(RESET)
line = line .. ("\n " .. offending_statement)
end
end
@@ -206,7 +214,7 @@ print_error = function(error_message, start_fn, stop_fn)
end
io.stderr:write(line, "\n")
if calling_fn.istailcall then
- io.stderr:write(" " .. tostring(colored.dim(colored.white(" (...tail calls...)"))) .. "\n")
+ io.stderr:write(" " .. tostring(DIM) .. "(...tail calls...)" .. tostring(RESET) .. "\n")
end
if calling_fn.func == stop_fn then
break
diff --git a/error_handling.moon b/error_handling.moon
index 5a5dd02..f43d6ba 100644
--- a/error_handling.moon
+++ b/error_handling.moon
@@ -3,6 +3,15 @@ files = require "files"
debug_getinfo = debug.getinfo
export SOURCE_MAP
+RED = "\027[31m"
+BRIGHT_RED = "\027[31;1m"
+RESET = "\027[0m"
+YELLOW = "\027[33m"
+CYAN = "\027[36m"
+GREEN = "\027[32m"
+BLUE = "\027[34m"
+DIM = "\027[37;2m"
+
ok, to_lua = pcall -> require('moonscript.base').to_lua
if not ok then to_lua = -> nil
MOON_SOURCE_MAP = setmetatable {},
@@ -40,7 +49,7 @@ debug.getinfo = (thread,f,what)->
return info
print_error = (error_message, start_fn, stop_fn)->
- io.stderr\write("#{colored.red "ERROR:"} #{colored.bright colored.red (error_message or "")}\n")
+ io.stderr\write("#{RED}ERROR: #{BRIGHT_RED}#{error_message or ""}#{RESET}\n")
io.stderr\write("stack traceback:\n")
level = 1
@@ -76,10 +85,10 @@ print_error = (error_message, start_fn, stop_fn)->
else "main chunk"
if err_line = files.get_line(file, calling_fn.currentline)
- offending_statement = colored.bright(colored.red(err_line\match("^[ ]*(.*)")))
- line = colored.yellow("#{filename}:#{calling_fn.currentline} in #{name}\n #{offending_statement}")
+ offending_statement = "#{BRIGHT_RED}#{err_line\match("^[ ]*(.*)")}#{RESET}"
+ line = "#{YELLOW}#{filename}:#{calling_fn.currentline} in #{name}\n #{offending_statement}#{RESET}"
else
- line = colored.yellow("#{filename}:#{calling_fn.currentline} in #{name}")
+ line = "#{YELLOW}#{filename}:#{calling_fn.currentline} in #{name}#{RESET}"
else
ok, file = pcall ->files.read(calling_fn.short_src)
if not ok then file = nil
@@ -111,21 +120,21 @@ print_error = (error_message, start_fn, stop_fn)->
char = MOON_SOURCE_MAP[file][calling_fn.currentline]
line_num = 1
for _ in file\sub(1,char)\gmatch("\n") do line_num += 1
- line = colored.cyan("#{calling_fn.short_src}:#{line_num} in #{name or '?'}")
+ line = "#{CYAN}#{calling_fn.short_src}:#{line_num} in #{name or '?'}#{RESET}"
else
line_num = calling_fn.currentline
if calling_fn.short_src == '[C]'
- line = colored.green("#{calling_fn.short_src} in #{name or '?'}")
+ line = "#{GREEN}#{calling_fn.short_src} in #{name or '?'}#{RESET}"
else
- line = colored.blue("#{calling_fn.short_src}:#{calling_fn.currentline} in #{name or '?'}")
+ line = "#{BLUE}#{calling_fn.short_src}:#{calling_fn.currentline} in #{name or '?'}#{RESET}"
if file
if err_line = files.get_line(file, line_num)
- offending_statement = colored.bright(colored.red(err_line\match("^[ ]*(.*)$")))
+ offending_statement = "#{BRIGHT_RED}#{err_line\match("^[ ]*(.*)$")}#{RESET}"
line ..= "\n "..offending_statement
io.stderr\write(line,"\n")
if calling_fn.istailcall
- io.stderr\write(" #{colored.dim colored.white " (...tail calls...)"}\n")
+ io.stderr\write(" #{DIM}(...tail calls...)#{RESET}\n")
if calling_fn.func == stop_fn then break
io.stderr\flush!
diff --git a/examples/how_do_i.nom b/examples/how_do_i.nom
index 91d1ae3..bb9e855 100644
--- a/examples/how_do_i.nom
+++ b/examples/how_do_i.nom
@@ -12,6 +12,8 @@ use "lib/os.nom"
# How do I import all the files in a directory?
use "lib"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
# How do I print stuff?
say "Hello world!"
diff --git a/files.lua b/files.lua
index 13679e3..d08d7df 100644
--- a/files.lua
+++ b/files.lua
@@ -25,9 +25,14 @@ local _FILE_CACHE = setmetatable({ }, {
__index = _SPOOFED_FILES
})
local _BROWSE_CACHE = { }
+local _anon_number = 0
Files.spoof = function(filename, contents)
+ if not contents then
+ filename, contents = "<anonymous file #" .. tostring(_anon_number) .. ">", filename
+ _anon_number = _anon_number + 1
+ end
_SPOOFED_FILES[filename] = contents
- return contents
+ return filename
end
Files.read = function(filename)
do
@@ -37,7 +42,9 @@ Files.read = function(filename)
end
end
if filename == 'stdin' then
- return Files.spoof('stdin', io.read('*a'))
+ local contents = io.read('*a')
+ Files.spoof('stdin', contents)
+ return contents
end
local file = io.open(filename)
if not (file) then
diff --git a/files.moon b/files.moon
index 738c5f8..ca50290 100644
--- a/files.moon
+++ b/files.moon
@@ -16,16 +16,22 @@ _FILE_CACHE = setmetatable {}, __index:_SPOOFED_FILES
_BROWSE_CACHE = {}
-- Create a fake file and put it in the cache
+_anon_number = 0
Files.spoof = (filename, contents)->
+ if not contents
+ filename, contents = "<anonymous file ##{_anon_number}>", filename
+ _anon_number += 1
_SPOOFED_FILES[filename] = contents
- return contents
+ return filename
-- Read a file's contents (searching first locally, then in the nomsupath)
Files.read = (filename)->
if file_contents = _FILE_CACHE[filename]
return file_contents
if filename == 'stdin'
- return Files.spoof('stdin', io.read('*a'))
+ contents = io.read('*a')
+ Files.spoof('stdin', contents)
+ return contents
file = io.open(filename)
return nil unless file
contents = file\read("*a")
diff --git a/importer.lua b/importer.lua
new file mode 100644
index 0000000..9b2c936
--- /dev/null
+++ b/importer.lua
@@ -0,0 +1,62 @@
+local import_to_1_from
+import_to_1_from = function(host, to_import)
+ do
+ local host_mt = getmetatable(host)
+ if host_mt then
+ if host_mt.__import then
+ host_mt.__import(host, to_import)
+ return
+ end
+ end
+ end
+ for k, v in pairs(to_import) do
+ host[k] = v
+ end
+end
+local _imports = setmetatable({ }, {
+ __mode = "k"
+})
+local Importer = setmetatable({
+ __index = function(self, key)
+ return _imports[self][key]
+ end,
+ __import = function(self, to_import)
+ local imports = assert(_imports[self])
+ for k, v in pairs(to_import) do
+ local _continue_0 = false
+ repeat
+ imports[k] = v
+ if v == to_import then
+ _continue_0 = true
+ break
+ end
+ local conflict = self[k]
+ if type(conflict) == 'table' then
+ import_to_1_from(conflict, v)
+ end
+ _continue_0 = true
+ until true
+ if not _continue_0 then
+ break
+ end
+ end
+ end
+}, {
+ __call = function(self, t)
+ _imports[t] = { }
+ setmetatable(t, self)
+ return t
+ end
+})
+local _1_forked
+_1_forked = function(self)
+ local f = Importer({ })
+ _imports[f] = assert(_imports[self])
+ import_to_1_from(f, self)
+ return f
+end
+return {
+ Importer = Importer,
+ import_to_1_from = import_to_1_from,
+ _1_forked = _1_forked
+}
diff --git a/importer.moon b/importer.moon
new file mode 100644
index 0000000..48a3800
--- /dev/null
+++ b/importer.moon
@@ -0,0 +1,33 @@
+-- This file defines Importer, which is a type of table that can import from other tables
+
+import_to_1_from = (host, to_import)->
+ if host_mt = getmetatable(host)
+ if host_mt.__import
+ host_mt.__import(host, to_import)
+ return
+ for k,v in pairs(to_import)
+ host[k] = v
+_imports = setmetatable({}, {__mode:"k"})
+Importer = setmetatable({
+ __index: (key)=> _imports[@][key]
+ __import: (to_import)=>
+ imports = assert _imports[@]
+ for k,v in pairs(to_import)
+ imports[k] = v
+ continue if v == to_import
+ conflict = @[k]
+ import_to_1_from(conflict, v) if type(conflict) == 'table'
+}, {
+ __call: (t)=>
+ _imports[t] = {}
+ setmetatable(t, @)
+ return t
+})
+
+_1_forked = =>
+ f = Importer{}
+ _imports[f] = assert _imports[@]
+ import_to_1_from(f, @)
+ return f
+
+return {:Importer, :import_to_1_from, :_1_forked}
diff --git a/lib/consolecolor.nom b/lib/consolecolor.nom
index fe7da4c..4b231a9 100644
--- a/lib/consolecolor.nom
+++ b/lib/consolecolor.nom
@@ -3,7 +3,7 @@
This file defines actions for ANSI console color escape codes.
test:
- bright "\(green)Color test passed."
+ % = (bright "\(green)Color test passed.")
%colors = {..}
normal:0, "reset color":0, bright:1, bold:1, dim:2, italic:3, underscore:4
"slow blink":5, "fast blink":6, reverse:7, inverse:7, inverted:7, hidden:8
@@ -16,7 +16,7 @@ for %name = %colornum in %colors:
%colornum = "\%colornum"
#(=lua "COMPILE_ACTIONS").%name = (..)
[%nomsu, %tree] -> (Lua value "'\\027[\(%colornum)m'")
- (=lua "COMPILE_ACTIONS")."\%name" = (..)
+ %compile.action.%name = (..)
[%nomsu, %tree, %text] ->:
if %text:
return (Lua value "('\\027[\(%colornum)m'..\(%text as lua expr)..'\\027[0m')")
diff --git a/lib/file_hash.nom b/lib/file_hash.nom
index 6c815f5..ba2f95c 100644
--- a/lib/file_hash.nom
+++ b/lib/file_hash.nom
@@ -5,6 +5,8 @@
use "lib/os.nom"
use "lib/base64.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
lua> "local \%use_sha1, \%hashlib = pcall(require, 'openssl.digest')"
test:
diff --git a/lib/object.nom b/lib/object.nom
index 6f6fc20..101499d 100644
--- a/lib/object.nom
+++ b/lib/object.nom
@@ -95,7 +95,7 @@ test:
return inst
end,
})
- nomsu.environment[class.name:as_lua_id()] = class
+ _ENV[class.name:as_lua_id()] = class
class.__index = class
class.class = class
class.__tostring = function(inst)
diff --git a/lib/os.nom b/lib/os.nom
index 00fe81f..2da395c 100644
--- a/lib/os.nom
+++ b/lib/os.nom
@@ -69,3 +69,6 @@ externally (source lines of %tree) means:
(line % in %file) for % in (line number of %source.start in %file) to (..)
line number of %source.stop in %file
..::joined with "\n"
+
+externally (spoof file %text) means (%Files.spoof %text)
+externally (spoof file %filename = %text) means (%Files.spoof %filename %text)
diff --git a/lib/things.nom b/lib/things.nom
index 2f6c95a..f11ca23 100644
--- a/lib/things.nom
+++ b/lib/things.nom
@@ -72,9 +72,9 @@ test:
lua:append("class.", fn_name)
else
lua:append("function(")
- lua:concat_append(table.map(\%alias_args, function(a) return nomsu:compile(a) end), ", ")
+ lua:concat_append(table.map(\%alias_args, function(a) return compile(a) end), ", ")
lua:append(")\\n return class.", fn_name, "(")
- lua:concat_append(table.map(\%args, function(a) return nomsu:compile(a) end), ", ")
+ lua:concat_append(table.map(\%args, function(a) return compile(a) end), ", ")
lua:append(")\\nend")
end
end
@@ -113,11 +113,11 @@ test:
end,
})
class.__members = \(%members as lua expr)
- nomsu.environment[("a "..class.name):as_lua_id()] = class
- nomsu.environment[("an "..class.name):as_lua_id()] = class
- nomsu.environment[("a "..class.name.." with"):as_lua_id()] = class
- nomsu.environment[("an "..class.name.." with"):as_lua_id()] = class
- nomsu.environment[class.name:as_lua_id()] = function() return class end
+ _ENV[("a "..class.name):as_lua_id()] = class
+ _ENV[("an "..class.name):as_lua_id()] = class
+ _ENV[("a "..class.name.." with"):as_lua_id()] = class
+ _ENV[("an "..class.name.." with"):as_lua_id()] = class
+ _ENV[class.name:as_lua_id()] = function() return class end
class.__index = class
class.class = class
local dict_tostring = getmetatable(Dict{}).__tostring
diff --git a/nomnom/ast.nom b/nomnom/ast.nom
index ef41b26..935b0c5 100644
--- a/nomnom/ast.nom
+++ b/nomnom/ast.nom
@@ -1,6 +1,8 @@
#!/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"
diff --git a/nomnom/code_obj.nom b/nomnom/code_obj.nom
index c8d2784..2c78ace 100644
--- a/nomnom/code_obj.nom
+++ b/nomnom/code_obj.nom
@@ -4,6 +4,8 @@
indentation levels.
use "lib/things.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
a (Code Buffer) is a thing:
that can (set up) by:
assume %its.source
diff --git a/nomnom/compile.nom b/nomnom/compile.nom
index 8241ef2..ad38b69 100644
--- a/nomnom/compile.nom
+++ b/nomnom/compile.nom
@@ -4,6 +4,8 @@ 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)
diff --git a/nomnom/decompile.nom b/nomnom/decompile.nom
index 11ec233..c958280 100644
--- a/nomnom/decompile.nom
+++ b/nomnom/decompile.nom
@@ -3,6 +3,8 @@
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")
@@ -175,10 +177,10 @@ externally (%tree decompiled) means:
"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)
+ if (%1.stub == "use"):
+ return (%2.stub == "use")
+ if (%1.stub == "test"): return (yes)
+ if (%2.stub == "test"): return (no)
return (not ((recurse on %1)::is multi-line))
@@ -199,14 +201,12 @@ externally (%tree decompiled) means:
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:
@@ -290,8 +290,9 @@ externally (%tree decompiled) means:
"Var":
if ((%tree.(%i+1) is text) and (not (%tree.(%i+1)::matches "^[ \n\t,.:#(){}[%]]"))):
%interp_nomsu::parenthesize
-
"List" "Dict":
+ do nothing
+ else:
%interp_nomsu::parenthesize
%nomsu::add %interp_nomsu
diff --git a/nomnom/files.nom b/nomnom/files.nom
index 352ddfa..61119c0 100644
--- a/nomnom/files.nom
+++ b/nomnom/files.nom
@@ -2,6 +2,8 @@
# 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 = {}
diff --git a/nomnom/source.nom b/nomnom/source.nom
index c36216f..bff8d8d 100644
--- a/nomnom/source.nom
+++ b/nomnom/source.nom
@@ -1,6 +1,8 @@
#!/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+)%]$")
diff --git a/nomsu.4.peg b/nomsu.4.peg
index a3677c6..e0a5781 100644
--- a/nomsu.4.peg
+++ b/nomsu.4.peg
@@ -114,14 +114,15 @@ index_chain (IndexChain):
-- Actions need either at least 1 word, or at least 2 tokens
inline_action (Action):
!section_division
- ({:target: inline_arg :} ws* "::" ws*)?
+ ({:target: (inline_expression / "(" inline_block ")") :} ws* "::" ws*)?
( (inline_arg (ws* (inline_arg / word))+)
/ (word (ws* (inline_arg / word))*))
(ws* inline_block)?
inline_arg: inline_expression / inline_block
action (Action):
!section_division
- ({:target: arg :} ((ws* "\")? eol nl_nodent "..")? ws* "::" ((ws* "\")? eol nl_nodent "..")? ws*)?
+ ({:target: (expression / "(" inline_block ")" / indented_block) :}
+ ((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
@@ -190,8 +191,8 @@ indented_list (List):
(","? unexpected_code)?
list_line:
(inline_list_item ws* "," ws*)+ eol
- / (inline_list_item ws* "," ws*)* (action / expression) eol
-inline_list_item: inline_action / inline_expression
+ / (inline_list_item ws* "," ws*)* (action / expression / inline_block / indented_block) eol
+inline_list_item: inline_action / inline_expression / inline_block
inline_dict (Dict):
!('{..}')
@@ -206,10 +207,14 @@ indented_dict (Dict):
dict_line:
(inline_dict_entry ws* "," ws*)+ eol
/ (inline_dict_entry ws* "," ws*)* dict_entry eol
-dict_entry(DictEntry):
+_dict_entry(DictEntry):
dict_key (ws* ":" ws* (action / expression))?
-inline_dict_entry(DictEntry):
+dict_entry:
+ _dict_entry / inline_block / indented_block
+_inline_dict_entry(DictEntry):
dict_key (ws* ":" ws* (inline_action / inline_expression)?)?
+inline_dict_entry:
+ _inline_dict_entry / inline_block
dict_key:
text_word / inline_expression
diff --git a/nomsu.lua b/nomsu.lua
index e7f8fdd..82c0d02 100644
--- a/nomsu.lua
+++ b/nomsu.lua
@@ -85,16 +85,21 @@ if not ok then
end
local Files = require("files")
local Errhand = require("error_handling")
-local NomsuCompiler = require("nomsu_compiler")
local NomsuCode, LuaCode, Source
do
local _obj_0 = require("code_obj")
NomsuCode, LuaCode, Source = _obj_0.NomsuCode, _obj_0.LuaCode, _obj_0.Source
end
+local List, Dict, Text
+do
+ local _obj_0 = require('containers')
+ List, Dict, Text = _obj_0.List, _obj_0.Dict, _obj_0.Text
+end
+local nomsu_environment = require('nomsu_environment')
if not arg or debug.getinfo(2).func == require then
- return NomsuCompiler
+ return nomsu_environment
end
-local file_queue = { }
+local file_queue = List({ })
local sep = "\3"
local parser = re.compile([[ args <- {| (flag %sep)* (({~ file ~} -> add_file) {:primary_file: %true :} %sep)?
{:nomsu_args: {| ({(!%sep .)*} %sep)* |} :} %sep? |} !.
@@ -116,12 +121,12 @@ local parser = re.compile([[ args <- {| (flag %sep)* (({~ file ~} -> add_file
number = lpeg.R("09") ^ 1 / tonumber,
sep = lpeg.P(sep),
add_file = function(f)
- return table.insert(file_queue, f)
+ return file_queue:add(f)
end,
add_exec_string = function(pos, s)
local name = "command line arg @" .. tostring(pos) .. ".nom"
Files.spoof(name, s)
- return table.insert(file_queue, name)
+ return file_queue:add(name)
end
})
local arg_string = table.concat(arg, sep) .. sep
@@ -130,24 +135,13 @@ if not args or args.help then
print(usage)
os.exit(EXIT_FAILURE)
end
-local nomsu = NomsuCompiler
-nomsu.environment.arg = NomsuCompiler.environment.List(args.nomsu_args)
+nomsu_environment.command_line_args = List(args.nomsu_args)
+nomsu_environment.optimization = args.optimization or 1
if args.version then
- nomsu:run([[(: use "core"; say (Nomsu version))]])
+ nomsu_environment.run_file_1_in('core', nomsu_environment)
+ nomsu_environment.run_1_in([[say (Nomsu version)]], nomsu_environment)
os.exit(EXIT_SUCCESS)
end
-FILE_CACHE = setmetatable({ }, {
- __index = function(self, filename)
- local file = io.open(filename)
- if not (file) then
- return nil
- end
- local contents = file:read("*a")
- file:close()
- self[filename] = contents
- return contents
- end
-})
local run
run = function()
local input_files = { }
@@ -172,62 +166,9 @@ run = function()
break
end
end
- nomsu.can_optimize = function(f)
- if args.optimization == 0 then
- return false
- end
- if args.compile and input_files[f] then
- return false
- end
- return true
- end
if not (args.no_core) then
- nomsu:import_file('core')
+ nomsu_environment.run_file_1_in('core', nomsu_environment)
end
- local get_file_and_source
- get_file_and_source = function(filename)
- local file, source
- if filename == 'stdin' or filename:match("%.nom$") then
- file = Files.read(filename)
- if not file then
- error("File does not exist: " .. tostring(filename), 0)
- end
- source = Source(filename, 1, #file)
- else
- return nil
- end
- source = Source(filename, 1, #file)
- return file, source
- end
- local run_file
- run_file = function(filename, lua_handler)
- if lua_handler == nil then
- lua_handler = nil
- end
- local file, source = get_file_and_source(filename)
- if not (file) then
- return
- end
- local tree = nomsu:parse(file, source)
- if tree then
- if tree.type ~= "FileChunks" then
- tree = {
- tree
- }
- end
- for _index_0 = 1, #tree do
- local chunk = tree[_index_0]
- local lua = nomsu:compile(chunk):as_statements("return ")
- lua:declare_locals()
- lua:prepend("-- File: " .. tostring(source.filename:gsub("\n.*", "...")) .. "\n")
- if lua_handler and input_files[filename] then
- lua_handler(tostring(lua))
- end
- nomsu:run_lua(lua)
- end
- end
- end
- local parse_errs = { }
for _index_0 = 1, #file_queue do
local f = file_queue[_index_0]
for _, filename in Files.walk(f) do
@@ -238,32 +179,57 @@ run = function()
break
end
if args.check_syntax then
- local file, source = get_file_and_source(filename)
- if not (file) then
- _continue_0 = true
- break
- end
- local tree = nomsu:parse(file, source)
+ local code = Files.read(filename)
+ local source = Source(filename, 1, #code)
+ nomsu_environment._1_parsed(NomsuCode(source, code))
print("Parse succeeded: " .. tostring(filename))
- end
- if args.compile then
+ elseif args.compile then
local output
if filename == 'stdin' then
output = io.output()
else
output = io.open(filename:gsub("%.nom$", ".lua"), "w")
end
- run_file(filename, function(lua)
+ local code = Files.read(filename)
+ local source = Source(filename, 1, #code)
+ code = NomsuCode(source, code)
+ local tree = nomsu_environment._1_parsed(code)
+ if not (tree.type == 'FileChunks') then
+ tree = {
+ tree
+ }
+ end
+ for _index_1 = 1, #tree do
+ local chunk = tree[_index_1]
+ local lua = nomsu_environment.compile(chunk)
+ lua:declare_locals()
+ nomsu_environment.run_1_in(chunk, nomsu_environment)
output:write(tostring(lua), "\n")
if args.verbose then
- return print(tostring(lua))
+ print(tostring(lua))
end
- end)
+ end
print(("Compiled %-25s -> %s"):format(filename, filename:gsub("%.nom$", ".lua")))
output:close()
- end
- if not args.check_syntax and not args.compile then
- run_file(filename, (args.verbose and print or nil))
+ elseif args.verbose then
+ local code = Files.read(filename)
+ local source = Source(filename, 1, #code)
+ code = NomsuCode(source, code)
+ local tree = nomsu_environment._1_parsed(code)
+ if not (tree.type == 'FileChunks') then
+ tree = {
+ tree
+ }
+ end
+ for _index_1 = 1, #tree do
+ local chunk = tree[_index_1]
+ local lua = nomsu_environment.compile(chunk)
+ lua:declare_locals()
+ nomsu_environment.run_1_in(chunk, nomsu_environment)
+ print(tostring(lua))
+ end
+ else
+ nomsu_environment.run_file_1_in(filename, nomsu_environment, 0)
end
_continue_0 = true
until true
@@ -273,59 +239,7 @@ run = function()
end
end
if not (args.primary_file or args.exec_strings) then
- nomsu:run([[#!/usr/bin/env nomsu -V4
-use "lib/consolecolor.nom"
-[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.
- To exit, type 'exit' or 'quit' and hit enter twice."
-
-say "\
- ..
- \(bright)\(underscore)Welcome to the Nomsu v\(Nomsu version) interactive console!\(reset color)
- press 'enter' twice to run a command
- "]])
- for repl_line = 1, math.huge do
- io.write(colored.bright(colored.yellow(">> ")))
- local buff = { }
- while true do
- io.write(colors.bright)
- local line = io.read("*L")
- io.write(colors.reset)
- if line == "\n" or not line then
- if #buff > 0 then
- io.write("\027[1A\027[2K")
- end
- break
- end
- line = line:gsub("\t", " ")
- table.insert(buff, line)
- io.write(colored.dim(colored.yellow(".. ")))
- end
- if #buff == 0 then
- break
- end
- buff = table.concat(buff)
- local pseudo_filename = "user input #" .. repl_line
- Files.spoof(pseudo_filename, buff)
- local err_hand
- err_hand = function(error_message)
- return Errhand.print_error(error_message)
- end
- local ret
- ok, ret = xpcall(nomsu.run, err_hand, nomsu, NomsuCode(Source(pseudo_filename, 1, #buff), buff))
- if ok and ret ~= nil then
- if type(ret) == 'number' then
- print("= " .. tostring(ret))
- else
- print("= " .. tostring(ret:as_nomsu()))
- end
- elseif not ok then
- Errhand.print_error(ret)
- end
- end
+ return nomsu_environment.run_file_1_in("tools/repl.nom", nomsu_environment)
end
end
local debugger
diff --git a/nomsu.moon b/nomsu.moon
index 9637358..8825ba5 100755
--- a/nomsu.moon
+++ b/nomsu.moon
@@ -46,14 +46,18 @@ if not ok
os.exit(EXIT_FAILURE)
Files = require "files"
Errhand = require "error_handling"
-NomsuCompiler = require "nomsu_compiler"
+--NomsuCompiler = require "nomsu_compiler"
{:NomsuCode, :LuaCode, :Source} = require "code_obj"
+--{:Importer, :import_to_1_from, :_1_forked} = require 'importer'
+{:List, :Dict, :Text} = require 'containers'
+--SyntaxTree = require "syntax_tree"
+nomsu_environment = require('nomsu_environment')
-- If this file was reached via require(), then just return the Nomsu compiler
if not arg or debug.getinfo(2).func == require
- return NomsuCompiler
+ return nomsu_environment
-file_queue = {}
+file_queue = List{}
sep = "\3"
parser = re.compile([[
args <- {| (flag %sep)* (({~ file ~} -> add_file) {:primary_file: %true :} %sep)?
@@ -73,37 +77,25 @@ parser = re.compile([[
file <- ("-" -> "stdin") / {(!%sep .)+}
]], {
true:lpeg.Cc(true), number:lpeg.R("09")^1/tonumber, sep:lpeg.P(sep)
- add_file: (f)-> table.insert(file_queue, f)
+ add_file: (f)-> file_queue\add(f)
add_exec_string: (pos, s)->
name = "command line arg @#{pos}.nom"
Files.spoof(name, s)
- table.insert(file_queue, name)
+ file_queue\add name
})
arg_string = table.concat(arg, sep)..sep
args = parser\match(arg_string)
if not args or args.help
print usage
os.exit(EXIT_FAILURE)
-
-nomsu = NomsuCompiler
-nomsu.environment.arg = NomsuCompiler.environment.List(args.nomsu_args)
+nomsu_environment.command_line_args = List(args.nomsu_args)
+nomsu_environment.optimization = args.optimization or 1
if args.version
- nomsu\run [[(: use "core"; say (Nomsu version))]]
+ nomsu_environment.run_file_1_in 'core', nomsu_environment
+ nomsu_environment.run_1_in([[say (Nomsu version)]], nomsu_environment)
os.exit(EXIT_SUCCESS)
-export FILE_CACHE
--- FILE_CACHE is a map from filename (string) -> string of file contents
-FILE_CACHE = setmetatable {}, {
- __index: (filename)=>
- file = io.open(filename)
- return nil unless file
- contents = file\read("*a")
- file\close!
- self[filename] = contents
- return contents
-}
-
run = ->
input_files = {}
for f in *file_queue
@@ -115,116 +107,52 @@ run = ->
for _,filename in Files.walk(f)
input_files[filename] = true
- nomsu.can_optimize = (f)->
- return false if args.optimization == 0
- return false if args.compile and input_files[f]
- return true
-
unless args.no_core
- nomsu\import_file('core')
-
- get_file_and_source = (filename)->
- local file, source
- if filename == 'stdin' or filename\match("%.nom$")
- file = Files.read(filename)
- if not file
- error("File does not exist: #{filename}", 0)
- source = Source(filename, 1, #file)
- else return nil
- source = Source(filename,1,#file)
- return file, source
-
- run_file = (filename, lua_handler=nil)->
- file, source = get_file_and_source(filename)
- return unless file
- tree = nomsu\parse(file, source)
- if tree
- if tree.type != "FileChunks"
- tree = {tree}
- -- Each chunk's compilation is affected by the code in the previous chunks
- -- (typically), so each chunk needs to compile and run before the next one
- -- compiles.
- for chunk in *tree
- lua = nomsu\compile(chunk)\as_statements("return ")
- lua\declare_locals!
- lua\prepend "-- File: #{source.filename\gsub("\n.*", "...")}\n"
- if lua_handler and input_files[filename] then lua_handler(tostring(lua))
- nomsu\run_lua(lua)
-
- parse_errs = {}
+ nomsu_environment.run_file_1_in 'core', nomsu_environment
+
for f in *file_queue
for _,filename in Files.walk(f)
continue unless filename == "stdin" or filename\match("%.nom$")
if args.check_syntax
-- Check syntax
- file, source = get_file_and_source(filename)
- continue unless file
- tree = nomsu\parse(file, source)
+ code = Files.read(filename)
+ source = Source(filename, 1, #code)
+ nomsu_environment._1_parsed(NomsuCode(source, code))
print("Parse succeeded: #{filename}")
-
- if args.compile
+ elseif args.compile
-- Compile .nom files into .lua
output = if filename == 'stdin' then io.output()
else io.open(filename\gsub("%.nom$", ".lua"), "w")
- run_file filename, (lua)->
+ code = Files.read(filename)
+ source = Source(filename, 1, #code)
+ code = NomsuCode(source, code)
+ tree = nomsu_environment._1_parsed(code)
+ tree = {tree} unless tree.type == 'FileChunks'
+ for chunk in *tree
+ lua = nomsu_environment.compile(chunk)
+ lua\declare_locals!
+ nomsu_environment.run_1_in(chunk, nomsu_environment)
output\write(tostring(lua), "\n")
if args.verbose then print(tostring(lua))
print ("Compiled %-25s -> %s")\format(filename, filename\gsub("%.nom$", ".lua"))
output\close!
-
- if not args.check_syntax and not args.compile
+ elseif args.verbose
+ code = Files.read(filename)
+ source = Source(filename, 1, #code)
+ code = NomsuCode(source, code)
+ tree = nomsu_environment._1_parsed(code)
+ tree = {tree} unless tree.type == 'FileChunks'
+ for chunk in *tree
+ lua = nomsu_environment.compile(chunk)
+ lua\declare_locals!
+ nomsu_environment.run_1_in(chunk, nomsu_environment)
+ print(tostring(lua))
+ else
-- Just run the file
- run_file filename, (args.verbose and print or nil)
+ nomsu_environment.run_file_1_in(filename, nomsu_environment, 0)
unless args.primary_file or args.exec_strings
- -- Run in interactive mode (REPL)
- nomsu\run [[
-#!/usr/bin/env nomsu -V4
-use "lib/consolecolor.nom"
-[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.
- To exit, type 'exit' or 'quit' and hit enter twice."
-
-say "\
- ..
- \(bright)\(underscore)Welcome to the Nomsu v\(Nomsu version) interactive console!\(reset color)
- press 'enter' twice to run a command
- "]]
- for repl_line=1,math.huge
- io.write(colored.bright colored.yellow ">> ")
- buff = {}
- while true
- io.write(colors.bright)
- line = io.read("*L")
- io.write(colors.reset)
- if line == "\n" or not line
- if #buff > 0
- io.write("\027[1A\027[2K")
- break -- Run buffer
- line = line\gsub("\t", " ")
- table.insert buff, line
- io.write(colored.dim colored.yellow ".. ")
- if #buff == 0
- 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, NomsuCode(Source(pseudo_filename,1,#buff), buff)
- if ok and ret != nil
- if type(ret) == 'number'
- print "= #{ret}"
- else
- print "= #{ret\as_nomsu!}"
- elseif not ok
- Errhand.print_error ret
+ nomsu_environment.run_file_1_in("tools/repl.nom", nomsu_environment)
debugger = if args.debugger == "nil" then {}
else require(args.debugger or 'error_handling')
diff --git a/nomsu_compiler.lua b/nomsu_compiler.lua
index c7fa456..0538776 100644
--- a/nomsu_compiler.lua
+++ b/nomsu_compiler.lua
@@ -2,20 +2,11 @@ local lpeg = require('lpeg')
local R, P, S
R, P, S = lpeg.R, lpeg.P, lpeg.S
local re = require('re')
-local Files = require('files')
local List, Dict, Text
do
local _obj_0 = require('containers')
List, Dict, Text = _obj_0.List, _obj_0.Dict, _obj_0.Text
end
-colors = require('consolecolors')
-colored = setmetatable({ }, {
- __index = function(_, color)
- return (function(msg)
- return colors[color] .. tostring(msg or '') .. colors.reset
- end)
- end
-})
local insert, remove, concat
do
local _obj_0 = table
@@ -27,15 +18,17 @@ do
local _obj_0 = string
match, sub, gsub, format, byte, find = _obj_0.match, _obj_0.sub, _obj_0.gsub, _obj_0.format, _obj_0.byte, _obj_0.find
end
-local NomsuCode, LuaCode, Source
+local LuaCode, Source
do
local _obj_0 = require("code_obj")
- NomsuCode, LuaCode, Source = _obj_0.NomsuCode, _obj_0.LuaCode, _obj_0.Source
+ LuaCode, Source = _obj_0.LuaCode, _obj_0.Source
end
local SyntaxTree = require("syntax_tree")
-local make_parser = require("parser")
-local pretty_error = require("pretty_errors")
-SOURCE_MAP = { }
+local Importer, import_to_1_from, _1_forked
+do
+ local _obj_0 = require('importer')
+ Importer, import_to_1_from, _1_forked = _obj_0.Importer, _obj_0.import_to_1_from, _obj_0._1_forked
+end
table.map = function(t, fn)
return setmetatable((function()
local _accum_0 = { }
@@ -47,389 +40,51 @@ table.map = function(t, fn)
return _accum_0
end)(), getmetatable(t))
end
-table.fork = function(t, values)
- return setmetatable(values or { }, {
- __index = t
+local pretty_error = require("pretty_errors")
+local compile_error
+compile_error = function(tree, err_msg, hint)
+ if hint == nil then
+ hint = nil
+ end
+ local err_str = pretty_error({
+ title = "Compile error",
+ error = err_msg,
+ hint = hint,
+ source = tree:get_source_code(),
+ start = tree.source.start,
+ stop = tree.source.stop,
+ filename = tree.source.filename
})
+ return error(err_str, 0)
end
-table.copy = function(t)
- return setmetatable((function()
- local _tbl_0 = { }
- for k, v in pairs(t) do
- _tbl_0[k] = v
- end
- return _tbl_0
- end)(), getmetatable(t))
-end
-local utf8_char_patt = (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"))
-local operator_patt = S("'`~!@$^&*+=|<>?/-") ^ 1 * -1
-local identifier_patt = (R("az", "AZ", "09") + P("_") + utf8_char_patt) ^ 1 * -1
-local is_operator
-is_operator = function(s)
- return not not operator_patt:match(s)
-end
-local is_identifier
-is_identifier = function(s)
- return not not identifier_patt:match(s)
-end
-local inline_escaper = re.compile("{~ (%utf8_char / ('\"' -> '\\\"') / ('\n' -> '\\n') / ('\t' -> '\\t') / ('\b' -> '\\b') / ('\a' -> '\\a') / ('\v' -> '\\v') / ('\f' -> '\\f') / ('\r' -> '\\r') / ('\\' -> '\\\\') / ([^ -~] -> escape) / .)* ~}", {
- utf8_char = utf8_char_patt,
- escape = (function(self)
- return ("\\%03d"):format(self:byte())
- end)
-})
-local inline_escape
-inline_escape = function(s)
- return inline_escaper:match(s)
-end
-local escaper = re.compile("{~ (%utf8_char / ('\\' -> '\\\\') / [\n\r\t -~] / (. -> escape))* ~}", {
- utf8_char = utf8_char_patt,
- escape = (function(self)
- return ("\\%03d"):format(self:byte())
- end)
-})
-local escape
-escape = function(s)
- return escaper:match(s)
-end
-local make_tree
-make_tree = function(tree, userdata)
- tree.source = Source(userdata.filename, tree.start, tree.stop)
- tree.start, tree.stop = nil, nil
- do
- local _accum_0 = { }
- local _len_0 = 1
- for _index_0 = 1, #tree do
- local t = tree[_index_0]
- if SyntaxTree:is_instance(t) and t.type == "Comment" then
- _accum_0[_len_0] = t
- _len_0 = _len_0 + 1
+local math_expression = re.compile([[ (([*/^+-] / [0-9]+) " ")* [*/^+-] !. ]])
+local compile_math_expression
+compile_math_expression = function(compile, tree, ...)
+ local lua = LuaCode(tree.source)
+ for i, tok in ipairs(tree) do
+ if type(tok) == 'string' then
+ lua:append(tok)
+ else
+ local tok_lua = compile(tok)
+ if tok.type == "Action" then
+ tok_lua:parenthesize()
end
+ lua:append(tok_lua)
end
- tree.comments = _accum_0
- end
- if #tree.comments == 0 then
- tree.comments = nil
- end
- for i = #tree, 1, -1 do
- if SyntaxTree:is_instance(tree[i]) and tree[i].type == "Comment" then
- table.remove(tree, i)
+ if i < #tree then
+ lua:append(" ")
end
end
- tree = SyntaxTree(tree)
- return tree
-end
-local Parsers = { }
-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
- 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)
+ return lua
end
local MAX_LINE = 80
-local NomsuCompiler = setmetatable({ }, {
- __tostring = function(self)
- return "Nomsu"
- end
-})
-local _anon_chunk = 0
-do
- NomsuCompiler.can_optimize = function()
- return false
- end
- NomsuCompiler.environment = {
- NOMSU_COMPILER_VERSION = 11,
- NOMSU_SYNTAX_VERSION = max_parser_version,
- next = next,
- unpack = unpack,
- setmetatable = setmetatable,
- coroutine = coroutine,
- rawequal = rawequal,
- getmetatable = getmetatable,
- pcall = pcall,
- error = error,
- package = package,
- os = os,
- require = require,
- tonumber = tonumber,
- tostring = tostring,
- string = string,
- xpcall = xpcall,
- module = module,
- print = print,
- loadfile = loadfile,
- rawset = rawset,
- _VERSION = _VERSION,
- collectgarbage = collectgarbage,
- rawget = rawget,
- rawlen = rawlen,
- table = table,
- assert = assert,
- dofile = dofile,
- loadstring = loadstring,
- lua_type_of = type,
- select = select,
- math = math,
- io = io,
- load = load,
- pairs = pairs,
- ipairs = ipairs,
- List = List,
- Dict = Dict,
- lpeg = lpeg,
- re = re,
- Files = Files,
- SyntaxTree = SyntaxTree,
- TESTS = Dict({ }),
- globals = Dict({ }),
- LuaCode = LuaCode,
- NomsuCode = NomsuCode,
- Source = Source,
- nomsu = NomsuCompiler,
- __imported = Dict({ }),
- __parent = nil
- }
- setmetatable(NomsuCompiler.environment, {
- __index = function(self, key)
- do
- local imported = rawget(self, "__imported")
- if imported then
- local ret = imported[key]
- if not (ret == nil) then
- return ret
- end
- end
- end
- do
- local parent = rawget(self, "__parent")
- if parent then
- return parent[key]
- end
- end
- end
- })
- if _VERSION == "Lua 5.4" then
- NomsuCompiler.environment.ipairs = function(x)
- do
- local mt = getmetatable(x)
- if mt then
- if mt.__ipairs then
- return mt.__ipairs(x)
- end
- end
- end
- return ipairs(x)
- end
- end
- if jit or _VERSION == "Lua 5.2" then
- NomsuCompiler.environment.bit = require("bitops")
- end
- NomsuCompiler.fork = function(self)
- local f = setmetatable({ }, {
- __index = self
- })
- f.environment = setmetatable({
- __parent = self.environment,
- __imported = Dict({ }),
- nomsu = f,
- COMPILE_ACTIONS = setmetatable({
- __parent = self.environment.COMPILE_ACTIONS,
- __imported = Dict({ })
- }, getmetatable(self.environment))
- }, getmetatable(self.environment))
- return f
- end
- NomsuCompiler.parse = function(self, nomsu_code, source, version)
- if source == nil then
- source = nil
- end
- if version == nil then
- version = nil
- end
- source = source or nomsu_code.source
- nomsu_code = tostring(nomsu_code)
- if not (source) then
- source = Source("anonymous chunk #" .. tostring(_anon_chunk), 1, #nomsu_code)
- _anon_chunk = _anon_chunk + 1
- end
- version = version or nomsu_code:match("^#![^\n]*nomsu[ ]+-V[ ]*([0-9.]+)")
- local syntax_version = version and tonumber(version:match("^[0-9]+")) or max_parser_version
- local parse = Parsers[syntax_version] or Parsers[max_parser_version]
- local tree = parse(nomsu_code, source.filename)
- local find_errors
- find_errors = function(t)
- if t.type == "Error" then
- return coroutine.yield(t)
- else
- for k, v in pairs(t) do
- local _continue_0 = false
- repeat
- if not (SyntaxTree:is_instance(v)) then
- _continue_0 = true
- break
- end
- find_errors(v)
- _continue_0 = true
- until true
- if not _continue_0 then
- break
- end
- end
- end
- end
- local errs
- do
- local _accum_0 = { }
- local _len_0 = 1
- for err in coroutine.wrap(function()
- return find_errors(tree)
- end) do
- _accum_0[_len_0] = err
- _len_0 = _len_0 + 1
- end
- errs = _accum_0
- end
- local num_errs = #errs
- if num_errs > 0 then
- local err_strings
- do
- local _accum_0 = { }
- local _len_0 = 1
- for i, t in ipairs(errs) do
- if i <= 3 then
- _accum_0[_len_0] = pretty_error({
- title = "Parse error",
- error = t.error,
- hint = t.hint,
- source = t:get_source_code(),
- start = t.source.start,
- stop = t.source.stop,
- filename = t.source.filename
- })
- _len_0 = _len_0 + 1
- end
- end
- err_strings = _accum_0
- end
- if num_errs > 3 then
- table.insert(err_strings, "\027[31;1m +" .. tostring(num_errs - #errs) .. " additional errors...\027[0m\n")
- end
- error(table.concat(err_strings, '\n\n'), 0)
- end
- return tree
- end
- NomsuCompiler.compile_error = function(self, tree, err_msg, hint)
- if hint == nil then
- hint = nil
- end
- local err_str = pretty_error({
- title = "Compile error",
- error = err_msg,
- hint = hint,
- source = tree:get_source_code(),
- start = tree.source.start,
- stop = tree.source.stop,
- filename = tree.source.filename
- })
- return error(err_str, 0)
- end
- local add_lua_bits
- add_lua_bits = function(self, val_or_stmt, code, compile_actions)
- local cls = val_or_stmt == "value" and LuaCode.Value or LuaCode
- local operate_on_text
- operate_on_text = function(text)
- local lua = cls(text.source)
- for _index_0 = 1, #text do
- local bit = text[_index_0]
- if type(bit) == "string" then
- lua:append(bit)
- elseif bit.type == "Text" then
- lua:append(operate_on_text(bit))
- else
- local bit_lua = self:compile(bit, compile_actions)
- if not (bit_lua.is_value) then
- self:compile_error(bit, "Can't use this as a string interpolation value, since it's not an expression.")
- end
- lua:append(bit_lua)
- end
- end
- return lua
- end
- return operate_on_text(code)
- end
- local add_lua_string_bits
- 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, tostring(code.source):as_lua(), ", ", self:compile(code), ")")
- end
- local add_bit_lua
- add_bit_lua = function(lua, bit_lua)
- local bit_leading_len = #(bit_lua:match("^[^\n]*"))
- lua:append(lua:trailing_line_len() + bit_leading_len > MAX_LINE and ",\n " or ", ")
- return lua:append(bit_lua)
- end
- local operate_on_text
- operate_on_text = function(text)
- 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, bit:as_lua())
- elseif bit.type == "Text" then
- add_bit_lua(lua, operate_on_text(bit))
- else
- local bit_lua = self:compile(bit)
- if not (bit_lua.is_value) then
- self:compile_error(bit, "Can't use this as a string interpolation value, since it's not an expression.")
- end
- add_bit_lua(lua, bit_lua)
- end
- end
- lua:append(")")
- return lua
- end
- return operate_on_text(code)
- end
- local math_expression = re.compile([[ (([*/^+-] / [0-9]+) " ")* [*/^+-] !. ]])
- local compile_math_expression
- compile_math_expression = function(self, tree, ...)
- local lua = LuaCode.Value(tree.source)
- for i, tok in ipairs(tree) do
- if type(tok) == 'string' then
- lua:append(tok)
- else
- local tok_lua = self:compile(tok, compile_actions)
- if not (tok_lua.is_value) then
- self:compile_error(tok, "Can't use this as a value in a math expression, since it's not a value.")
- end
- if tok.type == "Action" then
- tok_lua:parenthesize()
- end
- lua:append(tok_lua)
- end
- if i < #tree then
- lua:append(" ")
- end
- end
- return lua
- end
- NomsuCompiler.environment.COMPILE_ACTIONS = setmetatable({
- __imported = Dict({ }),
- [""] = function(self, tree, fn, ...)
- local lua = LuaCode.Value(tree.source)
- lua:append(self:compile(fn, compile_actions))
- if not (lua:text():is_lua_id()) then
+local compile = setmetatable({
+ action = Importer({
+ [""] = function(compile, tree, fn, ...)
+ local lua = LuaCode(tree.source)
+ local fn_lua = compile(fn)
+ lua:append(fn_lua)
+ if not (fn_lua:text():match("^%(.*%)$") or fn_lua:text():match("^[_a-zA-Z][_a-zA-Z0-9.]*$")) then
lua:parenthesize()
end
lua:append("(")
@@ -437,259 +92,99 @@ do
if i > 1 then
lua:append(", ")
end
- lua:append(self:compile(select(i, ...), compile_actions))
+ lua:append(compile(select(i, ...)))
end
lua:append(")")
return lua
end,
- ["Lua"] = function(self, tree, code)
- return add_lua_string_bits(self, 'statements', code)
- end,
- ["Lua value"] = function(self, tree, code)
- return add_lua_string_bits(self, 'value', code)
- end,
- ["lua >"] = function(self, tree, code)
+ ["Lua"] = function(compile, tree, code)
if code.type ~= "Text" then
- return LuaCode(tree.source, "nomsu:run_lua(", self:compile(code), ", nomsu);")
+ return LuaCode(code.source, "LuaCode(", tostring(code.source):as_lua(), ", ", compile(code), ")")
+ end
+ local add_bit_lua
+ add_bit_lua = function(lua, bit_lua)
+ local bit_leading_len = #(bit_lua:match("^[^\n]*"))
+ lua:append(lua:trailing_line_len() + bit_leading_len > MAX_LINE and ",\n " or ", ")
+ return lua:append(bit_lua)
+ end
+ local operate_on_text
+ operate_on_text = function(text)
+ local lua = LuaCode(text.source, "LuaCode(", 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, bit:as_lua())
+ elseif bit.type == "Text" then
+ add_bit_lua(lua, operate_on_text(bit))
+ else
+ add_bit_lua(lua, compile(bit))
+ end
+ end
+ lua:append(")")
+ return lua
end
- return add_lua_bits(self, "statements", code)
+ return operate_on_text(code)
+ end,
+ ["Lua value"] = function(compile, tree, code)
+ return compile.action["Lua"](compile, tree, code)
end,
- ["= lua"] = function(self, tree, code)
+ ["lua >"] = function(compile, tree, code)
if code.type ~= "Text" then
- return LuaCode.Value(tree.source, "nomsu:run_lua(", self:compile(code), ":as_statements('return '), nomsu)")
+ return tree
end
- return add_lua_bits(self, "value", code)
- end,
- ["use"] = function(self, tree, path)
- if path.type == 'Text' and #path == 1 and type(path[1]) == 'string' then
- if not (self:import_file(path[1])) then
- self:compile_error(tree, "Could not find anything to import for " .. tostring(path))
+ local operate_on_text
+ operate_on_text = function(text)
+ local lua = LuaCode(text.source)
+ for _index_0 = 1, #text do
+ local bit = text[_index_0]
+ if type(bit) == "string" then
+ lua:append(bit)
+ elseif bit.type == "Text" then
+ lua:append(operate_on_text(bit))
+ else
+ lua:append(compile(bit))
+ end
end
+ return lua
end
- return LuaCode(tree.source, "nomsu:import_file(" .. tostring(self:compile(path)) .. ")")
+ return operate_on_text(code)
end,
- ["tests"] = function(self, tree)
- return LuaCode.Value(tree.source, "TESTS")
+ ["= lua"] = function(compile, tree, code)
+ return compile.action["lua >"](compile, tree, code)
end,
- ["test"] = function(self, tree, body)
- local test_str = table.concat((function()
- local _accum_0 = { }
- local _len_0 = 1
- for _index_0 = 1, #body do
- local line = body[_index_0]
- _accum_0[_len_0] = tostring(self:tree_to_nomsu(line))
- _len_0 = _len_0 + 1
- end
- return _accum_0
- end)(), "\n")
- return LuaCode(tree.source, "TESTS[" .. tostring(tostring(tree.source):as_lua()) .. "] = ", test_str:as_lua())
+ ["use"] = function(compile, tree, path)
+ return LuaCode(tree.source, "run_file_1_in(" .. tostring(compile(path)) .. ", _ENV)")
end,
- ["is jit"] = function(self, tree, code)
- return LuaCode.Value(tree.source, jit and "true" or "false")
+ ["tests"] = function(compile, tree)
+ return LuaCode(tree.source, "TESTS")
end,
- ["Lua version"] = function(self, tree, code)
- return LuaCode.Value(tree.source, _VERSION:as_lua())
+ ["test"] = function(compile, tree, body)
+ return LuaCode(tree.source, "TESTS[" .. tostring(tostring(tree.source):as_lua()) .. "] = ", body:as_lua())
end,
- __parent = setmetatable({ }, {
- __index = function(self, key)
- if type(key) == 'string' and math_expression:match(key) then
- return compile_math_expression
- end
- end
- })
- }, getmetatable(NomsuCompiler.environment))
- NomsuCompiler.import = function(self, mod)
- for k, v in pairs(mod) do
- local _continue_0 = false
- repeat
- if k == "__imported" or k == "__parent" then
- _continue_0 = true
- break
- end
- self.environment.__imported[k] = v
- _continue_0 = true
- until true
- if not _continue_0 then
- break
- end
- end
- for k, v in pairs(mod.COMPILE_ACTIONS) do
- local _continue_0 = false
- repeat
- if k == "__imported" or k == "__parent" then
- _continue_0 = true
- break
- end
- self.environment.COMPILE_ACTIONS.__imported[k] = self.environment.COMPILE_ACTIONS.__imported[k] or v
- _continue_0 = true
- until true
- if not _continue_0 then
- break
- 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
- source = Source:from_string(source)
- end
- if not Files.read(source.filename) then
- Files.spoof(source.filename, to_run)
- end
- local tree
- if SyntaxTree:is_instance(to_run) then
- tree = to_run
- else
- tree = self:parse(to_run, source)
- end
- if tree == nil then
- return nil
- end
- if tree.type ~= "FileChunks" then
- tree = {
- tree
- }
- end
- local ret = nil
- local all_lua = { }
- for _index_0 = 1, #tree do
- local chunk = tree[_index_0]
- local lua = self:compile(chunk, compile_actions):as_statements("return ")
- lua:declare_locals()
- lua:prepend("-- File: " .. tostring(source.filename:gsub("\n.*", "...")) .. "\n")
- insert(all_lua, tostring(lua))
- ret = self:run_lua(lua)
- end
- return ret
- end
- local _running_files = { }
- local _loaded_files = { }
- NomsuCompiler.run_file = function(self, filename, compile_actions)
- if _loaded_files[filename] then
- return _loaded_files[filename]
- end
- for i, running in ipairs(_running_files) do
- if running == filename then
- local loop
- do
- local _accum_0 = { }
- local _len_0 = 1
- for j = i, #_running_files do
- _accum_0[_len_0] = _running_files[j]
- _len_0 = _len_0 + 1
- end
- loop = _accum_0
- end
- insert(loop, filename)
- error("Circular import, this loops forever: " .. tostring(concat(loop, " -> ")) .. "...")
- end
- end
- insert(_running_files, filename)
- local mod = self:fork()
- local ret = mod.environment
- mod.from_file = filename
- if match(filename, "%.lua$") then
- local file = assert(Files.read(filename), "Could not find file: " .. tostring(filename))
- ret = mod:run_lua(LuaCode(Source(filename, 1, #file), file)) or ret
- elseif match(filename, "%.nom$") or match(filename, "^/dev/fd/[012]$") then
- local ran_lua
- if self.can_optimize(filename) then
- local lua_filename = gsub(filename, "%.nom$", ".lua")
- do
- local file = Files.read(lua_filename)
- if file then
- ret = mod:run_lua(LuaCode(Source(lua_filename, 1, #file), file)) or ret
- ran_lua = true
- end
- end
- end
- if not (ran_lua) then
- local file = Files.read(filename)
- if not file then
- error("Tried to run file that does not exist: " .. tostring(filename))
- end
- ret = mod:run(NomsuCode(Source(filename, 1, #file), file), compile_actions) or ret
- end
- else
- error("Invalid filetype for " .. tostring(filename), 0)
- end
- _loaded_files[filename] = ret
- remove(_running_files)
- return ret
- end
- NomsuCompiler.run_lua = function(self, lua)
- local lua_string = tostring(lua)
- local run_lua_fn, err = load(lua_string, tostring(source or lua.source), "t", self.environment)
- if not run_lua_fn then
- local line_numbered_lua = concat((function()
- local _accum_0 = { }
- local _len_0 = 1
- for i, line in ipairs(Files.get_lines(lua_string)) do
- _accum_0[_len_0] = format("%3d|%s", i, line)
- _len_0 = _len_0 + 1
- end
- return _accum_0
- end)(), "\n")
- error("Failed to compile generated code:\n" .. tostring(colored.bright(colored.blue(colored.onblack(line_numbered_lua)))) .. "\n\n" .. tostring(err), 0)
- end
- local source = lua.source or Source(lua_string, 1, #lua_string)
- local source_key = tostring(source)
- if not (SOURCE_MAP[source_key]) then
- local map = { }
- local file = Files.read(source.filename)
- if not file then
- error("Failed to find file: " .. tostring(source.filename))
- end
- local nomsu_str = file:sub(source.start, source.stop)
- assert(type(nomsu_str) == 'string')
- local lua_line = 1
- local nomsu_line = Files.get_line_number(file, source.start)
- local map_sources
- map_sources = function(s)
- if type(s) == 'string' then
- for nl in s:gmatch("\n") do
- map[lua_line] = map[lua_line] or nomsu_line
- lua_line = lua_line + 1
- end
- else
- if s.source and s.source.filename == source.filename then
- nomsu_line = Files.get_line_number(file, s.source.start)
- end
- local _list_0 = s.bits
- for _index_0 = 1, #_list_0 do
- local b = _list_0[_index_0]
- map_sources(b)
- end
- end
- end
- map_sources(lua)
- map[lua_line] = map[lua_line] or nomsu_line
- map[0] = 0
- SOURCE_MAP[source_key] = map
+ ["is jit"] = function(compile, tree, code)
+ return LuaCode(tree.source, "jit")
+ end,
+ ["Lua version"] = function(compile, tree, code)
+ return LuaCode(tree.source, "_VERSION")
+ end,
+ ["nomsu environment"] = function(compile, tree)
+ return LuaCode(tree.source, "_ENV")
end
- return run_lua_fn()
- end
- NomsuCompiler.compile = function(self, tree, compile_actions, force_value)
+ })
+}, {
+ __import = function(self, other)
+ import_to_1_from(self.action, other.action)
+ end,
+ __call = function(compile, tree, force_value)
if force_value == nil then
force_value = false
end
- compile_actions = compile_actions or self.environment.COMPILE_ACTIONS
if tree.version then
do
- local get_version = self[("Nomsu version"):as_lua_id()]
+ local get_version = compile.action[("Nomsu version"):as_lua_id()]
if get_version then
do
- local upgrade = self[("1 upgraded from 2 to"):as_lua_id()]
+ local upgrade = compile.action[("1 upgraded from 2 to"):as_lua_id()]
if upgrade then
tree = upgrade(tree, tree.version, get_version())
end
@@ -700,7 +195,10 @@ do
local _exp_0 = tree.type
if "Action" == _exp_0 then
local stub = tree.stub
- local compile_action = compile_actions[stub]
+ local compile_action = compile.action[stub]
+ if not compile_action and math_expression:match(stub) then
+ compile_action = compile_math_expression
+ end
if compile_action and not tree.target then
local args
do
@@ -715,23 +213,24 @@ do
end
args = _accum_0
end
- local ret = compile_action(self, tree, unpack(args))
+ local ret = compile_action(compile, 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.")
+ 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 not (SyntaxTree:is_instance(ret)) then
return ret
end
if ret ~= tree then
- return self:compile(ret, compile_actions)
+ return compile(ret)
end
end
- local lua = LuaCode.Value(tree.source)
+ local lua = LuaCode(tree.source)
if tree.target then
- local target_lua = self:compile(tree.target, compile_actions)
- if tostring(target_lua):match("^%(.*%)$") or tostring(target_lua):match("^[_a-zA-Z][_a-zA-Z0-9]*$") then
+ local target_lua = compile(tree.target)
+ local target_text = target_lua:text()
+ if target_text:match("^%(.*%)$") or target_text:match("^[_a-zA-Z][_a-zA-Z0-9.]*$") then
lua:append(target_lua, ":")
else
lua:append("(", target_lua, "):")
@@ -746,16 +245,7 @@ do
_continue_0 = true
break
end
- local arg_lua = self:compile(tok, compile_actions, true)
- if not (arg_lua.is_value) then
- 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(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(tostring(arg_lua)))
- end
- end
+ local arg_lua = compile(tok, true)
insert(args, arg_lua)
_continue_0 = true
until true
@@ -767,14 +257,14 @@ do
lua:append(")")
return lua
elseif "EscapedNomsu" == _exp_0 then
- local lua = LuaCode.Value(tree.source, "SyntaxTree{")
+ local lua = LuaCode(tree.source, "SyntaxTree{")
local needs_comma, i = false, 1
local as_lua
as_lua = function(x)
if type(x) == 'number' then
return tostring(x)
elseif SyntaxTree:is_instance(x) then
- return self:compile(x, compile_actions)
+ return compile(x)
else
return x:as_lua()
end
@@ -808,51 +298,23 @@ do
local _len_0 = 1
for _index_0 = 1, #tree do
local line = tree[_index_0]
- _accum_0[_len_0] = self:compile(line, compile_actions):as_statements()
+ _accum_0[_len_0] = compile(line)
_len_0 = _len_0 + 1
end
return _accum_0
end)(), "\n")
return lua
else
- local lua = LuaCode.Value(tree.source)
- local values
- do
- local _accum_0 = { }
- local _len_0 = 1
- for _index_0 = 1, #tree do
- local line = tree[_index_0]
- _accum_0[_len_0] = self:compile(line)
- _len_0 = _len_0 + 1
- end
- values = _accum_0
- end
- local all_values = true
- for _index_0 = 1, #values do
- local v = values[_index_0]
- all_values = all_values and v.is_value
- end
- if all_values then
- if #values == 1 then
- return values[1]
- end
- lua:append("(")
- lua:concat_append(values, " and nil or ")
- lua:append(")")
- else
- lua:append("((function()")
- for i, v in ipairs(values) do
- if v.is_value then
- v = v:as_statements(i == #values and 'return ' or '')
- end
- lua:append("\n ", v)
- end
- lua:append("\nend)())")
+ local lua = LuaCode(tree.source)
+ lua:append("((function()")
+ for i, line in ipairs(tree) do
+ lua:append("\n ", compile(line))
end
+ lua:append("\nend)())")
return lua
end
elseif "Text" == _exp_0 then
- local lua = LuaCode.Value(tree.source)
+ local lua = LuaCode(tree.source)
local string_buffer = ""
for i, bit in ipairs(tree) do
local _continue_0 = false
@@ -869,17 +331,12 @@ do
lua:append(string_buffer:as_lua())
string_buffer = ""
end
- local bit_lua = self:compile(bit, compile_actions)
- if not (bit_lua.is_value) then
- local src = ' ' .. gsub(tostring(self:compile(bit, compile_actions)), '\n', '\n ')
- local line = tostring(bit.source.filename) .. ":" .. tostring(Files.get_line_number(Files.read(bit.source.filename), bit.source.start))
- self:compile_error(bit, "Can't this as a string interpolation value, since it's not an expression.")
- end
+ local bit_lua = compile(bit)
if #lua.bits > 0 then
lua:append("..")
end
if bit.type ~= "Text" then
- bit_lua = LuaCode.Value(bit.source, "tostring(", bit_lua, ")")
+ bit_lua = LuaCode(bit.source, "tostring(", bit_lua, ")")
end
lua:append(bit_lua)
_continue_0 = true
@@ -898,68 +355,65 @@ do
lua:parenthesize()
end
return lua
- elseif "List" == _exp_0 then
- local lua = LuaCode.Value(tree.source, "List{")
- lua:concat_append((function()
- local _accum_0 = { }
- local _len_0 = 1
- for _index_0 = 1, #tree do
- local e = tree[_index_0]
- _accum_0[_len_0] = self:compile(e, compile_actions)
- _len_0 = _len_0 + 1
+ elseif "List" == _exp_0 or "Dict" == _exp_0 then
+ local lua = LuaCode(tree.source, tostring(tree.type) .. "{")
+ local i = 1
+ local sep = ''
+ while i <= #tree do
+ local item = tree[i]
+ if item.type == "Block" then
+ break
end
- return _accum_0
- end)(), ", ", ",\n ")
- lua:append("}")
- return lua
- elseif "Dict" == _exp_0 then
- local lua = LuaCode.Value(tree.source, "Dict{")
- lua:concat_append((function()
- local _accum_0 = { }
- local _len_0 = 1
- for _index_0 = 1, #tree do
- local e = tree[_index_0]
- _accum_0[_len_0] = self:compile(e, compile_actions)
- _len_0 = _len_0 + 1
+ lua:append(sep)
+ if item.type == "Comment" then
+ lua:append(compile(item), "\n")
+ sep = ''
+ else
+ local item_lua = compile(item)
+ lua:append(item_lua)
+ sep = ', '
end
- return _accum_0
- end)(), ", ", ",\n ")
+ i = i + 1
+ end
lua:append("}")
+ if i <= #tree then
+ lua = LuaCode(tree.source, "(function()\n local it = ", lua)
+ while i <= #tree do
+ lua:append("\n ")
+ if tree[i].type == 'Block' or tree[i].type == 'Comment' then
+ lua:append(compile(tree[i]))
+ elseif tree[i].type == "DictEntry" then
+ lua:append("it[ ", compile(tree[i][1]), "] = ", (tree[i][2] and compile(tree[i][2]) or "true"))
+ else
+ lua:append("it:add(", compile(tree[i]), ")")
+ end
+ i = i + 1
+ end
+ lua:append("\n return it\nend)()")
+ end
return lua
elseif "DictEntry" == _exp_0 then
local key, value = tree[1], tree[2]
- local key_lua = self:compile(key, compile_actions)
- if not (key_lua.is_value) then
- self:compile_error(tree[1], "Can't use this as a dict key, since it's not an expression.")
- end
- local value_lua = value and self:compile(value, compile_actions) or LuaCode.Value(key.source, "true")
- if not (value_lua.is_value) then
- 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_]*)['"]$]=])
+ local key_lua = compile(key)
+ local value_lua = value and compile(value) or LuaCode(key.source, "true")
+ local key_str = match(key_lua:text(), [=[^["']([a-zA-Z_][a-zA-Z0-9_]*)['"]$]=])
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
+ elseif sub(key_lua:text(), 1, 1) == "[" then
return LuaCode(tree.source, "[ ", key_lua, "]=", value_lua)
else
return LuaCode(tree.source, "[", key_lua, "]=", value_lua)
end
elseif "IndexChain" == _exp_0 then
- local lua = self:compile(tree[1], compile_actions)
- if not (lua.is_value) then
- self:compile_error(tree[1], "Can't index into this, since it's not an expression.")
- end
- local first_char = sub(tostring(lua), 1, 1)
+ local lua = compile(tree[1])
+ local first_char = sub(lua:text(), 1, 1)
if first_char == "{" or first_char == '"' or first_char == "[" then
lua:parenthesize()
end
for i = 2, #tree do
local key = tree[i]
- local key_lua = self:compile(key, compile_actions)
- if not (key_lua.is_value) then
- 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)
+ local key_lua = compile(key)
+ local key_lua_str = key_lua:text()
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))
@@ -971,9 +425,9 @@ do
end
return lua
elseif "Number" == _exp_0 then
- return LuaCode.Value(tree.source, tostring(tree[1]))
+ return LuaCode(tree.source, tostring(tree[1]))
elseif "Var" == _exp_0 then
- return LuaCode.Value(tree.source, (tree[1]):as_lua_id())
+ return LuaCode(tree.source, (tree[1]):as_lua_id())
elseif "FileChunks" == _exp_0 then
return error("Can't convert FileChunks to a single block of lua, since each chunk's " .. "compilation depends on the earlier chunks")
elseif "Comment" == _exp_0 then
@@ -984,506 +438,5 @@ do
return error("Unknown type: " .. tostring(tree.type))
end
end
- NomsuCompiler.tree_to_inline_nomsu = function(self, tree, parenthesize_blocks, check, len)
- if parenthesize_blocks == nil then
- parenthesize_blocks = false
- end
- if check == nil then
- check = nil
- end
- if len == nil then
- len = 0
- end
- local recurse
- recurse = function(tree, nomsu, parenthesize_blocks)
- if nomsu == nil then
- nomsu = nil
- end
- if parenthesize_blocks == nil then
- parenthesize_blocks = false
- end
- return self:tree_to_inline_nomsu(tree, parenthesize_blocks, check, len + (nomsu and #tostring(nomsu) or 0))
- end
- local _exp_0 = tree.type
- if "FileChunks" == _exp_0 then
- return error("Can't inline a FileChunks")
- elseif "Comment" == _exp_0 then
- return NomsuCode(tree.source, "")
- elseif "Error" == _exp_0 then
- return error("Can't compile errors")
- elseif "Action" == _exp_0 then
- local nomsu = NomsuCode(tree.source)
- if tree.target then
- 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
- local clump_words = (type(tree[i - 1]) == 'string' and is_operator(bit) ~= is_operator(tree[i - 1]))
- if i > 1 and not clump_words then
- nomsu:append(" ")
- end
- nomsu:append(bit)
- else
- local arg_nomsu = recurse(bit, nomsu, parenthesize_blocks or (i == 1 or i < #tree))
- if not (arg_nomsu:match("^:") or i == 1) then
- nomsu:append(" ")
- end
- if bit.type == "Action" then
- arg_nomsu:parenthesize()
- end
- nomsu:append(arg_nomsu)
- end
- if check then
- check(len, nomsu, tree)
- end
- end
- return nomsu
- elseif "EscapedNomsu" == _exp_0 then
- local inner_nomsu = recurse(tree[1])
- if not (tree[1].type == "List" or tree[1].type == "Dict" or tree[1].type == "Var") then
- inner_nomsu:parenthesize()
- end
- local nomsu = NomsuCode(tree.source, "\\", inner_nomsu)
- if check then
- check(len, nomsu, tree)
- end
- return nomsu
- elseif "Block" == _exp_0 then
- local nomsu = NomsuCode(tree.source, ":")
- if check then
- check(len, nomsu, tree)
- end
- for i, line in ipairs(tree) do
- nomsu:append(i == 1 and " " or "; ")
- nomsu:append(recurse(line, nomsu, i == 1 or i < #tree))
- if check then
- check(len, nomsu, tree)
- end
- end
- if #tree > 1 or parenthesize_blocks then
- nomsu:parenthesize()
- end
- return nomsu
- elseif "Text" == _exp_0 then
- local add_text
- add_text = function(nomsu, tree)
- for i, bit in ipairs(tree) do
- if type(bit) == 'string' then
- local escaped = inline_escape(bit)
- nomsu:append(inline_escape(bit))
- elseif bit.type == "Text" then
- add_text(nomsu, bit)
- else
- local interp_nomsu = recurse(bit, nomsu)
- if bit.type ~= "Var" and bit.type ~= "List" and bit.type ~= "Dict" then
- interp_nomsu:parenthesize()
- elseif bit.type == "Var" and type(tree[i + 1]) == 'string' and not match(tree[i + 1], "^[ \n\t,.:;#(){}[%]]") then
- interp_nomsu:parenthesize()
- end
- nomsu:append("\\", interp_nomsu)
- end
- if check then
- check(len, nomsu, tree)
- end
- end
- end
- local nomsu = NomsuCode(tree.source)
- add_text(nomsu, tree)
- return NomsuCode(tree.source, '"', nomsu, '"')
- elseif "List" == _exp_0 or "Dict" == _exp_0 then
- local nomsu = NomsuCode(tree.source, (tree.type == "List" and "[" or "{"))
- for i, item in ipairs(tree) do
- if i > 1 then
- nomsu:append(", ")
- end
- nomsu:append(recurse(item, nomsu))
- if check then
- check(len, nomsu, tree)
- end
- end
- nomsu:append(tree.type == "List" and "]" or "}")
- return nomsu
- elseif "DictEntry" == _exp_0 then
- local key, value = tree[1], tree[2]
- local nomsu
- if key.type == "Text" and #key == 1 and is_identifier(key[1]) then
- nomsu = NomsuCode(key.source, key[1])
- else
- nomsu = recurse(key)
- end
- if key.type == "Action" or key.type == "Block" then
- nomsu:parenthesize()
- end
- assert(value.type ~= "Block", "Didn't expect to find a Block as a value in a dict")
- nomsu:append(":")
- if value then
- local value_nomsu = recurse(value, nomsu)
- if value.type == "Block" then
- value_nomsu:parenthesize()
- end
- nomsu:append(value_nomsu)
- end
- if check then
- check(len, nomsu, tree)
- end
- return nomsu
- elseif "IndexChain" == _exp_0 then
- local nomsu = NomsuCode(tree.source)
- for i, bit in ipairs(tree) do
- if i > 1 then
- nomsu:append(".")
- end
- local bit_nomsu
- if i > 1 and bit.type == "Text" and #bit == 1 and type(bit[1]) == 'string' and is_identifier(bit[1]) then
- bit_nomsu = bit[1]
- else
- bit_nomsu = recurse(bit, nomsu)
- end
- assert(bit.type ~= "Block")
- if bit.type == "Action" or bit.type == "IndexChain" or (bit.type == "Number" and i < #tree) then
- bit_nomsu:parenthesize()
- end
- nomsu:append(bit_nomsu)
- if check then
- check(len, nomsu, tree)
- end
- end
- return nomsu
- elseif "Number" == _exp_0 then
- return NomsuCode(tree.source, tostring(tree[1]))
- elseif "Var" == _exp_0 then
- return NomsuCode(tree.source, "%", tree[1])
- else
- return error("Unknown type: " .. tostring(tree.type))
- end
- end
- NomsuCompiler.tree_to_nomsu = function(self, tree, pop_comments)
- if pop_comments == nil then
- pop_comments = nil
- end
- if not (pop_comments) then
- local comment_set = { }
- local find_comments
- find_comments = function(t)
- if t.comments and t.source.filename == tree.source.filename then
- local _list_0 = t.comments
- for _index_0 = 1, #_list_0 do
- local c = _list_0[_index_0]
- comment_set[c] = true
- end
- end
- local _list_0 = t
- for _index_0 = 1, #_list_0 do
- local x = _list_0[_index_0]
- if SyntaxTree:is_instance(x) then
- find_comments(x)
- end
- end
- end
- find_comments(tree)
- local comments
- do
- local _accum_0 = { }
- local _len_0 = 1
- for c in pairs(comment_set) do
- _accum_0[_len_0] = c
- _len_0 = _len_0 + 1
- end
- comments = _accum_0
- end
- table.sort(comments, function(a, b)
- return (a.source.start > b.source.start)
- end)
- pop_comments = function(pos, prefix, suffix)
- if prefix == nil then
- prefix = ''
- end
- if suffix == nil then
- suffix = ''
- end
- local nomsu = NomsuCode(tree.source)
- for i = #comments, 1, -1 do
- if comments[i].source.start > pos then
- break
- end
- local comment
- comment, comments[i] = comments[i][1], nil
- nomsu:append("#" .. (gsub(comment, "\n", "\n ")) .. "\n")
- if comment:match("^\n.") then
- nomsu:append("\n")
- end
- end
- if #nomsu.bits == 0 then
- return ''
- end
- if not (prefix == '') then
- nomsu:prepend(prefix)
- end
- if not (suffix == '') then
- nomsu:append(suffix)
- end
- return nomsu
- end
- end
- local recurse
- recurse = function(t, pos)
- if pos == nil then
- pos = 0
- end
- if type(pos) ~= 'number' then
- pos = #tostring(pos):match("[ ]*([^\n]*)$")
- end
- local space = MAX_LINE - pos
- local inline
- for prefix, nomsu, tree in coroutine.wrap(function()
- inline = self:tree_to_inline_nomsu(t, false, coroutine.yield)
- end) do
- local len = #tostring(nomsu)
- if prefix + len > MAX_LINE then
- break
- end
- if tree.type == "Block" and (#tree > 1 or len > 20) then
- break
- end
- if tree.type == "Text" then
- local check_for_nl
- check_for_nl = function(tree)
- local found_nl = false
- for i, b in ipairs(tree) do
- if type(b) ~= 'string' and b.type == "Text" and check_for_nl(b) then
- return true
- end
- if i == 1 and type(b) == 'string' then
- b = b:match('^[\n]*(.*)')
- end
- found_nl = found_nl or (type(b) == 'string' and b:match('\n'))
- if found_nl and (type(b) ~= 'string' or b:match('[^\n]')) then
- return true
- end
- end
- end
- if check_for_nl(tree) then
- break
- end
- end
- end
- if inline and #tostring(inline) <= space then
- return inline
- end
- local indented = self:tree_to_nomsu(t, pop_comments, space)
- if t.type == "Action" and not (tree.type == "Block" or tree.type == "FileChunks") then
- indented = NomsuCode(t.source, "(..)\n ", pop_comments(t.source.start), indented)
- end
- return indented
- end
- local _exp_0 = tree.type
- if "FileChunks" == _exp_0 then
- local nomsu = NomsuCode(tree.source, pop_comments(tree.source.start))
- local should_clump
- should_clump = function(prev_line, line)
- if prev_line.type == "Action" and line.type == "Action" then
- if prev_line.stub == "use" then
- return line.stub == "use"
- end
- if prev_line.stub == "test" then
- return true
- end
- if line.stub == "test" then
- return false
- end
- end
- return not recurse(prev_line):is_multiline()
- end
- for chunk_no, chunk in ipairs(tree) do
- if chunk_no > 1 then
- nomsu:append("\n\n" .. tostring(("~"):rep(80)) .. "\n\n")
- end
- nomsu:append(pop_comments(chunk.source.start))
- if chunk.type == "Block" then
- for line_no, line in ipairs(chunk) do
- if line_no > 1 then
- if should_clump(chunk[line_no - 1], line) then
- nomsu:append("\n", pop_comments(line.source.start, '\n'))
- else
- nomsu:append("\n\n", pop_comments(line.source.start))
- end
- end
- nomsu:append(self:tree_to_nomsu(line, pop_comments))
- end
- nomsu:append(pop_comments(chunk.source.stop, '\n'))
- else
- nomsu:append(recurse(chunk))
- end
- end
- nomsu:append(pop_comments(tree.source.stop, '\n'))
- if not (nomsu:match("\n$")) then
- nomsu:append('\n')
- end
- return nomsu
- elseif "Action" == _exp_0 then
- local pos, next_space = tree.source.start, ''
- local nomsu = NomsuCode(tree.source, pop_comments(pos))
- if tree.target then
- if tree.target.type == "Block" then
- nomsu:append(recurse(tree.target, #nomsu:match('[^\n]*$')))
- pos = tree.target.source.stop
- next_space = inline and "::" or "\n..::"
- else
- local target_nomsu = recurse(tree.target, #nomsu:match('[^\n]*$'))
- if tree.target.type == "Action" and not target_nomsu:is_multiline() then
- target_nomsu:parenthesize()
- end
- nomsu:append(target_nomsu)
- pos = tree.target.source.stop
- next_space = target_nomsu:is_multiline() and "\n..::" or "::"
- end
- end
- for i, bit in ipairs(tree) do
- if next_space == "\n.." then
- nomsu:append("\n", pop_comments(pos), '..')
- next_space = ""
- elseif next_space == " " and nomsu:trailing_line_len() > MAX_LINE then
- nomsu:append(" \\\n", pop_comments(pos), '..')
- next_space = ""
- end
- if type(bit) == "string" then
- if not (type(tree[i - 1]) == 'string' and is_operator(tree[i - 1]) ~= is_operator(bit)) then
- nomsu:append(next_space)
- end
- nomsu:append(bit)
- next_space = ' '
- elseif bit.type == "Block" then
- nomsu:append(recurse(bit, #nomsu:match('[^\n]*$')))
- pos = bit.source.stop
- next_space = inline and " " or "\n.."
- else
- nomsu:append(next_space)
- local bit_nomsu = recurse(bit, #nomsu:match('[^\n]*$'))
- if bit.type == "Action" and not bit_nomsu:is_multiline() then
- bit_nomsu:parenthesize()
- end
- nomsu:append(bit_nomsu)
- pos = bit.source.stop
- next_space = bit_nomsu:is_multiline() and "\n.." or " "
- end
- end
- nomsu:append(pop_comments(tree.source.stop, '\n'))
- return nomsu
- elseif "EscapedNomsu" == _exp_0 then
- local val_nomsu = recurse(tree[1], 1)
- if tree[1].type == "Action" and not val_nomsu:is_multiline() then
- val_nomsu:parenthesize()
- end
- return NomsuCode(tree.source, "\\", val_nomsu)
- elseif "Block" == _exp_0 then
- local nomsu = NomsuCode(tree.source, pop_comments(tree.source.start))
- for i, line in ipairs(tree) do
- nomsu:append(pop_comments(line.source.start, i > 1 and '\n' or ''))
- local line_nomsu = recurse(line)
- nomsu:append(line_nomsu)
- if i < #tree then
- nomsu:append(line_nomsu:match('\n[^\n]*\n') and "\n\n" or "\n")
- end
- end
- nomsu:append(pop_comments(tree.source.stop, '\n'))
- return NomsuCode(tree.source, ":\n ", nomsu)
- elseif "Text" == _exp_0 then
- local max_line = math.floor(1.5 * MAX_LINE)
- local add_text
- add_text = function(nomsu, tree)
- for i, bit in ipairs(tree) do
- if type(bit) == 'string' then
- bit = escape(bit)
- local bit_lines = Files.get_lines(bit)
- for j, line in ipairs(bit_lines) do
- if j > 1 then
- nomsu:append("\n")
- elseif #line > 10 and nomsu:trailing_line_len() > max_line then
- nomsu:append("\\\n..")
- end
- while #line > 0 do
- local space = max_line - nomsu:trailing_line_len()
- local split = find(line, "[%p%s]", space)
- if not split or split > space + 10 then
- split = space + 10
- end
- if #line - split < 10 then
- split = #line
- end
- local bite
- bite, line = sub(line, 1, split), sub(line, split + 1, -1)
- nomsu:append(bite)
- if #line > 0 then
- nomsu:append("\\\n..")
- end
- end
- end
- elseif bit.type == "Text" then
- add_text(nomsu, bit)
- else
- nomsu:append("\\")
- local interp_nomsu = recurse(bit, #nomsu:match('[^\n]*$'))
- if not (interp_nomsu:is_multiline()) then
- if bit.type == "Var" then
- if type(tree[i + 1]) == 'string' and not match(tree[i + 1], "^[ \n\t,.:;#(){}[%]]") then
- interp_nomsu:parenthesize()
- end
- elseif bit.type ~= "List" and bit.type ~= "Dict" then
- interp_nomsu:parenthesize()
- end
- end
- nomsu:append(interp_nomsu)
- if interp_nomsu:is_multiline() then
- nomsu:append("\n..")
- end
- end
- end
- end
- local nomsu = NomsuCode(tree.source)
- add_text(nomsu, tree)
- return NomsuCode(tree.source, '"\\\n ..', nomsu, '"')
- elseif "List" == _exp_0 or "Dict" == _exp_0 then
- assert(#tree > 0)
- local nomsu = NomsuCode(tree.source, pop_comments(tree[1].source.start))
- for i, item in ipairs(tree) do
- if nomsu:trailing_line_len() == 0 then
- nomsu:append(pop_comments(item.source.start))
- end
- local inline_nomsu = self:tree_to_inline_nomsu(item)
- local item_nomsu = #tostring(inline_nomsu) <= MAX_LINE and inline_nomsu or recurse(item, #nomsu:match('[^\n]*$'))
- nomsu:append(item_nomsu)
- if i < #tree then
- nomsu:append((item_nomsu:is_multiline() or nomsu:trailing_line_len() + #tostring(item_nomsu) >= MAX_LINE) and '\n' or ', ')
- end
- end
- nomsu:append(pop_comments(tree.source.stop, '\n'))
- if tree.type == "List" then
- return NomsuCode(tree.source, "[..]\n ", nomsu)
- else
- return NomsuCode(tree.source, "{..}\n ", nomsu)
- end
- elseif "DictEntry" == _exp_0 then
- local key, value = tree[1], tree[2]
- local nomsu
- if key.type == "Text" and #key == 1 and is_identifier(key[1]) then
- nomsu = NomsuCode(key.source, key[1])
- else
- nomsu = self:tree_to_inline_nomsu(key)
- end
- if key.type == "Action" or key.type == "Block" then
- nomsu:parenthesize()
- end
- nomsu:append(": ", recurse(value, #tostring(nomsu)))
- return nomsu
- elseif "IndexChain" == _exp_0 or "Number" == _exp_0 or "Var" == _exp_0 or "Comment" == _exp_0 or "Error" == _exp_0 then
- return self:tree_to_inline_nomsu(tree)
- else
- return error("Unknown type: " .. tostring(tree.type))
- end
- end
-end
-return NomsuCompiler
+})
+return compile
diff --git a/nomsu_compiler.moon b/nomsu_compiler.moon
index 6fe6501..8c21eef 100644
--- a/nomsu_compiler.moon
+++ b/nomsu_compiler.moon
@@ -12,427 +12,153 @@
lpeg = require 'lpeg'
{:R,:P,:S} = lpeg
re = require 're'
-Files = require 'files'
{:List, :Dict, :Text} = require 'containers'
-export colors, colored
-colors = require 'consolecolors'
-colored = setmetatable({}, {__index:(_,color)-> ((msg)-> colors[color]..tostring(msg or '')..colors.reset)})
{:insert, :remove, :concat} = table
unpack or= table.unpack
{:match, :sub, :gsub, :format, :byte, :find} = string
-{:NomsuCode, :LuaCode, :Source} = require "code_obj"
+{:LuaCode, :Source} = require "code_obj"
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
--- from lua line number to nomsu line number
-export SOURCE_MAP
-SOURCE_MAP = {}
+{:Importer, :import_to_1_from, :_1_forked} = require 'importer'
table.map = (t, fn)-> setmetatable([fn(v) for _,v in ipairs(t)], getmetatable(t))
-table.fork = (t, values)-> setmetatable(values or {}, {__index:t})
-table.copy = (t)-> setmetatable({k,v for k,v in pairs(t)}, getmetatable(t))
-
--- Parsing helper functions
-utf8_char_patt = (
- 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"))
-operator_patt = S("'`~!@$^&*+=|<>?/-")^1 * -1
-identifier_patt = (R("az","AZ","09") + P("_") + utf8_char_patt)^1 * -1
-
-is_operator = (s)->
- return not not operator_patt\match(s)
-
-is_identifier = (s)->
- return not not identifier_patt\match(s)
-
-inline_escaper = re.compile("{~ (%utf8_char / ('\"' -> '\\\"') / ('\n' -> '\\n') / ('\t' -> '\\t') / ('\b' -> '\\b') / ('\a' -> '\\a') / ('\v' -> '\\v') / ('\f' -> '\\f') / ('\r' -> '\\r') / ('\\' -> '\\\\') / ([^ -~] -> escape) / .)* ~}", {utf8_char: utf8_char_patt, escape:(=> ("\\%03d")\format(@byte!))})
-inline_escape = (s)->
- return inline_escaper\match(s)
-
-escaper = re.compile("{~ (%utf8_char / ('\\' -> '\\\\') / [\n\r\t -~] / (. -> escape))* ~}",
- {utf8_char: utf8_char_patt, escape:(=> ("\\%03d")\format(@byte!))})
-escape = (s)->
- return escaper\match(s)
-- TODO:
-- Re-implement nomsu-to-lua comment translation?
-make_tree = (tree, userdata)->
- tree.source = Source(userdata.filename, tree.start, tree.stop)
- tree.start, tree.stop = nil, nil
- 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 SyntaxTree\is_instance(tree[i]) and tree[i].type == "Comment"
- table.remove(tree, i)
- tree = SyntaxTree(tree)
- return tree
-
-Parsers = {}
-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
- .can_optimize = -> false
-
- -- Discretionary/convenience stuff
- .environment = {
- 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, lua_type_of:type, :select, :math, :io, :load,
- :pairs, :ipairs,
- -- Nomsu types:
- List:List, Dict:Dict,
- -- Utilities and misc.
- lpeg:lpeg, re:re, Files:Files,
- :SyntaxTree, TESTS: Dict({}), globals: Dict({}),
- :LuaCode, :NomsuCode, :Source
- nomsu:NomsuCompiler
- __imported: Dict{}
- __parent: nil
+-- TODO: de-duplicate this
+pretty_error = require("pretty_errors")
+compile_error = (tree, err_msg, hint=nil)->
+ err_str = pretty_error{
+ title: "Compile error"
+ error:err_msg, hint:hint, source:tree\get_source_code!
+ start:tree.source.start, stop:tree.source.stop, filename:tree.source.filename
}
- setmetatable(.environment, {
- __index: (key)=>
- if imported = rawget(@, "__imported")
- ret = imported[key]
- return ret unless ret == nil
- if parent = rawget(@, "__parent")
- return parent[key]
- })
-
- if _VERSION == "Lua 5.4"
- .environment.ipairs = (x)->
- if mt = getmetatable(x)
- if mt.__ipairs then return mt.__ipairs(x)
- return ipairs(x)
- if jit or _VERSION == "Lua 5.2"
- .environment.bit = require("bitops")
-
- .fork = =>
- f = setmetatable({}, {__index:@})
- f.environment = setmetatable({
- __parent: @environment
- __imported: Dict{}
- nomsu: f
- COMPILE_ACTIONS: setmetatable({
- __parent: @environment.COMPILE_ACTIONS
- __imported: Dict{}
- }, getmetatable(@environment))
- }, getmetatable(@environment))
- return f
-
- .parse = (nomsu_code, source=nil, version=nil)=>
- source or= nomsu_code.source
- nomsu_code = tostring(nomsu_code)
- unless source
- source = Source("anonymous chunk ##{_anon_chunk}", 1, #nomsu_code)
- _anon_chunk += 1
- version or= nomsu_code\match("^#![^\n]*nomsu[ ]+-V[ ]*([0-9.]+)")
- syntax_version = version and tonumber(version\match("^[0-9]+")) or max_parser_version
- parse = Parsers[syntax_version] or Parsers[max_parser_version]
- tree = parse(nomsu_code, source.filename)
- find_errors = (t)->
- if t.type == "Error"
- coroutine.yield t
- else
- for k,v in pairs(t)
- continue unless SyntaxTree\is_instance(v)
- find_errors(v)
-
- errs = [err for err in coroutine.wrap(-> find_errors(tree))]
- num_errs = #errs
- if num_errs > 0
- err_strings = [pretty_error{
- title:"Parse error"
- error:t.error, hint:t.hint, source:t\get_source_code!
- start:t.source.start, stop:t.source.stop, filename:t.source.filename
- } for i, t in ipairs(errs) when i <= 3]
- if num_errs > 3
- table.insert(err_strings, "\027[31;1m +#{num_errs-#errs} additional errors...\027[0m\n")
- error(table.concat(err_strings, '\n\n'), 0)
- return tree
-
- .compile_error = (tree, err_msg, hint=nil)=>
- err_str = pretty_error{
- title: "Compile error"
- error:err_msg, hint:hint, source:tree\get_source_code!
- start:tree.source.start, stop:tree.source.stop, filename:tree.source.filename
- }
- error(err_str, 0)
-
- add_lua_bits = (val_or_stmt, code, compile_actions)=>
- cls = val_or_stmt == "value" and LuaCode.Value or LuaCode
- operate_on_text = (text)->
- lua = cls(text.source)
- for bit in *text
- if type(bit) == "string"
- lua\append bit
- elseif bit.type == "Text"
- lua\append(operate_on_text(bit))
- else
- bit_lua = @compile(bit, compile_actions)
- unless bit_lua.is_value
- @compile_error bit,
- "Can't use this as a string interpolation value, since it's not an expression."
- lua\append bit_lua
- return lua
- return operate_on_text code
-
- 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, 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, tostring(text.source)\as_lua!)
- for bit in *text
- if type(bit) == "string"
- add_bit_lua(lua, bit\as_lua!)
- elseif bit.type == "Text"
- add_bit_lua(lua, operate_on_text(bit))
- else
- bit_lua = @compile(bit)
- unless bit_lua.is_value
- @compile_error bit,
- "Can't use this as a string interpolation value, since it's not an expression."
- add_bit_lua(lua, bit_lua)
- lua\append ")"
- return lua
- return operate_on_text code
+ error(err_str, 0)
+
+
+-- 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]+) " ")* [*/^+-] !. ]]
+compile_math_expression = (compile, tree, ...)->
+ lua = LuaCode(tree.source)
+ for i,tok in ipairs tree
+ if type(tok) == 'string'
+ lua\append tok
+ else
+ tok_lua = compile(tok)
+ tok_lua\parenthesize! if tok.type == "Action"
+ lua\append tok_lua
+ lua\append " " if i < #tree
+ return lua
- -- 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]+) " ")* [*/^+-] !. ]]
- compile_math_expression = (tree, ...)=>
- lua = LuaCode.Value(tree.source)
- for i,tok in ipairs tree
- if type(tok) == 'string'
- lua\append tok
- else
- tok_lua = @compile(tok, compile_actions)
- unless tok_lua.is_value
- @compile_error tok,
- "Can't use this as a value in a math expression, since it's not a value."
- tok_lua\parenthesize! if tok.type == "Action"
- lua\append tok_lua
- lua\append " " if i < #tree
- return lua
- .environment.COMPILE_ACTIONS = setmetatable({
- __imported: Dict{}
- [""]: (tree, fn, ...)=>
- lua = LuaCode.Value(tree.source)
- lua\append @compile(fn, compile_actions)
- lua\parenthesize! unless lua\text!\is_lua_id!
+MAX_LINE = 80 -- For beautification purposes, try not to make lines much longer than this value
+compile = setmetatable({
+ action: Importer{
+ [""]: (compile, tree, fn, ...)->
+ lua = LuaCode(tree.source)
+ fn_lua = compile(fn)
+ lua\append fn_lua
+ unless fn_lua\text!\match("^%(.*%)$") or fn_lua\text!\match("^[_a-zA-Z][_a-zA-Z0-9.]*$")
+ lua\parenthesize!
lua\append "("
for i=1,select('#',...)
lua\append(", ") if i > 1
- lua\append @compile(select(i, ...), compile_actions)
+ lua\append compile(select(i, ...))
lua\append ")"
return lua
- ["Lua"]: (tree, code)=>
- return add_lua_string_bits(@, 'statements', code)
-
- ["Lua value"]: (tree, code)=>
- return add_lua_string_bits(@, 'value', 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"]: (tree, code)=>
+ ["Lua"]: (compile, 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"]: (tree, path)=>
- if path.type == 'Text' and #path == 1 and type(path[1]) == 'string'
- 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"]: (tree, body)=>
- test_str = table.concat [tostring(@tree_to_nomsu(line)) for line in *body], "\n"
- 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, _VERSION\as_lua!)
-
- __parent: setmetatable({}, {
- __index: (key)=>
- if type(key) == 'string' and math_expression\match(key)
- return compile_math_expression
- })
- }, getmetatable(.environment))
-
- .import = (mod)=>
- for k,v in pairs(mod)
- continue if k == "__imported" or k == "__parent"
- @environment.__imported[k] = v
- for k,v in pairs(mod.COMPILE_ACTIONS)
- 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
+ return LuaCode(code.source, "LuaCode(", 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(text.source, "LuaCode(", tostring(text.source)\as_lua!)
+ for bit in *text
+ if type(bit) == "string"
+ add_bit_lua(lua, bit\as_lua!)
+ elseif bit.type == "Text"
+ add_bit_lua(lua, operate_on_text(bit))
+ else
+ add_bit_lua(lua, compile(bit))
+ lua\append ")"
+ return lua
+ return operate_on_text code
- .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 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"
- tree = {tree}
- -- Each chunk's compilation is affected by the code in the previous chunks
- -- (typically), so each chunk needs to compile and run before the next one
- -- compiles.
- ret = nil
- all_lua = {}
- for chunk in *tree
- lua = @compile(chunk, compile_actions)\as_statements("return ")
- lua\declare_locals!
- lua\prepend "-- File: #{source.filename\gsub("\n.*", "...")}\n"
- insert all_lua, tostring(lua)
- ret = @run_lua(lua)
- return ret
+ -- TODO: remove shim
+ ["Lua value"]: (compile, tree, code)-> compile.action["Lua"](compile, tree, code)
- _running_files = {} -- For detecting circular imports
- _loaded_files = {}
- .run_file = (filename, compile_actions)=>
- -- Filename should be an absolute path, i.e. package.nomsupath will not be searched for it
- if _loaded_files[filename]
- return _loaded_files[filename]
- -- Check for circular import
- -- TODO: optimize?
- for i,running in ipairs _running_files
- if running == filename
- loop = [_running_files[j] for j=i,#_running_files]
- insert loop, filename
- error("Circular import, this loops forever: #{concat loop, " -> "}...")
+ ["lua >"]: (compile, tree, code)->
+ if code.type != "Text"
+ return tree
+ operate_on_text = (text)->
+ lua = LuaCode(text.source)
+ for bit in *text
+ if type(bit) == "string"
+ lua\append bit
+ elseif bit.type == "Text"
+ lua\append(operate_on_text(bit))
+ else
+ lua\append compile(bit)
+ return lua
+ return operate_on_text code
- insert _running_files, filename
- mod = @fork!
- ret = mod.environment
- mod.from_file = filename
- if match(filename, "%.lua$")
- file = assert(Files.read(filename), "Could not find file: #{filename}")
- ret = mod\run_lua(LuaCode(Source(filename, 1, #file), file)) or ret
- elseif match(filename, "%.nom$") or match(filename, "^/dev/fd/[012]$")
- ran_lua = if @.can_optimize(filename) -- Look for precompiled version
- lua_filename = gsub(filename, "%.nom$", ".lua")
- if file = Files.read(lua_filename)
- ret = mod\run_lua(LuaCode(Source(lua_filename, 1, #file), file)) or ret
- true
- unless ran_lua
- file = Files.read(filename)
- if not file
- error("Tried to run file that does not exist: #{filename}")
- ret = mod\run(NomsuCode(Source(filename,1,#file), file), compile_actions) or ret
- else
- error("Invalid filetype for #{filename}", 0)
- _loaded_files[filename] = ret
- remove _running_files
- return ret
+ ["= lua"]: (compile, tree, code)-> compile.action["lua >"](compile, tree, code)
- .run_lua = (lua)=>
- lua_string = tostring(lua)
- run_lua_fn, err = load(lua_string, tostring(source or lua.source), "t", @environment)
- if not run_lua_fn
- line_numbered_lua = concat(
- [format("%3d|%s",i,line) for i, line in ipairs Files.get_lines(lua_string)],
- "\n")
- error("Failed to compile generated code:\n#{colored.bright colored.blue colored.onblack line_numbered_lua}\n\n#{err}", 0)
- source = lua.source or Source(lua_string, 1, #lua_string)
- source_key = tostring(source)
- unless SOURCE_MAP[source_key]
- map = {}
- file = Files.read(source.filename)
- if not file
- error "Failed to find file: #{source.filename}"
- nomsu_str = file\sub(source.start, source.stop)
- assert type(nomsu_str) == 'string'
- lua_line = 1
- nomsu_line = Files.get_line_number(file, source.start)
- map_sources = (s)->
- if type(s) == 'string'
- for nl in s\gmatch("\n")
- map[lua_line] or= nomsu_line
- lua_line += 1
- else
- if s.source and s.source.filename == source.filename
- nomsu_line = Files.get_line_number(file, s.source.start)
- for b in *s.bits do map_sources(b)
- map_sources(lua)
- map[lua_line] or= nomsu_line
- map[0] = 0
- -- Mapping from lua line number to nomsu line numbers
- SOURCE_MAP[source_key] = map
+ ["use"]: (compile, tree, path)->
+ --if path.type == 'Text' and #path == 1 and type(path[1]) == 'string'
+ -- unless import_to_1_from(compile, path[1])
+ -- compile_error tree, "Could not find anything to import for #{path}"
+ return LuaCode(tree.source, "run_file_1_in(#{compile(path)}, _ENV)")
- return run_lua_fn!
+ ["tests"]: (compile, tree)-> LuaCode(tree.source, "TESTS")
+ ["test"]: (compile, tree, body)->
+ -- TODO: maybe go back to storing nomsu code instead of syntax tree
+ LuaCode tree.source, "TESTS[#{tostring(tree.source)\as_lua!}] = ", body\as_lua!
- .compile = (tree, compile_actions, force_value=false)=>
- compile_actions or= @environment.COMPILE_ACTIONS
+ ["is jit"]: (compile, tree, code)-> LuaCode(tree.source, "jit")
+ ["Lua version"]: (compile, tree, code)-> LuaCode(tree.source, "_VERSION")
+ ["nomsu environment"]: (compile, tree)-> LuaCode(tree.source, "_ENV")
+ }
+}, {
+ __import: (other)=>
+ import_to_1_from(@action, other.action)
+ return
+ __call: (compile, tree, force_value=false)->
if tree.version
- if get_version = @[("Nomsu version")\as_lua_id!]
- if upgrade = @[("1 upgraded from 2 to")\as_lua_id!]
+ if get_version = compile.action[("Nomsu version")\as_lua_id!]
+ if upgrade = compile.action[("1 upgraded from 2 to")\as_lua_id!]
tree = upgrade(tree, tree.version, get_version!)
switch tree.type
when "Action"
stub = tree.stub
- compile_action = compile_actions[stub]
+ compile_action = compile.action[stub]
+ if not compile_action and math_expression\match(stub)
+ compile_action = compile_math_expression
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?
- ret = compile_action(@, tree, unpack(args))
+ ret = compile_action(compile, tree, unpack(args))
if ret == nil
info = debug.getinfo(compile_action, "S")
filename = Source\from_string(info.source).filename
- @compile_error tree,
+ 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."
unless SyntaxTree\is_instance(ret)
return ret
if ret != tree
- return @compile(ret, compile_actions)
+ return compile(ret)
- lua = LuaCode.Value(tree.source)
+ lua = LuaCode(tree.source)
if tree.target -- Method call
- target_lua = @compile tree.target, compile_actions
- if tostring(target_lua)\match("^%(.*%)$") or tostring(target_lua)\match("^[_a-zA-Z][_a-zA-Z0-9]*$")
+ target_lua = compile tree.target
+ target_text = target_lua\text!
+ if target_text\match("^%(.*%)$") or target_text\match("^[_a-zA-Z][_a-zA-Z0-9.]*$")
lua\append target_lua, ":"
else
lua\append "(", target_lua, "):"
@@ -440,33 +166,20 @@ with NomsuCompiler
args = {}
for i, tok in ipairs tree
if type(tok) == "string" then continue
- arg_lua = @compile(tok, compile_actions, true)
- unless arg_lua.is_value
- if tok.type == "Block"
- @compile_error tok,
- "Can't compile action (#{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"
- @compile_error tok,
- "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: #{tostring(arg_lua)}"
+ arg_lua = compile(tok, true)
insert args, arg_lua
lua\concat_append args, ", "
lua\append ")"
return lua
when "EscapedNomsu"
- lua = LuaCode.Value tree.source, "SyntaxTree{"
+ lua = LuaCode tree.source, "SyntaxTree{"
needs_comma, i = false, 1
as_lua = (x)->
if type(x) == 'number'
tostring(x)
elseif SyntaxTree\is_instance(x)
- @compile(x, compile_actions)
+ compile(x)
else x\as_lua!
for k,v in pairs((SyntaxTree\is_instance(tree[1]) and tree[1].type == "EscapedNomsu" and tree) or tree[1])
@@ -488,29 +201,18 @@ with NomsuCompiler
when "Block"
if not force_value
lua = LuaCode(tree.source)
- lua\concat_append([@compile(line, compile_actions)\as_statements! for line in *tree], "\n")
+ lua\concat_append([compile(line) for line in *tree], "\n")
return lua
else
- lua = LuaCode.Value(tree.source)
- values = [@compile(line) for line in *tree]
- all_values = true
- for v in *values do all_values and= v.is_value
- if all_values
- return values[1] if #values == 1
- lua\append "("
- lua\concat_append(values, " and nil or ")
- lua\append ")"
- else
- lua\append("((function()")
- for i, v in ipairs(values)
- if v.is_value
- v = v\as_statements(i == #values and 'return ' or '')
- lua\append "\n ", v
- lua\append("\nend)())")
+ lua = LuaCode(tree.source)
+ lua\append("((function()")
+ for i, line in ipairs(tree)
+ lua\append "\n ", compile(line)
+ lua\append("\nend)())")
return lua
when "Text"
- lua = LuaCode.Value(tree.source)
+ lua = LuaCode(tree.source)
string_buffer = ""
for i, bit in ipairs tree
if type(bit) == "string"
@@ -520,15 +222,10 @@ with NomsuCompiler
if #lua.bits > 0 then lua\append ".."
lua\append string_buffer\as_lua!
string_buffer = ""
- bit_lua = @compile(bit, compile_actions)
- unless bit_lua.is_value
- src = ' '..gsub(tostring(@compile(bit, compile_actions)), '\n','\n ')
- line = "#{bit.source.filename}:#{Files.get_line_number(Files.read(bit.source.filename), bit.source.start)}"
- @compile_error bit,
- "Can't this as a string interpolation value, since it's not an expression."
+ bit_lua = compile(bit)
if #lua.bits > 0 then lua\append ".."
if bit.type != "Text"
- bit_lua = LuaCode.Value(bit.source, "tostring(",bit_lua,")")
+ bit_lua = LuaCode(bit.source, "tostring(",bit_lua,")")
lua\append bit_lua
if string_buffer ~= "" or #lua.bits == 0
@@ -539,32 +236,50 @@ with NomsuCompiler
lua\parenthesize!
return lua
- when "List"
- lua = LuaCode.Value tree.source, "List{"
- lua\concat_append([@compile(e, compile_actions) for e in *tree], ", ", ",\n ")
- lua\append "}"
- return lua
-
- when "Dict"
- lua = LuaCode.Value tree.source, "Dict{"
- lua\concat_append([@compile(e, compile_actions) for e in *tree], ", ", ",\n ")
+ when "List", "Dict"
+ lua = LuaCode tree.source, "#{tree.type}{"
+ i = 1
+ sep = ''
+ while i <= #tree
+ item = tree[i]
+ if item.type == "Block"
+ break
+ lua\append sep
+ if item.type == "Comment"
+ lua\append compile(item), "\n"
+ sep = ''
+ else
+ item_lua = compile(item)
+ lua\append item_lua
+ sep = ', '
+ i += 1
lua\append "}"
+ if i <= #tree
+ lua = LuaCode tree.source, "(function()\n local it = ", lua
+ while i <= #tree
+ lua\append "\n "
+ if tree[i].type == 'Block' or tree[i].type == 'Comment'
+ lua\append compile(tree[i])
+ elseif tree[i].type == "DictEntry"
+ lua\append "it[ ", compile(tree[i][1]), "] = ", (tree[i][2] and compile(tree[i][2]) or "true")
+ else
+ lua\append "it:add(", compile(tree[i]), ")"
+ i += 1
+ lua\append "\n return it\nend)()"
return lua
+ --lua = LuaCode tree.source, "#{tree.type}{"
+ --lua\concat_append([compile(e) for e in *tree when e.type != 'Comment'], ", ", ",\n ")
+ --lua\append "}"
+ --return lua
when "DictEntry"
key, value = tree[1], tree[2]
- key_lua = @compile(key, compile_actions)
- unless key_lua.is_value
- @compile_error tree[1],
- "Can't use this as a dict key, since it's not an expression."
- value_lua = value and @compile(value, compile_actions) or LuaCode.Value(key.source, "true")
- unless value_lua.is_value
- @compile_error tree[2],
- "Can't use this as a dict value, since it's not an expression."
- key_str = match(tostring(key_lua), [=[^["']([a-zA-Z_][a-zA-Z0-9_]*)['"]$]=])
+ key_lua = compile(key)
+ value_lua = value and compile(value) or LuaCode(key.source, "true")
+ key_str = match(key_lua\text!, [=[^["']([a-zA-Z_][a-zA-Z0-9_]*)['"]$]=])
return if key_str and key_str\is_lua_id!
LuaCode tree.source, key_str,"=",value_lua
- elseif sub(tostring(key_lua),1,1) == "["
+ elseif sub(key_lua\text!,1,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"]
@@ -573,21 +288,15 @@ with NomsuCompiler
LuaCode tree.source, "[",key_lua,"]=",value_lua
when "IndexChain"
- lua = @compile(tree[1], compile_actions)
- unless lua.is_value
- @compile_error tree[1],
- "Can't index into this, since it's not an expression."
- first_char = sub(tostring(lua),1,1)
+ lua = compile(tree[1])
+ first_char = sub(lua\text!,1,1)
if first_char == "{" or first_char == '"' or first_char == "["
lua\parenthesize!
for i=2,#tree
key = tree[i]
- key_lua = @compile(key, compile_actions)
- unless key_lua.is_value
- @compile_error key,
- "Can't use this as an index, since it's not an expression."
- key_lua_str = tostring(key_lua)
+ key_lua = compile(key)
+ key_lua_str = key_lua\text!
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}"
@@ -601,10 +310,10 @@ with NomsuCompiler
return lua
when "Number"
- return LuaCode.Value(tree.source, tostring(tree[1]))
+ return LuaCode(tree.source, tostring(tree[1]))
when "Var"
- return LuaCode.Value(tree.source, (tree[1])\as_lua_id!)
+ return LuaCode(tree.source, (tree[1])\as_lua_id!)
when "FileChunks"
error("Can't convert FileChunks to a single block of lua, since each chunk's "..
@@ -620,336 +329,6 @@ with NomsuCompiler
else
error("Unknown type: #{tree.type}")
- .tree_to_inline_nomsu = (tree, parenthesize_blocks=false, check=nil, len=0)=>
- recurse = (tree, nomsu=nil, parenthesize_blocks=false)->
- @tree_to_inline_nomsu(tree, parenthesize_blocks, check, len + (nomsu and #tostring(nomsu) or 0))
- switch tree.type
- when "FileChunks"
- error("Can't inline a FileChunks")
-
- when "Comment"
- -- TODO: implement?
- return NomsuCode(tree.source, "")
-
- when "Error"
- error("Can't compile errors")
-
- when "Action"
- nomsu = NomsuCode(tree.source)
- if 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]))
- nomsu\append " " if i > 1 and not clump_words
- nomsu\append bit
- else
- arg_nomsu = recurse(bit, nomsu, parenthesize_blocks or (i == 1 or i < #tree))
- nomsu\append " " unless arg_nomsu\match("^:") or i == 1
- arg_nomsu\parenthesize! if bit.type == "Action"
- nomsu\append arg_nomsu
- check(len, nomsu, tree) if check
- return nomsu
-
- when "EscapedNomsu"
- inner_nomsu = recurse(tree[1])
- unless tree[1].type == "List" or tree[1].type == "Dict" or tree[1].type == "Var"
- inner_nomsu\parenthesize!
- nomsu = NomsuCode(tree.source, "\\", inner_nomsu)
- check(len, nomsu, tree) if check
- return nomsu
-
- when "Block"
- nomsu = NomsuCode(tree.source, ":")
- check(len, nomsu, tree) if check
- for i,line in ipairs tree
- nomsu\append(i == 1 and " " or "; ")
- nomsu\append recurse(line, nomsu, i == 1 or i < #tree)
- check(len, nomsu, tree) if check
- nomsu\parenthesize! if #tree > 1 or parenthesize_blocks
- return nomsu
-
- when "Text"
- add_text = (nomsu, tree)->
- for i, bit in ipairs tree
- if type(bit) == 'string'
- escaped = inline_escape(bit)
- nomsu\append inline_escape(bit)
- elseif bit.type == "Text"
- add_text(nomsu, bit)
- else
- interp_nomsu = recurse(bit, nomsu)
- if bit.type != "Var" and bit.type != "List" and bit.type != "Dict"
- interp_nomsu\parenthesize!
- elseif bit.type == "Var" and type(tree[i+1]) == 'string' and not match(tree[i+1], "^[ \n\t,.:;#(){}[%]]")
- interp_nomsu\parenthesize!
- nomsu\append "\\", interp_nomsu
- check(len, nomsu, tree) if check
- nomsu = NomsuCode(tree.source)
- add_text(nomsu, tree)
- return NomsuCode(tree.source, '"', nomsu, '"')
-
- when "List", "Dict"
- nomsu = NomsuCode(tree.source, (tree.type == "List" and "[" or "{"))
- for i, item in ipairs tree
- nomsu\append ", " if i > 1
- nomsu\append recurse(item, nomsu)
- check(len, nomsu, tree) if check
- nomsu\append(tree.type == "List" and "]" or "}")
- return nomsu
-
- when "DictEntry"
- key, value = tree[1], tree[2]
- nomsu = if key.type == "Text" and #key == 1 and is_identifier(key[1])
- NomsuCode(key.source, key[1])
- else recurse(key)
- nomsu\parenthesize! if key.type == "Action" or key.type == "Block"
- assert(value.type != "Block", "Didn't expect to find a Block as a value in a dict")
- nomsu\append ":"
- if value
- value_nomsu = recurse(value, nomsu)
- value_nomsu\parenthesize! if value.type == "Block"
- nomsu\append value_nomsu
- check(len, nomsu, tree) if check
- return nomsu
-
- when "IndexChain"
- nomsu = NomsuCode(tree.source)
- for i, bit in ipairs tree
- nomsu\append "." if i > 1
- local bit_nomsu
- bit_nomsu = if i > 1 and bit.type == "Text" and #bit == 1 and type(bit[1]) == 'string' and is_identifier(bit[1])
- bit[1]
- else recurse(bit, nomsu)
- assert bit.type != "Block"
- if bit.type == "Action" or bit.type == "IndexChain" or (bit.type == "Number" and i < #tree)
- bit_nomsu\parenthesize!
- nomsu\append bit_nomsu
- check(len, nomsu, tree) if check
- return nomsu
-
- when "Number"
- return NomsuCode(tree.source, tostring(tree[1]))
-
- when "Var"
- return NomsuCode(tree.source, "%", tree[1])
-
- else
- error("Unknown type: #{tree.type}")
-
- .tree_to_nomsu = (tree, pop_comments=nil)=>
- unless pop_comments
- comment_set = {}
- 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 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]
- table.sort(comments, (a,b)->(a.source.start > b.source.start))
-
- pop_comments = (pos, prefix='', suffix='')->
- nomsu = NomsuCode(tree.source)
- for i=#comments,1,-1
- break if comments[i].source.start > pos
- comment, comments[i] = comments[i][1], nil
- nomsu\append("#"..(gsub(comment, "\n", "\n ")).."\n")
- if comment\match("^\n.") then nomsu\append("\n") -- for aesthetics
- return '' if #nomsu.bits == 0
- nomsu\prepend(prefix) unless prefix == ''
- nomsu\append(suffix) unless suffix == ''
- return nomsu
-
- -- For concision:
- recurse = (t, pos)->
- if pos == nil then pos = 0
- if type(pos) != 'number' then pos = #tostring(pos)\match("[ ]*([^\n]*)$")
- space = MAX_LINE - pos
- local inline
- for prefix, nomsu, tree in coroutine.wrap(-> inline = @tree_to_inline_nomsu(t, false, coroutine.yield))
- len = #tostring(nomsu)
- break if prefix+len > MAX_LINE
- break if tree.type == "Block" and (#tree > 1 or len > 20)
- if tree.type == "Text"
- -- Disallow inline text if it's got newlines between text, e.g. "hello\nworld"
- check_for_nl = (tree)->
- found_nl = false
- for i,b in ipairs tree
- return true if type(b) != 'string' and b.type == "Text" and check_for_nl(b)
- b = b\match('^[\n]*(.*)') if i == 1 and type(b) == 'string'
- found_nl or= type(b) == 'string' and b\match('\n')
- return true if found_nl and (type(b) != 'string' or b\match('[^\n]'))
- break if check_for_nl(tree)
- return inline if inline and #tostring(inline) <= space
- indented = @tree_to_nomsu(t, pop_comments, space)
- if t.type == "Action" and not (tree.type == "Block" or tree.type == "FileChunks")
- indented = NomsuCode(t.source, "(..)\n ", pop_comments(t.source.start), indented)
- return indented
-
- switch tree.type
- when "FileChunks"
- nomsu = NomsuCode(tree.source, pop_comments(tree.source.start))
- should_clump = (prev_line, line)->
- if prev_line.type == "Action" and line.type == "Action"
- if prev_line.stub == "use" then return line.stub == "use"
- if prev_line.stub == "test" then return true
- if line.stub == "test" then return false
- 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
- nomsu\append pop_comments(chunk.source.start)
- if chunk.type == "Block"
- for line_no, line in ipairs chunk
- if line_no > 1
- if should_clump(chunk[line_no-1], line)
- nomsu\append "\n", pop_comments(line.source.start, '\n')
- else
- nomsu\append "\n\n", pop_comments(line.source.start)
- nomsu\append @tree_to_nomsu(line, pop_comments)
- nomsu\append pop_comments(chunk.source.stop, '\n')
- else
- nomsu\append recurse(chunk)
- nomsu\append pop_comments(tree.source.stop, '\n')
- nomsu\append('\n') unless nomsu\match("\n$")
- return nomsu
-
- when "Action"
- pos, next_space = tree.source.start, ''
- nomsu = NomsuCode(tree.source, pop_comments(pos))
- if tree.target
- if tree.target.type == "Block"
- nomsu\append(recurse(tree.target, #nomsu\match('[^\n]*$')))
- pos = tree.target.source.stop
- next_space = inline and "::" or "\n..::"
- else
- target_nomsu = recurse(tree.target, #nomsu\match('[^\n]*$'))
- if tree.target.type == "Action" and not target_nomsu\is_multiline!
- target_nomsu\parenthesize!
- nomsu\append target_nomsu
- pos = tree.target.source.stop
- next_space = target_nomsu\is_multiline! and "\n..::" or "::"
-
- for i,bit in ipairs tree
- if next_space == "\n.."
- nomsu\append "\n", pop_comments(pos), '..'
- next_space = ""
- elseif next_space == " " and nomsu\trailing_line_len! > MAX_LINE
- nomsu\append " \\\n", pop_comments(pos), '..'
- next_space = ""
-
- if type(bit) == "string"
- unless type(tree[i-1]) == 'string' and is_operator(tree[i-1]) != is_operator(bit)
- nomsu\append(next_space)
- nomsu\append bit
- next_space = ' '
- elseif bit.type == "Block"
- nomsu\append(recurse(bit, #nomsu\match('[^\n]*$')))
- pos = bit.source.stop
- next_space = inline and " " or "\n.."
- else
- nomsu\append next_space
- bit_nomsu = recurse(bit, #nomsu\match('[^\n]*$'))
- if bit.type == "Action" and not bit_nomsu\is_multiline!
- bit_nomsu\parenthesize!
- nomsu\append bit_nomsu
- pos = bit.source.stop
- next_space = bit_nomsu\is_multiline! and "\n.." or " "
-
- nomsu\append pop_comments(tree.source.stop, '\n')
- return nomsu
-
- when "EscapedNomsu"
- val_nomsu = recurse(tree[1], 1)
- if tree[1].type == "Action" and not val_nomsu\is_multiline!
- val_nomsu\parenthesize!
- return NomsuCode tree.source, "\\", val_nomsu
-
- when "Block"
- nomsu = NomsuCode(tree.source, pop_comments(tree.source.start))
- for i, line in ipairs tree
- nomsu\append pop_comments(line.source.start, i > 1 and '\n' or '')
- line_nomsu = recurse(line)
- nomsu\append line_nomsu
- if i < #tree
- nomsu\append(line_nomsu\match('\n[^\n]*\n') and "\n\n" or "\n")
- nomsu\append pop_comments(tree.source.stop, '\n')
- return NomsuCode(tree.source, ":\n ", nomsu)
-
- when "Text"
- -- Multi-line text has more generous wrap margins
- max_line = math.floor(1.5*MAX_LINE)
- add_text = (nomsu, tree)->
- for i, bit in ipairs tree
- if type(bit) == 'string'
- bit = escape(bit)
- bit_lines = Files.get_lines(bit)
- for j, line in ipairs bit_lines
- if j > 1
- nomsu\append "\n"
- elseif #line > 10 and nomsu\trailing_line_len! > max_line
- nomsu\append "\\\n.."
-
- while #line > 0
- space = max_line - nomsu\trailing_line_len!
- split = find(line, "[%p%s]", space)
- if not split or split > space + 10
- split = space + 10
- if #line - split < 10
- split = #line
- bite, line = sub(line, 1, split), sub(line, split+1, -1)
- nomsu\append bite
- nomsu\append "\\\n.." if #line > 0
- elseif bit.type == "Text"
- add_text(nomsu, bit)
- else
- nomsu\append "\\"
- interp_nomsu = recurse(bit, #nomsu\match('[^\n]*$'))
- unless interp_nomsu\is_multiline!
- if bit.type == "Var"
- if type(tree[i+1]) == 'string' and not match(tree[i+1], "^[ \n\t,.:;#(){}[%]]")
- interp_nomsu\parenthesize!
- elseif bit.type != "List" and bit.type != "Dict"
- interp_nomsu\parenthesize!
- nomsu\append interp_nomsu
- if interp_nomsu\is_multiline!
- nomsu\append "\n.."
- nomsu = NomsuCode(tree.source)
- add_text(nomsu, tree)
- return NomsuCode(tree.source, '"\\\n ..', nomsu, '"')
-
- when "List", "Dict"
- assert #tree > 0
- nomsu = NomsuCode(tree.source, pop_comments(tree[1].source.start))
- for i, item in ipairs tree
- nomsu\append(pop_comments(item.source.start)) if nomsu\trailing_line_len! == 0
- inline_nomsu = @tree_to_inline_nomsu(item)
- item_nomsu = #tostring(inline_nomsu) <= MAX_LINE and inline_nomsu or recurse(item, #nomsu\match('[^\n]*$'))
- nomsu\append item_nomsu
- if i < #tree
- nomsu\append((item_nomsu\is_multiline! or nomsu\trailing_line_len! + #tostring(item_nomsu) >= MAX_LINE) and '\n' or ', ')
- nomsu\append pop_comments(tree.source.stop, '\n')
- return if tree.type == "List" then
- NomsuCode(tree.source, "[..]\n ", nomsu)
- else
- NomsuCode(tree.source, "{..}\n ", nomsu)
-
- when "DictEntry"
- key, value = tree[1], tree[2]
- nomsu = if key.type == "Text" and #key == 1 and is_identifier(key[1])
- NomsuCode(key.source, key[1])
- else @tree_to_inline_nomsu(key)
- nomsu\parenthesize! if key.type == "Action" or key.type == "Block"
- nomsu\append ": ", recurse(value, #tostring(nomsu))
- return nomsu
-
- when "IndexChain", "Number", "Var", "Comment", "Error"
- return @tree_to_inline_nomsu tree
-
- else
- error("Unknown type: #{tree.type}")
+})
-return NomsuCompiler
+return compile
diff --git a/nomsu_decompiler.lua b/nomsu_decompiler.lua
new file mode 100644
index 0000000..8d0aa7c
--- /dev/null
+++ b/nomsu_decompiler.lua
@@ -0,0 +1,411 @@
+local NomsuCode
+NomsuCode = require("code_obj").NomsuCode
+local find, sub, match
+do
+ local _obj_0 = string
+ find, sub, match = _obj_0.find, _obj_0.sub, _obj_0.match
+end
+local R, P, S
+do
+ local _obj_0 = require('lpeg')
+ R, P, S = _obj_0.R, _obj_0.P, _obj_0.S
+end
+local re = require('re')
+local MAX_LINE = 90
+local utf8_char_patt = (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"))
+local operator_patt = S("'`~!@$^&*+=|<>?/-") ^ 1 * -1
+local identifier_patt = (R("az", "AZ", "09") + P("_") + utf8_char_patt) ^ 1 * -1
+local is_operator
+is_operator = function(s)
+ return not not operator_patt:match(s)
+end
+local is_identifier
+is_identifier = function(s)
+ return not not identifier_patt:match(s)
+end
+local inline_escaper = re.compile("{~ (%utf8_char / ('\"' -> '\\\"') / ('\n' -> '\\n') / ('\t' -> '\\t') / ('\b' -> '\\b') / ('\a' -> '\\a') / ('\v' -> '\\v') / ('\f' -> '\\f') / ('\r' -> '\\r') / ('\\' -> '\\\\') / ([^ -~] -> escape) / .)* ~}", {
+ utf8_char = utf8_char_patt,
+ escape = (function(self)
+ return ("\\%03d"):format(self:byte())
+ end)
+})
+local inline_escape
+inline_escape = function(s)
+ return inline_escaper:match(s)
+end
+local escaper = re.compile("{~ (%utf8_char / ('\\' -> '\\\\') / [\n\r\t -~] / (. -> escape))* ~}", {
+ utf8_char = utf8_char_patt,
+ escape = (function(self)
+ return ("\\%03d"):format(self:byte())
+ end)
+})
+local escape
+escape = function(s)
+ return escaper:match(s)
+end
+local tree_to_inline_nomsu
+tree_to_inline_nomsu = function(tree)
+ local _exp_0 = tree.type
+ if "Action" == _exp_0 then
+ local nomsu = NomsuCode(tree.source)
+ if tree.target then
+ local inline_target = 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
+ local clump_words = (type(tree[i - 1]) == 'string' and is_operator(bit) ~= is_operator(tree[i - 1]))
+ if i > 1 and not clump_words then
+ nomsu:append(" ")
+ end
+ nomsu:append(bit)
+ else
+ local arg_nomsu = tree_to_inline_nomsu(bit)
+ if bit.type == "Block" then
+ if i > 1 and i < #tree then
+ nomsu:append(" ")
+ end
+ if not (i == #tree) then
+ arg_nomsu:parenthesize()
+ end
+ else
+ if i > 1 then
+ nomsu:append(" ")
+ end
+ if bit.type == "Action" then
+ arg_nomsu:parenthesize()
+ end
+ end
+ nomsu:append(arg_nomsu)
+ end
+ end
+ return nomsu
+ elseif "EscapedNomsu" == _exp_0 then
+ local inner_nomsu = tree_to_inline_nomsu(tree[1])
+ if not (tree[1].type == "List" or tree[1].type == "Dict" or tree[1].type == "Var") then
+ inner_nomsu:parenthesize()
+ end
+ return NomsuCode(tree.source, "\\", inner_nomsu)
+ elseif "Block" == _exp_0 then
+ local nomsu = NomsuCode(tree.source, ":")
+ for i, line in ipairs(tree) do
+ nomsu:append(i == 1 and " " or "; ")
+ nomsu:append(tree_to_inline_nomsu(line))
+ end
+ if #tree > 1 then
+ nomsu:parenthesize()
+ end
+ return nomsu
+ elseif "Text" == _exp_0 then
+ local add_text
+ add_text = function(nomsu, tree)
+ for i, bit in ipairs(tree) do
+ if type(bit) == 'string' then
+ local escaped = inline_escape(bit)
+ nomsu:append(inline_escape(bit))
+ elseif bit.type == "Text" then
+ add_text(nomsu, bit)
+ else
+ local interp_nomsu = tree_to_inline_nomsu(bit)
+ if bit.type ~= "Var" and bit.type ~= "List" and bit.type ~= "Dict" then
+ interp_nomsu:parenthesize()
+ elseif bit.type == "Var" and type(tree[i + 1]) == 'string' and not match(tree[i + 1], "^[ \n\t,.:;#(){}[%]]") then
+ interp_nomsu:parenthesize()
+ end
+ nomsu:append("\\", interp_nomsu)
+ end
+ end
+ end
+ local nomsu = NomsuCode(tree.source)
+ add_text(nomsu, tree)
+ return NomsuCode(tree.source, '"', nomsu, '"')
+ elseif "List" == _exp_0 or "Dict" == _exp_0 then
+ local nomsu = NomsuCode(tree.source, (tree.type == "List" and "[" or "{"))
+ for i, item in ipairs(tree) do
+ if i > 1 then
+ nomsu:append(", ")
+ end
+ nomsu:append(tree_to_inline_nomsu(item))
+ end
+ nomsu:append(tree.type == "List" and "]" or "}")
+ return nomsu
+ elseif "DictEntry" == _exp_0 then
+ local key, value = tree[1], tree[2]
+ local nomsu
+ if key.type == "Text" and #key == 1 and is_identifier(key[1]) then
+ nomsu = NomsuCode(key.source, key[1])
+ else
+ nomsu = tree_to_inline_nomsu(key)
+ end
+ if key.type == "Action" or key.type == "Block" then
+ nomsu:parenthesize()
+ end
+ assert(value.type ~= "Block", "Didn't expect to find a Block as a value in a dict")
+ nomsu:append(":")
+ if value then
+ local value_nomsu = tree_to_inline_nomsu(value)
+ if value.type == "Block" then
+ value_nomsu:parenthesize()
+ end
+ nomsu:append(value_nomsu)
+ end
+ return nomsu
+ elseif "IndexChain" == _exp_0 then
+ local nomsu = NomsuCode(tree.source)
+ for i, bit in ipairs(tree) do
+ if i > 1 then
+ nomsu:append(".")
+ end
+ local bit_nomsu
+ if i > 1 and bit.type == "Text" and #bit == 1 and type(bit[1]) == 'string' and is_identifier(bit[1]) then
+ bit_nomsu = bit[1]
+ else
+ bit_nomsu = tree_to_inline_nomsu(bit)
+ end
+ assert(bit.type ~= "Block")
+ if bit.type == "Action" or bit.type == "IndexChain" or (bit.type == "Number" and i < #tree) then
+ bit_nomsu:parenthesize()
+ end
+ nomsu:append(bit_nomsu)
+ end
+ return nomsu
+ elseif "Number" == _exp_0 then
+ return NomsuCode(tree.source, tostring(tree[1]))
+ elseif "Var" == _exp_0 then
+ return NomsuCode(tree.source, "%", tree[1])
+ elseif "FileChunks" == _exp_0 then
+ return error("Can't inline a FileChunks")
+ elseif "Comment" == _exp_0 then
+ return nil
+ elseif "Error" == _exp_0 then
+ return error("Can't compile errors")
+ else
+ return error("Unknown type: " .. tostring(tree.type))
+ end
+end
+local tree_to_nomsu
+tree_to_nomsu = function(tree)
+ local nomsu = NomsuCode(tree.source)
+ local recurse
+ recurse = function(t)
+ local space = MAX_LINE - nomsu:trailing_line_len()
+ local inline = true
+ for subtree in coroutine.wrap(function()
+ return (t:map(coroutine.yield) and nil)
+ end) do
+ if subtree.type == "Block" then
+ if #subtree > 1 or #tree_to_inline_nomsu(subtree):text() > 20 then
+ inline = false
+ end
+ end
+ end
+ if inline then
+ local inline_nomsu = tree_to_inline_nomsu(t)
+ if #inline_nomsu:text() <= space then
+ if t.type == "Action" then
+ inline_nomsu:parenthesize()
+ end
+ return inline_nomsu
+ end
+ end
+ local indented = tree_to_nomsu(t)
+ if t.type == "Action" then
+ if indented:is_multiline() then
+ return NomsuCode(t.source, "(..)\n ", indented)
+ else
+ indented:parenthesize()
+ end
+ end
+ return indented
+ end
+ local _exp_0 = tree.type
+ if "FileChunks" == _exp_0 then
+ local should_clump
+ should_clump = function(prev_line, line)
+ if prev_line.type == "Action" and line.type == "Action" then
+ if prev_line.stub == "use" then
+ return line.stub == "use"
+ end
+ if prev_line.stub == "test" then
+ return true
+ end
+ if line.stub == "test" then
+ return false
+ end
+ end
+ return not recurse(prev_line):is_multiline()
+ end
+ for chunk_no, chunk in ipairs(tree) do
+ if chunk_no > 1 then
+ nomsu:append("\n\n" .. tostring(("~"):rep(80)) .. "\n\n")
+ end
+ if chunk.type == "Block" then
+ for line_no, line in ipairs(chunk) do
+ if line_no > 1 then
+ if should_clump(chunk[line_no - 1], line) then
+ nomsu:append("\n")
+ else
+ nomsu:append("\n\n")
+ end
+ end
+ nomsu:append(tree_to_nomsu(line))
+ end
+ else
+ nomsu:append(tree_to_nomsu(chunk))
+ end
+ end
+ if not (nomsu:match("\n$")) then
+ nomsu:append('\n')
+ end
+ return nomsu
+ elseif "Action" == _exp_0 then
+ local next_space = ""
+ if tree.target then
+ local target_nomsu = recurse(tree.target)
+ if (tree.target.type == "Block" or tree.target.type == "EscapedNomsu") and not target_nomsu:is_multiline() then
+ target_nomsu:parenthesize()
+ end
+ nomsu:append(target_nomsu)
+ nomsu:append(target_nomsu:is_multiline() and "\n..::" or "::")
+ end
+ for i, bit in ipairs(tree) do
+ if type(bit) == "string" then
+ if not (next_space == " " and (type(tree[i - 1]) == 'string' and is_operator(tree[i - 1]) ~= is_operator(bit))) then
+ nomsu:append(next_space)
+ end
+ nomsu:append(bit)
+ next_space = nomsu:trailing_line_len() > MAX_LINE and " \\\n.." or " "
+ else
+ local bit_nomsu = recurse(bit)
+ if i < #tree and (bit.type == "Block" or bit.type == "EscapedNomsu") and not bit_nomsu:is_multiline() then
+ bit_nomsu:parenthesize()
+ end
+ if next_space == " " and not bit_nomsu:is_multiline() and nomsu:trailing_line_len() + #bit_nomsu:text() > MAX_LINE then
+ next_space = " \\\n.."
+ end
+ if not (next_space == " " and bit.type == "Block") then
+ nomsu:append(next_space)
+ end
+ nomsu:append(bit_nomsu)
+ next_space = bit_nomsu:is_multiline() and "\n.." or " "
+ end
+ end
+ return nomsu
+ elseif "EscapedNomsu" == _exp_0 then
+ return NomsuCode(tree.source, "\\", recurse(tree[1]))
+ elseif "Block" == _exp_0 then
+ for i, line in ipairs(tree) do
+ local line_nomsu = tree_to_nomsu(line)
+ nomsu:append(line_nomsu)
+ if i < #tree then
+ nomsu:append(line_nomsu:match('\n[^\n]*\n') and "\n\n" or "\n")
+ end
+ end
+ return NomsuCode(tree.source, ":\n ", nomsu)
+ elseif "Text" == _exp_0 then
+ local max_line = math.floor(1.25 * MAX_LINE)
+ local add_text
+ add_text = function(tree)
+ for i, bit in ipairs(tree) do
+ if type(bit) == 'string' then
+ bit = escape(bit)
+ for j, line in ipairs(bit:lines()) do
+ if j > 1 then
+ nomsu:append("\n")
+ elseif #line > 10 and nomsu:trailing_line_len() > max_line then
+ nomsu:append("\\\n..")
+ end
+ while #line > 0 do
+ local space = max_line - nomsu:trailing_line_len()
+ local split = find(line, "[%p%s]", space)
+ if not split or split > space + 10 then
+ split = space + 10
+ end
+ if #line - split < 10 then
+ split = #line
+ end
+ local bite
+ bite, line = sub(line, 1, split), sub(line, split + 1, -1)
+ nomsu:append(bite)
+ if #line > 0 then
+ nomsu:append("\\\n..")
+ end
+ end
+ end
+ elseif bit.type == "Text" then
+ add_text(bit)
+ else
+ nomsu:append("\\")
+ local interp_nomsu = recurse(bit)
+ if not (interp_nomsu:is_multiline()) then
+ if bit.type == "Var" then
+ if type(tree[i + 1]) == 'string' and not match(tree[i + 1], "^[ \n\t,.:;#(){}[%]]") then
+ interp_nomsu:parenthesize()
+ end
+ elseif bit.type == "EscapedNomsu" or bit.type == "Block" then
+ interp_nomsu:parenthesize()
+ end
+ end
+ nomsu:append(interp_nomsu)
+ if interp_nomsu:is_multiline() then
+ nomsu:append("\n..")
+ end
+ end
+ end
+ end
+ add_text(tree)
+ return NomsuCode(tree.source, '"\\\n ..', nomsu, '"')
+ elseif "List" == _exp_0 or "Dict" == _exp_0 then
+ if #tree == 0 then
+ nomsu:append(tree.type == "List" and "[]" or "{}")
+ return nomsu
+ end
+ for i, item in ipairs(tree) do
+ local item_nomsu = tree_to_inline_nomsu(item)
+ if #item_nomsu:text() > MAX_LINE then
+ item_nomsu = recurse(item)
+ elseif item.type == "Block" or item.type == "EscapedNomsu" then
+ item_nomsu:parenthesize()
+ end
+ nomsu:append(item_nomsu)
+ if i < #tree then
+ nomsu:append((item_nomsu:is_multiline() or nomsu:trailing_line_len() + #tostring(item_nomsu) >= MAX_LINE) and '\n' or ', ')
+ end
+ end
+ if tree.type == "List" then
+ return NomsuCode(tree.source, "[..]\n ", nomsu)
+ else
+ return NomsuCode(tree.source, "{..}\n ", nomsu)
+ end
+ elseif "DictEntry" == _exp_0 then
+ local key, value = tree[1], tree[2]
+ if key.type == "Text" and #key == 1 and is_identifier(key[1]) then
+ nomsu = NomsuCode(key.source, key[1])
+ else
+ nomsu = tree_to_inline_nomsu(key)
+ end
+ if key.type == "Block" then
+ nomsu:parenthesize()
+ end
+ local value_nomsu = tree_to_nomsu(value)
+ if (value.type == "Block" or value.type == "EscapedNomsu") and not value_nomsu:is_multiline() then
+ value_nomsu:parenthesize()
+ end
+ nomsu:append(": ", value_nomsu)
+ return nomsu
+ elseif "Comment" == _exp_0 then
+ nomsu:append("#", tree[1]:gsub("\n", "\n "))
+ return nomsu
+ elseif "IndexChain" == _exp_0 or "Number" == _exp_0 or "Var" == _exp_0 or "Comment" == _exp_0 or "Error" == _exp_0 then
+ return tree_to_inline_nomsu(tree)
+ else
+ return error("Unknown type: " .. tostring(tree.type))
+ end
+end
+return {
+ tree_to_nomsu = tree_to_nomsu,
+ tree_to_inline_nomsu = tree_to_inline_nomsu
+}
diff --git a/nomsu_decompiler.moon b/nomsu_decompiler.moon
new file mode 100644
index 0000000..df1c4d6
--- /dev/null
+++ b/nomsu_decompiler.moon
@@ -0,0 +1,319 @@
+{:NomsuCode} = require "code_obj"
+{:find, :sub, :match} = string
+{:R,:P,:S} = require 'lpeg'
+re = require 're'
+
+MAX_LINE = 90
+
+-- Parsing helper functions
+utf8_char_patt = (
+ 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"))
+operator_patt = S("'`~!@$^&*+=|<>?/-")^1 * -1
+identifier_patt = (R("az","AZ","09") + P("_") + utf8_char_patt)^1 * -1
+
+is_operator = (s)->
+ return not not operator_patt\match(s)
+
+is_identifier = (s)->
+ return not not identifier_patt\match(s)
+
+inline_escaper = re.compile("{~ (%utf8_char / ('\"' -> '\\\"') / ('\n' -> '\\n') / ('\t' -> '\\t') / ('\b' -> '\\b') / ('\a' -> '\\a') / ('\v' -> '\\v') / ('\f' -> '\\f') / ('\r' -> '\\r') / ('\\' -> '\\\\') / ([^ -~] -> escape) / .)* ~}", {utf8_char: utf8_char_patt, escape:(=> ("\\%03d")\format(@byte!))})
+inline_escape = (s)->
+ return inline_escaper\match(s)
+
+escaper = re.compile("{~ (%utf8_char / ('\\' -> '\\\\') / [\n\r\t -~] / (. -> escape))* ~}",
+ {utf8_char: utf8_char_patt, escape:(=> ("\\%03d")\format(@byte!))})
+escape = (s)->
+ return escaper\match(s)
+
+tree_to_inline_nomsu = (tree)->
+ switch tree.type
+ when "Action"
+ nomsu = NomsuCode(tree.source)
+ if 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]))
+ nomsu\append " " if i > 1 and not clump_words
+ nomsu\append bit
+ else
+ arg_nomsu = tree_to_inline_nomsu(bit)
+ if bit.type == "Block"
+ if i > 1 and i < #tree
+ nomsu\append " "
+ unless i == #tree
+ arg_nomsu\parenthesize!
+ else
+ nomsu\append " " if i > 1
+ if bit.type == "Action"
+ arg_nomsu\parenthesize!
+ nomsu\append arg_nomsu
+ return nomsu
+
+ when "EscapedNomsu"
+ inner_nomsu = tree_to_inline_nomsu(tree[1])
+ unless tree[1].type == "List" or tree[1].type == "Dict" or tree[1].type == "Var"
+ inner_nomsu\parenthesize!
+ return NomsuCode(tree.source, "\\", inner_nomsu)
+
+ when "Block"
+ nomsu = NomsuCode(tree.source, ":")
+ for i,line in ipairs tree
+ nomsu\append(i == 1 and " " or "; ")
+ nomsu\append tree_to_inline_nomsu(line)
+ nomsu\parenthesize! if #tree > 1
+ return nomsu
+
+ when "Text"
+ add_text = (nomsu, tree)->
+ for i, bit in ipairs tree
+ if type(bit) == 'string'
+ escaped = inline_escape(bit)
+ nomsu\append inline_escape(bit)
+ elseif bit.type == "Text"
+ add_text(nomsu, bit)
+ else
+ interp_nomsu = tree_to_inline_nomsu(bit)
+ if bit.type != "Var" and bit.type != "List" and bit.type != "Dict"
+ interp_nomsu\parenthesize!
+ elseif bit.type == "Var" and type(tree[i+1]) == 'string' and not match(tree[i+1], "^[ \n\t,.:;#(){}[%]]")
+ interp_nomsu\parenthesize!
+ nomsu\append "\\", interp_nomsu
+ nomsu = NomsuCode(tree.source)
+ add_text(nomsu, tree)
+ return NomsuCode(tree.source, '"', nomsu, '"')
+
+ when "List", "Dict"
+ nomsu = NomsuCode(tree.source, (tree.type == "List" and "[" or "{"))
+ for i, item in ipairs tree
+ nomsu\append ", " if i > 1
+ nomsu\append tree_to_inline_nomsu(item)
+ nomsu\append(tree.type == "List" and "]" or "}")
+ return nomsu
+
+ when "DictEntry"
+ key, value = tree[1], tree[2]
+ nomsu = if key.type == "Text" and #key == 1 and is_identifier(key[1])
+ NomsuCode(key.source, key[1])
+ else tree_to_inline_nomsu(key)
+ nomsu\parenthesize! if key.type == "Action" or key.type == "Block"
+ assert(value.type != "Block", "Didn't expect to find a Block as a value in a dict")
+ nomsu\append ":"
+ if value
+ value_nomsu = tree_to_inline_nomsu(value)
+ value_nomsu\parenthesize! if value.type == "Block"
+ nomsu\append value_nomsu
+ return nomsu
+
+ when "IndexChain"
+ nomsu = NomsuCode(tree.source)
+ for i, bit in ipairs tree
+ nomsu\append "." if i > 1
+ local bit_nomsu
+ bit_nomsu = if i > 1 and bit.type == "Text" and #bit == 1 and type(bit[1]) == 'string' and is_identifier(bit[1])
+ bit[1]
+ else tree_to_inline_nomsu(bit)
+ assert bit.type != "Block"
+ if bit.type == "Action" or bit.type == "IndexChain" or (bit.type == "Number" and i < #tree)
+ bit_nomsu\parenthesize!
+ nomsu\append bit_nomsu
+ return nomsu
+
+ when "Number"
+ return NomsuCode(tree.source, tostring(tree[1]))
+
+ when "Var"
+ return NomsuCode(tree.source, "%", tree[1])
+
+ when "FileChunks"
+ error("Can't inline a FileChunks")
+
+ when "Comment"
+ -- TODO: implement?
+ return nil
+
+ when "Error"
+ error("Can't compile errors")
+
+ else
+ error("Unknown type: #{tree.type}")
+
+tree_to_nomsu = (tree)->
+ nomsu = NomsuCode(tree.source)
+
+ -- For concision:
+ recurse = (t)->
+ space = MAX_LINE - nomsu\trailing_line_len!
+ inline = true
+ for subtree in coroutine.wrap(-> (t\map(coroutine.yield) and nil))
+ if subtree.type == "Block"
+ if #subtree > 1 or #tree_to_inline_nomsu(subtree)\text! > 20
+ inline = false
+
+ if inline
+ inline_nomsu = tree_to_inline_nomsu(t)
+ if #inline_nomsu\text! <= space
+ if t.type == "Action"
+ inline_nomsu\parenthesize!
+ return inline_nomsu
+ indented = tree_to_nomsu(t)
+ if t.type == "Action"
+ if indented\is_multiline!
+ return NomsuCode(t.source, "(..)\n ", indented)
+ else indented\parenthesize!
+ return indented
+
+ switch tree.type
+ when "FileChunks"
+ should_clump = (prev_line, line)->
+ if prev_line.type == "Action" and line.type == "Action"
+ if prev_line.stub == "use" then return line.stub == "use"
+ if prev_line.stub == "test" then return true
+ if line.stub == "test" then return false
+ 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
+ if chunk.type == "Block"
+ for line_no, line in ipairs chunk
+ if line_no > 1
+ if should_clump(chunk[line_no-1], line)
+ nomsu\append "\n"
+ else
+ nomsu\append "\n\n"
+ nomsu\append tree_to_nomsu(line)
+ else
+ nomsu\append tree_to_nomsu(chunk)
+ nomsu\append('\n') unless nomsu\match("\n$")
+ return nomsu
+
+ when "Action"
+ next_space = ""
+ if tree.target
+ target_nomsu = recurse(tree.target)
+ if (tree.target.type == "Block" or tree.target.type == "EscapedNomsu") and not target_nomsu\is_multiline!
+ target_nomsu\parenthesize!
+ nomsu\append target_nomsu
+ nomsu\append(target_nomsu\is_multiline! and "\n..::" or "::")
+
+ for i,bit in ipairs tree
+ if type(bit) == "string"
+ unless next_space == " " and (type(tree[i-1]) == 'string' and is_operator(tree[i-1]) != is_operator(bit))
+ nomsu\append next_space
+ nomsu\append bit
+ next_space = nomsu\trailing_line_len! > MAX_LINE and " \\\n.." or " "
+ else
+ bit_nomsu = recurse(bit)
+ if i < #tree and (bit.type == "Block" or bit.type == "EscapedNomsu") and not bit_nomsu\is_multiline!
+ bit_nomsu\parenthesize!
+
+ if next_space == " " and not bit_nomsu\is_multiline! and nomsu\trailing_line_len! + #bit_nomsu\text! > MAX_LINE
+ next_space = " \\\n.."
+ unless next_space == " " and bit.type == "Block"
+ nomsu\append next_space
+
+ nomsu\append bit_nomsu
+ next_space = bit_nomsu\is_multiline! and "\n.." or " "
+
+ return nomsu
+
+ when "EscapedNomsu"
+ return NomsuCode tree.source, "\\", recurse(tree[1])
+
+ when "Block"
+ for i, line in ipairs tree
+ line_nomsu = tree_to_nomsu(line)
+ nomsu\append line_nomsu
+ if i < #tree
+ -- number of lines > 2 (TODO: improve this)
+ nomsu\append(line_nomsu\match('\n[^\n]*\n') and "\n\n" or "\n")
+ return NomsuCode(tree.source, ":\n ", nomsu)
+
+ when "Text"
+ -- Multi-line text has more generous wrap margins
+ max_line = math.floor(1.25*MAX_LINE)
+ add_text = (tree)->
+ for i, bit in ipairs tree
+ if type(bit) == 'string'
+ bit = escape(bit)
+ for j, line in ipairs bit\lines!
+ if j > 1
+ nomsu\append "\n"
+ elseif #line > 10 and nomsu\trailing_line_len! > max_line
+ nomsu\append "\\\n.."
+
+ while #line > 0
+ space = max_line - nomsu\trailing_line_len!
+ split = find(line, "[%p%s]", space)
+ if not split or split > space + 10
+ split = space + 10
+ if #line - split < 10
+ split = #line
+ bite, line = sub(line, 1, split), sub(line, split+1, -1)
+ nomsu\append bite
+ nomsu\append "\\\n.." if #line > 0
+ elseif bit.type == "Text"
+ add_text(bit)
+ else
+ nomsu\append "\\"
+ interp_nomsu = recurse(bit)
+ unless interp_nomsu\is_multiline!
+ if bit.type == "Var"
+ if type(tree[i+1]) == 'string' and not match(tree[i+1], "^[ \n\t,.:;#(){}[%]]")
+ interp_nomsu\parenthesize!
+ elseif bit.type == "EscapedNomsu" or bit.type == "Block"
+ interp_nomsu\parenthesize!
+ nomsu\append interp_nomsu
+ if interp_nomsu\is_multiline!
+ nomsu\append "\n.."
+ add_text(tree)
+ return NomsuCode(tree.source, '"\\\n ..', nomsu, '"')
+
+ when "List", "Dict"
+ if #tree == 0
+ nomsu\append(tree.type == "List" and "[]" or "{}")
+ return nomsu
+ for i, item in ipairs tree
+ item_nomsu = tree_to_inline_nomsu(item)
+ if #item_nomsu\text! > MAX_LINE
+ item_nomsu = recurse(item)
+ elseif item.type == "Block" or item.type == "EscapedNomsu"
+ item_nomsu\parenthesize!
+ nomsu\append item_nomsu
+ if i < #tree
+ nomsu\append((item_nomsu\is_multiline! or nomsu\trailing_line_len! + #tostring(item_nomsu) >= MAX_LINE) and '\n' or ', ')
+ return if tree.type == "List" then
+ NomsuCode(tree.source, "[..]\n ", nomsu)
+ else
+ NomsuCode(tree.source, "{..}\n ", nomsu)
+
+ when "DictEntry"
+ key, value = tree[1], tree[2]
+ nomsu = if key.type == "Text" and #key == 1 and is_identifier(key[1])
+ NomsuCode(key.source, key[1])
+ else tree_to_inline_nomsu(key)
+ nomsu\parenthesize! if key.type == "Block"
+ value_nomsu = tree_to_nomsu(value)
+ if (value.type == "Block" or value.type == "EscapedNomsu") and not value_nomsu\is_multiline!
+ value_nomsu\parenthesize!
+ nomsu\append ": ", value_nomsu
+ return nomsu
+
+ when "Comment"
+ nomsu\append "#", tree[1]\gsub("\n", "\n ")
+ return nomsu
+
+ when "IndexChain", "Number", "Var", "Comment", "Error"
+ return tree_to_inline_nomsu tree
+
+ else
+ error("Unknown type: #{tree.type}")
+
+return {:tree_to_nomsu, :tree_to_inline_nomsu}
diff --git a/nomsu_environment.lua b/nomsu_environment.lua
new file mode 100644
index 0000000..3bb54b1
--- /dev/null
+++ b/nomsu_environment.lua
@@ -0,0 +1,325 @@
+local NomsuCode, LuaCode, Source
+do
+ local _obj_0 = require("code_obj")
+ NomsuCode, LuaCode, Source = _obj_0.NomsuCode, _obj_0.LuaCode, _obj_0.Source
+end
+local Importer, import_to_1_from, _1_forked
+do
+ local _obj_0 = require('importer')
+ Importer, import_to_1_from, _1_forked = _obj_0.Importer, _obj_0.import_to_1_from, _obj_0._1_forked
+end
+local List, Dict, Text
+do
+ local _obj_0 = require('containers')
+ List, Dict, Text = _obj_0.List, _obj_0.Dict, _obj_0.Text
+end
+local SyntaxTree = require("syntax_tree")
+local Files = require("files")
+local make_parser = require("parser")
+local pretty_error = require("pretty_errors")
+local make_tree
+make_tree = function(tree, userdata)
+ tree.source = Source(userdata.filename, tree.start, tree.stop)
+ tree.start, tree.stop = nil, nil
+ tree = SyntaxTree(tree)
+ return tree
+end
+local Parsers = { }
+local max_parser_version = 0
+for version = 1, 999 do
+ local peg_file
+ if 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
+ else
+ peg_file = io.open("nomsu." .. tostring(version) .. ".peg")
+ end
+ if not (peg_file) then
+ break
+ end
+ max_parser_version = version
+ local peg_contents = peg_file:read('*a')
+ peg_file:close()
+ Parsers[version] = make_parser(peg_contents, make_tree)
+end
+local tree_to_nomsu, tree_to_inline_nomsu
+do
+ local _obj_0 = require("nomsu_decompiler")
+ tree_to_nomsu, tree_to_inline_nomsu = _obj_0.tree_to_nomsu, _obj_0.tree_to_inline_nomsu
+end
+local nomsu_environment = Importer({
+ NOMSU_COMPILER_VERSION = 12,
+ NOMSU_SYNTAX_VERSION = max_parser_version,
+ next = next,
+ unpack = unpack or table.unpack,
+ setmetatable = setmetatable,
+ coroutine = coroutine,
+ rawequal = rawequal,
+ getmetatable = getmetatable,
+ pcall = pcall,
+ error = error,
+ package = package,
+ os = os,
+ require = require,
+ tonumber = tonumber,
+ tostring = tostring,
+ string = string,
+ xpcall = xpcall,
+ module = module,
+ print = print,
+ loadfile = loadfile,
+ rawset = rawset,
+ _VERSION = _VERSION,
+ collectgarbage = collectgarbage,
+ rawget = rawget,
+ rawlen = rawlen,
+ table = table,
+ assert = assert,
+ dofile = dofile,
+ loadstring = loadstring,
+ lua_type_of = type,
+ select = select,
+ math = math,
+ io = io,
+ load = load,
+ pairs = pairs,
+ ipairs = ipairs,
+ jit = jit,
+ _VERSION = _VERSION,
+ bit = (jit or _VERSION == "Lua 5.2") and require('bitops') or nil,
+ List = List,
+ Dict = Dict,
+ lpeg = lpeg,
+ re = re,
+ Files = Files,
+ SyntaxTree = SyntaxTree,
+ TESTS = Dict({ }),
+ globals = Dict({ }),
+ LuaCode = LuaCode,
+ NomsuCode = NomsuCode,
+ Source = Source,
+ SOURCE_MAP = Importer({ }),
+ _1_as_nomsu = tree_to_nomsu,
+ _1_as_inline_nomsu = tree_to_inline_nomsu,
+ compile = require('nomsu_compiler'),
+ _1_forked = _1_forked,
+ import_to_1_from = import_to_1_from,
+ _1_parsed = function(nomsu_code)
+ if type(nomsu_code) == 'string' then
+ local filename = Files.spoof(nomsu_code)
+ nomsu_code = NomsuCode(Source(filename, 1, #nomsu_code), nomsu_code)
+ end
+ local source = nomsu_code.source
+ nomsu_code = tostring(nomsu_code)
+ local version = nomsu_code:match("^#![^\n]*nomsu[ ]+-V[ ]*([0-9.]+)")
+ local syntax_version = version and tonumber(version:match("^[0-9]+")) or max_parser_version
+ local parse = Parsers[syntax_version] or Parsers[max_parser_version]
+ local tree = parse(nomsu_code, source.filename)
+ local find_errors
+ find_errors = function(t)
+ if t.type == "Error" then
+ return coroutine.yield(t)
+ else
+ for k, v in pairs(t) do
+ local _continue_0 = false
+ repeat
+ if not (SyntaxTree:is_instance(v)) then
+ _continue_0 = true
+ break
+ end
+ find_errors(v)
+ _continue_0 = true
+ until true
+ if not _continue_0 then
+ break
+ end
+ end
+ end
+ end
+ local errs
+ do
+ local _accum_0 = { }
+ local _len_0 = 1
+ for err in coroutine.wrap(function()
+ return find_errors(tree)
+ end) do
+ _accum_0[_len_0] = err
+ _len_0 = _len_0 + 1
+ end
+ errs = _accum_0
+ end
+ local num_errs = #errs
+ if num_errs > 0 then
+ local err_strings
+ do
+ local _accum_0 = { }
+ local _len_0 = 1
+ for i, e in ipairs(errs) do
+ if i <= 3 then
+ _accum_0[_len_0] = pretty_error({
+ title = "Parse error",
+ error = e.error,
+ hint = e.hint,
+ source = e:get_source_code(),
+ start = e.source.start,
+ stop = e.source.stop,
+ filename = e.source.filename
+ })
+ _len_0 = _len_0 + 1
+ end
+ end
+ err_strings = _accum_0
+ end
+ if num_errs > #err_strings then
+ table.insert(err_strings, "\027[31;1m +" .. tostring(num_errs - #err_strings) .. " additional errors...\027[0m\n")
+ end
+ error(table.concat(err_strings, '\n\n'), 0)
+ end
+ return tree
+ end,
+ run_1_in = function(to_run, environment)
+ if type(to_run) == 'string' then
+ local filename = Files.spoof(to_run)
+ to_run = NomsuCode(Source(filename, 1, #to_run), to_run)
+ local ret = environment.run_1_in(to_run, environment)
+ return ret
+ elseif NomsuCode:is_instance(to_run) then
+ local tree = environment._1_parsed(to_run)
+ if tree == nil then
+ return nil
+ end
+ local ret = environment.run_1_in(tree, environment)
+ return ret
+ elseif SyntaxTree:is_instance(to_run) then
+ local filename = to_run.source.filename:gsub("\n.*", "...")
+ if to_run.type ~= "FileChunks" then
+ to_run = {
+ to_run
+ }
+ end
+ local ret = nil
+ for _index_0 = 1, #to_run do
+ local chunk = to_run[_index_0]
+ local lua = environment.compile(chunk)
+ lua:declare_locals()
+ lua:prepend("-- File: " .. tostring(filename) .. "\n")
+ ret = environment.run_1_in(lua, environment)
+ end
+ return ret
+ elseif LuaCode:is_instance(to_run) then
+ local source = to_run.source
+ local lua_string = to_run:text()
+ local run_lua_fn, err = load(lua_string, tostring(source), "t", environment)
+ if not run_lua_fn then
+ local lines
+ do
+ local _accum_0 = { }
+ local _len_0 = 1
+ for i, line in ipairs(Files.get_lines(lua_string)) do
+ _accum_0[_len_0] = ("%3d|%s"):format(i, line)
+ _len_0 = _len_0 + 1
+ end
+ lines = _accum_0
+ end
+ local line_numbered_lua = table.concat(lines, "\n")
+ error("Failed to compile generated code:\n\027[1;34m" .. tostring(line_numbered_lua) .. "\027[0m\n\n" .. tostring(err), 0)
+ end
+ local source_key = tostring(source)
+ if not (environment.SOURCE_MAP[source_key]) then
+ local map = { }
+ local file = Files.read(source.filename)
+ if not file then
+ error("Failed to find file: " .. tostring(source.filename))
+ end
+ local nomsu_str = file:sub(source.start, source.stop)
+ local lua_line = 1
+ local nomsu_line = Files.get_line_number(file, source.start)
+ local map_sources
+ map_sources = function(s)
+ if type(s) == 'string' then
+ for nl in s:gmatch("\n") do
+ map[lua_line] = map[lua_line] or nomsu_line
+ lua_line = lua_line + 1
+ end
+ else
+ if s.source and s.source.filename == source.filename then
+ nomsu_line = Files.get_line_number(file, s.source.start)
+ end
+ local _list_0 = s.bits
+ for _index_0 = 1, #_list_0 do
+ local b = _list_0[_index_0]
+ map_sources(b)
+ end
+ end
+ end
+ map_sources(to_run)
+ map[lua_line] = map[lua_line] or nomsu_line
+ map[0] = 0
+ environment.SOURCE_MAP[source_key] = map
+ end
+ return run_lua_fn()
+ else
+ return error("Attempt to run unknown thing: " .. tostring(to_run))
+ end
+ end,
+ FILE_CACHE = { },
+ run_file_1_in = function(path, environment, optimization)
+ if optimization == nil then
+ optimization = 1
+ end
+ if environment.FILE_CACHE[path] then
+ import_to_1_from(environment, environment.FILE_CACHE[path])
+ return
+ end
+ local mod = _1_forked(environment)
+ assert(mod._1_parsed)
+ mod._ENV = mod
+ for _, filename in Files.walk(path) do
+ local _continue_0 = false
+ repeat
+ if not (filename == "stdin" or filename:match("%.nom$")) then
+ _continue_0 = true
+ break
+ end
+ local lua_filename = filename:gsub("%.nom$", ".lua")
+ local code
+ if optimization ~= 0 and Files.read(lua_filename) then
+ local file = Files.read(lua_filename)
+ code = LuaCode(Source(filename, 1, #file), file)
+ else
+ local file = Files.read(filename)
+ code = NomsuCode(Source(filename, 1, #file), file)
+ end
+ environment.run_1_in(code, mod)
+ _continue_0 = true
+ until true
+ if not _continue_0 then
+ break
+ end
+ end
+ import_to_1_from(environment, mod)
+ environment.FILE_CACHE[path] = mod
+ end,
+ compile_error_at = function(tree, err_msg, hint)
+ if hint == nil then
+ hint = nil
+ end
+ local err_str = pretty_error({
+ title = "Compile error",
+ error = err_msg,
+ hint = hint,
+ source = tree:get_source_code(),
+ start = tree.source.start,
+ stop = tree.source.stop,
+ filename = tree.source.filename
+ })
+ return error(err_str, 0)
+ end
+})
+nomsu_environment._ENV = nomsu_environment
+SOURCE_MAP = nomsu_environment.SOURCE_MAP
+return nomsu_environment
diff --git a/nomsu_environment.moon b/nomsu_environment.moon
new file mode 100644
index 0000000..6f38c70
--- /dev/null
+++ b/nomsu_environment.moon
@@ -0,0 +1,184 @@
+-- This file defines the environment in which Nomsu code runs, including some
+-- basic bootstrapping functionality.
+{:NomsuCode, :LuaCode, :Source} = require "code_obj"
+{:Importer, :import_to_1_from, :_1_forked} = require 'importer'
+{:List, :Dict, :Text} = require 'containers'
+SyntaxTree = require "syntax_tree"
+Files = require "files"
+make_parser = require("parser")
+pretty_error = require("pretty_errors")
+
+make_tree = (tree, userdata)->
+ tree.source = Source(userdata.filename, tree.start, tree.stop)
+ tree.start, tree.stop = nil, nil
+ tree = SyntaxTree(tree)
+ return tree
+
+Parsers = {}
+max_parser_version = 0
+for version=1,999
+ local peg_file
+ if package.nomsupath
+ for path in package.nomsupath\gmatch("[^;]+")
+ peg_file = io.open(path.."/nomsu.#{version}.peg")
+ break if peg_file
+ else
+ peg_file = io.open("nomsu.#{version}.peg")
+ break unless peg_file
+ max_parser_version = version
+ peg_contents = peg_file\read('*a')
+ peg_file\close!
+ Parsers[version] = make_parser(peg_contents, make_tree)
+
+{:tree_to_nomsu, :tree_to_inline_nomsu} = require "nomsu_decompiler"
+nomsu_environment = Importer{
+ NOMSU_COMPILER_VERSION: 12, NOMSU_SYNTAX_VERSION: max_parser_version
+ -- Lua stuff:
+ :next, unpack: unpack or table.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, lua_type_of:type, :select, :math, :io, :load,
+ :pairs, :ipairs, :jit, :_VERSION
+ bit: (jit or _VERSION == "Lua 5.2") and require('bitops') or nil
+ -- Nomsu types:
+ List:List, Dict:Dict,
+ -- Utilities and misc.
+ lpeg:lpeg, re:re, Files:Files,
+ :SyntaxTree, TESTS: Dict({}), globals: Dict({}),
+ :LuaCode, :NomsuCode, :Source
+ SOURCE_MAP: Importer({})
+
+ -- Nomsu functions:
+ _1_as_nomsu:tree_to_nomsu, _1_as_inline_nomsu:tree_to_inline_nomsu
+ compile: require('nomsu_compiler')
+ :_1_forked, :import_to_1_from
+
+ _1_parsed: (nomsu_code)->
+ if type(nomsu_code) == 'string'
+ filename = Files.spoof(nomsu_code)
+ nomsu_code = NomsuCode(Source(filename, 1, #nomsu_code), nomsu_code)
+ source = nomsu_code.source
+ nomsu_code = tostring(nomsu_code)
+ version = nomsu_code\match("^#![^\n]*nomsu[ ]+-V[ ]*([0-9.]+)")
+ syntax_version = version and tonumber(version\match("^[0-9]+")) or max_parser_version
+ parse = Parsers[syntax_version] or Parsers[max_parser_version]
+ tree = parse(nomsu_code, source.filename)
+ find_errors = (t)->
+ if t.type == "Error"
+ coroutine.yield t
+ else
+ for k,v in pairs(t)
+ continue unless SyntaxTree\is_instance(v)
+ find_errors(v)
+ errs = [err for err in coroutine.wrap(-> find_errors(tree))]
+ num_errs = #errs
+ if num_errs > 0
+ err_strings = [pretty_error{
+ title:"Parse error"
+ error:e.error, hint:e.hint, source:e\get_source_code!
+ start:e.source.start, stop:e.source.stop, filename:e.source.filename
+ } for i, e in ipairs(errs) when i <= 3]
+ if num_errs > #err_strings
+ table.insert(err_strings, "\027[31;1m +#{num_errs-#err_strings} additional errors...\027[0m\n")
+ error(table.concat(err_strings, '\n\n'), 0)
+
+ return tree
+
+ run_1_in: (to_run, environment)->
+ if type(to_run) == 'string'
+ filename = Files.spoof(to_run)
+ to_run = NomsuCode(Source(filename, 1, #to_run), to_run)
+ ret = environment.run_1_in(to_run, environment)
+ return ret
+ elseif NomsuCode\is_instance(to_run)
+ tree = environment._1_parsed(to_run)
+ return nil if tree == nil
+ ret = environment.run_1_in(tree, environment)
+ return ret
+ elseif SyntaxTree\is_instance(to_run)
+ filename = to_run.source.filename\gsub("\n.*", "...")
+ if to_run.type != "FileChunks"
+ to_run = {to_run}
+ -- Each chunk's compilation is affected by the code in the previous chunks
+ -- (typically), so each chunk needs to compile and run before the next one
+ -- compiles.
+ ret = nil
+ for chunk in *to_run
+ lua = environment.compile(chunk)
+ lua\declare_locals!
+ lua\prepend "-- File: #{filename}\n"
+ ret = environment.run_1_in(lua, environment)
+ return ret
+ elseif LuaCode\is_instance(to_run)
+ source = to_run.source
+ lua_string = to_run\text!
+ -- If you replace tostring(source) with "nil", source mapping won't happen
+ run_lua_fn, err = load(lua_string, tostring(source), "t", environment)
+ if not run_lua_fn
+ lines =[("%3d|%s")\format(i,line) for i, line in ipairs Files.get_lines(lua_string)]
+ line_numbered_lua = table.concat(lines, "\n")
+ error("Failed to compile generated code:\n\027[1;34m#{line_numbered_lua}\027[0m\n\n#{err}", 0)
+ source_key = tostring(source)
+ unless environment.SOURCE_MAP[source_key]
+ map = {}
+ file = Files.read(source.filename)
+ if not file
+ error "Failed to find file: #{source.filename}"
+ nomsu_str = file\sub(source.start, source.stop)
+ lua_line = 1
+ nomsu_line = Files.get_line_number(file, source.start)
+ map_sources = (s)->
+ if type(s) == 'string'
+ for nl in s\gmatch("\n")
+ map[lua_line] or= nomsu_line
+ lua_line += 1
+ else
+ if s.source and s.source.filename == source.filename
+ nomsu_line = Files.get_line_number(file, s.source.start)
+ for b in *s.bits do map_sources(b)
+ map_sources(to_run)
+ map[lua_line] or= nomsu_line
+ map[0] = 0
+ -- Mapping from lua line number to nomsu line numbers
+ environment.SOURCE_MAP[source_key] = map
+ return run_lua_fn!
+ else
+ error("Attempt to run unknown thing: "..tostring(to_run))
+
+ FILE_CACHE: {}
+ run_file_1_in: (path, environment, optimization=1)->
+ if environment.FILE_CACHE[path]
+ import_to_1_from(environment, environment.FILE_CACHE[path])
+ return
+ mod = _1_forked(environment)
+ assert mod._1_parsed
+ mod._ENV = mod
+ for _,filename in Files.walk(path)
+ continue unless filename == "stdin" or filename\match("%.nom$")
+ lua_filename = filename\gsub("%.nom$", ".lua")
+ -- TODO: don't automatically use precompiled version?
+ code = if optimization != 0 and Files.read(lua_filename)
+ file = Files.read(lua_filename)
+ LuaCode(Source(filename, 1, #file), file)
+ else
+ file = Files.read(filename)
+ NomsuCode(Source(filename, 1, #file), file)
+ environment.run_1_in(code, mod)
+ import_to_1_from(environment, mod)
+ environment.FILE_CACHE[path] = mod
+
+ compile_error_at: (tree, err_msg, hint=nil)->
+ err_str = pretty_error{
+ title: "Compile error"
+ error:err_msg, hint:hint, source:tree\get_source_code!
+ start:tree.source.start, stop:tree.source.stop, filename:tree.source.filename
+ }
+ error(err_str, 0)
+}
+nomsu_environment._ENV = nomsu_environment
+
+-- Hacky use of globals:
+export SOURCE_MAP
+SOURCE_MAP = nomsu_environment.SOURCE_MAP
+
+return nomsu_environment
diff --git a/syntax_tree.lua b/syntax_tree.lua
index fe4f6dc..6615f9b 100644
--- a/syntax_tree.lua
+++ b/syntax_tree.lua
@@ -22,6 +22,9 @@ as_lua = function(self)
end
end
end
+ if self.as_lua then
+ return self:as_lua()
+ end
return error("Not supported: " .. tostring(self))
end
local SyntaxTree
diff --git a/syntax_tree.moon b/syntax_tree.moon
index 03596f8..207fb9b 100644
--- a/syntax_tree.moon
+++ b/syntax_tree.moon
@@ -10,6 +10,7 @@ as_lua = =>
if mt = getmetatable(@)
if _as_lua = mt.as_lua
return _as_lua(@)
+ return @as_lua! if @as_lua
error("Not supported: #{@}")
--types = {"Number", "Var", "Block", "EscapedNomsu", "Text", "List", "Dict", "DictEntry",
diff --git a/tools/autoformat.nom b/tools/autoformat.nom
index 13f54ef..fed611d 100755
--- a/tools/autoformat.nom
+++ b/tools/autoformat.nom
@@ -7,6 +7,8 @@
use "lib/os.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
%args = (command line args)
%inplace = (no)
if (%args.1 is "-i"):
diff --git a/tools/find_action.nom b/tools/find_action.nom
index 02abdbc..04a20e0 100755
--- a/tools/find_action.nom
+++ b/tools/find_action.nom
@@ -7,6 +7,8 @@
use "lib/os.nom"
use "lib/consolecolor.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
%stub = (command line args).1
say "Looking for stub: \%stub..."
%files = ((command line args).% for % in 2 to (size of (command line args)))
diff --git a/tools/parse.nom b/tools/parse.nom
index de6aff4..1465bb3 100755
--- a/tools/parse.nom
+++ b/tools/parse.nom
@@ -5,6 +5,8 @@
use "lib/os.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
externally (print tree %t at indent %indent) means:
if %t.type is:
"Action":
diff --git a/tools/repl.nom b/tools/repl.nom
new file mode 100644
index 0000000..00efd77
--- /dev/null
+++ b/tools/repl.nom
@@ -0,0 +1,61 @@
+#!/usr/bin/env nomsu -V4
+use "lib/consolecolor.nom"
+use "lib/os.nom"
+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+[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.
+ To exit, type 'exit' or 'quit' and hit enter twice."
+
+say "\
+ ..
+ \(bright)\(underscore)Welcome to the Nomsu v\(Nomsu version) interactive console!\(reset color)
+ press 'enter' twice to run a command
+ "
+
+%repl_line = 0
+repeat:
+ %repl_line += 1
+ %io.write (bright (yellow ">> "))
+ %buff = []
+ repeat:
+ %io.write (bright)
+ %line = (%io.read "*L")
+ %io.write (reset color)
+ if ((%line == "\n") or (not %line)):
+ if ((size of %buff) > 0):
+ %io.write "\027[1A\027[2K"
+ go to (run buffer)
+ %buff::add (%line::with "\t" -> " ")
+ %io.write (dim (yellow ".. "))
+
+ === (run buffer) ===
+ if ((size of %buff) == 0):
+ stop
+
+ %buff = (%buff::joined)
+
+ # TODO: support local variables
+ spoof file %buff
+ try:
+ %ret = (run %buff)
+ ..and if it barfs %err:
+ say %err
+ ..or if it succeeds:
+ if (type of %ret) is:
+ "nil":
+ do nothing
+ "boolean":
+ say "= \("yes" if %ret else "no")"
+ "table":
+ if %ret.as_nomsu:
+ say "= \(%ret::as nomsu)"
+ ..else:
+ say "= \%ret"
+ else:
+ say "= \%ret"
diff --git a/tools/replace.nom b/tools/replace.nom
index 25ae0ae..a0391d6 100755
--- a/tools/replace.nom
+++ b/tools/replace.nom
@@ -7,6 +7,8 @@
use "lib/os.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
%args = (command line args)
%inplace = (no)
if (%args.1 is "-i"):
diff --git a/tools/test.nom b/tools/test.nom
index fb70d1c..6e74aa6 100755
--- a/tools/test.nom
+++ b/tools/test.nom
@@ -6,6 +6,8 @@
use "lib/os.nom"
use "lib/consolecolor.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
%args = (command line args)
if (%args.1 == "-v"):
%args::remove index 1
diff --git a/tools/upgrade.nom b/tools/upgrade.nom
index 575646e..4975aad 100755
--- a/tools/upgrade.nom
+++ b/tools/upgrade.nom
@@ -8,6 +8,8 @@
use "compatibility"
use "lib/os.nom"
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
%args = (command line args)
%inplace = (no)
%start_version = (nil)