diff --git a/src/build/Execute.go b/src/build/Execute.go index bd50435..3bdc61e 100644 --- a/src/build/Execute.go +++ b/src/build/Execute.go @@ -190,6 +190,16 @@ func (f *Function) TokenToRegister(t token.Token, register cpu.Register) error { switch t.Kind { case token.Identifier: name := t.Text() + constant, exists := f.definitions[name] + + if exists { + if config.Verbose { + f.Logf("constant %s = %s", constant.Name, constant.Value) + } + + return f.ExpressionToRegister(constant.Value, register) + } + variable, exists := f.variables[name] if !exists { diff --git a/src/build/Function.go b/src/build/Function.go index 8853605..d58afc2 100644 --- a/src/build/Function.go +++ b/src/build/Function.go @@ -133,6 +133,18 @@ func (f *Function) Wait() { // 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.definitions[name] + + if exists { + return true + } + + _, exists = f.functions[name] return exists } diff --git a/src/build/Variable.go b/src/build/Variable.go index 9d18c37..291d2d4 100644 --- a/src/build/Variable.go +++ b/src/build/Variable.go @@ -2,11 +2,18 @@ package build import ( "git.akyoto.dev/cli/q/src/build/cpu" + "git.akyoto.dev/cli/q/src/build/expression" ) -// Variable represents a variable in a function. +// Variable represents a named register. type Variable struct { Name string Register cpu.Register Alive int } + +// Definitions are single use expressions that don't reside in a register yet. +type Definition struct { + Name string + Value *expression.Expression +} diff --git a/src/build/VariableDefinition.go b/src/build/VariableDefinition.go index f0624da..58a4844 100644 --- a/src/build/VariableDefinition.go +++ b/src/build/VariableDefinition.go @@ -25,25 +25,32 @@ func (f *Function) CompileVariableDefinition(expr *expression.Expression) error return errors.New(&errors.UnusedVariable{Name: name}, f.File, expr.Children[0].Token.Position) } - reg, exists := f.cpu.FindFree(f.cpu.General) + value := expr.Children[1] - if !exists { - panic("no free registers") - } + 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) + } - err := f.ExpressionToRegister(expr.Children[1], reg) + return nil + }) if err != nil { return err } - f.addVariable(&Variable{ - Name: name, - Register: reg, - Alive: uses, - }) + if uses == 1 { + expr.RemoveChild(value) - return nil + f.definitions[name] = &Definition{ + Name: name, + Value: value, + } + + return nil + } + + return f.storeVariableInRegister(name, value, uses) } func (f *Function) addVariable(variable *Variable) { @@ -75,6 +82,24 @@ func (f *Function) useVariable(variable *Variable) { } } +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 diff --git a/src/build/compiler.go b/src/build/compiler.go index 19b75a2..60cfec0 100644 --- a/src/build/compiler.go +++ b/src/build/compiler.go @@ -16,6 +16,7 @@ type compiler struct { cpu cpu.CPU debug []debug err error + definitions map[string]*Definition variables map[string]*Variable functions map[string]*Function sideEffects int diff --git a/src/build/scan.go b/src/build/scan.go index 701a824..758bbec 100644 --- a/src/build/scan.go +++ b/src/build/scan.go @@ -250,8 +250,9 @@ func scanFile(path string, functions chan<- *Function) error { Syscall: x64.SyscallRegisters, Return: x64.ReturnValueRegisters, }, - variables: map[string]*Variable{}, - finished: make(chan struct{}), + definitions: map[string]*Definition{}, + variables: map[string]*Variable{}, + finished: make(chan struct{}), }, }