72 lines
1.5 KiB
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
|
|
}
|