Implemented variable scopes

This commit is contained in:
Eduard Urbach 2024-07-15 16:51:36 +02:00
parent 948d499231
commit 24d3e8f2be
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
13 changed files with 81 additions and 26 deletions

View File

@ -0,0 +1,11 @@
main() {
syscall(60, factorial(5))
}
factorial(x) {
if x <= 1 {
return 1
}
return x * factorial(x - 1)
}

View File

@ -14,9 +14,9 @@ func (f *Function) Compare(comparison *expression.Expression) error {
if left.IsLeaf() && left.Token.Kind == token.Identifier { if left.IsLeaf() && left.Token.Kind == token.Identifier {
name := left.Token.Text() name := left.Token.Text()
variable, exists := f.variables[name] variable := f.Variable(name)
if !exists { if variable == nil {
return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, left.Token.Position) return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, left.Token.Position)
} }

View File

@ -8,9 +8,9 @@ import (
// CompileAssign compiles an assign statement. // CompileAssign compiles an assign statement.
func (f *Function) CompileAssign(node *ast.Assign) error { func (f *Function) CompileAssign(node *ast.Assign) error {
name := node.Name.Text() name := node.Name.Text()
variable, exists := f.variables[name] variable := f.Variable(name)
if !exists { if variable == nil {
return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, node.Name.Position) return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, node.Name.Position)
} }

View File

@ -44,7 +44,7 @@ func (f *Function) AddVariable(variable *Variable) {
f.Comment("%s = %s (%s, %d uses)", variable.Name, variable.Value, variable.Register, variable.Alive) f.Comment("%s = %s (%s, %d uses)", variable.Name, variable.Value, variable.Register, variable.Alive)
} }
f.variables[variable.Name] = variable f.Scope()[variable.Name] = variable
f.cpu.Reserve(variable.Register) f.cpu.Reserve(variable.Register)
f.cpu.Use(variable.Register) f.cpu.Use(variable.Register)
} }
@ -67,13 +67,13 @@ func (f *Function) useVariable(variable *Variable) {
// identifierExists returns true if the identifier has been defined. // identifierExists returns true if the identifier has been defined.
func (f *Function) identifierExists(name string) bool { func (f *Function) identifierExists(name string) bool {
_, exists := f.variables[name] variable := f.Variable(name)
if exists { if variable != nil {
return true return true
} }
_, exists = f.functions[name] _, exists := f.functions[name]
return exists return exists
} }

View File

@ -16,8 +16,11 @@ func (f *Function) CompileIf(branch *ast.If) error {
return err return err
} }
f.AddLabel(success)
defer f.AddLabel(fail)
f.count.branch++ f.count.branch++
return f.CompileAST(branch.Body) f.AddLabel(success)
f.pushScope()
err = f.CompileAST(branch.Body)
f.popScope()
f.AddLabel(fail)
return err
} }

View File

@ -13,9 +13,9 @@ func (f *Function) ExecuteLeaf(operation token.Token, register cpu.Register, ope
switch operand.Kind { switch operand.Kind {
case token.Identifier: case token.Identifier:
name := operand.Text() name := operand.Text()
variable, exists := f.variables[name] variable := f.Variable(name)
if !exists { if variable == nil {
return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, operand.Position) return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, operand.Position)
} }

View File

@ -36,8 +36,8 @@ func NewFunction(name string, file *fs.File, body token.List) *Function {
Syscall: x64.SyscallRegisters, Syscall: x64.SyscallRegisters,
Output: x64.ReturnValueRegisters, Output: x64.ReturnValueRegisters,
}, },
variables: map[string]*Variable{}, scopes: []Scope{{}},
finished: make(chan struct{}), finished: make(chan struct{}),
}, },
} }
} }

View File

@ -18,14 +18,7 @@ func (f *Function) SaveRegister(register cpu.Register) {
} }
} }
var variable *Variable variable := f.VariableInRegister(register)
for _, v := range f.variables {
if v.Register == register {
variable = v
break
}
}
if variable == nil || variable.Alive == 0 { if variable == nil || variable.Alive == 0 {
return return

31
src/build/core/Scope.go Normal file
View File

@ -0,0 +1,31 @@
package core
// Scope represents a map of variables.
type Scope map[string]*Variable
// Scope returns the current scope.
func (s *state) Scope() Scope {
return s.scopes[len(s.scopes)-1]
}
// pushScope pushes a new scope to the top of the stack.
func (s *state) pushScope() {
lastScope := s.scopes[len(s.scopes)-1]
newScope := make(Scope, len(lastScope))
for k, v := range lastScope {
newScope[k] = &Variable{
Value: v.Value,
Name: v.Name,
Register: v.Register,
Alive: v.Alive,
}
}
s.scopes = append(s.scopes, newScope)
}
// popScope removes the scope at the top of the stack.
func (s *state) popScope() {
s.scopes = s.scopes[:len(s.scopes)-1]
}

View File

@ -16,9 +16,9 @@ func (f *Function) TokenToRegister(t token.Token, register cpu.Register) error {
switch t.Kind { switch t.Kind {
case token.Identifier: case token.Identifier:
name := t.Text() name := t.Text()
variable, exists := f.variables[name] variable := f.Variable(name)
if !exists { if variable == nil {
return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, t.Position) return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, t.Position)
} }

View File

@ -12,3 +12,19 @@ type Variable struct {
Register cpu.Register Register cpu.Register
Alive int Alive int
} }
// Variable returns the variable with the given name or `nil` if it doesn't exist.
func (s *state) Variable(name string) *Variable {
return s.Scope()[name]
}
// VariableInRegister returns the variable that occupies the given register or `nil` if none occupy the register.
func (s *state) VariableInRegister(register cpu.Register) *Variable {
for _, v := range s.Scope() {
if v.Register == register {
return v
}
}
return nil
}

View File

@ -12,7 +12,7 @@ import (
// state is the data structure we embed in each function to preserve compilation state. // state is the data structure we embed in each function to preserve compilation state.
type state struct { type state struct {
err error err error
variables map[string]*Variable scopes []Scope
functions map[string]*Function functions map[string]*Function
registerHistory []uint64 registerHistory []uint64
finished chan struct{} finished chan struct{}

View File

@ -11,6 +11,7 @@ var examples = []struct {
ExpectedExitCode int ExpectedExitCode int
}{ }{
{"hello", "Hello", 0}, {"hello", "Hello", 0},
{"factorial", "", 120},
{"fibonacci", "", 55}, {"fibonacci", "", 55},
} }