blob: d0ad91fbec85aa806fac3a0892da6fc71a1738f5 [file] [log] [blame]
Alan Donovan312d1a52017-10-02 10:10:28 -04001// Copyright 2017 The Bazel Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
Alan Donovane3deafe2018-10-23 11:05:09 -04005package starlark
Alan Donovan312d1a52017-10-02 10:10:28 -04006
7import (
Alan Donovan312d1a52017-10-02 10:10:28 -04008 "fmt"
alandonovan93f3e0c2018-03-30 10:42:28 -04009 "io"
alandonovan29f91002018-12-03 16:53:52 -050010 "io/ioutil"
alandonovan8c4023c2018-04-02 13:08:46 -040011 "log"
alandonovan8c4023c2018-04-02 13:08:46 -040012 "math/big"
Alan Donovan312d1a52017-10-02 10:10:28 -040013 "sort"
14 "strings"
alandonovan949cc6f2020-08-21 10:29:38 -040015 "sync/atomic"
alandonovan40b4ab62019-04-03 16:41:37 -040016 "time"
Alan Donovan312d1a52017-10-02 10:10:28 -040017 "unicode"
18 "unicode/utf8"
alandonovan949cc6f2020-08-21 10:29:38 -040019 "unsafe"
Alan Donovan312d1a52017-10-02 10:10:28 -040020
Alan Donovan6beab7e2018-10-31 17:53:09 -040021 "go.starlark.net/internal/compile"
alandonovan6afa1bb2019-02-06 17:49:05 -050022 "go.starlark.net/internal/spell"
Alan Donovan6beab7e2018-10-31 17:53:09 -040023 "go.starlark.net/resolve"
24 "go.starlark.net/syntax"
Alan Donovan312d1a52017-10-02 10:10:28 -040025)
26
Alan Donovane3deafe2018-10-23 11:05:09 -040027// A Thread contains the state of a Starlark thread,
Alan Donovan312d1a52017-10-02 10:10:28 -040028// such as its call stack and thread-local storage.
Alan Donovan7b0e4c32017-10-09 11:03:36 -040029// The Thread is threaded throughout the evaluator.
Alan Donovan312d1a52017-10-02 10:10:28 -040030type Thread struct {
alandonovan2c1f3622018-12-17 13:10:16 -050031 // Name is an optional name that describes the thread, for debugging.
32 Name string
33
alandonovand9868e92019-04-19 14:47:26 -040034 // stack is the stack of (internal) call frames.
alandonovan95b27832019-05-06 10:57:34 -040035 stack []*frame
Alan Donovan312d1a52017-10-02 10:10:28 -040036
Alan Donovane3deafe2018-10-23 11:05:09 -040037 // Print is the client-supplied implementation of the Starlark
Alan Donovan312d1a52017-10-02 10:10:28 -040038 // 'print' function. If nil, fmt.Fprintln(os.Stderr, msg) is
39 // used instead.
40 Print func(thread *Thread, msg string)
41
42 // Load is the client-supplied implementation of module loading.
43 // Repeated calls with the same module name must return the same
44 // module environment or error.
alandonovan062beb32018-02-26 10:41:09 -050045 // The error message need not include the module name.
Alan Donovan7b0e4c32017-10-09 11:03:36 -040046 //
47 // See example_test.go for some example implementations of Load.
Alan Donovan312d1a52017-10-02 10:10:28 -040048 Load func(thread *Thread, module string) (StringDict, error)
49
alandonovan949cc6f2020-08-21 10:29:38 -040050 // steps counts abstract computation steps executed by this thread.
51 steps, maxSteps uint64
52
53 // cancelReason records the reason from the first call to Cancel.
54 cancelReason *string
55
alandonovanc15f32e2018-03-05 14:44:10 -050056 // locals holds arbitrary "thread-local" Go values belonging to the client.
Alan Donovane3deafe2018-10-23 11:05:09 -040057 // They are accessible to the client but not to any Starlark program.
Alan Donovan312d1a52017-10-02 10:10:28 -040058 locals map[string]interface{}
alandonovan40b4ab62019-04-03 16:41:37 -040059
60 // proftime holds the accumulated execution time since the last profile event.
61 proftime time.Duration
Alan Donovan312d1a52017-10-02 10:10:28 -040062}
63
alandonovan949cc6f2020-08-21 10:29:38 -040064// ExecutionSteps returns a count of abstract computation steps executed
65// by this thread. It is incremented by the interpreter. It may be used
66// as a measure of the approximate cost of Starlark execution, by
67// computing the difference in its value before and after a computation.
68//
69// The precise meaning of "step" is not specified and may change.
70func (thread *Thread) ExecutionSteps() uint64 {
71 return thread.steps
72}
73
74// SetMaxExecutionSteps sets a limit on the number of Starlark
75// computation steps that may be executed by this thread. If the
76// thread's step counter exceeds this limit, the interpreter calls
77// thread.Cancel("too many steps").
78func (thread *Thread) SetMaxExecutionSteps(max uint64) {
79 thread.maxSteps = max
80}
81
82// Cancel causes execution of Starlark code in the specified thread to
83// promptly fail with an EvalError that includes the specified reason.
84// There may be a delay before the interpreter observes the cancellation
85// if the thread is currently in a call to a built-in function.
86//
87// Cancellation cannot be undone.
88//
89// Unlike most methods of Thread, it is safe to call Cancel from any
90// goroutine, even if the thread is actively executing.
91func (thread *Thread) Cancel(reason string) {
92 // Atomically set cancelReason, preserving earlier reason if any.
93 atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&thread.cancelReason)), nil, unsafe.Pointer(&reason))
94}
95
Alan Donovan312d1a52017-10-02 10:10:28 -040096// SetLocal sets the thread-local value associated with the specified key.
97// It must not be called after execution begins.
98func (thread *Thread) SetLocal(key string, value interface{}) {
99 if thread.locals == nil {
100 thread.locals = make(map[string]interface{})
101 }
102 thread.locals[key] = value
103}
104
105// Local returns the thread-local value associated with the specified key.
106func (thread *Thread) Local(key string) interface{} {
107 return thread.locals[key]
108}
109
alandonovand9868e92019-04-19 14:47:26 -0400110// CallFrame returns a copy of the specified frame of the callstack.
111// It should only be used in built-ins called from Starlark code.
112// Depth 0 means the frame of the built-in itself, 1 is its caller, and so on.
113//
114// It is equivalent to CallStack().At(depth), but more efficient.
115func (thread *Thread) CallFrame(depth int) CallFrame {
alandonovan95b27832019-05-06 10:57:34 -0400116 return thread.frameAt(depth).asCallFrame()
alandonovand9868e92019-04-19 14:47:26 -0400117}
alandonovancc7dbc22018-07-02 12:30:24 -0400118
alandonovan95b27832019-05-06 10:57:34 -0400119func (thread *Thread) frameAt(depth int) *frame {
alandonovand9868e92019-04-19 14:47:26 -0400120 return thread.stack[len(thread.stack)-1-depth]
121}
122
123// CallStack returns a new slice containing the thread's stack of call frames.
124func (thread *Thread) CallStack() CallStack {
125 frames := make([]CallFrame, len(thread.stack))
126 for i, fr := range thread.stack {
alandonovan95b27832019-05-06 10:57:34 -0400127 frames[i] = fr.asCallFrame()
alandonovand9868e92019-04-19 14:47:26 -0400128 }
129 return frames
130}
131
132// CallStackDepth returns the number of frames in the current call stack.
133func (thread *Thread) CallStackDepth() int { return len(thread.stack) }
Alan Donovan312d1a52017-10-02 10:10:28 -0400134
135// A StringDict is a mapping from names to values, and represents
136// an environment such as the global variables of a module.
Alan Donovane3deafe2018-10-23 11:05:09 -0400137// It is not a true starlark.Value.
Alan Donovan312d1a52017-10-02 10:10:28 -0400138type StringDict map[string]Value
139
alandonovan0ed7e5b2019-01-03 16:11:58 -0500140// Keys returns a new sorted slice of d's keys.
141func (d StringDict) Keys() []string {
Alan Donovan312d1a52017-10-02 10:10:28 -0400142 names := make([]string, 0, len(d))
143 for name := range d {
144 names = append(names, name)
145 }
146 sort.Strings(names)
alandonovan0ed7e5b2019-01-03 16:11:58 -0500147 return names
148}
Alan Donovan312d1a52017-10-02 10:10:28 -0400149
alandonovan0ed7e5b2019-01-03 16:11:58 -0500150func (d StringDict) String() string {
Josh Bleecher Snyder8cb25c82019-03-01 14:24:35 -0800151 buf := new(strings.Builder)
Alan Donovan312d1a52017-10-02 10:10:28 -0400152 buf.WriteByte('{')
153 sep := ""
alandonovan0ed7e5b2019-01-03 16:11:58 -0500154 for _, name := range d.Keys() {
Alan Donovan312d1a52017-10-02 10:10:28 -0400155 buf.WriteString(sep)
156 buf.WriteString(name)
157 buf.WriteString(": ")
Josh Bleecher Snyder8cb25c82019-03-01 14:24:35 -0800158 writeValue(buf, d[name], nil)
Alan Donovan312d1a52017-10-02 10:10:28 -0400159 sep = ", "
160 }
161 buf.WriteByte('}')
162 return buf.String()
163}
164
165func (d StringDict) Freeze() {
166 for _, v := range d {
167 v.Freeze()
168 }
169}
170
alandonovan631f7b52017-10-11 13:04:47 -0400171// Has reports whether the dictionary contains the specified key.
172func (d StringDict) Has(key string) bool { _, ok := d[key]; return ok }
Alan Donovan312d1a52017-10-02 10:10:28 -0400173
alandonovan95b27832019-05-06 10:57:34 -0400174// A frame records a call to a Starlark function (including module toplevel)
alandonovancc7dbc22018-07-02 12:30:24 -0400175// or a built-in function or method.
alandonovan95b27832019-05-06 10:57:34 -0400176type frame struct {
alandonovand9868e92019-04-19 14:47:26 -0400177 callable Callable // current function (or toplevel) or built-in
178 pc uint32 // program counter (Starlark frames only)
alandonovan95b27832019-05-06 10:57:34 -0400179 locals []Value // local variables (Starlark frames only)
alandonovand9868e92019-04-19 14:47:26 -0400180 spanStart int64 // start time of current profiler span
Alan Donovan312d1a52017-10-02 10:10:28 -0400181}
182
Alan Donovan312d1a52017-10-02 10:10:28 -0400183// Position returns the source position of the current point of execution in this frame.
alandonovan95b27832019-05-06 10:57:34 -0400184func (fr *frame) Position() syntax.Position {
alandonovan4a642672018-12-07 17:21:53 -0500185 switch c := fr.callable.(type) {
186 case *Function:
187 // Starlark function
alandonovan40b4ab62019-04-03 16:41:37 -0400188 return c.funcode.Position(fr.pc)
189 case callableWithPosition:
alandonovan4a642672018-12-07 17:21:53 -0500190 // If a built-in Callable defines
191 // a Position method, use it.
192 return c.Position()
alandonovancc7dbc22018-07-02 12:30:24 -0400193 }
alandonovan2494ae92019-04-04 15:38:05 -0400194 return syntax.MakePosition(&builtinFilename, 0, 0)
alandonovan93f3e0c2018-03-30 10:42:28 -0400195}
Alan Donovan312d1a52017-10-02 10:10:28 -0400196
alandonovancc7dbc22018-07-02 12:30:24 -0400197var builtinFilename = "<builtin>"
198
199// Function returns the frame's function or built-in.
alandonovan95b27832019-05-06 10:57:34 -0400200func (fr *frame) Callable() Callable { return fr.callable }
Alan Donovan312d1a52017-10-02 10:10:28 -0400201
alandonovand9868e92019-04-19 14:47:26 -0400202// A CallStack is a stack of call frames, outermost first.
203type CallStack []CallFrame
204
205// At returns a copy of the frame at depth i.
206// At(0) returns the topmost frame.
207func (stack CallStack) At(i int) CallFrame { return stack[len(stack)-1-i] }
208
209// Pop removes and returns the topmost frame.
210func (stack *CallStack) Pop() CallFrame {
211 last := len(*stack) - 1
212 top := (*stack)[last]
213 *stack = (*stack)[:last]
214 return top
215}
216
217// String returns a user-friendly description of the stack.
218func (stack CallStack) String() string {
219 out := new(strings.Builder)
alandonovana5c0cc42020-11-18 12:36:49 -0500220 if len(stack) > 0 {
221 fmt.Fprintf(out, "Traceback (most recent call last):\n")
222 }
alandonovand9868e92019-04-19 14:47:26 -0400223 for _, fr := range stack {
224 fmt.Fprintf(out, " %s: in %s\n", fr.Pos, fr.Name)
225 }
226 return out.String()
227}
228
229// An EvalError is a Starlark evaluation error and
230// a copy of the thread's stack at the moment of the error.
Alan Donovan312d1a52017-10-02 10:10:28 -0400231type EvalError struct {
alandonovand9868e92019-04-19 14:47:26 -0400232 Msg string
233 CallStack CallStack
Nick Santos58de16f2019-10-18 17:42:35 -0400234 cause error
alandonovand9868e92019-04-19 14:47:26 -0400235}
236
237// A CallFrame represents the function name and current
238// position of execution of an enclosing call frame.
239type CallFrame struct {
240 Name string
241 Pos syntax.Position
242}
243
alandonovan95b27832019-05-06 10:57:34 -0400244func (fr *frame) asCallFrame() CallFrame {
alandonovand9868e92019-04-19 14:47:26 -0400245 return CallFrame{
246 Name: fr.Callable().Name(),
247 Pos: fr.Position(),
248 }
249}
250
251func (thread *Thread) evalError(err error) *EvalError {
252 return &EvalError{
253 Msg: err.Error(),
254 CallStack: thread.CallStack(),
Nick Santos58de16f2019-10-18 17:42:35 -0400255 cause: err,
alandonovand9868e92019-04-19 14:47:26 -0400256 }
Alan Donovan312d1a52017-10-02 10:10:28 -0400257}
258
259func (e *EvalError) Error() string { return e.Msg }
260
261// Backtrace returns a user-friendly error message describing the stack
262// of calls that led to this error.
263func (e *EvalError) Backtrace() string {
alandonovana5c0cc42020-11-18 12:36:49 -0500264 // If the topmost stack frame is a built-in function,
265 // remove it from the stack and add print "Error in fn:".
266 stack := e.CallStack
267 suffix := ""
268 if last := len(stack) - 1; last >= 0 && stack[last].Pos.Filename() == builtinFilename {
269 suffix = " in " + stack[last].Name
270 stack = stack[:last]
271 }
272 return fmt.Sprintf("%sError%s: %s", stack, suffix, e.Msg)
Alan Donovan312d1a52017-10-02 10:10:28 -0400273}
274
Nick Santos58de16f2019-10-18 17:42:35 -0400275func (e *EvalError) Unwrap() error { return e.cause }
276
Alan Donovane3deafe2018-10-23 11:05:09 -0400277// A Program is a compiled Starlark program.
alandonovan93f3e0c2018-03-30 10:42:28 -0400278//
279// Programs are immutable, and contain no Values.
280// A Program may be created by parsing a source file (see SourceProgram)
281// or by loading a previously saved compiled program (see CompiledProgram).
282type Program struct {
283 compiled *compile.Program
284}
285
286// CompilerVersion is the version number of the protocol for compiled
287// files. Applications must not run programs compiled by one version
288// with an interpreter at another version, and should thus incorporate
289// the compiler version into the cache key when reusing compiled code.
290const CompilerVersion = compile.Version
291
alandonovan2c1f3622018-12-17 13:10:16 -0500292// Filename returns the name of the file from which this program was loaded.
293func (prog *Program) Filename() string { return prog.compiled.Toplevel.Pos.Filename() }
294
295func (prog *Program) String() string { return prog.Filename() }
296
alandonovan93f3e0c2018-03-30 10:42:28 -0400297// NumLoads returns the number of load statements in the compiled program.
298func (prog *Program) NumLoads() int { return len(prog.compiled.Loads) }
299
300// Load(i) returns the name and position of the i'th module directly
301// loaded by this one, where 0 <= i < NumLoads().
302// The name is unresolved---exactly as it appears in the source.
303func (prog *Program) Load(i int) (string, syntax.Position) {
304 id := prog.compiled.Loads[i]
305 return id.Name, id.Pos
306}
307
308// WriteTo writes the compiled module to the specified output stream.
alandonovan29f91002018-12-03 16:53:52 -0500309func (prog *Program) Write(out io.Writer) error {
310 data := prog.compiled.Encode()
311 _, err := out.Write(data)
312 return err
313}
alandonovan93f3e0c2018-03-30 10:42:28 -0400314
Alan Donovane3deafe2018-10-23 11:05:09 -0400315// ExecFile parses, resolves, and executes a Starlark file in the
Alan Donovan312d1a52017-10-02 10:10:28 -0400316// specified global environment, which may be modified during execution.
317//
Alan Donovane3deafe2018-10-23 11:05:09 -0400318// Thread is the state associated with the Starlark thread.
alandonovan93f3e0c2018-03-30 10:42:28 -0400319//
320// The filename and src parameters are as for syntax.Parse:
321// filename is the name of the file to execute,
322// and the name that appears in error messages;
323// src is an optional source of bytes to use
324// instead of filename.
325//
326// predeclared defines the predeclared names specific to this module.
327// Execution does not modify this dictionary, though it may mutate
328// its values.
Alan Donovan312d1a52017-10-02 10:10:28 -0400329//
330// If ExecFile fails during evaluation, it returns an *EvalError
331// containing a backtrace.
alandonovana1b28d82018-03-13 10:59:24 -0400332func ExecFile(thread *Thread, filename string, src interface{}, predeclared StringDict) (StringDict, error) {
Alan Donovane3deafe2018-10-23 11:05:09 -0400333 // Parse, resolve, and compile a Starlark source file.
alandonovan93f3e0c2018-03-30 10:42:28 -0400334 _, mod, err := SourceProgram(filename, src, predeclared.Has)
Alan Donovan312d1a52017-10-02 10:10:28 -0400335 if err != nil {
alandonovana1b28d82018-03-13 10:59:24 -0400336 return nil, err
Alan Donovan312d1a52017-10-02 10:10:28 -0400337 }
338
alandonovan93f3e0c2018-03-30 10:42:28 -0400339 g, err := mod.Init(thread, predeclared)
340 g.Freeze()
341 return g, err
342}
343
344// SourceProgram produces a new program by parsing, resolving,
Alan Donovane3deafe2018-10-23 11:05:09 -0400345// and compiling a Starlark source file.
alandonovan93f3e0c2018-03-30 10:42:28 -0400346// On success, it returns the parsed file and the compiled program.
347// The filename and src parameters are as for syntax.Parse.
348//
349// The isPredeclared predicate reports whether a name is
350// a pre-declared identifier of the current module.
351// Its typical value is predeclared.Has,
352// where predeclared is a StringDict of pre-declared values.
353func SourceProgram(filename string, src interface{}, isPredeclared func(string) bool) (*syntax.File, *Program, error) {
354 f, err := syntax.Parse(filename, src, 0)
355 if err != nil {
356 return nil, nil, err
357 }
alandonovan30e71c62019-01-04 13:48:12 -0500358 prog, err := FileProgram(f, isPredeclared)
359 return f, prog, err
360}
alandonovan93f3e0c2018-03-30 10:42:28 -0400361
alandonovan30e71c62019-01-04 13:48:12 -0500362// FileProgram produces a new program by resolving,
363// and compiling the Starlark source file syntax tree.
364// On success, it returns the compiled program.
365//
366// Resolving a syntax tree mutates it.
367// Do not call FileProgram more than once on the same file.
368//
369// The isPredeclared predicate reports whether a name is
370// a pre-declared identifier of the current module.
371// Its typical value is predeclared.Has,
372// where predeclared is a StringDict of pre-declared values.
373func FileProgram(f *syntax.File, isPredeclared func(string) bool) (*Program, error) {
alandonovan93f3e0c2018-03-30 10:42:28 -0400374 if err := resolve.File(f, isPredeclared, Universe.Has); err != nil {
alandonovan30e71c62019-01-04 13:48:12 -0500375 return nil, err
alandonovan93f3e0c2018-03-30 10:42:28 -0400376 }
377
alandonovan2c1f3622018-12-17 13:10:16 -0500378 var pos syntax.Position
379 if len(f.Stmts) > 0 {
380 pos = syntax.Start(f.Stmts[0])
381 } else {
alandonovan30e71c62019-01-04 13:48:12 -0500382 pos = syntax.MakePosition(&f.Path, 1, 1)
alandonovan2c1f3622018-12-17 13:10:16 -0500383 }
384
alandonovan6ddc71c2019-06-04 09:08:55 -0400385 module := f.Module.(*resolve.Module)
386 compiled := compile.File(f.Stmts, pos, "<toplevel>", module.Locals, module.Globals)
alandonovan93f3e0c2018-03-30 10:42:28 -0400387
alandonovan30e71c62019-01-04 13:48:12 -0500388 return &Program{compiled}, nil
alandonovan93f3e0c2018-03-30 10:42:28 -0400389}
390
391// CompiledProgram produces a new program from the representation
392// of a compiled program previously saved by Program.Write.
393func CompiledProgram(in io.Reader) (*Program, error) {
alandonovan29f91002018-12-03 16:53:52 -0500394 data, err := ioutil.ReadAll(in)
395 if err != nil {
396 return nil, err
397 }
alandonovan2c1f3622018-12-17 13:10:16 -0500398 compiled, err := compile.DecodeProgram(data)
alandonovan93f3e0c2018-03-30 10:42:28 -0400399 if err != nil {
alandonovana1b28d82018-03-13 10:59:24 -0400400 return nil, err
Alan Donovan312d1a52017-10-02 10:10:28 -0400401 }
alandonovan2c1f3622018-12-17 13:10:16 -0500402 return &Program{compiled}, nil
Alan Donovan312d1a52017-10-02 10:10:28 -0400403}
404
alandonovan93f3e0c2018-03-30 10:42:28 -0400405// Init creates a set of global variables for the program,
406// executes the toplevel code of the specified program,
407// and returns a new, unfrozen dictionary of the globals.
408func (prog *Program) Init(thread *Thread, predeclared StringDict) (StringDict, error) {
alandonovan3ee16852019-05-28 15:56:13 -0400409 toplevel := makeToplevelFunction(prog.compiled, predeclared)
Alan Donovan7b0e4c32017-10-09 11:03:36 -0400410
alandonovanaeec83f2018-10-22 13:24:13 -0400411 _, err := Call(thread, toplevel, nil, nil)
alandonovan93f3e0c2018-03-30 10:42:28 -0400412
alandonovan30e71c62019-01-04 13:48:12 -0500413 // Convert the global environment to a map.
alandonovan93f3e0c2018-03-30 10:42:28 -0400414 // We return a (partial) map even in case of error.
415 return toplevel.Globals(), err
Alan Donovan7b0e4c32017-10-09 11:03:36 -0400416}
417
alandonovan28350e62019-10-21 14:58:36 -0400418// ExecREPLChunk compiles and executes file f in the specified thread
419// and global environment. This is a variant of ExecFile specialized to
420// the needs of a REPL, in which a sequence of input chunks, each
421// syntactically a File, manipulates the same set of module globals,
422// which are not frozen after execution.
423//
424// This function is intended to support only go.starlark.net/repl.
425// Its API stability is not guaranteed.
426func ExecREPLChunk(f *syntax.File, thread *Thread, globals StringDict) error {
427 var predeclared StringDict
428
429 // -- variant of FileProgram --
430
431 if err := resolve.REPLChunk(f, globals.Has, predeclared.Has, Universe.Has); err != nil {
432 return err
433 }
434
435 var pos syntax.Position
436 if len(f.Stmts) > 0 {
437 pos = syntax.Start(f.Stmts[0])
438 } else {
439 pos = syntax.MakePosition(&f.Path, 1, 1)
440 }
441
442 module := f.Module.(*resolve.Module)
443 compiled := compile.File(f.Stmts, pos, "<toplevel>", module.Locals, module.Globals)
444 prog := &Program{compiled}
445
446 // -- variant of Program.Init --
447
448 toplevel := makeToplevelFunction(prog.compiled, predeclared)
449
450 // Initialize module globals from parameter.
451 for i, id := range prog.compiled.Globals {
452 if v := globals[id.Name]; v != nil {
453 toplevel.module.globals[i] = v
454 }
455 }
456
457 _, err := Call(thread, toplevel, nil, nil)
458
459 // Reflect changes to globals back to parameter, even after an error.
460 for i, id := range prog.compiled.Globals {
461 if v := toplevel.module.globals[i]; v != nil {
462 globals[id.Name] = v
463 }
464 }
465
466 return err
467}
468
alandonovan3ee16852019-05-28 15:56:13 -0400469func makeToplevelFunction(prog *compile.Program, predeclared StringDict) *Function {
Alan Donovane3deafe2018-10-23 11:05:09 -0400470 // Create the Starlark value denoted by each program constant c.
alandonovan3ee16852019-05-28 15:56:13 -0400471 constants := make([]Value, len(prog.Constants))
472 for i, c := range prog.Constants {
alandonovan8c4023c2018-04-02 13:08:46 -0400473 var v Value
474 switch c := c.(type) {
475 case int64:
476 v = MakeInt64(c)
477 case *big.Int:
Edward McFarlaned50186b2019-02-24 19:44:57 +0000478 v = MakeBigInt(c)
alandonovan8c4023c2018-04-02 13:08:46 -0400479 case string:
480 v = String(c)
alandonovanebe61bd2021-02-12 16:57:32 -0500481 case compile.Bytes:
482 v = Bytes(c)
alandonovan8c4023c2018-04-02 13:08:46 -0400483 case float64:
484 v = Float(c)
485 default:
Alessandro Arzilli688506e2019-08-19 16:15:39 +0200486 log.Panicf("unexpected constant %T: %v", c, c)
alandonovan8c4023c2018-04-02 13:08:46 -0400487 }
488 constants[i] = v
489 }
490
491 return &Function{
alandonovan3ee16852019-05-28 15:56:13 -0400492 funcode: prog.Toplevel,
493 module: &module{
494 program: prog,
495 predeclared: predeclared,
496 globals: make([]Value, len(prog.Globals)),
497 constants: constants,
498 },
alandonovan8c4023c2018-04-02 13:08:46 -0400499 }
500}
501
Alan Donovan312d1a52017-10-02 10:10:28 -0400502// Eval parses, resolves, and evaluates an expression within the
alandonovana1b28d82018-03-13 10:59:24 -0400503// specified (predeclared) environment.
Alan Donovan312d1a52017-10-02 10:10:28 -0400504//
alandonovana1b28d82018-03-13 10:59:24 -0400505// Evaluation cannot mutate the environment dictionary itself,
506// though it may modify variables reachable from the dictionary.
Alan Donovan312d1a52017-10-02 10:10:28 -0400507//
508// The filename and src parameters are as for syntax.Parse.
509//
510// If Eval fails during evaluation, it returns an *EvalError
511// containing a backtrace.
alandonovana1b28d82018-03-13 10:59:24 -0400512func Eval(thread *Thread, filename string, src interface{}, env StringDict) (Value, error) {
alandonovan30e71c62019-01-04 13:48:12 -0500513 expr, err := syntax.ParseExpr(filename, src, 0)
514 if err != nil {
515 return nil, err
516 }
517 f, err := makeExprFunc(expr, env)
alandonovan2c1f3622018-12-17 13:10:16 -0500518 if err != nil {
519 return nil, err
520 }
521 return Call(thread, f, nil, nil)
522}
523
alandonovan30e71c62019-01-04 13:48:12 -0500524// EvalExpr resolves and evaluates an expression within the
525// specified (predeclared) environment.
alandonovanf26cf182019-05-28 16:17:30 -0400526// Evaluating a comma-separated list of expressions yields a tuple value.
alandonovan30e71c62019-01-04 13:48:12 -0500527//
528// Resolving an expression mutates it.
529// Do not call EvalExpr more than once for the same expression.
530//
531// Evaluation cannot mutate the environment dictionary itself,
532// though it may modify variables reachable from the dictionary.
533//
534// If Eval fails during evaluation, it returns an *EvalError
535// containing a backtrace.
536func EvalExpr(thread *Thread, expr syntax.Expr, env StringDict) (Value, error) {
537 fn, err := makeExprFunc(expr, env)
538 if err != nil {
539 return nil, err
540 }
541 return Call(thread, fn, nil, nil)
542}
543
alandonovan2c1f3622018-12-17 13:10:16 -0500544// ExprFunc returns a no-argument function
545// that evaluates the expression whose source is src.
546func ExprFunc(filename string, src interface{}, env StringDict) (*Function, error) {
Laurent Le Brun689fc222018-02-22 19:37:18 +0100547 expr, err := syntax.ParseExpr(filename, src, 0)
Alan Donovan312d1a52017-10-02 10:10:28 -0400548 if err != nil {
549 return nil, err
550 }
alandonovan30e71c62019-01-04 13:48:12 -0500551 return makeExprFunc(expr, env)
552}
Alan Donovan312d1a52017-10-02 10:10:28 -0400553
alandonovan30e71c62019-01-04 13:48:12 -0500554// makeExprFunc returns a no-argument function whose body is expr.
555func makeExprFunc(expr syntax.Expr, env StringDict) (*Function, error) {
alandonovana1b28d82018-03-13 10:59:24 -0400556 locals, err := resolve.Expr(expr, env.Has, Universe.Has)
Alan Donovan312d1a52017-10-02 10:10:28 -0400557 if err != nil {
558 return nil, err
559 }
560
alandonovan2c1f3622018-12-17 13:10:16 -0500561 return makeToplevelFunction(compile.Expr(expr, "<expr>", locals), env), nil
Alan Donovan312d1a52017-10-02 10:10:28 -0400562}
563
alandonovan93f3e0c2018-03-30 10:42:28 -0400564// The following functions are primitive operations of the byte code interpreter.
Alan Donovan312d1a52017-10-02 10:10:28 -0400565
566// list += iterable
567func listExtend(x *List, y Iterable) {
568 if ylist, ok := y.(*List); ok {
569 // fast path: list += list
570 x.elems = append(x.elems, ylist.elems...)
571 } else {
572 iter := y.Iterate()
573 defer iter.Done()
574 var z Value
575 for iter.Next(&z) {
576 x.elems = append(x.elems, z)
577 }
578 }
579}
580
581// getAttr implements x.dot.
alandonovan82fc8c12019-01-03 16:16:33 -0500582func getAttr(x Value, name string) (Value, error) {
alandonovan6afa1bb2019-02-06 17:49:05 -0500583 hasAttr, ok := x.(HasAttrs)
584 if !ok {
585 return nil, fmt.Errorf("%s has no .%s field or method", x.Type(), name)
Alan Donovan312d1a52017-10-02 10:10:28 -0400586 }
587
alandonovan6afa1bb2019-02-06 17:49:05 -0500588 var errmsg string
589 v, err := hasAttr.Attr(name)
590 if err == nil {
591 if v != nil {
592 return v, nil // success
593 }
594 // (nil, nil) => generic error
595 errmsg = fmt.Sprintf("%s has no .%s field or method", x.Type(), name)
596 } else if nsa, ok := err.(NoSuchAttrError); ok {
597 errmsg = string(nsa)
598 } else {
599 return nil, err // return error as is
600 }
601
602 // add spelling hint
603 if n := spell.Nearest(name, hasAttr.AttrNames()); n != "" {
604 errmsg = fmt.Sprintf("%s (did you mean .%s?)", errmsg, n)
605 }
606
607 return nil, fmt.Errorf("%s", errmsg)
Alan Donovan312d1a52017-10-02 10:10:28 -0400608}
609
610// setField implements x.name = y.
alandonovan82fc8c12019-01-03 16:16:33 -0500611func setField(x Value, name string, y Value) error {
Alan Donovan312d1a52017-10-02 10:10:28 -0400612 if x, ok := x.(HasSetField); ok {
alandonovan6afa1bb2019-02-06 17:49:05 -0500613 err := x.SetField(name, y)
614 if _, ok := err.(NoSuchAttrError); ok {
615 // No such field: check spelling.
616 if n := spell.Nearest(name, x.AttrNames()); n != "" {
617 err = fmt.Errorf("%s (did you mean .%s?)", err, n)
618 }
Alan Donovan557c1f12019-02-04 16:12:53 -0500619 }
alandonovan6afa1bb2019-02-06 17:49:05 -0500620 return err
Alan Donovan312d1a52017-10-02 10:10:28 -0400621 }
Alan Donovan557c1f12019-02-04 16:12:53 -0500622
alandonovan93f3e0c2018-03-30 10:42:28 -0400623 return fmt.Errorf("can't assign to .%s field of %s", name, x.Type())
Alan Donovan312d1a52017-10-02 10:10:28 -0400624}
625
626// getIndex implements x[y].
alandonovan82fc8c12019-01-03 16:16:33 -0500627func getIndex(x, y Value) (Value, error) {
Alan Donovan312d1a52017-10-02 10:10:28 -0400628 switch x := x.(type) {
629 case Mapping: // dict
630 z, found, err := x.Get(y)
631 if err != nil {
alandonovan93f3e0c2018-03-30 10:42:28 -0400632 return nil, err
Alan Donovan312d1a52017-10-02 10:10:28 -0400633 }
634 if !found {
alandonovan93f3e0c2018-03-30 10:42:28 -0400635 return nil, fmt.Errorf("key %v not in %s", y, x.Type())
Alan Donovan312d1a52017-10-02 10:10:28 -0400636 }
637 return z, nil
638
639 case Indexable: // string, list, tuple
640 n := x.Len()
641 i, err := AsInt32(y)
642 if err != nil {
alandonovan93f3e0c2018-03-30 10:42:28 -0400643 return nil, fmt.Errorf("%s index: %s", x.Type(), err)
Alan Donovan312d1a52017-10-02 10:10:28 -0400644 }
alandonovane560c9b2019-01-23 19:08:06 -0500645 origI := i
Alan Donovan312d1a52017-10-02 10:10:28 -0400646 if i < 0 {
647 i += n
648 }
649 if i < 0 || i >= n {
alandonovane560c9b2019-01-23 19:08:06 -0500650 return nil, outOfRange(origI, n, x)
Alan Donovan312d1a52017-10-02 10:10:28 -0400651 }
652 return x.Index(i), nil
653 }
alandonovan93f3e0c2018-03-30 10:42:28 -0400654 return nil, fmt.Errorf("unhandled index operation %s[%s]", x.Type(), y.Type())
Alan Donovan312d1a52017-10-02 10:10:28 -0400655}
656
alandonovane560c9b2019-01-23 19:08:06 -0500657func outOfRange(i, n int, x Value) error {
658 if n == 0 {
659 return fmt.Errorf("index %d out of range: empty %s", i, x.Type())
660 } else {
661 return fmt.Errorf("%s index %d out of range [%d:%d]", x.Type(), i, -n, n-1)
662 }
663}
664
Alan Donovan312d1a52017-10-02 10:10:28 -0400665// setIndex implements x[y] = z.
alandonovan82fc8c12019-01-03 16:16:33 -0500666func setIndex(x, y, z Value) error {
Alan Donovan312d1a52017-10-02 10:10:28 -0400667 switch x := x.(type) {
jmillikin-stripe3ccab942018-10-05 07:09:12 -0700668 case HasSetKey:
669 if err := x.SetKey(y, z); err != nil {
alandonovan93f3e0c2018-03-30 10:42:28 -0400670 return err
Alan Donovan312d1a52017-10-02 10:10:28 -0400671 }
672
673 case HasSetIndex:
alandonovane560c9b2019-01-23 19:08:06 -0500674 n := x.Len()
Alan Donovan312d1a52017-10-02 10:10:28 -0400675 i, err := AsInt32(y)
676 if err != nil {
alandonovan93f3e0c2018-03-30 10:42:28 -0400677 return err
Alan Donovan312d1a52017-10-02 10:10:28 -0400678 }
alandonovane560c9b2019-01-23 19:08:06 -0500679 origI := i
Alan Donovan312d1a52017-10-02 10:10:28 -0400680 if i < 0 {
alandonovane560c9b2019-01-23 19:08:06 -0500681 i += n
Alan Donovan312d1a52017-10-02 10:10:28 -0400682 }
alandonovane560c9b2019-01-23 19:08:06 -0500683 if i < 0 || i >= n {
684 return outOfRange(origI, n, x)
Alan Donovan312d1a52017-10-02 10:10:28 -0400685 }
alandonovan93f3e0c2018-03-30 10:42:28 -0400686 return x.SetIndex(i, z)
Alan Donovan312d1a52017-10-02 10:10:28 -0400687
688 default:
alandonovan93f3e0c2018-03-30 10:42:28 -0400689 return fmt.Errorf("%s value does not support item assignment", x.Type())
Alan Donovan312d1a52017-10-02 10:10:28 -0400690 }
691 return nil
692}
693
Hittorp0a5e39a2018-08-09 15:02:30 +0300694// Unary applies a unary operator (+, -, ~, not) to its operand.
Alan Donovan312d1a52017-10-02 10:10:28 -0400695func Unary(op syntax.Token, x Value) (Value, error) {
alandonovan58f91012019-01-03 16:32:47 -0500696 // The NOT operator is not customizable.
697 if op == syntax.NOT {
Alan Donovan312d1a52017-10-02 10:10:28 -0400698 return !x.Truth(), nil
699 }
alandonovan58f91012019-01-03 16:32:47 -0500700
701 // Int, Float, and user-defined types
702 if x, ok := x.(HasUnary); ok {
703 // (nil, nil) => unhandled
704 y, err := x.Unary(op)
705 if y != nil || err != nil {
706 return y, err
707 }
708 }
709
Alan Donovan312d1a52017-10-02 10:10:28 -0400710 return nil, fmt.Errorf("unknown unary op: %s %s", op, x.Type())
711}
712
713// Binary applies a strict binary operator (not AND or OR) to its operands.
714// For equality tests or ordered comparisons, use Compare instead.
715func Binary(op syntax.Token, x, y Value) (Value, error) {
716 switch op {
717 case syntax.PLUS:
718 switch x := x.(type) {
719 case String:
720 if y, ok := y.(String); ok {
721 return x + y, nil
722 }
723 case Int:
724 switch y := y.(type) {
725 case Int:
726 return x.Add(y), nil
727 case Float:
alandonovan3b7e02e2020-11-11 12:03:41 -0500728 xf, err := x.finiteFloat()
729 if err != nil {
730 return nil, err
731 }
732 return xf + y, nil
Alan Donovan312d1a52017-10-02 10:10:28 -0400733 }
734 case Float:
735 switch y := y.(type) {
736 case Float:
737 return x + y, nil
738 case Int:
alandonovan3b7e02e2020-11-11 12:03:41 -0500739 yf, err := y.finiteFloat()
740 if err != nil {
741 return nil, err
742 }
743 return x + yf, nil
Alan Donovan312d1a52017-10-02 10:10:28 -0400744 }
745 case *List:
746 if y, ok := y.(*List); ok {
747 z := make([]Value, 0, x.Len()+y.Len())
748 z = append(z, x.elems...)
749 z = append(z, y.elems...)
750 return NewList(z), nil
751 }
752 case Tuple:
753 if y, ok := y.(Tuple); ok {
754 z := make(Tuple, 0, len(x)+len(y))
755 z = append(z, x...)
756 z = append(z, y...)
757 return z, nil
758 }
Alan Donovan312d1a52017-10-02 10:10:28 -0400759 }
760
761 case syntax.MINUS:
762 switch x := x.(type) {
763 case Int:
764 switch y := y.(type) {
765 case Int:
766 return x.Sub(y), nil
767 case Float:
alandonovan3b7e02e2020-11-11 12:03:41 -0500768 xf, err := x.finiteFloat()
769 if err != nil {
770 return nil, err
771 }
772 return xf - y, nil
Alan Donovan312d1a52017-10-02 10:10:28 -0400773 }
774 case Float:
775 switch y := y.(type) {
776 case Float:
777 return x - y, nil
778 case Int:
alandonovan3b7e02e2020-11-11 12:03:41 -0500779 yf, err := y.finiteFloat()
780 if err != nil {
781 return nil, err
782 }
783 return x - yf, nil
Alan Donovan312d1a52017-10-02 10:10:28 -0400784 }
785 }
786
787 case syntax.STAR:
788 switch x := x.(type) {
789 case Int:
790 switch y := y.(type) {
791 case Int:
792 return x.Mul(y), nil
793 case Float:
alandonovan3b7e02e2020-11-11 12:03:41 -0500794 xf, err := x.finiteFloat()
795 if err != nil {
796 return nil, err
797 }
798 return xf * y, nil
Alan Donovan312d1a52017-10-02 10:10:28 -0400799 case String:
alandonovanca226722018-11-28 12:39:39 -0500800 return stringRepeat(y, x)
alandonovanebe61bd2021-02-12 16:57:32 -0500801 case Bytes:
802 return bytesRepeat(y, x)
Alan Donovan312d1a52017-10-02 10:10:28 -0400803 case *List:
alandonovanca226722018-11-28 12:39:39 -0500804 elems, err := tupleRepeat(Tuple(y.elems), x)
805 if err != nil {
806 return nil, err
Alan Donovan312d1a52017-10-02 10:10:28 -0400807 }
alandonovanca226722018-11-28 12:39:39 -0500808 return NewList(elems), nil
Alan Donovan312d1a52017-10-02 10:10:28 -0400809 case Tuple:
alandonovanca226722018-11-28 12:39:39 -0500810 return tupleRepeat(y, x)
Alan Donovan312d1a52017-10-02 10:10:28 -0400811 }
812 case Float:
813 switch y := y.(type) {
814 case Float:
815 return x * y, nil
816 case Int:
alandonovan3b7e02e2020-11-11 12:03:41 -0500817 yf, err := y.finiteFloat()
818 if err != nil {
819 return nil, err
820 }
821 return x * yf, nil
Alan Donovan312d1a52017-10-02 10:10:28 -0400822 }
823 case String:
824 if y, ok := y.(Int); ok {
alandonovanca226722018-11-28 12:39:39 -0500825 return stringRepeat(x, y)
Alan Donovan312d1a52017-10-02 10:10:28 -0400826 }
alandonovanebe61bd2021-02-12 16:57:32 -0500827 case Bytes:
828 if y, ok := y.(Int); ok {
829 return bytesRepeat(x, y)
830 }
Alan Donovan312d1a52017-10-02 10:10:28 -0400831 case *List:
832 if y, ok := y.(Int); ok {
alandonovanca226722018-11-28 12:39:39 -0500833 elems, err := tupleRepeat(Tuple(x.elems), y)
834 if err != nil {
835 return nil, err
Alan Donovan312d1a52017-10-02 10:10:28 -0400836 }
alandonovanca226722018-11-28 12:39:39 -0500837 return NewList(elems), nil
Alan Donovan312d1a52017-10-02 10:10:28 -0400838 }
839 case Tuple:
840 if y, ok := y.(Int); ok {
alandonovanca226722018-11-28 12:39:39 -0500841 return tupleRepeat(x, y)
Alan Donovan312d1a52017-10-02 10:10:28 -0400842 }
843
844 }
845
846 case syntax.SLASH:
847 switch x := x.(type) {
848 case Int:
alandonovan3b7e02e2020-11-11 12:03:41 -0500849 xf, err := x.finiteFloat()
850 if err != nil {
851 return nil, err
852 }
Alan Donovan312d1a52017-10-02 10:10:28 -0400853 switch y := y.(type) {
854 case Int:
alandonovan3b7e02e2020-11-11 12:03:41 -0500855 yf, err := y.finiteFloat()
856 if err != nil {
857 return nil, err
Alan Donovan312d1a52017-10-02 10:10:28 -0400858 }
alandonovan3b7e02e2020-11-11 12:03:41 -0500859 if yf == 0.0 {
860 return nil, fmt.Errorf("floating-point division by zero")
861 }
862 return xf / yf, nil
Alan Donovan312d1a52017-10-02 10:10:28 -0400863 case Float:
864 if y == 0.0 {
alandonovan3b7e02e2020-11-11 12:03:41 -0500865 return nil, fmt.Errorf("floating-point division by zero")
Alan Donovan312d1a52017-10-02 10:10:28 -0400866 }
alandonovan3b7e02e2020-11-11 12:03:41 -0500867 return xf / y, nil
Alan Donovan312d1a52017-10-02 10:10:28 -0400868 }
869 case Float:
870 switch y := y.(type) {
871 case Float:
872 if y == 0.0 {
alandonovan3b7e02e2020-11-11 12:03:41 -0500873 return nil, fmt.Errorf("floating-point division by zero")
Alan Donovan312d1a52017-10-02 10:10:28 -0400874 }
875 return x / y, nil
876 case Int:
alandonovan3b7e02e2020-11-11 12:03:41 -0500877 yf, err := y.finiteFloat()
878 if err != nil {
879 return nil, err
880 }
Alan Donovan312d1a52017-10-02 10:10:28 -0400881 if yf == 0.0 {
alandonovan3b7e02e2020-11-11 12:03:41 -0500882 return nil, fmt.Errorf("floating-point division by zero")
Alan Donovan312d1a52017-10-02 10:10:28 -0400883 }
884 return x / yf, nil
885 }
886 }
887
888 case syntax.SLASHSLASH:
889 switch x := x.(type) {
890 case Int:
891 switch y := y.(type) {
892 case Int:
893 if y.Sign() == 0 {
894 return nil, fmt.Errorf("floored division by zero")
895 }
896 return x.Div(y), nil
897 case Float:
alandonovan3b7e02e2020-11-11 12:03:41 -0500898 xf, err := x.finiteFloat()
899 if err != nil {
900 return nil, err
901 }
Alan Donovan312d1a52017-10-02 10:10:28 -0400902 if y == 0.0 {
903 return nil, fmt.Errorf("floored division by zero")
904 }
alandonovan3b7e02e2020-11-11 12:03:41 -0500905 return floor(xf / y), nil
Alan Donovan312d1a52017-10-02 10:10:28 -0400906 }
907 case Float:
908 switch y := y.(type) {
909 case Float:
910 if y == 0.0 {
911 return nil, fmt.Errorf("floored division by zero")
912 }
913 return floor(x / y), nil
914 case Int:
alandonovan3b7e02e2020-11-11 12:03:41 -0500915 yf, err := y.finiteFloat()
916 if err != nil {
917 return nil, err
918 }
Alan Donovan312d1a52017-10-02 10:10:28 -0400919 if yf == 0.0 {
920 return nil, fmt.Errorf("floored division by zero")
921 }
922 return floor(x / yf), nil
923 }
924 }
925
926 case syntax.PERCENT:
927 switch x := x.(type) {
928 case Int:
929 switch y := y.(type) {
930 case Int:
931 if y.Sign() == 0 {
932 return nil, fmt.Errorf("integer modulo by zero")
933 }
934 return x.Mod(y), nil
935 case Float:
alandonovan3b7e02e2020-11-11 12:03:41 -0500936 xf, err := x.finiteFloat()
937 if err != nil {
938 return nil, err
Alan Donovan312d1a52017-10-02 10:10:28 -0400939 }
alandonovan3b7e02e2020-11-11 12:03:41 -0500940 if y == 0 {
941 return nil, fmt.Errorf("floating-point modulo by zero")
942 }
943 return xf.Mod(y), nil
Alan Donovan312d1a52017-10-02 10:10:28 -0400944 }
945 case Float:
946 switch y := y.(type) {
947 case Float:
948 if y == 0.0 {
alandonovan3b7e02e2020-11-11 12:03:41 -0500949 return nil, fmt.Errorf("floating-point modulo by zero")
Alan Donovan312d1a52017-10-02 10:10:28 -0400950 }
alandonovan227f4aa2020-10-06 17:39:52 -0400951 return x.Mod(y), nil
Alan Donovan312d1a52017-10-02 10:10:28 -0400952 case Int:
953 if y.Sign() == 0 {
alandonovan3b7e02e2020-11-11 12:03:41 -0500954 return nil, fmt.Errorf("floating-point modulo by zero")
Alan Donovan312d1a52017-10-02 10:10:28 -0400955 }
alandonovan3b7e02e2020-11-11 12:03:41 -0500956 yf, err := y.finiteFloat()
957 if err != nil {
958 return nil, err
959 }
960 return x.Mod(yf), nil
Alan Donovan312d1a52017-10-02 10:10:28 -0400961 }
962 case String:
963 return interpolate(string(x), y)
964 }
965
966 case syntax.NOT_IN:
967 z, err := Binary(syntax.IN, x, y)
968 if err != nil {
969 return nil, err
970 }
971 return !z.Truth(), nil
972
973 case syntax.IN:
974 switch y := y.(type) {
975 case *List:
976 for _, elem := range y.elems {
977 if eq, err := Equal(elem, x); err != nil {
978 return nil, err
979 } else if eq {
980 return True, nil
981 }
982 }
983 return False, nil
984 case Tuple:
985 for _, elem := range y {
986 if eq, err := Equal(elem, x); err != nil {
987 return nil, err
988 } else if eq {
989 return True, nil
990 }
991 }
992 return False, nil
993 case Mapping: // e.g. dict
alandonovan7f065b62018-03-19 14:58:49 -0400994 // Ignore error from Get as we cannot distinguish true
995 // errors (value cycle, type error) from "key not found".
996 _, found, _ := y.Get(x)
997 return Bool(found), nil
Alan Donovan312d1a52017-10-02 10:10:28 -0400998 case *Set:
999 ok, err := y.Has(x)
1000 return Bool(ok), err
1001 case String:
1002 needle, ok := x.(String)
1003 if !ok {
1004 return nil, fmt.Errorf("'in <string>' requires string as left operand, not %s", x.Type())
1005 }
1006 return Bool(strings.Contains(string(y), string(needle))), nil
alandonovanebe61bd2021-02-12 16:57:32 -05001007 case Bytes:
1008 switch needle := x.(type) {
1009 case Bytes:
1010 return Bool(strings.Contains(string(y), string(needle))), nil
1011 case Int:
1012 var b byte
1013 if err := AsInt(needle, &b); err != nil {
1014 return nil, fmt.Errorf("int in bytes: %s", err)
1015 }
1016 return Bool(strings.IndexByte(string(y), b) >= 0), nil
1017 default:
1018 return nil, fmt.Errorf("'in bytes' requires bytes or int as left operand, not %s", x.Type())
1019 }
alandonovan05f260d2017-10-18 12:43:53 -04001020 case rangeValue:
1021 i, err := NumberToInt(x)
1022 if err != nil {
1023 return nil, fmt.Errorf("'in <range>' requires integer as left operand, not %s", x.Type())
1024 }
1025 return Bool(y.contains(i)), nil
Alan Donovan312d1a52017-10-02 10:10:28 -04001026 }
1027
1028 case syntax.PIPE:
1029 switch x := x.(type) {
1030 case Int:
1031 if y, ok := y.(Int); ok {
1032 return x.Or(y), nil
1033 }
1034 case *Set: // union
alandonovanf3709072017-10-19 10:18:36 -04001035 if y, ok := y.(*Set); ok {
1036 iter := Iterate(y)
Alan Donovan312d1a52017-10-02 10:10:28 -04001037 defer iter.Done()
1038 return x.Union(iter)
1039 }
1040 }
1041
1042 case syntax.AMP:
1043 switch x := x.(type) {
1044 case Int:
1045 if y, ok := y.(Int); ok {
1046 return x.And(y), nil
1047 }
1048 case *Set: // intersection
1049 if y, ok := y.(*Set); ok {
1050 set := new(Set)
1051 if x.Len() > y.Len() {
1052 x, y = y, x // opt: range over smaller set
1053 }
1054 for _, xelem := range x.elems() {
1055 // Has, Insert cannot fail here.
1056 if found, _ := y.Has(xelem); found {
1057 set.Insert(xelem)
1058 }
1059 }
1060 return set, nil
1061 }
1062 }
1063
Hittorp0a5e39a2018-08-09 15:02:30 +03001064 case syntax.CIRCUMFLEX:
1065 switch x := x.(type) {
1066 case Int:
1067 if y, ok := y.(Int); ok {
1068 return x.Xor(y), nil
1069 }
1070 case *Set: // symmetric difference
1071 if y, ok := y.(*Set); ok {
1072 set := new(Set)
1073 for _, xelem := range x.elems() {
1074 if found, _ := y.Has(xelem); !found {
1075 set.Insert(xelem)
1076 }
1077 }
1078 for _, yelem := range y.elems() {
1079 if found, _ := x.Has(yelem); !found {
1080 set.Insert(yelem)
1081 }
1082 }
1083 return set, nil
1084 }
1085 }
1086
1087 case syntax.LTLT, syntax.GTGT:
1088 if x, ok := x.(Int); ok {
1089 y, err := AsInt32(y)
1090 if err != nil {
1091 return nil, err
1092 }
1093 if y < 0 {
1094 return nil, fmt.Errorf("negative shift count: %v", y)
1095 }
1096 if op == syntax.LTLT {
1097 if y >= 512 {
1098 return nil, fmt.Errorf("shift count too large: %v", y)
1099 }
1100 return x.Lsh(uint(y)), nil
1101 } else {
1102 return x.Rsh(uint(y)), nil
1103 }
1104 }
1105
Alan Donovan312d1a52017-10-02 10:10:28 -04001106 default:
1107 // unknown operator
1108 goto unknown
1109 }
1110
1111 // user-defined types
alandonovan58f91012019-01-03 16:32:47 -05001112 // (nil, nil) => unhandled
Alan Donovan312d1a52017-10-02 10:10:28 -04001113 if x, ok := x.(HasBinary); ok {
1114 z, err := x.Binary(op, y, Left)
1115 if z != nil || err != nil {
1116 return z, err
1117 }
1118 }
1119 if y, ok := y.(HasBinary); ok {
1120 z, err := y.Binary(op, x, Right)
1121 if z != nil || err != nil {
1122 return z, err
1123 }
1124 }
1125
1126 // unsupported operand types
1127unknown:
1128 return nil, fmt.Errorf("unknown binary op: %s %s %s", x.Type(), op, y.Type())
1129}
1130
alandonovanca226722018-11-28 12:39:39 -05001131// It's always possible to overeat in small bites but we'll
1132// try to stop someone swallowing the world in one gulp.
1133const maxAlloc = 1 << 30
1134
1135func tupleRepeat(elems Tuple, n Int) (Tuple, error) {
1136 if len(elems) == 0 {
1137 return nil, nil
Alan Donovan312d1a52017-10-02 10:10:28 -04001138 }
alandonovanca226722018-11-28 12:39:39 -05001139 i, err := AsInt32(n)
1140 if err != nil {
1141 return nil, fmt.Errorf("repeat count %s too large", n)
1142 }
1143 if i < 1 {
1144 return nil, nil
1145 }
1146 // Inv: i > 0, len > 0
1147 sz := len(elems) * i
1148 if sz < 0 || sz >= maxAlloc { // sz < 0 => overflow
alandonovan50ca8202020-06-19 10:36:48 -04001149 // Don't print sz.
1150 return nil, fmt.Errorf("excessive repeat (%d * %d elements)", len(elems), i)
alandonovanca226722018-11-28 12:39:39 -05001151 }
Josh Bleecher Snyder60d9d462018-12-07 15:01:10 -08001152 res := make([]Value, sz)
1153 // copy elems into res, doubling each time
1154 x := copy(res, elems)
1155 for x < len(res) {
1156 copy(res[x:], res[:x])
1157 x *= 2
alandonovanca226722018-11-28 12:39:39 -05001158 }
1159 return res, nil
1160}
1161
alandonovanebe61bd2021-02-12 16:57:32 -05001162func bytesRepeat(b Bytes, n Int) (Bytes, error) {
1163 res, err := stringRepeat(String(b), n)
1164 return Bytes(res), err
1165}
1166
alandonovanca226722018-11-28 12:39:39 -05001167func stringRepeat(s String, n Int) (String, error) {
1168 if s == "" {
1169 return "", nil
1170 }
1171 i, err := AsInt32(n)
1172 if err != nil {
1173 return "", fmt.Errorf("repeat count %s too large", n)
1174 }
1175 if i < 1 {
1176 return "", nil
1177 }
1178 // Inv: i > 0, len > 0
1179 sz := len(s) * i
1180 if sz < 0 || sz >= maxAlloc { // sz < 0 => overflow
alandonovan50ca8202020-06-19 10:36:48 -04001181 // Don't print sz.
1182 return "", fmt.Errorf("excessive repeat (%d * %d elements)", len(s), i)
alandonovanca226722018-11-28 12:39:39 -05001183 }
1184 return String(strings.Repeat(string(s), i)), nil
Alan Donovan312d1a52017-10-02 10:10:28 -04001185}
1186
Alan Donovan312d1a52017-10-02 10:10:28 -04001187// Call calls the function fn with the specified positional and keyword arguments.
1188func Call(thread *Thread, fn Value, args Tuple, kwargs []Tuple) (Value, error) {
1189 c, ok := fn.(Callable)
1190 if !ok {
1191 return nil, fmt.Errorf("invalid call of non-function (%s)", fn.Type())
1192 }
alandonovanaeec83f2018-10-22 13:24:13 -04001193
alandonovan95b27832019-05-06 10:57:34 -04001194 // Allocate and push a new frame.
1195 var fr *frame
1196 // Optimization: use slack portion of thread.stack
1197 // slice as a freelist of empty frames.
1198 if n := len(thread.stack); n < cap(thread.stack) {
1199 fr = thread.stack[n : n+1][0]
alandonovand9868e92019-04-19 14:47:26 -04001200 }
alandonovan95b27832019-05-06 10:57:34 -04001201 if fr == nil {
1202 fr = new(frame)
1203 }
alandonovan949cc6f2020-08-21 10:29:38 -04001204
1205 if thread.stack == nil {
1206 // one-time initialization of thread
1207 if thread.maxSteps == 0 {
1208 thread.maxSteps-- // (MaxUint64)
1209 }
1210 }
1211
alandonovand9868e92019-04-19 14:47:26 -04001212 thread.stack = append(thread.stack, fr) // push
alandonovan95b27832019-05-06 10:57:34 -04001213
1214 fr.callable = c
1215
alandonovan40b4ab62019-04-03 16:41:37 -04001216 thread.beginProfSpan()
alandonovanaeec83f2018-10-22 13:24:13 -04001217 result, err := c.CallInternal(thread, args, kwargs)
alandonovan40b4ab62019-04-03 16:41:37 -04001218 thread.endProfSpan()
alandonovanaeec83f2018-10-22 13:24:13 -04001219
Alan Donovane3deafe2018-10-23 11:05:09 -04001220 // Sanity check: nil is not a valid Starlark value.
alandonovanaeec83f2018-10-22 13:24:13 -04001221 if result == nil && err == nil {
alandonovan2494ae92019-04-04 15:38:05 -04001222 err = fmt.Errorf("internal error: nil (not None) returned from %s", fn)
1223 }
1224
1225 // Always return an EvalError with an accurate frame.
1226 if err != nil {
1227 if _, ok := err.(*EvalError); !ok {
alandonovand9868e92019-04-19 14:47:26 -04001228 err = thread.evalError(err)
alandonovan2494ae92019-04-04 15:38:05 -04001229 }
Alan Donovan312d1a52017-10-02 10:10:28 -04001230 }
alandonovanaeec83f2018-10-22 13:24:13 -04001231
alandonovan95b27832019-05-06 10:57:34 -04001232 *fr = frame{} // clear out any references
alandonovand9868e92019-04-19 14:47:26 -04001233 thread.stack = thread.stack[:len(thread.stack)-1] // pop
1234
alandonovanaeec83f2018-10-22 13:24:13 -04001235 return result, err
Alan Donovan312d1a52017-10-02 10:10:28 -04001236}
1237
Alan Donovan312d1a52017-10-02 10:10:28 -04001238func slice(x, lo, hi, step_ Value) (Value, error) {
Nick Santosd3cd7362018-05-18 12:58:53 -04001239 sliceable, ok := x.(Sliceable)
1240 if !ok {
1241 return nil, fmt.Errorf("invalid slice operand %s", x.Type())
Alan Donovan312d1a52017-10-02 10:10:28 -04001242 }
1243
Nick Santosd3cd7362018-05-18 12:58:53 -04001244 n := sliceable.Len()
Alan Donovan312d1a52017-10-02 10:10:28 -04001245 step := 1
1246 if step_ != None {
1247 var err error
1248 step, err = AsInt32(step_)
1249 if err != nil {
tdakkota88a10932020-09-29 15:29:13 +03001250 return nil, fmt.Errorf("invalid slice step: %s", err)
Alan Donovan312d1a52017-10-02 10:10:28 -04001251 }
1252 if step == 0 {
1253 return nil, fmt.Errorf("zero is not a valid slice step")
1254 }
1255 }
1256
1257 // TODO(adonovan): opt: preallocate result array.
1258
1259 var start, end int
1260 if step > 0 {
1261 // positive stride
1262 // default indices are [0:n].
1263 var err error
1264 start, end, err = indices(lo, hi, n)
1265 if err != nil {
1266 return nil, err
1267 }
1268
1269 if end < start {
1270 end = start // => empty result
1271 }
Alan Donovan312d1a52017-10-02 10:10:28 -04001272 } else {
1273 // negative stride
1274 // default indices are effectively [n-1:-1], though to
1275 // get this effect using explicit indices requires
1276 // [n-1:-1-n:-1] because of the treatment of -ve values.
1277 start = n - 1
1278 if err := asIndex(lo, n, &start); err != nil {
1279 return nil, fmt.Errorf("invalid start index: %s", err)
1280 }
1281 if start >= n {
1282 start = n - 1
1283 }
1284
1285 end = -1
1286 if err := asIndex(hi, n, &end); err != nil {
1287 return nil, fmt.Errorf("invalid end index: %s", err)
1288 }
1289 if end < -1 {
1290 end = -1
1291 }
1292
1293 if start < end {
1294 start = end // => empty result
1295 }
1296 }
1297
Nick Santosd3cd7362018-05-18 12:58:53 -04001298 return sliceable.Slice(start, end, step), nil
Alan Donovan312d1a52017-10-02 10:10:28 -04001299}
1300
1301// From Hacker's Delight, section 2.8.
Edward McFarlaned50186b2019-02-24 19:44:57 +00001302func signum64(x int64) int { return int(uint64(x>>63) | uint64(-x)>>63) }
1303func signum(x int) int { return signum64(int64(x)) }
Alan Donovan312d1a52017-10-02 10:10:28 -04001304
1305// indices converts start_ and end_ to indices in the range [0:len].
1306// The start index defaults to 0 and the end index defaults to len.
1307// An index -len < i < 0 is treated like i+len.
1308// All other indices outside the range are clamped to the nearest value in the range.
1309// Beware: start may be greater than end.
1310// This function is suitable only for slices with positive strides.
1311func indices(start_, end_ Value, len int) (start, end int, err error) {
1312 start = 0
1313 if err := asIndex(start_, len, &start); err != nil {
1314 return 0, 0, fmt.Errorf("invalid start index: %s", err)
1315 }
1316 // Clamp to [0:len].
1317 if start < 0 {
1318 start = 0
1319 } else if start > len {
1320 start = len
1321 }
1322
1323 end = len
1324 if err := asIndex(end_, len, &end); err != nil {
1325 return 0, 0, fmt.Errorf("invalid end index: %s", err)
1326 }
1327 // Clamp to [0:len].
1328 if end < 0 {
1329 end = 0
1330 } else if end > len {
1331 end = len
1332 }
1333
1334 return start, end, nil
1335}
1336
1337// asIndex sets *result to the integer value of v, adding len to it
1338// if it is negative. If v is nil or None, *result is unchanged.
1339func asIndex(v Value, len int, result *int) error {
1340 if v != nil && v != None {
1341 var err error
1342 *result, err = AsInt32(v)
1343 if err != nil {
tdakkota88a10932020-09-29 15:29:13 +03001344 return err
Alan Donovan312d1a52017-10-02 10:10:28 -04001345 }
1346 if *result < 0 {
1347 *result += len
1348 }
1349 }
1350 return nil
1351}
1352
Alan Donovan312d1a52017-10-02 10:10:28 -04001353// setArgs sets the values of the formal parameters of function fn in
alandonovan93f3e0c2018-03-30 10:42:28 -04001354// based on the actual parameter values in args and kwargs.
1355func setArgs(locals []Value, fn *Function, args Tuple, kwargs []Tuple) error {
alandonovan8313b542019-02-15 13:17:43 -05001356
1357 // This is the general schema of a function:
1358 //
1359 // def f(p1, p2=dp2, p3=dp3, *args, k1, k2=dk2, k3, **kwargs)
1360 //
1361 // The p parameters are non-kwonly, and may be specified positionally.
1362 // The k parameters are kwonly, and must be specified by name.
1363 // The defaults tuple is (dp2, dp3, mandatory, dk2, mandatory).
1364 //
1365 // Arguments are processed as follows:
1366 // - positional arguments are bound to a prefix of [p1, p2, p3].
1367 // - surplus positional arguments are bound to *args.
1368 // - keyword arguments are bound to any of {p1, p2, p3, k1, k2, k3};
1369 // duplicate bindings are rejected.
1370 // - surplus keyword arguments are bound to **kwargs.
1371 // - defaults are bound to each parameter from p2 to k3 if no value was set.
1372 // default values come from the tuple above.
1373 // It is an error if the tuple entry for an unset parameter is 'mandatory'.
alandonovanb7e3b1f2018-12-12 17:14:58 -05001374
1375 // Nullary function?
1376 if fn.NumParams() == 0 {
1377 if nactual := len(args) + len(kwargs); nactual > 0 {
alandonovan8313b542019-02-15 13:17:43 -05001378 return fmt.Errorf("function %s accepts no arguments (%d given)", fn.Name(), nactual)
alandonovanb7e3b1f2018-12-12 17:14:58 -05001379 }
1380 return nil
1381 }
1382
Alan Donovan312d1a52017-10-02 10:10:28 -04001383 cond := func(x bool, y, z interface{}) interface{} {
1384 if x {
1385 return y
1386 }
1387 return z
1388 }
1389
alandonovan8313b542019-02-15 13:17:43 -05001390 // nparams is the number of ordinary parameters (sans *args and **kwargs).
alandonovan93f3e0c2018-03-30 10:42:28 -04001391 nparams := fn.NumParams()
alandonovanb7e3b1f2018-12-12 17:14:58 -05001392 var kwdict *Dict
1393 if fn.HasKwargs() {
1394 nparams--
1395 kwdict = new(Dict)
1396 locals[nparams] = kwdict
1397 }
alandonovan93f3e0c2018-03-30 10:42:28 -04001398 if fn.HasVarargs() {
Alan Donovan312d1a52017-10-02 10:10:28 -04001399 nparams--
1400 }
alandonovanb7e3b1f2018-12-12 17:14:58 -05001401
alandonovan8313b542019-02-15 13:17:43 -05001402 // nonkwonly is the number of non-kwonly parameters.
1403 nonkwonly := nparams - fn.NumKwonlyParams()
1404
alandonovanb7e3b1f2018-12-12 17:14:58 -05001405 // Too many positional args?
1406 n := len(args)
alandonovan8313b542019-02-15 13:17:43 -05001407 if len(args) > nonkwonly {
alandonovanb7e3b1f2018-12-12 17:14:58 -05001408 if !fn.HasVarargs() {
alandonovan8313b542019-02-15 13:17:43 -05001409 return fmt.Errorf("function %s accepts %s%d positional argument%s (%d given)",
alandonovanb7e3b1f2018-12-12 17:14:58 -05001410 fn.Name(),
alandonovan8313b542019-02-15 13:17:43 -05001411 cond(len(fn.defaults) > fn.NumKwonlyParams(), "at most ", ""),
1412 nonkwonly,
1413 cond(nonkwonly == 1, "", "s"),
1414 len(args))
alandonovanb7e3b1f2018-12-12 17:14:58 -05001415 }
alandonovan8313b542019-02-15 13:17:43 -05001416 n = nonkwonly
Alan Donovan312d1a52017-10-02 10:10:28 -04001417 }
1418
alandonovan8313b542019-02-15 13:17:43 -05001419 // Bind positional arguments to non-kwonly parameters.
alandonovanb7e3b1f2018-12-12 17:14:58 -05001420 for i := 0; i < n; i++ {
1421 locals[i] = args[i]
alandonovanb7e3b1f2018-12-12 17:14:58 -05001422 }
1423
alandonovan8313b542019-02-15 13:17:43 -05001424 // Bind surplus positional arguments to *args parameter.
alandonovanb7e3b1f2018-12-12 17:14:58 -05001425 if fn.HasVarargs() {
1426 tuple := make(Tuple, len(args)-n)
1427 for i := n; i < len(args); i++ {
1428 tuple[i-n] = args[i]
Alan Donovan312d1a52017-10-02 10:10:28 -04001429 }
alandonovanb7e3b1f2018-12-12 17:14:58 -05001430 locals[nparams] = tuple
1431 }
Alan Donovan312d1a52017-10-02 10:10:28 -04001432
alandonovan8313b542019-02-15 13:17:43 -05001433 // Bind keyword arguments to parameters.
alandonovanb7e3b1f2018-12-12 17:14:58 -05001434 paramIdents := fn.funcode.Locals[:nparams]
1435 for _, pair := range kwargs {
1436 k, v := pair[0].(String), pair[1]
1437 if i := findParam(paramIdents, string(k)); i >= 0 {
alandonovan8313b542019-02-15 13:17:43 -05001438 if locals[i] != nil {
1439 return fmt.Errorf("function %s got multiple values for parameter %s", fn.Name(), k)
alandonovanea6d2812018-11-21 15:01:40 -05001440 }
alandonovanb7e3b1f2018-12-12 17:14:58 -05001441 locals[i] = v
1442 continue
Alan Donovan312d1a52017-10-02 10:10:28 -04001443 }
alandonovanb7e3b1f2018-12-12 17:14:58 -05001444 if kwdict == nil {
1445 return fmt.Errorf("function %s got an unexpected keyword argument %s", fn.Name(), k)
1446 }
alandonovan8313b542019-02-15 13:17:43 -05001447 oldlen := kwdict.Len()
alandonovanb7e3b1f2018-12-12 17:14:58 -05001448 kwdict.SetKey(k, v)
alandonovan8313b542019-02-15 13:17:43 -05001449 if kwdict.Len() == oldlen {
1450 return fmt.Errorf("function %s got multiple values for parameter %s", fn.Name(), k)
alandonovanb7e3b1f2018-12-12 17:14:58 -05001451 }
1452 }
Alan Donovan312d1a52017-10-02 10:10:28 -04001453
alandonovan8313b542019-02-15 13:17:43 -05001454 // Are defaults required?
Alan Donovan52153852019-02-13 19:18:15 -05001455 if n < nparams || fn.NumKwonlyParams() > 0 {
alandonovanb7e3b1f2018-12-12 17:14:58 -05001456 m := nparams - len(fn.defaults) // first default
Alan Donovan312d1a52017-10-02 10:10:28 -04001457
alandonovan8313b542019-02-15 13:17:43 -05001458 // Report errors for missing required arguments.
1459 var missing []string
1460 var i int
1461 for i = n; i < m; i++ {
1462 if locals[i] == nil {
1463 missing = append(missing, paramIdents[i].Name)
Alan Donovan312d1a52017-10-02 10:10:28 -04001464 }
1465 }
alandonovanb7e3b1f2018-12-12 17:14:58 -05001466
alandonovan8313b542019-02-15 13:17:43 -05001467 // Bind default values to parameters.
alandonovanb7e3b1f2018-12-12 17:14:58 -05001468 for ; i < nparams; i++ {
alandonovan8313b542019-02-15 13:17:43 -05001469 if locals[i] == nil {
1470 dflt := fn.defaults[i-m]
1471 if _, ok := dflt.(mandatory); ok {
1472 missing = append(missing, paramIdents[i].Name)
1473 continue
1474 }
1475 locals[i] = dflt
alandonovanb7e3b1f2018-12-12 17:14:58 -05001476 }
1477 }
alandonovan8313b542019-02-15 13:17:43 -05001478
1479 if missing != nil {
1480 return fmt.Errorf("function %s missing %d argument%s (%s)",
1481 fn.Name(), len(missing), cond(len(missing) > 1, "s", ""), strings.Join(missing, ", "))
1482 }
Alan Donovan312d1a52017-10-02 10:10:28 -04001483 }
1484 return nil
1485}
1486
alandonovanf763f8b2019-03-08 15:33:54 -05001487func findParam(params []compile.Binding, name string) int {
Alan Donovan312d1a52017-10-02 10:10:28 -04001488 for i, param := range params {
1489 if param.Name == name {
1490 return i
1491 }
1492 }
1493 return -1
1494}
1495
alandonovan04aba6e2018-11-05 17:45:33 -05001496// https://github.com/google/starlark-go/blob/master/doc/spec.md#string-interpolation
Alan Donovan312d1a52017-10-02 10:10:28 -04001497func interpolate(format string, x Value) (Value, error) {
Josh Bleecher Snyder8cb25c82019-03-01 14:24:35 -08001498 buf := new(strings.Builder)
Alan Donovan312d1a52017-10-02 10:10:28 -04001499 index := 0
Josh Bleecher Snyder783d9fb2019-01-01 12:21:25 -10001500 nargs := 1
1501 if tuple, ok := x.(Tuple); ok {
1502 nargs = len(tuple)
1503 }
Alan Donovan312d1a52017-10-02 10:10:28 -04001504 for {
1505 i := strings.IndexByte(format, '%')
1506 if i < 0 {
1507 buf.WriteString(format)
1508 break
1509 }
1510 buf.WriteString(format[:i])
1511 format = format[i+1:]
1512
1513 if format != "" && format[0] == '%' {
1514 buf.WriteByte('%')
1515 format = format[1:]
1516 continue
1517 }
1518
1519 var arg Value
1520 if format != "" && format[0] == '(' {
1521 // keyword argument: %(name)s.
1522 format = format[1:]
1523 j := strings.IndexByte(format, ')')
1524 if j < 0 {
1525 return nil, fmt.Errorf("incomplete format key")
1526 }
1527 key := format[:j]
1528 if dict, ok := x.(Mapping); !ok {
1529 return nil, fmt.Errorf("format requires a mapping")
1530 } else if v, found, _ := dict.Get(String(key)); found {
1531 arg = v
1532 } else {
1533 return nil, fmt.Errorf("key not found: %s", key)
1534 }
1535 format = format[j+1:]
1536 } else {
1537 // positional argument: %s.
Josh Bleecher Snyder783d9fb2019-01-01 12:21:25 -10001538 if index >= nargs {
Alan Donovan312d1a52017-10-02 10:10:28 -04001539 return nil, fmt.Errorf("not enough arguments for format string")
Josh Bleecher Snyder783d9fb2019-01-01 12:21:25 -10001540 }
1541 if tuple, ok := x.(Tuple); ok {
1542 arg = tuple[index]
Alan Donovan312d1a52017-10-02 10:10:28 -04001543 } else {
1544 arg = x
1545 }
1546 }
1547
Alan Donovane3deafe2018-10-23 11:05:09 -04001548 // NOTE: Starlark does not support any of these optional Python features:
Alan Donovan312d1a52017-10-02 10:10:28 -04001549 // - optional conversion flags: [#0- +], etc.
1550 // - optional minimum field width (number or *).
1551 // - optional precision (.123 or *)
1552 // - optional length modifier
1553
1554 // conversion type
1555 if format == "" {
1556 return nil, fmt.Errorf("incomplete format")
1557 }
1558 switch c := format[0]; c {
1559 case 's', 'r':
1560 if str, ok := AsString(arg); ok && c == 's' {
1561 buf.WriteString(str)
1562 } else {
Josh Bleecher Snyder8cb25c82019-03-01 14:24:35 -08001563 writeValue(buf, arg, nil)
Alan Donovan312d1a52017-10-02 10:10:28 -04001564 }
1565 case 'd', 'i', 'o', 'x', 'X':
alandonovan05f260d2017-10-18 12:43:53 -04001566 i, err := NumberToInt(arg)
Alan Donovan312d1a52017-10-02 10:10:28 -04001567 if err != nil {
1568 return nil, fmt.Errorf("%%%c format requires integer: %v", c, err)
1569 }
1570 switch c {
1571 case 'd', 'i':
Josh Bleecher Snyder8cb25c82019-03-01 14:24:35 -08001572 fmt.Fprintf(buf, "%d", i)
Alan Donovan312d1a52017-10-02 10:10:28 -04001573 case 'o':
Josh Bleecher Snyder8cb25c82019-03-01 14:24:35 -08001574 fmt.Fprintf(buf, "%o", i)
Alan Donovan312d1a52017-10-02 10:10:28 -04001575 case 'x':
Josh Bleecher Snyder8cb25c82019-03-01 14:24:35 -08001576 fmt.Fprintf(buf, "%x", i)
Alan Donovan312d1a52017-10-02 10:10:28 -04001577 case 'X':
Josh Bleecher Snyder8cb25c82019-03-01 14:24:35 -08001578 fmt.Fprintf(buf, "%X", i)
Alan Donovan312d1a52017-10-02 10:10:28 -04001579 }
1580 case 'e', 'f', 'g', 'E', 'F', 'G':
1581 f, ok := AsFloat(arg)
1582 if !ok {
1583 return nil, fmt.Errorf("%%%c format requires float, not %s", c, arg.Type())
1584 }
alandonovan3b7e02e2020-11-11 12:03:41 -05001585 Float(f).format(buf, c)
Alan Donovan312d1a52017-10-02 10:10:28 -04001586 case 'c':
1587 switch arg := arg.(type) {
1588 case Int:
1589 // chr(int)
1590 r, err := AsInt32(arg)
1591 if err != nil || r < 0 || r > unicode.MaxRune {
1592 return nil, fmt.Errorf("%%c format requires a valid Unicode code point, got %s", arg)
1593 }
1594 buf.WriteRune(rune(r))
1595 case String:
1596 r, size := utf8.DecodeRuneInString(string(arg))
Josh Bleecher Snyderabdd33c2018-12-09 05:13:33 -08001597 if size != len(arg) || len(arg) == 0 {
Alan Donovan312d1a52017-10-02 10:10:28 -04001598 return nil, fmt.Errorf("%%c format requires a single-character string")
1599 }
1600 buf.WriteRune(r)
1601 default:
1602 return nil, fmt.Errorf("%%c format requires int or single-character string, not %s", arg.Type())
1603 }
1604 case '%':
1605 buf.WriteByte('%')
1606 default:
1607 return nil, fmt.Errorf("unknown conversion %%%c", c)
1608 }
1609 format = format[1:]
1610 index++
1611 }
1612
Josh Bleecher Snyder783d9fb2019-01-01 12:21:25 -10001613 if index < nargs {
Alan Donovan312d1a52017-10-02 10:10:28 -04001614 return nil, fmt.Errorf("too many arguments for format string")
1615 }
1616
1617 return String(buf.String()), nil
1618}