diff --git a/errors_test.go b/errors_test.go index efd2ae7..8eecad6 100644 --- a/errors_test.go +++ b/errors_test.go @@ -21,12 +21,13 @@ func TestErrors(t *testing.T) { {"InvalidInstructionIdentifier.q", &errors.InvalidInstruction{Instruction: "abc"}}, {"InvalidInstructionNumber.q", &errors.InvalidInstruction{Instruction: "123"}}, {"InvalidExpression.q", errors.InvalidExpression}, + {"InvalidOperator.q", &errors.InvalidOperator{Operator: "+++"}}, {"MissingAssignValue.q", errors.MissingAssignValue}, {"MissingBlockEnd.q", errors.MissingBlockEnd}, {"MissingBlockStart.q", errors.MissingBlockStart}, {"MissingGroupEnd.q", errors.MissingGroupEnd}, {"MissingGroupStart.q", errors.MissingGroupStart}, - {"VariableAlreadyExists.q", &errors.VariableAlreadyExists{Name: "a"}}, + {"VariableAlreadyExists.q", &errors.VariableAlreadyExists{Name: "x"}}, {"UnknownIdentifier.q", &errors.UnknownIdentifier{Name: "x"}}, {"UnknownIdentifier2.q", &errors.UnknownIdentifier{Name: "x"}}, } diff --git a/src/build/Assignment.go b/src/build/Assignment.go index 7ef8d06..f2618f6 100644 --- a/src/build/Assignment.go +++ b/src/build/Assignment.go @@ -14,15 +14,5 @@ func (f *Function) CompileAssignment(expr *expression.Expression) error { return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, expr.Children[0].Token.Position) } - if expr.Token.Text() == "=" { - return f.ExpressionToRegister(expr.Children[1], variable.Register) - } - - right := expr.Children[1] - - if right.IsLeaf() { - return f.Calculate(variable.Register, expr.Token, right.Token) - } - return f.Execute(expr.Token, variable.Register, expr.Children[1]) } diff --git a/src/build/Calculate.go b/src/build/Calculate.go deleted file mode 100644 index 5daeb72..0000000 --- a/src/build/Calculate.go +++ /dev/null @@ -1,81 +0,0 @@ -package build - -import ( - "strconv" - - "git.akyoto.dev/cli/q/src/build/asm" - "git.akyoto.dev/cli/q/src/build/cpu" - "git.akyoto.dev/cli/q/src/build/token" - "git.akyoto.dev/cli/q/src/errors" -) - -// Calculate performs an operation on a register with the given operand. -func (f *Function) Calculate(register cpu.Register, operation token.Token, operand token.Token) error { - switch operand.Kind { - case token.Number: - value := operand.Text() - number, err := strconv.Atoi(value) - - if err != nil { - return err - } - - return f.CalculateRegisterNumber(operation, register, number) - - case token.Identifier: - name := operand.Text() - variable, exists := f.Variables[name] - - if !exists { - return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, operand.Position) - } - - return f.CalculateRegisterRegister(operation, register, variable.Register) - } - - return errors.New(errors.NotImplemented, f.File, operation.Position) -} - -// CalculateRegisterNumber performs an operation on a register and a number. -func (f *Function) CalculateRegisterNumber(operation token.Token, register cpu.Register, number int) error { - switch operation.Text() { - case "+", "+=": - f.Assembler.RegisterNumber(asm.ADD, register, number) - - case "-", "-=": - f.Assembler.RegisterNumber(asm.SUB, register, number) - - case "*", "*=": - f.Assembler.RegisterNumber(asm.MUL, register, number) - - case "/", "/=": - f.Assembler.RegisterNumber(asm.DIV, register, number) - - default: - return errors.New(errors.NotImplemented, f.File, operation.Position) - } - - return nil -} - -// CalculateRegisterRegister performs an operation on two registers. -func (f *Function) CalculateRegisterRegister(operation token.Token, destination cpu.Register, source cpu.Register) error { - switch operation.Text() { - case "+", "+=": - f.Assembler.RegisterRegister(asm.ADD, destination, source) - - case "-", "-=": - f.Assembler.RegisterRegister(asm.SUB, destination, source) - - case "*", "*=": - f.Assembler.RegisterRegister(asm.MUL, destination, source) - - case "/", "/=": - f.Assembler.RegisterRegister(asm.DIV, destination, source) - - default: - return errors.New(errors.NotImplemented, f.File, operation.Position) - } - - return nil -} diff --git a/src/build/Execute.go b/src/build/Execute.go new file mode 100644 index 0000000..1f78ae0 --- /dev/null +++ b/src/build/Execute.go @@ -0,0 +1,111 @@ +package build + +import ( + "strconv" + + "git.akyoto.dev/cli/q/src/build/asm" + "git.akyoto.dev/cli/q/src/build/cpu" + "git.akyoto.dev/cli/q/src/build/expression" + "git.akyoto.dev/cli/q/src/build/token" + "git.akyoto.dev/cli/q/src/errors" +) + +// Execute executes an operation on a register with a value operand. +func (f *Function) Execute(operation token.Token, register cpu.Register, value *expression.Expression) error { + if value.IsLeaf() { + return f.ExecuteLeaf(operation, register, value.Token) + } + + temporary, found := f.CPU.FindFree(f.CPU.General) + + if !found { + panic("no free registers") + } + + f.CPU.Use(temporary) + defer f.CPU.Free(temporary) + err := f.ExpressionToRegister(value, temporary) + + if err != nil { + return err + } + + return f.ExecuteRegisterRegister(operation, register, temporary) +} + +// ExecuteLeaf performs an operation on a register with the given leaf operand. +func (f *Function) ExecuteLeaf(operation token.Token, register cpu.Register, operand token.Token) error { + switch operand.Kind { + case token.Number: + value := operand.Text() + number, err := strconv.Atoi(value) + + if err != nil { + return err + } + + return f.ExecuteRegisterNumber(operation, register, number) + + case token.Identifier: + name := operand.Text() + variable, exists := f.Variables[name] + + if !exists { + return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, operand.Position) + } + + return f.ExecuteRegisterRegister(operation, register, variable.Register) + } + + return errors.New(errors.NotImplemented, f.File, operation.Position) +} + +// ExecuteRegisterNumber performs an operation on a register and a number. +func (f *Function) ExecuteRegisterNumber(operation token.Token, register cpu.Register, number int) error { + switch operation.Text() { + case "+", "+=": + f.Assembler.RegisterNumber(asm.ADD, register, number) + + case "-", "-=": + f.Assembler.RegisterNumber(asm.SUB, register, number) + + case "*", "*=": + f.Assembler.RegisterNumber(asm.MUL, register, number) + + case "/", "/=": + f.Assembler.RegisterNumber(asm.DIV, register, number) + + case "=": + f.Assembler.RegisterNumber(asm.MOVE, register, number) + + default: + return errors.New(&errors.InvalidOperator{Operator: operation.Text()}, f.File, operation.Position) + } + + return nil +} + +// ExecuteRegisterRegister performs an operation on two registers. +func (f *Function) ExecuteRegisterRegister(operation token.Token, destination cpu.Register, source cpu.Register) error { + switch operation.Text() { + case "+", "+=": + f.Assembler.RegisterRegister(asm.ADD, destination, source) + + case "-", "-=": + f.Assembler.RegisterRegister(asm.SUB, destination, source) + + case "*", "*=": + f.Assembler.RegisterRegister(asm.MUL, destination, source) + + case "/", "/=": + f.Assembler.RegisterRegister(asm.DIV, destination, source) + + case "=": + f.Assembler.RegisterRegister(asm.MOVE, destination, source) + + default: + return errors.New(&errors.InvalidOperator{Operator: operation.Text()}, f.File, operation.Position) + } + + return nil +} diff --git a/src/build/Function.go b/src/build/Function.go index 1a50f0a..9d90c42 100644 --- a/src/build/Function.go +++ b/src/build/Function.go @@ -140,29 +140,6 @@ func (f *Function) ExpressionToRegister(root *expression.Expression, register cp return f.Execute(operation, register, right) } -// Execute executes an operation on a register with a value operand. -func (f *Function) Execute(operation token.Token, register cpu.Register, value *expression.Expression) error { - if value.IsLeaf() { - return f.Calculate(register, operation, value.Token) - } - - temporary, found := f.CPU.FindFree(f.CPU.General) - - if !found { - panic("no free registers") - } - - f.CPU.Use(temporary) - defer f.CPU.Free(temporary) - err := f.ExpressionToRegister(value, temporary) - - if err != nil { - return err - } - - return f.CalculateRegisterRegister(operation, register, temporary) -} - // TokenToRegister moves a token into a register. // It only works with identifiers, numbers and strings. func (f *Function) TokenToRegister(t token.Token, register cpu.Register) error { diff --git a/src/build/expression/Operator.go b/src/build/expression/Operator.go index a1823e1..391ed74 100644 --- a/src/build/expression/Operator.go +++ b/src/build/expression/Operator.go @@ -1,6 +1,10 @@ package expression -import "git.akyoto.dev/cli/q/src/build/token" +import ( + "math" + + "git.akyoto.dev/cli/q/src/build/token" +) // Operator represents an operator for mathematical expressions. type Operator struct { @@ -12,35 +16,35 @@ type Operator struct { // Operators defines the operators used in the language. // The number corresponds to the operator priority and can not be zero. var Operators = map[string]*Operator{ - ".": {".", 14, 2}, - "λ": {"λ", 13, 1}, - "!": {"!", 12, 1}, - "*": {"*", 11, 2}, - "/": {"/", 11, 2}, - "%": {"%", 11, 2}, - "+": {"+", 10, 2}, - "-": {"-", 10, 2}, - ">>": {">>", 9, 2}, - "<<": {"<<", 9, 2}, - ">": {">", 8, 2}, - "<": {"<", 8, 2}, - ">=": {">=", 8, 2}, - "<=": {"<=", 8, 2}, - "==": {"==", 7, 2}, - "!=": {"!=", 7, 2}, - "&": {"&", 6, 2}, - "^": {"^", 5, 2}, - "|": {"|", 4, 2}, - "&&": {"&&", 3, 2}, - "||": {"||", 2, 2}, - "=": {"=", 1, 2}, - ":=": {":=", 1, 2}, - "+=": {"+=", 1, 2}, - "-=": {"-=", 1, 2}, - "*=": {"*=", 1, 2}, - "/=": {"/=", 1, 2}, - ">>=": {">>=", 1, 2}, - "<<=": {"<<=", 1, 2}, + ".": {".", 13, 2}, + "λ": {"λ", 12, 1}, + "!": {"!", 11, 1}, + "*": {"*", 10, 2}, + "/": {"/", 10, 2}, + "%": {"%", 10, 2}, + "+": {"+", 9, 2}, + "-": {"-", 9, 2}, + ">>": {">>", 8, 2}, + "<<": {"<<", 8, 2}, + ">": {">", 7, 2}, + "<": {"<", 7, 2}, + ">=": {">=", 7, 2}, + "<=": {"<=", 7, 2}, + "==": {"==", 6, 2}, + "!=": {"!=", 6, 2}, + "&": {"&", 5, 2}, + "^": {"^", 4, 2}, + "|": {"|", 3, 2}, + "&&": {"&&", 2, 2}, + "||": {"||", 1, 2}, + "=": {"=", math.MinInt, 2}, + ":=": {":=", math.MinInt, 2}, + "+=": {"+=", math.MinInt, 2}, + "-=": {"-=", math.MinInt, 2}, + "*=": {"*=", math.MinInt, 2}, + "/=": {"/=", math.MinInt, 2}, + ">>=": {">>=", math.MinInt, 2}, + "<<=": {"<<=", math.MinInt, 2}, } func isComplete(expr *Expression) bool { @@ -60,9 +64,21 @@ func isComplete(expr *Expression) bool { } func numOperands(symbol string) int { - return Operators[symbol].Operands + operator, exists := Operators[symbol] + + if !exists { + return -1 + } + + return operator.Operands } func precedence(symbol string) int { - return Operators[symbol].Precedence + operator, exists := Operators[symbol] + + if !exists { + return -1 + } + + return operator.Precedence } diff --git a/src/errors/InvalidOperator.go b/src/errors/InvalidOperator.go new file mode 100644 index 0000000..b3748ce --- /dev/null +++ b/src/errors/InvalidOperator.go @@ -0,0 +1,13 @@ +package errors + +import "fmt" + +// InvalidOperator error is created when an operator is not valid. +type InvalidOperator struct { + Operator string +} + +// Error generates the string representation. +func (err *InvalidOperator) Error() string { + return fmt.Sprintf("Invalid operator '%s'", err.Operator) +} diff --git a/tests/errors/InvalidOperator.q b/tests/errors/InvalidOperator.q new file mode 100644 index 0000000..7b5d4d2 --- /dev/null +++ b/tests/errors/InvalidOperator.q @@ -0,0 +1,3 @@ +main() { + x := 123 +++ 456 +} \ No newline at end of file diff --git a/tests/errors/MissingAssignValue.q b/tests/errors/MissingAssignValue.q index ae9e1eb..4eefb33 100644 --- a/tests/errors/MissingAssignValue.q +++ b/tests/errors/MissingAssignValue.q @@ -1,3 +1,3 @@ main() { - a := + x := } \ No newline at end of file diff --git a/tests/errors/VariableAlreadyExists.q b/tests/errors/VariableAlreadyExists.q index e55bbe6..a179f22 100644 --- a/tests/errors/VariableAlreadyExists.q +++ b/tests/errors/VariableAlreadyExists.q @@ -1,4 +1,4 @@ main() { - a := 1 - a := 2 + x := 1 + x := 2 } \ No newline at end of file