Implemented complex expressions

This commit is contained in:
Eduard Urbach 2024-06-26 22:51:14 +02:00
parent 988a538661
commit d1a3ffb1a5
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
7 changed files with 137 additions and 102 deletions

View File

@ -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

View File

@ -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])
}

81
src/build/Calculate.go Normal file
View File

@ -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
}

View File

@ -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)
if right.IsLeaf() {
value := right.Token.Text()
n, err := strconv.Atoi(value)
err := f.ExpressionToRegister(left, register)
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 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")
}
return nil
} else {
temporary, _ := f.CPU.FindFree(f.CPU.General)
f.CPU.Use(temporary)
f.ExpressionToRegister(right, temporary)
f.CPU.Free(temporary)
defer f.CPU.Free(temporary)
err := f.ExpressionToRegister(value, 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.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:

View File

@ -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{

View File

@ -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{

View File

@ -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)
}