aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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