aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/metaprogramming.nom6
-rw-r--r--lib/object.nom148
-rwxr-xr-xnomsu.moon53
-rw-r--r--nomsu.peg24
4 files changed, 126 insertions, 105 deletions
diff --git a/lib/metaprogramming.nom b/lib/metaprogramming.nom
index 8f2bde9..c6bf491 100644
--- a/lib/metaprogramming.nom
+++ b/lib/metaprogramming.nom
@@ -98,13 +98,13 @@ immediately
action [remove action %stub]
lua> ".."
- local fn = ACTIONS[\%stub];
+ local fn = ACTION[\%stub];
local metadata = ACTION_METADATA[fn];
for i=#metadata.aliases,1,-1 do
metadata.arg_orders[metadata.aliases[i]] = nil;
table.remove(metadata.aliases, i);
end
- ACTIONS[\%stub] = nil;
+ ACTION[\%stub] = nil;
immediately
action [%tree as lua]
@@ -141,7 +141,7 @@ immediately
compile [%var as lua identifier] to {expr:"nomsu:var_to_lua_identifier(\(%var as lua expr))"}
action [action %names metadata]
- =lua "ACTION_METADATA[ACTIONS[\%names]]"
+ =lua "ACTION_METADATA[ACTION[\%names]]"
# Get the source code for a function
action [help %action]
diff --git a/lib/object.nom b/lib/object.nom
index b7506f3..f3fcd86 100644
--- a/lib/object.nom
+++ b/lib/object.nom
@@ -6,26 +6,26 @@ compile [@%var] to
local key_attr = (key_lua:match("'([a-zA-Z][a-zA-Z0-9]*)'")
or key_lua:match('"([a-zA-Z][a-zA-Z0-9]*)"'));
if key_attr then
- return "_me."..key_attr;
+ return {expr="_me."..key_attr};
elseif key_lua:sub(1,1) == "[" then
key_lua = " "..key_lua.." ";
end
- return "_me["..key_lua.."]";
+ return {expr="_me["..key_lua.."]"};
-compile [@%var <- %val] to code
+compile [@%var <- %val] to
lua> ".."
- local val_lua = \(%val as lua);
+ local val_lua = \(%val as lua expr);
local key_lua = repr(\%var.value);
local key_attr = (key_lua:match("'([a-zA-Z][a-zA-Z0-9]*)'")
or key_lua:match('"([a-zA-Z][a-zA-Z0-9]*)"'));
if key_attr then
- return "_me."..key_attr.." = "..val_lua..";";
+ return {statements="_me."..key_attr.." = "..val_lua..";"};
elseif key_lua:sub(1,1) == "[" then
key_lua = " "..key_lua.." ";
end
- return "_me["..key_lua.."] = "..val_lua..";";
+ return {statements="_me["..key_lua.."] = "..val_lua..";"};
-compile [object %classname %class_body] to
+compile [define object %classname %class_body] to
%class_identifier <- (=lua "nomsu:var_to_lua_identifier(\(%classname as value)):sub(2,-1)")
if: %class_identifier is ""
%class_identifier <- "class"
@@ -34,71 +34,91 @@ compile [object %classname %class_body] to
if: (%line's "type") is "Comment"
do next %line
assume (((%line's "type") == "FunctionCall") and ((%line's "stub") == "action % %"))
- ..or barf "Only action definitions are supported inside 'object % %', not \(%line's "src")"
+ ..or barf "Only action definitions are supported inside 'define object % %', not \(%line's "src")"
+ %actions <- (2nd in (%line's "value"))
+ %body <- (3rd in (%line's "value"))
lua> ".."
- assert(\%line.value[3].type == "Block",
- "Invalid type for action definition body. Expected Block, but got: "..tostring(\%line.value[3].type));
- local names, args = nomsu:parse_spec(\%line.value[2]);
- local stub = nomsu:get_stub(names[1]);
- local body_lua = nomsu:tree_to_lua(\%line.value[3]);
- body_lua = body_lua.statements or ("return "..body_lua.expr..";");
- local arg_nomsus = {};
- for i, arg in ipairs(args) do arg_nomsus[i] = "nomsu:tree_to_lua("..arg..").expr"; end
+ local signature = {};
+ for i, action in ipairs(\%actions.value) do signature[i] = action:get_src(); end
+ local stubs = nomsu:get_stubs_from_signature(signature);
+ local stub_args = nomsu:get_args_from_signature(signature);
+ local arg_set = {};
+ for i, arg in ipairs(stub_args[1]) do arg_set[arg] = true; end
+ local body_lua = nomsu:tree_to_lua(\%body);
+ local body_code = body_lua.statements or ("return "..body_lua.expr..";");
+ local undeclared_locals = {};
+ for i, body_local in ipairs(body_lua.locals or {}) do
+ if not arg_set[body_local] then
+ table.insert(undeclared_locals, body_local);
+ end
+ end
+ if #undeclared_locals > 0 then
+ body_code = "local "..table.concat(undeclared_locals, ", ")..";\\n"..body_code;
+ end
+ local lua_fn_args = table.concat(stub_args[1], ", ");
+ local def_tree = nomsu.compilestack[#nomsu.compilestack];
+ local code_location = ("%s:%s,%s"):format(def_tree.filename, def_tree.start, def_tree.stop);
+
+ local compiled_args = {};
+ for i, arg in ipairs(stub_args[1]) do
+ compiled_args[i] = "nomsu:tree_to_lua("..arg..").expr";
+ end
+ compiled_args = table.concat(compiled_args, "..', '..");
table.insert(\%methods, ([==[
%s[ %s] = function(%s)
%s
end
nomsu:define_compile_action(%s, %s, function(%s)
return {expr="("..nomsu:tree_to_lua(_me).expr..")[ "..%s.."]("..%s..")"};
- end, %s);
- ACTION_METADATA[%s[ %s]] = ACTION_METADATA[ACTIONS[ %s]];
+ end);
+ ACTION_METADATA[%s[ %s]] = ACTION_METADATA[ACTION[ %s]];
]==]):format(
- \%class_identifier, repr(stub), table.concat(args, ", "),
- body_lua,
- repr(names), repr(\%line:get_line_no()), table.concat(args, ", "),
- repr(repr(stub)), table.concat(arg_nomsus, ".."),
- repr(nomsu:dedent(\%line:get_src())),
- \%class_identifier, repr(stub), repr(stub)));
+ \%class_identifier, repr(stubs[1]), lua_fn_args,
+ body_code,
+ repr(signature), repr(code_location), lua_fn_args,
+ repr(repr(stubs[1])), compiled_args,
+ \%class_identifier, repr(stubs[1]), repr(stubs[1])));
- return ".."
- do -- \%class_identifier
- -- Create the class object:
- local \%class_identifier = setmetatable({
- name=\(%classname as lua), instances=setmetatable({}, {__mode="k"}),
- }, {
- __tostring=function(c) return c.name; end,
- __call=function(cls, inst)
- inst = inst or {};
- inst.id = tostring(inst):match('table: (.*)');
- setmetatable(inst, cls.instance_metatable);
- cls.instances[inst] = true;
- if inst['set % up'] then
- inst['set % up'](inst);
- end
- return inst;
- end,
- });
- \%class_identifier.class = \%class_identifier;
+ return {..}
+ statements:".."
+ do -- \%class_identifier
+ -- Create the class object:
+ local \%class_identifier = setmetatable({
+ name=\(%classname as lua expr), instances=setmetatable({}, {__mode="k"}),
+ }, {
+ __tostring=function(c) return c.name; end,
+ __call=function(cls, inst)
+ inst = inst or {};
+ inst.id = tostring(inst):match('table: (.*)');
+ setmetatable(inst, cls.instance_metatable);
+ cls.instances[inst] = true;
+ if inst['set % up'] then
+ inst['set % up'](inst);
+ end
+ return inst;
+ end,
+ });
+ \%class_identifier.class = \%class_identifier;
- -- Define the methods:
- \(%methods joined with "\n")
+ -- Define the methods:
+ \(%methods joined with "\n")
- -- Define class methods for instantiating and accessing instances:
- \%class_identifier.instance_metatable = {
- __index=\%class_identifier,
- __tostring=\%class_identifier['% as text'] or function(inst)
- return "<"..inst.class.name..": "..inst.id..">";
- end,
- };
- nomsu:define_action("instances of "..\%class_identifier.name, "lib/class.nom", function()
- return utils.keys(\%class_identifier.instances);
- end, "");
- nomsu:define_action("new "..\%class_identifier.name.." %instance", "lib/class.nom", function(_instance)
- return \%class_identifier(_instance);
- end, "");
- nomsu:define_action("new "..\%class_identifier.name, "lib/class.nom", function()
- return \%class_identifier({});
- end, "");
- end -- End of definition of \%class_identifier
- \("\n\n")
-
+ -- Define class methods for instantiating and accessing instances:
+ \%class_identifier.instance_metatable = {
+ __index=\%class_identifier,
+ __tostring=\%class_identifier['% as text'] or function(inst)
+ return "<"..inst.class.name..": "..inst.id..">";
+ end,
+ };
+ nomsu:define_action("instances of "..\%class_identifier.name, "lib/class.nom", function()
+ return utils.keys(\%class_identifier.instances);
+ end, "");
+ nomsu:define_action("new "..\%class_identifier.name.." %instance", "lib/class.nom", function(_instance)
+ return \%class_identifier(_instance);
+ end, "");
+ nomsu:define_action("new "..\%class_identifier.name, "lib/class.nom", function()
+ return \%class_identifier({});
+ end, "");
+ end -- End of definition of \%class_identifier
+ \("\n\n")
+
diff --git a/nomsu.moon b/nomsu.moon
index a42e2e9..0f9ebbf 100755
--- a/nomsu.moon
+++ b/nomsu.moon
@@ -66,33 +66,28 @@ NOMSU_DEFS = with {}
R("\240\244")*R("\128\191")*R("\128\191")*R("\128\191"))
.ident_char = R("az","AZ","09") + P("_") + .utf8_char
- -- If the number of leading space characters is greater than number in the top of the
- -- stack, this pattern matches and pushes the number onto the stack.
+ -- If the line begins with #indent+4 spaces, the pattern matches *those* spaces
+ -- and adds them to the stack (not any more).
.indent = P (start)=>
- spaces = @match("[ \t]*", start)
- if #spaces > lpeg.userdata.indent_stack[#lpeg.userdata.indent_stack]
- insert(lpeg.userdata.indent_stack, #spaces)
- return start + #spaces
- -- If the number of leading space characters is less than number in the top of the
- -- stack, this pattern matches and pops off the top of the stack exactly once.
+ nodent = lpeg.userdata.indent_stack[#lpeg.userdata.indent_stack]
+ indented = nodent.." "
+ if @sub(start, start+#indented-1) == indented
+ insert(lpeg.userdata.indent_stack, indented)
+ return start + #indented
+ -- If the number of leading space characters is <= the number of space on the top of the
+ -- stack minus 4, this pattern matches and pops off the top of the stack exactly once.
.dedent = P (start)=>
- spaces = @match("[ \t]*", start)
- if #spaces < lpeg.userdata.indent_stack[#lpeg.userdata.indent_stack]
+ nodent = lpeg.userdata.indent_stack[#lpeg.userdata.indent_stack]
+ spaces = @match("[ ]*", start)
+ if #spaces <= #nodent-4
remove(lpeg.userdata.indent_stack)
return start
- -- If the number of leading space characters is equal to the number on the top of the
+ -- If the number of leading space characters is >= the number on the top of the
-- stack, this pattern matches and does not modify the stack.
.nodent = P (start)=>
- spaces = @match("[ \t]*", start)
- if #spaces == lpeg.userdata.indent_stack[#lpeg.userdata.indent_stack]
- return start + #spaces
- -- If the number of leading space characters is 4+ more than the number on the top of the
- -- stack, this pattern matches the first n+4 spaces and does not modify the stack.
- .gt_nodent = P (start)=>
- -- Note! This assumes indent is exactly 4 spaces!!!
- spaces = @match("[ \t]*", start)
- if #spaces >= lpeg.userdata.indent_stack[#lpeg.userdata.indent_stack] + 4
- return start + lpeg.userdata.indent_stack[#lpeg.userdata.indent_stack] + 4
+ nodent = lpeg.userdata.indent_stack[#lpeg.userdata.indent_stack]
+ if @sub(start, start+#nodent-1) == nodent
+ return start + #nodent
.error = (src,pos,err_msg)->
if lpeg.userdata.source_code\sub(pos,pos)\match("[\r\n]")
@@ -146,7 +141,6 @@ NOMSU = do
re.compile(nomsu_peg, NOMSU_DEFS)
class NomsuCompiler
- @def_number: 0
new:()=>
-- Weak-key mapping from objects to randomly generated unique IDs
NaN_surrogate = {}
@@ -173,7 +167,7 @@ class NomsuCompiler
:table, :assert, :dofile, :loadstring, :type, :select, :debug, :math, :io, :pairs,
:load, :ipairs,
}
- @environment.ACTIONS = setmetatable({}, {__index:(key)=>
+ @environment.ACTION = setmetatable({}, {__index:(key)=>
error("Attempt to run undefined action: #{key}", 0)
})
@action_metadata = setmetatable({}, {__mode:"k"})
@@ -192,7 +186,6 @@ class NomsuCompiler
error("Invalid signature, expected list of strings, but got: #{repr signature}", 0)
stubs = @get_stubs_from_signature signature
stub_args = @get_args_from_signature signature
- @@def_number += 1
fn_info = debug.getinfo(fn, "u")
local fn_arg_positions, arg_orders
@@ -202,9 +195,9 @@ class NomsuCompiler
for sig_i=1,#stubs
stub, args = stubs[sig_i], stub_args[sig_i]
if @debug
- print "#{colored.bright "ALIAS:"} #{colored.underscore colored.magenta repr(stub)} #{colored.bright "WITH ARGS"} #{colored.dim repr(args)} ON: #{@environment.ACTIONS}"
- -- TODO: use debug.getupvalue instead of @environment.ACTIONS?
- @environment.ACTIONS[stub] = fn
+ print "#{colored.bright "ALIAS:"} #{colored.underscore colored.magenta repr(stub)} #{colored.bright "WITH ARGS"} #{colored.dim repr(args)} ON: #{@environment.ACTION}"
+ -- TODO: use debug.getupvalue instead of @environment.ACTION?
+ @environment.ACTION[stub] = fn
unless fn_info.isvararg
arg_positions = [fn_arg_positions[a] for a in *args]
-- TODO: better error checking?
@@ -255,7 +248,7 @@ class NomsuCompiler
if @debug
print "#{colored.bright "PARSING:"}\n#{colored.yellow nomsu_code}"
- userdata = with {source_code:nomsu_code, :filename, indent_stack: {0}}
+ userdata = with {source_code:nomsu_code, :filename, indent_stack: {""}}
.get_src = => nomsu_code\sub(@start, @stop-1)
.line_starts = line_counter\match(.source_code)
.get_line_no = =>
@@ -650,7 +643,7 @@ class NomsuCompiler
insert @compilestack, tree
-- Rawget here to avoid triggering an error for accessing an undefined action
- fn = rawget(@environment.ACTIONS, tree.stub)
+ fn = rawget(@environment.ACTION, tree.stub)
metadata = @environment.ACTION_METADATA[fn]
if metadata and metadata.compile_time
args = [arg for arg in *tree.value when arg.type != "Word"]
@@ -691,7 +684,7 @@ class NomsuCompiler
args = new_args
remove @compilestack
- return expr:@@comma_separated_items("ACTIONS[#{repr tree.stub}](", args, ")")
+ return expr:@@comma_separated_items("ACTION[#{repr tree.stub}](", args, ")")
when "Text"
concat_parts = {}
diff --git a/nomsu.peg b/nomsu.peg
index e7f34a3..ae3e488 100644
--- a/nomsu.peg
+++ b/nomsu.peg
@@ -10,7 +10,7 @@ shebang: "#!" [^%nl]* %nl
statement: functioncall / expression
indented_block (Block):
- {| indent
+ {| ("(..)")? indent
statement (nodent statement)*
(dedent / (("" -> "Error while parsing block") => error))
|}
@@ -36,20 +36,28 @@ functioncall (FunctionCall):
word (Word): { %operator / (!number plain_word) }
inline_text (Text):
- !('".."' %ws* line_comment? %nl (%ws* %nl)* %gt_nodent)
+ !('".."' eol)
'"' {|
({~ (('\"' -> '"') / ('\\' -> '\') / %escaped_char / [^%nl\"])+ ~}
/ text_interpolation)*
|} '"'
+-- Have to use "%indent" instead of "indent" etc. to avoid messing up text lines that start with "#"
indented_text (Text):
- '".."' %ws* line_comment? %nl %gt_nodent? {|
+ '".."' eol %nl {|
+ {~ (%nl*) (%indent -> "") ~}
({~
- (("\\" -> "\") / (("\" eol %nl %gt_nodent "..") -> "")
- / (%nl+ {~ %gt_nodent -> "" ~}) / [^%nl\])+
+ (("\\" -> "\") / (("\" eol %nl+ %nodent "..") -> "")
+ / (%nl+ {~ %nodent -> "" ~}) / [^%nl\])+
~} / text_interpolation)*
- |} ((!.) / (&(%nl+ !%gt_nodent)) / (("" -> "Error while parsing Text") => error))
+ |} (((!.) &%dedent) / (&(%nl %dedent)) / (("" -> "Error while parsing Text") => error))
text_interpolation:
- "\" (variable / inline_list / inline_dict / inline_text / ("(" %ws* (inline_functioncall / inline_expression) %ws* ")"))
+ "\" (
+ variable / inline_list / inline_dict / inline_text
+ / ("(" %ws* (inline_functioncall / inline_expression) %ws* ")")
+ / (%ws* (block_comment / line_comment)? nodent "..")
+ / (indented_text %nl+ %nodent "..")
+ / ((indented_list / indented_block) nodent "..")
+ )
number (Number): (("-"? (([0-9]+ "." [0-9]+) / ("." [0-9]+) / ([0-9]+)))-> tonumber)
@@ -84,7 +92,7 @@ dict_line:
inline_dict_item:
{| {:dict_key: inline_expression / word :} %ws* ":" %ws* {:dict_value: inline_functioncall / inline_expression :} |}
-block_comment(Comment): "#.." { [^%nl]* (%nl %gt_nodent [^%nl]*)* }
+block_comment(Comment): "#.." { [^%nl]* (%nl+ %indent [^%nl]* (%nl+ %nodent [^%nl]*)* %dedent)? }
line_comment(Comment): "#" { [^%nl]* }
eol: %ws* line_comment? (!. / &%nl)