-
-
Notifications
You must be signed in to change notification settings - Fork 68
/
record_inheretance.nelua
177 lines (148 loc) · 4.46 KB
/
record_inheretance.nelua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
--------------------------------------------------------------------------------
-- naive inheritance example without compile time magic
local ShapeKind = @enum(integer) {
None = 0,
Rectangle = 1,
Circle = 2,
}
local Shape = @record{
kind: ShapeKind,
x: number, y: number
}
local Rectangle = @record{
kind: ShapeKind,
x: number, y: number,
w: number, h: number
}
local Circle = @record{
kind: ShapeKind,
x: number, y: number,
r: number
}
function Rectangle.create(x: number, y: number, w: number, h: number): Rectangle
return Rectangle{kind=ShapeKind.Rectangle, x=x, y=y, w=w, h=h}
end
function Rectangle:area(): number
return self.w * self.h
end
function Circle.create(x: number, y: number, r: number): Circle
return Circle{kind=ShapeKind.Circle, x=x, y=y, r=r}
end
function Circle:area(): number
return 3.14 * self.r * self.r
end
function Shape:area(): number
switch self.kind
case ShapeKind.Rectangle then
return Rectangle.area((@*Rectangle)(self))
case ShapeKind.Circle then
return Circle.area((@*Circle)(self))
else
return 0
end
end
do -- test it
local circle = Circle.create(0, 0, 1)
local rectangle = Rectangle.create(0, 0, 2, 2)
print 'naive example =>'
print(' rectangle area is', rectangle:area())
print(' circle area is', circle:area())
local shape: *Shape
shape = (@*Shape)(&rectangle)
print(' circle shape area is', shape:area())
shape = (@*Shape)(&circle)
print('rectangle shape area is', shape:area())
print ''
end
--------------------------------------------------------------------------------
-- meta programming utilities for inheritance
##[[
local function check_record_type(sym)
static_assert(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.fields.__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_visiting_node(1)[2]
local rectype, name = fundefnode[2].attr.value
local name = context:get_visiting_node(1)[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_visiting_node(1)[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
local Shape = @record{
__kind: integer,
x: number, y: number
} ## class(Shape)
function Shape:area(): number ## overrideable()
return 0
end
local Rectangle = @record{
w: number,
h: number
} ## class(Rectangle, Shape)
local Circle = @record{
r: number
} ## class(Circle, Shape)
function Rectangle.create(x: number, y: number, w: number, h: number): Rectangle
return Rectangle{__kind = Rectangle.KindId, x=x, y=y, w=w, h=h}
end
function Rectangle:area(): number ## override()
return self.w * self.h
end
function Circle.create(x: number, y: number, r: number): Circle
return Circle{__kind = Circle.KindId, x=x, y=y, r=r}
end
function Circle:area(): number ## override()
return 3.14 * self.r * self.r
end
do -- test it
local circle = Circle.create(0, 0, 1)
local rectangle = Rectangle.create(0, 0, 2, 2)
print 'meta programming example =>'
print(' rectangle area is', rectangle:area())
print(' circle area is', circle:area())
local shape: *Shape
shape = (@*Shape)(&rectangle)
print(' circle shape area is', shape:area())
shape = (@*Shape)(&circle)
print('rectangle shape area is', shape:area())
print ''
end