aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bitbucket@bruce-hill.com>2018-07-15 19:41:22 -0700
committerBruce Hill <bitbucket@bruce-hill.com>2018-07-15 19:43:28 -0700
commitbe06fc096aa28358914bd6a76b4ea380a04b8873 (patch)
tree1f6c300bc8fbeb6cd84008124fca1e43f5513616
parent8a44869c4a548692a19afe5258e2540f7d351c8a (diff)
Major changes to how versioning and parsing work. This should be a
better path going forward to handling upgrades. Old syntax files will stick around for compatibility purposes. Old syntax can be parsed into valid syntax trees via the old syntax (.peg) files, and then old syntax trees should be valid and can be upgraded via the normal code path. This change has lots of improvements to Nomsu codegen too.
-rw-r--r--core/collections.nom1
-rw-r--r--core/control_flow.nom1
-rw-r--r--core/coroutines.nom1
-rw-r--r--core/errors.nom1
-rw-r--r--core/io.nom1
-rw-r--r--core/math.nom1
-rw-r--r--core/metaprogramming.nom5
-rw-r--r--core/operators.nom1
-rw-r--r--core/scopes.nom4
-rw-r--r--core/text.nom1
-rw-r--r--examples/how_do_i.nom1
-rw-r--r--lib/consolecolor.nom5
-rw-r--r--lib/file_hash.nom4
-rw-r--r--lib/object.nom1
-rw-r--r--lib/os.nom4
-rw-r--r--lib/training_wheels.nom1
-rw-r--r--lib/version.nom2
-rw-r--r--nomsu.1.peg (renamed from nomsu.peg)2
-rw-r--r--nomsu.2.peg148
-rw-r--r--nomsu.lua2
-rwxr-xr-xnomsu.moon2
-rw-r--r--nomsu_compiler.lua368
-rw-r--r--nomsu_compiler.moon290
-rw-r--r--nomsu_tree.lua40
-rw-r--r--nomsu_tree.moon18
-rw-r--r--parser.lua50
-rw-r--r--parser.moon36
-rw-r--r--tests/collections.nom1
-rw-r--r--tests/colors.nom1
-rw-r--r--tests/control_flow.nom1
-rw-r--r--tests/coroutines.nom1
-rw-r--r--tests/errors.nom1
-rw-r--r--tests/inner/inner.nom1
-rw-r--r--tests/math.nom1
-rw-r--r--tests/metaprogramming.nom1
-rw-r--r--tests/object.nom1
-rw-r--r--tests/operators.nom1
-rw-r--r--tests/os.nom3
-rw-r--r--tests/scopes.nom1
-rw-r--r--tests/text.nom1
40 files changed, 563 insertions, 443 deletions
diff --git a/core/collections.nom b/core/collections.nom
index 94fc02c..ce3f61e 100644
--- a/core/collections.nom
+++ b/core/collections.nom
@@ -1,3 +1,4 @@
+#!/usr/bin/env nomsu -V1
#
This file contains code that supports manipulating and using collections like lists
and dictionaries.
diff --git a/core/control_flow.nom b/core/control_flow.nom
index a3fd5a0..5de062d 100644
--- a/core/control_flow.nom
+++ b/core/control_flow.nom
@@ -1,3 +1,4 @@
+#!/usr/bin/env nomsu -V1
#
This file contains compile-time actions that define basic control flow structures
like "if" statements and loops.
diff --git a/core/coroutines.nom b/core/coroutines.nom
index d0d9c36..8a1f8c9 100644
--- a/core/coroutines.nom
+++ b/core/coroutines.nom
@@ -1,3 +1,4 @@
+#!/usr/bin/env nomsu -V1
#
This file defines the code that creates and manipulates coroutines
diff --git a/core/errors.nom b/core/errors.nom
index 91f9b4f..c6551b5 100644
--- a/core/errors.nom
+++ b/core/errors.nom
@@ -1,3 +1,4 @@
+#!/usr/bin/env nomsu -V1
#
This file contains basic error reporting code
diff --git a/core/io.nom b/core/io.nom
index 48db71f..55c5702 100644
--- a/core/io.nom
+++ b/core/io.nom
@@ -1,3 +1,4 @@
+#!/usr/bin/env nomsu -V1
#
This file contains basic input/output code
diff --git a/core/math.nom b/core/math.nom
index 57088eb..5717482 100644
--- a/core/math.nom
+++ b/core/math.nom
@@ -1,3 +1,4 @@
+#!/usr/bin/env nomsu -V1
#
This file defines some common math literals and functions
diff --git a/core/metaprogramming.nom b/core/metaprogramming.nom
index d445737..2be7132 100644
--- a/core/metaprogramming.nom
+++ b/core/metaprogramming.nom
@@ -1,3 +1,4 @@
+#!/usr/bin/env nomsu -V1
#
This File contains actions for making actions and compile-time actions and some helper
functions to make that easier.
@@ -186,6 +187,8 @@ compile [remove free vars %vars from %code] to
compile [%lua <-write %code, to %lua write %code] to: Lua "\(%lua as lua expr):append(\(%code as lua expr));"
+compile [to %lua write %code joined by %glue] to: Lua "\(%lua as lua expr):concat_append(\(%code as lua expr), \(%glue as lua expr));"
+
compile [quote %s] to
Lua value ".."
repr(\(%s as lua expr))
@@ -212,7 +215,7 @@ compile [compile %block, compiled %block, %block compiled] to
# Return statement is wrapped in a do..end block because Lua is unhappy if you
put code after a return statement, unless you wrap it in a block.
compile [return] to: Lua "do return; end"
-compile [return %return_value] to: Lua "do return \(%return_value as lua expr); end"
+compile [return %return_value] to: Lua "do return \(%return_value as lua expr) end"
# Literals
compile [yes] to: Lua value "true"
diff --git a/core/operators.nom b/core/operators.nom
index 0712ada..995b561 100644
--- a/core/operators.nom
+++ b/core/operators.nom
@@ -1,3 +1,4 @@
+#!/usr/bin/env nomsu -V1
#
This file contains definitions of operators like "+" and "and".
diff --git a/core/scopes.nom b/core/scopes.nom
index 3af59c5..19b9e49 100644
--- a/core/scopes.nom
+++ b/core/scopes.nom
@@ -1,3 +1,7 @@
+#!/usr/bin/env nomsu -V1
+#
+ This file contains definitions pertaining to variable scoping
+
use "core/metaprogramming.nom"
use "core/operators.nom"
use "core/collections.nom"
diff --git a/core/text.nom b/core/text.nom
index d09e6b5..514334b 100644
--- a/core/text.nom
+++ b/core/text.nom
@@ -1,3 +1,4 @@
+#!/usr/bin/env nomsu -V1
#
This file contains some definitions of text escape sequences, including ANSI console
color codes.
diff --git a/examples/how_do_i.nom b/examples/how_do_i.nom
index c6c7ba3..604ea26 100644
--- a/examples/how_do_i.nom
+++ b/examples/how_do_i.nom
@@ -1,3 +1,4 @@
+#!/usr/bin/env nomsu -V1
# How do I...
# Write a comment? Put a # and go till the end of the line
diff --git a/lib/consolecolor.nom b/lib/consolecolor.nom
index 8e3ca2f..3e325d6 100644
--- a/lib/consolecolor.nom
+++ b/lib/consolecolor.nom
@@ -1,3 +1,7 @@
+#!/usr/bin/env nomsu -V1
+#
+ This file defines actions for ANSI console color escape codes.
+
use "core"
test: bright: "\(green)Color test passed."
@@ -12,6 +16,7 @@ test: bright: "\(green)Color test passed."
for %name = %colornum in %colors
with {%escapecode: "\27[\(%colornum)m"}
run ".."
+ #!/usr/bin/env nomsu -V1
compile [\%name] to: Lua value (quote \(quote %escapecode))
compile [\%name %text] to
Lua value ".."
diff --git a/lib/file_hash.nom b/lib/file_hash.nom
index 1f263de..9882481 100644
--- a/lib/file_hash.nom
+++ b/lib/file_hash.nom
@@ -1,3 +1,7 @@
+#!/usr/bin/env nomsu -V1
+#
+ This file defines some actions for hashing files and looking up files by hash.
+
use "core"
action [file with hash %hash]
diff --git a/lib/object.nom b/lib/object.nom
index 5f12170..45f7551 100644
--- a/lib/object.nom
+++ b/lib/object.nom
@@ -1,3 +1,4 @@
+#!/usr/bin/env nomsu -V1
#
This file contains the implementation of an Object-Oriented programming system.
diff --git a/lib/os.nom b/lib/os.nom
index ee8b79b..329a77f 100644
--- a/lib/os.nom
+++ b/lib/os.nom
@@ -1,3 +1,7 @@
+#!/usr/bin/env nomsu -V1
+#
+ This file defines some actions that interact with the operating system and filesystem.
+
use "core"
action [path of Nomsu file %filename]
diff --git a/lib/training_wheels.nom b/lib/training_wheels.nom
index 19625bc..970265c 100644
--- a/lib/training_wheels.nom
+++ b/lib/training_wheels.nom
@@ -1,3 +1,4 @@
+#!/usr/bin/env nomsu -V1
#
This file contains a set of definitions that bring some familiar language features
from other languages into nomsu (e.g. "==" and "continue")
diff --git a/lib/version.nom b/lib/version.nom
index 3e9a29d..f90a39c 100644
--- a/lib/version.nom
+++ b/lib/version.nom
@@ -1 +1,3 @@
+#!/usr/bin/env nomsu -V1
+# This file sets the current library version.
lua> "NOMSU_LIB_VERSION = 3"
diff --git a/nomsu.peg b/nomsu.1.peg
index d06c1ff..1551b6d 100644
--- a/nomsu.peg
+++ b/nomsu.1.peg
@@ -1,5 +1,5 @@
-- Nomsu version 2
-file (File):
+file (FileChunks):
{:curr_indent: ' '* :}
comment? blank_lines?
(chunk (nl_nodent chunk_delimeter nl_nodent chunk)*)?
diff --git a/nomsu.2.peg b/nomsu.2.peg
new file mode 100644
index 0000000..f6a5d23
--- /dev/null
+++ b/nomsu.2.peg
@@ -0,0 +1,148 @@
+-- Nomsu version 2
+file (FileChunks):
+ {:curr_indent: ' '* :}
+ comment? blank_lines?
+ (chunk (nl_nodent section_division nl_nodent chunk)*)?
+ blank_lines?
+ %ws* (!! .+ -> "Parse error" !!)?
+
+nodent: =curr_indent !(" ")
+indent: =curr_indent " "
+blank_lines: %nl ((nodent comment / %ws*) %nl)*
+eol: %ws* eol_comment? (!. / &%nl)
+
+nl_nodent: blank_lines nodent
+nl_indent: blank_lines {:curr_indent: indent :} (comment nl_nodent)?
+
+comment:
+ "#" (({} {~ [^%nl]* ((%nl (!indent %ws* %nl)*) (indent -> '') [^%nl]*)* ~} %userdata) => add_comment)
+eol_comment:
+ "#" (({} {[^%nl]*} %userdata) => add_comment)
+
+section_division: ("~")^+3 eol
+
+inline_block (Block):
+ ":" %ws* ((inline_statement (%ws* ";" %ws* inline_statement)*) / !(eol nl_indent))
+chunk (Block):
+ statement (nl_nodent statement)*
+indented_block (Block):
+ ":" eol nl_indent statement (nl_nodent statement)*
+
+statement: (action / expression) (eol / (!! [^%nl]+ -> "Unexpected character while parsing line" !!))
+inline_statement: (inline_action / inline_expression)
+
+noindex_inline_expression:
+ number / variable / inline_text / inline_list / inline_dict / inline_nomsu
+ / ( "("
+ %ws* (inline_block / inline_action / inline_expression) %ws*
+ (%ws* ',' %ws* (inline_block / inline_action / inline_expression) %ws*)*
+ (")"
+ / (!! eol -> 'Line ended without finding a closing )-parenthesis' !!)
+ / (!! [^%nl]+ -> 'Unexpected character while parsing subexpression' !!)
+ )
+ )
+inline_expression: index_chain / noindex_inline_expression
+indented_expression:
+ indented_text / indented_nomsu / indented_list / indented_dict / indented_block / ({|
+ "(..)" nl_indent
+ (action / expression) (nl_nodent comment)*
+ (eol / (!! [^%nl]+ -> "Unexpected character while parsing indented expression" !!))
+ |} -> unpack)
+expression:
+ inline_expression / indented_expression / inline_block
+
+inline_nomsu (EscapedNomsu): "\" inline_expression
+indented_nomsu (EscapedNomsu):
+ "\" (noindex_inline_expression / indented_expression)
+
+index_chain (IndexChain):
+ noindex_inline_expression ("." (text_word / noindex_inline_expression))+
+
+-- Actions need either at least 1 word, or at least 2 tokens
+inline_action (Action):
+ !section_division
+ ( (inline_expression (%ws* (inline_expression / word))+)
+ / (word (%ws* (inline_expression / word))*))
+ (%ws* inline_block)?
+action (Action):
+ !section_division
+ ( (expression ((nl_nodent "..")? %ws* (expression / word))+)
+ / (word ((nl_nodent "..")? %ws* (expression / word))*))
+
+word: !number { %operator_char+ / %ident_char+ }
+
+text_word (Text): word
+
+inline_text (Text):
+ !('".."' eol)
+ '"'
+ ({~ (('\"' -> '"') / ('\\' -> '\') / %escaped_char / [^%nl\"])+ ~}
+ / inline_text_interpolation)*
+ ('"'
+ / (!! eol -> 'Line ended before finding a closing double quotation mark' !!)
+ / (!! [^%nl]+ -> 'Unexpected character while parsing Text' !!))
+inline_text_interpolation:
+ "\" (
+ variable / inline_list / inline_dict / inline_text
+ / ("("
+ %ws* (inline_block / inline_action / inline_expression) %ws*
+ (%ws* ',' %ws* (inline_block / inline_action / inline_expression) %ws*)*
+ (")"
+ / (!! eol -> 'Line ended without finding a closing )-parenthesis' !!)
+ / (!! [^%nl]+ -> 'Unexpected character while parsing Text interpolation' !!)))
+ )
+
+indented_text (Text):
+ '".."' eol %nl {:curr_indent: indent :}
+ (indented_plain_text / text_interpolation / {~ blank_lines (=curr_indent -> "") ~})*
+ (!! [^%nl]+ -> "Unexpected character while parsing Text" !!)?
+indented_plain_text (Text):
+ {~ (("\\" -> "\") / (("\" blank_lines =curr_indent "..") -> "") / (!text_interpolation "\") / [^%nl\]+)+
+ (blank_lines (=curr_indent -> ""))* ~}
+text_interpolation:
+ inline_text_interpolation / ("\" indented_expression blank_lines =curr_indent "..")
+
+number (Number): (("-"? (([0-9]+ "." [0-9]+) / ("." [0-9]+) / ([0-9]+)))-> tonumber)
+
+-- Variables can be nameless (i.e. just %) and can't contain operators like apostrophe
+-- which is a hack to allow %'s to parse as "%" and "' s" separately
+variable (Var): "%" {(%ident_char+ ((!"'" %operator_char+) / %ident_char+)*)?}
+
+inline_list (List):
+ !('[..]')
+ "[" %ws*
+ (inline_list_item (%ws* ',' %ws* inline_list_item)* (%ws* ',')?)? %ws*
+ ("]" / (","? (
+ (!! eol -> "Line ended before finding a closing ]-bracket" !!)
+ /(!! [^%nl]+ -> "Unexpected character while parsing List" !!)
+ )))
+indented_list (List):
+ "[..]" eol nl_indent
+ list_line (nl_nodent list_line)* (nl_nodent comment)*
+ (","? (!! [^%nl]+ -> "Unexpected character while parsing List" !!))?
+list_line:
+ (inline_list_item %ws* "," %ws*)+ eol
+ / (inline_list_item %ws* "," %ws*)* (action / expression) eol
+inline_list_item: inline_block / inline_action / inline_expression
+
+inline_dict (Dict):
+ !('{..}')
+ "{" %ws*
+ (inline_dict_entry (%ws* ',' %ws* inline_dict_entry)*)? %ws*
+ ("}" / (","? (
+ (!! eol -> "Line ended before finding a closing }-brace" !!)
+ / (!! [^%nl]* -> "Unexpected character while parsing Dictionary" !!)
+ )))
+indented_dict (Dict):
+ "{..}" eol nl_indent
+ dict_line (nl_nodent dict_line)* (nl_nodent comment)*
+ (","? (!! [^%nl]+ -> "Unexpected character while parsing Dictionary" !!))?
+dict_line:
+ (inline_dict_entry %ws* "," %ws*)+ eol
+ / (inline_dict_entry %ws* "," %ws*)* dict_entry eol
+dict_entry(DictEntry):
+ dict_key (%ws* ":" %ws* (action / expression))?
+inline_dict_entry(DictEntry):
+ dict_key (%ws* ":" %ws* (inline_action / inline_expression)?)?
+dict_key:
+ text_word / inline_expression
diff --git a/nomsu.lua b/nomsu.lua
index a69c8df..6080637 100644
--- a/nomsu.lua
+++ b/nomsu.lua
@@ -197,7 +197,7 @@ run = function()
end
local tree = nomsu:parse(file, source)
if tree then
- if tree.type ~= "File" then
+ if tree.type ~= "FileChunks" then
tree = {
tree
}
diff --git a/nomsu.moon b/nomsu.moon
index e2d79bd..cba9eee 100755
--- a/nomsu.moon
+++ b/nomsu.moon
@@ -130,7 +130,7 @@ run = ->
return unless file
tree = nomsu\parse(file, source)
if tree
- if tree.type != "File"
+ 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
diff --git a/nomsu_compiler.lua b/nomsu_compiler.lua
index b56fde8..57ed5c1 100644
--- a/nomsu_compiler.lua
+++ b/nomsu_compiler.lua
@@ -350,10 +350,13 @@ do
end
end
})
- NomsuCompiler.run = function(self, to_run, source)
+ NomsuCompiler.run = function(self, to_run, source, version)
if source == nil then
source = nil
end
+ if version == nil then
+ version = nil
+ end
source = source or (to_run.source or Source(to_run, 1, #to_run))
if type(source) == 'string' then
source = Source:from_string(source)
@@ -365,29 +368,27 @@ do
if AST.is_syntax_tree(to_run) then
tree = to_run
else
- tree = self:parse(to_run, source)
+ tree = self:parse(to_run, source, version)
end
if tree == nil then
return nil
end
- if tree.type == "File" then
- local ret = nil
- local all_lua = { }
- for _index_0 = 1, #tree do
- local chunk = tree[_index_0]
- local lua = self:compile(chunk):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
- else
- local lua = self:compile(tree):as_statements("return ")
+ 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):as_statements("return ")
lua:declare_locals()
lua:prepend("-- File: " .. tostring(source.filename:gsub("\n.*", "...")) .. "\n")
- return self:run_lua(lua)
+ insert(all_lua, tostring(lua))
+ ret = self:run_lua(lua)
end
+ return ret
end
local _running_files = { }
NomsuCompiler.run_file = function(self, filename)
@@ -571,7 +572,8 @@ do
end
bits = _accum_0
end
- return t.type .. "(" .. repr(tostring(t.source)) .. ", " .. table.concat(bits, ", ") .. ")"
+ insert(bits, 1, repr(tostring(t.source)))
+ return t.type .. "(" .. concat(bits, ", ") .. ")"
end
return LuaCode.Value(tree.source, make_tree(tree[1]))
elseif "Block" == _exp_0 then
@@ -711,13 +713,13 @@ do
return LuaCode.Value(tree.source, tostring(tree[1]))
elseif "Var" == _exp_0 then
return LuaCode.Value(tree.source, string.as_lua_id(tree[1]))
- elseif "File" == _exp_0 then
- return error("Cannot convert File to a single block of lua, since each chunk's " .. "compilation depends on the earlier chunks")
+ elseif "FileChunks" == _exp_0 then
+ return error("Cannot convert FileChunks to a single block of lua, since each chunk's " .. "compilation depends on the earlier chunks")
else
return error("Unknown type: " .. tostring(tree.type))
end
end
- local MIN_COLON_LEN = 25
+ local MIN_COLON_LEN = 20
NomsuCompiler.tree_to_nomsu = function(self, tree, options)
options = options or { }
if not (options.pop_comments) then
@@ -740,7 +742,10 @@ do
return a.pos < b.pos
end)
local comment_i = 1
- options.pop_comments = function(pos)
+ options.pop_comments = function(pos, prefix)
+ if prefix == nil then
+ prefix = ''
+ end
local nomsu = NomsuCode(tree.source)
while comments[comment_i] and comments[comment_i].pos <= pos do
local comment = comments[comment_i].comment
@@ -750,7 +755,11 @@ do
end
comment_i = comment_i + 1
end
- return #nomsu.bits == 0 and '' or nomsu
+ if #nomsu.bits == 0 then
+ return ''
+ end
+ nomsu:prepend(prefix)
+ return nomsu
end
end
local recurse
@@ -759,12 +768,12 @@ do
opts.pop_comments = options.pop_comments
return self:tree_to_nomsu(t, opts)
end
- local inline, can_use_colon, pop_comments
- inline, can_use_colon, pop_comments = options.inline, options.can_use_colon, options.pop_comments
+ local inline, pop_comments
+ inline, pop_comments = options.inline, options.pop_comments
local _exp_0 = tree.type
- if "File" == _exp_0 then
+ if "FileChunks" == _exp_0 then
if inline then
- return nil
+ error("Cannot inline a FileChunks")
end
local nomsu = NomsuCode(tree.source)
for i, chunk in ipairs(tree) do
@@ -772,7 +781,9 @@ do
nomsu:append("\n\n" .. tostring(("~"):rep(80)) .. "\n\n")
end
nomsu:append(pop_comments(chunk.source.start))
- nomsu:append(recurse(chunk))
+ nomsu:append(recurse(chunk, {
+ top = true
+ }))
end
nomsu:append(pop_comments(tree.source.stop))
return nomsu
@@ -792,16 +803,12 @@ do
if not (arg_nomsu) then
return nil
end
- if bit.type == "Action" or bit.type == "Block" then
- if bit.type == "Action" and i == #tree and #tostring(arg_nomsu) >= MIN_COLON_LEN then
- nomsu:append(":")
- else
- arg_nomsu:parenthesize()
- end
- end
if not (i == 1) then
nomsu:append(" ")
end
+ if bit.type == "Action" or (bit.type == "Block" and (#bit > 1 or i < #tree)) then
+ arg_nomsu:parenthesize()
+ end
nomsu:append(arg_nomsu)
end
end
@@ -809,100 +816,79 @@ do
else
local nomsu = NomsuCode(tree.source)
local next_space = ""
- local line_len, last_colon = 0, nil
for i, bit in ipairs(tree) do
if type(bit) == "string" then
- line_len = line_len + #next_space + #bit
nomsu:append(next_space, bit)
next_space = " "
else
local arg_nomsu
- if last_colon == i - 1 and bit.type == "Action" then
- arg_nomsu = nil
- elseif bit.type == "Block" then
+ if bit.type == "Block" and #bit > 1 then
arg_nomsu = nil
else
- arg_nomsu = recurse(bit, {
+ arg_nomsu = assert(recurse(bit, {
inline = true
- })
+ }))
end
- if arg_nomsu and line_len + #tostring(arg_nomsu) < MAX_LINE then
- if bit.type == "Action" then
- if can_use_colon and i > 1 and #tostring(arg_nomsu) >= MIN_COLON_LEN then
- nomsu:append(match(next_space, "[^ ]*"), ": ", arg_nomsu)
- next_space = "\n.."
- line_len = 2
- last_colon = i
- else
- nomsu:append(next_space, "(", arg_nomsu, ")")
- line_len = line_len + #next_space + 2 + #tostring(arg_nomsu)
- next_space = " "
- end
+ if bit.type == "Block" then
+ next_space = match(next_space, "[^ ]*")
+ end
+ nomsu:append(next_space)
+ if arg_nomsu and nomsu.trailing_line_len + #tostring(arg_nomsu) < MAX_LINE then
+ if bit.type == "Block" then
+ nomsu:append(arg_nomsu)
+ next_space = "\n.."
else
- nomsu:append(next_space, arg_nomsu)
- line_len = line_len + #next_space + #tostring(arg_nomsu)
+ if bit.type == "Action" then
+ arg_nomsu:parenthesize()
+ end
+ nomsu:append(arg_nomsu)
next_space = " "
end
else
- arg_nomsu = recurse(bit, {
- can_use_colon = true
- })
- if not (arg_nomsu) then
- return nil
- end
- if bit.type ~= "List" and bit.type ~= "Dict" and bit.type ~= "Text" then
- if i == 1 then
- arg_nomsu = NomsuCode(bit.source, "(..)\n ", pop_comments(bit.source.start), arg_nomsu)
- else
- arg_nomsu = NomsuCode(bit.source, "\n ", pop_comments(bit.source.start), arg_nomsu)
- end
- end
- if last_colon == i - 1 and (bit.type == "Action" or bit.type == "Block") then
- next_space = ""
+ arg_nomsu = assert(recurse(bit))
+ if bit.type ~= "List" and bit.type ~= "Dict" and bit.type ~= "Text" and bit.type ~= "Block" then
+ nomsu:append(NomsuCode(bit.source, "(..)\n ", pop_comments(bit.source.start), arg_nomsu))
+ else
+ nomsu:append(arg_nomsu)
end
- nomsu:append(next_space, arg_nomsu)
- next_space = "\n.."
- line_len = 2
- end
- if next_space == " " and #(match(tostring(nomsu), "[^\n]*$")) > MAX_LINE then
next_space = "\n.."
end
end
+ if next_space == " " and nomsu.trailing_line_len > MAX_LINE then
+ next_space = "\n.."
+ end
end
return nomsu
end
elseif "EscapedNomsu" == _exp_0 then
- local nomsu = recurse(tree[1], {
+ local nomsu = NomsuCode(tree.source, "\\(", assert(recurse(tree[1], {
inline = true
- })
- if nomsu == nil and not inline then
- nomsu = recurse(tree[1])
- return nomsu and NomsuCode(tree.source, "\\:\n ", pop_comments(tree.source.start), nomsu)
+ })), ")")
+ if inline or #tostring(nomsu) <= MAX_LINE then
+ return nomsu
+ end
+ nomsu = assert(recurse(tree[1]))
+ local _exp_1 = tree[1].type
+ if "List" == _exp_1 or "Dict" == _exp_1 or "Text" == _exp_1 or "Block" == _exp_1 then
+ return NomsuCode(tree.source, "\\", nomsu)
+ else
+ return NomsuCode(tree.source, "\\(..)\n ", pop_comments(tree.source.start), nomsu)
end
- return nomsu and NomsuCode(tree.source, "\\(", nomsu, ")")
elseif "Block" == _exp_0 then
if inline then
- local nomsu = NomsuCode(tree.source)
+ local nomsu = NomsuCode(tree.source, ":")
for i, line in ipairs(tree) do
- if i > 1 then
- nomsu:append("; ")
- end
- local line_nomsu = recurse(line, {
+ nomsu:append(i == 1 and " " or "; ")
+ nomsu:append(assert(recurse(line, {
inline = true
- })
- if not (line_nomsu) then
- return nil
- end
- nomsu:append(line_nomsu)
+ })))
end
return nomsu
end
local nomsu = NomsuCode(tree.source)
for i, line in ipairs(tree) do
nomsu:append(pop_comments(line.source.start))
- line = assert(recurse(line, {
- can_use_colon = true
- }), "Could not convert line to nomsu")
+ line = assert(recurse(line), "Could not convert line to nomsu")
nomsu:append(line)
if i < #tree then
nomsu:append("\n")
@@ -911,7 +897,7 @@ do
end
end
end
- return nomsu
+ return options.top and nomsu or NomsuCode(tree.source, ":\n ", nomsu)
elseif "Text" == _exp_0 then
if inline then
local make_text
@@ -927,7 +913,7 @@ do
local interp_nomsu = assert(recurse(bit, {
inline = true
}))
- if bit.type ~= "Var" and bit.type ~= "List" and bit.type ~= "Dict" and bit.type ~= "Text" then
+ if bit.type ~= "Var" and bit.type ~= "List" and bit.type ~= "Dict" then
interp_nomsu:parenthesize()
end
nomsu:append("\\", interp_nomsu)
@@ -985,7 +971,7 @@ do
inline = true
})
if interp_nomsu then
- if bit.type ~= "Var" and bit.type ~= "List" and bit.type ~= "Dict" and bit.type ~= "Text" then
+ if bit.type ~= "Var" and bit.type ~= "List" and bit.type ~= "Dict" then
interp_nomsu:parenthesize()
end
nomsu:append("\\", interp_nomsu)
@@ -1009,16 +995,12 @@ do
if inline then
local nomsu = NomsuCode(tree.source, "[")
for i, item in ipairs(tree) do
- local item_nomsu = recurse(item, {
- inline = true
- })
- if not (item_nomsu) then
- return nil
- end
if i > 1 then
nomsu:append(", ")
end
- nomsu:append(item_nomsu)
+ nomsu:append(assert(recurse(item, {
+ inline = true
+ })))
end
nomsu:append("]")
return nomsu
@@ -1026,67 +1008,56 @@ do
local inline_version = recurse(tree, {
inline = true
})
- if inline_version and #inline_version <= MAX_LINE then
+ if inline_version and #tostring(inline_version) <= MAX_LINE then
return inline_version
end
- local nomsu = NomsuCode(tree.source, "[..]")
- local line = NomsuCode(tree.source, "\n ")
- local line_comments
- if #tree > 0 then
- line_comments = pop_comments(tree[1].source.start)
- else
- line_comments = ''
- end
+ assert(#tree > 0)
+ local nomsu = NomsuCode(tree.source, pop_comments(tree[1].source.start))
for i, item in ipairs(tree) do
- local item_nomsu = recurse(item, {
+ local item_nomsu = assert(recurse(item, {
inline = true
- })
- if item_nomsu and #tostring(line) + #", " + #item_nomsu <= MAX_LINE then
- if #line.bits > 1 then
- line:append(", ")
+ }))
+ if item.type == "Block" then
+ item_nomsu:parenthesize()
+ end
+ if nomsu.trailing_line_len + #tostring(item_nomsu) <= MAX_LINE then
+ if nomsu.trailing_line_len > 0 then
+ nomsu:append(", ")
end
- line:append(item_nomsu)
+ nomsu:append(item_nomsu)
else
- if not (item_nomsu) then
+ if #tostring(item_nomsu) > MAX_LINE then
item_nomsu = recurse(item)
- if not (item_nomsu) then
- return nil
- end
- end
- if #line.bits > 1 then
- if #tostring(line_comments) > 0 then
- nomsu:append('\n ', line_comments)
+ local _exp_1 = item.type
+ if "List" == _exp_1 or "Dict" == _exp_1 or "Text" == _exp_1 or "Block" == _exp_1 then
+ nomsu:append(item_nomsu)
+ else
+ nomsu:append("(..)\n ", item_nomsu)
end
- nomsu:append(line)
- line = NomsuCode(line.source, "\n ")
if i < #tree then
- line_comments = pop_comments(tree[i + 1].source.start)
- else
- line_comments = ''
+ nomsu:append("\n")
+ end
+ else
+ if nomsu.trailing_line_len > 0 then
+ nomsu:append('\n')
end
+ nomsu:append(pop_comments(item.source.start), item_nomsu)
end
- line:append(item_nomsu)
end
end
- if #line.bits > 1 then
- nomsu:append(line)
- end
- return nomsu
+ nomsu:append(pop_comments(tree.source.stop, '\n'))
+ return NomsuCode(tree.source, "[..]\n ", nomsu)
end
elseif "Dict" == _exp_0 then
if inline then
local nomsu = NomsuCode(tree.source, "{")
for i, entry in ipairs(tree) do
- local entry_nomsu = recurse(entry, {
- inline = true
- })
- if not (entry_nomsu) then
- return nil
- end
if i > 1 then
nomsu:append(", ")
end
- nomsu:append(entry_nomsu)
+ nomsu:append(assert(recurse(entry, {
+ inline = true
+ })))
end
nomsu:append("}")
return nomsu
@@ -1094,80 +1065,75 @@ do
local inline_version = recurse(tree, {
inline = true
})
- if inline_version then
+ if inline_version and #tostring(inline_version) <= MAX_LINE then
return inline_version
end
- local nomsu = NomsuCode(tree.source, "{..}")
- local line = NomsuCode(tree.source, "\n ")
- local line_comments
- if #tree > 0 then
- line_comments = pop_comments(tree[1].source.start)
- else
- line_comments = ''
- end
- for i, entry in ipairs(tree) do
- local entry_nomsu = recurse(entry)
- if not (entry_nomsu) then
- return nil
+ assert(#tree > 0)
+ local nomsu = NomsuCode(tree.source, pop_comments(tree[1].source.start))
+ for i, item in ipairs(tree) do
+ local item_nomsu = assert(recurse(item, {
+ inline = true
+ }))
+ if item.type == "Block" then
+ item_nomsu:parenthesize()
end
- if #line + #tostring(entry_nomsu) <= MAX_LINE then
- if #line.bits > 1 then
- line:append(", ")
+ if nomsu.trailing_line_len + #tostring(item_nomsu) <= MAX_LINE then
+ if nomsu.trailing_line_len > 0 then
+ nomsu:append(", ")
end
- line:append(entry_nomsu)
+ nomsu:append(item_nomsu)
else
- if #line.bits > 1 then
- if #tostring(line_comments) > 0 then
- nomsu:append("\n ", line_comments)
+ if #tostring(item_nomsu) > MAX_LINE then
+ item_nomsu = recurse(item)
+ local _exp_1 = item.type
+ if "List" == _exp_1 or "Dict" == _exp_1 or "Text" == _exp_1 or "Block" == _exp_1 then
+ nomsu:append(item_nomsu)
+ else
+ nomsu:append("(..)\n ", item_nomsu)
end
- nomsu:append(line)
- line = NomsuCode(line.source, "\n ")
if i < #tree then
- line_comments = pop_comments(tree[1].source.start)
- else
- line_comments = ''
+ nomsu:append("\n")
+ end
+ else
+ if nomsu.trailing_line_len > 0 then
+ nomsu:append('\n')
end
+ nomsu:append(pop_comments(item.source.start), item_nomsu)
end
- line:append(entry_nomsu)
end
end
- if #line.bits > 1 then
- nomsu:append(line)
- end
- return nomsu
+ nomsu:append(pop_comments(tree.source.stop, '\n'))
+ return NomsuCode(tree.source, "{..}\n ", nomsu)
end
elseif "DictEntry" == _exp_0 then
local key, value = tree[1], tree[2]
- local key_nomsu = recurse(key, {
+ local key_nomsu = assert(recurse(key, {
inline = true
- })
- if not (key_nomsu) then
- return nil
- end
+ }))
if key.type == "Action" or key.type == "Block" then
key_nomsu:parenthesize()
end
local value_nomsu
if value then
- value_nomsu = recurse(value, {
+ value_nomsu = assert(recurse(value, {
inline = true
- })
+ }))
else
value_nomsu = NomsuCode(tree.source, "")
end
- if inline and not value_nomsu then
- return nil
+ assert(value.type ~= "Block", "Didn't expect to find a Block as a value in a dict")
+ if value.type == "Block" then
+ value_nomsu:parenthesize()
end
- if not value_nomsu then
- if inline then
- return nil
- end
- value_nomsu = recurse(value)
- if not (value_nomsu) then
- return nil
- end
+ if inline or #tostring(key_nomsu) + 2 + #tostring(value_nomsu) <= MAX_LINE then
+ return NomsuCode(tree.source, key_nomsu, ": ", value_nomsu)
+ end
+ value_nomsu = recurse(value)
+ if value.type == "List" or value.type == "Dict" or value.type == "Text" then
+ return NomsuCode(tree.source, key_nomsu, ": ", value_nomsu)
+ else
+ return NomsuCode(tree.source, key_nomsu, ": (..)\n ", value_nomsu)
end
- return NomsuCode(tree.source, key_nomsu, ":", value_nomsu)
elseif "IndexChain" == _exp_0 then
local nomsu = NomsuCode(tree.source)
for i, bit in ipairs(tree) do
@@ -1175,18 +1141,12 @@ do
nomsu:append(".")
end
local bit_nomsu
- if bit.type == "Text" and #bit == 1 and type(bit[1]) == 'string' then
- if bit[1]:match("[_a-zA-Z][_a-zA-Z0-9]*") then
- bit_nomsu = bit[1]
- end
- end
- if not (bit_nomsu) then
- bit_nomsu = recurse(bit, {
+ if bit.type == "Text" and #bit == 1 and type(bit[1]) == 'string' and bit[1]:match("[_a-zA-Z][_a-zA-Z0-9]*") then
+ bit_nomsu = bit[1]
+ else
+ bit_nomsu = assert(recurse(bit, {
inline = true
- })
- end
- if not (bit_nomsu) then
- return nil
+ }))
end
local _exp_1 = bit.type
if "Action" == _exp_1 or "Block" == _exp_1 or "IndexChain" == _exp_1 then
diff --git a/nomsu_compiler.moon b/nomsu_compiler.moon
index eda79d8..d1b76f4 100644
--- a/nomsu_compiler.moon
+++ b/nomsu_compiler.moon
@@ -229,31 +229,27 @@ with NomsuCompiler
return @["# compile math expr #"]
}
- .run = (to_run, source=nil)=>
+ .run = (to_run, source=nil, version=nil)=>
source or= to_run.source or Source(to_run, 1, #to_run)
if type(source) == 'string' then source = Source\from_string(source)
if not files.read(source.filename) then files.spoof(source.filename, to_run)
- tree = if AST.is_syntax_tree(to_run) then to_run else @parse(to_run, source)
+ tree = if AST.is_syntax_tree(to_run) then to_run else @parse(to_run, source, version)
if tree == nil -- Happens if pattern matches, but there are no captures, e.g. an empty string
return nil
- if tree.type == "File"
- -- 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)\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
- else
- lua = @compile(tree)\as_statements("return ")
+ 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)\as_statements("return ")
lua\declare_locals!
lua\prepend "-- File: #{source.filename\gsub("\n.*", "...")}\n"
- return @run_lua(lua)
+ insert all_lua, tostring(lua)
+ ret = @run_lua(lua)
+ return ret
_running_files = {} -- For detecting circular imports
.run_file = (filename)=>
@@ -362,7 +358,8 @@ with NomsuCompiler
unless AST.is_syntax_tree(t)
return repr(t)
bits = [make_tree(bit) for bit in *t]
- return t.type.."("..repr(tostring t.source)..", "..table.concat(bits, ", ")..")"
+ insert bits, 1, repr(tostring t.source)
+ return t.type.."("..concat(bits, ", ")..")"
return LuaCode.Value tree.source, make_tree(tree[1])
when "Block"
@@ -474,44 +471,45 @@ with NomsuCompiler
when "Var"
return LuaCode.Value(tree.source, string.as_lua_id(tree[1]))
- when "File"
- error("Cannot convert File to a single block of lua, since each chunk's "..
+ when "FileChunks"
+ error("Cannot convert FileChunks to a single block of lua, since each chunk's "..
"compilation depends on the earlier chunks")
else
error("Unknown type: #{tree.type}")
- MIN_COLON_LEN = 25 -- For beautification purposes, don't bother using colon syntax for short bits
+ MIN_COLON_LEN = 20 -- For beautification purposes, don't bother using colon syntax for short bits
.tree_to_nomsu = (tree, options)=>
options or= {}
unless options.pop_comments
comments = [{comment:c, pos:p} for p,c in pairs(tree.comments or {}) when tree.source.start <= p and p <= tree.source.stop]
table.sort comments, (a,b)-> a.pos < b.pos
comment_i = 1
- options.pop_comments = (pos)->
+ options.pop_comments = (pos, prefix='')->
nomsu = NomsuCode(tree.source)
while comments[comment_i] and comments[comment_i].pos <= pos
comment = comments[comment_i].comment
nomsu\append("#"..(gsub(comment, "\n", "\n ")).."\n")
if comment\match("^\n.") then nomsu\append("\n") -- for aesthetics
comment_i += 1
- return #nomsu.bits == 0 and '' or nomsu
+ return '' if #nomsu.bits == 0
+ nomsu\prepend prefix
+ return nomsu
recurse = (t, opts)->
opts or= {}
opts.pop_comments = options.pop_comments
return @tree_to_nomsu(t, opts)
- {:inline, :can_use_colon, :pop_comments} = options
+ {:inline, :pop_comments} = options
switch tree.type
- when "File"
- return nil if inline
+ when "FileChunks"
+ error("Cannot inline a FileChunks") if inline
nomsu = NomsuCode(tree.source)
for i, chunk in ipairs tree
- if i > 1
- nomsu\append "\n\n#{("~")\rep(80)}\n\n"
+ nomsu\append "\n\n#{("~")\rep(80)}\n\n" if i > 1
nomsu\append pop_comments(chunk.source.start)
- nomsu\append recurse(chunk)
+ nomsu\append recurse(chunk, top:true)
nomsu\append pop_comments(tree.source.stop)
return nomsu
@@ -520,97 +518,78 @@ with NomsuCompiler
nomsu = NomsuCode(tree.source)
for i,bit in ipairs tree
if type(bit) == "string"
- if i > 1
- nomsu\append " "
+ nomsu\append " " if i > 1
nomsu\append bit
else
arg_nomsu = recurse(bit,inline:true)
return nil unless arg_nomsu
- if bit.type == "Action" or bit.type == "Block"
- if bit.type == "Action" and i == #tree and #tostring(arg_nomsu) >= MIN_COLON_LEN
- nomsu\append ":"
- else
- arg_nomsu\parenthesize!
unless i == 1
nomsu\append " "
+ if bit.type == "Action" or (bit.type == "Block" and (#bit > 1 or i < #tree))
+ arg_nomsu\parenthesize!
nomsu\append arg_nomsu
return nomsu
else
nomsu = NomsuCode(tree.source)
next_space = ""
- line_len, last_colon = 0, nil
for i,bit in ipairs tree
if type(bit) == "string"
- line_len += #next_space + #bit
nomsu\append next_space, bit
next_space = " "
else
- arg_nomsu = if last_colon == i-1 and bit.type == "Action" then nil
- elseif bit.type == "Block" then nil
- else recurse(bit,inline:true)
-
- if arg_nomsu and line_len + #tostring(arg_nomsu) < MAX_LINE
- if bit.type == "Action"
- if can_use_colon and i > 1 and #tostring(arg_nomsu) >= MIN_COLON_LEN
- nomsu\append match(next_space,"[^ ]*"), ": ", arg_nomsu
- next_space = "\n.."
- line_len = 2
- last_colon = i
- else
- nomsu\append next_space, "(", arg_nomsu, ")"
- line_len += #next_space + 2 + #tostring(arg_nomsu)
- next_space = " "
+ arg_nomsu = if bit.type == "Block" and #bit > 1 then nil
+ else assert recurse(bit,inline:true)
+ next_space = match(next_space, "[^ ]*") if bit.type == "Block"
+ nomsu\append next_space
+ if arg_nomsu and nomsu.trailing_line_len + #tostring(arg_nomsu) < MAX_LINE
+ if bit.type == "Block"
+ nomsu\append arg_nomsu
+ next_space = "\n.."
else
- nomsu\append next_space, arg_nomsu
- line_len += #next_space + #tostring(arg_nomsu)
+ arg_nomsu\parenthesize! if bit.type == "Action"
+ nomsu\append arg_nomsu
next_space = " "
else
- arg_nomsu = recurse(bit, can_use_colon:true)
- return nil unless arg_nomsu
+ arg_nomsu = assert recurse(bit)
-- These types carry their own indentation
- if bit.type != "List" and bit.type != "Dict" and bit.type != "Text"
- if i == 1
- arg_nomsu = NomsuCode(bit.source, "(..)\n ", pop_comments(bit.source.start), arg_nomsu)
- else
- arg_nomsu = NomsuCode(bit.source, "\n ", pop_comments(bit.source.start), arg_nomsu)
-
- if last_colon == i-1 and (bit.type == "Action" or bit.type == "Block")
- next_space = ""
- nomsu\append next_space, arg_nomsu
+ if bit.type != "List" and bit.type != "Dict" and bit.type != "Text" and bit.type != "Block"
+ nomsu\append NomsuCode(bit.source, "(..)\n ", pop_comments(bit.source.start), arg_nomsu)
+ else
+ nomsu\append arg_nomsu
next_space = "\n.."
- line_len = 2
- if next_space == " " and #(match(tostring(nomsu),"[^\n]*$")) > MAX_LINE
- next_space = "\n.."
+ if next_space == " " and nomsu.trailing_line_len > MAX_LINE
+ next_space = "\n.."
return nomsu
when "EscapedNomsu"
- nomsu = recurse(tree[1], inline:true)
- if nomsu == nil and not inline
- nomsu = recurse(tree[1])
- return nomsu and NomsuCode tree.source, "\\:\n ", pop_comments(tree.source.start), nomsu
- return nomsu and NomsuCode tree.source, "\\(", nomsu, ")"
+ nomsu = NomsuCode(tree.source, "\\(", assert(recurse(tree[1], inline:true)), ")")
+ if inline or #tostring(nomsu) <= MAX_LINE
+ return nomsu
+ nomsu = assert recurse(tree[1])
+ switch tree[1].type
+ when "List", "Dict", "Text", "Block"
+ return NomsuCode tree.source, "\\", nomsu
+ else
+ return NomsuCode tree.source, "\\(..)\n ", pop_comments(tree.source.start), nomsu
when "Block"
if inline
- nomsu = NomsuCode(tree.source)
+ nomsu = NomsuCode(tree.source, ":")
for i,line in ipairs tree
- if i > 1
- nomsu\append "; "
- line_nomsu = recurse(line,inline:true)
- return nil unless line_nomsu
- nomsu\append line_nomsu
+ nomsu\append(i == 1 and " " or "; ")
+ nomsu\append assert(recurse(line, inline:true))
return nomsu
nomsu = NomsuCode(tree.source)
for i, line in ipairs tree
nomsu\append pop_comments(line.source.start)
- line = assert(recurse(line, can_use_colon:true), "Could not convert line to nomsu")
+ line = assert(recurse(line), "Could not convert line to nomsu")
nomsu\append line
if i < #tree
nomsu\append "\n"
if match(tostring(line), "\n")
nomsu\append "\n"
- return nomsu
+ return options.top and nomsu or NomsuCode(tree.source, ":\n ", nomsu)
when "Text"
if inline
@@ -624,7 +603,7 @@ with NomsuCompiler
nomsu\append(make_text(bit))
else
interp_nomsu = assert recurse(bit, inline:true)
- if bit.type != "Var" and bit.type != "List" and bit.type != "Dict" and bit.type != "Text"
+ if bit.type != "Var" and bit.type != "List" and bit.type != "Dict"
interp_nomsu\parenthesize!
nomsu\append "\\", interp_nomsu
return nomsu
@@ -662,7 +641,7 @@ with NomsuCompiler
else
interp_nomsu = recurse(bit, inline:true)
if interp_nomsu
- if bit.type != "Var" and bit.type != "List" and bit.type != "Dict" and bit.type != "Text"
+ if bit.type != "Var" and bit.type != "List" and bit.type != "Dict"
interp_nomsu\parenthesize!
nomsu\append "\\", interp_nomsu
else
@@ -678,113 +657,98 @@ with NomsuCompiler
if inline
nomsu = NomsuCode(tree.source, "[")
for i, item in ipairs tree
- item_nomsu = recurse(item, inline:true)
- return nil unless item_nomsu
- if i > 1
- nomsu\append ", "
- nomsu\append item_nomsu
+ nomsu\append ", " if i > 1
+ nomsu\append assert(recurse(item, inline:true))
nomsu\append "]"
return nomsu
else
inline_version = recurse(tree, inline:true)
- if inline_version and #inline_version <= MAX_LINE
+ if inline_version and #tostring(inline_version) <= MAX_LINE
return inline_version
- nomsu = NomsuCode(tree.source, "[..]")
- line = NomsuCode(tree.source, "\n ")
- line_comments = if #tree > 0
- pop_comments(tree[1].source.start)
- else ''
+ assert #tree > 0
+ nomsu = NomsuCode(tree.source, pop_comments(tree[1].source.start))
for i, item in ipairs tree
- item_nomsu = recurse(item, inline:true)
- if item_nomsu and #tostring(line) + #", " + #item_nomsu <= MAX_LINE
- if #line.bits > 1
- line\append ", "
- line\append item_nomsu
+ item_nomsu = assert recurse(item, inline:true)
+ item_nomsu\parenthesize! if item.type == "Block"
+ if nomsu.trailing_line_len + #tostring(item_nomsu) <= MAX_LINE
+ nomsu\append ", " if nomsu.trailing_line_len > 0
+ nomsu\append item_nomsu
else
- unless item_nomsu
+ if #tostring(item_nomsu) > MAX_LINE
item_nomsu = recurse(item)
- return nil unless item_nomsu
- if #line.bits > 1
- if #tostring(line_comments) > 0
- nomsu\append '\n ', line_comments
- nomsu\append line
- line = NomsuCode(line.source, "\n ")
- line_comments = if i < #tree
- pop_comments(tree[i+1].source.start)
- else ''
- line\append item_nomsu
- if #line.bits > 1
- nomsu\append line
- return nomsu
+ switch item.type
+ when "List", "Dict", "Text", "Block"
+ nomsu\append item_nomsu
+ else
+ nomsu\append "(..)\n ", item_nomsu
+ nomsu\append "\n" if i < #tree
+ else
+ nomsu\append '\n' if nomsu.trailing_line_len > 0
+ nomsu\append pop_comments(item.source.start), item_nomsu
+ nomsu\append pop_comments(tree.source.stop, '\n')
+ return NomsuCode(tree.source, "[..]\n ", nomsu)
when "Dict"
if inline
nomsu = NomsuCode(tree.source, "{")
for i, entry in ipairs tree
- entry_nomsu = recurse(entry, inline:true)
- return nil unless entry_nomsu
- if i > 1
- nomsu\append ", "
- nomsu\append entry_nomsu
+ nomsu\append ", " if i > 1
+ nomsu\append assert(recurse(entry, inline:true))
nomsu\append "}"
return nomsu
else
inline_version = recurse(tree, inline:true)
- if inline_version then return inline_version
- nomsu = NomsuCode(tree.source, "{..}")
- line = NomsuCode(tree.source, "\n ")
- line_comments = if #tree > 0
- pop_comments(tree[1].source.start)
- else ''
- for i, entry in ipairs tree
- entry_nomsu = recurse(entry)
- return nil unless entry_nomsu
- if #line + #tostring(entry_nomsu) <= MAX_LINE
- if #line.bits > 1
- line\append ", "
- line\append entry_nomsu
+ if inline_version and #tostring(inline_version) <= MAX_LINE
+ return inline_version
+ assert #tree > 0
+ nomsu = NomsuCode(tree.source, pop_comments(tree[1].source.start))
+ for i, item in ipairs tree
+ item_nomsu = assert recurse(item, inline:true)
+ item_nomsu\parenthesize! if item.type == "Block"
+ if nomsu.trailing_line_len + #tostring(item_nomsu) <= MAX_LINE
+ nomsu\append ", " if nomsu.trailing_line_len > 0
+ nomsu\append item_nomsu
else
- if #line.bits > 1
- if #tostring(line_comments) > 0
- nomsu\append "\n ", line_comments
- nomsu\append line
- line = NomsuCode(line.source, "\n ")
- line_comments = if i < #tree
- pop_comments(tree[1].source.start)
- else ''
- line\append entry_nomsu
- if #line.bits > 1
- nomsu\append line
- return nomsu
+ if #tostring(item_nomsu) > MAX_LINE
+ item_nomsu = recurse(item)
+ switch item.type
+ when "List", "Dict", "Text", "Block"
+ nomsu\append item_nomsu
+ else
+ nomsu\append "(..)\n ", item_nomsu
+ nomsu\append "\n" if i < #tree
+ else
+ nomsu\append '\n' if nomsu.trailing_line_len > 0
+ nomsu\append pop_comments(item.source.start), item_nomsu
+ nomsu\append pop_comments(tree.source.stop, '\n')
+ return NomsuCode(tree.source, "{..}\n ", nomsu)
when "DictEntry"
key, value = tree[1], tree[2]
- key_nomsu = recurse(key, inline:true)
- return nil unless key_nomsu
- if key.type == "Action" or key.type == "Block"
- key_nomsu\parenthesize!
+ key_nomsu = assert recurse(key, inline:true)
+ key_nomsu\parenthesize! if key.type == "Action" or key.type == "Block"
value_nomsu = if value
- recurse(value, inline:true)
+ assert recurse(value, inline:true)
else NomsuCode(tree.source, "")
- if inline and not value_nomsu then return nil
- if not value_nomsu
- return nil if inline
- value_nomsu = recurse(value)
- return nil unless value_nomsu
- return NomsuCode tree.source, key_nomsu, ":", value_nomsu
+ assert(value.type != "Block", "Didn't expect to find a Block as a value in a dict")
+ value_nomsu\parenthesize! if value.type == "Block"
+ if inline or #tostring(key_nomsu) + 2 + #tostring(value_nomsu) <= MAX_LINE
+ return NomsuCode tree.source, key_nomsu, ": ", value_nomsu
+ value_nomsu = recurse(value)
+ if value.type == "List" or value.type == "Dict" or value.type == "Text"
+ return NomsuCode tree.source, key_nomsu, ": ", value_nomsu
+ else
+ return NomsuCode tree.source, key_nomsu, ": (..)\n ", value_nomsu
when "IndexChain"
nomsu = NomsuCode(tree.source)
for i, bit in ipairs tree
- if i > 1
- nomsu\append "."
+ nomsu\append "." if i > 1
local bit_nomsu
- if bit.type == "Text" and #bit == 1 and type(bit[1]) == 'string'
+ bit_nomsu = if bit.type == "Text" and #bit == 1 and type(bit[1]) == 'string' and bit[1]\match("[_a-zA-Z][_a-zA-Z0-9]*")
-- TODO: support arbitrary words here, including operators and unicode
- if bit[1]\match("[_a-zA-Z][_a-zA-Z0-9]*")
- bit_nomsu = bit[1]
- unless bit_nomsu then bit_nomsu = recurse(bit, inline:true)
- return nil unless bit_nomsu
+ bit[1]
+ else assert recurse(bit, inline:true)
switch bit.type
when "Action", "Block", "IndexChain"
bit_nomsu\parenthesize!
diff --git a/nomsu_tree.lua b/nomsu_tree.lua
index d5875bc..2e733a2 100644
--- a/nomsu_tree.lua
+++ b/nomsu_tree.lua
@@ -26,7 +26,7 @@ local types = {
"DictEntry",
"IndexChain",
"Action",
- "File"
+ "FileChunks"
}
for _index_0 = 1, #types do
local name = types[_index_0]
@@ -52,35 +52,25 @@ for _index_0 = 1, #types do
end)(), ', ')) .. ")"
end
cls.map = function(self, fn)
- do
- local replacement = fn(self)
- if replacement then
- return replacement
- end
+ local replacement = fn(self)
+ if replacement ~= nil then
+ return replacement or nil
end
- local replacements
- do
- local _accum_0 = { }
- local _len_0 = 1
- for _index_1 = 1, #self do
- local v = self[_index_1]
- _accum_0[_len_0] = AST.is_syntax_tree(v) and v:map(fn) or nil
- _len_0 = _len_0 + 1
+ local replacements = { }
+ local changes = false
+ for i, v in ipairs(self) do
+ if AST.is_syntax_tree(v) then
+ replacement = v:map(fn)
+ else
+ replacement = v
end
- replacements = _accum_0
+ changes = changes or (replacement ~= v)
+ replacements[#replacements + 1] = replacement
end
- if not (next(replacements)) then
+ if not (changes) then
return self
end
- return (self.__class)(self.source, unpack((function()
- local _accum_0 = { }
- local _len_0 = 1
- for i, v in ipairs(self) do
- _accum_0[_len_0] = replacements[i] or v
- _len_0 = _len_0 + 1
- end
- return _accum_0
- end)()))
+ return (self.__class)(self.source, unpack(replacements))
end
end
AST[name] = setmetatable(cls, {
diff --git a/nomsu_tree.moon b/nomsu_tree.moon
index e387f37..923bd72 100644
--- a/nomsu_tree.moon
+++ b/nomsu_tree.moon
@@ -10,7 +10,7 @@ AST.is_syntax_tree = (n, t=nil)->
type(n) == 'table' and getmetatable(n) and AST[n.type] == getmetatable(n) and (t == nil or n.type == t)
types = {"Number", "Var", "Block", "EscapedNomsu", "Text", "List", "Dict", "DictEntry",
- "IndexChain", "Action", "File"}
+ "IndexChain", "Action", "FileChunks"}
for name in *types
cls = {}
with cls
@@ -21,10 +21,18 @@ for name in *types
.is_instance = (x)=> getmetatable(x) == @
.__tostring = => "#{@type}(#{repr tostring(@source)}, #{concat([repr(v) for v in *@], ', ')})"
.map = (fn)=>
- if replacement = fn(@) then return replacement
- replacements = [AST.is_syntax_tree(v) and v\map(fn) or nil for v in *@]
- return @ unless next(replacements)
- return (@__class)(@source, unpack([replacements[i] or v for i,v in ipairs(@)]))
+ replacement = fn(@)
+ if replacement != nil then return replacement or nil
+ replacements = {}
+ changes = false
+ for i,v in ipairs(@)
+ replacement = if AST.is_syntax_tree(v)
+ v\map(fn)
+ else v
+ changes or= replacement != v
+ replacements[#replacements+1] = replacement
+ return @ unless changes
+ return (@__class)(@source, unpack(replacements))
AST[name] = setmetatable cls,
__tostring: => @name
diff --git a/parser.lua b/parser.lua
index d928fc8..4689e16 100644
--- a/parser.lua
+++ b/parser.lua
@@ -110,11 +110,12 @@ setmetatable(NOMSU_DEFS, {
return make_node
end
})
-local Parser = { }
-local NOMSU_PATTERN
+local Parser = {
+ version = 2,
+ patterns = { }
+}
do
- local peg_tidier = re.compile([[ file <- %nl* version %nl* {~ (def/comment) (%nl+ (def/comment))* %nl* ~}
- version <- "--" (!"version" [^%nl])* "version" (" ")* (([0-9])+ -> set_version) ([^%nl])*
+ local peg_tidier = re.compile([[ file <- %nl* {~ (def/comment) (%nl+ (def/comment))* %nl* ~}
def <- anon_def / captured_def
anon_def <- ({ident} (" "*) ":"
{~ ((%nl " "+ def_line?)+) / def_line ~}) -> "%1 <- %2"
@@ -124,39 +125,40 @@ do
err <- ("(!!" { (!("!!)") .)* } "!!)") -> "(({} (%1) %%userdata) => error)"
ident <- [a-zA-Z_][a-zA-Z0-9_]*
comment <- "--" [^%nl]*
- ]], {
- set_version = function(v)
- Parser.version = tonumber(v), {
- nl = NOMSU_DEFS.nl
- }
- end
- })
- local peg_file = io.open("nomsu.peg")
- if not peg_file and package.nomsupath then
- for path in package.nomsupath:gmatch("[^;]+") do
- peg_file = io.open(path .. "/nomsu.peg")
- if peg_file then
- break
+ ]])
+ for version = 1, 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 nomsu_peg = peg_tidier:match(peg_file:read('*a'))
+ peg_file:close()
+ Parser.patterns[version] = re.compile(nomsu_peg, NOMSU_DEFS)
end
- assert(peg_file, "could not find nomsu.peg file")
- local nomsu_peg = peg_tidier:match(peg_file:read('*a'))
- peg_file:close()
- NOMSU_PATTERN = re.compile(nomsu_peg, NOMSU_DEFS)
end
-Parser.parse = function(nomsu_code, source)
+Parser.parse = function(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)
+ version = version or nomsu_code:match("^#![^\n]*nomsu[ ]+-V[ ]*([0-9.]+)")
+ version = (version and tonumber(version)) or Parser.version
local userdata = {
errors = { },
source = source,
comments = { }
}
- local tree = NOMSU_PATTERN:match(nomsu_code, nil, userdata)
+ local tree = Parser.patterns[version]:match(nomsu_code, nil, userdata)
if not (tree) then
error("In file " .. tostring(colored.blue(tostring(source or "<unknown>"))) .. " failed to parse:\n" .. tostring(colored.onyellow(colored.black(nomsu_code))))
end
@@ -186,7 +188,7 @@ Parser.parse = function(nomsu_code, source)
end
errors = _accum_0
end
- error("Errors occurred while parsing:\n\n" .. table.concat(errors, "\n\n"), 0)
+ error("Errors occurred while parsing (v" .. tostring(version) .. "):\n\n" .. table.concat(errors, "\n\n"), 0)
end
tree.version = userdata.version
return tree
diff --git a/parser.moon b/parser.moon
index 96f6b6c..39ad93a 100644
--- a/parser.moon
+++ b/parser.moon
@@ -73,13 +73,12 @@ setmetatable(NOMSU_DEFS, {__index:(key)=>
return make_node
})
-Parser = {}
-NOMSU_PATTERN = do
+Parser = {version:2, patterns:{}}
+do
-- Just for cleanliness, I put the language spec in its own file using a slightly modified
-- version of the lpeg.re syntax.
peg_tidier = re.compile [[
- file <- %nl* version %nl* {~ (def/comment) (%nl+ (def/comment))* %nl* ~}
- version <- "--" (!"version" [^%nl])* "version" (" ")* (([0-9])+ -> set_version) ([^%nl])*
+ file <- %nl* {~ (def/comment) (%nl+ (def/comment))* %nl* ~}
def <- anon_def / captured_def
anon_def <- ({ident} (" "*) ":"
{~ ((%nl " "+ def_line?)+) / def_line ~}) -> "%1 <- %2"
@@ -89,24 +88,27 @@ NOMSU_PATTERN = do
err <- ("(!!" { (!("!!)") .)* } "!!)") -> "(({} (%1) %%userdata) => error)"
ident <- [a-zA-Z_][a-zA-Z0-9_]*
comment <- "--" [^%nl]*
- ]], {set_version: (v) -> Parser.version = tonumber(v), nl:NOMSU_DEFS.nl}
- peg_file = io.open("nomsu.peg")
- if not peg_file and package.nomsupath
- for path in package.nomsupath\gmatch("[^;]+")
- peg_file = io.open(path.."/nomsu.peg")
- break if peg_file
- assert(peg_file, "could not find nomsu.peg file")
- nomsu_peg = peg_tidier\match(peg_file\read('*a'))
- peg_file\close!
- re.compile(nomsu_peg, NOMSU_DEFS)
+ ]]
+ for version=1,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")
+ nomsu_peg = peg_tidier\match(peg_file\read('*a'))
+ peg_file\close!
+ Parser.patterns[version] = re.compile(nomsu_peg, NOMSU_DEFS)
-Parser.parse = (nomsu_code, source=nil)->
+Parser.parse = (nomsu_code, source=nil, version=nil)->
source or= nomsu_code.source
nomsu_code = tostring(nomsu_code)
+ version or= nomsu_code\match("^#![^\n]*nomsu[ ]+-V[ ]*([0-9.]+)")
+ version = (version and tonumber(version)) or Parser.version
userdata = {
errors: {}, :source, comments: {}
}
- tree = NOMSU_PATTERN\match(nomsu_code, nil, userdata)
+ tree = Parser.patterns[version]\match(nomsu_code, nil, userdata)
unless tree
error "In file #{colored.blue tostring(source or "<unknown>")} failed to parse:\n#{colored.onyellow colored.black nomsu_code}"
if type(tree) == 'number'
@@ -116,7 +118,7 @@ Parser.parse = (nomsu_code, source=nil)->
keys = [k for k,v in pairs(userdata.errors)]
table.sort(keys)
errors = [userdata.errors[k] for k in *keys]
- error("Errors occurred while parsing:\n\n"..table.concat(errors, "\n\n"), 0)
+ error("Errors occurred while parsing (v#{version}):\n\n"..table.concat(errors, "\n\n"), 0)
tree.version = userdata.version
return tree
diff --git a/tests/collections.nom b/tests/collections.nom
index d08a20e..9df9eba 100644
--- a/tests/collections.nom
+++ b/tests/collections.nom
@@ -1,3 +1,4 @@
+#!/usr/bin/env nomsu -V1
#..
Tests for the stuff defined in core/control_flow.nom
diff --git a/tests/colors.nom b/tests/colors.nom
index b3c22e7..16610f4 100644
--- a/tests/colors.nom
+++ b/tests/colors.nom
@@ -1,2 +1,3 @@
+#!/usr/bin/env nomsu -V1
use "lib/consolecolor.nom"
say: bright: green "Color test passed."
diff --git a/tests/control_flow.nom b/tests/control_flow.nom
index 340bd7d..62b59fa 100644
--- a/tests/control_flow.nom
+++ b/tests/control_flow.nom
@@ -1,3 +1,4 @@
+#!/usr/bin/env nomsu -V1
#
Tests for the stuff defined in core/control_flow.nom
diff --git a/tests/coroutines.nom b/tests/coroutines.nom
index 8d58170..16ef100 100644
--- a/tests/coroutines.nom
+++ b/tests/coroutines.nom
@@ -1,3 +1,4 @@
+#!/usr/bin/env nomsu -V1
#
Tests for the stuff defined in core/control_flow.nom
diff --git a/tests/errors.nom b/tests/errors.nom
index 1788a34..7f52532 100644
--- a/tests/errors.nom
+++ b/tests/errors.nom
@@ -1,3 +1,4 @@
+#!/usr/bin/env nomsu -V1
#
Tests for the stuff defined in core/errors.nom
diff --git a/tests/inner/inner.nom b/tests/inner/inner.nom
index 790ea0f..a4341f0 100644
--- a/tests/inner/inner.nom
+++ b/tests/inner/inner.nom
@@ -1,2 +1,3 @@
+#!/usr/bin/env nomsu -V1
use "core"
say "Inner directory 'use' test passed."
diff --git a/tests/math.nom b/tests/math.nom
index ffc0a2f..3764dee 100644
--- a/tests/math.nom
+++ b/tests/math.nom
@@ -1,3 +1,4 @@
+#!/usr/bin/env nomsu -V1
#..
Tests for the stuff defined in core/control_flow.nom
diff --git a/tests/metaprogramming.nom b/tests/metaprogramming.nom
index 0150350..083ddc7 100644
--- a/tests/metaprogramming.nom
+++ b/tests/metaprogramming.nom
@@ -1,3 +1,4 @@
+#!/usr/bin/env nomsu -V1
#
Tests for the stuff defined in core/metaprogramming.nom
diff --git a/tests/object.nom b/tests/object.nom
index 4848bf9..795d85e 100644
--- a/tests/object.nom
+++ b/tests/object.nom
@@ -1,3 +1,4 @@
+#!/usr/bin/env nomsu -V1
#
Tests for the object model defined in lib/object.nom
diff --git a/tests/operators.nom b/tests/operators.nom
index 4b0d6ad..375a80a 100644
--- a/tests/operators.nom
+++ b/tests/operators.nom
@@ -1,3 +1,4 @@
+#!/usr/bin/env nomsu -V1
#..
Tests for the stuff defined in core/operators.nom
diff --git a/tests/os.nom b/tests/os.nom
index 9b546ef..efc6e56 100644
--- a/tests/os.nom
+++ b/tests/os.nom
@@ -1,3 +1,4 @@
+#!/usr/bin/env nomsu -V1
#
Tests for the stuff defined in lib/os.nom
@@ -5,7 +6,7 @@ use "core"
use "lib/os.nom"
%lines <-: lines in: read file "tests/os.nom"
-assume: %lines.2 = " Tests for the stuff defined in lib/os.nom"
+assume: %lines.3 = " Tests for the stuff defined in lib/os.nom"
%n <- 0
for file %f in "core"
diff --git a/tests/scopes.nom b/tests/scopes.nom
index 67598be..ffda7f6 100644
--- a/tests/scopes.nom
+++ b/tests/scopes.nom
@@ -1,3 +1,4 @@
+#!/usr/bin/env nomsu -V1
use "core"
%x <- "outer"
diff --git a/tests/text.nom b/tests/text.nom
index e42fe73..57480d8 100644
--- a/tests/text.nom
+++ b/tests/text.nom
@@ -1,3 +1,4 @@
+#!/usr/bin/env nomsu -V1
#..
Tests for the stuff defined in core/text.nom