diff --git a/examples/hello/hello.q b/examples/hello/hello.q index 7cdf32f..43be3f4 100644 --- a/examples/hello/hello.q +++ b/examples/hello/hello.q @@ -1,8 +1,8 @@ main() { x := f(1) + f(2) + f(3) - if x != f(8) || x != 9 || x == 6 { - exit(42) + if x != 9 { + exit(1) } exit(0) diff --git a/src/build/core/CompileCondition.go b/src/build/core/CompileCondition.go index 3a50df7..0365bc4 100644 --- a/src/build/core/CompileCondition.go +++ b/src/build/core/CompileCondition.go @@ -1,44 +1,74 @@ package core import ( + "fmt" + "git.akyoto.dev/cli/q/src/build/asm" "git.akyoto.dev/cli/q/src/build/expression" ) // CompileCondition inserts code to jump to the start label or end label depending on the truth of the condition. -func (f *Function) CompileCondition(condition *expression.Expression, startLabel string, endLabel string) error { +func (f *Function) CompileCondition(condition *expression.Expression, successLabel string, failLabel string) error { switch condition.Token.Text() { case "||": + leftFailLabel := fmt.Sprintf("%s_false_%d", f.Name, f.count.subBranch) + f.count.subBranch++ + + // Left left := condition.Children[0] - err := f.CompileCondition(left, startLabel, endLabel) + err := f.CompileCondition(left, successLabel, leftFailLabel) if err != nil { return err } - f.JumpIfTrue(left.Token.Text(), startLabel) + f.JumpIfTrue(left.Token.Text(), successLabel) + // Right + f.assembler.Label(asm.LABEL, leftFailLabel) right := condition.Children[1] - err = f.CompileCondition(right, startLabel, endLabel) + err = f.CompileCondition(right, successLabel, failLabel) + + if condition.Parent != nil && condition.Parent.Token.Text() == "||" && condition != condition.Parent.LastChild() { + f.JumpIfTrue(right.Token.Text(), successLabel) + } else { + f.JumpIfFalse(right.Token.Text(), failLabel) + } + + return err + + case "&&": + leftSuccessLabel := fmt.Sprintf("%s_true_%d", f.Name, f.count.subBranch) + f.count.subBranch++ + + // Left + left := condition.Children[0] + err := f.CompileCondition(left, leftSuccessLabel, failLabel) if err != nil { return err } - if condition.Parent == nil { - f.JumpIfFalse(right.Token.Text(), endLabel) + f.JumpIfFalse(left.Token.Text(), failLabel) + + // Right + f.assembler.Label(asm.LABEL, leftSuccessLabel) + right := condition.Children[1] + err = f.CompileCondition(right, successLabel, failLabel) + + if condition.Parent != nil && condition.Parent.Token.Text() == "||" && condition != condition.Parent.LastChild() { + f.JumpIfTrue(right.Token.Text(), successLabel) } else { - f.JumpIfTrue(right.Token.Text(), startLabel) + f.JumpIfFalse(right.Token.Text(), failLabel) } - return nil - case "&&": - return nil + return err + default: err := f.Compare(condition) if condition.Parent == nil { - f.JumpIfFalse(condition.Token.Text(), endLabel) + f.JumpIfFalse(condition.Token.Text(), failLabel) } return err diff --git a/src/build/core/CompileIf.go b/src/build/core/CompileIf.go index c73faba..7f0117f 100644 --- a/src/build/core/CompileIf.go +++ b/src/build/core/CompileIf.go @@ -9,16 +9,16 @@ import ( // CompileIf compiles a branch instruction. func (f *Function) CompileIf(branch *ast.If) error { - startLabel := fmt.Sprintf("%s_if_start_%d", f.Name, f.count.branch) - endLabel := fmt.Sprintf("%s_if_end_%d", f.Name, f.count.branch) - err := f.CompileCondition(branch.Condition, startLabel, endLabel) + success := fmt.Sprintf("%s_if_%d_true", f.Name, f.count.branch) + fail := fmt.Sprintf("%s_if_%d_false", f.Name, f.count.branch) + err := f.CompileCondition(branch.Condition, success, fail) if err != nil { return err } - f.assembler.Label(asm.LABEL, startLabel) - defer f.assembler.Label(asm.LABEL, endLabel) + f.assembler.Label(asm.LABEL, success) + defer f.assembler.Label(asm.LABEL, fail) f.count.branch++ return f.CompileAST(branch.Body) } diff --git a/src/build/core/state.go b/src/build/core/state.go index 3127eda..c10dc7e 100644 --- a/src/build/core/state.go +++ b/src/build/core/state.go @@ -21,8 +21,9 @@ type state struct { // counter stores how often a certain statement appeared so we can generate a unique label from it. type counter struct { - loop int - branch int + loop int + branch int + subBranch int } // PrintInstructions shows the assembly instructions. diff --git a/tests/programs/branch-and.q b/tests/programs/branch-and.q new file mode 100644 index 0000000..6761f1e --- /dev/null +++ b/tests/programs/branch-and.q @@ -0,0 +1,37 @@ +main() { + x := 0 + + if x != x && x != x { + exit(1) + } + + if x == x && x != x { + exit(1) + } + + if x != x && x == x { + exit(1) + } + + if x == x && x != x && x != x { + exit(1) + } + + if x != x && x == x && x != x { + exit(1) + } + + if x != x && x != x && x == x { + exit(1) + } + + if x == x && x == x && x == x { + exit(0) + } + + exit(1) +} + +exit(x) { + syscall(60, x) +} \ No newline at end of file diff --git a/tests/programs/branch-combined.q b/tests/programs/branch-combined.q new file mode 100644 index 0000000..3c1f86e --- /dev/null +++ b/tests/programs/branch-combined.q @@ -0,0 +1,57 @@ +main() { + x := 0 + + if x == x && x != x || x != x && x != x { + exit(1) + } + + if x != x && x == x || x != x && x != x { + exit(1) + } + + if x != x && x != x || x == x && x != x { + exit(1) + } + + if x != x && x != x || x != x && x == x { + exit(1) + } + + if (x == x || x != x) && (x != x || x != x) { + exit(1) + } + + if (x != x || x == x) && (x != x || x != x) { + exit(1) + } + + if (x != x || x != x) && (x == x || x != x) { + exit(1) + } + + if (x != x || x != x) && (x != x || x == x) { + exit(1) + } + + if x != x || x != x || x != x || x == x || x != x { + if x + 1 == inc(x) && x - 1 == dec(x) && x == dec(inc(x)) { + if (x == x && x != x || x == x && x == x) && (x == x && x == x || x == x && x != x) { + exit(0) + } + } + } + + exit(1) +} + +exit(x) { + syscall(60, x) +} + +inc(x) { + return x + 1 +} + +dec(x) { + return x - 1 +} \ No newline at end of file diff --git a/tests/programs/branch-or.q b/tests/programs/branch-or.q new file mode 100644 index 0000000..da340a8 --- /dev/null +++ b/tests/programs/branch-or.q @@ -0,0 +1,29 @@ +main() { + x := 0 + + if x != x || x != x { + exit(1) + } + + if x != x || x != x || x != x { + exit(1) + } + + if x == x || x != x { + if x != x || x == x { + if x == x || x != x || x != x { + if x != x || x == x || x != x { + if x != x || x != x || x == x { + exit(0) + } + } + } + } + } + + exit(1) +} + +exit(x) { + syscall(60, x) +} \ No newline at end of file diff --git a/tests/programs/branch.q b/tests/programs/branch.q index 5a2830e..8887013 100644 --- a/tests/programs/branch.q +++ b/tests/programs/branch.q @@ -25,15 +25,15 @@ main() { exit(1) } - if x >= 1 || 1 <= x { + if x >= 1 { exit(1) } - if x == inc(x) || x == dec(x) { + if 1 <= x { exit(1) } - if inc(0) == x || dec(0) == x || inc(x) == dec(x) { + if x + 1 != x + 1 { exit(1) } @@ -41,7 +41,23 @@ main() { exit(1) } - if x + 1 != x + 1 { + if x - 1 != dec(x) { + exit(1) + } + + if inc(x) != x + 1 { + exit(1) + } + + if dec(x) != x - 1 { + exit(1) + } + + if x != inc(dec(x)) { + exit(1) + } + + if inc(dec(x)) != x { exit(1) } diff --git a/tests/programs_test.go b/tests/programs_test.go index feba797..dfd64e8 100644 --- a/tests/programs_test.go +++ b/tests/programs_test.go @@ -23,6 +23,10 @@ var programs = []struct { {"nested-calls.q", "", 4}, {"return.q", "", 6}, {"reassign.q", "", 2}, + {"branch.q", "", 0}, + {"branch-and.q", "", 0}, + {"branch-or.q", "", 0}, + {"branch-combined.q", "", 0}, } func TestPrograms(t *testing.T) {