Implemented variable scopes
This commit is contained in:
parent
948d499231
commit
24d3e8f2be
11
examples/factorial/factorial.q
Normal file
11
examples/factorial/factorial.q
Normal file
@ -0,0 +1,11 @@
|
||||
main() {
|
||||
syscall(60, factorial(5))
|
||||
}
|
||||
|
||||
factorial(x) {
|
||||
if x <= 1 {
|
||||
return 1
|
||||
}
|
||||
|
||||
return x * factorial(x - 1)
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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{}),
|
||||
},
|
||||
}
|
||||
|
@ -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
31
src/build/core/Scope.go
Normal 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]
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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{}
|
||||
|
@ -11,6 +11,7 @@ var examples = []struct {
|
||||
ExpectedExitCode int
|
||||
}{
|
||||
{"hello", "Hello", 0},
|
||||
{"factorial", "", 120},
|
||||
{"fibonacci", "", 55},
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user