diff --git a/src/core/ArrayElementToRegister.go b/src/core/ArrayElementToRegister.go deleted file mode 100644 index 8274a97..0000000 --- a/src/core/ArrayElementToRegister.go +++ /dev/null @@ -1,75 +0,0 @@ -package core - -import ( - "math" - - "git.urbach.dev/cli/q/src/asm" - "git.urbach.dev/cli/q/src/cpu" - "git.urbach.dev/cli/q/src/errors" - "git.urbach.dev/cli/q/src/expression" - "git.urbach.dev/cli/q/src/token" - "git.urbach.dev/cli/q/src/types" -) - -// ArrayElementToRegister moves the value of an array element into the given register. -func (f *Function) ArrayElementToRegister(node *expression.Expression, register cpu.Register) (types.Type, error) { - name := node.Children[0].Token.Text(f.File.Bytes) - array := f.VariableByName(name) - - if array == nil { - return nil, errors.New(&errors.UnknownIdentifier{Name: name}, f.File, node.Children[0].Token.Position) - } - - defer f.UseVariable(array) - index := node.Children[1] - - memory := asm.Memory{ - Base: array.Value.Register, - Offset: 0, - OffsetRegister: math.MaxUint8, - Length: byte(1), - } - - switch { - case index.Token.IsNumeric(): - offset, err := f.ToNumber(index.Token) - - if err != nil { - return nil, err - } - - memory.Offset = int8(offset) - - case index.Token.Kind == token.Identifier: - indexName := index.Token.Text(f.File.Bytes) - indexVariable := f.VariableByName(indexName) - - if indexVariable == nil { - return nil, errors.New(&errors.UnknownIdentifier{Name: indexName}, f.File, index.Token.Position) - } - - defer f.UseVariable(indexVariable) - - if !types.Is(indexVariable.Value.Typ, types.AnyInt) { - return nil, errors.New(&errors.TypeMismatch{Encountered: indexVariable.Value.Typ.Name(), Expected: types.AnyInt.Name()}, f.File, index.Token.Position) - } - - memory.OffsetRegister = indexVariable.Value.Register - - default: - typ, err := f.ExpressionToRegister(index, register) - - if err != nil { - return nil, err - } - - if !types.Is(typ, types.AnyInt) { - return nil, errors.New(&errors.TypeMismatch{Encountered: typ.Name(), Expected: types.AnyInt.Name()}, f.File, index.Token.Position) - } - - memory.OffsetRegister = register - } - - f.MemoryRegister(asm.LOAD, memory, register) - return types.Int, nil -} diff --git a/src/core/CallToRegister.go b/src/core/CallToRegister.go deleted file mode 100644 index 9137790..0000000 --- a/src/core/CallToRegister.go +++ /dev/null @@ -1,27 +0,0 @@ -package core - -import ( - "git.urbach.dev/cli/q/src/asm" - "git.urbach.dev/cli/q/src/cpu" - "git.urbach.dev/cli/q/src/expression" - "git.urbach.dev/cli/q/src/types" -) - -// CallToRegister moves the result of a function call into the given register. -func (f *Function) CallToRegister(node *expression.Expression, register cpu.Register) (types.Type, error) { - types, err := f.CompileCall(node) - - if err != nil { - return nil, err - } - - if register != f.CPU.Output[0] { - f.RegisterRegister(asm.MOVE, register, f.CPU.Output[0]) - } - - if len(types) == 0 { - return nil, nil - } - - return types[0], err -} diff --git a/src/core/Compare.go b/src/core/Compare.go index f367020..f9bc25e 100644 --- a/src/core/Compare.go +++ b/src/core/Compare.go @@ -31,7 +31,7 @@ func (f *Function) Compare(comparison *expression.Expression) error { return err } - return f.ExecuteLeaf(comparison.Token, f.CPU.Output[0], right.Token) + return f.ExecuteToken(comparison.Token, f.CPU.Output[0], right.Token) } tmp := f.NewRegister() diff --git a/src/core/CompileDefinition.go b/src/core/CompileDefinition.go index 58b89be..1ea9564 100644 --- a/src/core/CompileDefinition.go +++ b/src/core/CompileDefinition.go @@ -1,10 +1,8 @@ package core import ( - "git.urbach.dev/cli/q/src/asm" "git.urbach.dev/cli/q/src/ast" "git.urbach.dev/cli/q/src/errors" - "git.urbach.dev/cli/q/src/expression" "git.urbach.dev/cli/q/src/token" "git.urbach.dev/cli/q/src/types" ) @@ -48,27 +46,5 @@ func (f *Function) CompileDefinition(node *ast.Define) error { return errors.New(errors.NotImplemented, f.File, node.Expression.Token.Position) } - count := 0 - types, err := f.CompileCall(right) - - if err != nil { - return err - } - - return left.EachLeaf(func(leaf *expression.Expression) error { - variable, err := f.Define(leaf) - - if err != nil { - return err - } - - if count < len(types) { - variable.Value.Typ = types[count] - } - - f.RegisterRegister(asm.MOVE, variable.Value.Register, f.CPU.Output[count]) - f.AddVariable(variable) - count++ - return nil - }) + return f.MultiDefine(left, right) } diff --git a/src/core/DotToRegister.go b/src/core/DotToRegister.go deleted file mode 100644 index b3c617c..0000000 --- a/src/core/DotToRegister.go +++ /dev/null @@ -1,60 +0,0 @@ -package core - -import ( - "fmt" - "math" - - "git.urbach.dev/cli/q/src/asm" - "git.urbach.dev/cli/q/src/cpu" - "git.urbach.dev/cli/q/src/errors" - "git.urbach.dev/cli/q/src/expression" - "git.urbach.dev/cli/q/src/types" -) - -// DotToRegister moves a constant or a function address into the given register. -func (f *Function) DotToRegister(node *expression.Expression, register cpu.Register) (types.Type, error) { - left := node.Children[0] - right := node.Children[1] - leftText := left.Token.Text(f.File.Bytes) - rightText := right.Token.Text(f.File.Bytes) - variable := f.VariableByName(leftText) - - if variable != nil { - field := variable.Value.Typ.(*types.Pointer).To.(*types.Struct).FieldByName(rightText) - - memory := asm.Memory{ - Base: variable.Value.Register, - Offset: int8(field.Offset), - OffsetRegister: math.MaxUint8, - Length: byte(field.Type.Size()), - } - - f.MemoryRegister(asm.LOAD, memory, register) - return field.Type, nil - } - - constant, isConst := f.All.Constants[f.Package+"."+leftText+"."+rightText] - - if isConst { - number, err := ToNumber(constant.Token, constant.File) - - if err != nil { - return nil, err - } - - f.SaveRegister(register) - f.RegisterNumber(asm.MOVE, register, number) - return types.AnyInt, nil - } - - uniqueName := fmt.Sprintf("%s.%s", leftText, rightText) - function, exists := f.All.Functions[uniqueName] - - if exists { - f.File.Imports[leftText].Used = true - f.RegisterLabel(asm.MOVE, register, function.UniqueName) - return types.AnyPointer, nil - } - - return nil, errors.New(&errors.UnknownIdentifier{Name: uniqueName}, f.File, left.Token.Position) -} diff --git a/src/core/Evaluate.go b/src/core/Evaluate.go index b13cd17..574b51b 100644 --- a/src/core/Evaluate.go +++ b/src/core/Evaluate.go @@ -19,7 +19,7 @@ func (f *Function) Evaluate(expr *expression.Expression) (eval.Value, error) { } if expr.IsLeaf() { - return f.EvaluateLeaf(expr) + return f.EvaluateToken(expr.Token) } switch expr.Token.Kind { diff --git a/src/core/EvaluateArray.go b/src/core/EvaluateArray.go index 857b542..bd32ebd 100644 --- a/src/core/EvaluateArray.go +++ b/src/core/EvaluateArray.go @@ -11,13 +11,13 @@ import ( "git.urbach.dev/cli/q/src/types" ) -// EvaluateArray evaluates a function call. -func (f *Function) EvaluateArray(expr *expression.Expression) (eval.Value, error) { +// EvaluateArray evaluates an array access. +func (f *Function) EvaluateArray(expr *expression.Expression) (eval.Memory, error) { name := expr.Children[0].Token.Text(f.File.Bytes) array := f.VariableByName(name) if array == nil { - return nil, errors.New(&errors.UnknownIdentifier{Name: name}, f.File, expr.Children[0].Token.Position) + return eval.Memory{}, errors.New(&errors.UnknownIdentifier{Name: name}, f.File, expr.Children[0].Token.Position) } defer f.UseVariable(array) @@ -33,11 +33,11 @@ func (f *Function) EvaluateArray(expr *expression.Expression) (eval.Value, error index, err := f.Evaluate(indexExpr) if err != nil { - return nil, err + return eval.Memory{}, err } if !types.Is(index.Type(), types.AnyInt) { - return nil, errors.New(&errors.TypeMismatch{Encountered: index.Type().Name(), Expected: types.AnyInt.Name()}, f.File, indexExpr.Token.Position) + return eval.Memory{}, errors.New(&errors.TypeMismatch{Encountered: index.Type().Name(), Expected: types.AnyInt.Name()}, f.File, indexExpr.Token.Position) } switch index := index.(type) { diff --git a/src/core/EvaluateCall.go b/src/core/EvaluateCall.go index a35c2f0..3f338b1 100644 --- a/src/core/EvaluateCall.go +++ b/src/core/EvaluateCall.go @@ -1,26 +1,22 @@ package core import ( - "git.urbach.dev/cli/q/src/errors" "git.urbach.dev/cli/q/src/eval" "git.urbach.dev/cli/q/src/expression" ) // EvaluateCall evaluates a function call. -func (f *Function) EvaluateCall(expr *expression.Expression) (eval.Value, error) { - types, err := f.CompileCall(expr) +func (f *Function) EvaluateCall(expr *expression.Expression) (eval.Register, error) { + typ, err := f.CompileCall(expr) if err != nil { - return nil, err + return eval.Register{}, err } - if len(types) == 0 { - return nil, errors.New(errors.UntypedExpression, f.File, expr.Token.Position) - } + value := eval.Register{Register: f.CPU.Output[0]} - value := eval.Register{ - Typ: types[0], - Register: f.CPU.Output[0], + if len(typ) > 0 { + value.Typ = typ[0] } return value, nil diff --git a/src/core/EvaluateLeaf.go b/src/core/EvaluateToken.go similarity index 78% rename from src/core/EvaluateLeaf.go rename to src/core/EvaluateToken.go index 86b3629..6a14446 100644 --- a/src/core/EvaluateLeaf.go +++ b/src/core/EvaluateToken.go @@ -6,16 +6,15 @@ import ( "git.urbach.dev/cli/q/src/asm" "git.urbach.dev/cli/q/src/errors" "git.urbach.dev/cli/q/src/eval" - "git.urbach.dev/cli/q/src/expression" "git.urbach.dev/cli/q/src/token" "git.urbach.dev/cli/q/src/types" ) -// EvaluateLeaf evaluates a leaf expression. -func (f *Function) EvaluateLeaf(expr *expression.Expression) (eval.Value, error) { - switch expr.Token.Kind { +// EvaluateToken evaluates a single token. +func (f *Function) EvaluateToken(t token.Token) (eval.Value, error) { + switch t.Kind { case token.Identifier: - name := expr.Token.Text(f.File.Bytes) + name := t.Text(f.File.Bytes) if name == "true" { value := eval.Number{ @@ -64,10 +63,10 @@ func (f *Function) EvaluateLeaf(expr *expression.Expression) (eval.Value, error) return value, nil } - return nil, errors.New(&errors.UnknownIdentifier{Name: name}, f.File, expr.Token.Position) + return nil, errors.New(&errors.UnknownIdentifier{Name: name}, f.File, t.Position) case token.Number, token.Rune: - number, err := f.ToNumber(expr.Token) + number, err := f.ToNumber(t) if err != nil { return nil, err @@ -81,7 +80,7 @@ func (f *Function) EvaluateLeaf(expr *expression.Expression) (eval.Value, error) return value, nil case token.String: - data := expr.Token.Bytes(f.File.Bytes) + data := t.Bytes(f.File.Bytes) data = String(data) slice := make([]byte, len(data)+8+1) @@ -97,5 +96,5 @@ func (f *Function) EvaluateLeaf(expr *expression.Expression) (eval.Value, error) return value, nil } - return nil, errors.New(errors.InvalidExpression, f.File, expr.Token.Position) + return nil, errors.New(errors.InvalidExpression, f.File, t.Position) } diff --git a/src/core/Execute.go b/src/core/Execute.go index 8d01a4e..22b6904 100644 --- a/src/core/Execute.go +++ b/src/core/Execute.go @@ -8,13 +8,13 @@ import ( ) // 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) +func (f *Function) Execute(operation token.Token, register cpu.Register, expr *expression.Expression) error { + if expr.IsLeaf() { + return f.ExecuteToken(operation, register, expr.Token) } - if ast.IsFunctionCall(value) { - _, err := f.CompileCall(value) + if ast.IsFunctionCall(expr) { + _, err := f.CompileCall(expr) if err != nil { return err @@ -26,7 +26,7 @@ func (f *Function) Execute(operation token.Token, register cpu.Register, value * tmp := f.NewRegister() defer f.FreeRegister(tmp) - _, err := f.ExpressionToRegister(value, tmp) + _, err := f.ExpressionToRegister(expr, tmp) if err != nil { return err diff --git a/src/core/ExecuteLeaf.go b/src/core/ExecuteToken.go similarity index 72% rename from src/core/ExecuteLeaf.go rename to src/core/ExecuteToken.go index 6ee59af..77b22b9 100644 --- a/src/core/ExecuteLeaf.go +++ b/src/core/ExecuteToken.go @@ -6,8 +6,8 @@ import ( "git.urbach.dev/cli/q/src/token" ) -// 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 { +// ExecuteToken performs an operation on a register with the given leaf operand. +func (f *Function) ExecuteToken(operation token.Token, register cpu.Register, operand token.Token) error { switch operand.Kind { case token.Identifier: name := operand.Text(f.File.Bytes) @@ -31,7 +31,13 @@ func (f *Function) ExecuteLeaf(operation token.Token, register cpu.Register, ope case token.String: if operation.Kind == token.Assign { - _, err := f.TokenToRegister(operand, register) + value, err := f.EvaluateToken(operand) + + if err != nil { + return err + } + + f.ValueToRegister(value, register) return err } } diff --git a/src/core/ExpressionToMemory.go b/src/core/ExpressionToMemory.go index 8ec2efa..75b6e23 100644 --- a/src/core/ExpressionToMemory.go +++ b/src/core/ExpressionToMemory.go @@ -1,10 +1,7 @@ package core import ( - "fmt" - "git.urbach.dev/cli/q/src/asm" - "git.urbach.dev/cli/q/src/eval" "git.urbach.dev/cli/q/src/expression" "git.urbach.dev/cli/q/src/types" ) @@ -17,22 +14,6 @@ func (f *Function) ExpressionToMemory(node *expression.Expression, memory asm.Me return nil, err } - switch value := value.(type) { - case eval.Number: - f.MemoryNumber(asm.STORE, memory, value.Number) - case eval.Register: - f.MemoryRegister(asm.STORE, memory, value.Register) - f.FreeRegister(value.Register) - case eval.Memory: - tmp := f.NewRegister() - f.MemoryRegister(asm.LOAD, value.Memory, tmp) - f.MemoryRegister(asm.STORE, memory, tmp) - f.FreeRegister(tmp) - case eval.Label: - f.MemoryLabel(asm.STORE, memory, value.Label) - default: - panic(fmt.Errorf("%s: not implemented: %v", f.UniqueName, value)) - } - - return value.Type(), err + f.ValueToMemory(value, memory) + return value.Type(), nil } diff --git a/src/core/ExpressionToRegister.go b/src/core/ExpressionToRegister.go index a5bfcdc..bd40f05 100644 --- a/src/core/ExpressionToRegister.go +++ b/src/core/ExpressionToRegister.go @@ -17,16 +17,46 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp } if node.IsLeaf() { - return f.TokenToRegister(node.Token, register) + value, err := f.EvaluateToken(node.Token) + + if err != nil { + return nil, err + } + + f.ValueToRegister(value, register) + return value.Type(), nil } switch node.Token.Kind { - case token.Call: - return f.CallToRegister(node, register) case token.Array: - return f.ArrayElementToRegister(node, register) + value, err := f.EvaluateArray(node) + + if err != nil { + return nil, err + } + + f.ValueToRegister(value, register) + return value.Type(), nil + case token.Dot: - return f.DotToRegister(node, register) + value, err := f.EvaluateDot(node) + + if err != nil { + return nil, err + } + + f.ValueToRegister(value, register) + return value.Type(), nil + + case token.Call: + value, err := f.EvaluateCall(node) + + if err != nil { + return nil, err + } + + f.ValueToRegister(value, register) + return value.Type(), nil } if len(node.Children) == 1 { diff --git a/src/core/MultiDefine.go b/src/core/MultiDefine.go new file mode 100644 index 0000000..045afb6 --- /dev/null +++ b/src/core/MultiDefine.go @@ -0,0 +1,33 @@ +package core + +import ( + "git.urbach.dev/cli/q/src/asm" + "git.urbach.dev/cli/q/src/expression" +) + +// MultiDefine defines multiple variables at once. +func (f *Function) MultiDefine(left *expression.Expression, right *expression.Expression) error { + count := 0 + types, err := f.CompileCall(right) + + if err != nil { + return err + } + + return left.EachLeaf(func(leaf *expression.Expression) error { + variable, err := f.Define(leaf) + + if err != nil { + return err + } + + if count < len(types) { + variable.Value.Typ = types[count] + } + + f.RegisterRegister(asm.MOVE, variable.Value.Register, f.CPU.Output[count]) + f.AddVariable(variable) + count++ + return nil + }) +} diff --git a/src/core/TokenToRegister.go b/src/core/TokenToRegister.go deleted file mode 100644 index 70989e1..0000000 --- a/src/core/TokenToRegister.go +++ /dev/null @@ -1,74 +0,0 @@ -package core - -import ( - "encoding/binary" - - "git.urbach.dev/cli/q/src/asm" - "git.urbach.dev/cli/q/src/cpu" - "git.urbach.dev/cli/q/src/errors" - "git.urbach.dev/cli/q/src/token" - "git.urbach.dev/cli/q/src/types" -) - -// 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) (types.Type, error) { - switch t.Kind { - case token.Identifier: - name := t.Text(f.File.Bytes) - - if name == "true" { - f.RegisterNumber(asm.MOVE, register, 1) - return types.Bool, nil - } - - if name == "false" { - f.RegisterNumber(asm.MOVE, register, 0) - return types.Bool, nil - } - - variable, function := f.Identifier(name) - - if variable != nil { - f.UseVariable(variable) - f.SaveRegister(register) - f.RegisterRegister(asm.MOVE, register, variable.Value.Register) - return variable.Value.Typ, nil - } - - if function != nil { - f.SaveRegister(register) - f.RegisterLabel(asm.MOVE, register, function.UniqueName) - return types.AnyPointer, nil - } - - return nil, errors.New(&errors.UnknownIdentifier{Name: name}, f.File, t.Position) - - case token.Number, token.Rune: - number, err := f.ToNumber(t) - - if err != nil { - return nil, err - } - - f.SaveRegister(register) - f.RegisterNumber(asm.MOVE, register, number) - return types.AnyInt, nil - - case token.String: - data := t.Bytes(f.File.Bytes) - data = String(data) - - slice := make([]byte, len(data)+8+1) - binary.LittleEndian.PutUint64(slice, uint64(len(data))) - copy(slice[8:], data) - - label := f.AddBytes(slice) - f.SaveRegister(register) - f.RegisterLabel(asm.MOVE, register, label) - return types.String, nil - - default: - return nil, errors.New(errors.InvalidExpression, f.File, t.Position) - } -} diff --git a/src/core/ValueToMemory.go b/src/core/ValueToMemory.go new file mode 100644 index 0000000..7380b73 --- /dev/null +++ b/src/core/ValueToMemory.go @@ -0,0 +1,24 @@ +package core + +import ( + "git.urbach.dev/cli/q/src/asm" + "git.urbach.dev/cli/q/src/eval" +) + +// ValueToMemory moves a value into a memory region. +func (f *Function) ValueToMemory(value eval.Value, memory asm.Memory) { + switch value := value.(type) { + case eval.Number: + f.MemoryNumber(asm.STORE, memory, value.Number) + case eval.Register: + f.MemoryRegister(asm.STORE, memory, value.Register) + f.FreeRegister(value.Register) + case eval.Memory: + tmp := f.NewRegister() + f.MemoryRegister(asm.LOAD, value.Memory, tmp) + f.MemoryRegister(asm.STORE, memory, tmp) + f.FreeRegister(tmp) + case eval.Label: + f.MemoryLabel(asm.STORE, memory, value.Label) + } +} diff --git a/src/core/ValueToRegister.go b/src/core/ValueToRegister.go new file mode 100644 index 0000000..94da6cd --- /dev/null +++ b/src/core/ValueToRegister.go @@ -0,0 +1,22 @@ +package core + +import ( + "git.urbach.dev/cli/q/src/asm" + "git.urbach.dev/cli/q/src/cpu" + "git.urbach.dev/cli/q/src/eval" +) + +// ValueToRegister moves a value into a register. +func (f *Function) ValueToRegister(value eval.Value, register cpu.Register) { + switch value := value.(type) { + case eval.Number: + f.RegisterNumber(asm.MOVE, register, value.Number) + case eval.Register: + f.RegisterRegister(asm.MOVE, register, value.Register) + f.FreeRegister(value.Register) + case eval.Memory: + f.MemoryRegister(asm.LOAD, value.Memory, register) + case eval.Label: + f.RegisterLabel(asm.MOVE, register, value.Label) + } +}