aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJay Conrod <jayconrod@gmail.com>2019-01-02 17:58:35 -0500
committerGitHub <noreply@github.com>2019-01-02 17:58:35 -0500
commitf9eba728dc959ed06f508aaac74f1fe710800c8f (patch)
tree4a7d9625e499b884b39b7e60a0d631b896305761
parent70a4f43bd14540d466ebd8601bbcd358d47ef7af (diff)
parentc0b6b768d91b2b919fd75c060b95f91ecb3b6d8a (diff)
downloadstarlark-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.go3
-rw-r--r--doc/spec.md65
-rw-r--r--resolve/resolve.go8
-rw-r--r--resolve/resolve_test.go25
-rw-r--r--resolve/testdata/resolve.star46
-rw-r--r--starlark/eval_test.go31
-rw-r--r--starlark/testdata/assign.star3
-rw-r--r--starlark/testdata/bool.star8
-rw-r--r--starlark/testdata/builtins.star1
-rw-r--r--starlark/testdata/dict.star1
-rw-r--r--starlark/testdata/float.star1
-rw-r--r--starlark/testdata/function.star1
-rw-r--r--starlark/testdata/int.star1
-rw-r--r--starlark/testdata/list.star216
-rw-r--r--starlark/testdata/misc.star2
-rw-r--r--starlark/testdata/set.star3
-rw-r--r--starlark/testdata/string.star1
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")