Go: store scopes and globals as maps

This commit is contained in:
Přemysl Eric Janouch 2018-10-10 19:49:17 +02:00
parent b210216c71
commit 563e8ba069
Signed by: p
GPG Key ID: A0420B94F92B9493
2 changed files with 18 additions and 33 deletions

View File

@ -48,8 +48,7 @@ func complete(L *ell.Ell, line string, pos int) (
head, line = line[:lastSpace+1], line[lastSpace+1:] head, line = line[:lastSpace+1], line[lastSpace+1:]
} }
for _, v := range L.Globals { for name := range L.Globals {
name := v.List[0].String
if strings.HasPrefix(strings.ToLower(name), line) { if strings.HasPrefix(strings.ToLower(name), line) {
completions = append(completions, name) completions = append(completions, name)
} }

View File

@ -538,8 +538,8 @@ type Handler func(*Ell, []V, *[]V) bool
// Ell is an interpreter context. // Ell is an interpreter context.
type Ell struct { type Ell struct {
Globals []*V // list of global variables Globals map[string]V // list of global variables
scopes [][]*V // dynamic scopes from the newest scopes []map[string]V // dynamic scopes from the newest
Native map[string]Handler // maps strings to Go functions Native map[string]Handler // maps strings to Go functions
Error string // error information Error string // error information
@ -548,7 +548,8 @@ type Ell struct {
// New returns a new interpreter context ready for program execution. // New returns a new interpreter context ready for program execution.
func New() *Ell { func New() *Ell {
return &Ell{ return &Ell{
Native: make(map[string]Handler), Globals: make(map[string]V),
Native: make(map[string]Handler),
} }
} }
@ -561,26 +562,15 @@ func scopeFind(scope []*V, name string) int {
return -1 return -1
} }
// TODO: This is O(n), let's just make them a map[string]*V.
func scopePrepend(scope []*V, name string, v *V) []*V {
key := NewString(name)
pair := NewList([]V{*key, *v})
result := make([]*V, len(scope)+1)
copy(result[1:], scope)
result[0] = pair
return result
}
// Get retrieves a value by name from the scope or from global variables. // Get retrieves a value by name from the scope or from global variables.
func (ell *Ell) Get(name string) *V { func (ell *Ell) Get(name string) *V {
for _, scope := range ell.scopes { for _, scope := range ell.scopes {
if place := scopeFind(scope, name); place >= 0 { if v, ok := scope[name]; ok {
return &scope[place].List[1] return &v
} }
} }
if place := scopeFind(ell.Globals, name); place >= 0 { if v, ok := ell.Globals[name]; ok {
return &ell.Globals[place].List[1] return &v
} }
return nil return nil
} }
@ -588,18 +578,14 @@ func (ell *Ell) Get(name string) *V {
// Set sets a value by name in the scope or in global variables. // Set sets a value by name in the scope or in global variables.
func (ell *Ell) Set(name string, v *V) { func (ell *Ell) Set(name string, v *V) {
for _, scope := range ell.scopes { for _, scope := range ell.scopes {
if place := scopeFind(scope, name); place >= 0 { if _, ok := scope[name]; ok {
scope[place].List[1] = *v scope[name] = *v
return return
} }
} }
// Variables only get deleted by "arg" or from the global scope. // Variables only get deleted by "arg" or from the global scope.
if place := scopeFind(ell.Globals, name); place >= 0 { ell.Globals[name] = *v
ell.Globals[place].List[1] = *v
} else {
ell.Globals = scopePrepend(ell.Globals, name, v)
}
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -714,10 +700,10 @@ func (ell *Ell) evalStatement(statement *V, result *[]V) bool {
return false return false
} }
func argsToScope(args []V) []*V { func argsToScope(args []V) map[string]V {
scope := scopePrepend(nil, "args", NewList(args)) scope := map[string]V{"args": *NewList(args)}
for i, arg := range args { for i, arg := range args {
scope = scopePrepend(scope, fmt.Sprintf("%d", i+1), arg.Clone()) scope[fmt.Sprintf("%d", i+1)] = *arg.Clone()
} }
return scope return scope
} }
@ -726,7 +712,7 @@ func argsToScope(args []V) []*V {
// eats args. // eats args.
func (ell *Ell) EvalBlock(body []V, args []V, result *[]V) bool { func (ell *Ell) EvalBlock(body []V, args []V, result *[]V) bool {
// TODO: This is O(n), let's just rather append and traverse in reverse. // TODO: This is O(n), let's just rather append and traverse in reverse.
newScopes := make([][]*V, len(ell.scopes)+1) newScopes := make([]map[string]V, len(ell.scopes)+1)
newScopes[0] = argsToScope(args) newScopes[0] = argsToScope(args)
copy(newScopes[1:], ell.scopes) copy(newScopes[1:], ell.scopes)
ell.scopes = newScopes ell.scopes = newScopes
@ -796,12 +782,12 @@ func fnLocal(ell *Ell, args []V, result *[]V) bool {
} }
// Duplicates or non-strings don't really matter to us, user's problem. // Duplicates or non-strings don't really matter to us, user's problem.
scope := &ell.scopes[0] scope := ell.scopes[0]
values := args[1:] values := args[1:]
for _, name := range args[0].List { for _, name := range args[0].List {
*scope = scopePrepend(*scope, name.String, values[0].Clone())
if len(values) > 0 { if len(values) > 0 {
scope[name.String] = *values[0].Clone()
values = values[1:] values = values[1:]
} }
} }