mirror of
https://github.com/MontFerret/ferret.git
synced 2024-12-14 11:23:02 +02:00
115 lines
1.9 KiB
Go
115 lines
1.9 KiB
Go
package core
|
|
|
|
import (
|
|
"github.com/pkg/errors"
|
|
"io"
|
|
)
|
|
|
|
type (
|
|
CloseFunc func()
|
|
|
|
Scope struct {
|
|
closed bool
|
|
vars map[string]Value
|
|
parent *Scope
|
|
children []*Scope
|
|
}
|
|
)
|
|
|
|
func NewRootScope() (*Scope, CloseFunc) {
|
|
scope := NewScope(nil)
|
|
|
|
return scope, func() {
|
|
scope.close()
|
|
}
|
|
}
|
|
|
|
func NewScope(parent *Scope) *Scope {
|
|
return &Scope{
|
|
closed: false,
|
|
vars: make(map[string]Value),
|
|
parent: parent,
|
|
children: make([]*Scope, 0, 5),
|
|
}
|
|
}
|
|
|
|
func (s *Scope) SetVariable(name string, val Value) error {
|
|
_, exists := s.vars[name]
|
|
|
|
// it already has been declared in the current scope
|
|
if exists {
|
|
return errors.Wrapf(ErrNotUnique, "variable is already declared '%s'", name)
|
|
}
|
|
|
|
s.vars[name] = val
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *Scope) HasVariable(name string) bool {
|
|
_, exists := s.vars[name]
|
|
|
|
// does not exist in the current scope
|
|
// try to find in a parent scope
|
|
if !exists {
|
|
if s.parent != nil {
|
|
return s.parent.HasVariable(name)
|
|
}
|
|
}
|
|
|
|
return exists
|
|
}
|
|
|
|
func (s *Scope) GetVariable(name string) (Value, error) {
|
|
out, exists := s.vars[name]
|
|
|
|
// does not exist in the current scope
|
|
// try to find in the parent scope
|
|
if !exists {
|
|
if s.parent != nil {
|
|
return s.parent.GetVariable(name)
|
|
}
|
|
|
|
return nil, errors.Wrapf(ErrNotFound, "variable '%s'", name)
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
func (s *Scope) Fork() *Scope {
|
|
child := NewScope(s)
|
|
|
|
s.children = append(s.children, child)
|
|
|
|
return child
|
|
}
|
|
|
|
func (s *Scope) close() error {
|
|
if s.closed {
|
|
return errors.Wrap(ErrInvalidOperation, "scope is already closed")
|
|
}
|
|
|
|
s.closed = true
|
|
|
|
// close all active child scopes
|
|
for _, c := range s.children {
|
|
c.close()
|
|
}
|
|
|
|
// do clean up
|
|
// if some of the variables implements io.Closer interface
|
|
// we need to close them
|
|
for _, v := range s.vars {
|
|
closer, ok := v.(io.Closer)
|
|
|
|
if ok {
|
|
closer.Close()
|
|
}
|
|
}
|
|
|
|
s.children = nil
|
|
s.vars = nil
|
|
|
|
return nil
|
|
}
|