package core import ( "fmt" "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 := CountIdentifier(f.Body, 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.assembler.Comment(fmt.Sprintf("%s = %s (%s, %d uses)", variable.Name, variable.Value, variable.Register, variable.Alive)) } f.variables[variable.Name] = variable f.cpu.Use(variable.Register) } func (f *Function) useVariable(variable *Variable) { variable.Alive-- if variable.Alive < 0 { panic("incorrect number of variable use calls") } if variable.Alive == 0 { if config.Comments { f.assembler.Comment(fmt.Sprintf("%s died (%s)", variable.Name, variable.Register)) } f.cpu.Free(variable.Register) } } // identifierExists returns true if the identifier has been defined. func (f *Function) identifierExists(name string) bool { _, exists := f.variables[name] if exists { return true } _, exists = f.functions[name] return exists } func (f *Function) storeVariableInRegister(name string, value *expression.Expression, uses int) error { reg, exists := f.cpu.FindFree(f.cpu.General) if !exists { panic("no free registers") } err := f.ExpressionToRegister(value, reg) f.AddVariable(&Variable{ Name: name, Register: reg, Alive: uses, }) return err } 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 }