q/src/build/VariableDefinition.go

72 lines
1.5 KiB
Go

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"
)
// CompileVariableDefinition compiles a variable definition.
func (f *Function) CompileVariableDefinition(expr *expression.Expression) error {
if len(expr.Children) < 2 {
return errors.New(errors.MissingAssignValue, f.File, expr.LastChild().Token.End())
}
name := expr.Children[0].Token.Text()
if f.identifierExists(name) {
return errors.New(&errors.VariableAlreadyExists{Name: name}, f.File, expr.Children[0].Token.Position)
}
reg, exists := f.CPU.FindFree(f.CPU.General)
if !exists {
panic("no free registers")
}
err := f.ExpressionToRegister(expr.Children[1], reg)
if err != nil {
return err
}
variable := &Variable{
Name: name,
Register: reg,
}
f.addVariable(variable)
f.useVariable(variable)
return nil
}
func (f *Function) addVariable(variable *Variable) {
variable.UsesRemaining = countIdentifier(f.Body, variable.Name)
f.Variables[variable.Name] = variable
f.CPU.Use(variable.Register)
}
func (f *Function) useVariable(variable *Variable) {
variable.UsesRemaining--
if variable.UsesRemaining < 0 {
panic("incorrect number of variable use calls")
}
if variable.UsesRemaining == 0 {
f.CPU.Free(variable.Register)
}
}
func countIdentifier(tokens token.List, name string) int {
count := 0
for _, t := range tokens {
if t.Kind == token.Identifier && t.Text() == name {
count++
}
}
return count
}