Skip to content

Instantly share code, notes, and snippets.

@edubart
Created February 3, 2020 13:54
Show Gist options
  • Save edubart/51de191cdbe02f87cfac999ee0cc70d8 to your computer and use it in GitHub Desktop.
Save edubart/51de191cdbe02f87cfac999ee0cc70d8 to your computer and use it in GitHub Desktop.
--------------------------------------------------------------------------------
-- 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