aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorBruce Hill <bitbucket@bruce-hill.com>2018-01-18 01:49:13 -0800
committerBruce Hill <bitbucket@bruce-hill.com>2018-01-18 01:49:27 -0800
commitf91d06d9fa567b26a098ff26a0339afdd5daa778 (patch)
tree6899c345433629b05c5b51f6545ae7ba02a692e7 /lib
parent12cc294c7ac31e3d08d6b8924dfc6cc427a1f712 (diff)
Initial commit of object oriented classes.
Diffstat (limited to 'lib')
-rw-r--r--lib/class.nom116
-rw-r--r--lib/control_flow.nom4
-rw-r--r--lib/metaprogramming.nom55
-rw-r--r--lib/operators.nom22
-rw-r--r--lib/text.nom8
5 files changed, 179 insertions, 26 deletions
diff --git a/lib/class.nom b/lib/class.nom
new file mode 100644
index 0000000..7099d48
--- /dev/null
+++ b/lib/class.nom
@@ -0,0 +1,116 @@
+use "lib/core.nom"
+
+compile [@%var] to:
+ lua> ".."
+ 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;
+ elseif key_lua:sub(1,1) == "[" then
+ key_lua = " "..key_lua.." ";
+ end
+ return "_me["..key_lua.."]";
+
+compile [set @%var = %val] to code:
+ lua> ".."
+ local val_lua = \(%val as lua);
+ 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..";";
+ elseif key_lua:sub(1,1) == "[" then
+ key_lua = " "..key_lua.." ";
+ end
+ return "_me["..key_lua.."] = "..val_lua..";";
+
+compile [object %classname %class_body] to:
+ local [%methods]
+ set %methods = []
+ for %line in (%class_body's "value"):
+ if ((%line's "type") == "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")"
+ 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..";");
+ table.insert(\%methods, ([[
+ { aliases=%s,
+ line=%s,
+ source=%s,
+ action=function(%s)
+ %s
+ end,
+ compile_to=function(%s)
+ return {expr="("..nomsu:tree_to_lua(_me).expr..")[ "..%s.."]("..nomsu:tree_to_lua(%s).expr..")"};
+ end,
+ }]]):format(repr(names), repr(\%line:get_line_no()), repr(nomsu:dedent(\%line.src)),
+ table.concat(args, ", "), body_lua,
+ table.concat(args, ", "), repr(repr(stub)), table.concat(args, ").expr..nomsu:tree_to_lua(")));
+
+ local %class_identifier
+ set %class_identifier = (=lua "nomsu:var_to_lua_identifier(\(%classname as value)):sub(2,-1)")
+ if (%class_identifier == ""):
+ set %class_identifier = "class"
+
+ 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;
+
+ -- Define the methods:
+ \%class_identifier.methods = {
+ \(join %methods with ",\n ")
+ }
+
+ -- Define nomsu compile time actions that rewrite "%foo baz %x" to %foo['% baz %'](%foo, %x)
+ -- so classes can have overlapping method names:
+ for i,method in ipairs(\%class_identifier.methods) do
+ for a_i, alias in ipairs(method.aliases) do
+ \%class_identifier[nomsu:get_stub(alias)] = method.action;
+ end
+ ACTION_METADATA[method.action] = {
+ fn=method.action, src=method.source, line_no=method.line, aliases=method.aliases,
+ };
+ nomsu:define_compile_action(method.aliases, method.line, method.compile_to, method.source);
+ end
+
+ -- 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/lib/control_flow.nom b/lib/control_flow.nom
index 7455ee9..dd602ff 100644
--- a/lib/control_flow.nom
+++ b/lib/control_flow.nom
@@ -75,7 +75,7 @@ immediately:
action [tree %tree has function call %call]:
lua> ".."
local target = (\%call).stub;
- for subtree,_ in coroutine.wrap(function() nomsu:walk_tree(\%tree); end) do
+ for subtree,depth in coroutine.wrap(function() nomsu:walk_tree(\%tree); end) do
if type(subtree) == 'table' and subtree.type == "FunctionCall"
and subtree.stub == target then
return true;
@@ -397,7 +397,7 @@ immediately:
# Do/finally:
immediately:
compile [do %action] to code:
- (%action as lua statements) if ((%action's "type") is "Block")
+ "do\n \(%action as lua statements)\nend" if ((%action's "type") is "Block")
..else "(\(%action as lua))(nomsu);"
compile [do %action then always %final_action] to code: ".."
diff --git a/lib/metaprogramming.nom b/lib/metaprogramming.nom
index a0b8685..ba63c25 100644
--- a/lib/metaprogramming.nom
+++ b/lib/metaprogramming.nom
@@ -6,26 +6,40 @@
immediately:
lua> ".."
nomsu.parse_spec = function(nomsu, spec)
- local names = {};
- for i, alias in ipairs(spec.value) do
- names[i] = alias.src;
+ if spec.type == 'List' then
+ local names = {};
+ for i, alias in ipairs(spec.value) do
+ if alias.type == "FunctionCall" then
+ names[i] = alias.src;
+ elseif alias.type == "Text" then
+ names[i] = nomsu:tree_to_value(alias);
+ end
+ end
+ local junk, arg_names, junk = nomsu:get_stub(names[1]);
+ local args = {};
+ for i, a in ipairs(arg_names) do args[i] = nomsu:var_to_lua_identifier(a); end
+ return names, args;
+ else
+ local alias = nomsu:tree_to_value(spec);
+ print("ALIAS!!! "..repr(alias).." from "..repr(spec));
+ local junk, arg_names, junk = nomsu:get_stub(alias);
+ local args = {};
+ for i, a in ipairs(arg_names) do args[i] = nomsu:var_to_lua_identifier(a); end
+ return {alias}, args;
end
- local _, arg_names, _ = nomsu:get_stub(spec.value[1]);
- local args = {};
- for i, a in ipairs(arg_names) do args[i] = nomsu:var_to_lua_identifier(a); end
- names, args = repr(names), table.concat(args, ", ");
- return names, args;
end
# Compile-time action to make compile-time actions:
+# TODO: reduce code duplication here
immediately:
lua> ".."
nomsu:define_compile_action("compile %names to %body", \(__line_no__), function(\%names, \%body)
- assert(\%names.type == "List",
- "Invalid type for compile definition names. Expected List, but got: "..tostring(\%names.type));
+ --assert(\%names.type == "List",
+ -- "Invalid type for compile definition names. Expected List, but got: "..tostring(\%names.type));
assert(\%body.type == "Block",
"Invalid type for compile definition body. Expected Block, but got: "..tostring(\%body.type));
local names, args = nomsu:parse_spec(\%names);
+ names, args = repr(names), table.concat(args, ", ");
local body_lua = nomsu:tree_to_lua(\%body);
body_lua = body_lua.statements or ("return "..body_lua.expr..";");
local lua = ([[
@@ -36,17 +50,18 @@ immediately:
local function compile_action_wrapper(%s) return {expr=compile_action(%s)}; end
nomsu:define_compile_action(%s, %s, compile_action_wrapper, %s);
end]]):format(args, body_lua, args, args, names, repr(\%names:get_line_no()),
- repr(("compile %s\\n..to code %s"):format(\%names.src, \%body.src)));
+ repr(("compile %s\\n..to %s"):format(\%names.src, \%body.src)));
return {statements=lua};
end, \(__src__ 1));
lua> ".."
nomsu:define_compile_action("compile %names to code %body", \(__line_no__), function(\%names, \%body)
- assert(\%names.type == "List",
- "Invalid type for compile definition names. Expected List, but got: "..tostring(\%names.type));
+ --assert(\%names.type == "List",
+ -- "Invalid type for compile definition names. Expected List, but got: "..tostring(\%names.type));
assert(\%body.type == "Block",
"Invalid type for compile definition body. Expected Block, but got: "..tostring(\%body.type));
local names, args = nomsu:parse_spec(\%names);
+ names, args = repr(names), table.concat(args, ", ");
local body_lua = nomsu:tree_to_lua(\%body);
body_lua = body_lua.statements or ("return "..body_lua.expr..";");
local lua = ([[
@@ -65,11 +80,12 @@ immediately:
immediately:
compile [action %names %body] to code:
lua> ".."
- assert(\%names.type == "List",
- "Invalid type for action definition names. Expected List, but got: "..tostring(\%names.type));
+ --assert(\%names.type == "List",
+ -- "Invalid type for action definition names. Expected List, but got: "..tostring(\%names.type));
assert(\%body.type == "Block",
"Invalid type for action definition body. Expected Block, but got: "..tostring(\%body.type));
local names, args = nomsu:parse_spec(\%names);
+ names, args = repr(names), table.concat(args, ", ");
local body_lua = nomsu:tree_to_lua(\%body);
body_lua = body_lua.statements or ("return "..body_lua.expr..";");
local src = nomsu:dedent(nomsu:source_code(0));
@@ -83,17 +99,18 @@ immediately:
immediately:
lua> ".."
nomsu:define_compile_action("parse %shorthand as %longhand", \(__line_no__), (function(\%shorthand, \%longhand)
- assert(\%shorthand.type == "List",
- "Invalid type for parse definition shorthand. Expected List, but got: "..tostring(\%shorthand.type));
+ --assert(\%shorthand.type == "List",
+ -- "Invalid type for parse definition shorthand. Expected List, but got: "..tostring(\%shorthand.type));
assert(\%longhand.type == "Block",
"Invalid type for parse definition body. Expected Block, but got: "..tostring(\%longhand.type));
local names, args = nomsu:parse_spec(\%shorthand);
+ names, args = repr(names), table.concat(args, ", ");
local template = {};
for i, line in ipairs(\%longhand.value) do
template[i] = nomsu:dedent(line.src);
end
template = repr(table.concat(template, "\\n"));
- local _, arg_names, _ = nomsu:get_stub(\%shorthand.value[1]);
+ local junk, arg_names, junk = nomsu:get_stub(\%shorthand.value[1]);
local replacements = {};
for i, a in ipairs(arg_names) do replacements[i] = "["..repr(a).."]="..nomsu:var_to_lua_identifier(a); end
replacements = "{"..table.concat(replacements, ", ").."}";
@@ -165,7 +182,7 @@ action [help %action]:
parse [eval %code, run %code] as: nomsu "run" [%code]
action [source code from tree %tree]:
lua> ".."
- local _,_,leading_space = \%tree.src:find("\\n(%s*)%S");
+ local junk,junk,leading_space = \%tree.src:find("\\n(%s*)%S");
if leading_space then
local chunk1, chunk2 = \%tree.src:match(":%s*([^\\n]*)(\\n.*)");
chunk2 = chunk2:gsub("\\n"..leading_space, "\\n");
diff --git a/lib/operators.nom b/lib/operators.nom
index b697aad..2bbeadf 100644
--- a/lib/operators.nom
+++ b/lib/operators.nom
@@ -5,7 +5,27 @@ use "lib/metaprogramming.nom"
# Indexing:
immediately:
- compile [%obj'%key, %obj's %key, %obj -> %key] to: "(\(%obj as lua))[\(%key as lua)]"
+ #.. NOTE!!! It's critical that there are spaces around %key if it's a string,
+ otherwise, Lua will get confused and interpret %obj[[[foo]]] as %obj("[foo]")
+ instead of %obj[ "foo" ].
+ It's also critical to have parens around %obj, otherwise Lua is too dumb to
+ realize that {x=1}["x"] is the same as ({x=1})["x"] or that
+ {x=1}.x is the same as ({x=1}).x
+ compile [%obj'%key, %obj's %key, %obj -> %key] to:
+ lua> ".."
+ local obj_lua = \(%obj as lua);
+ if not obj_lua:sub(-1,-1):match("[a-zA-Z)]") then
+ obj_lua = "("..obj_lua..")";
+ end
+ local key_lua = \(%key as lua);
+ 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 obj_lua.."."..key_attr;
+ elseif key_lua:sub(1,1) == "[" then
+ key_lua = " "..key_lua.." ";
+ end
+ return obj_lua.."["..key_lua.."]";
# Comparison Operators
immediately:
diff --git a/lib/text.nom b/lib/text.nom
index 9db8a07..5cb268d 100644
--- a/lib/text.nom
+++ b/lib/text.nom
@@ -52,8 +52,8 @@ lua do> ".."
local color = "'"..c.."'";
local reset = "'"..colors["reset color"].."'";
nomsu:define_compile_action(name, \(__line_no__), function() return {expr=color}; end, \(__src__ 1));
- nomsu:define_compile_action(name.." %", \(__line_no__), function(_)
- return {expr=color..".."..nomsu:tree_to_lua(_).expr..".."..reset};
+ nomsu:define_compile_action(name.." %", \(__line_no__), function(\%)
+ return {expr=color..".."..nomsu:tree_to_lua(\%).expr..".."..reset};
end, \(__src__ 1));
end
@@ -85,8 +85,8 @@ lua do> ".."
local e = "'"..\%str.."'";
local reset = "'"..\(%colors->"reset color").."'";
nomsu:define_compile_action(\%name, \(__line_no__), function(nomsu) return {expr=e}; end, \(__src__ 1));
- nomsu:define_compile_action(\%name.." %", \(__line_no__), function(nomsu, _)
- return {expr=e..".."..nomsu:tree_to_lua(_).expr..".."..reset};
+ nomsu:define_compile_action(\%name.." %", \(__line_no__), function(nomsu, \%)
+ return {expr=e..".."..nomsu:tree_to_lua(\%).expr..".."..reset};
end, \(__src__ 1));
# FIXME