diff --git a/errors_test.go b/errors_test.go index c38a382..38e1cdb 100644 --- a/errors_test.go +++ b/errors_test.go @@ -20,12 +20,14 @@ func TestErrors(t *testing.T) { {"ExpectedFunctionParameters.q", errors.ExpectedFunctionParameters}, {"InvalidInstructionIdentifier.q", &errors.InvalidInstruction{Instruction: "abc"}}, {"InvalidInstructionNumber.q", &errors.InvalidInstruction{Instruction: "123"}}, + {"InvalidExpression.q", errors.InvalidExpression}, {"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"}}, + {"UnknownIdentifier.q", &errors.UnknownIdentifier{Name: "x"}}, } for _, test := range tests { diff --git a/src/build/Function.go b/src/build/Function.go index 1c2dc0c..671c4e0 100644 --- a/src/build/Function.go +++ b/src/build/Function.go @@ -156,29 +156,10 @@ func (f *Function) CompileFunctionCall(expr *expression.Expression) error { parameters := expr.Children[1:] for i, parameter := range parameters { - switch parameter.Token.Kind { - case token.Identifier: - name := parameter.Token.Text() - variable, exists := f.Variables[name] + err := f.ExpressionToRegister(parameter, f.CPU.Syscall[i]) - if !exists { - panic("Unknown identifier " + name) - } - - if !variable.IsConst { - panic("Not implemented yet") - } - - n, _ := strconv.Atoi(variable.Value.Token.Text()) - f.Assembler.MoveRegisterNumber(f.CPU.Syscall[i], uint64(n)) - - case token.Number: - value := parameter.Token.Text() - n, _ := strconv.Atoi(value) - f.Assembler.MoveRegisterNumber(f.CPU.Syscall[i], uint64(n)) - - default: - panic("Unknown expression") + if err != nil { + return err } } @@ -191,6 +172,59 @@ func (f *Function) CompileFunctionCall(expr *expression.Expression) error { return nil } +// ExpressionToRegister moves the result of an expression into the given register. +func (f *Function) ExpressionToRegister(root *expression.Expression, register cpu.Register) error { + if root.IsLeaf() { + return f.TokenToRegister(root.Token, register) + } + + return errors.New(errors.NotImplemented, f.File, root.Token.Position) +} + +// 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 { + switch t.Kind { + case token.Identifier: + name := t.Text() + variable, exists := f.Variables[name] + + if !exists { + return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, t.Position) + } + + if !variable.IsConst { + return errors.New(errors.NotImplemented, f.File, t.Position) + } + + n, err := strconv.Atoi(variable.Value.Token.Text()) + + if err != nil { + return err + } + + f.Assembler.MoveRegisterNumber(register, uint64(n)) + return nil + + case token.Number: + value := t.Text() + n, err := strconv.Atoi(value) + + if err != nil { + return err + } + + f.Assembler.MoveRegisterNumber(register, uint64(n)) + return nil + + case token.String: + return errors.New(errors.NotImplemented, f.File, t.Position) + + default: + return errors.New(errors.InvalidExpression, f.File, t.Position) + } +} + // PrintAsm shows the assembly instructions. func (f *Function) PrintAsm() { ansi.Bold.Println(f.Name) diff --git a/src/errors/CompileErrors.go b/src/errors/CompileErrors.go index 9f43de2..34515d0 100644 --- a/src/errors/CompileErrors.go +++ b/src/errors/CompileErrors.go @@ -2,5 +2,7 @@ package errors var ( InvalidStatement = &Base{"Invalid statement"} + InvalidExpression = &Base{"Invalid expression"} MissingAssignValue = &Base{"Missing assignment value"} + NotImplemented = &Base{"Not implemented"} ) diff --git a/src/errors/UnknownIdentifier.go b/src/errors/UnknownIdentifier.go new file mode 100644 index 0000000..d6f8dc8 --- /dev/null +++ b/src/errors/UnknownIdentifier.go @@ -0,0 +1,18 @@ +package errors + +import "fmt" + +// UnknownIdentifier represents unknown variables. +type UnknownIdentifier struct { + Name string + CorrectName string +} + +// Error generates the string representation. +func (err *UnknownIdentifier) Error() string { + if err.CorrectName != "" { + return fmt.Sprintf("Unknown variable '%s', did you mean '%s'?", err.Name, err.CorrectName) + } + + return fmt.Sprintf("Unknown variable '%s'", err.Name) +} diff --git a/tests/errors/InvalidExpression.q b/tests/errors/InvalidExpression.q new file mode 100644 index 0000000..77e99f8 --- /dev/null +++ b/tests/errors/InvalidExpression.q @@ -0,0 +1,3 @@ +main() { + syscall(+, -, *, /) +} \ No newline at end of file diff --git a/tests/errors/UnknownIdentifier.q b/tests/errors/UnknownIdentifier.q new file mode 100644 index 0000000..9b13514 --- /dev/null +++ b/tests/errors/UnknownIdentifier.q @@ -0,0 +1,3 @@ +main() { + syscall(x) +} \ No newline at end of file