Simplified file structure

This commit is contained in:
Eduard Urbach 2024-08-07 19:39:10 +02:00
parent cacee7260a
commit a466281307
Signed by: eduard
GPG key ID: 49226B848C78F6C8
219 changed files with 453 additions and 457 deletions

30
src/scope/Scope.go Normal file
View file

@ -0,0 +1,30 @@
package scope
import (
"git.akyoto.dev/cli/q/src/cpu"
)
// Scope represents an independent code block.
type Scope struct {
Variables []*Variable
Depth uint8
InLoop bool
cpu.State
}
// AddVariable adds a new variable to the current scope.
func (s *Scope) AddVariable(variable *Variable) {
s.Variables = append(s.Variables, variable)
s.Use(variable.Register)
}
// VariableByName returns the variable with the given name or `nil` if it doesn't exist.
func (s *Scope) VariableByName(name string) *Variable {
for _, v := range s.Variables {
if v.Name == name {
return v
}
}
return nil
}

94
src/scope/Stack.go Normal file
View file

@ -0,0 +1,94 @@
package scope
import (
"git.akyoto.dev/cli/q/src/ast"
"git.akyoto.dev/cli/q/src/cpu"
"git.akyoto.dev/cli/q/src/token"
)
// Stack is a stack of scopes.
type Stack struct {
Scopes []*Scope
}
// AddVariable adds a new variable to the current scope.
func (stack *Stack) AddVariable(variable *Variable) {
stack.CurrentScope().AddVariable(variable)
}
// CurrentScope returns the current scope.
func (stack *Stack) CurrentScope() *Scope {
return stack.Scopes[len(stack.Scopes)-1]
}
// PopScope removes the scope at the top of the stack.
func (stack *Stack) PopScope() {
stack.Scopes = stack.Scopes[:len(stack.Scopes)-1]
}
// PushScope pushes a new scope to the top of the stack.
func (stack *Stack) PushScope(body ast.AST, buffer []byte) *Scope {
s := &Scope{}
if len(stack.Scopes) > 0 {
lastScope := stack.Scopes[len(stack.Scopes)-1]
s.State = lastScope.State
s.Variables = make([]*Variable, 0, len(lastScope.Variables))
s.InLoop = lastScope.InLoop
for _, v := range lastScope.Variables {
count := ast.Count(body, buffer, token.Identifier, v.Name)
if count == 0 {
continue
}
s.Variables = append(s.Variables, &Variable{
Name: v.Name,
Register: v.Register,
Alive: count,
Type: v.Type,
})
}
}
stack.Scopes = append(stack.Scopes, s)
return s
}
// UseVariable reduces the lifetime of the variable in all scopes.
func (stack *Stack) UseVariable(variable *Variable) {
for _, scope := range stack.Scopes {
if scope.InLoop {
continue
}
local := scope.VariableByName(variable.Name)
if local == nil {
continue
}
local.Use()
if !local.IsAlive() {
scope.Free(local.Register)
}
}
}
// VariableByName returns the variable with the given name or `nil` if it doesn't exist.
func (stack *Stack) VariableByName(name string) *Variable {
return stack.CurrentScope().VariableByName(name)
}
// VariableByRegister returns the variable that occupies the given register or `nil` if none occupy the register.
func (stack *Stack) VariableByRegister(register cpu.Register) *Variable {
for _, v := range stack.CurrentScope().Variables {
if v.Register == register {
return v
}
}
return nil
}

28
src/scope/Variable.go Normal file
View file

@ -0,0 +1,28 @@
package scope
import (
"git.akyoto.dev/cli/q/src/cpu"
"git.akyoto.dev/cli/q/src/types"
)
// Variable represents a named register.
type Variable struct {
Name string
Type types.Type
Alive uint8
Register cpu.Register
}
// IsAlive returns true if the variable is still alive.
func (v *Variable) IsAlive() bool {
return v.Alive > 0
}
// Use reduces the lifetime counter by one.
func (v *Variable) Use() {
if v.Alive == 0 {
panic("incorrect number of variable use calls")
}
v.Alive--
}