Skip to content

Commit 345d42d

Browse files
committed
Added support for inlining #ifNil:* and #ifNotNil:* in the AST interpreter
Signed-off-by: Stefan Marr <[email protected]>
1 parent 2209150 commit 345d42d

File tree

3 files changed

+183
-0
lines changed

3 files changed

+183
-0
lines changed

src/som/compiler/ast/parser.py

+66
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
from som.interpreter.ast.nodes.specialized.literal_if import (
2525
IfInlinedNode,
2626
IfElseInlinedNode,
27+
IfNilInlinedNode,
28+
IfNotNilInlinedNode,
29+
IfNilNotNilInlinedNode,
2730
)
2831
from som.interpreter.ast.nodes.specialized.literal_while import WhileInlinedNode
2932
from som.vm.symbols import symbol_for
@@ -249,6 +252,26 @@ def _try_inlining_if(if_true, receiver, arguments, source, mgenc):
249252
body_expr = method.inline(mgenc)
250253
return IfInlinedNode(receiver, body_expr, if_true, source)
251254

255+
@staticmethod
256+
def _try_inlining_if_nil(receiver, arguments, source, mgenc):
257+
arg = arguments[0]
258+
if not isinstance(arg, BlockNode):
259+
return None
260+
261+
method = arg.get_method()
262+
body_expr = method.inline(mgenc)
263+
return IfNilInlinedNode(receiver, body_expr, source)
264+
265+
@staticmethod
266+
def _try_inlining_if_not_nil(receiver, arguments, source, mgenc):
267+
arg = arguments[0]
268+
if not isinstance(arg, BlockNode):
269+
return None
270+
271+
method = arg.get_method()
272+
body_expr = method.inline(mgenc)
273+
return IfNotNilInlinedNode(receiver, body_expr, source)
274+
252275
@staticmethod
253276
def _try_inlining_if_else(if_true, receiver, arguments, source, mgenc):
254277
arg1 = arguments[0]
@@ -263,6 +286,25 @@ def _try_inlining_if_else(if_true, receiver, arguments, source, mgenc):
263286
false_expr = arg2.get_method().inline(mgenc)
264287
return IfElseInlinedNode(receiver, true_expr, false_expr, if_true, source)
265288

289+
@staticmethod
290+
def _try_inlining_if_nil_not_nil(is_if_nil, receiver, arguments, source, mgenc):
291+
arg1 = arguments[0]
292+
if not isinstance(arg1, BlockNode):
293+
return None
294+
295+
arg2 = arguments[1]
296+
if not isinstance(arg2, BlockNode):
297+
return None
298+
299+
arg1_expr = arg1.get_method().inline(mgenc)
300+
arg2_expr = arg2.get_method().inline(mgenc)
301+
return IfNilNotNilInlinedNode(
302+
receiver,
303+
arg1_expr if is_if_nil else arg2_expr,
304+
arg2_expr if is_if_nil else arg1_expr,
305+
source,
306+
)
307+
266308
@staticmethod
267309
def _try_inlining_while(while_true, receiver, arguments, source, mgenc):
268310
if not isinstance(receiver, BlockNode):
@@ -321,6 +363,18 @@ def _keyword_message(self, mgenc, receiver):
321363
)
322364
if inlined is not None:
323365
return inlined
366+
elif keyword == "ifNil:":
367+
inlined = self._try_inlining_if_nil(
368+
receiver, arguments, source, mgenc
369+
)
370+
if inlined is not None:
371+
return inlined
372+
elif keyword == "ifNotNil:":
373+
inlined = self._try_inlining_if_not_nil(
374+
receiver, arguments, source, mgenc
375+
)
376+
if inlined is not None:
377+
return inlined
324378
elif keyword == "whileTrue:":
325379
inlined = self._try_inlining_while(
326380
True, receiver, arguments, source, mgenc
@@ -358,6 +412,18 @@ def _keyword_message(self, mgenc, receiver):
358412
)
359413
if inlined is not None:
360414
return inlined
415+
elif keyword == "ifNil:ifNotNil:":
416+
inlined = self._try_inlining_if_nil_not_nil(
417+
True, receiver, arguments, source, mgenc
418+
)
419+
if inlined is not None:
420+
return inlined
421+
elif keyword == "ifNotNil:ifNil:":
422+
inlined = self._try_inlining_if_nil_not_nil(
423+
False, receiver, arguments, source, mgenc
424+
)
425+
if inlined is not None:
426+
return inlined
361427

362428
selector = symbol_for(keyword)
363429

src/som/interpreter/ast/nodes/specialized/literal_if.py

+62
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,46 @@ def execute(self, frame):
3131
)
3232

3333

34+
class IfNilInlinedNode(ExpressionNode):
35+
_immutable_fields_ = [
36+
"_condition_expr?",
37+
"_body_expr?",
38+
"universe",
39+
]
40+
_child_nodes_ = ["_condition_expr", "_body_expr"]
41+
42+
def __init__(self, condition_expr, body_expr, source_section):
43+
ExpressionNode.__init__(self, source_section)
44+
self._condition_expr = self.adopt_child(condition_expr)
45+
self._body_expr = self.adopt_child(body_expr)
46+
47+
def execute(self, frame):
48+
result = self._condition_expr.execute(frame)
49+
if result is nilObject:
50+
return self._body_expr.execute(frame)
51+
return result
52+
53+
54+
class IfNotNilInlinedNode(ExpressionNode):
55+
_immutable_fields_ = [
56+
"_condition_expr?",
57+
"_body_expr?",
58+
"universe",
59+
]
60+
_child_nodes_ = ["_condition_expr", "_body_expr"]
61+
62+
def __init__(self, condition_expr, body_expr, source_section):
63+
ExpressionNode.__init__(self, source_section)
64+
self._condition_expr = self.adopt_child(condition_expr)
65+
self._body_expr = self.adopt_child(body_expr)
66+
67+
def execute(self, frame):
68+
result = self._condition_expr.execute(frame)
69+
if result is nilObject:
70+
return result
71+
return self._body_expr.execute(frame)
72+
73+
3474
class IfElseInlinedNode(ExpressionNode):
3575
_immutable_fields_ = [
3676
"_condition_expr?",
@@ -61,3 +101,25 @@ def execute(self, frame):
61101
"Would need to generalize, but we haven't implemented that "
62102
+ "for the bytecode interpreter either"
63103
)
104+
105+
106+
class IfNilNotNilInlinedNode(ExpressionNode):
107+
_immutable_fields_ = [
108+
"_condition_expr?",
109+
"_nil_expr?",
110+
"_not_nil_expr?",
111+
"universe",
112+
]
113+
_child_nodes_ = ["_condition_expr", "_nil_expr", "_not_nil_expr"]
114+
115+
def __init__(self, condition_expr, nil_expr, not_nil_expr, source_section):
116+
ExpressionNode.__init__(self, source_section)
117+
self._condition_expr = self.adopt_child(condition_expr)
118+
self._nil_expr = self.adopt_child(nil_expr)
119+
self._not_nil_expr = self.adopt_child(not_nil_expr)
120+
121+
def execute(self, frame):
122+
result = self._condition_expr.execute(frame)
123+
if result is nilObject:
124+
return self._nil_expr.execute(frame)
125+
return self._not_nil_expr.execute(frame)

tests/test_ast_inlining.py

+55
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
from som.interpreter.ast.nodes.specialized.literal_if import (
2323
IfInlinedNode,
2424
IfElseInlinedNode,
25+
IfNilInlinedNode,
26+
IfNotNilInlinedNode,
27+
IfNilNotNilInlinedNode,
2528
)
2629
from som.interpreter.ast.nodes.specialized.literal_while import WhileInlinedNode
2730
from som.interpreter.ast.nodes.variable_node import (
@@ -205,6 +208,29 @@ def test_if_arg(mgenc, if_selector, expected_bool, unexpected_bool):
205208
assert if_node._not_expected_bool is unexpected_bool
206209

207210

211+
@pytest.mark.parametrize(
212+
"if_selector,expected_class",
213+
[
214+
("ifNil:", IfNilInlinedNode),
215+
("ifNotNil:", IfNotNilInlinedNode),
216+
],
217+
)
218+
def test_if_nil_arg(mgenc, if_selector, expected_class):
219+
ast = parse_method(
220+
mgenc,
221+
"""
222+
test: arg = (
223+
#start.
224+
self method IF_SELECTOR [ arg ].
225+
#end
226+
)""".replace(
227+
"IF_SELECTOR", if_selector
228+
),
229+
)
230+
231+
assert isinstance(ast._exprs[1], expected_class)
232+
233+
208234
def test_if_true_and_inc_field(cgenc, mgenc):
209235
add_field(cgenc, "field")
210236
ast = parse_method(
@@ -430,6 +456,35 @@ def test_if_true_if_false_return(mgenc, sel1, sel2, expected_bool, unexpected_bo
430456
assert if_node._not_expected_bool is unexpected_bool
431457

432458

459+
@pytest.mark.parametrize(
460+
"sel1,sel2,expected_nil_expr,expected_not_nil_expr",
461+
[
462+
("ifNil:", "ifNotNil:", ReturnLocalNode, LiteralNode),
463+
("ifNotNil:", "ifNil:", LiteralNode, ReturnLocalNode),
464+
],
465+
)
466+
def test_if_nil_not_nil_return(
467+
mgenc, sel1, sel2, expected_nil_expr, expected_not_nil_expr
468+
):
469+
seq = parse_method(
470+
mgenc,
471+
"""
472+
test: arg1 with: arg2 = (
473+
#start.
474+
^ self method SEL1 [ ^ arg1 ] SEL2 [ #foo ]
475+
)""".replace(
476+
"SEL1", sel1
477+
).replace(
478+
"SEL2", sel2
479+
),
480+
)
481+
482+
assert isinstance(seq._exprs[1], IfNilNotNilInlinedNode)
483+
if_node = seq._exprs[1]
484+
assert isinstance(if_node._nil_expr, expected_nil_expr)
485+
assert isinstance(if_node._not_nil_expr, expected_not_nil_expr)
486+
487+
433488
@pytest.mark.parametrize(
434489
"while_sel,expected_bool,unexpected_bool",
435490
[

0 commit comments

Comments
 (0)