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 similarity index 99% rename from nomsu.peg rename to 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 + 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 - 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 = "" - 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) - end - nomsu:append(line) - line = NomsuCode(line.source, "\n ") - if i < #tree then - line_comments = pop_comments(tree[i + 1].source.start) + 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 - line_comments = '' + nomsu:append("(..)\n ", item_nomsu) end + if i < #tree then + 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) - end - nomsu:append(line) - line = NomsuCode(line.source, "\n ") - if i < #tree then - line_comments = pop_comments(tree[1].source.start) + 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 - line_comments = '' + nomsu:append("(..)\n ", item_nomsu) end + if i < #tree then + 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 ""))) .. " 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 "")} 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