From c088697446fe529817a7eb46444e9163416919e8 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Sat, 22 Feb 2025 12:54:23 +0100 Subject: [PATCH] Fixed incorrect division results --- examples/prime/prime.q | 2 +- src/core/CompileAssign.go | 2 +- src/core/CompileAssignDivision.go | 62 ++++++++++++----- src/core/CompileDefinition.go | 5 ++ src/core/ExecuteRegisterNumber.go | 5 +- src/core/ExecuteRegisterRegister.go | 5 +- tests/programs/div-split.q | 8 +-- tests/programs/op-assign.q | 9 --- tests/programs/operator-assign.q | 30 ++++++++ tests/programs/variables.q | 14 ++-- tests/programs_test.go | 102 ++++++++++++++-------------- 11 files changed, 150 insertions(+), 94 deletions(-) delete mode 100644 tests/programs/op-assign.q create mode 100644 tests/programs/operator-assign.q diff --git a/examples/prime/prime.q b/examples/prime/prime.q index e417cd8..fdd1bcf 100644 --- a/examples/prime/prime.q +++ b/examples/prime/prime.q @@ -22,7 +22,7 @@ main() { } } -isPrime(x int) -> int { +isPrime(x int) -> bool { if x == 2 { return 1 } diff --git a/src/core/CompileAssign.go b/src/core/CompileAssign.go index 6325e21..bbc237b 100644 --- a/src/core/CompileAssign.go +++ b/src/core/CompileAssign.go @@ -35,7 +35,7 @@ func (f *Function) CompileAssign(node *ast.Assign) error { } if left.Token.Kind == token.Separator && right.Token.Kind == token.Div { - return f.CompileAssignDivision(node) + return f.CompileAssignDivision(node.Expression) } if !ast.IsFunctionCall(right) { diff --git a/src/core/CompileAssignDivision.go b/src/core/CompileAssignDivision.go index c427b04..c93d90b 100644 --- a/src/core/CompileAssignDivision.go +++ b/src/core/CompileAssignDivision.go @@ -2,30 +2,60 @@ package core import ( "git.akyoto.dev/cli/q/src/asm" - "git.akyoto.dev/cli/q/src/ast" "git.akyoto.dev/cli/q/src/errors" + "git.akyoto.dev/cli/q/src/expression" + "git.akyoto.dev/cli/q/src/scope" + "git.akyoto.dev/cli/q/src/token" + "git.akyoto.dev/cli/q/src/types" "git.akyoto.dev/cli/q/src/x86" ) // CompileAssignDivision compiles an assign statement that has quotient and remainder on the left side and division on the right. -func (f *Function) CompileAssignDivision(node *ast.Assign) error { - left := node.Expression.Children[0] - right := node.Expression.Children[1] +func (f *Function) CompileAssignDivision(expr *expression.Expression) error { + var ( + left = expr.Children[0] + right = expr.Children[1] + quotientVariable *scope.Variable + remainderVariable *scope.Variable + err error + ) - quotient := left.Children[0] - name := quotient.Token.Text(f.File.Bytes) - quotientVariable := f.VariableByName(name) + if expr.Token.Kind == token.Define { + quotientVariable, err = f.Define(left.Children[0]) - if quotientVariable == nil { - return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, quotient.Token.Position) - } + if err != nil { + return err + } - remainder := left.Children[1] - name = remainder.Token.Text(f.File.Bytes) - remainderVariable := f.VariableByName(name) + remainderVariable, err = f.Define(left.Children[1]) - if remainderVariable == nil { - return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, remainder.Token.Position) + if err != nil { + return err + } + + quotientVariable.Type = types.Int + remainderVariable.Type = types.Int + f.AddVariable(quotientVariable) + f.AddVariable(remainderVariable) + } else { + quotient := left.Children[0] + name := quotient.Token.Text(f.File.Bytes) + quotientVariable = f.VariableByName(name) + + if quotientVariable == nil { + return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, quotient.Token.Position) + } + + remainder := left.Children[1] + name = remainder.Token.Text(f.File.Bytes) + remainderVariable = f.VariableByName(name) + + if remainderVariable == nil { + return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, remainder.Token.Position) + } + + defer f.UseVariable(quotientVariable) + defer f.UseVariable(remainderVariable) } dividend := right.Children[0] @@ -38,9 +68,7 @@ func (f *Function) CompileAssignDivision(node *ast.Assign) error { divisor := right.Children[1] err = f.Execute(right.Token, dividendRegister, divisor) f.RegisterRegister(asm.MOVE, quotientVariable.Register, x86.RAX) - f.UseVariable(quotientVariable) f.RegisterRegister(asm.MOVE, remainderVariable.Register, x86.RDX) - f.UseVariable(remainderVariable) if isTemporary { f.FreeRegister(dividendRegister) diff --git a/src/core/CompileDefinition.go b/src/core/CompileDefinition.go index 1143315..6b46991 100644 --- a/src/core/CompileDefinition.go +++ b/src/core/CompileDefinition.go @@ -5,6 +5,7 @@ import ( "git.akyoto.dev/cli/q/src/ast" "git.akyoto.dev/cli/q/src/errors" "git.akyoto.dev/cli/q/src/expression" + "git.akyoto.dev/cli/q/src/token" ) // CompileDefinition compiles a variable definition. @@ -34,6 +35,10 @@ func (f *Function) CompileDefinition(node *ast.Define) error { return nil } + if left.Token.Kind == token.Separator && right.Token.Kind == token.Div { + return f.CompileAssignDivision(node.Expression) + } + if !ast.IsFunctionCall(right) { return errors.New(errors.NotImplemented, f.File, node.Expression.Token.Position) } diff --git a/src/core/ExecuteRegisterNumber.go b/src/core/ExecuteRegisterNumber.go index f26f1a3..77a6ffb 100644 --- a/src/core/ExecuteRegisterNumber.go +++ b/src/core/ExecuteRegisterNumber.go @@ -25,7 +25,10 @@ func (f *Function) ExecuteRegisterNumber(operation token.Token, register cpu.Reg f.RegisterNumber(asm.MUL, register, number) case token.Div, token.DivAssign: - f.SaveRegister(x86.RAX) + if register != x86.RAX { + f.SaveRegister(x86.RAX) + } + f.SaveRegister(x86.RDX) tmp := f.NewRegister() f.RegisterNumber(asm.MOVE, tmp, number) diff --git a/src/core/ExecuteRegisterRegister.go b/src/core/ExecuteRegisterRegister.go index ebb6811..fc785b7 100644 --- a/src/core/ExecuteRegisterRegister.go +++ b/src/core/ExecuteRegisterRegister.go @@ -25,7 +25,10 @@ func (f *Function) ExecuteRegisterRegister(operation token.Token, register cpu.R f.RegisterRegister(asm.MUL, register, operand) case token.Div, token.DivAssign: - f.SaveRegister(x86.RAX) + if register != x86.RAX { + f.SaveRegister(x86.RAX) + } + f.SaveRegister(x86.RDX) f.RegisterRegister(asm.DIV, register, operand) diff --git a/tests/programs/div-split.q b/tests/programs/div-split.q index e44e409..847950a 100644 --- a/tests/programs/div-split.q +++ b/tests/programs/div-split.q @@ -1,13 +1,11 @@ main() { - quotient := 0 - remainder := 0 dividend := 256 divisor := 100 - quotient, remainder = 256 / 100 + quotient, remainder := 256 / 100 assert quotient == 2 assert remainder == 56 - + quotient, remainder = dividend / 100 assert quotient == 2 assert remainder == 56 @@ -15,7 +13,7 @@ main() { quotient, remainder = 256 / divisor assert quotient == 2 assert remainder == 56 - + quotient, remainder = dividend / divisor assert quotient == 2 assert remainder == 56 diff --git a/tests/programs/op-assign.q b/tests/programs/op-assign.q deleted file mode 100644 index 85dd9c4..0000000 --- a/tests/programs/op-assign.q +++ /dev/null @@ -1,9 +0,0 @@ -main() { - f(10) -} - -f(new int) { - old := new - new -= 1 - assert new != old -} \ No newline at end of file diff --git a/tests/programs/operator-assign.q b/tests/programs/operator-assign.q new file mode 100644 index 0000000..36809f2 --- /dev/null +++ b/tests/programs/operator-assign.q @@ -0,0 +1,30 @@ +main() { + number(10) + register(10) +} + +number(x int) { + y := x + x -= 1 + assert x < y + x += 1 + assert x == y + x *= 2 + assert x > y + x /= 2 + assert x == y +} + +register(x int) { + y := x + num := 1 + x -= num + assert x < y + x += num + assert x == y + num = 2 + x *= num + assert x > y + x /= num + assert x == y +} \ No newline at end of file diff --git a/tests/programs/variables.q b/tests/programs/variables.q index 8334626..554f438 100644 --- a/tests/programs/variables.q +++ b/tests/programs/variables.q @@ -7,11 +7,11 @@ main() { f := 6 g := 7 - assert a != 0 - assert b != 0 - assert c != 0 - assert d != 0 - assert e != 0 - assert f != 0 - assert g != 0 + assert a == 1 + assert b == 2 + assert c == 3 + assert d == 4 + assert e == 5 + assert f == 6 + assert g == 7 } \ No newline at end of file diff --git a/tests/programs_test.go b/tests/programs_test.go index 3097960..da5dfc8 100644 --- a/tests/programs_test.go +++ b/tests/programs_test.go @@ -14,58 +14,56 @@ import ( var programs = []struct { Name string - Input string - Output string ExitCode int }{ - {"empty", "", "", 0}, - {"assert", "", "", 1}, - {"variables", "", "", 0}, - {"reassign", "", "", 0}, - {"reuse", "", "", 0}, - {"return", "", "", 0}, - {"return-multi", "", "", 0}, - {"math", "", "", 0}, - {"precedence", "", "", 0}, - {"op-assign", "", "", 0}, - {"binary", "", "", 0}, - {"octal", "", "", 0}, - {"hexadecimal", "", "", 0}, - {"const", "", "", 0}, - {"escape-rune", "", "", 0}, - {"escape-string", "", "", 0}, - {"bitwise-and", "", "", 0}, - {"bitwise-or", "", "", 0}, - {"bitwise-xor", "", "", 0}, - {"shift", "", "", 0}, - {"modulo", "", "", 0}, - {"modulo-assign", "", "", 0}, - {"div-split", "", "", 0}, - {"int64", "", "", 0}, - {"negative", "", "", 0}, - {"negation", "", "", 0}, - {"square-sum", "", "", 0}, - {"chained-calls", "", "", 0}, - {"nested-calls", "", "", 0}, - {"param", "", "", 0}, - {"param-multi", "", "", 0}, - {"param-order", "", "", 0}, - {"branch", "", "", 0}, - {"branch-and", "", "", 0}, - {"branch-or", "", "", 0}, - {"branch-both", "", "", 0}, - {"branch-save", "", "", 0}, - {"jump-near", "", "", 0}, - {"switch", "", "", 0}, - {"loop-infinite", "", "", 0}, - {"loop-lifetime", "", "", 0}, - {"loop-for", "", "", 0}, - {"memory-free", "", "", 0}, - {"out-of-memory", "", "", 0}, - {"index-static", "", "", 0}, - {"index-dynamic", "", "", 0}, - {"struct", "", "", 0}, - {"len", "", "", 0}, + {"empty", 0}, + {"assert", 1}, + {"binary", 0}, + {"octal", 0}, + {"hexadecimal", 0}, + {"variables", 0}, + {"reassign", 0}, + {"reuse", 0}, + {"return", 0}, + {"return-multi", 0}, + {"math", 0}, + {"precedence", 0}, + {"operator-assign", 0}, + {"const", 0}, + {"escape-rune", 0}, + {"escape-string", 0}, + {"bitwise-and", 0}, + {"bitwise-or", 0}, + {"bitwise-xor", 0}, + {"shift", 0}, + {"modulo", 0}, + {"modulo-assign", 0}, + {"div-split", 0}, + {"int64", 0}, + {"negative", 0}, + {"negation", 0}, + {"square-sum", 0}, + {"chained-calls", 0}, + {"nested-calls", 0}, + {"param", 0}, + {"param-multi", 0}, + {"param-order", 0}, + {"branch", 0}, + {"branch-and", 0}, + {"branch-or", 0}, + {"branch-both", 0}, + {"branch-save", 0}, + {"jump-near", 0}, + {"switch", 0}, + {"loop-infinite", 0}, + {"loop-lifetime", 0}, + {"loop-for", 0}, + {"memory-free", 0}, + {"out-of-memory", 0}, + {"index-static", 0}, + {"index-dynamic", 0}, + {"struct", 0}, + {"len", 0}, } func TestPrograms(t *testing.T) { @@ -74,12 +72,12 @@ func TestPrograms(t *testing.T) { t.Run(test.Name+"/debug", func(t *testing.T) { config.Optimize(false) - run(t, file, "debug", test.Input, test.Output, test.ExitCode) + run(t, file, "debug", "", "", test.ExitCode) }) t.Run(test.Name+"/release", func(t *testing.T) { config.Optimize(true) - run(t, file, "release", test.Input, test.Output, test.ExitCode) + run(t, file, "release", "", "", test.ExitCode) }) } }