Simplified Evaluate function

This commit is contained in:
Eduard Urbach 2025-02-27 15:30:44 +01:00
parent 9f78733d5d
commit efb3089211
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
15 changed files with 98 additions and 96 deletions

View File

@ -41,7 +41,7 @@ func (f *Function) CompileAssignArray(node *ast.Assign) error {
memory.Offset = int8(index)
} else {
index, isTemporary, err := f.Evaluate(indexExpr)
index, err := f.Evaluate(indexExpr)
if err != nil {
return err
@ -52,11 +52,8 @@ func (f *Function) CompileAssignArray(node *ast.Assign) error {
}
memory.OffsetRegister = index.Register
if isTemporary {
defer f.FreeRegister(index.Register)
}
}
_, err := f.ExpressionToMemory(right, memory)
return err

View File

@ -59,7 +59,7 @@ func (f *Function) CompileAssignDivision(expr *expression.Expression) error {
}
dividendExpr := right.Children[0]
dividend, isTemporary, err := f.Evaluate(dividendExpr)
dividend, err := f.Evaluate(dividendExpr)
if err != nil {
return err
@ -73,10 +73,6 @@ func (f *Function) CompileAssignDivision(expr *expression.Expression) error {
err = f.Execute(right.Token, dividend.Register, divisor)
f.RegisterRegister(asm.MOVE, quotientVariable.Register, x86.RAX)
f.RegisterRegister(asm.MOVE, remainderVariable.Register, x86.RDX)
if isTemporary {
f.FreeRegister(dividend.Register)
}
return err
}

View File

@ -68,7 +68,7 @@ func (f *Function) CompileFor(loop *ast.For) error {
f.AddLabel(label)
f.RegisterNumber(asm.COMPARE, counter, number)
} else {
value, isTemporary, err := f.Evaluate(to)
value, err := f.Evaluate(to)
if err != nil {
return err
@ -78,12 +78,9 @@ func (f *Function) CompileFor(loop *ast.For) error {
return errors.New(&errors.TypeMismatch{Encountered: value.Type.Name(), Expected: types.AnyInt.Name()}, f.File, to.Token.Position)
}
if isTemporary {
defer f.FreeRegister(value.Register)
}
f.AddLabel(label)
f.RegisterRegister(asm.COMPARE, counter, value.Register)
defer f.FreeRegister(value.Register)
}
f.Jump(asm.JGE, labelEnd)

View File

@ -13,7 +13,7 @@ var _len = Function{OutputTypes: []types.Type{types.AnyInt}}
// CompileLen returns the length of a slice.
func (f *Function) CompileLen(root *expression.Expression) error {
value, isTemporary, err := f.Evaluate(root.Children[1])
value, err := f.Evaluate(root.Children[1])
if err != nil {
return err
@ -25,10 +25,6 @@ func (f *Function) CompileLen(root *expression.Expression) error {
f.SaveRegister(f.CPU.Output[0])
f.MemoryRegister(asm.LOAD, asm.Memory{Base: value.Register, Offset: -8, OffsetRegister: math.MaxUint8, Length: 8}, f.CPU.Output[0])
if isTemporary {
f.FreeRegister(value.Register)
}
return nil
}

View File

@ -24,8 +24,10 @@ func (f *Function) Define(leaf *expression.Expression) (*scope.Variable, error)
variable = &scope.Variable{
Name: name,
Value: scope.Value{
Register: f.NewRegister(),
Alive: uses,
},
}
return variable, nil

View File

@ -4,22 +4,23 @@ import (
"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/scope"
"git.urbach.dev/cli/q/src/token"
)
// Evaluate evaluates an expression and returns a register that contains the value of the expression.
func (f *Function) Evaluate(expr *expression.Expression) (Value, bool, error) {
func (f *Function) Evaluate(expr *expression.Expression) (scope.Value, error) {
if expr.Token.Kind == token.Identifier {
name := expr.Token.Text(f.File.Bytes)
variable := f.VariableByName(name)
if variable == nil {
return Value{}, false, errors.New(&errors.UnknownIdentifier{Name: name}, f.File, expr.Token.Position)
return scope.Value{}, errors.New(&errors.UnknownIdentifier{Name: name}, f.File, expr.Token.Position)
}
if variable.Alive == 1 {
f.UseVariable(variable)
return Value{variable.Type, variable.Register}, false, nil
return variable.Value, nil
}
}
@ -27,13 +28,26 @@ func (f *Function) Evaluate(expr *expression.Expression) (Value, bool, error) {
types, err := f.CompileCall(expr)
if err != nil {
return Value{}, false, err
return scope.Value{}, err
}
return Value{types[0], f.CPU.Output[0]}, false, nil
value := scope.Value{
Type: types[0],
Register: f.CPU.Output[0],
Alive: 1,
}
return value, nil
}
tmp := f.NewRegister()
typ, err := f.ExpressionToRegister(expr, tmp)
return Value{typ, tmp}, true, err
value := scope.Value{
Type: typ,
Register: tmp,
Alive: 1,
}
return value, err
}

View File

@ -52,17 +52,13 @@ func (f *Function) ExpressionToMemory(node *expression.Expression, memory asm.Me
}
}
value, isTemporary, err := f.Evaluate(node)
value, err := f.Evaluate(node)
if err != nil {
return nil, err
}
f.MemoryRegister(asm.STORE, memory, value.Register)
if isTemporary {
f.FreeRegister(value.Register)
}
return value.Type, err
}

View File

@ -31,9 +31,11 @@ func (f *Function) ResolveTypes() error {
f.AddVariable(&scope.Variable{
Name: param.name,
Value: scope.Value{
Type: param.typ,
Register: x86.InputRegisters[i],
Alive: uses,
},
})
}

View File

@ -1,12 +0,0 @@
package core
import (
"git.urbach.dev/cli/q/src/cpu"
"git.urbach.dev/cli/q/src/types"
)
// Value combines a register with its data type.
type Value struct {
Type types.Type
Register cpu.Register
}

View File

@ -45,9 +45,11 @@ func (stack *Stack) PushScope(body ast.AST, buffer []byte) *Scope {
s.Variables = append(s.Variables, &Variable{
Name: v.Name,
Value: Value{
Register: v.Register,
Alive: count,
Type: v.Type,
},
})
}
}

27
src/scope/Value.go Normal file
View File

@ -0,0 +1,27 @@
package scope
import (
"git.urbach.dev/cli/q/src/cpu"
"git.urbach.dev/cli/q/src/types"
)
// Value combines a register with its data type.
type Value struct {
Type types.Type
Register cpu.Register
Alive uint8
}
// IsAlive returns true if the Value is still alive.
func (v *Value) IsAlive() bool {
return v.Alive > 0
}
// Use reduces the lifetime counter by one.
func (v *Value) Use() {
if v.Alive == 0 {
panic("incorrect number of value use calls")
}
v.Alive--
}

View File

@ -1,28 +1,7 @@
package scope
import (
"git.urbach.dev/cli/q/src/cpu"
"git.urbach.dev/cli/q/src/types"
)
// Variable represents a named register.
// Variable is a named value.
type Variable struct {
Type types.Type
Value
Name string
Alive uint8
Register cpu.Register
}
// IsAlive returns true if the variable is still alive.
func (v *Variable) IsAlive() bool {
return v.Alive > 0
}
// Use reduces the lifetime counter by one.
func (v *Variable) Use() {
if v.Alive == 0 {
panic("incorrect number of variable use calls")
}
v.Alive--
}

20
tests/programs/for.q Normal file
View File

@ -0,0 +1,20 @@
main() {
total := 0
for 0..10 {
total += 1
}
assert total == 10
for 0..total {
total -= 1
}
assert total == 5
for i := 0..10 {
assert i >= 0
assert i < 10
}
}

View File

@ -1,14 +0,0 @@
main() {
x := 0
for 0..5 {
x += 1
}
assert x == 5
for i := 0..5 {
assert i >= 0
assert i < 5
}
}

View File

@ -60,7 +60,7 @@ var programs = []struct {
{"switch", 0},
{"loop-infinite", 0},
{"loop-lifetime", 0},
{"loop-for", 0},
{"for", 0},
{"memory-free", 0},
{"out-of-memory", 0},
{"index-static", 0},