diff options
| author | Bruce Hill <bitbucket@bruce-hill.com> | 2018-01-18 01:49:13 -0800 |
|---|---|---|
| committer | Bruce Hill <bitbucket@bruce-hill.com> | 2018-01-18 01:49:27 -0800 |
| commit | f91d06d9fa567b26a098ff26a0339afdd5daa778 (patch) | |
| tree | 6899c345433629b05c5b51f6545ae7ba02a692e7 /lib/class.nom | |
| parent | 12cc294c7ac31e3d08d6b8924dfc6cc427a1f712 (diff) | |
Initial commit of object oriented classes.
Diffstat (limited to 'lib/class.nom')
| -rw-r--r-- | lib/class.nom | 116 |
1 files changed, 116 insertions, 0 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") + |
