diff options
author | Jay Conrod <jayconrod@gmail.com> | 2019-01-02 17:58:35 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-01-02 17:58:35 -0500 |
commit | f9eba728dc959ed06f508aaac74f1fe710800c8f (patch) | |
tree | 4a7d9625e499b884b39b7e60a0d631b896305761 | |
parent | 70a4f43bd14540d466ebd8601bbcd358d47ef7af (diff) | |
parent | c0b6b768d91b2b919fd75c060b95f91ecb3b6d8a (diff) | |
download | starlark-go-f9eba728dc959ed06f508aaac74f1fe710800c8f.tar.gz |
Merge pull request #77 from google/options
starlark: permit if/for/while at toplevel if -globalreassign
-rw-r--r-- | cmd/starlark/starlark.go | 3 | ||||
-rw-r--r-- | doc/spec.md | 65 | ||||
-rw-r--r-- | resolve/resolve.go | 8 | ||||
-rw-r--r-- | resolve/resolve_test.go | 25 | ||||
-rw-r--r-- | resolve/testdata/resolve.star | 46 | ||||
-rw-r--r-- | starlark/eval_test.go | 31 | ||||
-rw-r--r-- | starlark/testdata/assign.star | 3 | ||||
-rw-r--r-- | starlark/testdata/bool.star | 8 | ||||
-rw-r--r-- | starlark/testdata/builtins.star | 1 | ||||
-rw-r--r-- | starlark/testdata/dict.star | 1 | ||||
-rw-r--r-- | starlark/testdata/float.star | 1 | ||||
-rw-r--r-- | starlark/testdata/function.star | 1 | ||||
-rw-r--r-- | starlark/testdata/int.star | 1 | ||||
-rw-r--r-- | starlark/testdata/list.star | 216 | ||||
-rw-r--r-- | starlark/testdata/misc.star | 2 | ||||
-rw-r--r-- | starlark/testdata/set.star | 3 | ||||
-rw-r--r-- | starlark/testdata/string.star | 1 |
17 files changed, 255 insertions, 161 deletions
diff --git a/cmd/starlark/starlark.go b/cmd/starlark/starlark.go index 0fad4c3..1035a1b 100644 --- a/cmd/starlark/starlark.go +++ b/cmd/starlark/starlark.go @@ -29,12 +29,13 @@ var ( // non-standard dialect flags func init() { - flag.BoolVar(&resolve.AllowFloat, "fp", resolve.AllowFloat, "allow floating-point numbers") + flag.BoolVar(&resolve.AllowFloat, "float", resolve.AllowFloat, "allow floating-point numbers") flag.BoolVar(&resolve.AllowSet, "set", resolve.AllowSet, "allow set data type") flag.BoolVar(&resolve.AllowLambda, "lambda", resolve.AllowLambda, "allow lambda expressions") flag.BoolVar(&resolve.AllowNestedDef, "nesteddef", resolve.AllowNestedDef, "allow nested def statements") flag.BoolVar(&resolve.AllowBitwise, "bitwise", resolve.AllowBitwise, "allow bitwise operations (&, |, ^, ~, <<, and >>)") flag.BoolVar(&resolve.AllowRecursion, "recursion", resolve.AllowRecursion, "allow while statements and recursive functions") + flag.BoolVar(&resolve.AllowGlobalReassign, "globalreassign", resolve.AllowGlobalReassign, "allow reassignment of globals, and if/for/while statements at top level") } func main() { diff --git a/doc/spec.md b/doc/spec.md index b8f7379..d27cf52 100644 --- a/doc/spec.md +++ b/doc/spec.md @@ -16,8 +16,7 @@ used by Bazel. We identify places where their behaviors differ, and an [appendix](#dialect-differences) provides a summary of those differences. -We plan to converge both implementations on a single specification -in early 2018. +We plan to converge both implementations on a single specification. This document is maintained by Alan Donovan <adonovan@google.com>. It was influenced by the Python specification, @@ -241,11 +240,10 @@ characters are tokens: identifiers: ```text -and else load -break for not -continue if or -def in pass -elif lambda return +and elif in or +break else lambda pass +continue for load return +def if not while ``` The tokens below also may not be used as identifiers although they do not @@ -254,14 +252,11 @@ appear in the grammar; they are reserved as possible future keywords: <!-- and to remain a syntactic subset of Python --> ```text -as import -assert is -class nonlocal -del raise -except try -finally while -from with -global yield +as finally nonlocal +assert from raise +class global try +del import with +except is yield ``` <b>Implementation note:</b> @@ -449,7 +444,7 @@ of protocol messages which may contain signed and unsigned 64-bit integers. The Java implementation currently supports only signed 32-bit integers. -The Go implementation of the Starlark REPL requires the `-bitwise` flag to +The Go implementation of Starlark requires the `-bitwise` flag to enable support for `&`, `|`, `^`, `~`, `<<`, and `>>` operations. The Java implementation does not support `^`, `~`, `<<`, and `>>` operations. @@ -503,9 +498,8 @@ float(3) / 2 # 1.5 The Go implementation of Starlark supports floating-point numbers as an optional feature, motivated by the need for lossless manipulation of protocol messages. -The Go implementation of the Starlark REPL requires the `-fp` flag to -enable support for floating-point literals, the `float` built-in -function, and the real division operator `/`. +The `-float` flag enables support for floating-point literals, +the `float` built-in function, and the real division operator `/`. The Java implementation does not yet support floating-point numbers. @@ -842,7 +836,7 @@ The only method of a set is `union`, which is equivalent to the `|` operator. A set used in a Boolean context is considered true if it is non-empty. <b>Implementation note:</b> -The Go implementation of the Starlark REPL requires the `-set` flag to +The Go implementation of Starlark requires the `-set` flag to enable support for sets and the `-bitwise` flag to enable support for the `&`, `|`, and `^` operators. The Java implementation does not support sets. @@ -1016,7 +1010,7 @@ f(-1) # returns 1 without printing ``` <b>Implementation note:</b> -The Go implementation of the Starlark REPL requires the `-recursion` +The Go implementation of Starlark requires the `-recursion` flag to allow recursive functions. @@ -1074,7 +1068,7 @@ The parameter names serve merely as documentation. After a Starlark file is parsed, but before its execution begins, the Starlark interpreter checks statically that the program is well formed. For example, `break` and `continue` statements may appear only within -a loop; `if`, `for`, `while`, and `return` statements may appear only within a +a loop; a `return` statement may appear only within a function; and `load` statements may appear only outside any function. _Name resolution_ is the static checking process that @@ -1920,7 +1914,7 @@ set([1, 2]) ^ set([2, 3]) # set([1, 3]) ``` <b>Implementation note:</b> -The Go implementation of the Starlark REPL requires the `-set` flag to +The Go implementation of Starlark requires the `-set` flag to enable support for sets. The Java implementation does not support sets, nor recognize `&` as a token, nor support `int | int`. @@ -2364,7 +2358,7 @@ twice = lambda(x): x * 2 ``` <b>Implementation note:</b> -The Go implementation of the Starlark REPL requires the `-lambda` flag +The Go implementation of Starlark requires the `-lambda` flag to enable support for lambda expressions. The Java implementation does not support them. See Google Issue b/36358844. @@ -2550,7 +2544,7 @@ current module. <!-- this is too implementation-oriented; it's not a spec. --> <b>Implementation note:</b> -The Go implementation of the Starlark REPL requires the `-nesteddef` +The Go implementation of Starlark requires the `-nesteddef` flag to enable support for nested `def` statements. The Java implementation does not permit a `def` expression to be nested within the body of another function. @@ -2638,6 +2632,11 @@ else: An `if` statement is permitted only within a function definition. An `if` statement at top level results in a static error. +<b>Implementation note<b>: +The Go implementation of Starlark permits `if`-statements to appear at top-level +if the `-globalreassign` flag is enabled. + + ### While loops A `while` loop evaluates an expression (the _condition_) and if the truth @@ -2659,8 +2658,9 @@ while n > 0: A `while` statement is permitted only within a function definition. A `while` statement at top level results in a static error. -<b>Implementation note:</b> `while` loops are only allowed when the `-recursion` -flag is specified. +<b>Implementation note:</b> +The Go implementation of Starlark permits `while` loops only if the `-recursion` flag is enabled. +A `while` statement is permitted at top-level if the `-globalreassign` flag is enabled. ### For loops @@ -2701,6 +2701,10 @@ iteration. In Starlark, a `for` loop is permitted only within a function definition. A `for` loop at top level results in a static error. +<b>Implementation note<b>: +The Go implementation of Starlark permits loops to appear at top-level +if the `-globalreassign` flag is enabled. + ### Break and Continue @@ -2939,7 +2943,7 @@ With no arguments, `float()` returns `0.0`. <b>Implementation note:</b> Floating-point numbers are an optional feature. -The Go implementation of the Starlark REPL requires the `-fp` flag to +The Go implementation of Starlark requires the `-float` flag to enable support for floating-point literals, the `float` built-in function, and the real division operator `/`. The Java implementation does not yet support floating-point numbers. @@ -3150,7 +3154,8 @@ set([3, 1, 4, 1, 5, 9]) # set([3, 1, 4, 5, 9]) ``` <b>Implementation note:</b> -Sets are an optional feature of the Go implementation of Starlark. +Sets are an optional feature of the Go implementation of Starlark, +enabled by the `-set` flag. ### sorted @@ -4029,3 +4034,5 @@ See [Starlark spec issue 20](https://github.com/bazelbuild/starlark/issues/20). * `hash` accepts operands besides strings. * `sorted` accepts the additional parameters `key` and `reverse`. * `type(x)` returns `"builtin_function_or_method"` for built-in functions. +* `if`, `for`, and `while` are permitted at toplevel (option: `-globalreassign`). +* top-level rebindings are permitted (option: `-globalreassign`). diff --git a/resolve/resolve.go b/resolve/resolve.go index 9e21ee1..9c5a0b8 100644 --- a/resolve/resolve.go +++ b/resolve/resolve.go @@ -431,7 +431,7 @@ func (r *resolver) stmt(stmt syntax.Stmt) { } case *syntax.IfStmt: - if r.container().function == nil { + if !AllowGlobalReassign && r.container().function == nil { r.errorf(stmt.If, "if statement not within a function") } r.expr(stmt.Cond) @@ -463,7 +463,7 @@ func (r *resolver) stmt(stmt syntax.Stmt) { r.function(stmt.Def, stmt.Name.Name, &stmt.Function) case *syntax.ForStmt: - if r.container().function == nil { + if !AllowGlobalReassign && r.container().function == nil { r.errorf(stmt.For, "for loop not within a function") } r.expr(stmt.X) @@ -475,9 +475,9 @@ func (r *resolver) stmt(stmt syntax.Stmt) { case *syntax.WhileStmt: if !AllowRecursion { - r.errorf(stmt.While, "while loop not allowed") + r.errorf(stmt.While, doesnt+"support while loops") } - if r.container().function == nil { + if !AllowGlobalReassign && r.container().function == nil { r.errorf(stmt.While, "while loop not within a function") } r.expr(stmt.Cond) diff --git a/resolve/resolve_test.go b/resolve/resolve_test.go index 0b9b58f..2885a5b 100644 --- a/resolve/resolve_test.go +++ b/resolve/resolve_test.go @@ -14,7 +14,22 @@ import ( "go.starlark.net/syntax" ) +func setOptions(src string) { + resolve.AllowBitwise = option(src, "bitwise") + resolve.AllowFloat = option(src, "float") + resolve.AllowGlobalReassign = option(src, "globalreassign") + resolve.AllowLambda = option(src, "lambda") + resolve.AllowNestedDef = option(src, "nesteddef") + resolve.AllowRecursion = option(src, "recursion") + resolve.AllowSet = option(src, "set") +} + +func option(chunk, name string) bool { + return strings.Contains(chunk, "option:"+name) +} + func TestResolve(t *testing.T) { + defer setOptions("") filename := starlarktest.DataFile("resolve", "testdata/resolve.star") for _, chunk := range chunkedfile.Read(filename, t) { f, err := syntax.Parse(filename, chunk.Source, 0) @@ -24,11 +39,7 @@ func TestResolve(t *testing.T) { } // A chunk may set options by containing e.g. "option:float". - resolve.AllowNestedDef = option(chunk.Source, "nesteddef") - resolve.AllowLambda = option(chunk.Source, "lambda") - resolve.AllowFloat = option(chunk.Source, "float") - resolve.AllowSet = option(chunk.Source, "set") - resolve.AllowGlobalReassign = option(chunk.Source, "global_reassign") + setOptions(chunk.Source) if err := resolve.File(f, isPredeclared, isUniversal); err != nil { for _, err := range err.(resolve.ErrorList) { @@ -39,10 +50,6 @@ func TestResolve(t *testing.T) { } } -func option(chunk, name string) bool { - return strings.Contains(chunk, "option:"+name) -} - func TestDefVarargsAndKwargsSet(t *testing.T) { source := "def f(*args, **kwargs): pass\n" file, err := syntax.Parse("foo.star", source, 0) diff --git a/resolve/testdata/resolve.star b/resolve/testdata/resolve.star index 7356c57..bd038ce 100644 --- a/resolve/testdata/resolve.star +++ b/resolve/testdata/resolve.star @@ -144,7 +144,13 @@ load("foo", _e="f") # ok --- -# return, if statements and for loops at top-level are forbidden +# return statements must be within a function + +return ### "return statement not within a function" + +--- +# if-statements and for-loops at top-level are forbidden +# (without globalreassign option) for x in "abc": ### "for loop not within a function" pass @@ -152,7 +158,37 @@ for x in "abc": ### "for loop not within a function" if x: ### "if statement not within a function" pass -return ### "return statement not within a function" +--- +# option:globalreassign + +for x in "abc": # ok + pass + +if x: # ok + pass + +--- +# while loops are forbidden (without -recursion option) + +def f(): + while U: ### "dialect does not support while loops" + pass + +--- +# option:recursion + +def f(): + while U: # ok + pass + +while U: ### "while loop not within a function" + pass + +--- +# option:globalreassign option:recursion + +while U: # ok + pass --- # The parser allows any expression on the LHS of an assignment. @@ -247,20 +283,20 @@ b = 1 / 2 c = 3.141 --- -# option:global_reassign +# option:globalreassign # Legacy Bazel (and Python) semantics: def must precede use even for globals. _ = x ### `undefined: x` x = 1 --- -# option:global_reassign +# option:globalreassign # Legacy Bazel (and Python) semantics: reassignment of globals is allowed. x = 1 x = 2 # ok --- -# option:global_reassign +# option:globalreassign # Redeclaration of predeclared names is allowed. # module-specific predeclared name diff --git a/starlark/eval_test.go b/starlark/eval_test.go index f9747be..b6471fb 100644 --- a/starlark/eval_test.go +++ b/starlark/eval_test.go @@ -19,13 +19,19 @@ import ( "go.starlark.net/syntax" ) -func init() { - // The tests make extensive use of these not-yet-standard features. - resolve.AllowLambda = true - resolve.AllowNestedDef = true - resolve.AllowFloat = true - resolve.AllowSet = true - resolve.AllowBitwise = true +// A test may enable non-standard options by containing (e.g.) "option:recursion". +func setOptions(src string) { + resolve.AllowBitwise = option(src, "bitwise") + resolve.AllowFloat = option(src, "float") + resolve.AllowGlobalReassign = option(src, "globalreassign") + resolve.AllowLambda = option(src, "lambda") + resolve.AllowNestedDef = option(src, "nesteddef") + resolve.AllowRecursion = option(src, "recursion") + resolve.AllowSet = option(src, "set") +} + +func option(chunk, name string) bool { + return strings.Contains(chunk, "option:"+name) } func TestEvalExpr(t *testing.T) { @@ -93,6 +99,7 @@ func TestEvalExpr(t *testing.T) { } func TestExecFile(t *testing.T) { + defer setOptions("") testdata := starlarktest.DataFile("starlark", ".") thread := &starlark.Thread{Load: load} starlarktest.SetReporter(thread, t) @@ -119,7 +126,8 @@ func TestExecFile(t *testing.T) { "fibonacci": fib{}, } - resolve.AllowRecursion = option(chunk.Source, "recursion") + setOptions(chunk.Source) + resolve.AllowLambda = true // used extensively _, err := starlark.ExecFile(thread, filename, chunk.Source, predeclared) switch err := err.(type) { @@ -139,18 +147,13 @@ func TestExecFile(t *testing.T) { case nil: // success default: - t.Error(err) + t.Errorf("\n%s", err) } - resolve.AllowRecursion = false chunk.Done() } } } -func option(chunk, name string) bool { - return strings.Contains(chunk, "option:"+name) -} - // A fib is an iterable value representing the infinite Fibonacci sequence. type fib struct{} diff --git a/starlark/testdata/assign.star b/starlark/testdata/assign.star index 84b3660..ae97bb2 100644 --- a/starlark/testdata/assign.star +++ b/starlark/testdata/assign.star @@ -66,6 +66,7 @@ assert.eq(m, 3) assert.eq(n, 4) --- +# option:nesteddef # misc assignment load("assert.star", "assert") @@ -151,6 +152,7 @@ f() g = 1 --- +# option:nesteddef # free variable captured before assignment def f(): @@ -204,6 +206,7 @@ assert.fails(lambda: tuple, "global variable tuple referenced before assignment" tuple = () --- +# option:float option:set # Same as above, but set and float are dialect-specific; # we shouldn't notice any difference. load("assert.star", "assert") diff --git a/starlark/testdata/bool.star b/starlark/testdata/bool.star index 326f869..d83a8c2 100644 --- a/starlark/testdata/bool.star +++ b/starlark/testdata/bool.star @@ -39,10 +39,10 @@ assert.eq(1 if "" else 0, 0) # short-circuit evaluation of 'and' and 'or': # 'or' yields the first true operand, or the last if all are false. assert.eq(0 or "" or [] or 0, 0) -assert.eq(0 or "" or [] or 123 or 1 / 0, 123) -assert.fails(lambda : 0 or "" or [] or 0 or 1 / 0, "division by zero") +assert.eq(0 or "" or [] or 123 or 1 // 0, 123) +assert.fails(lambda : 0 or "" or [] or 0 or 1 // 0, "division by zero") # 'and' yields the first false operand, or the last if all are true. assert.eq(1 and "a" and [1] and 123, 123) -assert.eq(1 and "a" and [1] and 0 and 1 / 0, 0) -assert.fails(lambda : 1 and "a" and [1] and 123 and 1 / 0, "division by zero") +assert.eq(1 and "a" and [1] and 0 and 1 // 0, 0) +assert.fails(lambda : 1 and "a" and [1] and 123 and 1 // 0, "division by zero") diff --git a/starlark/testdata/builtins.star b/starlark/testdata/builtins.star index 64d7f23..799ac6d 100644 --- a/starlark/testdata/builtins.star +++ b/starlark/testdata/builtins.star @@ -1,4 +1,5 @@ # Tests of Starlark built-in functions +# option:float option:set load("assert.star", "assert") diff --git a/starlark/testdata/dict.star b/starlark/testdata/dict.star index 1bfd207..207d27d 100644 --- a/starlark/testdata/dict.star +++ b/starlark/testdata/dict.star @@ -1,4 +1,5 @@ # Tests of Starlark 'dict' +# option:nesteddef load("assert.star", "assert", "freeze") diff --git a/starlark/testdata/float.star b/starlark/testdata/float.star index b7ba8fe..ca076a4 100644 --- a/starlark/testdata/float.star +++ b/starlark/testdata/float.star @@ -1,4 +1,5 @@ # Tests of Starlark 'float' +# option:float option:set load("assert.star", "assert") diff --git a/starlark/testdata/function.star b/starlark/testdata/function.star index 49f7872..5e930ec 100644 --- a/starlark/testdata/function.star +++ b/starlark/testdata/function.star @@ -1,4 +1,5 @@ # Tests of Starlark 'function' +# option:nesteddef option:set # TODO(adonovan): # - add some introspection functions for looking at function values diff --git a/starlark/testdata/int.star b/starlark/testdata/int.star index 52e8af8..ca810d5 100644 --- a/starlark/testdata/int.star +++ b/starlark/testdata/int.star @@ -1,4 +1,5 @@ # Tests of Starlark 'int' +# option:bitwise option:float load("assert.star", "assert") diff --git a/starlark/testdata/list.star b/starlark/testdata/list.star index 9995c06..921de41 100644 --- a/starlark/testdata/list.star +++ b/starlark/testdata/list.star @@ -1,11 +1,12 @@ # Tests of Starlark 'list' +# option:nesteddef load("assert.star", "assert", "freeze") # literals assert.eq([], []) assert.eq([1], [1]) -assert.eq([1,], [1]) +assert.eq([1], [1]) assert.eq([1, 2], [1, 2]) assert.ne([1, 2, 3], [1, 2, 4]) @@ -15,31 +16,37 @@ assert.true(not []) # indexing, x[i] abc = list("abc".elems()) -assert.fails(lambda: abc[-4], "list index -1 out of range \\[0:3\\]") +assert.fails(lambda : abc[-4], "list index -1 out of range \\[0:3\\]") assert.eq(abc[-3], "a") assert.eq(abc[-2], "b") assert.eq(abc[-1], "c") assert.eq(abc[0], "a") assert.eq(abc[1], "b") assert.eq(abc[2], "c") -assert.fails(lambda: abc[3], "list index 3 out of range \\[0:3\\]") +assert.fails(lambda : abc[3], "list index 3 out of range \\[0:3\\]") # x[i] = ... x3 = [0, 1, 2] x3[1] = 2 x3[2] += 3 assert.eq(x3, [0, 2, 5]) -def f2(): x3[3] = 4 + +def f2(): + x3[3] = 4 + assert.fails(f2, "out of range") freeze(x3) -def f3(): x3[0] = 0 + +def f3(): + x3[0] = 0 + assert.fails(f3, "cannot assign to element of frozen list") assert.fails(x3.clear, "cannot clear frozen list") # list + list assert.eq([1, 2, 3] + [3, 4, 5], [1, 2, 3, 3, 4, 5]) -assert.fails(lambda: [1, 2] + (3, 4), "unknown.*list \+ tuple") -assert.fails(lambda: (1, 2) + [3, 4], "unknown.*tuple \+ list") +assert.fails(lambda : [1, 2] + (3, 4), "unknown.*list \+ tuple") +assert.fails(lambda : (1, 2) + [3, 4], "unknown.*tuple \+ list") # list * int, int * list assert.eq(abc * 0, []) @@ -54,17 +61,20 @@ assert.eq(3 * abc, ["a", "b", "c", "a", "b", "c", "a", "b", "c"]) # list comprehensions assert.eq([2 * x for x in [1, 2, 3]], [2, 4, 6]) assert.eq([2 * x for x in [1, 2, 3] if x > 1], [4, 6]) -assert.eq([(x, y) for x in [1, 2] for y in [3, 4]], - [(1, 3), (1, 4), (2, 3), (2, 4)]) -assert.eq([(x, y) for x in [1, 2] if x == 2 for y in [3, 4]],[(2, 3), (2, 4)]) +assert.eq( + [(x, y) for x in [1, 2] for y in [3, 4]], + [(1, 3), (1, 4), (2, 3), (2, 4)], +) +assert.eq([(x, y) for x in [1, 2] if x == 2 for y in [3, 4]], [(2, 3), (2, 4)]) assert.eq([2 * x for x in (1, 2, 3)], [2, 4, 6]) assert.eq([x for x in "abc".elems()], ["a", "b", "c"]) assert.eq([x for x in {"a": 1, "b": 2}], ["a", "b"]) assert.eq([(y, x) for x, y in {1: 2, 3: 4}.items()], [(2, 1), (4, 3)]) + # corner cases of parsing: -assert.eq([x for x in range(12) if x%2 == 0 if x%3 == 0], [0, 6]) -assert.eq([x for x in [1, 2] if lambda: None], [1, 2]) -assert.eq([x for x in [1, 2] if (lambda: 3 if True else 4)], [1, 2]) +assert.eq([x for x in range(12) if x % 2 == 0 if x % 3 == 0], [0, 6]) +assert.eq([x for x in [1, 2] if lambda : None], [1, 2]) +assert.eq([x for x in [1, 2] if (lambda : 3 if True else 4)], [1, 2]) # list function assert.eq(list(), []) @@ -76,22 +86,24 @@ a = [1, 2] b = [a for a in [3, 4]] assert.eq(a, [1, 2]) assert.eq(b, [3, 4]) + # ...or local to a function. def listcompblock(): - c = [1, 2] - d = [c for c in [3, 4]] - assert.eq(c, [1, 2]) - assert.eq(d, [3, 4]) + c = [1, 2] + d = [c for c in [3, 4]] + assert.eq(c, [1, 2]) + assert.eq(d, [3, 4]) + listcompblock() # list.pop -x4 = [1,2,3,4,5] +x4 = [1, 2, 3, 4, 5] assert.eq(x4.pop(), 5) -assert.eq(x4, [1,2,3,4]) +assert.eq(x4, [1, 2, 3, 4]) assert.eq(x4.pop(1), 2) -assert.eq(x4, [1,3,4]) +assert.eq(x4, [1, 3, 4]) assert.eq(x4.pop(0), 1) -assert.eq(x4, [3,4]) +assert.eq(x4, [3, 4]) # TODO(adonovan): test uses of list as sequence # (for loop, comprehension, library functions). @@ -100,50 +112,56 @@ assert.eq(x4, [3,4]) # y may be a sequence. # TODO: Test that side-effects of 'x' occur only once. def list_extend(): - a = [1, 2, 3] - b = a - a = a + [4] # creates a new list - assert.eq(a, [1, 2, 3, 4]) - assert.eq(b, [1, 2, 3]) # b is unchanged - - a = [1, 2, 3] - b = a - a += [4] # updates a (and thus b) in place - assert.eq(a, [1, 2, 3, 4]) - assert.eq(b, [1, 2, 3, 4]) # alias observes the change - - a = [1, 2, 3] - b = a - a.extend([4]) # updates existing list - assert.eq(a, [1, 2, 3, 4]) - assert.eq(b, [1, 2, 3, 4]) # alias observes the change + a = [1, 2, 3] + b = a + a = a + [4] # creates a new list + assert.eq(a, [1, 2, 3, 4]) + assert.eq(b, [1, 2, 3]) # b is unchanged + + a = [1, 2, 3] + b = a + a += [4] # updates a (and thus b) in place + assert.eq(a, [1, 2, 3, 4]) + assert.eq(b, [1, 2, 3, 4]) # alias observes the change + + a = [1, 2, 3] + b = a + a.extend([4]) # updates existing list + assert.eq(a, [1, 2, 3, 4]) + assert.eq(b, [1, 2, 3, 4]) # alias observes the change + list_extend() # Unlike list.extend(iterable), list += iterable makes its LHS name local. a_list = [] + def f4(): - a_list += [1] # binding use => a_list is a local var + a_list += [1] # binding use => a_list is a local var + assert.fails(f4, "local variable a_list referenced before assignment") # list += <not iterable> def f5(): - x = [] - x += 1 + x = [] + x += 1 + assert.fails(f5, "unknown binary op: list \\+ int") # frozen list += iterable def f6(): - x = [] - freeze(x) - x += [1] + x = [] + freeze(x) + x += [1] + assert.fails(f6, "cannot apply \\+= to frozen list") # list += hasfields (hasfields is not iterable but defines list+hasfields) def f7(): - x = [] - x += hasfields() - return x -assert.eq(f7(), 42) # weird, but exercises a corner case in list+=x. + x = [] + x += hasfields() + return x + +assert.eq(f7(), 42) # weird, but exercises a corner case in list+=x. # append x5 = [1, 2, 3] @@ -153,53 +171,57 @@ assert.eq(x5, [1, 2, 3, 4, "abc"]) # extend x5a = [1, 2, 3] -x5a.extend("abc".elems()) # string -x5a.extend((True, False)) # tuple +x5a.extend("abc".elems()) # string +x5a.extend((True, False)) # tuple assert.eq(x5a, [1, 2, 3, "a", "b", "c", True, False]) # list.insert def insert_at(index): - x = list(range(3)) - x.insert(index, 42) - return x + x = list(range(3)) + x.insert(index, 42) + return x + assert.eq(insert_at(-99), [42, 0, 1, 2]) assert.eq(insert_at(-2), [0, 42, 1, 2]) assert.eq(insert_at(-1), [0, 1, 42, 2]) -assert.eq(insert_at( 0), [42, 0, 1, 2]) -assert.eq(insert_at( 1), [0, 42, 1, 2]) -assert.eq(insert_at( 2), [0, 1, 42, 2]) -assert.eq(insert_at( 3), [0, 1, 2, 42]) -assert.eq(insert_at( 4), [0, 1, 2, 42]) +assert.eq(insert_at(0), [42, 0, 1, 2]) +assert.eq(insert_at(1), [0, 42, 1, 2]) +assert.eq(insert_at(2), [0, 1, 42, 2]) +assert.eq(insert_at(3), [0, 1, 2, 42]) +assert.eq(insert_at(4), [0, 1, 2, 42]) # list.remove def remove(v): - x = [3, 1, 4, 1] - x.remove(v) - return x + x = [3, 1, 4, 1] + x.remove(v) + return x + assert.eq(remove(3), [1, 4, 1]) assert.eq(remove(1), [3, 4, 1]) assert.eq(remove(4), [3, 1, 1]) -assert.fails(lambda: [3, 1, 4, 1].remove(42), "remove: element not found") +assert.fails(lambda : [3, 1, 4, 1].remove(42), "remove: element not found") # list.index bananas = list("bananas".elems()) -assert.eq(bananas.index('a'), 1) # bAnanas -assert.fails(lambda: bananas.index('d'), "value not in list") +assert.eq(bananas.index("a"), 1) # bAnanas +assert.fails(lambda : bananas.index("d"), "value not in list") + # start -assert.eq(bananas.index('a', -1000), 1) # bAnanas -assert.eq(bananas.index('a', 0), 1) # bAnanas -assert.eq(bananas.index('a', 1), 1) # bAnanas -assert.eq(bananas.index('a', 2), 3) # banAnas -assert.eq(bananas.index('a', 3), 3) # banAnas -assert.eq(bananas.index('b', 0), 0) # Bananas -assert.eq(bananas.index('n', -3), 4) # banaNas -assert.fails(lambda: bananas.index('n', -2), "value not in list") -assert.eq(bananas.index('s', -2), 6) # bananaS -assert.fails(lambda: bananas.index('b', 1), "value not in list") +assert.eq(bananas.index("a", -1000), 1) # bAnanas +assert.eq(bananas.index("a", 0), 1) # bAnanas +assert.eq(bananas.index("a", 1), 1) # bAnanas +assert.eq(bananas.index("a", 2), 3) # banAnas +assert.eq(bananas.index("a", 3), 3) # banAnas +assert.eq(bananas.index("b", 0), 0) # Bananas +assert.eq(bananas.index("n", -3), 4) # banaNas +assert.fails(lambda : bananas.index("n", -2), "value not in list") +assert.eq(bananas.index("s", -2), 6) # bananaS +assert.fails(lambda : bananas.index("b", 1), "value not in list") + # start, end -assert.eq(bananas.index('s', -1000, 7), 6) # bananaS -assert.fails(lambda: bananas.index('s', -1000, 6), "value not in list") -assert.fails(lambda: bananas.index('d', -1000, 1000), "value not in list") +assert.eq(bananas.index("s", -1000, 7), 6) # bananaS +assert.fails(lambda : bananas.index("s", -1000, 6), "value not in list") +assert.fails(lambda : bananas.index("d", -1000, 1000), "value not in list") # slicing, x[i:j:k] assert.eq(bananas[6::-2], list("snnb".elems())) @@ -211,33 +233,39 @@ assert.eq(bananas[100::-2], list("snnb".elems())) # iterator invalidation def iterator1(): - list = [0, 1, 2] - for x in list: - list[x] = 2 * x - return list + list = [0, 1, 2] + for x in list: + list[x] = 2 * x + return list + assert.fails(iterator1, "assign to element.* during iteration") def iterator2(): - list = [0, 1, 2] - for x in list: - list.remove(x) + list = [0, 1, 2] + for x in list: + list.remove(x) + assert.fails(iterator2, "remove.*during iteration") def iterator3(): - list = [0, 1, 2] - for x in list: - list.append(3) + list = [0, 1, 2] + for x in list: + list.append(3) + assert.fails(iterator3, "append.*during iteration") def iterator4(): - list = [0, 1, 2] - for x in list: - list.extend([3, 4]) + list = [0, 1, 2] + for x in list: + list.extend([3, 4]) + assert.fails(iterator4, "extend.*during iteration") def iterator5(): - def f(x): - x.append(4) - list = [1, 2, 3] - _ = [f(list) for x in list] + def f(x): + x.append(4) + + list = [1, 2, 3] + _ = [f(list) for x in list] + assert.fails(iterator5, "append.*during iteration") diff --git a/starlark/testdata/misc.star b/starlark/testdata/misc.star index 3bba7cb..b630ecc 100644 --- a/starlark/testdata/misc.star +++ b/starlark/testdata/misc.star @@ -36,6 +36,8 @@ # tuple slice # interpolate with %c, %% +# option:float + load("assert.star", "assert") # Ordered comparisons require values of the same type. diff --git a/starlark/testdata/set.star b/starlark/testdata/set.star index 6f0957d..61a4716 100644 --- a/starlark/testdata/set.star +++ b/starlark/testdata/set.star @@ -1,6 +1,7 @@ # Tests of Starlark 'set' +# option:set option:bitwise -# Sets are not (yet) a standard part of Starlark, so the features +# Sets are not a standard part of Starlark, so the features # tested in this file must be enabled in the application by setting # resolve.AllowSet. (All sets are created by calls to the 'set' # built-in or derived from operations on existing sets.) diff --git a/starlark/testdata/string.star b/starlark/testdata/string.star index 7042e1c..db95c2e 100644 --- a/starlark/testdata/string.star +++ b/starlark/testdata/string.star @@ -1,4 +1,5 @@ # Tests of Starlark 'string' +# option:float option:bitwise option:set load("assert.star", "assert") |