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 {
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ 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{}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -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
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 {
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -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{}
|
||||||
|
@ -11,6 +11,7 @@ var examples = []struct {
|
|||||||
ExpectedExitCode int
|
ExpectedExitCode int
|
||||||
}{
|
}{
|
||||||
{"hello", "Hello", 0},
|
{"hello", "Hello", 0},
|
||||||
|
{"factorial", "", 120},
|
||||||
{"fibonacci", "", 55},
|
{"fibonacci", "", 55},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user