diff --git a/examples/hello/hello.q b/examples/hello/hello.q index 7eddb30..9032441 100644 --- a/examples/hello/hello.q +++ b/examples/hello/hello.q @@ -11,7 +11,10 @@ hello() { address += 4194304 address += 1 - length = 0 + length = 0 + 1 + length -= length + length += write + stdout + length -= length length += 50 length -= 20 length *= 10 diff --git a/src/build/Assignment.go b/src/build/Assignment.go index 96ebd79..7ef8d06 100644 --- a/src/build/Assignment.go +++ b/src/build/Assignment.go @@ -1,9 +1,6 @@ package build import ( - "strconv" - - "git.akyoto.dev/cli/q/src/build/asm" "git.akyoto.dev/cli/q/src/build/expression" "git.akyoto.dev/cli/q/src/errors" ) @@ -11,31 +8,21 @@ import ( // CompileAssignment compiles an assignment. func (f *Function) CompileAssignment(expr *expression.Expression) error { name := expr.Children[0].Token.Text() - register := f.Variables[name].Register + variable, exists := f.Variables[name] - switch expr.Token.Text() { - case "=": - f.ExpressionToRegister(expr.Children[1], register) - - case "+=": - number, _ := strconv.Atoi(expr.Children[1].Token.Text()) - f.Assembler.RegisterNumber(asm.ADD, register, number) - - case "-=": - number, _ := strconv.Atoi(expr.Children[1].Token.Text()) - f.Assembler.RegisterNumber(asm.SUB, register, number) - - case "*=": - number, _ := strconv.Atoi(expr.Children[1].Token.Text()) - f.Assembler.RegisterNumber(asm.MUL, register, number) - - case "/=": - number, _ := strconv.Atoi(expr.Children[1].Token.Text()) - f.Assembler.RegisterNumber(asm.DIV, register, number) - - default: - return errors.New(&errors.InvalidInstruction{Instruction: expr.Token.Text()}, f.File, expr.Token.Position) + if !exists { + return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, expr.Children[0].Token.Position) } - return nil + 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 new file mode 100644 index 0000000..5daeb72 --- /dev/null +++ b/src/build/Calculate.go @@ -0,0 +1,81 @@ +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/Function.go b/src/build/Function.go index a7b0847..1a50f0a 100644 --- a/src/build/Function.go +++ b/src/build/Function.go @@ -122,60 +122,45 @@ func (f *Function) CompileInstruction(line token.List) error { // ExpressionToRegister moves the result of an expression into the given register. func (f *Function) ExpressionToRegister(root *expression.Expression, register cpu.Register) error { + operation := root.Token + if root.IsLeaf() { - return f.TokenToRegister(root.Token, register) + return f.TokenToRegister(operation, register) } left := root.Children[0] right := root.Children[1] - f.ExpressionToRegister(left, register) + err := f.ExpressionToRegister(left, register) - if right.IsLeaf() { - value := right.Token.Text() - n, err := strconv.Atoi(value) - - if err != nil { - return err - } - - switch root.Token.Text() { - case "+": - f.Assembler.RegisterNumber(asm.ADD, register, n) - - case "-": - f.Assembler.RegisterNumber(asm.SUB, register, n) - - case "*": - f.Assembler.RegisterNumber(asm.MUL, register, n) - - case "/": - f.Assembler.RegisterNumber(asm.DIV, register, n) - } - - return nil - } else { - temporary, _ := f.CPU.FindFree(f.CPU.General) - f.CPU.Use(temporary) - f.ExpressionToRegister(right, temporary) - f.CPU.Free(temporary) - - switch root.Token.Text() { - case "+": - f.Assembler.RegisterRegister(asm.ADD, register, temporary) - - case "-": - f.Assembler.RegisterRegister(asm.SUB, register, temporary) - - case "*": - f.Assembler.RegisterRegister(asm.MUL, register, temporary) - - case "/": - f.Assembler.RegisterRegister(asm.DIV, register, temporary) - } + if err != nil { + return err } - return errors.New(errors.NotImplemented, f.File, root.Token.Position) + 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. @@ -190,7 +175,7 @@ func (f *Function) TokenToRegister(t token.Token, register cpu.Register) error { return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, t.Position) } - f.Assembler.MoveRegisterRegister(register, variable.Register) + f.Assembler.RegisterRegister(asm.MOVE, register, variable.Register) return nil case token.Number: diff --git a/src/build/VariableDefinition.go b/src/build/VariableDefinition.go index 29a7899..270b8e1 100644 --- a/src/build/VariableDefinition.go +++ b/src/build/VariableDefinition.go @@ -2,7 +2,6 @@ package build import ( "git.akyoto.dev/cli/q/src/build/expression" - "git.akyoto.dev/cli/q/src/build/token" "git.akyoto.dev/cli/q/src/errors" ) @@ -18,27 +17,18 @@ func (f *Function) CompileVariableDefinition(expr *expression.Expression) error return errors.New(&errors.VariableAlreadyExists{Name: name}, f.File, expr.Children[0].Token.Position) } - value := expr.Children[1] - - err := value.EachLeaf(func(leaf *expression.Expression) error { - if leaf.Token.Kind == token.Identifier && !f.identifierExists(leaf.Token.Text()) { - return errors.New(&errors.UnknownIdentifier{Name: leaf.Token.Text()}, f.File, leaf.Token.Position) - } - - return nil - }) - - if err != nil { - return err - } - reg, exists := f.CPU.FindFree(f.CPU.General) if !exists { panic("no free registers") } - f.ExpressionToRegister(value, reg) + err := f.ExpressionToRegister(expr.Children[1], reg) + + if err != nil { + return err + } + f.CPU.Use(reg) f.Variables[name] = &Variable{ diff --git a/src/build/asm/Instructions.go b/src/build/asm/Instructions.go index 92842e2..04de2ea 100644 --- a/src/build/asm/Instructions.go +++ b/src/build/asm/Instructions.go @@ -24,17 +24,6 @@ func (a *Assembler) RegisterRegister(mnemonic Mnemonic, left cpu.Register, right }) } -// MoveRegisterRegister moves a register value into another register. -func (a *Assembler) MoveRegisterRegister(destination cpu.Register, source cpu.Register) { - a.Instructions = append(a.Instructions, Instruction{ - Mnemonic: MOVE, - Data: &RegisterRegister{ - Destination: destination, - Source: source, - }, - }) -} - // Label adds a label at the current position. func (a *Assembler) Label(name string) { a.Instructions = append(a.Instructions, Instruction{ diff --git a/src/build/asm/RegisterNumber.go b/src/build/asm/RegisterNumber.go index 7e4ba23..ff5d616 100644 --- a/src/build/asm/RegisterNumber.go +++ b/src/build/asm/RegisterNumber.go @@ -14,5 +14,5 @@ type RegisterNumber struct { // String returns a human readable version. func (data *RegisterNumber) String() string { - return fmt.Sprintf("%s, %Xₕ", data.Register, data.Number) + return fmt.Sprintf("%s, %d", data.Register, data.Number) }