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 {
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)
}

View File

@ -8,9 +8,9 @@ import (
// CompileAssign compiles an assign statement.
func (f *Function) CompileAssign(node *ast.Assign) error {
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)
}

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.variables[variable.Name] = variable
f.Scope()[variable.Name] = variable
f.cpu.Reserve(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.
func (f *Function) identifierExists(name string) bool {
_, exists := f.variables[name]
variable := f.Variable(name)
if exists {
if variable != nil {
return true
}
_, exists = f.functions[name]
_, exists := f.functions[name]
return exists
}

View File

@ -16,8 +16,11 @@ func (f *Function) CompileIf(branch *ast.If) error {
return err
}
f.AddLabel(success)
defer f.AddLabel(fail)
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 {
case token.Identifier:
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)
}

View File

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

View File

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

View File

@ -12,3 +12,19 @@ type Variable struct {
Register cpu.Register
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.
type state struct {
err error
variables map[string]*Variable
scopes []Scope
functions map[string]*Function
registerHistory []uint64
finished chan struct{}

View File

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