Created
February 3, 2020 13:54
-
-
Save edubart/51de191cdbe02f87cfac999ee0cc70d8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-------------------------------------------------------------------------------- | |
-- meta programming utilities for inheritance | |
##[[ | |
local function check_record_type(sym) | |
staticassert(sym and sym.type and sym.type:is_type() and sym.value:is_record(), | |
"symbol '%s' must be a type holding a record type", sym.name) | |
return sym.value | |
end | |
local function class(recordsym, basesym) | |
local rectype = check_record_type(recordsym) | |
local kindid | |
if basesym then -- derived record | |
local basetype = check_record_type(basesym) | |
table.insert(basetype.classes, rectype) | |
kindid = #basetype.classes | |
for i,field in ipairs(basetype.fields) do | |
rectype:add_field(field.name, field.type, i) | |
end | |
rectype.base = basetype | |
else -- base record | |
assert(rectype:get_field('__kind'), 'missing __kind field') | |
rectype.classes = {} | |
rectype.methods = {} | |
kindid = 0 | |
end | |
rectype.kindid = kindid | |
]] | |
global #(recordsym.name)#.KindId: integer <comptime> = #[kindid]# | |
## end | |
##[[ | |
local function overrideable() | |
local fundefnode = context:get_parent_node()[2] | |
local rectype, name = fundefnode[2].attr.value | |
local name = context:get_parent_node()[2][1] | |
-- hygienize saves the current traversing scope for a callback | |
rectype.methods[name] = hygienize(function(f) f() end) | |
end | |
]] | |
##[[ | |
local function override() | |
local fundefnode = context:get_parent_node()[2] | |
local rectype, name = fundefnode[2].attr.value, fundefnode[1] | |
local method = rectype.base.methods[name] | |
method(function()]] | |
-- no problem to use ifs instead of switches because C compilers usually optimizes as a switch | |
if self.__kind == #[rectype.kindid]# then | |
return (@#[rectype]#*)(self):#(name)#() | |
end ##[[ | |
end) | |
end | |
]] | |
-------------------------------------------------------------------------------- | |
-- inheritance example via meta programming | |
require 'memory' | |
local Shape = @record{ | |
__kind: integer, | |
x: integer, y: integer | |
} ## class(Shape) | |
function Shape:area(): integer ## overrideable() | |
return 0 | |
end | |
local Rectangle = @record{ | |
w: integer, | |
h: integer | |
} ## class(Rectangle, Shape) | |
local Circle = @record{ | |
r: integer | |
} ## class(Circle, Shape) | |
function Rectangle:init(x: integer, y: integer, w: integer, h: integer) | |
$self = Rectangle{__kind = Rectangle.KindId, x=x, y=y, w=w, h=h} | |
end | |
function Rectangle:area(): integer ## override() | |
return self.w * self.h | |
end | |
function Circle:init(x: integer, y: integer, r: integer) | |
$self = Circle{__kind = Circle.KindId, x=x, y=y, r=r} | |
end | |
function Circle:area(): integer ## override() | |
return 3 * self.r * self.r | |
end | |
local r = memory.new(@Rectangle) | |
r:init(1,2,3,4) | |
local c = memory.new(@Circle) | |
c:init(5,6,7) | |
local sr, sc = (@Shape*)(r), (@Shape*)(c) | |
local sum = 0 | |
for i=1,100000000 do | |
sum = sum + (sr:area() + sc:area()) | |
end | |
memory.delete(r) | |
memory.delete(c) | |
print(sum) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment