Skip to content

Commit

Permalink
fix: Complex chained comparison compile into broken code
Browse files Browse the repository at this point in the history
Squashed commit of the following:

commit 12c5908629226cefc2bfca43d3a6c78ccbdae02c
Author: Jan Max Meyer <[email protected]>
Date:   Sat Dec 28 16:39:11 2024 +0100

    Adding testcases

commit 5909fff
Author: Jan Max Meyer <[email protected]>
Date:   Fri Dec 27 23:01:46 2024 +0100

    Fixing chained comparison using ImlOp::If

commit 38b5a95
Author: Jan Max Meyer <[email protected]>
Date:   Fri Dec 27 20:11:06 2024 +0100

    Improve comparison-ast-traversal
  • Loading branch information
phorward committed Dec 28, 2024
1 parent a0e249d commit be76d76
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 33 deletions.
88 changes: 55 additions & 33 deletions src/compiler/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1022,10 +1022,14 @@ fn traverse_node(scope: &Scope, node: &Dict) -> ImlOp {
// comparison -----------------------------------------------------
"comparison" => {
// comparison can be a chain of comparisons, allowing to compare e.g. `1 < 2 < 3`
let mut branches = Vec::new();

let children = node["children"].borrow();
let mut children = children.object::<List>().unwrap().clone();
let children = children.object::<List>().unwrap();

let mut child_iter = children.iter().peekable();

let first = children.remove(0);
let first = child_iter.next().unwrap();
let first = first.borrow();

let mut ops = Vec::new();
Expand All @@ -1036,28 +1040,37 @@ fn traverse_node(scope: &Scope, node: &Dict) -> ImlOp {
Rvalue::CallOrLoad,
));

let mut backpatch = Vec::new();
while let Some(op) = child_iter.next() {
let op = op.borrow();
let op = op.object::<Dict>().unwrap();

while !children.is_empty() {
let child = children.remove(0);
let child = child.borrow();
let child = child.object::<Dict>().unwrap();

let emit = child["emit"].borrow();
let emit = op["emit"].borrow();
let emit = emit.object::<Str>().unwrap().as_str();

let next = child["children"].borrow();
let next = op["children"].borrow();

ops.push(traverse_node_rvalue(
scope,
&next.object::<Dict>().unwrap(),
Rvalue::CallOrLoad,
));
// Old (current) path
if let Some(next) = next.object::<Dict>() {
ops.push(traverse_node_rvalue(scope, &next, Rvalue::CallOrLoad));
}
// New (desired) path
else {
let next = child_iter.next().unwrap();
let next = next.borrow();

// Chained comparison requires forperand duplication
if !children.is_empty() {
ops.push(traverse_node_rvalue(
scope,
&next.object::<Dict>().unwrap(),
Rvalue::CallOrLoad,
));
}

let is_chained = child_iter.peek().is_some();

// Chained comparison requires for operand duplication
if is_chained {
ops.push(ImlOp::from(Op::Swap(2))); // Swap operands
ops.push(ImlOp::from(Op::Copy(2))); // Copy second operand
ops.push(ImlOp::from(Op::Copy(2))); // Copy second operand, to keep a copy
}

ops.push(ImlOp::from(match emit {
Expand All @@ -1070,25 +1083,34 @@ fn traverse_node(scope: &Scope, node: &Dict) -> ImlOp {
_ => unimplemented!("{}", emit),
}));

// Push and remember placeholder for later clean-up jump
if !children.is_empty() {
backpatch.push(ops.len());
ops.push(ImlOp::Nop); // Placeholder for condition
}
// Push this branch
branches.push(ops);
ops = Vec::new();
}

if backpatch.len() > 0 {
// Jump over clean-up part with last result
ops.push(ImlOp::from(Op::Forward(3)));
let mut branches = branches.into_iter().rev().peekable();

while let Some(mut branch) = branches.next() {
// println!("{:?} {:?}", branch, branches.peek().is_none());
branch.extend(ops);
ops = branch;

// Otherwise, remember clean-up start
let clean_up = ops.len();
ops.push(ImlOp::from(Op::Drop));
ops.push(ImlOp::from(Op::PushFalse));
if branches.peek().is_some() {
let then: Vec<_> = ops.drain(..).collect();

// Backpatch all placeholders to relative jump to the clean-up part
for index in backpatch {
ops[index] = ImlOp::from(Op::ForwardIfFalse(clean_up - index + 1));
ops.push(ImlOp::If {
peek: false,
test: true,
then: Box::new(if then.len() == 0 {
ImlOp::from(Op::PushTrue)
} else {
ImlOp::from(then)
}),
else_: Box::new(ImlOp::from(vec![
ImlOp::from(Op::Drop),
ImlOp::from(Op::PushFalse),
])),
})
}
}

Expand Down
8 changes: 8 additions & 0 deletions tests/comparison.tok
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
1 >= 1 > 2
2 >= 1 > 0

# complex chained comparison
l = 1, 2, 3
l[0] > l[1] > l[2]
l[0] < l[1] < l[2]

#---

#false
Expand All @@ -22,3 +27,6 @@
#true
#false
#true

#false
#true
13 changes: 13 additions & 0 deletions tests/comparison_captures.tok
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Testcase for chained comparions from capture.

_ : ' '

(Int _)+ print($1[0] < $1[1] < $1[2])

#---
#6 5 4
#4 5 6

#---
#false
#true

0 comments on commit be76d76

Please sign in to comment.