Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

use StringDictLike instead of StringDict to provide predefined symbols #464

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions repl/repl.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ var interrupted = make(chan os.Signal, 1)
// variable named "context" to a context.Context that is cancelled by a
// SIGINT (Control-C). Client-supplied global functions may use this
// context to make long-running operations interruptable.
//
func REPL(thread *starlark.Thread, globals starlark.StringDict) {
signal.Notify(interrupted, os.Interrupt)
defer signal.Stop(interrupted)
Expand Down Expand Up @@ -152,15 +151,15 @@ func PrintError(err error) {
// MakeLoad returns a simple sequential implementation of module loading
// suitable for use in the REPL.
// Each function returned by MakeLoad accesses a distinct private cache.
func MakeLoad() func(thread *starlark.Thread, module string) (starlark.StringDict, error) {
func MakeLoad() func(thread *starlark.Thread, module string) (starlark.StringDictLike, error) {
type entry struct {
globals starlark.StringDict
err error
}

var cache = make(map[string]*entry)

return func(thread *starlark.Thread, module string) (starlark.StringDict, error) {
return func(thread *starlark.Thread, module string) (starlark.StringDictLike, error) {
e, ok := cache[module]
if e == nil {
if ok {
Expand Down
31 changes: 24 additions & 7 deletions starlark/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ import (
"go.starlark.net/syntax"
)

// StringDictLike is an interface for a dictionary of Starlark values.
type StringDictLike interface {
Get(string) Value
Has(string) bool
Keys() []string
}

// A Thread contains the state of a Starlark thread,
// such as its call stack and thread-local storage.
// The Thread is threaded throughout the evaluator.
Expand All @@ -45,7 +52,7 @@ type Thread struct {
// The error message need not include the module name.
//
// See example_test.go for some example implementations of Load.
Load func(thread *Thread, module string) (StringDict, error)
Load func(thread *Thread, module string) (StringDictLike, error)

// OnMaxSteps is called when the thread reaches the limit set by SetMaxExecutionSteps.
// The default behavior is to call thread.Cancel("too many steps").
Expand Down Expand Up @@ -184,6 +191,9 @@ func (d StringDict) Freeze() {
// Has reports whether the dictionary contains the specified key.
func (d StringDict) Has(key string) bool { _, ok := d[key]; return ok }

// Get returns the value associated with the specified key.
func (d StringDict) Get(key string) Value { return d[key] }

// A frame records a call to a Starlark function (including module toplevel)
// or a built-in function or method.
type frame struct {
Expand Down Expand Up @@ -342,9 +352,16 @@ func (prog *Program) Write(out io.Writer) error {
//
// If ExecFile fails during evaluation, it returns an *EvalError
// containing a backtrace.
func ExecFile(thread *Thread, filename string, src interface{}, predeclared StringDict) (StringDict, error) {
func ExecFile(thread *Thread, filename string, src interface{}, predeclared StringDictLike) (StringDict, error) {
var has func(string) bool
if predeclared != nil {
has = predeclared.Has
} else {
has = func(string) bool { return false }
}

// Parse, resolve, and compile a Starlark source file.
_, mod, err := SourceProgram(filename, src, predeclared.Has)
_, mod, err := SourceProgram(filename, src, has)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -418,7 +435,7 @@ func CompiledProgram(in io.Reader) (*Program, error) {
// Init creates a set of global variables for the program,
// executes the toplevel code of the specified program,
// and returns a new, unfrozen dictionary of the globals.
func (prog *Program) Init(thread *Thread, predeclared StringDict) (StringDict, error) {
func (prog *Program) Init(thread *Thread, predeclared StringDictLike) (StringDict, error) {
toplevel := makeToplevelFunction(prog.compiled, predeclared)

_, err := Call(thread, toplevel, nil, nil)
Expand Down Expand Up @@ -479,7 +496,7 @@ func ExecREPLChunk(f *syntax.File, thread *Thread, globals StringDict) error {
return err
}

func makeToplevelFunction(prog *compile.Program, predeclared StringDict) *Function {
func makeToplevelFunction(prog *compile.Program, predeclared StringDictLike) *Function {
// Create the Starlark value denoted by each program constant c.
constants := make([]Value, len(prog.Constants))
for i, c := range prog.Constants {
Expand Down Expand Up @@ -556,7 +573,7 @@ func EvalExpr(thread *Thread, expr syntax.Expr, env StringDict) (Value, error) {

// ExprFunc returns a no-argument function
// that evaluates the expression whose source is src.
func ExprFunc(filename string, src interface{}, env StringDict) (*Function, error) {
func ExprFunc(filename string, src interface{}, env StringDictLike) (*Function, error) {
expr, err := syntax.ParseExpr(filename, src, 0)
if err != nil {
return nil, err
Expand All @@ -565,7 +582,7 @@ func ExprFunc(filename string, src interface{}, env StringDict) (*Function, erro
}

// makeExprFunc returns a no-argument function whose body is expr.
func makeExprFunc(expr syntax.Expr, env StringDict) (*Function, error) {
func makeExprFunc(expr syntax.Expr, env StringDictLike) (*Function, error) {
locals, err := resolve.Expr(expr, env.Has, Universe.Has)
if err != nil {
return nil, err
Expand Down
4 changes: 2 additions & 2 deletions starlark/eval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ func (it *fibIterator) Next(p *starlark.Value) bool {
func (it *fibIterator) Done() {}

// load implements the 'load' operation as used in the evaluator tests.
func load(thread *starlark.Thread, module string) (starlark.StringDict, error) {
func load(thread *starlark.Thread, module string) (starlark.StringDictLike, error) {
if module == "assert.star" {
return starlarktest.LoadAssertModule()
}
Expand Down Expand Up @@ -588,7 +588,7 @@ def f(x):
f(0)
`
thread := new(starlark.Thread)
thread.Load = func(t *starlark.Thread, module string) (starlark.StringDict, error) {
thread.Load = func(t *starlark.Thread, module string) (starlark.StringDictLike, error) {
return starlark.ExecFile(new(starlark.Thread), module, loadedSrc, nil)
}
_, err := starlark.ExecFile(thread, "root.star", src, nil)
Expand Down
8 changes: 4 additions & 4 deletions starlark/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ func ExampleThread_Load_sequential() {

cache := make(map[string]*entry)

var load func(_ *starlark.Thread, module string) (starlark.StringDict, error)
load = func(_ *starlark.Thread, module string) (starlark.StringDict, error) {
var load func(_ *starlark.Thread, module string) (starlark.StringDictLike, error)
load = func(_ *starlark.Thread, module string) (starlark.StringDictLike, error) {
e, ok := cache[module]
if e == nil {
if ok {
Expand All @@ -120,7 +120,7 @@ func ExampleThread_Load_sequential() {
if err != nil {
log.Fatal(err)
}
fmt.Println(globals["c"])
fmt.Println(globals.Get("c"))

// Output:
// "Hello, world!"
Expand Down Expand Up @@ -267,7 +267,7 @@ func (c *cache) doLoad(cc *cycleChecker, module string) (starlark.StringDict, er
thread := &starlark.Thread{
Name: "exec " + module,
Print: func(_ *starlark.Thread, msg string) { fmt.Println(msg) },
Load: func(_ *starlark.Thread, module string) (starlark.StringDict, error) {
Load: func(_ *starlark.Thread, module string) (starlark.StringDictLike, error) {
// Tunnel the cycle-checker state for this "thread of loading".
return c.get(cc, module)
},
Expand Down
7 changes: 3 additions & 4 deletions starlark/interp.go
Original file line number Diff line number Diff line change
Expand Up @@ -575,15 +575,14 @@ loop:

for i := 0; i < n; i++ {
from := string(stack[sp-1-i].(String))
v, ok := dict[from]
if !ok {
if ok := dict.Has(from); !ok {
err = fmt.Errorf("load: name %s not found in module %s", from, module)
if n := spell.Nearest(from, dict.Keys()); n != "" {
err = fmt.Errorf("%s (did you mean %s?)", err, n)
}
break loop
}
stack[sp-1-i] = v
stack[sp-1-i] = dict.Get(from)
}

case compile.SETLOCAL:
Expand Down Expand Up @@ -640,7 +639,7 @@ loop:

case compile.PREDECLARED:
name := f.Prog.Names[arg]
x := fn.module.predeclared[name]
x := fn.module.predeclared.Get(name)
if x == nil {
err = fmt.Errorf("internal error: predeclared variable %s is uninitialized", name)
break loop
Expand Down
2 changes: 1 addition & 1 deletion starlark/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -691,7 +691,7 @@ type Function struct {
// All functions in the same program share a module.
type module struct {
program *compile.Program
predeclared StringDict
predeclared StringDictLike
globals []Value
constants []Value
}
Expand Down
2 changes: 1 addition & 1 deletion starlarkstruct/struct_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func Test(t *testing.T) {
}

// load implements the 'load' operation as used in the evaluator tests.
func load(thread *starlark.Thread, module string) (starlark.StringDict, error) {
func load(thread *starlark.Thread, module string) (starlark.StringDictLike, error) {
if module == "assert.star" {
return starlarktest.LoadAssertModule()
}
Expand Down