112 lines
2.4 KiB
Go
112 lines
2.4 KiB
Go
package core
|
|
|
|
import (
|
|
"git.akyoto.dev/cli/q/src/build/ast"
|
|
"git.akyoto.dev/cli/q/src/build/config"
|
|
"git.akyoto.dev/cli/q/src/build/errors"
|
|
"git.akyoto.dev/cli/q/src/build/expression"
|
|
"git.akyoto.dev/cli/q/src/build/token"
|
|
)
|
|
|
|
// CompileDefinition compiles a variable definition.
|
|
func (f *Function) CompileDefinition(node *ast.Define) error {
|
|
name := node.Name.Text()
|
|
|
|
if f.identifierExists(name) {
|
|
return errors.New(&errors.VariableAlreadyExists{Name: name}, f.File, node.Name.Position)
|
|
}
|
|
|
|
uses := token.Count(f.Body, token.Identifier, name) - 1
|
|
|
|
if uses == 0 {
|
|
return errors.New(&errors.UnusedVariable{Name: name}, f.File, node.Name.Position)
|
|
}
|
|
|
|
value := node.Value
|
|
|
|
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
|
|
}
|
|
|
|
return f.storeVariableInRegister(name, value, uses)
|
|
}
|
|
|
|
func (f *Function) AddVariable(variable *Variable) {
|
|
if config.Comments {
|
|
f.Comment("%s = %s (%d uses)", variable.Name, variable.Register, variable.Alive)
|
|
}
|
|
|
|
scope := f.Scope()
|
|
variable.Scope = scope
|
|
|
|
scope.variables[variable.Name] = variable
|
|
scope.Use(variable.Register)
|
|
}
|
|
|
|
func (f *Function) useVariable(variable *Variable) {
|
|
for i, scope := range f.scopes {
|
|
if scope.inLoop && variable.Scope != scope {
|
|
continue
|
|
}
|
|
|
|
local := scope.variables[variable.Name]
|
|
|
|
if local == nil {
|
|
continue
|
|
}
|
|
|
|
local.Alive--
|
|
|
|
if local.Alive < 0 {
|
|
panic("incorrect number of variable use calls")
|
|
}
|
|
|
|
if local.Alive == 0 {
|
|
if config.Comments {
|
|
f.Comment("%s died (%s) in scope %d", local.Name, local.Register, i)
|
|
}
|
|
|
|
scope.Free(local.Register)
|
|
}
|
|
}
|
|
}
|
|
|
|
// identifierExists returns true if the identifier has been defined.
|
|
func (f *Function) identifierExists(name string) bool {
|
|
variable := f.Variable(name)
|
|
|
|
if variable != nil {
|
|
return true
|
|
}
|
|
|
|
_, exists := f.functions[name]
|
|
return exists
|
|
}
|
|
|
|
func (f *Function) storeVariableInRegister(name string, value *expression.Expression, uses int) error {
|
|
reg, exists := f.Scope().FindFree(f.cpu.General)
|
|
|
|
if !exists {
|
|
panic("no free registers")
|
|
}
|
|
|
|
f.Scope().Reserve(reg)
|
|
err := f.ExpressionToRegister(value, reg)
|
|
|
|
f.AddVariable(&Variable{
|
|
Name: name,
|
|
Register: reg,
|
|
Alive: uses,
|
|
})
|
|
|
|
return err
|
|
}
|