Skip to content

Commit 0070ade

Browse files
committed
starlark: add support for addressing
This change adds support for addressable variables and the unary prefix operators * and &, which are needed to correctly support Stargo (see wip-stargo branch), an upcoming extension that enables Starlark bindings for Go types and variables. Start reading at the starlark.Variable interface for background. All new features are behind the -addressing flag. Change-Id: I0258f5d38f85c0d03bb08dc98165b0335a88a98a
1 parent c122e65 commit 0070ade

12 files changed

Lines changed: 388 additions & 29 deletions

File tree

doc/spec.md

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1505,7 +1505,7 @@ Operand = identifier
15051505
| ListExpr | ListComp
15061506
| DictExpr | DictComp
15071507
| '(' [Expression] [,] ')'
1508-
| ('-' | '+') PrimaryExpr
1508+
| ('-' | '+' | '*' | '&') PrimaryExpr
15091509
.
15101510
15111511
DotSuffix = '.' identifier .
@@ -1637,13 +1637,15 @@ Examples:
16371637

16381638
### Unary operators
16391639

1640-
There are three unary operators, all appearing before their operand:
1641-
`+`, `-`, `~`, and `not`.
1640+
Starlark has the following unary operators, which all appear before their operand:
1641+
`+`, `-`, `~`, `*`, `&`, and `not`.
16421642

16431643
```grammar {.good}
16441644
UnaryExpr = '+' PrimaryExpr
16451645
| '-' PrimaryExpr
16461646
| '~' PrimaryExpr
1647+
| '*' PrimaryExpr
1648+
| '&' PrimaryExpr
16471649
| 'not' Test
16481650
.
16491651
```
@@ -1690,9 +1692,21 @@ The bitwise inversion of x is defined as -(x+1).
16901692
~0 # -1
16911693
```
16921694

1695+
The Go implementation of Starlark additionally has the unary
1696+
prefix operators `*` and `&`, in order to support Stargo, which
1697+
provides Starlark bindings for Go variables.
1698+
These operators are not part of standard Starlark, and must be enabled
1699+
in Go by the `-addressing` flag.
1700+
1701+
The expression `*ptr` dereferences a pointer value, `ptr`.
1702+
The expression `&expr` returns the address of an expression `expr`;
1703+
it may be applied only to field selection or index expressions
1704+
such as `&a[i]`, `&x.f`, or `&x[i].f[j].g`.
1705+
Consult the Stargo documentation for more details.
1706+
16931707
<b>Implementation note:</b>
16941708
The parser in the Java implementation of Starlark does not accept unary
1695-
`+` and `~` expressions.
1709+
`+` and `~` expressions, nor `*` and `&` as unary operators.
16961710

16971711
### Binary operators
16981712

internal/compile/compile.go

Lines changed: 104 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import (
3737
const debug = false // TODO(adonovan): use a bitmap of options; and regexp to match files
3838

3939
// Increment this to force recompilation of saved bytecode files.
40-
const Version = 5
40+
const Version = 6
4141

4242
type Opcode uint8
4343

@@ -80,9 +80,10 @@ const (
8080

8181
IN
8282

83-
// unary operators
83+
// unary operators (order of +/-/* must match Token)
8484
UPLUS // x UPLUS x
8585
UMINUS // x UMINUS -x
86+
USTAR // x USTAR *x
8687
TILDE // x TILDE ~x
8788

8889
NONE // - NONE None
@@ -94,13 +95,16 @@ const (
9495
NOT // value NOT bool
9596
RETURN // value RETURN -
9697
SETINDEX // a i new SETINDEX -
97-
INDEX // a i INDEX elem
98+
INDEX // a i INDEX elem elem = a[i]
9899
SETDICT // dict key value SETDICT -
99100
SETDICTUNIQ // dict key value SETDICTUNIQ -
100101
APPEND // list elem APPEND -
101102
SLICE // x lo hi step SLICE slice
102-
INPLACE_ADD // x y INPLACE_ADD z where z is x+y or x.extend(y)
103+
INPLACE_ADD // x y INPLACE_ADD z where z is x+y or x.extend(y)
103104
MAKEDICT // - MAKEDICT dict
105+
ADDRESS // var ADDRESS addr fails if not Variable [stargo only]
106+
VALUE // var VALUE value identity if not Variable [stargo only]
107+
SETVALUE // var x SETVALUE - fails if not Variable [stargo only]
104108

105109
// --- opcodes with an argument must go below this line ---
106110

@@ -139,6 +143,7 @@ const (
139143
// TODO(adonovan): add dynamic checks for missing opcodes in the tables below.
140144

141145
var opcodeNames = [...]string{
146+
ADDRESS: "address",
142147
AMP: "amp",
143148
APPEND: "append",
144149
ATTR: "attr",
@@ -186,12 +191,14 @@ var opcodeNames = [...]string{
186191
POP: "pop",
187192
PREDECLARED: "predeclared",
188193
RETURN: "return",
194+
VALUE: "value",
189195
SETDICT: "setdict",
190196
SETDICTUNIQ: "setdictuniq",
191197
SETFIELD: "setfield",
192198
SETGLOBAL: "setglobal",
193199
SETINDEX: "setindex",
194200
SETLOCAL: "setlocal",
201+
SETVALUE: "setvalue",
195202
SLASH: "slash",
196203
SLASHSLASH: "slashslash",
197204
SLICE: "slice",
@@ -202,13 +209,15 @@ var opcodeNames = [...]string{
202209
UNIVERSAL: "universal",
203210
UNPACK: "unpack",
204211
UPLUS: "uplus",
212+
USTAR: "ustar",
205213
}
206214

207215
const variableStackEffect = 0x7f
208216

209217
// stackEffect records the effect on the size of the operand stack of
210218
// each kind of instruction. For some instructions this requires computation.
211219
var stackEffect = [...]int8{
220+
ADDRESS: 0,
212221
AMP: -1,
213222
APPEND: -2,
214223
ATTR: 0,
@@ -261,6 +270,7 @@ var stackEffect = [...]int8{
261270
SETGLOBAL: -1,
262271
SETINDEX: -3,
263272
SETLOCAL: -1,
273+
SETVALUE: -2,
264274
SLASH: -1,
265275
SLASHSLASH: -1,
266276
SLICE: -3,
@@ -270,6 +280,8 @@ var stackEffect = [...]int8{
270280
UNIVERSAL: +1,
271281
UNPACK: variableStackEffect,
272282
UPLUS: 0,
283+
USTAR: 0,
284+
VALUE: 0,
273285
}
274286

275287
func (op Opcode) String() string {
@@ -1005,25 +1017,41 @@ func (fcomp *fcomp) stmt(stmt syntax.Stmt) {
10051017
fcomp.set(lhs)
10061018
}
10071019

1020+
case *syntax.UnaryExpr:
1021+
// *x = ...
1022+
fcomp.expr(lhs.X)
1023+
fcomp.emit(DUP)
1024+
fcomp.emit(VALUE)
1025+
set = func() {
1026+
fcomp.setPos(lhs.OpPos) // op == STAR
1027+
fcomp.emit(SETVALUE)
1028+
}
1029+
10081030
case *syntax.IndexExpr:
10091031
// x[y] = ...
1010-
fcomp.expr(lhs.X)
1032+
fcomp.addr(lhs.X)
10111033
fcomp.expr(lhs.Y)
10121034
fcomp.emit(DUP2)
10131035
fcomp.setPos(lhs.Lbrack)
10141036
fcomp.emit(INDEX)
1037+
if resolve.AllowAddressing {
1038+
fcomp.emit(VALUE)
1039+
}
10151040
set = func() {
10161041
fcomp.setPos(lhs.Lbrack)
10171042
fcomp.emit(SETINDEX)
10181043
}
10191044

10201045
case *syntax.DotExpr:
10211046
// x.f = ...
1022-
fcomp.expr(lhs.X)
1047+
fcomp.addr(lhs.X)
10231048
fcomp.emit(DUP)
10241049
name := fcomp.pcomp.nameIndex(lhs.Name.Name)
10251050
fcomp.setPos(lhs.Dot)
10261051
fcomp.emit1(ATTR, name)
1052+
if resolve.AllowAddressing {
1053+
fcomp.emit(VALUE)
1054+
}
10271055
set = func() {
10281056
fcomp.setPos(lhs.Dot)
10291057
fcomp.emit1(SETFIELD, name)
@@ -1143,7 +1171,7 @@ func (fcomp *fcomp) assign(pos syntax.Position, lhs syntax.Expr) {
11431171

11441172
case *syntax.IndexExpr:
11451173
// x[y] = rhs
1146-
fcomp.expr(lhs.X)
1174+
fcomp.addr(lhs.X)
11471175
fcomp.emit(EXCH)
11481176
fcomp.expr(lhs.Y)
11491177
fcomp.emit(EXCH)
@@ -1152,11 +1180,19 @@ func (fcomp *fcomp) assign(pos syntax.Position, lhs syntax.Expr) {
11521180

11531181
case *syntax.DotExpr:
11541182
// x.f = rhs
1155-
fcomp.expr(lhs.X)
1183+
fcomp.addr(lhs.X)
11561184
fcomp.emit(EXCH)
11571185
fcomp.setPos(lhs.Dot)
11581186
fcomp.emit1(SETFIELD, fcomp.pcomp.nameIndex(lhs.Name.Name))
11591187

1188+
case *syntax.UnaryExpr:
1189+
// *x = rhs
1190+
fcomp.expr(lhs.X)
1191+
fcomp.emit(USTAR)
1192+
fcomp.emit(EXCH)
1193+
fcomp.setPos(lhs.OpPos) // op == STAR
1194+
fcomp.emit(SETVALUE)
1195+
11601196
default:
11611197
panic(lhs)
11621198
}
@@ -1170,6 +1206,47 @@ func (fcomp *fcomp) assignSequence(pos syntax.Position, lhs []syntax.Expr) {
11701206
}
11711207
}
11721208

1209+
// addr emits code for a chain of x.f and a[i] operations such as a.f[i].g[j].
1210+
//
1211+
// In Addressing mode, all nonrecursive calls to addr() (a chain of ATTR
1212+
// and INDEX ops) must be followed by one of ADDRESS, SETFIELD,
1213+
// SETINDEX, or VALUE:
1214+
//
1215+
// &(expr.f[i].g) expr ATTR<f> i INDEX ATTR<g> ADDRESS
1216+
// (expr.f[i].g).x = y expr ATTR<f> i INDEX ATTR<g> y SETFIELD<x>
1217+
// (expr.f[i].g)[j] = y expr ATTR<f> i INDEX ATTR<g> y j SETINDEX
1218+
// _ = expr.f[i].g expr ATTR<f> i INDEX ATTR<g> VALUE
1219+
//
1220+
// We could in principle extend this scheme to all variables (such as x
1221+
// in x.f)), not just the x[i] and x.f operations applied to them, but
1222+
// it would add a cost to every variable lookup, and in practice it is
1223+
// unnecessary as most starlark.Variables live in a module such as http
1224+
// for http.DefaultServeMux, so they are acccessed using a dot expression.
1225+
//
1226+
// See comments at starlark.Variable for background.
1227+
//
1228+
func (fcomp *fcomp) addr(e syntax.Expr) {
1229+
switch e := e.(type) {
1230+
case *syntax.ParenExpr:
1231+
fcomp.addr(e.X)
1232+
1233+
case *syntax.DotExpr:
1234+
fcomp.addr(e.X)
1235+
fcomp.setPos(e.Dot)
1236+
fcomp.emit1(ATTR, fcomp.pcomp.nameIndex(e.Name.Name))
1237+
1238+
case *syntax.IndexExpr:
1239+
fcomp.addr(e.X)
1240+
fcomp.expr(e.Y)
1241+
fcomp.setPos(e.Lbrack)
1242+
fcomp.emit(INDEX)
1243+
1244+
default:
1245+
fcomp.expr(e)
1246+
1247+
}
1248+
}
1249+
11731250
func (fcomp *fcomp) expr(e syntax.Expr) {
11741251
switch e := e.(type) {
11751252
case *syntax.ParenExpr:
@@ -1207,10 +1284,10 @@ func (fcomp *fcomp) expr(e syntax.Expr) {
12071284
fcomp.block = done
12081285

12091286
case *syntax.IndexExpr:
1210-
fcomp.expr(e.X)
1211-
fcomp.expr(e.Y)
1212-
fcomp.setPos(e.Lbrack)
1213-
fcomp.emit(INDEX)
1287+
fcomp.addr(e)
1288+
if resolve.AllowAddressing {
1289+
fcomp.emit(VALUE)
1290+
}
12141291

12151292
case *syntax.SliceExpr:
12161293
fcomp.setPos(e.Lbrack)
@@ -1255,13 +1332,23 @@ func (fcomp *fcomp) expr(e syntax.Expr) {
12551332
}
12561333

12571334
case *syntax.UnaryExpr:
1258-
fcomp.expr(e.X)
1335+
switch e.Op {
1336+
case syntax.AMP, syntax.STAR:
1337+
fcomp.addr(e.X)
1338+
default:
1339+
fcomp.expr(e.X)
1340+
}
12591341
fcomp.setPos(e.OpPos)
12601342
switch e.Op {
1343+
case syntax.AMP:
1344+
fcomp.emit(ADDRESS)
12611345
case syntax.MINUS:
12621346
fcomp.emit(UMINUS)
12631347
case syntax.PLUS:
12641348
fcomp.emit(UPLUS)
1349+
case syntax.STAR:
1350+
fcomp.emit(USTAR)
1351+
fcomp.emit(VALUE)
12651352
case syntax.NOT:
12661353
fcomp.emit(NOT)
12671354
case syntax.TILDE:
@@ -1317,9 +1404,10 @@ func (fcomp *fcomp) expr(e syntax.Expr) {
13171404
}
13181405

13191406
case *syntax.DotExpr:
1320-
fcomp.expr(e.X)
1321-
fcomp.setPos(e.Dot)
1322-
fcomp.emit1(ATTR, fcomp.pcomp.nameIndex(e.Name.Name))
1407+
fcomp.addr(e)
1408+
if resolve.AllowAddressing {
1409+
fcomp.emit(VALUE)
1410+
}
13231411

13241412
case *syntax.CallExpr:
13251413
fcomp.call(e)

0 commit comments

Comments
 (0)