aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2018-12-14 17:49:36 -0800
committerBruce Hill <bruce@bruce-hill.com>2018-12-14 17:49:46 -0800
commit6ba84a0f507270fba8e7a68901dc256c2979d7f9 (patch)
tree8f342ace5a015cf14df12bb17525f02de89bf47d
parent0d88091f8d6cba8c552e2d3ffd3551819f8a4aed (diff)
Initial setup work for syntax version 5.
-rw-r--r--Makefile12
-rw-r--r--core/metaprogramming.nom37
-rw-r--r--core/operators.nom3
-rw-r--r--nomsu.3.peg13
-rw-r--r--nomsu.4.peg16
-rw-r--r--nomsu.5.peg245
-rw-r--r--nomsu_compiler.lua65
-rw-r--r--nomsu_compiler.moon52
-rw-r--r--nomsu_decompiler.lua65
-rw-r--r--nomsu_decompiler.moon60
-rw-r--r--syntax_tree.lua33
-rw-r--r--syntax_tree.moon18
-rwxr-xr-xtools/parse.nom9
13 files changed, 466 insertions, 162 deletions
diff --git a/Makefile b/Makefile
index 1c77c8e..0ae6bea 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
# Nomsu makefile
# To build, run `make`
-# To install,
+# To install, run `make install`
# ========= User-controlled variables ========
LUA= lua
@@ -23,10 +23,10 @@ LIB_LUA_FILES= $(patsubst %.nom,%.lua,$(LIB_NOM_FILES))
PEG_FILES= $(wildcard nomsu.*.peg)
GET_VERSION= $(LUA_BIN) nomsu.lua --version
-all: build optimize
+all: lua optimize
.PHONY: test
-test: build optimize
+test: lua optimize
@echo "\033[1;4mRunning unoptimized tests...\033[0m"
@$(LUA_BIN) nomsu.lua -O0 tools/test.nom $(CORE_NOM_FILES) $(LIB_NOM_FILES)
@echo "\n\033[1;4mRunning optimized tests...\033[0m"
@@ -42,10 +42,10 @@ test: build optimize
version: $(LUA_FILES) $(CORE_NOM_FILES) $(LIB_NOM_FILES)
@$(LUA_BIN) nomsu.lua --version > version || exit
-build: $(LUA_FILES)
+lua: $(LUA_FILES)
.PHONY: optimize
-optimize: build $(CORE_LUA_FILES) $(LIB_LUA_FILES)
+optimize: lua $(CORE_LUA_FILES) $(LIB_LUA_FILES)
.PHONY: clean
clean:
@@ -53,7 +53,7 @@ clean:
@rm -rvf version core/*.lua lib/*.lua tools/*.lua compatibility/*.lua
.PHONY: install
-install: build version optimize
+install: lua version optimize
@prefix="$(PREFIX)"; \
if [[ ! $$prefix ]]; then \
read -p $$'\033[1mWhere do you want to install Nomsu? (default: /usr/local) \033[0m' prefix; \
diff --git a/core/metaprogramming.nom b/core/metaprogramming.nom
index 43585b0..77dbd24 100644
--- a/core/metaprogramming.nom
+++ b/core/metaprogramming.nom
@@ -26,7 +26,8 @@ lua> "\
local body_lua = SyntaxTree:is_instance(\%body) and compile(\%body) or \%body
if SyntaxTree:is_instance(\%body) and \%body.type ~= "Block" then body_lua:prepend("return ") end
local lua = LuaCode("(function(")
- if SyntaxTree:is_instance(\%args) and \%args.type == "Action" then \%args = \%args:get_args()
+ if SyntaxTree:is_instance(\%args) and (\%args.type == "Action" or \%args.type == "MethodCall") then
+ \%args = \%args:get_args()
elseif SyntaxTree:is_instance(\%args) and \%args.type == "Var" then \%args = {\%args} end
for i, arg in ipairs(\%args) do
local arg_lua = SyntaxTree:is_instance(arg) and compile(arg):text() or arg
@@ -38,7 +39,7 @@ lua> "\
elseif not arg_lua:is_lua_id() then
compile_error_at(SyntaxTree:is_instance(arg) and arg or nil,
"This does not compile to a Lua identifier, so it can't be used as a function argument.",
- "This should probably be a Nomsu variable instead (like %x).")
+ "This should probably be a Nomsu variable instead (like $x).")
end
lua:add(i > 1 and ", " or "", arg_lua)
body_lua:remove_free_vars({arg_lua})
@@ -136,34 +137,38 @@ test:
lua> "\
..
local lua = LuaCode()
- local fn_name = \%action.stub:as_lua_id()
- if \%action.target then lua:add(compile(\%action.target), ".")
- else lua:add_free_vars({fn_name}) end
- lua:add(fn_name, " = ", \(\(%action -> %body) as lua), ";")
+ if \%action.type == "MethodCall" then
+ lua:add(compile(\%action[1]), ".", \%action[2].stub:as_lua_id())
+ elseif \%action.type == "Action" then
+ lua:add(\%action.stub:as_lua_id())
+ lua:add_free_vars({\%action.stub:as_lua_id()})
+ else
+ compile_error_at(\%action, "Expected an action or method call here")
+ end
+ lua:add(" = ", \(\(%action -> %body) as lua), ";")
return lua"
(%actions all mean %body) compiles to:
lua> "\
- ..local fn_name = \%actions[1].stub:as_lua_id()
- local target = \%actions[1].target and compile(\%actions[1].target) or nil
+ ..local lua = \(\(%actions.1 means %body) as lua)
+ local first_def = (\%actions[1].type == "MethodCall"
+ and LuaCode(compile(\%actions[1][1]), ".", \%actions[1].stub:as_lua_id())
+ or LuaCode(\%actions[1].stub:as_lua_id()))
local \%args = List(\%actions[1]:get_args())
- local lua = \(\(%actions.1 means %body) as lua)
for i=2,#\%actions do
local alias = \%actions[i]
- local alias_name = alias.stub:as_lua_id()
local \%alias_args = List(alias:get_args())
lua:add("\\n")
- if alias.target then
- lua:add(compile(alias.target), ".")
+ if alias.type == "MethodCall" then
+ lua:add(compile(alias[1]), ".", alias.stub:as_lua_id())
else
+ lua:add(alias.stub:as_lua_id())
lua:add_free_vars({alias_name})
end
- lua:add(alias_name, " = ")
if \%args == \%alias_args then
- if target then lua:add(target, ".") end
- lua:add(fn_name, ";")
+ lua:add(" = ", first_def, ";")
else
- lua:add(\(\(%alias_args -> %actions.1) as lua), ";")
+ lua:add(" = ", \(\(%alias_args -> %actions.1) as lua), ";")
end
end
return lua"
diff --git a/core/operators.nom b/core/operators.nom
index fc90699..bc8068e 100644
--- a/core/operators.nom
+++ b/core/operators.nom
@@ -35,8 +35,7 @@ test:
# Variable assignment operator
(%var = %value) compiles to:
lua> "\
- ..
- local lua = LuaCode()
+ ..local lua = LuaCode()
if \%var.type == "List" then
for i, \%assignment in ipairs(\%var) do
if i > 1 then lua:add(", ") end
diff --git a/nomsu.3.peg b/nomsu.3.peg
index 21fa86c..d43b404 100644
--- a/nomsu.3.peg
+++ b/nomsu.3.peg
@@ -101,17 +101,22 @@ indented_nomsu (EscapedNomsu):
index_chain (IndexChain):
noindex_inline_expression ("." (text_word / noindex_inline_expression))+
+inline_action: inline_methodcall / _inline_action
+inline_methodcall:
+ inline_arg ws* "::" ws* _inline_action
-- Actions need either at least 1 word, or at least 2 tokens
-inline_action (Action):
+_inline_action (Action):
!section_division
- ({:target: inline_arg :} ws* "::" ws*)?
( (inline_arg (ws* (inline_arg / word))+)
/ (word (ws* (inline_arg / word))*))
(ws* inline_block)?
inline_arg: inline_expression / inline_block
-action (Action):
+
+action: methodcall / _action
+methodcall:
+ arg (nl_nodent "..")? ws* "::" (nl_nodent "..")? ws* _action
+_action (Action):
!section_division
- ({:target: arg :} (nl_nodent "..")? ws* "::" (nl_nodent "..")? ws*)?
( (arg ((nl_nodent "..")? ws* (arg / word))+)
/ (word ((nl_nodent "..")? ws* (arg / word))*))
arg: expression / inline_block / indented_block
diff --git a/nomsu.4.peg b/nomsu.4.peg
index 95e3ef0..5f84688 100644
--- a/nomsu.4.peg
+++ b/nomsu.4.peg
@@ -111,18 +111,24 @@ indented_nomsu (EscapedNomsu):
index_chain (IndexChain):
noindex_inline_expression ("." (text_word / noindex_inline_expression))+
+inline_action: inline_methodcall / _inline_action
+inline_methodcall (MethodCall):
+ (inline_expression / "(" inline_block ")") ws* "::" ws* _inline_action
-- Actions need either at least 1 word, or at least 2 tokens
-inline_action (Action):
+_inline_action (Action):
!section_division
- ({:target: (inline_expression / "(" inline_block ")") :} ws* "::" ws*)?
( (inline_arg (ws* (inline_arg / word))+)
/ (word (ws* (inline_arg / word))*))
(ws* inline_block)?
inline_arg: inline_expression / inline_block / "(" ws* ")"
-action (Action):
+
+action: methodcall / _action
+methodcall (MethodCall):
+ (expression / "(" inline_block ")" / indented_block)
+ ((ws* "\")? eol nl_nodent "..")? ws* "::" ((ws* "\")? eol nl_nodent "..")? ws*
+ _action
+_action (Action):
!section_division
- ({:target: (expression / "(" inline_block ")" / indented_block) :}
- ((ws* "\")? eol nl_nodent "..")? ws* "::" ((ws* "\")? eol nl_nodent "..")? ws*)?
( (arg (((ws* "\")? eol nl_nodent "..")? ws* (arg / word))+)
/ (word (((ws* "\")? eol nl_nodent "..")? ws* (arg / word))*))
arg: expression / inline_block / indented_block / "(" ws* ")"
diff --git a/nomsu.5.peg b/nomsu.5.peg
new file mode 100644
index 0000000..07c1973
--- /dev/null
+++ b/nomsu.5.peg
@@ -0,0 +1,245 @@
+-- Nomsu version 5
+file:
+ {:curr_indent: ' '* :}
+ (((methodcall / action / expression / inline_block / indented_block) eol !.)
+ / file_chunks / empty_block)
+ {:curr_indent: %nil :}
+ !.
+
+shebang: "#!" (!"nomsu" [^%nl])* "nomsu" ws+ "-V" ws* [0-9.]+ [^%nl]* (%nl / !.)
+
+eof: !.
+
+file_chunks (FileChunks):
+ {:curr_indent: ' '* :}
+ {:shebang: shebang :}?
+ (top_block (nl_nodent section_division top_block)*)
+ blank_lines?
+ ws* unexpected_chunk?
+ {:curr_indent: %nil :}
+
+top_block (Block):
+ {:curr_indent: ' '* :}
+ comment? blank_lines? statement (nl_nodent statement)*
+ {:curr_indent: %nil :}
+
+empty_block (Block):
+ {:curr_indent: ' '* :}
+ comment? blank_lines?
+ {:curr_indent: %nil :}
+
+nodent: (unexpected_indent [^%nl]* / =curr_indent)
+indent: {~ =curr_indent (ws / (%tab -> ' '))+ ~}
+blank_lines: %nl ((nodent comment / ws*) %nl)*
+eol: ws* eol_comment? (!. / &%nl)
+
+nl_nodent: blank_lines nodent
+nl_indent: blank_lines tab_error? {:curr_indent: indent :} (comment nl_nodent)*
+
+comment (Comment):
+ "#" {~ [^%nl]* (%nl+ (indent -> '') [^%nl]*)* (%nl &%nl)* ~}
+eol_comment (Comment):
+ "#" {[^%nl]*}
+
+unexpected_code: ws* _unexpected_code
+_unexpected_code (Error):
+ {:error: {~ [^%nl]+ -> "Couldn't parse this code" ~} :}
+unexpected_chunk (Error):
+ {:error: {~ .+ -> "Couldn't parse this code" ~} :}
+unexpected_indent (Error):
+ {:error: {~ (=curr_indent ws+) -> "Messed up indentation" ~} :}
+ {:hint: {~ '' -> 'Either make sure this line is aligned with the one above it, or make sure the previous line ends with something that uses indentation, like ":" or "(..)"' ~} :}
+missing_paren_err (Error):
+ {:error: {~ eol -> 'Line ended without finding a closing )-parenthesis' ~} :}
+ {:hint: {~ '' -> 'Put a ")" here' ~} :}
+missing_quote_err (Error):
+ {:error: {~ eol -> "Line ended without finding a closing quotation mark." ~} :}
+ {:hint: {~ "" -> "Put a quotation mark here." ~} :}
+missing_indented_quote_err (Error):
+ {:error: {~ eol -> "This text doesn't have a closing quotation mark." ~} :}
+ {:hint: {~ "" -> "Put a quotation mark here on its own line." ~} :}
+missing_bracket_error (Error):
+ {:error: {~ eol -> "Line ended before finding a closing ]-bracket" ~} :}
+ {:hint: {~ '' -> 'Put a "]" here' ~} :}
+missing_brace_error (Error):
+ {:error: {~ eol -> "Line ended before finding a closing }-brace" ~} :}
+ {:hint: {~ '' -> 'Put a "}" here' ~} :}
+tab_error (Error):
+ &(=curr_indent %tab)
+ {:error: {~ '' -> 'Tabs are not allowed for indentation.' ~} :}
+ {:hint: {~ '' -> 'Use spaces instead of tabs.' ~} :}
+
+section_division: ("~")^+3 eol
+
+inline_block:
+ "(" ws* inline_block ws* (eof / ")") / raw_inline_block
+raw_inline_block (Block):
+ (!"::") ":" ws* ((inline_statement (ws* ";" ws* inline_statement)*) / !(eol nl_indent))
+indented_block (Block):
+ ":" eol nl_indent statement (nl_nodent statement)*
+ (%nl (ws* %nl)* nodent (comment / eol / unexpected_code))*
+ {:curr_indent: %nil :}
+
+statement:
+ (methodcall / action / expression) (eol / unexpected_code)
+
+inline_statement: (inline_methodcall / inline_action / inline_expression)
+
+noindex_inline_expression:
+ number / variable / inline_text / inline_list / inline_dict / inline_nomsu
+ / ( "("
+ ws* (inline_methodcall / inline_action / inline_expression) ws*
+ (")" / eof / missing_paren_err / unexpected_code)
+ )
+inline_expression: index_chain / noindex_inline_expression
+indented_expression:
+ indented_text / indented_nomsu / indented_list / indented_dict / ({|
+ "(..)" eol nl_indent
+ (methodcall / action / expression) (eol / unexpected_code)
+ (%nl (ws* %nl)* nodent (comment / eol / unexpected_code))*
+ {:curr_indent: %nil :}
+ |} -> unpack)
+expression:
+ inline_expression / indented_expression
+
+inline_nomsu (EscapedNomsu): "\" (inline_expression / inline_block)
+indented_nomsu (EscapedNomsu):
+ "\" (noindex_inline_expression / inline_block / indented_expression / indented_block)
+
+index_chain (IndexChain):
+ noindex_inline_expression
+ ("." (hex_integer / integer / text_word / noindex_inline_expression))+
+
+index_chain_before_method (IndexChain):
+ noindex_inline_expression
+ ("." (hex_integer / integer / text_word / noindex_inline_expression) &".")+
+
+-- Actions need 1 argument and either another argument or a word.
+inline_action (Action):
+ !section_division
+ ( (word (ws* (inline_arg / word))*)
+ /(inline_arg (ws* (inline_arg / word))+))
+inline_arg: inline_expression / inline_block
+inline_methodcall (MethodCall):
+ (index_chain / noindex_inline_expression / "(" inline_block ")")
+ "|" inline_action
+
+action (Action):
+ !section_division
+ ( (word ((linesplit / ws*) (arg / word))*)
+ /(arg ((linesplit / ws*) (arg / word))+))
+linesplit: (ws* "\")? eol nl_nodent ".." ws*
+arg: expression / inline_block / indented_block
+methodcall (MethodCall):
+ (index_chain / noindex_inline_expression / indented_expression / "(" inline_block ")" / indented_block)
+ linesplit? "|" linesplit? action
+
+word: !number { operator_char+ / ident_char+ }
+
+text_word (Text): word
+
+inline_text (Text):
+ !(indented_text)
+ '"' _inline_text* ('"' / eof / missing_quote_err / unexpected_code)
+_inline_text:
+ {~ (('\"' -> '"') / ('\\' -> '\') / escaped_char / text_char+)+ ~}
+ / inline_text_interpolation / illegal_char
+inline_text_interpolation:
+ "\" (
+ variable / inline_list / inline_dict
+ / ("("
+ ws* ((inline_methodcall / inline_action / inline_expression) ws*)?
+ (")" / eof / missing_paren_err / unexpected_code))
+ )
+
+text_char: %utf8_char / !["\] %print / %tab
+illegal_char (Error):
+ {:error: {~ (!(%nl / %tab / %print) .) -> "Illegal unprintable character here (it may not be visible, but it's there)" ~} :}
+ {:hint: {~ '' -> "This sort of thing can happen when copying and pasting code. Try deleting and retyping the code." ~} :}
+
+terminal_quote: '"' !([^%nl] / (%nl (ws* eol)?)+ =curr_indent [^%nl])
+nonterminal_quote: !terminal_quote '"'
+indented_text (Text):
+ '"' %nl {%nl*} {:curr_indent: indent :}
+ (indented_plain_text / text_interpolation / illegal_char / blank_text_lines)*
+ (terminal_quote eol / eof / missing_indented_quote_err)
+ {:curr_indent: %nil :}
+-- Tracking text-lines-within-indented-text as separate objects allows for better debugging line info
+indented_plain_text (Text):
+ {~
+ ((("\" blank_lines =curr_indent "..") -> "") / ('\\' -> '\')
+ / (!text_interpolation ((!("\n") escaped_char) / '\'))
+ / (nonterminal_quote / text_char)+)+
+ blank_text_lines?
+ ~}
+blank_text_lines:
+ {~ (%nl ((ws* -> '') eol / (=curr_indent -> '') &[^%nl]))+ ~}
+
+text_interpolation:
+ ("\" indented_expression (blank_lines =curr_indent "..")?) / inline_text_interpolation
+
+number:
+ hex_integer / real_number / integer
+
+integer (Number):
+ (("-"? [0-9]+)-> tonumber)
+
+hex_integer (Number):
+ (("-"? "0x" [0-9a-fA-F]+)-> tonumber)
+ {:hex: '' -> 'yes' :}
+
+real_number (Number):
+ (("-"? ([0-9]+ "." [0-9]+) / ("." [0-9]+))-> tonumber)
+
+variable (Var): "$" ({ident_char+} / "(" {(ws+ / operator_char+ / ident_char+)*} ")" / {''})
+
+inline_list (List):
+ !('[..]')
+ "[" ws*
+ (inline_list_item (ws* ',' ws* inline_list_item)* (ws* ',')?)? ws*
+ ("]" / eof / (","? (missing_bracket_error / unexpected_code)))
+indented_list (List):
+ "[..]" eol nl_indent
+ list_line (nl_nodent list_line)*
+ (%nl (ws* %nl)* nodent (comment / eol / unexpected_code))*
+ (","? unexpected_code)?
+list_line:
+ (inline_list_item ws* "," ws*)+ eol
+ / (inline_list_item ws* "," ws*)* (methodcall / action / expression / inline_block / indented_block) eol
+inline_list_item: inline_methodcall / inline_action / inline_expression / inline_block
+
+inline_dict (Dict):
+ !('{..}')
+ "{" ws*
+ (inline_dict_entry (ws* ',' ws* inline_dict_entry)*)? ws*
+ ("}" / eof / (","? (missing_brace_error / unexpected_code)))
+indented_dict (Dict):
+ "{..}" eol nl_indent
+ dict_line (nl_nodent dict_line)*
+ (%nl (ws* %nl)* nodent (comment / eol / unexpected_code))*
+ (","? unexpected_code)?
+dict_line:
+ (inline_dict_entry ws* "," ws*)+ eol
+ / (inline_dict_entry ws* "," ws*)* dict_entry eol
+_dict_entry(DictEntry):
+ dict_key (ws* ":" ws* (methodcall / action / expression))?
+dict_entry:
+ _dict_entry / inline_block / indented_block
+_inline_dict_entry(DictEntry):
+ dict_key (ws* ":" ws* (inline_methodcall / inline_action / inline_expression)?)?
+inline_dict_entry:
+ _inline_dict_entry / inline_block
+dict_key:
+ text_word / inline_expression
+
+operator_char: ['`~@^&*+=|<>?/%!-]
+ident_char: [a-zA-Z0-9_] / %utf8_char
+ws: " "
+
+escaped_char:
+ ("\"->'') (
+ (([xX]->'') ((({[0-9a-fA-F]^2} %number_16) -> tonumber) -> tochar))
+ / ((([0-9] [0-9]^-2) -> tonumber) -> tochar)
+ / ("a"->ascii_7) / ("b"->ascii_8) / ("t"->ascii_9) / ("n"->ascii_10)
+ / ("v"->ascii_11) / ("f"->ascii_12) / ("r"->ascii_13)
+ )
diff --git a/nomsu_compiler.lua b/nomsu_compiler.lua
index 29c1668..d51fc1d 100644
--- a/nomsu_compiler.lua
+++ b/nomsu_compiler.lua
@@ -188,7 +188,7 @@ local compile = setmetatable({
if "Action" == _exp_0 then
local stub = tree.stub
local compile_action = compile.action[stub]
- if not compile_action and not tree.target and math_expression:match(stub) then
+ if not compile_action and math_expression:match(stub) then
local lua = LuaCode:from(tree.source)
for i, tok in ipairs(tree) do
if type(tok) == 'string' then
@@ -206,7 +206,7 @@ local compile = setmetatable({
end
return lua
end
- if compile_action and not tree.target then
+ if compile_action then
local args
do
local _accum_0 = { }
@@ -235,39 +235,42 @@ local compile = setmetatable({
end
end
local lua = LuaCode:from(tree.source)
- if tree.target then
- local target_lua = compile(tree.target)
- local target_text = target_lua:text()
- if target_text:match("^%(.*%)$") or target_text:match("^[_a-zA-Z][_a-zA-Z0-9.]*$") or tree.target.type == "IndexChain" then
- lua:add(target_lua, ":")
- else
- lua:add("(", target_lua, "):")
+ lua:add((stub):as_lua_id(), "(")
+ for i, arg in ipairs(tree:get_args()) do
+ local arg_lua = compile(arg)
+ if arg.type == "Block" then
+ arg_lua = LuaCode:from(arg.source, "(function()\n ", arg_lua, "\nend)()")
+ end
+ if i > 1 then
+ lua:add(",")
end
+ lua:add(lua:trailing_line_len() + #arg_lua:text() > MAX_LINE and "\n " or " ")
+ lua:add(arg_lua)
+ end
+ lua:add(")")
+ return lua
+ elseif "MethodCall" == _exp_0 then
+ local stub = tree[2].stub
+ local lua = LuaCode:from(tree.source)
+ local target_lua = compile(tree[1])
+ local target_text = target_lua:text()
+ if target_text:match("^%(.*%)$") or target_text:match("^[_a-zA-Z][_a-zA-Z0-9.]*$") or tree[1].type == "IndexChain" then
+ lua:add(target_lua, ":")
+ else
+ lua:add("(", target_lua, "):")
end
+ assert(tree[2].type == "Action")
lua:add((stub):as_lua_id(), "(")
- local arg_count = 0
- for i, tok in ipairs(tree) do
- local _continue_0 = false
- repeat
- if type(tok) == "string" then
- _continue_0 = true
- break
- end
- arg_count = arg_count + 1
- local arg_lua = compile(tok)
- if tok.type == "Block" then
- arg_lua = LuaCode:from(tok.source, "(function()\n ", arg_lua, "\nend)()")
- end
- if arg_count > 1 then
- lua:add(",")
- end
- lua:add(lua:trailing_line_len() + #arg_lua:text() > MAX_LINE and "\n " or " ")
- lua:add(arg_lua)
- _continue_0 = true
- until true
- if not _continue_0 then
- break
+ for i, arg in ipairs(tree[2]:get_args()) do
+ local arg_lua = compile(arg)
+ if arg.type == "Block" then
+ arg_lua = LuaCode:from(arg.source, "(function()\n ", arg_lua, "\nend)()")
+ end
+ if i > 1 then
+ lua:add(",")
end
+ lua:add(lua:trailing_line_len() + #arg_lua:text() > MAX_LINE and "\n " or " ")
+ lua:add(arg_lua)
end
lua:add(")")
return lua
diff --git a/nomsu_compiler.moon b/nomsu_compiler.moon
index 1a1c4f9..411878f 100644
--- a/nomsu_compiler.moon
+++ b/nomsu_compiler.moon
@@ -143,7 +143,7 @@ compile = setmetatable({
when "Action"
stub = tree.stub
compile_action = compile.action[stub]
- if not compile_action and not tree.target and math_expression\match(stub)
+ if not compile_action and math_expression\match(stub)
lua = LuaCode\from(tree.source)
for i,tok in ipairs tree
if type(tok) == 'string'
@@ -155,7 +155,7 @@ compile = setmetatable({
lua\add " " if i < #tree
return lua
- if compile_action and not tree.target
+ if compile_action
args = [arg for arg in *tree when type(arg) != "string"]
-- Force Lua to avoid tail call optimization for debugging purposes
-- TODO: use tail call?
@@ -173,25 +173,37 @@ compile = setmetatable({
return compile(ret)
lua = LuaCode\from(tree.source)
- if tree.target -- Method call
- target_lua = compile tree.target
- target_text = target_lua\text!
- -- TODO: this parenthesizing is maybe overly conservative
- if target_text\match("^%(.*%)$") or target_text\match("^[_a-zA-Z][_a-zA-Z0-9.]*$") or
- tree.target.type == "IndexChain"
- lua\add target_lua, ":"
- else
- lua\add "(", target_lua, "):"
lua\add((stub)\as_lua_id!,"(")
- arg_count = 0
- for i, tok in ipairs tree
- if type(tok) == "string" then continue
- arg_count += 1
- arg_lua = compile(tok)
- if tok.type == "Block"
- arg_lua = LuaCode\from(tok.source, "(function()\n ", arg_lua, "\nend)()")
- if arg_count > 1
- lua\add ","
+ for i, arg in ipairs tree\get_args!
+ arg_lua = compile(arg)
+ if arg.type == "Block"
+ arg_lua = LuaCode\from(arg.source, "(function()\n ", arg_lua, "\nend)()")
+ lua\add "," if i > 1
+ lua\add(lua\trailing_line_len! + #arg_lua\text! > MAX_LINE and "\n " or " ")
+ lua\add arg_lua
+ lua\add ")"
+ return lua
+
+ when "MethodCall"
+ stub = tree[2].stub
+ lua = LuaCode\from tree.source
+ target_lua = compile tree[1]
+ target_text = target_lua\text!
+ -- TODO: this parenthesizing is maybe overly conservative
+ if target_text\match("^%(.*%)$") or target_text\match("^[_a-zA-Z][_a-zA-Z0-9.]*$") or
+ tree[1].type == "IndexChain"
+ lua\add target_lua, ":"
+ else
+ lua\add "(", target_lua, "):"
+
+ -- TODO: de-duplicate this code
+ assert tree[2].type == "Action"
+ lua\add((stub)\as_lua_id!,"(")
+ for i, arg in ipairs tree[2]\get_args!
+ arg_lua = compile(arg)
+ if arg.type == "Block"
+ arg_lua = LuaCode\from(arg.source, "(function()\n ", arg_lua, "\nend)()")
+ lua\add "," if i > 1
lua\add(lua\trailing_line_len! + #arg_lua\text! > MAX_LINE and "\n " or " ")
lua\add arg_lua
lua\add ")"
diff --git a/nomsu_decompiler.lua b/nomsu_decompiler.lua
index 51a21ec..7882596 100644
--- a/nomsu_decompiler.lua
+++ b/nomsu_decompiler.lua
@@ -49,13 +49,7 @@ tree_to_inline_nomsu = function(tree)
local _exp_0 = tree.type
if "Action" == _exp_0 then
local nomsu = NomsuCode:from(tree.source)
- if tree.target then
- local inline_target = tree_to_inline_nomsu(tree.target)
- if tree.target.type == "Action" then
- inline_target:parenthesize()
- end
- nomsu:add(inline_target, "::")
- end
+ local num_args = 0
for i, bit in ipairs(tree) do
if type(bit) == "string" then
local clump_words
@@ -69,6 +63,7 @@ tree_to_inline_nomsu = function(tree)
end
nomsu:add(bit)
else
+ num_args = num_args + 1
local arg_nomsu = tree_to_inline_nomsu(bit)
if bit.type == "Block" then
if i > 1 and i < #tree then
@@ -81,17 +76,16 @@ tree_to_inline_nomsu = function(tree)
if i > 1 then
nomsu:add(" ")
end
- if bit.type == "Action" then
+ if bit.type == "Action" or bit.type == "MethodCall" then
arg_nomsu:parenthesize()
end
end
nomsu:add(arg_nomsu)
end
end
- if #tree == 1 and type(tree[1]) ~= "string" then
- nomsu:add("()")
- end
return nomsu
+ elseif "MethodCall" == _exp_0 then
+ return NomsuCode:from(tree.source, tree_to_inline_nomsu(tree[1]), "|", tree_to_inline_nomsu(tree[2]))
elseif "EscapedNomsu" == _exp_0 then
local inner_nomsu = tree_to_inline_nomsu(tree[1])
if not (tree[1].type == "List" or tree[1].type == "Dict" or tree[1].type == "Var") then
@@ -149,7 +143,7 @@ tree_to_inline_nomsu = function(tree)
else
nomsu = tree_to_inline_nomsu(key)
end
- if key.type == "Action" or key.type == "Block" then
+ if key.type == "Action" or key.type == "MethodCall" or key.type == "Block" then
nomsu:parenthesize()
end
if value then
@@ -174,8 +168,13 @@ tree_to_inline_nomsu = function(tree)
bit_nomsu = tree_to_inline_nomsu(bit)
end
assert(bit.type ~= "Block")
- if bit.type == "Action" or bit.type == "IndexChain" or (bit.type == "Number" and i < #tree) then
+ local _exp_1 = bit.type
+ if "Action" == _exp_1 or "MethodCall" == _exp_1 or "IndexChain" == _exp_1 then
bit_nomsu:parenthesize()
+ elseif "Number" == _exp_1 then
+ if bit_nomsu:text():match("%.") then
+ bit_nomsu:parenthesize()
+ end
end
nomsu:add(bit_nomsu)
end
@@ -191,7 +190,12 @@ tree_to_inline_nomsu = function(tree)
end
return NomsuCode:from(tree.source, s)
elseif "Var" == _exp_0 then
- return NomsuCode:from(tree.source, "%", tree[1])
+ local varname = tree[1]:gsub("_", " ")
+ if varname == "" or is_identifier(varname) then
+ return NomsuCode:from(tree.source, "$", varname)
+ else
+ return NomsuCode:from(tree.source, "$(", varname, ")")
+ end
elseif "FileChunks" == _exp_0 then
return error("Can't inline a FileChunks")
elseif "Comment" == _exp_0 then
@@ -222,14 +226,17 @@ tree_to_nomsu = function(tree)
if try_inline then
inline_nomsu = tree_to_inline_nomsu(t)
if #inline_nomsu:text() <= space or #inline_nomsu:text() <= 8 then
- if t.type == "Action" then
+ if t.type == "Action" or t.type == "MethodCall" then
inline_nomsu:parenthesize()
end
return inline_nomsu
end
+ if t.type == "Text" and #inline_nomsu:text() + 2 < MAX_LINE then
+ return inline_nomsu
+ end
end
local indented = tree_to_nomsu(t)
- if t.type == "Action" then
+ if t.type == "Action" or t.type == "MethodCall" then
if indented:is_multiline() then
return NomsuCode:from(t.source, "(..)\n ", indented)
else
@@ -262,15 +269,8 @@ tree_to_nomsu = function(tree)
return nomsu
elseif "Action" == _exp_0 then
local next_space = ""
- if tree.target then
- local target_nomsu = recurse(tree.target)
- if tree.target.type == "Block" and not target_nomsu:is_multiline() then
- target_nomsu:parenthesize()
- end
- nomsu:add(target_nomsu)
- nomsu:add(target_nomsu:is_multiline() and "\n..::" or "::")
- end
local word_buffer = { }
+ local num_args = 0
for i, bit in ipairs(tree) do
local _continue_0 = false
repeat
@@ -295,6 +295,7 @@ tree_to_nomsu = function(tree)
word_buffer = { }
next_space = " "
end
+ num_args = num_args + 1
local bit_nomsu = recurse(bit)
if bit.type == "Block" and not bit_nomsu:is_multiline() then
if #bit_nomsu:text() > nomsu:trailing_line_len() * GOLDEN_RATIO and #bit_nomsu:text() > 8 then
@@ -302,7 +303,7 @@ tree_to_nomsu = function(tree)
end
end
if (next_space == " " and not bit_nomsu:is_multiline() and nomsu:trailing_line_len() + #bit_nomsu:text() > MAX_LINE and nomsu:trailing_line_len() > 8) then
- if bit.type == 'Action' then
+ if bit.type == 'Action' or bit.type == "MethodCall" then
bit_nomsu = NomsuCode:from(bit.source, "(..)\n ", tree_to_nomsu(bit))
else
next_space = " \\\n.."
@@ -331,12 +332,14 @@ tree_to_nomsu = function(tree)
nomsu:add(next_space, words)
next_space = " "
end
- if #tree == 1 and type(tree[1]) ~= "string" then
- if next_space == " " then
- next_space = ""
- end
- nomsu:add(next_space, "()")
+ return nomsu
+ elseif "MethodCall" == _exp_0 then
+ local target_nomsu = recurse(tree[1])
+ if tree[1].type == "Block" and not target_nomsu:is_multiline() then
+ target_nomsu:parenthesize()
end
+ nomsu:add(target_nomsu)
+ nomsu:add(target_nomsu:is_multiline() and " \\\n..|" or "|")
return nomsu
elseif "EscapedNomsu" == _exp_0 then
nomsu = recurse(tree[1])
@@ -413,7 +416,7 @@ tree_to_nomsu = function(tree)
end
end
add_text(tree)
- return NomsuCode:from(tree.source, '"\\\n ..', nomsu, '"')
+ return NomsuCode:from(tree.source, '"\n ', nomsu, '"')
elseif "List" == _exp_0 or "Dict" == _exp_0 then
if #tree == 0 then
nomsu:add(tree.type == "List" and "[]" or "{}")
diff --git a/nomsu_decompiler.moon b/nomsu_decompiler.moon
index 4bc67f5..18f01cc 100644
--- a/nomsu_decompiler.moon
+++ b/nomsu_decompiler.moon
@@ -33,12 +33,7 @@ tree_to_inline_nomsu = (tree)->
switch tree.type
when "Action"
nomsu = NomsuCode\from(tree.source)
- if tree.target
- inline_target = tree_to_inline_nomsu(tree.target)
- if tree.target.type == "Action"
- inline_target\parenthesize!
- nomsu\add inline_target, "::"
-
+ num_args = 0
for i,bit in ipairs tree
if type(bit) == "string"
clump_words = if type(tree[i-1]) == 'string'
@@ -47,6 +42,7 @@ tree_to_inline_nomsu = (tree)->
nomsu\add " " if i > 1 and not clump_words
nomsu\add bit
else
+ num_args += 1
arg_nomsu = tree_to_inline_nomsu(bit)
if bit.type == "Block"
if i > 1 and i < #tree
@@ -55,13 +51,14 @@ tree_to_inline_nomsu = (tree)->
arg_nomsu\parenthesize!
else
nomsu\add " " if i > 1
- if bit.type == "Action"
+ if bit.type == "Action" or bit.type == "MethodCall"
arg_nomsu\parenthesize!
nomsu\add arg_nomsu
- if #tree == 1 and type(tree[1]) != "string"
- nomsu\add "()"
return nomsu
+ when "MethodCall"
+ return NomsuCode\from(tree.source, tree_to_inline_nomsu(tree[1]), "|", tree_to_inline_nomsu(tree[2]))
+
when "EscapedNomsu"
inner_nomsu = tree_to_inline_nomsu(tree[1])
unless tree[1].type == "List" or tree[1].type == "Dict" or tree[1].type == "Var"
@@ -108,7 +105,7 @@ tree_to_inline_nomsu = (tree)->
nomsu = if key.type == "Text" and #key == 1 and is_identifier(key[1])
NomsuCode\from(key.source, key[1])
else tree_to_inline_nomsu(key)
- nomsu\parenthesize! if key.type == "Action" or key.type == "Block"
+ nomsu\parenthesize! if key.type == "Action" or key.type == "MethodCall" or key.type == "Block"
if value
nomsu\add ": "
value_nomsu = tree_to_inline_nomsu(value)
@@ -125,8 +122,11 @@ tree_to_inline_nomsu = (tree)->
bit[1]
else tree_to_inline_nomsu(bit)
assert bit.type != "Block"
- if bit.type == "Action" or bit.type == "IndexChain" or (bit.type == "Number" and i < #tree)
- bit_nomsu\parenthesize!
+ switch bit.type
+ when "Action", "MethodCall", "IndexChain"
+ bit_nomsu\parenthesize!
+ when "Number"
+ bit_nomsu\parenthesize! if bit_nomsu\text!\match("%.")
nomsu\add bit_nomsu
return nomsu
@@ -139,7 +139,12 @@ tree_to_inline_nomsu = (tree)->
return NomsuCode\from(tree.source, s)
when "Var"
- return NomsuCode\from(tree.source, "%", tree[1])
+ -- TODO: remove this hack:
+ varname = tree[1]\gsub("_", " ")
+ if varname == "" or is_identifier(varname)
+ return NomsuCode\from(tree.source, "$", varname)
+ else
+ return NomsuCode\from(tree.source, "$(", varname, ")")
when "FileChunks"
error("Can't inline a FileChunks")
@@ -170,11 +175,13 @@ tree_to_nomsu = (tree)->
if try_inline
inline_nomsu = tree_to_inline_nomsu(t)
if #inline_nomsu\text! <= space or #inline_nomsu\text! <= 8
- if t.type == "Action"
+ if t.type == "Action" or t.type == "MethodCall"
inline_nomsu\parenthesize!
return inline_nomsu
+ if t.type == "Text" and #inline_nomsu\text! + 2 < MAX_LINE
+ return inline_nomsu
indented = tree_to_nomsu(t)
- if t.type == "Action"
+ if t.type == "Action" or t.type == "MethodCall"
if indented\is_multiline!
return NomsuCode\from(t.source, "(..)\n ", indented)
else indented\parenthesize!
@@ -199,14 +206,8 @@ tree_to_nomsu = (tree)->
when "Action"
next_space = ""
- if tree.target
- target_nomsu = recurse(tree.target)
- if tree.target.type == "Block" and not target_nomsu\is_multiline!
- target_nomsu\parenthesize!
- nomsu\add target_nomsu
- nomsu\add(target_nomsu\is_multiline! and "\n..::" or "::")
-
word_buffer = {}
+ num_args = 0
for i,bit in ipairs tree
if type(bit) == "string"
if #word_buffer > 0 and is_operator(bit) == is_operator(word_buffer[#word_buffer])
@@ -225,6 +226,7 @@ tree_to_nomsu = (tree)->
word_buffer = {}
next_space = " "
+ num_args += 1
bit_nomsu = recurse(bit)
if bit.type == "Block" and not bit_nomsu\is_multiline!
-- Rule of thumb: nontrivial one-liner block arguments should be no more
@@ -235,7 +237,7 @@ tree_to_nomsu = (tree)->
if (next_space == " " and not bit_nomsu\is_multiline! and
nomsu\trailing_line_len! + #bit_nomsu\text! > MAX_LINE and
nomsu\trailing_line_len! > 8)
- if bit.type == 'Action'
+ if bit.type == 'Action' or bit.type == "MethodCall"
bit_nomsu = NomsuCode\from bit.source, "(..)\n ", tree_to_nomsu(bit)
else
next_space = " \\\n.."
@@ -255,10 +257,14 @@ tree_to_nomsu = (tree)->
nomsu\add next_space, words
next_space = " "
- if #tree == 1 and type(tree[1]) != "string"
- if next_space == " " then next_space = ""
- nomsu\add next_space, "()"
+ return nomsu
+ when "MethodCall"
+ target_nomsu = recurse(tree[1])
+ if tree[1].type == "Block" and not target_nomsu\is_multiline!
+ target_nomsu\parenthesize!
+ nomsu\add target_nomsu
+ nomsu\add(target_nomsu\is_multiline! and " \\\n..|" or "|")
return nomsu
when "EscapedNomsu"
@@ -321,7 +327,7 @@ tree_to_nomsu = (tree)->
if interp_nomsu\is_multiline!
nomsu\add "\n.."
add_text(tree)
- return NomsuCode\from(tree.source, '"\\\n ..', nomsu, '"')
+ return NomsuCode\from(tree.source, '"\n ', nomsu, '"')
when "List", "Dict"
if #tree == 0
diff --git a/syntax_tree.lua b/syntax_tree.lua
index ddd607c..ba74708 100644
--- a/syntax_tree.lua
+++ b/syntax_tree.lua
@@ -59,9 +59,6 @@ do
return false
end
end
- if self.target ~= other.target then
- return false
- end
return true
end,
as_lua = function(self)
@@ -146,19 +143,31 @@ do
return replacement
end,
get_args = function(self)
- assert(self.type == "Action", "Only actions have arguments")
- local args = {
- self.target
- }
- for _index_0 = 1, #self do
- local tok = self[_index_0]
- if type(tok) ~= 'string' then
- args[#args + 1] = tok
+ assert(self.type == "Action" or self.type == "MethodCall", "Only actions and method calls have arguments")
+ local args = { }
+ if self.type == "MethodCall" then
+ args[1] = self[1]
+ local _list_0 = self[2]
+ for _index_0 = 1, #_list_0 do
+ local tok = _list_0[_index_0]
+ if type(tok) ~= 'string' then
+ args[#args + 1] = tok
+ end
+ end
+ else
+ for _index_0 = 1, #self do
+ local tok = self[_index_0]
+ if type(tok) ~= 'string' then
+ args[#args + 1] = tok
+ end
end
end
return args
end,
get_stub = function(self)
+ if self.type == "MethodCall" then
+ return self[2]:get_stub()
+ end
local stub_bits = { }
local arg_i = 1
for _index_0 = 1, #self do
@@ -213,6 +222,8 @@ getmetatable(SyntaxTree).__call = function(self, t)
setmetatable(t, self.__base)
if t.type == 'Action' then
t.stub = t:get_stub()
+ elseif t.type == 'MethodCall' then
+ t.stub = t[2]:get_stub()
end
return t
end
diff --git a/syntax_tree.moon b/syntax_tree.moon
index 71b9bac..e2bbb4e 100644
--- a/syntax_tree.moon
+++ b/syntax_tree.moon
@@ -29,7 +29,6 @@ class SyntaxTree
return false if type(@) != type(other) or #@ != #other or getmetatable(@) != getmetatable(other)
for i=1,#@
return false if @[i] != other[i]
- return false if @target != other.target
return true
as_lua: =>
@@ -74,13 +73,20 @@ class SyntaxTree
return replacement
get_args: =>
- assert(@type == "Action", "Only actions have arguments")
- args = {@target}
- for tok in *@
- if type(tok) != 'string' then args[#args+1] = tok
+ assert(@type == "Action" or @type == "MethodCall", "Only actions and method calls have arguments")
+ args = {}
+ if @type == "MethodCall"
+ args[1] = @[1]
+ for tok in *@[2]
+ if type(tok) != 'string' then args[#args+1] = tok
+ else
+ for tok in *@
+ if type(tok) != 'string' then args[#args+1] = tok
return args
get_stub: =>
+ if @type == "MethodCall"
+ return @[2]\get_stub!
stub_bits = {}
arg_i = 1
for a in *@
@@ -103,6 +109,8 @@ getmetatable(SyntaxTree).__call = (t)=>
setmetatable(t, @__base)
if t.type == 'Action'
t.stub = t\get_stub!
+ elseif t.type == 'MethodCall'
+ t.stub = t[2]\get_stub!
return t
return SyntaxTree
diff --git a/tools/parse.nom b/tools/parse.nom
index 4e61473..0745847 100755
--- a/tools/parse.nom
+++ b/tools/parse.nom
@@ -11,13 +11,14 @@ externally (print tree %t at indent %indent) means:
if %t.type is:
"Action":
say "\(%indent)Action (\(%t.stub)):"
- if %t.target:
- say "\%indent Target:"
- print tree %t.target at indent "\%indent "
-
for %arg in %t:
if (%arg is syntax tree):
print tree %arg at indent "\%indent "
+
+ "MethodCall":
+ say "\(%indent)MethodCall on:"
+ print tree %t.1 at indent "\%indent "
+ print tree %t.2 at indent "\%indent "
"Number":
say "\%indent\(%t.1)"