feat: kubesphere 4.0 (#6115)
* feat: kubesphere 4.0 Signed-off-by: ci-bot <ci-bot@kubesphere.io> * feat: kubesphere 4.0 Signed-off-by: ci-bot <ci-bot@kubesphere.io> --------- Signed-off-by: ci-bot <ci-bot@kubesphere.io> Co-authored-by: ks-ci-bot <ks-ci-bot@example.com> Co-authored-by: joyceliu <joyceliu@yunify.com>
This commit is contained in:
committed by
GitHub
parent
b5015ec7b9
commit
447a51f08b
379
vendor/go.starlark.net/internal/compile/compile.go
generated
vendored
379
vendor/go.starlark.net/internal/compile/compile.go
generated
vendored
@@ -33,6 +33,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"go.starlark.net/resolve"
|
||||
@@ -46,7 +47,7 @@ var Disassemble = false
|
||||
const debug = false // make code generation verbose, for debugging the compiler
|
||||
|
||||
// Increment this to force recompilation of saved bytecode files.
|
||||
const Version = 10
|
||||
const Version = 13
|
||||
|
||||
type Opcode uint8
|
||||
|
||||
@@ -99,20 +100,19 @@ const (
|
||||
FALSE // - FALSE False
|
||||
MANDATORY // - MANDATORY Mandatory [sentinel value for required kwonly args]
|
||||
|
||||
ITERPUSH // iterable ITERPUSH - [pushes the iterator stack]
|
||||
ITERPOP // - ITERPOP - [pops the iterator stack]
|
||||
NOT // value NOT bool
|
||||
RETURN // value RETURN -
|
||||
SETINDEX // a i new SETINDEX -
|
||||
INDEX // a i INDEX elem
|
||||
SETDICT // dict key value SETDICT -
|
||||
SETDICTUNIQ // dict key value SETDICTUNIQ -
|
||||
APPEND // list elem APPEND -
|
||||
SLICE // x lo hi step SLICE slice
|
||||
INPLACE_ADD // x y INPLACE_ADD z where z is x+y or x.extend(y)
|
||||
MAKEDICT // - MAKEDICT dict
|
||||
SETCELL // value cell SETCELL -
|
||||
CELL // cell CELL value
|
||||
ITERPUSH // iterable ITERPUSH - [pushes the iterator stack]
|
||||
ITERPOP // - ITERPOP - [pops the iterator stack]
|
||||
NOT // value NOT bool
|
||||
RETURN // value RETURN -
|
||||
SETINDEX // a i new SETINDEX -
|
||||
INDEX // a i INDEX elem
|
||||
SETDICT // dict key value SETDICT -
|
||||
SETDICTUNIQ // dict key value SETDICTUNIQ -
|
||||
APPEND // list elem APPEND -
|
||||
SLICE // x lo hi step SLICE slice
|
||||
INPLACE_ADD // x y INPLACE_ADD z where z is x+y or x.extend(y)
|
||||
INPLACE_PIPE // x y INPLACE_PIPE z where z is x|y
|
||||
MAKEDICT // - MAKEDICT dict
|
||||
|
||||
// --- opcodes with an argument must go below this line ---
|
||||
|
||||
@@ -122,21 +122,24 @@ const (
|
||||
ITERJMP // - ITERJMP<addr> elem (and fall through) [acts on topmost iterator]
|
||||
// or: - ITERJMP<addr> - (and jump)
|
||||
|
||||
CONSTANT // - CONSTANT<constant> value
|
||||
MAKETUPLE // x1 ... xn MAKETUPLE<n> tuple
|
||||
MAKELIST // x1 ... xn MAKELIST<n> list
|
||||
MAKEFUNC // defaults+freevars MAKEFUNC<func> fn
|
||||
LOAD // from1 ... fromN module LOAD<n> v1 ... vN
|
||||
SETLOCAL // value SETLOCAL<local> -
|
||||
SETGLOBAL // value SETGLOBAL<global> -
|
||||
LOCAL // - LOCAL<local> value
|
||||
FREE // - FREE<freevar> cell
|
||||
GLOBAL // - GLOBAL<global> value
|
||||
PREDECLARED // - PREDECLARED<name> value
|
||||
UNIVERSAL // - UNIVERSAL<name> value
|
||||
ATTR // x ATTR<name> y y = x.name
|
||||
SETFIELD // x y SETFIELD<name> - x.name = y
|
||||
UNPACK // iterable UNPACK<n> vn ... v1
|
||||
CONSTANT // - CONSTANT<constant> value
|
||||
MAKETUPLE // x1 ... xn MAKETUPLE<n> tuple
|
||||
MAKELIST // x1 ... xn MAKELIST<n> list
|
||||
MAKEFUNC // defaults+freevars MAKEFUNC<func> fn
|
||||
LOAD // from1 ... fromN module LOAD<n> v1 ... vN
|
||||
SETLOCAL // value SETLOCAL<local> -
|
||||
SETGLOBAL // value SETGLOBAL<global> -
|
||||
LOCAL // - LOCAL<local> value
|
||||
FREE // - FREE<freevar> cell
|
||||
FREECELL // - FREECELL<freevar> value (content of FREE cell)
|
||||
LOCALCELL // - LOCALCELL<local> value (content of LOCAL cell)
|
||||
SETLOCALCELL // value SETLOCALCELL<local> - (set content of LOCAL cell)
|
||||
GLOBAL // - GLOBAL<global> value
|
||||
PREDECLARED // - PREDECLARED<name> value
|
||||
UNIVERSAL // - UNIVERSAL<name> value
|
||||
ATTR // x ATTR<name> y y = x.name
|
||||
SETFIELD // x y SETFIELD<name> - x.name = y
|
||||
UNPACK // iterable UNPACK<n> vn ... v1
|
||||
|
||||
// n>>8 is #positional args and n&0xff is #named args (pairs).
|
||||
CALL // fn positional named CALL<n> result
|
||||
@@ -151,72 +154,74 @@ const (
|
||||
// TODO(adonovan): add dynamic checks for missing opcodes in the tables below.
|
||||
|
||||
var opcodeNames = [...]string{
|
||||
AMP: "amp",
|
||||
APPEND: "append",
|
||||
ATTR: "attr",
|
||||
CALL: "call",
|
||||
CALL_KW: "call_kw ",
|
||||
CALL_VAR: "call_var",
|
||||
CALL_VAR_KW: "call_var_kw",
|
||||
CELL: "cell",
|
||||
CIRCUMFLEX: "circumflex",
|
||||
CJMP: "cjmp",
|
||||
CONSTANT: "constant",
|
||||
DUP2: "dup2",
|
||||
DUP: "dup",
|
||||
EQL: "eql",
|
||||
EXCH: "exch",
|
||||
FALSE: "false",
|
||||
FREE: "free",
|
||||
GE: "ge",
|
||||
GLOBAL: "global",
|
||||
GT: "gt",
|
||||
GTGT: "gtgt",
|
||||
IN: "in",
|
||||
INDEX: "index",
|
||||
INPLACE_ADD: "inplace_add",
|
||||
ITERJMP: "iterjmp",
|
||||
ITERPOP: "iterpop",
|
||||
ITERPUSH: "iterpush",
|
||||
JMP: "jmp",
|
||||
LE: "le",
|
||||
LOAD: "load",
|
||||
LOCAL: "local",
|
||||
LT: "lt",
|
||||
LTLT: "ltlt",
|
||||
MAKEDICT: "makedict",
|
||||
MAKEFUNC: "makefunc",
|
||||
MAKELIST: "makelist",
|
||||
MAKETUPLE: "maketuple",
|
||||
MANDATORY: "mandatory",
|
||||
MINUS: "minus",
|
||||
NEQ: "neq",
|
||||
NONE: "none",
|
||||
NOP: "nop",
|
||||
NOT: "not",
|
||||
PERCENT: "percent",
|
||||
PIPE: "pipe",
|
||||
PLUS: "plus",
|
||||
POP: "pop",
|
||||
PREDECLARED: "predeclared",
|
||||
RETURN: "return",
|
||||
SETCELL: "setcell",
|
||||
SETDICT: "setdict",
|
||||
SETDICTUNIQ: "setdictuniq",
|
||||
SETFIELD: "setfield",
|
||||
SETGLOBAL: "setglobal",
|
||||
SETINDEX: "setindex",
|
||||
SETLOCAL: "setlocal",
|
||||
SLASH: "slash",
|
||||
SLASHSLASH: "slashslash",
|
||||
SLICE: "slice",
|
||||
STAR: "star",
|
||||
TILDE: "tilde",
|
||||
TRUE: "true",
|
||||
UMINUS: "uminus",
|
||||
UNIVERSAL: "universal",
|
||||
UNPACK: "unpack",
|
||||
UPLUS: "uplus",
|
||||
AMP: "amp",
|
||||
APPEND: "append",
|
||||
ATTR: "attr",
|
||||
CALL: "call",
|
||||
CALL_KW: "call_kw ",
|
||||
CALL_VAR: "call_var",
|
||||
CALL_VAR_KW: "call_var_kw",
|
||||
CIRCUMFLEX: "circumflex",
|
||||
CJMP: "cjmp",
|
||||
CONSTANT: "constant",
|
||||
DUP2: "dup2",
|
||||
DUP: "dup",
|
||||
EQL: "eql",
|
||||
EXCH: "exch",
|
||||
FALSE: "false",
|
||||
FREE: "free",
|
||||
FREECELL: "freecell",
|
||||
GE: "ge",
|
||||
GLOBAL: "global",
|
||||
GT: "gt",
|
||||
GTGT: "gtgt",
|
||||
IN: "in",
|
||||
INDEX: "index",
|
||||
INPLACE_ADD: "inplace_add",
|
||||
INPLACE_PIPE: "inplace_pipe",
|
||||
ITERJMP: "iterjmp",
|
||||
ITERPOP: "iterpop",
|
||||
ITERPUSH: "iterpush",
|
||||
JMP: "jmp",
|
||||
LE: "le",
|
||||
LOAD: "load",
|
||||
LOCAL: "local",
|
||||
LOCALCELL: "localcell",
|
||||
LT: "lt",
|
||||
LTLT: "ltlt",
|
||||
MAKEDICT: "makedict",
|
||||
MAKEFUNC: "makefunc",
|
||||
MAKELIST: "makelist",
|
||||
MAKETUPLE: "maketuple",
|
||||
MANDATORY: "mandatory",
|
||||
MINUS: "minus",
|
||||
NEQ: "neq",
|
||||
NONE: "none",
|
||||
NOP: "nop",
|
||||
NOT: "not",
|
||||
PERCENT: "percent",
|
||||
PIPE: "pipe",
|
||||
PLUS: "plus",
|
||||
POP: "pop",
|
||||
PREDECLARED: "predeclared",
|
||||
RETURN: "return",
|
||||
SETDICT: "setdict",
|
||||
SETDICTUNIQ: "setdictuniq",
|
||||
SETFIELD: "setfield",
|
||||
SETGLOBAL: "setglobal",
|
||||
SETINDEX: "setindex",
|
||||
SETLOCAL: "setlocal",
|
||||
SETLOCALCELL: "setlocalcell",
|
||||
SLASH: "slash",
|
||||
SLASHSLASH: "slashslash",
|
||||
SLICE: "slice",
|
||||
STAR: "star",
|
||||
TILDE: "tilde",
|
||||
TRUE: "true",
|
||||
UMINUS: "uminus",
|
||||
UNIVERSAL: "universal",
|
||||
UNPACK: "unpack",
|
||||
UPLUS: "uplus",
|
||||
}
|
||||
|
||||
const variableStackEffect = 0x7f
|
||||
@@ -224,70 +229,72 @@ const variableStackEffect = 0x7f
|
||||
// stackEffect records the effect on the size of the operand stack of
|
||||
// each kind of instruction. For some instructions this requires computation.
|
||||
var stackEffect = [...]int8{
|
||||
AMP: -1,
|
||||
APPEND: -2,
|
||||
ATTR: 0,
|
||||
CALL: variableStackEffect,
|
||||
CALL_KW: variableStackEffect,
|
||||
CALL_VAR: variableStackEffect,
|
||||
CALL_VAR_KW: variableStackEffect,
|
||||
CELL: 0,
|
||||
CIRCUMFLEX: -1,
|
||||
CJMP: -1,
|
||||
CONSTANT: +1,
|
||||
DUP2: +2,
|
||||
DUP: +1,
|
||||
EQL: -1,
|
||||
FALSE: +1,
|
||||
FREE: +1,
|
||||
GE: -1,
|
||||
GLOBAL: +1,
|
||||
GT: -1,
|
||||
GTGT: -1,
|
||||
IN: -1,
|
||||
INDEX: -1,
|
||||
INPLACE_ADD: -1,
|
||||
ITERJMP: variableStackEffect,
|
||||
ITERPOP: 0,
|
||||
ITERPUSH: -1,
|
||||
JMP: 0,
|
||||
LE: -1,
|
||||
LOAD: -1,
|
||||
LOCAL: +1,
|
||||
LT: -1,
|
||||
LTLT: -1,
|
||||
MAKEDICT: +1,
|
||||
MAKEFUNC: 0,
|
||||
MAKELIST: variableStackEffect,
|
||||
MAKETUPLE: variableStackEffect,
|
||||
MANDATORY: +1,
|
||||
MINUS: -1,
|
||||
NEQ: -1,
|
||||
NONE: +1,
|
||||
NOP: 0,
|
||||
NOT: 0,
|
||||
PERCENT: -1,
|
||||
PIPE: -1,
|
||||
PLUS: -1,
|
||||
POP: -1,
|
||||
PREDECLARED: +1,
|
||||
RETURN: -1,
|
||||
SETCELL: -2,
|
||||
SETDICT: -3,
|
||||
SETDICTUNIQ: -3,
|
||||
SETFIELD: -2,
|
||||
SETGLOBAL: -1,
|
||||
SETINDEX: -3,
|
||||
SETLOCAL: -1,
|
||||
SLASH: -1,
|
||||
SLASHSLASH: -1,
|
||||
SLICE: -3,
|
||||
STAR: -1,
|
||||
TRUE: +1,
|
||||
UMINUS: 0,
|
||||
UNIVERSAL: +1,
|
||||
UNPACK: variableStackEffect,
|
||||
UPLUS: 0,
|
||||
AMP: -1,
|
||||
APPEND: -2,
|
||||
ATTR: 0,
|
||||
CALL: variableStackEffect,
|
||||
CALL_KW: variableStackEffect,
|
||||
CALL_VAR: variableStackEffect,
|
||||
CALL_VAR_KW: variableStackEffect,
|
||||
CIRCUMFLEX: -1,
|
||||
CJMP: -1,
|
||||
CONSTANT: +1,
|
||||
DUP2: +2,
|
||||
DUP: +1,
|
||||
EQL: -1,
|
||||
FALSE: +1,
|
||||
FREE: +1,
|
||||
FREECELL: +1,
|
||||
GE: -1,
|
||||
GLOBAL: +1,
|
||||
GT: -1,
|
||||
GTGT: -1,
|
||||
IN: -1,
|
||||
INDEX: -1,
|
||||
INPLACE_ADD: -1,
|
||||
INPLACE_PIPE: -1,
|
||||
ITERJMP: variableStackEffect,
|
||||
ITERPOP: 0,
|
||||
ITERPUSH: -1,
|
||||
JMP: 0,
|
||||
LE: -1,
|
||||
LOAD: -1,
|
||||
LOCAL: +1,
|
||||
LOCALCELL: +1,
|
||||
LT: -1,
|
||||
LTLT: -1,
|
||||
MAKEDICT: +1,
|
||||
MAKEFUNC: 0,
|
||||
MAKELIST: variableStackEffect,
|
||||
MAKETUPLE: variableStackEffect,
|
||||
MANDATORY: +1,
|
||||
MINUS: -1,
|
||||
NEQ: -1,
|
||||
NONE: +1,
|
||||
NOP: 0,
|
||||
NOT: 0,
|
||||
PERCENT: -1,
|
||||
PIPE: -1,
|
||||
PLUS: -1,
|
||||
POP: -1,
|
||||
PREDECLARED: +1,
|
||||
RETURN: -1,
|
||||
SETLOCALCELL: -1,
|
||||
SETDICT: -3,
|
||||
SETDICTUNIQ: -3,
|
||||
SETFIELD: -2,
|
||||
SETGLOBAL: -1,
|
||||
SETINDEX: -3,
|
||||
SETLOCAL: -1,
|
||||
SLASH: -1,
|
||||
SLASHSLASH: -1,
|
||||
SLICE: -3,
|
||||
STAR: -1,
|
||||
TRUE: +1,
|
||||
UMINUS: 0,
|
||||
UNIVERSAL: +1,
|
||||
UNPACK: variableStackEffect,
|
||||
UPLUS: 0,
|
||||
}
|
||||
|
||||
func (op Opcode) String() string {
|
||||
@@ -306,12 +313,15 @@ func (op Opcode) String() string {
|
||||
type Program struct {
|
||||
Loads []Binding // name (really, string) and position of each load stmt
|
||||
Names []string // names of attributes and predeclared variables
|
||||
Constants []interface{} // = string | int64 | float64 | *big.Int
|
||||
Constants []interface{} // = string | int64 | float64 | *big.Int | Bytes
|
||||
Functions []*Funcode
|
||||
Globals []Binding // for error messages and tracing
|
||||
Toplevel *Funcode // module initialization function
|
||||
}
|
||||
|
||||
// The type of a bytes literal value, to distinguish from text string.
|
||||
type Bytes string
|
||||
|
||||
// A Funcode is the code of a compiled Starlark function.
|
||||
//
|
||||
// Funcodes are serialized by the encoder.function method,
|
||||
@@ -860,6 +870,8 @@ func PrintOp(fn *Funcode, pc uint32, op Opcode, arg uint32) {
|
||||
switch x := fn.Prog.Constants[arg].(type) {
|
||||
case string:
|
||||
comment = strconv.Quote(x)
|
||||
case Bytes:
|
||||
comment = "b" + strconv.Quote(string(x))
|
||||
default:
|
||||
comment = fmt.Sprint(x)
|
||||
}
|
||||
@@ -994,9 +1006,7 @@ func (fcomp *fcomp) set(id *syntax.Ident) {
|
||||
case resolve.Local:
|
||||
fcomp.emit1(SETLOCAL, uint32(bind.Index))
|
||||
case resolve.Cell:
|
||||
// TODO(adonovan): opt: make a single op for LOCAL<n>, SETCELL.
|
||||
fcomp.emit1(LOCAL, uint32(bind.Index))
|
||||
fcomp.emit(SETCELL)
|
||||
fcomp.emit1(SETLOCALCELL, uint32(bind.Index))
|
||||
case resolve.Global:
|
||||
fcomp.emit1(SETGLOBAL, uint32(bind.Index))
|
||||
default:
|
||||
@@ -1014,13 +1024,9 @@ func (fcomp *fcomp) lookup(id *syntax.Ident) {
|
||||
case resolve.Local:
|
||||
fcomp.emit1(LOCAL, uint32(bind.Index))
|
||||
case resolve.Free:
|
||||
// TODO(adonovan): opt: make a single op for FREE<n>, CELL.
|
||||
fcomp.emit1(FREE, uint32(bind.Index))
|
||||
fcomp.emit(CELL)
|
||||
fcomp.emit1(FREECELL, uint32(bind.Index))
|
||||
case resolve.Cell:
|
||||
// TODO(adonovan): opt: make a single op for LOCAL<n>, CELL.
|
||||
fcomp.emit1(LOCAL, uint32(bind.Index))
|
||||
fcomp.emit(CELL)
|
||||
fcomp.emit1(LOCALCELL, uint32(bind.Index))
|
||||
case resolve.Global:
|
||||
fcomp.emit1(GLOBAL, uint32(bind.Index))
|
||||
case resolve.Predeclared:
|
||||
@@ -1142,11 +1148,16 @@ func (fcomp *fcomp) stmt(stmt syntax.Stmt) {
|
||||
|
||||
fcomp.expr(stmt.RHS)
|
||||
|
||||
if stmt.Op == syntax.PLUS_EQ {
|
||||
// Allow the runtime to optimize list += iterable.
|
||||
// In-place x+=y and x|=y have special semantics:
|
||||
// the resulting x aliases the original x.
|
||||
switch stmt.Op {
|
||||
case syntax.PLUS_EQ:
|
||||
fcomp.setPos(stmt.OpPos)
|
||||
fcomp.emit(INPLACE_ADD)
|
||||
} else {
|
||||
case syntax.PIPE_EQ:
|
||||
fcomp.setPos(stmt.OpPos)
|
||||
fcomp.emit(INPLACE_PIPE)
|
||||
default:
|
||||
fcomp.binop(stmt.OpPos, stmt.Op-syntax.PLUS_EQ+syntax.PLUS)
|
||||
}
|
||||
set()
|
||||
@@ -1286,8 +1297,12 @@ func (fcomp *fcomp) expr(e syntax.Expr) {
|
||||
fcomp.lookup(e)
|
||||
|
||||
case *syntax.Literal:
|
||||
// e.Value is int64, float64, *bigInt, or string.
|
||||
fcomp.emit1(CONSTANT, fcomp.pcomp.constantIndex(e.Value))
|
||||
// e.Value is int64, float64, *bigInt, string
|
||||
v := e.Value
|
||||
if e.Token == syntax.BYTES {
|
||||
v = Bytes(v.(string))
|
||||
}
|
||||
fcomp.emit1(CONSTANT, fcomp.pcomp.constantIndex(v))
|
||||
|
||||
case *syntax.ListExpr:
|
||||
for _, x := range e.List {
|
||||
@@ -1525,7 +1540,7 @@ func (fcomp *fcomp) plus(e *syntax.BinaryExpr) {
|
||||
}
|
||||
|
||||
// addable reports whether e is a statically addable
|
||||
// expression: a [s]tring, [l]ist, or [t]uple.
|
||||
// expression: a [s]tring, [b]ytes, [l]ist, or [t]uple.
|
||||
func addable(e syntax.Expr) rune {
|
||||
switch e := e.(type) {
|
||||
case *syntax.Literal:
|
||||
@@ -1533,6 +1548,8 @@ func addable(e syntax.Expr) rune {
|
||||
switch e.Token {
|
||||
case syntax.STRING:
|
||||
return 's'
|
||||
case syntax.BYTES:
|
||||
return 'b'
|
||||
}
|
||||
case *syntax.ListExpr:
|
||||
return 'l'
|
||||
@@ -1547,12 +1564,16 @@ func addable(e syntax.Expr) rune {
|
||||
// The resulting syntax is degenerate, lacking position, etc.
|
||||
func add(code rune, args []summand) syntax.Expr {
|
||||
switch code {
|
||||
case 's':
|
||||
var buf bytes.Buffer
|
||||
case 's', 'b':
|
||||
var buf strings.Builder
|
||||
for _, arg := range args {
|
||||
buf.WriteString(arg.x.(*syntax.Literal).Value.(string))
|
||||
}
|
||||
return &syntax.Literal{Token: syntax.STRING, Value: buf.String()}
|
||||
tok := syntax.STRING
|
||||
if code == 'b' {
|
||||
tok = syntax.BYTES
|
||||
}
|
||||
return &syntax.Literal{Token: tok, Value: buf.String()}
|
||||
case 'l':
|
||||
var elems []syntax.Expr
|
||||
for _, arg := range args {
|
||||
|
||||
22
vendor/go.starlark.net/internal/compile/serial.go
generated
vendored
22
vendor/go.starlark.net/internal/compile/serial.go
generated
vendored
@@ -51,9 +51,10 @@ package compile
|
||||
//
|
||||
// Constant: # type data
|
||||
// type varint # 0=string string
|
||||
// data ... # 1=int varint
|
||||
// # 2=float varint (bits as uint64)
|
||||
// # 3=bigint string (decimal ASCII text)
|
||||
// data ... # 1=bytes string
|
||||
// # 2=int varint
|
||||
// # 3=float varint (bits as uint64)
|
||||
// # 4=bigint string (decimal ASCII text)
|
||||
//
|
||||
// The encoding starts with a four-byte magic number.
|
||||
// The next four bytes are a little-endian uint32
|
||||
@@ -109,14 +110,17 @@ func (prog *Program) Encode() []byte {
|
||||
case string:
|
||||
e.int(0)
|
||||
e.string(c)
|
||||
case int64:
|
||||
case Bytes:
|
||||
e.int(1)
|
||||
e.string(string(c))
|
||||
case int64:
|
||||
e.int(2)
|
||||
e.int64(c)
|
||||
case float64:
|
||||
e.int(2)
|
||||
e.int(3)
|
||||
e.uint64(math.Float64bits(c))
|
||||
case *big.Int:
|
||||
e.int(3)
|
||||
e.int(4)
|
||||
e.string(c.Text(10))
|
||||
}
|
||||
}
|
||||
@@ -249,10 +253,12 @@ func DecodeProgram(data []byte) (_ *Program, err error) {
|
||||
case 0:
|
||||
c = d.string()
|
||||
case 1:
|
||||
c = d.int64()
|
||||
c = Bytes(d.string())
|
||||
case 2:
|
||||
c = math.Float64frombits(d.uint64())
|
||||
c = d.int64()
|
||||
case 3:
|
||||
c = math.Float64frombits(d.uint64())
|
||||
case 4:
|
||||
c, _ = new(big.Int).SetString(d.string(), 10)
|
||||
}
|
||||
constants[i] = c
|
||||
|
||||
51
vendor/go.starlark.net/resolve/resolve.go
generated
vendored
51
vendor/go.starlark.net/resolve/resolve.go
generated
vendored
@@ -98,14 +98,16 @@ const doesnt = "this Starlark dialect does not "
|
||||
// These features are either not standard Starlark (yet), or deprecated
|
||||
// features of the BUILD language, so we put them behind flags.
|
||||
var (
|
||||
AllowNestedDef = false // allow def statements within function bodies
|
||||
AllowLambda = false // allow lambda expressions
|
||||
AllowFloat = false // allow floating point literals, the 'float' built-in, and x / y
|
||||
AllowSet = false // allow the 'set' built-in
|
||||
AllowGlobalReassign = false // allow reassignment to top-level names; also, allow if/for/while at top-level
|
||||
AllowRecursion = false // allow while statements and recursive functions
|
||||
AllowBitwise = true // obsolete; bitwise operations (&, |, ^, ~, <<, and >>) are always enabled
|
||||
LoadBindsGlobally = false // load creates global not file-local bindings (deprecated)
|
||||
|
||||
// obsolete flags for features that are now standard. No effect.
|
||||
AllowNestedDef = true
|
||||
AllowLambda = true
|
||||
AllowFloat = true
|
||||
AllowBitwise = true
|
||||
)
|
||||
|
||||
// File resolves the specified file and records information about the
|
||||
@@ -214,7 +216,8 @@ type resolver struct {
|
||||
// isGlobal may be nil.
|
||||
isGlobal, isPredeclared, isUniversal func(name string) bool
|
||||
|
||||
loops int // number of enclosing for loops
|
||||
loops int // number of enclosing for/while loops
|
||||
ifstmts int // number of enclosing if statements loops
|
||||
|
||||
errors ErrorList
|
||||
}
|
||||
@@ -417,9 +420,6 @@ func (r *resolver) useToplevel(use use) (bind *Binding) {
|
||||
r.predeclared[id.Name] = bind // save it
|
||||
} else if r.isUniversal(id.Name) {
|
||||
// use of universal name
|
||||
if !AllowFloat && id.Name == "float" {
|
||||
r.errorf(id.NamePos, doesnt+"support floating point")
|
||||
}
|
||||
if !AllowSet && id.Name == "set" {
|
||||
r.errorf(id.NamePos, doesnt+"support sets")
|
||||
}
|
||||
@@ -497,8 +497,10 @@ func (r *resolver) stmt(stmt syntax.Stmt) {
|
||||
r.errorf(stmt.If, "if statement not within a function")
|
||||
}
|
||||
r.expr(stmt.Cond)
|
||||
r.ifstmts++
|
||||
r.stmts(stmt.True)
|
||||
r.stmts(stmt.False)
|
||||
r.ifstmts--
|
||||
|
||||
case *syntax.AssignStmt:
|
||||
r.expr(stmt.RHS)
|
||||
@@ -506,9 +508,6 @@ func (r *resolver) stmt(stmt syntax.Stmt) {
|
||||
r.assign(stmt.LHS, isAugmented)
|
||||
|
||||
case *syntax.DefStmt:
|
||||
if !AllowNestedDef && r.container().function != nil {
|
||||
r.errorf(stmt.Def, doesnt+"support nested def")
|
||||
}
|
||||
r.bind(stmt.Name)
|
||||
fn := &Function{
|
||||
Name: stmt.Name.Name,
|
||||
@@ -551,8 +550,13 @@ func (r *resolver) stmt(stmt syntax.Stmt) {
|
||||
}
|
||||
|
||||
case *syntax.LoadStmt:
|
||||
// A load statement may not be nested in any other statement.
|
||||
if r.container().function != nil {
|
||||
r.errorf(stmt.Load, "load statement within a function")
|
||||
} else if r.loops > 0 {
|
||||
r.errorf(stmt.Load, "load statement within a loop")
|
||||
} else if r.ifstmts > 0 {
|
||||
r.errorf(stmt.Load, "load statement within a conditional")
|
||||
}
|
||||
|
||||
for i, from := range stmt.From {
|
||||
@@ -597,9 +601,6 @@ func (r *resolver) assign(lhs syntax.Expr, isAugmented bool) {
|
||||
|
||||
case *syntax.TupleExpr:
|
||||
// (x, y) = ...
|
||||
if len(lhs.List) == 0 {
|
||||
r.errorf(syntax.Start(lhs), "can't assign to ()")
|
||||
}
|
||||
if isAugmented {
|
||||
r.errorf(syntax.Start(lhs), "can't use tuple expression in augmented assignment")
|
||||
}
|
||||
@@ -609,9 +610,6 @@ func (r *resolver) assign(lhs syntax.Expr, isAugmented bool) {
|
||||
|
||||
case *syntax.ListExpr:
|
||||
// [x, y, z] = ...
|
||||
if len(lhs.List) == 0 {
|
||||
r.errorf(syntax.Start(lhs), "can't assign to []")
|
||||
}
|
||||
if isAugmented {
|
||||
r.errorf(syntax.Start(lhs), "can't use list expression in augmented assignment")
|
||||
}
|
||||
@@ -634,9 +632,6 @@ func (r *resolver) expr(e syntax.Expr) {
|
||||
r.use(e)
|
||||
|
||||
case *syntax.Literal:
|
||||
if !AllowFloat && e.Token == syntax.FLOAT {
|
||||
r.errorf(e.TokenPos, doesnt+"support floating point")
|
||||
}
|
||||
|
||||
case *syntax.ListExpr:
|
||||
for _, x := range e.List {
|
||||
@@ -711,9 +706,6 @@ func (r *resolver) expr(e syntax.Expr) {
|
||||
r.expr(e.X)
|
||||
|
||||
case *syntax.BinaryExpr:
|
||||
if !AllowFloat && e.Op == syntax.SLASH {
|
||||
r.errorf(e.OpPos, doesnt+"support floating point (use //)")
|
||||
}
|
||||
r.expr(e.X)
|
||||
r.expr(e.Y)
|
||||
|
||||
@@ -748,11 +740,13 @@ func (r *resolver) expr(e syntax.Expr) {
|
||||
// k=v
|
||||
n++
|
||||
if seenKwargs {
|
||||
r.errorf(pos, "argument may not follow **kwargs")
|
||||
r.errorf(pos, "keyword argument may not follow **kwargs")
|
||||
} else if seenVarargs {
|
||||
r.errorf(pos, "keyword argument may not follow *args")
|
||||
}
|
||||
x := binop.X.(*syntax.Ident)
|
||||
if seenName[x.Name] {
|
||||
r.errorf(x.NamePos, "keyword argument %s repeated", x.Name)
|
||||
r.errorf(x.NamePos, "keyword argument %q is repeated", x.Name)
|
||||
} else {
|
||||
if seenName == nil {
|
||||
seenName = make(map[string]bool)
|
||||
@@ -764,9 +758,9 @@ func (r *resolver) expr(e syntax.Expr) {
|
||||
// positional argument
|
||||
p++
|
||||
if seenVarargs {
|
||||
r.errorf(pos, "argument may not follow *args")
|
||||
r.errorf(pos, "positional argument may not follow *args")
|
||||
} else if seenKwargs {
|
||||
r.errorf(pos, "argument may not follow **kwargs")
|
||||
r.errorf(pos, "positional argument may not follow **kwargs")
|
||||
} else if len(seenName) > 0 {
|
||||
r.errorf(pos, "positional argument may not follow named")
|
||||
}
|
||||
@@ -785,9 +779,6 @@ func (r *resolver) expr(e syntax.Expr) {
|
||||
}
|
||||
|
||||
case *syntax.LambdaExpr:
|
||||
if !AllowLambda {
|
||||
r.errorf(e.Lambda, doesnt+"support lambda")
|
||||
}
|
||||
fn := &Function{
|
||||
Name: "lambda",
|
||||
Pos: e.Lambda,
|
||||
|
||||
267
vendor/go.starlark.net/starlark/eval.go
generated
vendored
267
vendor/go.starlark.net/starlark/eval.go
generated
vendored
@@ -9,13 +9,14 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math"
|
||||
"math/big"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
"unsafe"
|
||||
|
||||
"go.starlark.net/internal/compile"
|
||||
"go.starlark.net/internal/spell"
|
||||
@@ -46,6 +47,21 @@ type Thread struct {
|
||||
// See example_test.go for some example implementations of Load.
|
||||
Load func(thread *Thread, module string) (StringDict, error)
|
||||
|
||||
// OnMaxSteps is called when the thread reaches the limit set by SetMaxExecutionSteps.
|
||||
// The default behavior is to call thread.Cancel("too many steps").
|
||||
OnMaxSteps func(thread *Thread)
|
||||
|
||||
// Steps a count of abstract computation steps executed
|
||||
// by this thread. It is incremented by the interpreter. It may be used
|
||||
// as a measure of the approximate cost of Starlark execution, by
|
||||
// computing the difference in its value before and after a computation.
|
||||
//
|
||||
// The precise meaning of "step" is not specified and may change.
|
||||
Steps, maxSteps uint64
|
||||
|
||||
// cancelReason records the reason from the first call to Cancel.
|
||||
cancelReason *string
|
||||
|
||||
// locals holds arbitrary "thread-local" Go values belonging to the client.
|
||||
// They are accessible to the client but not to any Starlark program.
|
||||
locals map[string]interface{}
|
||||
@@ -54,6 +70,42 @@ type Thread struct {
|
||||
proftime time.Duration
|
||||
}
|
||||
|
||||
// ExecutionSteps returns the current value of Steps.
|
||||
func (thread *Thread) ExecutionSteps() uint64 {
|
||||
return thread.Steps
|
||||
}
|
||||
|
||||
// SetMaxExecutionSteps sets a limit on the number of Starlark
|
||||
// computation steps that may be executed by this thread. If the
|
||||
// thread's step counter exceeds this limit, the interpreter calls
|
||||
// the optional OnMaxSteps function or the default behavior
|
||||
// of calling thread.Cancel("too many steps").
|
||||
func (thread *Thread) SetMaxExecutionSteps(max uint64) {
|
||||
thread.maxSteps = max
|
||||
}
|
||||
|
||||
// Uncancel resets the cancellation state.
|
||||
//
|
||||
// Unlike most methods of Thread, it is safe to call Uncancel from any
|
||||
// goroutine, even if the thread is actively executing.
|
||||
func (thread *Thread) Uncancel() {
|
||||
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&thread.cancelReason)), nil)
|
||||
}
|
||||
|
||||
// Cancel causes execution of Starlark code in the specified thread to
|
||||
// promptly fail with an EvalError that includes the specified reason.
|
||||
// There may be a delay before the interpreter observes the cancellation
|
||||
// if the thread is currently in a call to a built-in function.
|
||||
//
|
||||
// Call [Uncancel] to reset the cancellation state.
|
||||
//
|
||||
// Unlike most methods of Thread, it is safe to call Cancel from any
|
||||
// goroutine, even if the thread is actively executing.
|
||||
func (thread *Thread) Cancel(reason string) {
|
||||
// Atomically set cancelReason, preserving earlier reason if any.
|
||||
atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&thread.cancelReason)), nil, unsafe.Pointer(&reason))
|
||||
}
|
||||
|
||||
// SetLocal sets the thread-local value associated with the specified key.
|
||||
// It must not be called after execution begins.
|
||||
func (thread *Thread) SetLocal(key string, value interface{}) {
|
||||
@@ -178,7 +230,9 @@ func (stack *CallStack) Pop() CallFrame {
|
||||
// String returns a user-friendly description of the stack.
|
||||
func (stack CallStack) String() string {
|
||||
out := new(strings.Builder)
|
||||
fmt.Fprintf(out, "Traceback (most recent call last):\n")
|
||||
if len(stack) > 0 {
|
||||
fmt.Fprintf(out, "Traceback (most recent call last):\n")
|
||||
}
|
||||
for _, fr := range stack {
|
||||
fmt.Fprintf(out, " %s: in %s\n", fr.Pos, fr.Name)
|
||||
}
|
||||
@@ -220,7 +274,15 @@ func (e *EvalError) Error() string { return e.Msg }
|
||||
// Backtrace returns a user-friendly error message describing the stack
|
||||
// of calls that led to this error.
|
||||
func (e *EvalError) Backtrace() string {
|
||||
return fmt.Sprintf("%sError: %s", e.CallStack, e.Msg)
|
||||
// If the topmost stack frame is a built-in function,
|
||||
// remove it from the stack and add print "Error in fn:".
|
||||
stack := e.CallStack
|
||||
suffix := ""
|
||||
if last := len(stack) - 1; last >= 0 && stack[last].Pos.Filename() == builtinFilename {
|
||||
suffix = " in " + stack[last].Name
|
||||
stack = stack[:last]
|
||||
}
|
||||
return fmt.Sprintf("%sError%s: %s", stack, suffix, e.Msg)
|
||||
}
|
||||
|
||||
func (e *EvalError) Unwrap() error { return e.cause }
|
||||
@@ -429,6 +491,8 @@ func makeToplevelFunction(prog *compile.Program, predeclared StringDict) *Functi
|
||||
v = MakeBigInt(c)
|
||||
case string:
|
||||
v = String(c)
|
||||
case compile.Bytes:
|
||||
v = Bytes(c)
|
||||
case float64:
|
||||
v = Float(c)
|
||||
default:
|
||||
@@ -674,14 +738,22 @@ func Binary(op syntax.Token, x, y Value) (Value, error) {
|
||||
case Int:
|
||||
return x.Add(y), nil
|
||||
case Float:
|
||||
return x.Float() + y, nil
|
||||
xf, err := x.finiteFloat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return xf + y, nil
|
||||
}
|
||||
case Float:
|
||||
switch y := y.(type) {
|
||||
case Float:
|
||||
return x + y, nil
|
||||
case Int:
|
||||
return x + y.Float(), nil
|
||||
yf, err := y.finiteFloat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x + yf, nil
|
||||
}
|
||||
case *List:
|
||||
if y, ok := y.(*List); ok {
|
||||
@@ -706,14 +778,22 @@ func Binary(op syntax.Token, x, y Value) (Value, error) {
|
||||
case Int:
|
||||
return x.Sub(y), nil
|
||||
case Float:
|
||||
return x.Float() - y, nil
|
||||
xf, err := x.finiteFloat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return xf - y, nil
|
||||
}
|
||||
case Float:
|
||||
switch y := y.(type) {
|
||||
case Float:
|
||||
return x - y, nil
|
||||
case Int:
|
||||
return x - y.Float(), nil
|
||||
yf, err := y.finiteFloat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x - yf, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -724,9 +804,15 @@ func Binary(op syntax.Token, x, y Value) (Value, error) {
|
||||
case Int:
|
||||
return x.Mul(y), nil
|
||||
case Float:
|
||||
return x.Float() * y, nil
|
||||
xf, err := x.finiteFloat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return xf * y, nil
|
||||
case String:
|
||||
return stringRepeat(y, x)
|
||||
case Bytes:
|
||||
return bytesRepeat(y, x)
|
||||
case *List:
|
||||
elems, err := tupleRepeat(Tuple(y.elems), x)
|
||||
if err != nil {
|
||||
@@ -741,12 +827,20 @@ func Binary(op syntax.Token, x, y Value) (Value, error) {
|
||||
case Float:
|
||||
return x * y, nil
|
||||
case Int:
|
||||
return x * y.Float(), nil
|
||||
yf, err := y.finiteFloat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x * yf, nil
|
||||
}
|
||||
case String:
|
||||
if y, ok := y.(Int); ok {
|
||||
return stringRepeat(x, y)
|
||||
}
|
||||
case Bytes:
|
||||
if y, ok := y.(Int); ok {
|
||||
return bytesRepeat(x, y)
|
||||
}
|
||||
case *List:
|
||||
if y, ok := y.(Int); ok {
|
||||
elems, err := tupleRepeat(Tuple(x.elems), y)
|
||||
@@ -765,30 +859,40 @@ func Binary(op syntax.Token, x, y Value) (Value, error) {
|
||||
case syntax.SLASH:
|
||||
switch x := x.(type) {
|
||||
case Int:
|
||||
xf, err := x.finiteFloat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch y := y.(type) {
|
||||
case Int:
|
||||
yf := y.Float()
|
||||
if yf == 0.0 {
|
||||
return nil, fmt.Errorf("real division by zero")
|
||||
yf, err := y.finiteFloat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x.Float() / yf, nil
|
||||
if yf == 0.0 {
|
||||
return nil, fmt.Errorf("floating-point division by zero")
|
||||
}
|
||||
return xf / yf, nil
|
||||
case Float:
|
||||
if y == 0.0 {
|
||||
return nil, fmt.Errorf("real division by zero")
|
||||
return nil, fmt.Errorf("floating-point division by zero")
|
||||
}
|
||||
return x.Float() / y, nil
|
||||
return xf / y, nil
|
||||
}
|
||||
case Float:
|
||||
switch y := y.(type) {
|
||||
case Float:
|
||||
if y == 0.0 {
|
||||
return nil, fmt.Errorf("real division by zero")
|
||||
return nil, fmt.Errorf("floating-point division by zero")
|
||||
}
|
||||
return x / y, nil
|
||||
case Int:
|
||||
yf := y.Float()
|
||||
yf, err := y.finiteFloat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if yf == 0.0 {
|
||||
return nil, fmt.Errorf("real division by zero")
|
||||
return nil, fmt.Errorf("floating-point division by zero")
|
||||
}
|
||||
return x / yf, nil
|
||||
}
|
||||
@@ -804,10 +908,14 @@ func Binary(op syntax.Token, x, y Value) (Value, error) {
|
||||
}
|
||||
return x.Div(y), nil
|
||||
case Float:
|
||||
xf, err := x.finiteFloat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if y == 0.0 {
|
||||
return nil, fmt.Errorf("floored division by zero")
|
||||
}
|
||||
return floor((x.Float() / y)), nil
|
||||
return floor(xf / y), nil
|
||||
}
|
||||
case Float:
|
||||
switch y := y.(type) {
|
||||
@@ -817,7 +925,10 @@ func Binary(op syntax.Token, x, y Value) (Value, error) {
|
||||
}
|
||||
return floor(x / y), nil
|
||||
case Int:
|
||||
yf := y.Float()
|
||||
yf, err := y.finiteFloat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if yf == 0.0 {
|
||||
return nil, fmt.Errorf("floored division by zero")
|
||||
}
|
||||
@@ -835,23 +946,31 @@ func Binary(op syntax.Token, x, y Value) (Value, error) {
|
||||
}
|
||||
return x.Mod(y), nil
|
||||
case Float:
|
||||
if y == 0 {
|
||||
return nil, fmt.Errorf("float modulo by zero")
|
||||
xf, err := x.finiteFloat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x.Float().Mod(y), nil
|
||||
if y == 0 {
|
||||
return nil, fmt.Errorf("floating-point modulo by zero")
|
||||
}
|
||||
return xf.Mod(y), nil
|
||||
}
|
||||
case Float:
|
||||
switch y := y.(type) {
|
||||
case Float:
|
||||
if y == 0.0 {
|
||||
return nil, fmt.Errorf("float modulo by zero")
|
||||
return nil, fmt.Errorf("floating-point modulo by zero")
|
||||
}
|
||||
return Float(math.Mod(float64(x), float64(y))), nil
|
||||
return x.Mod(y), nil
|
||||
case Int:
|
||||
if y.Sign() == 0 {
|
||||
return nil, fmt.Errorf("float modulo by zero")
|
||||
return nil, fmt.Errorf("floating-point modulo by zero")
|
||||
}
|
||||
return x.Mod(y.Float()), nil
|
||||
yf, err := y.finiteFloat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x.Mod(yf), nil
|
||||
}
|
||||
case String:
|
||||
return interpolate(string(x), y)
|
||||
@@ -898,6 +1017,19 @@ func Binary(op syntax.Token, x, y Value) (Value, error) {
|
||||
return nil, fmt.Errorf("'in <string>' requires string as left operand, not %s", x.Type())
|
||||
}
|
||||
return Bool(strings.Contains(string(y), string(needle))), nil
|
||||
case Bytes:
|
||||
switch needle := x.(type) {
|
||||
case Bytes:
|
||||
return Bool(strings.Contains(string(y), string(needle))), nil
|
||||
case Int:
|
||||
var b byte
|
||||
if err := AsInt(needle, &b); err != nil {
|
||||
return nil, fmt.Errorf("int in bytes: %s", err)
|
||||
}
|
||||
return Bool(strings.IndexByte(string(y), b) >= 0), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("'in bytes' requires bytes or int as left operand, not %s", x.Type())
|
||||
}
|
||||
case rangeValue:
|
||||
i, err := NumberToInt(x)
|
||||
if err != nil {
|
||||
@@ -912,6 +1044,12 @@ func Binary(op syntax.Token, x, y Value) (Value, error) {
|
||||
if y, ok := y.(Int); ok {
|
||||
return x.Or(y), nil
|
||||
}
|
||||
|
||||
case *Dict: // union
|
||||
if y, ok := y.(*Dict); ok {
|
||||
return x.Union(y), nil
|
||||
}
|
||||
|
||||
case *Set: // union
|
||||
if y, ok := y.(*Set); ok {
|
||||
iter := Iterate(y)
|
||||
@@ -932,10 +1070,10 @@ func Binary(op syntax.Token, x, y Value) (Value, error) {
|
||||
if x.Len() > y.Len() {
|
||||
x, y = y, x // opt: range over smaller set
|
||||
}
|
||||
for _, xelem := range x.elems() {
|
||||
for xe := x.ht.head; xe != nil; xe = xe.next {
|
||||
// Has, Insert cannot fail here.
|
||||
if found, _ := y.Has(xelem); found {
|
||||
set.Insert(xelem)
|
||||
if found, _ := y.Has(xe.key); found {
|
||||
set.Insert(xe.key)
|
||||
}
|
||||
}
|
||||
return set, nil
|
||||
@@ -951,14 +1089,14 @@ func Binary(op syntax.Token, x, y Value) (Value, error) {
|
||||
case *Set: // symmetric difference
|
||||
if y, ok := y.(*Set); ok {
|
||||
set := new(Set)
|
||||
for _, xelem := range x.elems() {
|
||||
if found, _ := y.Has(xelem); !found {
|
||||
set.Insert(xelem)
|
||||
for xe := x.ht.head; xe != nil; xe = xe.next {
|
||||
if found, _ := y.Has(xe.key); !found {
|
||||
set.Insert(xe.key)
|
||||
}
|
||||
}
|
||||
for _, yelem := range y.elems() {
|
||||
if found, _ := x.Has(yelem); !found {
|
||||
set.Insert(yelem)
|
||||
for ye := y.ht.head; ye != nil; ye = ye.next {
|
||||
if found, _ := x.Has(ye.key); !found {
|
||||
set.Insert(ye.key)
|
||||
}
|
||||
}
|
||||
return set, nil
|
||||
@@ -1027,7 +1165,8 @@ func tupleRepeat(elems Tuple, n Int) (Tuple, error) {
|
||||
// Inv: i > 0, len > 0
|
||||
sz := len(elems) * i
|
||||
if sz < 0 || sz >= maxAlloc { // sz < 0 => overflow
|
||||
return nil, fmt.Errorf("excessive repeat (%d elements)", sz)
|
||||
// Don't print sz.
|
||||
return nil, fmt.Errorf("excessive repeat (%d * %d elements)", len(elems), i)
|
||||
}
|
||||
res := make([]Value, sz)
|
||||
// copy elems into res, doubling each time
|
||||
@@ -1039,6 +1178,11 @@ func tupleRepeat(elems Tuple, n Int) (Tuple, error) {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func bytesRepeat(b Bytes, n Int) (Bytes, error) {
|
||||
res, err := stringRepeat(String(b), n)
|
||||
return Bytes(res), err
|
||||
}
|
||||
|
||||
func stringRepeat(s String, n Int) (String, error) {
|
||||
if s == "" {
|
||||
return "", nil
|
||||
@@ -1053,7 +1197,8 @@ func stringRepeat(s String, n Int) (String, error) {
|
||||
// Inv: i > 0, len > 0
|
||||
sz := len(s) * i
|
||||
if sz < 0 || sz >= maxAlloc { // sz < 0 => overflow
|
||||
return "", fmt.Errorf("excessive repeat (%d elements)", sz)
|
||||
// Don't print sz.
|
||||
return "", fmt.Errorf("excessive repeat (%d * %d elements)", len(s), i)
|
||||
}
|
||||
return String(strings.Repeat(string(s), i)), nil
|
||||
}
|
||||
@@ -1075,13 +1220,35 @@ func Call(thread *Thread, fn Value, args Tuple, kwargs []Tuple) (Value, error) {
|
||||
if fr == nil {
|
||||
fr = new(frame)
|
||||
}
|
||||
|
||||
if thread.stack == nil {
|
||||
// one-time initialization of thread
|
||||
if thread.maxSteps == 0 {
|
||||
thread.maxSteps-- // (MaxUint64)
|
||||
}
|
||||
}
|
||||
|
||||
thread.stack = append(thread.stack, fr) // push
|
||||
|
||||
fr.callable = c
|
||||
|
||||
thread.beginProfSpan()
|
||||
|
||||
// Use defer to ensure that panics from built-ins
|
||||
// pass through the interpreter without leaving
|
||||
// it in a bad state.
|
||||
defer func() {
|
||||
thread.endProfSpan()
|
||||
|
||||
// clear out any references
|
||||
// TODO(adonovan): opt: zero fr.Locals and
|
||||
// reuse it if it is large enough.
|
||||
*fr = frame{}
|
||||
|
||||
thread.stack = thread.stack[:len(thread.stack)-1] // pop
|
||||
}()
|
||||
|
||||
result, err := c.CallInternal(thread, args, kwargs)
|
||||
thread.endProfSpan()
|
||||
|
||||
// Sanity check: nil is not a valid Starlark value.
|
||||
if result == nil && err == nil {
|
||||
@@ -1095,9 +1262,6 @@ func Call(thread *Thread, fn Value, args Tuple, kwargs []Tuple) (Value, error) {
|
||||
}
|
||||
}
|
||||
|
||||
*fr = frame{} // clear out any references
|
||||
thread.stack = thread.stack[:len(thread.stack)-1] // pop
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
@@ -1113,7 +1277,7 @@ func slice(x, lo, hi, step_ Value) (Value, error) {
|
||||
var err error
|
||||
step, err = AsInt32(step_)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("got %s for slice step, want int", step_.Type())
|
||||
return nil, fmt.Errorf("invalid slice step: %s", err)
|
||||
}
|
||||
if step == 0 {
|
||||
return nil, fmt.Errorf("zero is not a valid slice step")
|
||||
@@ -1207,7 +1371,7 @@ func asIndex(v Value, len int, result *int) error {
|
||||
var err error
|
||||
*result, err = AsInt32(v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("got %s, want int", v.Type())
|
||||
return err
|
||||
}
|
||||
if *result < 0 {
|
||||
*result += len
|
||||
@@ -1448,20 +1612,7 @@ func interpolate(format string, x Value) (Value, error) {
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%%%c format requires float, not %s", c, arg.Type())
|
||||
}
|
||||
switch c {
|
||||
case 'e':
|
||||
fmt.Fprintf(buf, "%e", f)
|
||||
case 'f':
|
||||
fmt.Fprintf(buf, "%f", f)
|
||||
case 'g':
|
||||
fmt.Fprintf(buf, "%g", f)
|
||||
case 'E':
|
||||
fmt.Fprintf(buf, "%E", f)
|
||||
case 'F':
|
||||
fmt.Fprintf(buf, "%F", f)
|
||||
case 'G':
|
||||
fmt.Fprintf(buf, "%G", f)
|
||||
}
|
||||
Float(f).format(buf, c)
|
||||
case 'c':
|
||||
switch arg := arg.(type) {
|
||||
case Int:
|
||||
|
||||
81
vendor/go.starlark.net/starlark/hashtable.go
generated
vendored
81
vendor/go.starlark.net/starlark/hashtable.go
generated
vendored
@@ -12,6 +12,8 @@ import (
|
||||
// hashtable is used to represent Starlark dict and set values.
|
||||
// It is a hash table whose key/value entries form a doubly-linked list
|
||||
// in the order the entries were inserted.
|
||||
//
|
||||
// Initialized instances of hashtable must not be copied.
|
||||
type hashtable struct {
|
||||
table []bucket // len is zero or a power of two
|
||||
bucket0 [1]bucket // inline allocation for small maps.
|
||||
@@ -20,8 +22,17 @@ type hashtable struct {
|
||||
head *entry // insertion order doubly-linked list; may be nil
|
||||
tailLink **entry // address of nil link at end of list (perhaps &head)
|
||||
frozen bool
|
||||
|
||||
_ noCopy // triggers vet copylock check on this type.
|
||||
}
|
||||
|
||||
// noCopy is zero-sized type that triggers vet's copylock check.
|
||||
// See https://github.com/golang/go/issues/8005#issuecomment-190753527.
|
||||
type noCopy struct{}
|
||||
|
||||
func (*noCopy) Lock() {}
|
||||
func (*noCopy) Unlock() {}
|
||||
|
||||
const bucketSize = 8
|
||||
|
||||
type bucket struct {
|
||||
@@ -55,26 +66,16 @@ func (ht *hashtable) init(size int) {
|
||||
func (ht *hashtable) freeze() {
|
||||
if !ht.frozen {
|
||||
ht.frozen = true
|
||||
for i := range ht.table {
|
||||
for p := &ht.table[i]; p != nil; p = p.next {
|
||||
for i := range p.entries {
|
||||
e := &p.entries[i]
|
||||
if e.hash != 0 {
|
||||
e.key.Freeze()
|
||||
e.value.Freeze()
|
||||
}
|
||||
}
|
||||
}
|
||||
for e := ht.head; e != nil; e = e.next {
|
||||
e.key.Freeze()
|
||||
e.value.Freeze()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ht *hashtable) insert(k, v Value) error {
|
||||
if ht.frozen {
|
||||
return fmt.Errorf("cannot insert into frozen hash table")
|
||||
}
|
||||
if ht.itercount > 0 {
|
||||
return fmt.Errorf("cannot insert into hash table during iteration")
|
||||
if err := ht.checkMutable("insert into"); err != nil {
|
||||
return err
|
||||
}
|
||||
if ht.table == nil {
|
||||
ht.init(1)
|
||||
@@ -154,13 +155,12 @@ func overloaded(elems, buckets int) bool {
|
||||
|
||||
func (ht *hashtable) grow() {
|
||||
// Double the number of buckets and rehash.
|
||||
// TODO(adonovan): opt:
|
||||
// - avoid reentrant calls to ht.insert, and specialize it.
|
||||
// e.g. we know the calls to Equals will return false since
|
||||
// there are no duplicates among the old keys.
|
||||
// - saving the entire hash in the bucket would avoid the need to
|
||||
// recompute the hash.
|
||||
// - save the old buckets on a free list.
|
||||
//
|
||||
// Even though this makes reentrant calls to ht.insert,
|
||||
// calls Equals unnecessarily (since there can't be duplicate keys),
|
||||
// and recomputes the hash unnecessarily, the gains from
|
||||
// avoiding these steps were found to be too small to justify
|
||||
// the extra logic: -2% on hashtable benchmark.
|
||||
ht.table = make([]bucket, len(ht.table)<<1)
|
||||
oldhead := ht.head
|
||||
ht.head = nil
|
||||
@@ -230,11 +230,8 @@ func (ht *hashtable) keys() []Value {
|
||||
}
|
||||
|
||||
func (ht *hashtable) delete(k Value) (v Value, found bool, err error) {
|
||||
if ht.frozen {
|
||||
return nil, false, fmt.Errorf("cannot delete from frozen hash table")
|
||||
}
|
||||
if ht.itercount > 0 {
|
||||
return nil, false, fmt.Errorf("cannot delete from hash table during iteration")
|
||||
if err := ht.checkMutable("delete from"); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
if ht.table == nil {
|
||||
return None, false, nil // empty
|
||||
@@ -277,12 +274,21 @@ func (ht *hashtable) delete(k Value) (v Value, found bool, err error) {
|
||||
return None, false, nil // not found
|
||||
}
|
||||
|
||||
func (ht *hashtable) clear() error {
|
||||
// checkMutable reports an error if the hash table should not be mutated.
|
||||
// verb+" dict" should describe the operation.
|
||||
func (ht *hashtable) checkMutable(verb string) error {
|
||||
if ht.frozen {
|
||||
return fmt.Errorf("cannot clear frozen hash table")
|
||||
return fmt.Errorf("cannot %s frozen hash table", verb)
|
||||
}
|
||||
if ht.itercount > 0 {
|
||||
return fmt.Errorf("cannot clear hash table during iteration")
|
||||
return fmt.Errorf("cannot %s hash table during iteration", verb)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ht *hashtable) clear() error {
|
||||
if err := ht.checkMutable("clear"); err != nil {
|
||||
return err
|
||||
}
|
||||
if ht.table != nil {
|
||||
for i := range ht.table {
|
||||
@@ -295,6 +301,15 @@ func (ht *hashtable) clear() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ht *hashtable) addAll(other *hashtable) error {
|
||||
for e := other.head; e != nil; e = e.next {
|
||||
if err := ht.insert(e.key, e.value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// dump is provided as an aid to debugging.
|
||||
func (ht *hashtable) dump() {
|
||||
fmt.Printf("hashtable %p len=%d head=%p tailLink=%p",
|
||||
@@ -349,6 +364,8 @@ func (it *keyIterator) Done() {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(adonovan): use go1.19's maphash.String.
|
||||
|
||||
// hashString computes the hash of s.
|
||||
func hashString(s string) uint32 {
|
||||
if len(s) >= 12 {
|
||||
@@ -362,9 +379,9 @@ func hashString(s string) uint32 {
|
||||
//go:linkname goStringHash runtime.stringHash
|
||||
func goStringHash(s string, seed uintptr) uintptr
|
||||
|
||||
// softHashString computes the FNV hash of s in software.
|
||||
// softHashString computes the 32-bit FNV-1a hash of s in software.
|
||||
func softHashString(s string) uint32 {
|
||||
var h uint32
|
||||
var h uint32 = 2166136261
|
||||
for i := 0; i < len(s); i++ {
|
||||
h ^= uint32(s[i])
|
||||
h *= 16777619
|
||||
|
||||
312
vendor/go.starlark.net/starlark/int.go
generated
vendored
312
vendor/go.starlark.net/starlark/int.go
generated
vendored
@@ -8,33 +8,18 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"go.starlark.net/syntax"
|
||||
)
|
||||
|
||||
// Int is the type of a Starlark int.
|
||||
type Int struct {
|
||||
// We use only the signed 32 bit range of small to ensure
|
||||
// that small+small and small*small do not overflow.
|
||||
//
|
||||
// The zero value is not a legal value; use MakeInt(0).
|
||||
type Int struct{ impl intImpl }
|
||||
|
||||
small int64 // minint32 <= small <= maxint32
|
||||
big *big.Int // big != nil <=> value is not representable as int32
|
||||
}
|
||||
|
||||
// newBig allocates a new big.Int.
|
||||
func newBig(x int64) *big.Int {
|
||||
if 0 <= x && int64(big.Word(x)) == x {
|
||||
// x is guaranteed to fit into a single big.Word.
|
||||
// Most starlark ints are small,
|
||||
// but math/big assumes that since you've chosen to use math/big,
|
||||
// your big.Ints will probably grow, so it over-allocates.
|
||||
// Avoid that over-allocation by manually constructing a single-word slice.
|
||||
// See https://golang.org/cl/150999, which will hopefully land in Go 1.13.
|
||||
return new(big.Int).SetBits([]big.Word{big.Word(x)})
|
||||
}
|
||||
return big.NewInt(x)
|
||||
}
|
||||
// --- high-level accessors ---
|
||||
|
||||
// MakeInt returns a Starlark int for the specified signed integer.
|
||||
func MakeInt(x int) Int { return MakeInt64(int64(x)) }
|
||||
@@ -42,9 +27,9 @@ func MakeInt(x int) Int { return MakeInt64(int64(x)) }
|
||||
// MakeInt64 returns a Starlark int for the specified int64.
|
||||
func MakeInt64(x int64) Int {
|
||||
if math.MinInt32 <= x && x <= math.MaxInt32 {
|
||||
return Int{small: x}
|
||||
return makeSmallInt(x)
|
||||
}
|
||||
return Int{big: newBig(x)}
|
||||
return makeBigInt(big.NewInt(x))
|
||||
}
|
||||
|
||||
// MakeUint returns a Starlark int for the specified unsigned integer.
|
||||
@@ -53,27 +38,29 @@ func MakeUint(x uint) Int { return MakeUint64(uint64(x)) }
|
||||
// MakeUint64 returns a Starlark int for the specified uint64.
|
||||
func MakeUint64(x uint64) Int {
|
||||
if x <= math.MaxInt32 {
|
||||
return Int{small: int64(x)}
|
||||
return makeSmallInt(int64(x))
|
||||
}
|
||||
if uint64(big.Word(x)) == x {
|
||||
// See comment in newBig for an explanation of this optimization.
|
||||
return Int{big: new(big.Int).SetBits([]big.Word{big.Word(x)})}
|
||||
}
|
||||
return Int{big: new(big.Int).SetUint64(x)}
|
||||
return makeBigInt(new(big.Int).SetUint64(x))
|
||||
}
|
||||
|
||||
// MakeBigInt returns a Starlark int for the specified big.Int.
|
||||
// The caller must not subsequently modify x.
|
||||
// The new Int value will contain a copy of x. The caller is safe to modify x.
|
||||
func MakeBigInt(x *big.Int) Int {
|
||||
if n := x.BitLen(); n < 32 || n == 32 && x.Int64() == math.MinInt32 {
|
||||
return Int{small: x.Int64()}
|
||||
if isSmall(x) {
|
||||
return makeSmallInt(x.Int64())
|
||||
}
|
||||
return Int{big: x}
|
||||
z := new(big.Int).Set(x)
|
||||
return makeBigInt(z)
|
||||
}
|
||||
|
||||
func isSmall(x *big.Int) bool {
|
||||
n := x.BitLen()
|
||||
return n < 32 || n == 32 && x.Int64() == math.MinInt32
|
||||
}
|
||||
|
||||
var (
|
||||
zero, one = Int{small: 0}, Int{small: 1}
|
||||
oneBig = newBig(1)
|
||||
zero, one = makeSmallInt(0), makeSmallInt(1)
|
||||
oneBig = big.NewInt(1)
|
||||
|
||||
_ HasUnary = Int{}
|
||||
)
|
||||
@@ -94,39 +81,52 @@ func (i Int) Unary(op syntax.Token) (Value, error) {
|
||||
// Int64 returns the value as an int64.
|
||||
// If it is not exactly representable the result is undefined and ok is false.
|
||||
func (i Int) Int64() (_ int64, ok bool) {
|
||||
if i.big != nil {
|
||||
x, acc := bigintToInt64(i.big)
|
||||
iSmall, iBig := i.get()
|
||||
if iBig != nil {
|
||||
x, acc := bigintToInt64(iBig)
|
||||
if acc != big.Exact {
|
||||
return // inexact
|
||||
}
|
||||
return x, true
|
||||
}
|
||||
return i.small, true
|
||||
return iSmall, true
|
||||
}
|
||||
|
||||
// BigInt returns the value as a big.Int.
|
||||
// The returned variable must not be modified by the client.
|
||||
// BigInt returns a new big.Int with the same value as the Int.
|
||||
func (i Int) BigInt() *big.Int {
|
||||
if i.big != nil {
|
||||
return i.big
|
||||
iSmall, iBig := i.get()
|
||||
if iBig != nil {
|
||||
return new(big.Int).Set(iBig)
|
||||
}
|
||||
return newBig(i.small)
|
||||
return big.NewInt(iSmall)
|
||||
}
|
||||
|
||||
// bigInt returns the value as a big.Int.
|
||||
// It differs from BigInt in that this method returns the actual
|
||||
// reference and any modification will change the state of i.
|
||||
func (i Int) bigInt() *big.Int {
|
||||
iSmall, iBig := i.get()
|
||||
if iBig != nil {
|
||||
return iBig
|
||||
}
|
||||
return big.NewInt(iSmall)
|
||||
}
|
||||
|
||||
// Uint64 returns the value as a uint64.
|
||||
// If it is not exactly representable the result is undefined and ok is false.
|
||||
func (i Int) Uint64() (_ uint64, ok bool) {
|
||||
if i.big != nil {
|
||||
x, acc := bigintToUint64(i.big)
|
||||
iSmall, iBig := i.get()
|
||||
if iBig != nil {
|
||||
x, acc := bigintToUint64(iBig)
|
||||
if acc != big.Exact {
|
||||
return // inexact
|
||||
}
|
||||
return x, true
|
||||
}
|
||||
if i.small < 0 {
|
||||
if iSmall < 0 {
|
||||
return // inexact
|
||||
}
|
||||
return uint64(i.small), true
|
||||
return uint64(iSmall), true
|
||||
}
|
||||
|
||||
// The math/big API should provide this function.
|
||||
@@ -163,104 +163,145 @@ var (
|
||||
)
|
||||
|
||||
func (i Int) Format(s fmt.State, ch rune) {
|
||||
if i.big != nil {
|
||||
i.big.Format(s, ch)
|
||||
iSmall, iBig := i.get()
|
||||
if iBig != nil {
|
||||
iBig.Format(s, ch)
|
||||
return
|
||||
}
|
||||
newBig(i.small).Format(s, ch)
|
||||
big.NewInt(iSmall).Format(s, ch)
|
||||
}
|
||||
func (i Int) String() string {
|
||||
if i.big != nil {
|
||||
return i.big.Text(10)
|
||||
iSmall, iBig := i.get()
|
||||
if iBig != nil {
|
||||
return iBig.Text(10)
|
||||
}
|
||||
return strconv.FormatInt(i.small, 10)
|
||||
return strconv.FormatInt(iSmall, 10)
|
||||
}
|
||||
func (i Int) Type() string { return "int" }
|
||||
func (i Int) Freeze() {} // immutable
|
||||
func (i Int) Truth() Bool { return i.Sign() != 0 }
|
||||
func (i Int) Hash() (uint32, error) {
|
||||
iSmall, iBig := i.get()
|
||||
var lo big.Word
|
||||
if i.big != nil {
|
||||
lo = i.big.Bits()[0]
|
||||
if iBig != nil {
|
||||
lo = iBig.Bits()[0]
|
||||
} else {
|
||||
lo = big.Word(i.small)
|
||||
lo = big.Word(iSmall)
|
||||
}
|
||||
return 12582917 * uint32(lo+3), nil
|
||||
}
|
||||
func (x Int) CompareSameType(op syntax.Token, v Value, depth int) (bool, error) {
|
||||
|
||||
// Required by the TotallyOrdered interface
|
||||
func (x Int) Cmp(v Value, depth int) (int, error) {
|
||||
y := v.(Int)
|
||||
if x.big != nil || y.big != nil {
|
||||
return threeway(op, x.BigInt().Cmp(y.BigInt())), nil
|
||||
xSmall, xBig := x.get()
|
||||
ySmall, yBig := y.get()
|
||||
if xBig != nil || yBig != nil {
|
||||
return x.bigInt().Cmp(y.bigInt()), nil
|
||||
}
|
||||
return threeway(op, signum64(x.small-y.small)), nil
|
||||
return signum64(xSmall - ySmall), nil // safe: int32 operands
|
||||
}
|
||||
|
||||
// Float returns the float value nearest i.
|
||||
func (i Int) Float() Float {
|
||||
if i.big != nil {
|
||||
f, _ := new(big.Float).SetInt(i.big).Float64()
|
||||
iSmall, iBig := i.get()
|
||||
if iBig != nil {
|
||||
// Fast path for hardware int-to-float conversions.
|
||||
if iBig.IsUint64() {
|
||||
return Float(iBig.Uint64())
|
||||
} else if iBig.IsInt64() {
|
||||
return Float(iBig.Int64())
|
||||
}
|
||||
|
||||
f, _ := new(big.Float).SetInt(iBig).Float64()
|
||||
return Float(f)
|
||||
}
|
||||
return Float(i.small)
|
||||
return Float(iSmall)
|
||||
}
|
||||
|
||||
// finiteFloat returns the finite float value nearest i,
|
||||
// or an error if the magnitude is too large.
|
||||
func (i Int) finiteFloat() (Float, error) {
|
||||
f := i.Float()
|
||||
if math.IsInf(float64(f), 0) {
|
||||
return 0, fmt.Errorf("int too large to convert to float")
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (x Int) Sign() int {
|
||||
if x.big != nil {
|
||||
return x.big.Sign()
|
||||
xSmall, xBig := x.get()
|
||||
if xBig != nil {
|
||||
return xBig.Sign()
|
||||
}
|
||||
return signum64(x.small)
|
||||
return signum64(xSmall)
|
||||
}
|
||||
|
||||
func (x Int) Add(y Int) Int {
|
||||
if x.big != nil || y.big != nil {
|
||||
return MakeBigInt(new(big.Int).Add(x.BigInt(), y.BigInt()))
|
||||
xSmall, xBig := x.get()
|
||||
ySmall, yBig := y.get()
|
||||
if xBig != nil || yBig != nil {
|
||||
return MakeBigInt(new(big.Int).Add(x.bigInt(), y.bigInt()))
|
||||
}
|
||||
return MakeInt64(x.small + y.small)
|
||||
return MakeInt64(xSmall + ySmall)
|
||||
}
|
||||
func (x Int) Sub(y Int) Int {
|
||||
if x.big != nil || y.big != nil {
|
||||
return MakeBigInt(new(big.Int).Sub(x.BigInt(), y.BigInt()))
|
||||
xSmall, xBig := x.get()
|
||||
ySmall, yBig := y.get()
|
||||
if xBig != nil || yBig != nil {
|
||||
return MakeBigInt(new(big.Int).Sub(x.bigInt(), y.bigInt()))
|
||||
}
|
||||
return MakeInt64(x.small - y.small)
|
||||
return MakeInt64(xSmall - ySmall)
|
||||
}
|
||||
func (x Int) Mul(y Int) Int {
|
||||
if x.big != nil || y.big != nil {
|
||||
return MakeBigInt(new(big.Int).Mul(x.BigInt(), y.BigInt()))
|
||||
xSmall, xBig := x.get()
|
||||
ySmall, yBig := y.get()
|
||||
if xBig != nil || yBig != nil {
|
||||
return MakeBigInt(new(big.Int).Mul(x.bigInt(), y.bigInt()))
|
||||
}
|
||||
return MakeInt64(x.small * y.small)
|
||||
return MakeInt64(xSmall * ySmall)
|
||||
}
|
||||
func (x Int) Or(y Int) Int {
|
||||
if x.big != nil || y.big != nil {
|
||||
return Int{big: new(big.Int).Or(x.BigInt(), y.BigInt())}
|
||||
xSmall, xBig := x.get()
|
||||
ySmall, yBig := y.get()
|
||||
if xBig != nil || yBig != nil {
|
||||
return MakeBigInt(new(big.Int).Or(x.bigInt(), y.bigInt()))
|
||||
}
|
||||
return Int{small: x.small | y.small}
|
||||
return makeSmallInt(xSmall | ySmall)
|
||||
}
|
||||
func (x Int) And(y Int) Int {
|
||||
if x.big != nil || y.big != nil {
|
||||
return MakeBigInt(new(big.Int).And(x.BigInt(), y.BigInt()))
|
||||
xSmall, xBig := x.get()
|
||||
ySmall, yBig := y.get()
|
||||
if xBig != nil || yBig != nil {
|
||||
return MakeBigInt(new(big.Int).And(x.bigInt(), y.bigInt()))
|
||||
}
|
||||
return Int{small: x.small & y.small}
|
||||
return makeSmallInt(xSmall & ySmall)
|
||||
}
|
||||
func (x Int) Xor(y Int) Int {
|
||||
if x.big != nil || y.big != nil {
|
||||
return MakeBigInt(new(big.Int).Xor(x.BigInt(), y.BigInt()))
|
||||
xSmall, xBig := x.get()
|
||||
ySmall, yBig := y.get()
|
||||
if xBig != nil || yBig != nil {
|
||||
return MakeBigInt(new(big.Int).Xor(x.bigInt(), y.bigInt()))
|
||||
}
|
||||
return Int{small: x.small ^ y.small}
|
||||
return makeSmallInt(xSmall ^ ySmall)
|
||||
}
|
||||
func (x Int) Not() Int {
|
||||
if x.big != nil {
|
||||
return MakeBigInt(new(big.Int).Not(x.big))
|
||||
xSmall, xBig := x.get()
|
||||
if xBig != nil {
|
||||
return MakeBigInt(new(big.Int).Not(xBig))
|
||||
}
|
||||
return Int{small: ^x.small}
|
||||
return makeSmallInt(^xSmall)
|
||||
}
|
||||
func (x Int) Lsh(y uint) Int { return MakeBigInt(new(big.Int).Lsh(x.BigInt(), y)) }
|
||||
func (x Int) Rsh(y uint) Int { return MakeBigInt(new(big.Int).Rsh(x.BigInt(), y)) }
|
||||
func (x Int) Lsh(y uint) Int { return MakeBigInt(new(big.Int).Lsh(x.bigInt(), y)) }
|
||||
func (x Int) Rsh(y uint) Int { return MakeBigInt(new(big.Int).Rsh(x.bigInt(), y)) }
|
||||
|
||||
// Precondition: y is nonzero.
|
||||
func (x Int) Div(y Int) Int {
|
||||
xSmall, xBig := x.get()
|
||||
ySmall, yBig := y.get()
|
||||
// http://python-history.blogspot.com/2010/08/why-pythons-integer-division-floors.html
|
||||
if x.big != nil || y.big != nil {
|
||||
xb, yb := x.BigInt(), y.BigInt()
|
||||
if xBig != nil || yBig != nil {
|
||||
xb, yb := x.bigInt(), y.bigInt()
|
||||
|
||||
var quo, rem big.Int
|
||||
quo.QuoRem(xb, yb, &rem)
|
||||
@@ -269,9 +310,9 @@ func (x Int) Div(y Int) Int {
|
||||
}
|
||||
return MakeBigInt(&quo)
|
||||
}
|
||||
quo := x.small / y.small
|
||||
rem := x.small % y.small
|
||||
if (x.small < 0) != (y.small < 0) && rem != 0 {
|
||||
quo := xSmall / ySmall
|
||||
rem := xSmall % ySmall
|
||||
if (xSmall < 0) != (ySmall < 0) && rem != 0 {
|
||||
quo -= 1
|
||||
}
|
||||
return MakeInt64(quo)
|
||||
@@ -279,8 +320,10 @@ func (x Int) Div(y Int) Int {
|
||||
|
||||
// Precondition: y is nonzero.
|
||||
func (x Int) Mod(y Int) Int {
|
||||
if x.big != nil || y.big != nil {
|
||||
xb, yb := x.BigInt(), y.BigInt()
|
||||
xSmall, xBig := x.get()
|
||||
ySmall, yBig := y.get()
|
||||
if xBig != nil || yBig != nil {
|
||||
xb, yb := x.bigInt(), y.bigInt()
|
||||
|
||||
var quo, rem big.Int
|
||||
quo.QuoRem(xb, yb, &rem)
|
||||
@@ -289,18 +332,19 @@ func (x Int) Mod(y Int) Int {
|
||||
}
|
||||
return MakeBigInt(&rem)
|
||||
}
|
||||
rem := x.small % y.small
|
||||
if (x.small < 0) != (y.small < 0) && rem != 0 {
|
||||
rem += y.small
|
||||
rem := xSmall % ySmall
|
||||
if (xSmall < 0) != (ySmall < 0) && rem != 0 {
|
||||
rem += ySmall
|
||||
}
|
||||
return Int{small: rem}
|
||||
return makeSmallInt(rem)
|
||||
}
|
||||
|
||||
func (i Int) rational() *big.Rat {
|
||||
if i.big != nil {
|
||||
return new(big.Rat).SetInt(i.big)
|
||||
iSmall, iBig := i.get()
|
||||
if iBig != nil {
|
||||
return new(big.Rat).SetInt(iBig)
|
||||
}
|
||||
return new(big.Rat).SetInt64(i.small)
|
||||
return new(big.Rat).SetInt64(iSmall)
|
||||
}
|
||||
|
||||
// AsInt32 returns the value of x if is representable as an int32.
|
||||
@@ -309,10 +353,66 @@ func AsInt32(x Value) (int, error) {
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("got %s, want int", x.Type())
|
||||
}
|
||||
if i.big != nil {
|
||||
iSmall, iBig := i.get()
|
||||
if iBig != nil {
|
||||
return 0, fmt.Errorf("%s out of range", i)
|
||||
}
|
||||
return int(i.small), nil
|
||||
return int(iSmall), nil
|
||||
}
|
||||
|
||||
// AsInt sets *ptr to the value of Starlark int x, if it is exactly representable,
|
||||
// otherwise it returns an error.
|
||||
// The type of ptr must be one of the pointer types *int, *int8, *int16, *int32, or *int64,
|
||||
// or one of their unsigned counterparts including *uintptr.
|
||||
func AsInt(x Value, ptr interface{}) error {
|
||||
xint, ok := x.(Int)
|
||||
if !ok {
|
||||
return fmt.Errorf("got %s, want int", x.Type())
|
||||
}
|
||||
|
||||
bits := reflect.TypeOf(ptr).Elem().Size() * 8
|
||||
switch ptr.(type) {
|
||||
case *int, *int8, *int16, *int32, *int64:
|
||||
i, ok := xint.Int64()
|
||||
if !ok || bits < 64 && !(-1<<(bits-1) <= i && i < 1<<(bits-1)) {
|
||||
return fmt.Errorf("%s out of range (want value in signed %d-bit range)", xint, bits)
|
||||
}
|
||||
switch ptr := ptr.(type) {
|
||||
case *int:
|
||||
*ptr = int(i)
|
||||
case *int8:
|
||||
*ptr = int8(i)
|
||||
case *int16:
|
||||
*ptr = int16(i)
|
||||
case *int32:
|
||||
*ptr = int32(i)
|
||||
case *int64:
|
||||
*ptr = int64(i)
|
||||
}
|
||||
|
||||
case *uint, *uint8, *uint16, *uint32, *uint64, *uintptr:
|
||||
i, ok := xint.Uint64()
|
||||
if !ok || bits < 64 && i >= 1<<bits {
|
||||
return fmt.Errorf("%s out of range (want value in unsigned %d-bit range)", xint, bits)
|
||||
}
|
||||
switch ptr := ptr.(type) {
|
||||
case *uint:
|
||||
*ptr = uint(i)
|
||||
case *uint8:
|
||||
*ptr = uint8(i)
|
||||
case *uint16:
|
||||
*ptr = uint16(i)
|
||||
case *uint32:
|
||||
*ptr = uint32(i)
|
||||
case *uint64:
|
||||
*ptr = uint64(i)
|
||||
case *uintptr:
|
||||
*ptr = uintptr(i)
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid argument type: %T", ptr))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NumberToInt converts a number x to an integer value.
|
||||
@@ -338,7 +438,9 @@ func NumberToInt(x Value) (Int, error) {
|
||||
// finiteFloatToInt converts f to an Int, truncating towards zero.
|
||||
// f must be finite.
|
||||
func finiteFloatToInt(f Float) Int {
|
||||
if math.MinInt64 <= f && f <= math.MaxInt64 {
|
||||
// We avoid '<= MaxInt64' so that both constants are exactly representable as floats.
|
||||
// See https://github.com/google/starlark-go/issues/375.
|
||||
if math.MinInt64 <= f && f < math.MaxInt64+1 {
|
||||
// small values
|
||||
return MakeInt64(int64(f))
|
||||
}
|
||||
|
||||
34
vendor/go.starlark.net/starlark/int_generic.go
generated
vendored
Normal file
34
vendor/go.starlark.net/starlark/int_generic.go
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
//go:build (!linux && !darwin && !dragonfly && !freebsd && !netbsd && !solaris) || (!amd64 && !arm64 && !mips64x && !ppc64x && !loong64)
|
||||
// +build !linux,!darwin,!dragonfly,!freebsd,!netbsd,!solaris !amd64,!arm64,!mips64x,!ppc64x,!loong64
|
||||
|
||||
package starlark
|
||||
|
||||
// generic Int implementation as a union
|
||||
|
||||
import "math/big"
|
||||
|
||||
type intImpl struct {
|
||||
// We use only the signed 32-bit range of small to ensure
|
||||
// that small+small and small*small do not overflow.
|
||||
small_ int64 // minint32 <= small <= maxint32
|
||||
big_ *big.Int // big != nil <=> value is not representable as int32
|
||||
}
|
||||
|
||||
// --- low-level accessors ---
|
||||
|
||||
// get returns the small and big components of the Int.
|
||||
// small is defined only if big is nil.
|
||||
// small is sign-extended to 64 bits for ease of subsequent arithmetic.
|
||||
func (i Int) get() (small int64, big *big.Int) {
|
||||
return i.impl.small_, i.impl.big_
|
||||
}
|
||||
|
||||
// Precondition: math.MinInt32 <= x && x <= math.MaxInt32
|
||||
func makeSmallInt(x int64) Int {
|
||||
return Int{intImpl{small_: x}}
|
||||
}
|
||||
|
||||
// Precondition: x cannot be represented as int32.
|
||||
func makeBigInt(x *big.Int) Int {
|
||||
return Int{intImpl{big_: x}}
|
||||
}
|
||||
91
vendor/go.starlark.net/starlark/int_posix64.go
generated
vendored
Normal file
91
vendor/go.starlark.net/starlark/int_posix64.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
//go:build (linux || darwin || dragonfly || freebsd || netbsd || solaris) && (amd64 || arm64 || mips64x || ppc64x || loong64)
|
||||
// +build linux darwin dragonfly freebsd netbsd solaris
|
||||
// +build amd64 arm64 mips64x ppc64x loong64
|
||||
|
||||
package starlark
|
||||
|
||||
// This file defines an optimized Int implementation for 64-bit machines
|
||||
// running POSIX. It reserves a 4GB portion of the address space using
|
||||
// mmap and represents int32 values as addresses within that range. This
|
||||
// disambiguates int32 values from *big.Int pointers, letting all Int
|
||||
// values be represented as an unsafe.Pointer, so that Int-to-Value
|
||||
// interface conversion need not allocate.
|
||||
|
||||
// Although iOS (which, like macOS, appears as darwin/arm64) is
|
||||
// POSIX-compliant, it limits each process to about 700MB of virtual
|
||||
// address space, which defeats the optimization. Similarly,
|
||||
// OpenBSD's default ulimit for virtual memory is a measly GB or so.
|
||||
// On both those platforms the attempted optimization will fail and
|
||||
// fall back to the slow implementation.
|
||||
|
||||
// An alternative approach to this optimization would be to embed the
|
||||
// int32 values in pointers using odd values, which can be distinguished
|
||||
// from (even) *big.Int pointers. However, the Go runtime does not allow
|
||||
// user programs to manufacture pointers to arbitrary locations such as
|
||||
// within the zero page, or non-span, non-mmap, non-stack locations,
|
||||
// and it may panic if it encounters them; see Issue #382.
|
||||
|
||||
import (
|
||||
"log"
|
||||
"math"
|
||||
"math/big"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// intImpl represents a union of (int32, *big.Int) in a single pointer,
|
||||
// so that Int-to-Value conversions need not allocate.
|
||||
//
|
||||
// The pointer is either a *big.Int, if the value is big, or a pointer into a
|
||||
// reserved portion of the address space (smallints), if the value is small
|
||||
// and the address space allocation succeeded.
|
||||
//
|
||||
// See int_generic.go for the basic representation concepts.
|
||||
type intImpl unsafe.Pointer
|
||||
|
||||
// get returns the (small, big) arms of the union.
|
||||
func (i Int) get() (int64, *big.Int) {
|
||||
if smallints == 0 {
|
||||
// optimization disabled
|
||||
if x := (*big.Int)(i.impl); isSmall(x) {
|
||||
return x.Int64(), nil
|
||||
} else {
|
||||
return 0, x
|
||||
}
|
||||
}
|
||||
|
||||
if ptr := uintptr(i.impl); ptr >= smallints && ptr < smallints+1<<32 {
|
||||
return math.MinInt32 + int64(ptr-smallints), nil
|
||||
}
|
||||
return 0, (*big.Int)(i.impl)
|
||||
}
|
||||
|
||||
// Precondition: math.MinInt32 <= x && x <= math.MaxInt32
|
||||
func makeSmallInt(x int64) Int {
|
||||
if smallints == 0 {
|
||||
// optimization disabled
|
||||
return Int{intImpl(big.NewInt(x))}
|
||||
}
|
||||
|
||||
return Int{intImpl(uintptr(x-math.MinInt32) + smallints)}
|
||||
}
|
||||
|
||||
// Precondition: x cannot be represented as int32.
|
||||
func makeBigInt(x *big.Int) Int { return Int{intImpl(x)} }
|
||||
|
||||
// smallints is the base address of a 2^32 byte memory region.
|
||||
// Pointers to addresses in this region represent int32 values.
|
||||
// We assume smallints is not at the very top of the address space.
|
||||
//
|
||||
// Zero means the optimization is disabled and all Ints allocate a big.Int.
|
||||
var smallints = reserveAddresses(1 << 32)
|
||||
|
||||
func reserveAddresses(len int) uintptr {
|
||||
b, err := unix.Mmap(-1, 0, len, unix.PROT_READ, unix.MAP_PRIVATE|unix.MAP_ANON)
|
||||
if err != nil {
|
||||
log.Printf("Starlark failed to allocate 4GB address space: %v. Integer performance may suffer.", err)
|
||||
return 0 // optimization disabled
|
||||
}
|
||||
return uintptr(unsafe.Pointer(&b[0]))
|
||||
}
|
||||
106
vendor/go.starlark.net/starlark/interp.go
generated
vendored
106
vendor/go.starlark.net/starlark/interp.go
generated
vendored
@@ -5,6 +5,8 @@ package starlark
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
|
||||
"go.starlark.net/internal/compile"
|
||||
"go.starlark.net/internal/spell"
|
||||
@@ -19,6 +21,9 @@ const vmdebug = false // TODO(adonovan): use a bitfield of specific kinds of err
|
||||
// - opt: record MaxIterStack during compilation and preallocate the stack.
|
||||
|
||||
func (fn *Function) CallInternal(thread *Thread, args Tuple, kwargs []Tuple) (Value, error) {
|
||||
// Postcondition: args is not mutated. This is stricter than required by Callable,
|
||||
// but allows CALL to avoid a copy.
|
||||
|
||||
if !resolve.AllowRecursion {
|
||||
// detect recursion
|
||||
for _, fr := range thread.stack[:len(thread.stack)-1] {
|
||||
@@ -76,12 +81,36 @@ func (fn *Function) CallInternal(thread *Thread, args Tuple, kwargs []Tuple) (Va
|
||||
|
||||
var iterstack []Iterator // stack of active iterators
|
||||
|
||||
// Use defer so that application panics can pass through
|
||||
// interpreter without leaving thread in a bad state.
|
||||
defer func() {
|
||||
// ITERPOP the rest of the iterator stack.
|
||||
for _, iter := range iterstack {
|
||||
iter.Done()
|
||||
}
|
||||
|
||||
fr.locals = nil
|
||||
}()
|
||||
|
||||
sp := 0
|
||||
var pc uint32
|
||||
var result Value
|
||||
code := f.Code
|
||||
loop:
|
||||
for {
|
||||
thread.Steps++
|
||||
if thread.Steps >= thread.maxSteps {
|
||||
if thread.OnMaxSteps != nil {
|
||||
thread.OnMaxSteps(thread)
|
||||
} else {
|
||||
thread.Cancel("too many steps")
|
||||
}
|
||||
}
|
||||
if reason := atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&thread.cancelReason))); reason != nil {
|
||||
err = fmt.Errorf("Starlark computation cancelled: %s", *(*string)(reason))
|
||||
break loop
|
||||
}
|
||||
|
||||
fr.pc = pc
|
||||
|
||||
op := compile.Opcode(code[pc])
|
||||
@@ -206,6 +235,34 @@ loop:
|
||||
stack[sp] = z
|
||||
sp++
|
||||
|
||||
case compile.INPLACE_PIPE:
|
||||
y := stack[sp-1]
|
||||
x := stack[sp-2]
|
||||
sp -= 2
|
||||
|
||||
// It's possible that y is not Dict but
|
||||
// nonetheless defines x|y, in which case we
|
||||
// should fall back to the general case.
|
||||
var z Value
|
||||
if xdict, ok := x.(*Dict); ok {
|
||||
if ydict, ok := y.(*Dict); ok {
|
||||
if err = xdict.ht.checkMutable("apply |= to"); err != nil {
|
||||
break loop
|
||||
}
|
||||
xdict.ht.addAll(&ydict.ht) // can't fail
|
||||
z = xdict
|
||||
}
|
||||
}
|
||||
if z == nil {
|
||||
z, err = Binary(syntax.PIPE, x, y)
|
||||
if err != nil {
|
||||
break loop
|
||||
}
|
||||
}
|
||||
|
||||
stack[sp] = z
|
||||
sp++
|
||||
|
||||
case compile.NONE:
|
||||
stack[sp] = None
|
||||
sp++
|
||||
@@ -276,9 +333,15 @@ loop:
|
||||
// positional args
|
||||
var positional Tuple
|
||||
if npos := int(arg >> 8); npos > 0 {
|
||||
positional = make(Tuple, npos)
|
||||
positional = stack[sp-npos : sp]
|
||||
sp -= npos
|
||||
copy(positional, stack[sp:])
|
||||
|
||||
// Copy positional arguments into a new array,
|
||||
// unless the callee is another Starlark function,
|
||||
// in which case it can be trusted not to mutate them.
|
||||
if _, ok := stack[sp-1].(*Function); !ok || args != nil {
|
||||
positional = append(Tuple(nil), positional...)
|
||||
}
|
||||
}
|
||||
if args != nil {
|
||||
// Add elements from *args sequence.
|
||||
@@ -527,11 +590,9 @@ loop:
|
||||
locals[arg] = stack[sp-1]
|
||||
sp--
|
||||
|
||||
case compile.SETCELL:
|
||||
x := stack[sp-2]
|
||||
y := stack[sp-1]
|
||||
sp -= 2
|
||||
y.(*cell).v = x
|
||||
case compile.SETLOCALCELL:
|
||||
locals[arg].(*cell).v = stack[sp-1]
|
||||
sp--
|
||||
|
||||
case compile.SETGLOBAL:
|
||||
fn.module.globals[arg] = stack[sp-1]
|
||||
@@ -550,9 +611,23 @@ loop:
|
||||
stack[sp] = fn.freevars[arg]
|
||||
sp++
|
||||
|
||||
case compile.CELL:
|
||||
x := stack[sp-1]
|
||||
stack[sp-1] = x.(*cell).v
|
||||
case compile.LOCALCELL:
|
||||
v := locals[arg].(*cell).v
|
||||
if v == nil {
|
||||
err = fmt.Errorf("local variable %s referenced before assignment", f.Locals[arg].Name)
|
||||
break loop
|
||||
}
|
||||
stack[sp] = v
|
||||
sp++
|
||||
|
||||
case compile.FREECELL:
|
||||
v := fn.freevars[arg].(*cell).v
|
||||
if v == nil {
|
||||
err = fmt.Errorf("local variable %s referenced before assignment", f.Freevars[arg].Name)
|
||||
break loop
|
||||
}
|
||||
stack[sp] = v
|
||||
sp++
|
||||
|
||||
case compile.GLOBAL:
|
||||
x := fn.module.globals[arg]
|
||||
@@ -582,14 +657,7 @@ loop:
|
||||
break loop
|
||||
}
|
||||
}
|
||||
|
||||
// ITERPOP the rest of the iterator stack.
|
||||
for _, iter := range iterstack {
|
||||
iter.Done()
|
||||
}
|
||||
|
||||
fr.locals = nil
|
||||
|
||||
// (deferred cleanup runs here)
|
||||
return result, err
|
||||
}
|
||||
|
||||
@@ -621,7 +689,7 @@ func (mandatory) Hash() (uint32, error) { return 0, nil }
|
||||
// A cell is a box containing a Value.
|
||||
// Local variables marked as cells hold their value indirectly
|
||||
// so that they may be shared by outer and inner nested functions.
|
||||
// Cells are always accessed using indirect CELL/SETCELL instructions.
|
||||
// Cells are always accessed using indirect {FREE,LOCAL,SETLOCAL}CELL instructions.
|
||||
// The FreeVars tuple contains only cells.
|
||||
// The FREE instruction always yields a cell.
|
||||
type cell struct{ v Value }
|
||||
|
||||
421
vendor/go.starlark.net/starlark/library.go
generated
vendored
421
vendor/go.starlark.net/starlark/library.go
generated
vendored
@@ -12,6 +12,7 @@ package starlark
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"os"
|
||||
"sort"
|
||||
@@ -38,15 +39,17 @@ func init() {
|
||||
"None": None,
|
||||
"True": True,
|
||||
"False": False,
|
||||
"abs": NewBuiltin("abs", abs),
|
||||
"any": NewBuiltin("any", any),
|
||||
"all": NewBuiltin("all", all),
|
||||
"bool": NewBuiltin("bool", bool_),
|
||||
"bytes": NewBuiltin("bytes", bytes_),
|
||||
"chr": NewBuiltin("chr", chr),
|
||||
"dict": NewBuiltin("dict", dict),
|
||||
"dir": NewBuiltin("dir", dir),
|
||||
"enumerate": NewBuiltin("enumerate", enumerate),
|
||||
"fail": NewBuiltin("fail", fail),
|
||||
"float": NewBuiltin("float", float), // requires resolve.AllowFloat
|
||||
"float": NewBuiltin("float", float),
|
||||
"getattr": NewBuiltin("getattr", getattr),
|
||||
"hasattr": NewBuiltin("hasattr", hasattr),
|
||||
"hash": NewBuiltin("hash", hash),
|
||||
@@ -72,6 +75,10 @@ func init() {
|
||||
// methods of built-in types
|
||||
// https://github.com/google/starlark-go/blob/master/doc/spec.md#built-in-methods
|
||||
var (
|
||||
bytesMethods = map[string]*Builtin{
|
||||
"elems": NewBuiltin("elems", bytes_elems),
|
||||
}
|
||||
|
||||
dictMethods = map[string]*Builtin{
|
||||
"clear": NewBuiltin("clear", dict_clear),
|
||||
"get": NewBuiltin("get", dict_get),
|
||||
@@ -116,6 +123,8 @@ var (
|
||||
"lower": NewBuiltin("lower", string_lower),
|
||||
"lstrip": NewBuiltin("lstrip", string_strip), // sic
|
||||
"partition": NewBuiltin("partition", string_partition),
|
||||
"removeprefix": NewBuiltin("removeprefix", string_removefix),
|
||||
"removesuffix": NewBuiltin("removesuffix", string_removefix),
|
||||
"replace": NewBuiltin("replace", string_replace),
|
||||
"rfind": NewBuiltin("rfind", string_rfind),
|
||||
"rindex": NewBuiltin("rindex", string_rindex),
|
||||
@@ -154,6 +163,25 @@ func builtinAttrNames(methods map[string]*Builtin) []string {
|
||||
|
||||
// ---- built-in functions ----
|
||||
|
||||
// https://github.com/google/starlark-go/blob/master/doc/spec.md#abs
|
||||
func abs(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
|
||||
var x Value
|
||||
if err := UnpackPositionalArgs("abs", args, kwargs, 1, &x); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch x := x.(type) {
|
||||
case Float:
|
||||
return Float(math.Abs(float64(x))), nil
|
||||
case Int:
|
||||
if x.Sign() >= 0 {
|
||||
return x, nil
|
||||
}
|
||||
return zero.Sub(x), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("got %s, want int or float", x.Type())
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/google/starlark-go/blob/master/doc/spec.md#all
|
||||
func all(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
|
||||
var iterable Iterable
|
||||
@@ -197,6 +225,45 @@ func bool_(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error
|
||||
return x.Truth(), nil
|
||||
}
|
||||
|
||||
// https://github.com/google/starlark-go/blob/master/doc/spec.md#bytes
|
||||
func bytes_(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
|
||||
if len(kwargs) > 0 {
|
||||
return nil, fmt.Errorf("bytes does not accept keyword arguments")
|
||||
}
|
||||
if len(args) != 1 {
|
||||
return nil, fmt.Errorf("bytes: got %d arguments, want exactly 1", len(args))
|
||||
}
|
||||
switch x := args[0].(type) {
|
||||
case Bytes:
|
||||
return x, nil
|
||||
case String:
|
||||
// Invalid encodings are replaced by that of U+FFFD.
|
||||
return Bytes(utf8Transcode(string(x))), nil
|
||||
case Iterable:
|
||||
// iterable of numeric byte values
|
||||
var buf strings.Builder
|
||||
if n := Len(x); n >= 0 {
|
||||
// common case: known length
|
||||
buf.Grow(n)
|
||||
}
|
||||
iter := x.Iterate()
|
||||
defer iter.Done()
|
||||
var elem Value
|
||||
var b byte
|
||||
for i := 0; iter.Next(&elem); i++ {
|
||||
if err := AsInt(elem, &b); err != nil {
|
||||
return nil, fmt.Errorf("bytes: at index %d, %s", i, err)
|
||||
}
|
||||
buf.WriteByte(b)
|
||||
}
|
||||
return Bytes(buf.String()), nil
|
||||
|
||||
default:
|
||||
// Unlike string(foo), which stringifies it, bytes(foo) is an error.
|
||||
return nil, fmt.Errorf("bytes: got %s, want string, bytes, or iterable of ints", x.Type())
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/google/starlark-go/blob/master/doc/spec.md#chr
|
||||
func chr(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
|
||||
if len(kwargs) > 0 {
|
||||
@@ -207,7 +274,7 @@ func chr(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error)
|
||||
}
|
||||
i, err := AsInt32(args[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("chr: got %s, want int", args[0].Type())
|
||||
return nil, fmt.Errorf("chr: %s", err)
|
||||
}
|
||||
if i < 0 {
|
||||
return nil, fmt.Errorf("chr: Unicode code point %d out of range (<0)", i)
|
||||
@@ -215,7 +282,7 @@ func chr(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error)
|
||||
if i > unicode.MaxRune {
|
||||
return nil, fmt.Errorf("chr: Unicode code point U+%X out of range (>0x10FFFF)", i)
|
||||
}
|
||||
return String(string(i)), nil
|
||||
return String(string(rune(i))), nil
|
||||
}
|
||||
|
||||
// https://github.com/google/starlark-go/blob/master/doc/spec.md#dict
|
||||
@@ -260,9 +327,6 @@ func enumerate(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, e
|
||||
}
|
||||
|
||||
iter := iterable.Iterate()
|
||||
if iter == nil {
|
||||
return nil, fmt.Errorf("enumerate: got %s, want iterable", iterable.Type())
|
||||
}
|
||||
defer iter.Done()
|
||||
|
||||
var pairs []Value
|
||||
@@ -330,13 +394,39 @@ func float(thread *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error
|
||||
return Float(0.0), nil
|
||||
}
|
||||
case Int:
|
||||
return x.Float(), nil
|
||||
return x.finiteFloat()
|
||||
case Float:
|
||||
return x, nil
|
||||
case String:
|
||||
f, err := strconv.ParseFloat(string(x), 64)
|
||||
if x == "" {
|
||||
return nil, fmt.Errorf("float: empty string")
|
||||
}
|
||||
// +/- NaN or Inf or Infinity (case insensitive)?
|
||||
s := string(x)
|
||||
switch x[len(x)-1] {
|
||||
case 'y', 'Y':
|
||||
if strings.EqualFold(s, "infinity") || strings.EqualFold(s, "+infinity") {
|
||||
return inf, nil
|
||||
} else if strings.EqualFold(s, "-infinity") {
|
||||
return neginf, nil
|
||||
}
|
||||
case 'f', 'F':
|
||||
if strings.EqualFold(s, "inf") || strings.EqualFold(s, "+inf") {
|
||||
return inf, nil
|
||||
} else if strings.EqualFold(s, "-inf") {
|
||||
return neginf, nil
|
||||
}
|
||||
case 'n', 'N':
|
||||
if strings.EqualFold(s, "nan") || strings.EqualFold(s, "+nan") || strings.EqualFold(s, "-nan") {
|
||||
return nan, nil
|
||||
}
|
||||
}
|
||||
f, err := strconv.ParseFloat(s, 64)
|
||||
if math.IsInf(f, 0) {
|
||||
return nil, fmt.Errorf("floating-point number too large")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nameErr(b, err)
|
||||
return nil, fmt.Errorf("invalid float literal: %s", s)
|
||||
}
|
||||
return Float(f), nil
|
||||
default:
|
||||
@@ -344,6 +434,12 @@ func float(thread *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
inf = Float(math.Inf(+1))
|
||||
neginf = Float(math.Inf(-1))
|
||||
nan = Float(math.NaN())
|
||||
)
|
||||
|
||||
// https://github.com/google/starlark-go/blob/master/doc/spec.md#getattr
|
||||
func getattr(thread *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
|
||||
var object, dflt Value
|
||||
@@ -400,19 +496,27 @@ func hasattr(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, err
|
||||
|
||||
// https://github.com/google/starlark-go/blob/master/doc/spec.md#hash
|
||||
func hash(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
|
||||
var s string
|
||||
if err := UnpackPositionalArgs("hash", args, kwargs, 1, &s); err != nil {
|
||||
var x Value
|
||||
if err := UnpackPositionalArgs("hash", args, kwargs, 1, &x); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// The Starlark spec requires that the hash function be
|
||||
// deterministic across all runs, motivated by the need
|
||||
// for reproducibility of builds. Thus we cannot call
|
||||
// String.Hash, which uses the fastest implementation
|
||||
// available, because as varies across process restarts,
|
||||
// and may evolve with the implementation.
|
||||
|
||||
return MakeInt(int(javaStringHash(s))), nil
|
||||
var h int64
|
||||
switch x := x.(type) {
|
||||
case String:
|
||||
// The Starlark spec requires that the hash function be
|
||||
// deterministic across all runs, motivated by the need
|
||||
// for reproducibility of builds. Thus we cannot call
|
||||
// String.Hash, which uses the fastest implementation
|
||||
// available, because as varies across process restarts,
|
||||
// and may evolve with the implementation.
|
||||
h = int64(javaStringHash(string(x)))
|
||||
case Bytes:
|
||||
h = int64(softHashString(string(x))) // FNV32
|
||||
default:
|
||||
return nil, fmt.Errorf("hash: got %s, want string or bytes", x.Type())
|
||||
}
|
||||
return MakeInt64(h), nil
|
||||
}
|
||||
|
||||
// javaStringHash returns the same hash as would be produced by
|
||||
@@ -440,95 +544,23 @@ func int_(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// "If x is not a number or base is given, x must be a string."
|
||||
if s, ok := AsString(x); ok {
|
||||
b := 10
|
||||
if base != nil {
|
||||
var err error
|
||||
b, err = AsInt32(base)
|
||||
if err != nil || b != 0 && (b < 2 || b > 36) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("int: for base, got %s, want int", base.Type())
|
||||
}
|
||||
if b != 0 && (b < 2 || b > 36) {
|
||||
return nil, fmt.Errorf("int: base must be an integer >= 2 && <= 36")
|
||||
}
|
||||
}
|
||||
|
||||
orig := s // save original for error message
|
||||
|
||||
// remove sign
|
||||
var neg bool
|
||||
if s != "" {
|
||||
if s[0] == '+' {
|
||||
s = s[1:]
|
||||
} else if s[0] == '-' {
|
||||
neg = true
|
||||
s = s[1:]
|
||||
}
|
||||
res := parseInt(s, b)
|
||||
if res == nil {
|
||||
return nil, fmt.Errorf("int: invalid literal with base %d: %s", b, s)
|
||||
}
|
||||
|
||||
// remove base prefix
|
||||
baseprefix := 0
|
||||
if len(s) > 1 && s[0] == '0' {
|
||||
if len(s) > 2 {
|
||||
switch s[1] {
|
||||
case 'o', 'O':
|
||||
s = s[2:]
|
||||
baseprefix = 8
|
||||
case 'x', 'X':
|
||||
s = s[2:]
|
||||
baseprefix = 16
|
||||
case 'b', 'B':
|
||||
s = s[2:]
|
||||
baseprefix = 2
|
||||
}
|
||||
}
|
||||
|
||||
// For automatic base detection,
|
||||
// a string starting with zero
|
||||
// must be all zeros.
|
||||
// Thus we reject int("0755", 0).
|
||||
if baseprefix == 0 && b == 0 {
|
||||
for i := 1; i < len(s); i++ {
|
||||
if s[i] != '0' {
|
||||
goto invalid
|
||||
}
|
||||
}
|
||||
return zero, nil
|
||||
}
|
||||
|
||||
if b != 0 && baseprefix != 0 && baseprefix != b {
|
||||
// Explicit base doesn't match prefix,
|
||||
// e.g. int("0o755", 16).
|
||||
goto invalid
|
||||
}
|
||||
}
|
||||
|
||||
// select base
|
||||
if b == 0 {
|
||||
if baseprefix != 0 {
|
||||
b = baseprefix
|
||||
} else {
|
||||
b = 10
|
||||
}
|
||||
}
|
||||
|
||||
// we explicitly handled sign above.
|
||||
// if a sign remains, it is invalid.
|
||||
if s != "" && (s[0] == '-' || s[0] == '+') {
|
||||
goto invalid
|
||||
}
|
||||
|
||||
// s has no sign or base prefix.
|
||||
//
|
||||
// int(x) permits arbitrary precision, unlike the scanner.
|
||||
if i, ok := new(big.Int).SetString(s, b); ok {
|
||||
res := MakeBigInt(i)
|
||||
if neg {
|
||||
res = zero.Sub(res)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
invalid:
|
||||
return nil, fmt.Errorf("int: invalid literal with base %d: %s", b, orig)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
if base != nil {
|
||||
@@ -550,6 +582,76 @@ func int_(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error)
|
||||
return i, nil
|
||||
}
|
||||
|
||||
// parseInt defines the behavior of int(string, base=int). It returns nil on error.
|
||||
func parseInt(s string, base int) Value {
|
||||
// remove sign
|
||||
var neg bool
|
||||
if s != "" {
|
||||
if s[0] == '+' {
|
||||
s = s[1:]
|
||||
} else if s[0] == '-' {
|
||||
neg = true
|
||||
s = s[1:]
|
||||
}
|
||||
}
|
||||
|
||||
// remove optional base prefix
|
||||
baseprefix := 0
|
||||
if len(s) > 1 && s[0] == '0' {
|
||||
if len(s) > 2 {
|
||||
switch s[1] {
|
||||
case 'o', 'O':
|
||||
baseprefix = 8
|
||||
case 'x', 'X':
|
||||
baseprefix = 16
|
||||
case 'b', 'B':
|
||||
baseprefix = 2
|
||||
}
|
||||
}
|
||||
if baseprefix != 0 {
|
||||
// Remove the base prefix if it matches
|
||||
// the explicit base, or if base=0.
|
||||
if base == 0 || baseprefix == base {
|
||||
base = baseprefix
|
||||
s = s[2:]
|
||||
}
|
||||
} else {
|
||||
// For automatic base detection,
|
||||
// a string starting with zero
|
||||
// must be all zeros.
|
||||
// Thus we reject int("0755", 0).
|
||||
if base == 0 {
|
||||
for i := 1; i < len(s); i++ {
|
||||
if s[i] != '0' {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return zero
|
||||
}
|
||||
}
|
||||
}
|
||||
if base == 0 {
|
||||
base = 10
|
||||
}
|
||||
|
||||
// we explicitly handled sign above.
|
||||
// if a sign remains, it is invalid.
|
||||
if s != "" && (s[0] == '-' || s[0] == '+') {
|
||||
return nil
|
||||
}
|
||||
|
||||
// s has no sign or base prefix.
|
||||
if i, ok := new(big.Int).SetString(s, base); ok {
|
||||
res := MakeBigInt(i)
|
||||
if neg {
|
||||
res = zero.Sub(res)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// https://github.com/google/starlark-go/blob/master/doc/spec.md#len
|
||||
func len_(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
|
||||
var x Value
|
||||
@@ -660,16 +762,26 @@ func ord(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error)
|
||||
if len(args) != 1 {
|
||||
return nil, fmt.Errorf("ord: got %d arguments, want 1", len(args))
|
||||
}
|
||||
s, ok := AsString(args[0])
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("ord: got %s, want string", args[0].Type())
|
||||
switch x := args[0].(type) {
|
||||
case String:
|
||||
// ord(string) returns int value of sole rune.
|
||||
s := string(x)
|
||||
r, sz := utf8.DecodeRuneInString(s)
|
||||
if sz == 0 || sz != len(s) {
|
||||
n := utf8.RuneCountInString(s)
|
||||
return nil, fmt.Errorf("ord: string encodes %d Unicode code points, want 1", n)
|
||||
}
|
||||
return MakeInt(int(r)), nil
|
||||
|
||||
case Bytes:
|
||||
// ord(bytes) returns int value of sole byte.
|
||||
if len(x) != 1 {
|
||||
return nil, fmt.Errorf("ord: bytes has length %d, want 1", len(x))
|
||||
}
|
||||
return MakeInt(int(x[0])), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("ord: got %s, want string or bytes", x.Type())
|
||||
}
|
||||
r, sz := utf8.DecodeRuneInString(s)
|
||||
if sz == 0 || sz != len(s) {
|
||||
n := utf8.RuneCountInString(s)
|
||||
return nil, fmt.Errorf("ord: string encodes %d Unicode code points, want 1", n)
|
||||
}
|
||||
return MakeInt(int(r)), nil
|
||||
}
|
||||
|
||||
// https://github.com/google/starlark-go/blob/master/doc/spec.md#print
|
||||
@@ -685,6 +797,8 @@ func print(thread *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error
|
||||
}
|
||||
if s, ok := AsString(v); ok {
|
||||
buf.WriteString(s)
|
||||
} else if b, ok := v.(Bytes); ok {
|
||||
buf.WriteString(string(b))
|
||||
} else {
|
||||
writeValue(buf, v, nil)
|
||||
}
|
||||
@@ -707,7 +821,6 @@ func range_(thread *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, erro
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO(adonovan): analyze overflow/underflows cases for 32-bit implementations.
|
||||
if len(args) == 1 {
|
||||
// range(stop)
|
||||
start, stop = 0, start
|
||||
@@ -963,11 +1076,29 @@ func str(thread *Thread, _ *Builtin, args Tuple, kwargs []Tuple) (Value, error)
|
||||
if len(args) != 1 {
|
||||
return nil, fmt.Errorf("str: got %d arguments, want exactly 1", len(args))
|
||||
}
|
||||
x := args[0]
|
||||
if _, ok := AsString(x); !ok {
|
||||
x = String(x.String())
|
||||
switch x := args[0].(type) {
|
||||
case String:
|
||||
return x, nil
|
||||
case Bytes:
|
||||
// Invalid encodings are replaced by that of U+FFFD.
|
||||
return String(utf8Transcode(string(x))), nil
|
||||
default:
|
||||
return String(x.String()), nil
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// utf8Transcode returns the UTF-8-to-UTF-8 transcoding of s.
|
||||
// The effect is that each code unit that is part of an
|
||||
// invalid sequence is replaced by U+FFFD.
|
||||
func utf8Transcode(s string) string {
|
||||
if utf8.ValidString(s) {
|
||||
return s
|
||||
}
|
||||
var out strings.Builder
|
||||
for _, r := range s {
|
||||
out.WriteRune(r)
|
||||
}
|
||||
return out.String()
|
||||
}
|
||||
|
||||
// https://github.com/google/starlark-go/blob/master/doc/spec.md#tuple
|
||||
@@ -1344,13 +1475,51 @@ func string_iterable(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value,
|
||||
if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return stringIterable{
|
||||
s: b.Receiver().(String),
|
||||
ords: b.Name()[len(b.Name())-2] == 'd',
|
||||
codepoints: b.Name()[0] == 'c',
|
||||
}, nil
|
||||
s := b.Receiver().(String)
|
||||
ords := b.Name()[len(b.Name())-2] == 'd'
|
||||
codepoints := b.Name()[0] == 'c'
|
||||
if codepoints {
|
||||
return stringCodepoints{s, ords}, nil
|
||||
} else {
|
||||
return stringElems{s, ords}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// bytes_elems returns an unspecified iterable value whose
|
||||
// iterator yields the int values of successive elements.
|
||||
func bytes_elems(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
|
||||
if err := UnpackPositionalArgs(b.Name(), args, kwargs, 0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bytesIterable{b.Receiver().(Bytes)}, nil
|
||||
}
|
||||
|
||||
// A bytesIterable is an iterable returned by bytes.elems(),
|
||||
// whose iterator yields a sequence of numeric bytes values.
|
||||
type bytesIterable struct{ bytes Bytes }
|
||||
|
||||
var _ Iterable = (*bytesIterable)(nil)
|
||||
|
||||
func (bi bytesIterable) String() string { return bi.bytes.String() + ".elems()" }
|
||||
func (bi bytesIterable) Type() string { return "bytes.elems" }
|
||||
func (bi bytesIterable) Freeze() {} // immutable
|
||||
func (bi bytesIterable) Truth() Bool { return True }
|
||||
func (bi bytesIterable) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable: %s", bi.Type()) }
|
||||
func (bi bytesIterable) Iterate() Iterator { return &bytesIterator{bi.bytes} }
|
||||
|
||||
type bytesIterator struct{ bytes Bytes }
|
||||
|
||||
func (it *bytesIterator) Next(p *Value) bool {
|
||||
if it.bytes == "" {
|
||||
return false
|
||||
}
|
||||
*p = MakeInt(int(it.bytes[0]))
|
||||
it.bytes = it.bytes[1:]
|
||||
return true
|
||||
}
|
||||
|
||||
func (*bytesIterator) Done() {}
|
||||
|
||||
// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·count
|
||||
func string_count(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
|
||||
var sub string
|
||||
@@ -1722,6 +1891,22 @@ func string_partition(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value,
|
||||
return tuple, nil
|
||||
}
|
||||
|
||||
// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·removeprefix
|
||||
// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·removesuffix
|
||||
func string_removefix(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
|
||||
recv := string(b.Receiver().(String))
|
||||
var fix string
|
||||
if err := UnpackPositionalArgs(b.Name(), args, kwargs, 1, &fix); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if b.name[len("remove")] == 'p' {
|
||||
recv = strings.TrimPrefix(recv, fix)
|
||||
} else {
|
||||
recv = strings.TrimSuffix(recv, fix)
|
||||
}
|
||||
return String(recv), nil
|
||||
}
|
||||
|
||||
// https://github.com/google/starlark-go/blob/master/doc/spec.md#string·replace
|
||||
func string_replace(_ *Thread, b *Builtin, args Tuple, kwargs []Tuple) (Value, error) {
|
||||
recv := string(b.Receiver().(String))
|
||||
|
||||
139
vendor/go.starlark.net/starlark/unpack.go
generated
vendored
139
vendor/go.starlark.net/starlark/unpack.go
generated
vendored
@@ -8,37 +8,103 @@ import (
|
||||
"log"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"go.starlark.net/internal/spell"
|
||||
)
|
||||
|
||||
// An Unpacker defines custom argument unpacking behavior.
|
||||
// See UnpackArgs.
|
||||
type Unpacker interface {
|
||||
Unpack(v Value) error
|
||||
}
|
||||
|
||||
// UnpackArgs unpacks the positional and keyword arguments into the
|
||||
// supplied parameter variables. pairs is an alternating list of names
|
||||
// and pointers to variables.
|
||||
//
|
||||
// If the variable is a bool, int, string, *List, *Dict, Callable,
|
||||
// If the variable is a bool, integer, string, *List, *Dict, Callable,
|
||||
// Iterable, or user-defined implementation of Value,
|
||||
// UnpackArgs performs the appropriate type check.
|
||||
// An int uses the AsInt32 check.
|
||||
// If the parameter name ends with "?",
|
||||
// it and all following parameters are optional.
|
||||
// Predeclared Go integer types uses the AsInt check.
|
||||
//
|
||||
// If the parameter name ends with "?", it is optional.
|
||||
//
|
||||
// If the parameter name ends with "??", it is optional and treats the None value
|
||||
// as if the argument was absent.
|
||||
//
|
||||
// If a parameter is marked optional, then all following parameters are
|
||||
// implicitly optional where or not they are marked.
|
||||
//
|
||||
// If the variable implements Unpacker, its Unpack argument
|
||||
// is called with the argument value, allowing an application
|
||||
// to define its own argument validation and conversion.
|
||||
//
|
||||
// If the variable implements Value, UnpackArgs may call
|
||||
// its Type() method while constructing the error message.
|
||||
//
|
||||
// Beware: an optional *List, *Dict, Callable, Iterable, or Value variable that is
|
||||
// not assigned is not a valid Starlark Value, so the caller must
|
||||
// explicitly handle such cases by interpreting nil as None or some
|
||||
// computed default.
|
||||
// Examples:
|
||||
//
|
||||
// var (
|
||||
// a Value
|
||||
// b = MakeInt(42)
|
||||
// c Value = starlark.None
|
||||
// )
|
||||
//
|
||||
// // 1. mixed parameters, like def f(a, b=42, c=None).
|
||||
// err := UnpackArgs("f", args, kwargs, "a", &a, "b?", &b, "c?", &c)
|
||||
//
|
||||
// // 2. keyword parameters only, like def f(*, a, b, c=None).
|
||||
// if len(args) > 0 {
|
||||
// return fmt.Errorf("f: unexpected positional arguments")
|
||||
// }
|
||||
// err := UnpackArgs("f", args, kwargs, "a", &a, "b?", &b, "c?", &c)
|
||||
//
|
||||
// // 3. positional parameters only, like def f(a, b=42, c=None, /) in Python 3.8.
|
||||
// err := UnpackPositionalArgs("f", args, kwargs, 1, &a, &b, &c)
|
||||
//
|
||||
// More complex forms such as def f(a, b=42, *args, c, d=123, **kwargs)
|
||||
// require additional logic, but their need in built-ins is exceedingly rare.
|
||||
//
|
||||
// In the examples above, the declaration of b with type Int causes UnpackArgs
|
||||
// to require that b's argument value, if provided, is also an int.
|
||||
// To allow arguments of any type, while retaining the default value of 42,
|
||||
// declare b as a Value:
|
||||
//
|
||||
// var b Value = MakeInt(42)
|
||||
//
|
||||
// The zero value of a variable of type Value, such as 'a' in the
|
||||
// examples above, is not a valid Starlark value, so if the parameter is
|
||||
// optional, the caller must explicitly handle the default case by
|
||||
// interpreting nil as None or some computed default. The same is true
|
||||
// for the zero values of variables of type *List, *Dict, Callable, or
|
||||
// Iterable. For example:
|
||||
//
|
||||
// // def myfunc(d=None, e=[], f={})
|
||||
// var (
|
||||
// d Value
|
||||
// e *List
|
||||
// f *Dict
|
||||
// )
|
||||
// err := UnpackArgs("myfunc", args, kwargs, "d?", &d, "e?", &e, "f?", &f)
|
||||
// if d == nil { d = None; }
|
||||
// if e == nil { e = new(List); }
|
||||
// if f == nil { f = new(Dict); }
|
||||
//
|
||||
func UnpackArgs(fnname string, args Tuple, kwargs []Tuple, pairs ...interface{}) error {
|
||||
nparams := len(pairs) / 2
|
||||
var defined intset
|
||||
defined.init(nparams)
|
||||
|
||||
paramName := func(x interface{}) string { // (no free variables)
|
||||
name := x.(string)
|
||||
if name[len(name)-1] == '?' {
|
||||
paramName := func(x interface{}) (name string, skipNone bool) { // (no free variables)
|
||||
name = x.(string)
|
||||
if strings.HasSuffix(name, "??") {
|
||||
name = strings.TrimSuffix(name, "??")
|
||||
skipNone = true
|
||||
} else if name[len(name)-1] == '?' {
|
||||
name = name[:len(name)-1]
|
||||
}
|
||||
return name
|
||||
|
||||
return name, skipNone
|
||||
}
|
||||
|
||||
// positional arguments
|
||||
@@ -48,8 +114,13 @@ func UnpackArgs(fnname string, args Tuple, kwargs []Tuple, pairs ...interface{})
|
||||
}
|
||||
for i, arg := range args {
|
||||
defined.set(i)
|
||||
name, skipNone := paramName(pairs[2*i])
|
||||
if skipNone {
|
||||
if _, isNone := arg.(NoneType); isNone {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if err := unpackOneArg(arg, pairs[2*i+1]); err != nil {
|
||||
name := paramName(pairs[2*i])
|
||||
return fmt.Errorf("%s: for parameter %s: %s", fnname, name, err)
|
||||
}
|
||||
}
|
||||
@@ -59,12 +130,20 @@ kwloop:
|
||||
for _, item := range kwargs {
|
||||
name, arg := item[0].(String), item[1]
|
||||
for i := 0; i < nparams; i++ {
|
||||
if paramName(pairs[2*i]) == string(name) {
|
||||
pName, skipNone := paramName(pairs[2*i])
|
||||
if pName == string(name) {
|
||||
// found it
|
||||
if defined.set(i) {
|
||||
return fmt.Errorf("%s: got multiple values for keyword argument %s",
|
||||
fnname, name)
|
||||
}
|
||||
|
||||
if skipNone {
|
||||
if _, isNone := arg.(NoneType); isNone {
|
||||
continue kwloop
|
||||
}
|
||||
}
|
||||
|
||||
ptr := pairs[2*i+1]
|
||||
if err := unpackOneArg(arg, ptr); err != nil {
|
||||
return fmt.Errorf("%s: for parameter %s: %s", fnname, name, err)
|
||||
@@ -72,7 +151,16 @@ kwloop:
|
||||
continue kwloop
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("%s: unexpected keyword argument %s", fnname, name)
|
||||
err := fmt.Errorf("%s: unexpected keyword argument %s", fnname, name)
|
||||
names := make([]string, 0, nparams)
|
||||
for i := 0; i < nparams; i += 2 {
|
||||
param, _ := paramName(pairs[i])
|
||||
names = append(names, param)
|
||||
}
|
||||
if n := spell.Nearest(string(name), names); n != "" {
|
||||
err = fmt.Errorf("%s (did you mean %s?)", err.Error(), n)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Check that all non-optional parameters are defined.
|
||||
@@ -97,6 +185,8 @@ kwloop:
|
||||
// UnpackPositionalArgs reports an error if the number of arguments is
|
||||
// less than min or greater than len(vars), if kwargs is nonempty, or if
|
||||
// any conversion fails.
|
||||
//
|
||||
// See UnpackArgs for general comments.
|
||||
func UnpackPositionalArgs(fnname string, args Tuple, kwargs []Tuple, min int, vars ...interface{}) error {
|
||||
if len(kwargs) > 0 {
|
||||
return fmt.Errorf("%s: unexpected keyword arguments", fnname)
|
||||
@@ -127,6 +217,8 @@ func UnpackPositionalArgs(fnname string, args Tuple, kwargs []Tuple, min int, va
|
||||
func unpackOneArg(v Value, ptr interface{}) error {
|
||||
// On failure, don't clobber *ptr.
|
||||
switch ptr := ptr.(type) {
|
||||
case Unpacker:
|
||||
return ptr.Unpack(v)
|
||||
case *Value:
|
||||
*ptr = v
|
||||
case *string:
|
||||
@@ -141,12 +233,15 @@ func unpackOneArg(v Value, ptr interface{}) error {
|
||||
return fmt.Errorf("got %s, want bool", v.Type())
|
||||
}
|
||||
*ptr = bool(b)
|
||||
case *int:
|
||||
i, err := AsInt32(v)
|
||||
if err != nil {
|
||||
return err
|
||||
case *int, *int8, *int16, *int32, *int64,
|
||||
*uint, *uint8, *uint16, *uint32, *uint64, *uintptr:
|
||||
return AsInt(v, ptr)
|
||||
case *float64:
|
||||
f, ok := v.(Float)
|
||||
if !ok {
|
||||
return fmt.Errorf("got %s, want float", v.Type())
|
||||
}
|
||||
*ptr = i
|
||||
*ptr = float64(f)
|
||||
case **List:
|
||||
list, ok := v.(*List)
|
||||
if !ok {
|
||||
@@ -205,7 +300,9 @@ func unpackOneArg(v Value, ptr interface{}) error {
|
||||
// Attempt to call Value.Type method.
|
||||
func() {
|
||||
defer func() { recover() }()
|
||||
paramType = paramVar.MethodByName("Type").Call(nil)[0].String()
|
||||
if typer, _ := paramVar.Interface().(interface{ Type() string }); typer != nil {
|
||||
paramType = typer.Type()
|
||||
}
|
||||
}()
|
||||
return fmt.Errorf("got %s, want %s", v.Type(), paramType)
|
||||
}
|
||||
|
||||
475
vendor/go.starlark.net/starlark/value.go
generated
vendored
475
vendor/go.starlark.net/starlark/value.go
generated
vendored
@@ -7,34 +7,35 @@
|
||||
// Starlark values are represented by the Value interface.
|
||||
// The following built-in Value types are known to the evaluator:
|
||||
//
|
||||
// NoneType -- NoneType
|
||||
// Bool -- bool
|
||||
// Int -- int
|
||||
// Float -- float
|
||||
// String -- string
|
||||
// *List -- list
|
||||
// Tuple -- tuple
|
||||
// *Dict -- dict
|
||||
// *Set -- set
|
||||
// *Function -- function (implemented in Starlark)
|
||||
// *Builtin -- builtin_function_or_method (function or method implemented in Go)
|
||||
// NoneType -- NoneType
|
||||
// Bool -- bool
|
||||
// Bytes -- bytes
|
||||
// Int -- int
|
||||
// Float -- float
|
||||
// String -- string
|
||||
// *List -- list
|
||||
// Tuple -- tuple
|
||||
// *Dict -- dict
|
||||
// *Set -- set
|
||||
// *Function -- function (implemented in Starlark)
|
||||
// *Builtin -- builtin_function_or_method (function or method implemented in Go)
|
||||
//
|
||||
// Client applications may define new data types that satisfy at least
|
||||
// the Value interface. Such types may provide additional operations by
|
||||
// implementing any of these optional interfaces:
|
||||
//
|
||||
// Callable -- value is callable like a function
|
||||
// Comparable -- value defines its own comparison operations
|
||||
// Iterable -- value is iterable using 'for' loops
|
||||
// Sequence -- value is iterable sequence of known length
|
||||
// Indexable -- value is sequence with efficient random access
|
||||
// Mapping -- value maps from keys to values, like a dictionary
|
||||
// HasBinary -- value defines binary operations such as * and +
|
||||
// HasAttrs -- value has readable fields or methods x.f
|
||||
// HasSetField -- value has settable fields x.f
|
||||
// HasSetIndex -- value supports element update using x[i]=y
|
||||
// HasSetKey -- value supports map update using x[k]=v
|
||||
// HasUnary -- value defines unary operations such as + and -
|
||||
// Callable -- value is callable like a function
|
||||
// Comparable -- value defines its own comparison operations
|
||||
// Iterable -- value is iterable using 'for' loops
|
||||
// Sequence -- value is iterable sequence of known length
|
||||
// Indexable -- value is sequence with efficient random access
|
||||
// Mapping -- value maps from keys to values, like a dictionary
|
||||
// HasBinary -- value defines binary operations such as * and +
|
||||
// HasAttrs -- value has readable fields or methods x.f
|
||||
// HasSetField -- value has settable fields x.f
|
||||
// HasSetIndex -- value supports element update using x[i]=y
|
||||
// HasSetKey -- value supports map update using x[k]=v
|
||||
// HasUnary -- value defines unary operations such as + and -
|
||||
//
|
||||
// Client applications may also define domain-specific functions in Go
|
||||
// and make them available to Starlark programs. Use NewBuiltin to
|
||||
@@ -62,7 +63,6 @@
|
||||
// through Starlark code and into callbacks. When evaluation fails it
|
||||
// returns an EvalError from which the application may obtain a
|
||||
// backtrace of active Starlark calls.
|
||||
//
|
||||
package starlark // import "go.starlark.net/starlark"
|
||||
|
||||
// This file defines the data types of Starlark and their basic operations.
|
||||
@@ -131,16 +131,41 @@ type Comparable interface {
|
||||
CompareSameType(op syntax.Token, y Value, depth int) (bool, error)
|
||||
}
|
||||
|
||||
// A TotallyOrdered is a type whose values form a total order:
|
||||
// if x and y are of the same TotallyOrdered type, then x must be less than y,
|
||||
// greater than y, or equal to y.
|
||||
//
|
||||
// It is simpler than Comparable and should be preferred in new code,
|
||||
// but if a type implements both interfaces, Comparable takes precedence.
|
||||
type TotallyOrdered interface {
|
||||
Value
|
||||
// Cmp compares two values x and y of the same totally ordered type.
|
||||
// It returns negative if x < y, positive if x > y, and zero if the values are equal.
|
||||
//
|
||||
// Implementations that recursively compare subcomponents of
|
||||
// the value should use the CompareDepth function, not Cmp, to
|
||||
// avoid infinite recursion on cyclic structures.
|
||||
//
|
||||
// The depth parameter is used to bound comparisons of cyclic
|
||||
// data structures. Implementations should decrement depth
|
||||
// before calling CompareDepth and should return an error if depth
|
||||
// < 1.
|
||||
//
|
||||
// Client code should not call this method. Instead, use the
|
||||
// standalone Compare or Equals functions, which are defined for
|
||||
// all pairs of operands.
|
||||
Cmp(y Value, depth int) (int, error)
|
||||
}
|
||||
|
||||
var (
|
||||
_ Comparable = None
|
||||
_ Comparable = Int{}
|
||||
_ Comparable = False
|
||||
_ Comparable = Float(0)
|
||||
_ Comparable = String("")
|
||||
_ Comparable = (*Dict)(nil)
|
||||
_ Comparable = (*List)(nil)
|
||||
_ Comparable = Tuple(nil)
|
||||
_ Comparable = (*Set)(nil)
|
||||
_ TotallyOrdered = Int{}
|
||||
_ TotallyOrdered = Float(0)
|
||||
_ Comparable = False
|
||||
_ Comparable = String("")
|
||||
_ Comparable = (*Dict)(nil)
|
||||
_ Comparable = (*List)(nil)
|
||||
_ Comparable = Tuple(nil)
|
||||
_ Comparable = (*Set)(nil)
|
||||
)
|
||||
|
||||
// A Callable value f may be the operand of a function call, f(x).
|
||||
@@ -229,13 +254,12 @@ var (
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// iter := iterable.Iterator()
|
||||
// iter := iterable.Iterator()
|
||||
// defer iter.Done()
|
||||
// var x Value
|
||||
// for iter.Next(&x) {
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
type Iterator interface {
|
||||
// If the iterator is exhausted, Next returns false.
|
||||
// Otherwise it sets *p to the current element of the sequence,
|
||||
@@ -276,7 +300,7 @@ type HasSetKey interface {
|
||||
var _ HasSetKey = (*Dict)(nil)
|
||||
|
||||
// A HasBinary value may be used as either operand of these binary operators:
|
||||
// + - * / // % in not in | & ^ << >>
|
||||
// + - * / // % in not in | & ^ << >>
|
||||
//
|
||||
// The Side argument indicates whether the receiver is the left or right operand.
|
||||
//
|
||||
@@ -296,7 +320,7 @@ const (
|
||||
)
|
||||
|
||||
// A HasUnary value may be used as the operand of these unary operators:
|
||||
// + - ~
|
||||
// + - ~
|
||||
//
|
||||
// An implementation may decline to handle an operation by returning (nil, nil).
|
||||
// For this reason, clients should always call the standalone Unary(op, x)
|
||||
@@ -354,9 +378,6 @@ func (NoneType) Type() string { return "NoneType" }
|
||||
func (NoneType) Freeze() {} // immutable
|
||||
func (NoneType) Truth() Bool { return False }
|
||||
func (NoneType) Hash() (uint32, error) { return 0, nil }
|
||||
func (NoneType) CompareSameType(op syntax.Token, y Value, depth int) (bool, error) {
|
||||
return threeway(op, 0), nil
|
||||
}
|
||||
|
||||
// Bool is the type of a Starlark bool.
|
||||
type Bool bool
|
||||
@@ -385,10 +406,47 @@ func (x Bool) CompareSameType(op syntax.Token, y_ Value, depth int) (bool, error
|
||||
// Float is the type of a Starlark float.
|
||||
type Float float64
|
||||
|
||||
func (f Float) String() string { return strconv.FormatFloat(float64(f), 'g', 6, 64) }
|
||||
func (f Float) Type() string { return "float" }
|
||||
func (f Float) Freeze() {} // immutable
|
||||
func (f Float) Truth() Bool { return f != 0.0 }
|
||||
func (f Float) String() string {
|
||||
var buf strings.Builder
|
||||
f.format(&buf, 'g')
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (f Float) format(buf *strings.Builder, conv byte) {
|
||||
ff := float64(f)
|
||||
if !isFinite(ff) {
|
||||
if math.IsInf(ff, +1) {
|
||||
buf.WriteString("+inf")
|
||||
} else if math.IsInf(ff, -1) {
|
||||
buf.WriteString("-inf")
|
||||
} else {
|
||||
buf.WriteString("nan")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// %g is the default format used by str.
|
||||
// It uses the minimum precision to avoid ambiguity,
|
||||
// and always includes a '.' or an 'e' so that the value
|
||||
// is self-evidently a float, not an int.
|
||||
if conv == 'g' || conv == 'G' {
|
||||
s := strconv.FormatFloat(ff, conv, -1, 64)
|
||||
buf.WriteString(s)
|
||||
// Ensure result always has a decimal point if no exponent.
|
||||
// "123" -> "123.0"
|
||||
if strings.IndexByte(s, conv-'g'+'e') < 0 && strings.IndexByte(s, '.') < 0 {
|
||||
buf.WriteString(".0")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// %[eEfF] use 6-digit precision
|
||||
buf.WriteString(strconv.FormatFloat(ff, conv, 6, 64))
|
||||
}
|
||||
|
||||
func (f Float) Type() string { return "float" }
|
||||
func (f Float) Freeze() {} // immutable
|
||||
func (f Float) Truth() Bool { return f != 0.0 }
|
||||
func (f Float) Hash() (uint32, error) {
|
||||
// Equal float and int values must yield the same hash.
|
||||
// TODO(adonovan): opt: if f is non-integral, and thus not equal
|
||||
@@ -407,29 +465,36 @@ func isFinite(f float64) bool {
|
||||
return math.Abs(f) <= math.MaxFloat64
|
||||
}
|
||||
|
||||
func (x Float) CompareSameType(op syntax.Token, y_ Value, depth int) (bool, error) {
|
||||
func (x Float) Cmp(y_ Value, depth int) (int, error) {
|
||||
y := y_.(Float)
|
||||
switch op {
|
||||
case syntax.EQL:
|
||||
return x == y, nil
|
||||
case syntax.NEQ:
|
||||
return x != y, nil
|
||||
case syntax.LE:
|
||||
return x <= y, nil
|
||||
case syntax.LT:
|
||||
return x < y, nil
|
||||
case syntax.GE:
|
||||
return x >= y, nil
|
||||
case syntax.GT:
|
||||
return x > y, nil
|
||||
return floatCmp(x, y), nil
|
||||
}
|
||||
|
||||
// floatCmp performs a three-valued comparison on floats,
|
||||
// which are totally ordered with NaN > +Inf.
|
||||
func floatCmp(x, y Float) int {
|
||||
if x > y {
|
||||
return +1
|
||||
} else if x < y {
|
||||
return -1
|
||||
} else if x == y {
|
||||
return 0
|
||||
}
|
||||
panic(op)
|
||||
|
||||
// At least one operand is NaN.
|
||||
if x == x {
|
||||
return -1 // y is NaN
|
||||
} else if y == y {
|
||||
return +1 // x is NaN
|
||||
}
|
||||
return 0 // both NaN
|
||||
}
|
||||
|
||||
func (f Float) rational() *big.Rat { return new(big.Rat).SetFloat64(float64(f)) }
|
||||
|
||||
// AsFloat returns the float64 value closest to x.
|
||||
// The f result is undefined if x is not a float or int.
|
||||
// The f result is undefined if x is not a float or Int.
|
||||
// The result may be infinite if x is a very large Int.
|
||||
func AsFloat(x Value) (f float64, ok bool) {
|
||||
switch x := x.(type) {
|
||||
case Float:
|
||||
@@ -440,7 +505,13 @@ func AsFloat(x Value) (f float64, ok bool) {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
func (x Float) Mod(y Float) Float { return Float(math.Mod(float64(x), float64(y))) }
|
||||
func (x Float) Mod(y Float) Float {
|
||||
z := Float(math.Mod(float64(x), float64(y)))
|
||||
if (x < 0) != (y < 0) && z != 0 {
|
||||
z += y
|
||||
}
|
||||
return z
|
||||
}
|
||||
|
||||
// Unary implements the operations +float and -float.
|
||||
func (f Float) Unary(op syntax.Token) (Value, error) {
|
||||
@@ -453,13 +524,20 @@ func (f Float) Unary(op syntax.Token) (Value, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// String is the type of a Starlark string.
|
||||
// String is the type of a Starlark text string.
|
||||
//
|
||||
// A String encapsulates an an immutable sequence of bytes,
|
||||
// but strings are not directly iterable. Instead, iterate
|
||||
// over the result of calling one of these four methods:
|
||||
// codepoints, codepoint_ords, elems, elem_ords.
|
||||
//
|
||||
// Strings typically contain text; use Bytes for binary strings.
|
||||
// The Starlark spec defines text strings as sequences of UTF-k
|
||||
// codes that encode Unicode code points. In this Go implementation,
|
||||
// k=8, whereas in a Java implementation, k=16. For portability,
|
||||
// operations on strings should aim to avoid assumptions about
|
||||
// the value of k.
|
||||
//
|
||||
// Warning: the contract of the Value interface's String method is that
|
||||
// it returns the value printed in Starlark notation,
|
||||
// so s.String() or fmt.Sprintf("%s", s) returns a quoted string.
|
||||
@@ -467,7 +545,7 @@ func (f Float) Unary(op syntax.Token) (Value, error) {
|
||||
// of a Starlark string as a Go string.
|
||||
type String string
|
||||
|
||||
func (s String) String() string { return strconv.Quote(string(s)) }
|
||||
func (s String) String() string { return syntax.Quote(string(s), false) }
|
||||
func (s String) GoString() string { return string(s) }
|
||||
func (s String) Type() string { return "string" }
|
||||
func (s String) Freeze() {} // immutable
|
||||
@@ -499,73 +577,106 @@ func (x String) CompareSameType(op syntax.Token, y_ Value, depth int) (bool, err
|
||||
|
||||
func AsString(x Value) (string, bool) { v, ok := x.(String); return string(v), ok }
|
||||
|
||||
// A stringIterable is an iterable whose iterator yields a sequence of
|
||||
// either Unicode code points or elements (bytes),
|
||||
// either numerically or as successive substrings.
|
||||
type stringIterable struct {
|
||||
s String
|
||||
ords bool
|
||||
codepoints bool
|
||||
// A stringElems is an iterable whose iterator yields a sequence of
|
||||
// elements (bytes), either numerically or as successive substrings.
|
||||
// It is an indexable sequence.
|
||||
type stringElems struct {
|
||||
s String
|
||||
ords bool
|
||||
}
|
||||
|
||||
var _ Iterable = (*stringIterable)(nil)
|
||||
var (
|
||||
_ Iterable = (*stringElems)(nil)
|
||||
_ Indexable = (*stringElems)(nil)
|
||||
)
|
||||
|
||||
func (si stringIterable) String() string {
|
||||
var etype string
|
||||
if si.codepoints {
|
||||
etype = "codepoint"
|
||||
} else {
|
||||
etype = "elem"
|
||||
}
|
||||
func (si stringElems) String() string {
|
||||
if si.ords {
|
||||
return si.s.String() + "." + etype + "_ords()"
|
||||
return si.s.String() + ".elem_ords()"
|
||||
} else {
|
||||
return si.s.String() + "." + etype + "s()"
|
||||
return si.s.String() + ".elems()"
|
||||
}
|
||||
}
|
||||
func (si stringIterable) Type() string {
|
||||
if si.codepoints {
|
||||
return "codepoints"
|
||||
func (si stringElems) Type() string { return "string.elems" }
|
||||
func (si stringElems) Freeze() {} // immutable
|
||||
func (si stringElems) Truth() Bool { return True }
|
||||
func (si stringElems) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable: %s", si.Type()) }
|
||||
func (si stringElems) Iterate() Iterator { return &stringElemsIterator{si, 0} }
|
||||
func (si stringElems) Len() int { return len(si.s) }
|
||||
func (si stringElems) Index(i int) Value {
|
||||
if si.ords {
|
||||
return MakeInt(int(si.s[i]))
|
||||
} else {
|
||||
return "elems"
|
||||
// TODO(adonovan): opt: preallocate canonical 1-byte strings
|
||||
// to avoid interface allocation.
|
||||
return si.s[i : i+1]
|
||||
}
|
||||
}
|
||||
func (si stringIterable) Freeze() {} // immutable
|
||||
func (si stringIterable) Truth() Bool { return True }
|
||||
func (si stringIterable) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable: %s", si.Type()) }
|
||||
func (si stringIterable) Iterate() Iterator { return &stringIterator{si, 0} }
|
||||
|
||||
type stringIterator struct {
|
||||
si stringIterable
|
||||
type stringElemsIterator struct {
|
||||
si stringElems
|
||||
i int
|
||||
}
|
||||
|
||||
func (it *stringIterator) Next(p *Value) bool {
|
||||
func (it *stringElemsIterator) Next(p *Value) bool {
|
||||
if it.i == len(it.si.s) {
|
||||
return false
|
||||
}
|
||||
*p = it.si.Index(it.i)
|
||||
it.i++
|
||||
return true
|
||||
}
|
||||
|
||||
func (*stringElemsIterator) Done() {}
|
||||
|
||||
// A stringCodepoints is an iterable whose iterator yields a sequence of
|
||||
// Unicode code points, either numerically or as successive substrings.
|
||||
// It is not indexable.
|
||||
type stringCodepoints struct {
|
||||
s String
|
||||
ords bool
|
||||
}
|
||||
|
||||
var _ Iterable = (*stringCodepoints)(nil)
|
||||
|
||||
func (si stringCodepoints) String() string {
|
||||
if si.ords {
|
||||
return si.s.String() + ".codepoint_ords()"
|
||||
} else {
|
||||
return si.s.String() + ".codepoints()"
|
||||
}
|
||||
}
|
||||
func (si stringCodepoints) Type() string { return "string.codepoints" }
|
||||
func (si stringCodepoints) Freeze() {} // immutable
|
||||
func (si stringCodepoints) Truth() Bool { return True }
|
||||
func (si stringCodepoints) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable: %s", si.Type()) }
|
||||
func (si stringCodepoints) Iterate() Iterator { return &stringCodepointsIterator{si, 0} }
|
||||
|
||||
type stringCodepointsIterator struct {
|
||||
si stringCodepoints
|
||||
i int
|
||||
}
|
||||
|
||||
func (it *stringCodepointsIterator) Next(p *Value) bool {
|
||||
s := it.si.s[it.i:]
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
if it.si.codepoints {
|
||||
r, sz := utf8.DecodeRuneInString(string(s))
|
||||
if !it.si.ords {
|
||||
r, sz := utf8.DecodeRuneInString(string(s))
|
||||
if !it.si.ords {
|
||||
if r == utf8.RuneError {
|
||||
*p = String(r)
|
||||
} else {
|
||||
*p = s[:sz]
|
||||
} else {
|
||||
*p = MakeInt(int(r))
|
||||
}
|
||||
it.i += sz
|
||||
} else {
|
||||
b := int(s[0])
|
||||
if !it.si.ords {
|
||||
*p = s[:1]
|
||||
} else {
|
||||
*p = MakeInt(b)
|
||||
}
|
||||
it.i += 1
|
||||
*p = MakeInt(int(r))
|
||||
}
|
||||
it.i += sz
|
||||
return true
|
||||
}
|
||||
|
||||
func (*stringIterator) Done() {}
|
||||
func (*stringCodepointsIterator) Done() {}
|
||||
|
||||
// A Function is a function defined by a Starlark def statement or lambda expression.
|
||||
// The initialization behavior of a Starlark module is also represented by a Function.
|
||||
@@ -624,6 +735,34 @@ func (fn *Function) Param(i int) (string, syntax.Position) {
|
||||
id := fn.funcode.Locals[i]
|
||||
return id.Name, id.Pos
|
||||
}
|
||||
|
||||
// ParamDefault returns the default value of the specified parameter
|
||||
// (0 <= i < NumParams()), or nil if the parameter is not optional.
|
||||
func (fn *Function) ParamDefault(i int) Value {
|
||||
if i < 0 || i >= fn.NumParams() {
|
||||
panic(i)
|
||||
}
|
||||
|
||||
// fn.defaults omits all required params up to the first optional param. It
|
||||
// also does not include *args or **kwargs at the end.
|
||||
firstOptIdx := fn.NumParams() - len(fn.defaults)
|
||||
if fn.HasVarargs() {
|
||||
firstOptIdx--
|
||||
}
|
||||
if fn.HasKwargs() {
|
||||
firstOptIdx--
|
||||
}
|
||||
if i < firstOptIdx || i >= firstOptIdx+len(fn.defaults) {
|
||||
return nil
|
||||
}
|
||||
|
||||
dflt := fn.defaults[i-firstOptIdx]
|
||||
if _, ok := dflt.(mandatory); ok {
|
||||
return nil
|
||||
}
|
||||
return dflt
|
||||
}
|
||||
|
||||
func (fn *Function) HasVarargs() bool { return fn.funcode.HasVarargs }
|
||||
func (fn *Function) HasKwargs() bool { return fn.funcode.HasKwargs }
|
||||
|
||||
@@ -667,13 +806,12 @@ func NewBuiltin(name string, fn func(thread *Thread, fn *Builtin, args Tuple, kw
|
||||
// In the example below, the value of f is the string.index
|
||||
// built-in method bound to the receiver value "abc":
|
||||
//
|
||||
// f = "abc".index; f("a"); f("b")
|
||||
// f = "abc".index; f("a"); f("b")
|
||||
//
|
||||
// In the common case, the receiver is bound only during the call,
|
||||
// but this still results in the creation of a temporary method closure:
|
||||
//
|
||||
// "abc".index("a")
|
||||
//
|
||||
// "abc".index("a")
|
||||
func (b *Builtin) BindReceiver(recv Value) *Builtin {
|
||||
return &Builtin{name: b.name, fn: b.fn, recv: recv}
|
||||
}
|
||||
@@ -708,6 +846,14 @@ func (d *Dict) Freeze() { d.ht.freeze()
|
||||
func (d *Dict) Truth() Bool { return d.Len() > 0 }
|
||||
func (d *Dict) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable type: dict") }
|
||||
|
||||
func (x *Dict) Union(y *Dict) *Dict {
|
||||
z := new(Dict)
|
||||
z.ht.init(x.Len()) // a lower bound
|
||||
z.ht.addAll(&x.ht) // can't fail
|
||||
z.ht.addAll(&y.ht) // can't fail
|
||||
return z
|
||||
}
|
||||
|
||||
func (d *Dict) Attr(name string) (Value, error) { return builtinAttr(d, name, dictMethods) }
|
||||
func (d *Dict) AttrNames() []string { return builtinAttrNames(dictMethods) }
|
||||
|
||||
@@ -729,8 +875,8 @@ func dictsEqual(x, y *Dict, depth int) (bool, error) {
|
||||
if x.Len() != y.Len() {
|
||||
return false, nil
|
||||
}
|
||||
for _, xitem := range x.Items() {
|
||||
key, xval := xitem[0], xitem[1]
|
||||
for e := x.ht.head; e != nil; e = e.next {
|
||||
key, xval := e.key, e.value
|
||||
|
||||
if yval, found, _ := y.Get(key); !found {
|
||||
return false, nil
|
||||
@@ -970,7 +1116,6 @@ func (s *Set) Len() int { return int(s.ht.len) }
|
||||
func (s *Set) Iterate() Iterator { return s.ht.iterate() }
|
||||
func (s *Set) String() string { return toString(s) }
|
||||
func (s *Set) Type() string { return "set" }
|
||||
func (s *Set) elems() []Value { return s.ht.keys() }
|
||||
func (s *Set) Freeze() { s.ht.freeze() }
|
||||
func (s *Set) Hash() (uint32, error) { return 0, fmt.Errorf("unhashable type: set") }
|
||||
func (s *Set) Truth() Bool { return s.Len() > 0 }
|
||||
@@ -996,8 +1141,8 @@ func setsEqual(x, y *Set, depth int) (bool, error) {
|
||||
if x.Len() != y.Len() {
|
||||
return false, nil
|
||||
}
|
||||
for _, elem := range x.elems() {
|
||||
if found, _ := y.Has(elem); !found {
|
||||
for e := x.ht.head; e != nil; e = e.next {
|
||||
if found, _ := y.Has(e.key); !found {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
@@ -1006,8 +1151,8 @@ func setsEqual(x, y *Set, depth int) (bool, error) {
|
||||
|
||||
func (s *Set) Union(iter Iterator) (Value, error) {
|
||||
set := new(Set)
|
||||
for _, elem := range s.elems() {
|
||||
set.Insert(elem) // can't fail
|
||||
for e := s.ht.head; e != nil; e = e.next {
|
||||
set.Insert(e.key) // can't fail
|
||||
}
|
||||
var x Value
|
||||
for iter.Next(&x) {
|
||||
@@ -1038,6 +1183,7 @@ func writeValue(out *strings.Builder, x Value, path []Value) {
|
||||
case nil:
|
||||
out.WriteString("<nil>") // indicates a bug
|
||||
|
||||
// These four cases are duplicates of T.String(), for efficiency.
|
||||
case NoneType:
|
||||
out.WriteString("None")
|
||||
|
||||
@@ -1052,7 +1198,7 @@ func writeValue(out *strings.Builder, x Value, path []Value) {
|
||||
}
|
||||
|
||||
case String:
|
||||
fmt.Fprintf(out, "%q", string(x))
|
||||
out.WriteString(syntax.Quote(string(x), false))
|
||||
|
||||
case *List:
|
||||
out.WriteByte('[')
|
||||
@@ -1097,8 +1243,8 @@ func writeValue(out *strings.Builder, x Value, path []Value) {
|
||||
out.WriteString("...") // dict contains itself
|
||||
} else {
|
||||
sep := ""
|
||||
for _, item := range x.Items() {
|
||||
k, v := item[0], item[1]
|
||||
for e := x.ht.head; e != nil; e = e.next {
|
||||
k, v := e.key, e.value
|
||||
out.WriteString(sep)
|
||||
writeValue(out, k, path)
|
||||
out.WriteString(": ")
|
||||
@@ -1110,11 +1256,11 @@ func writeValue(out *strings.Builder, x Value, path []Value) {
|
||||
|
||||
case *Set:
|
||||
out.WriteString("set([")
|
||||
for i, elem := range x.elems() {
|
||||
if i > 0 {
|
||||
for e := x.ht.head; e != nil; e = e.next {
|
||||
if e != x.ht.head {
|
||||
out.WriteString(", ")
|
||||
}
|
||||
writeValue(out, elem, path)
|
||||
writeValue(out, e.key, path)
|
||||
}
|
||||
out.WriteString("])")
|
||||
|
||||
@@ -1132,14 +1278,16 @@ func pathContains(path []Value, x Value) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
const maxdepth = 10
|
||||
// CompareLimit is the depth limit on recursive comparison operations such as == and <.
|
||||
// Comparison of data structures deeper than this limit may fail.
|
||||
var CompareLimit = 10
|
||||
|
||||
// Equal reports whether two Starlark values are equal.
|
||||
func Equal(x, y Value) (bool, error) {
|
||||
if x, ok := x.(String); ok {
|
||||
return x == y, nil // fast path for an important special case
|
||||
}
|
||||
return EqualDepth(x, y, maxdepth)
|
||||
return EqualDepth(x, y, CompareLimit)
|
||||
}
|
||||
|
||||
// EqualDepth reports whether two Starlark values are equal.
|
||||
@@ -1158,7 +1306,7 @@ func EqualDepth(x, y Value, depth int) (bool, error) {
|
||||
// Recursive comparisons by implementations of Value.CompareSameType
|
||||
// should use CompareDepth to prevent infinite recursion.
|
||||
func Compare(op syntax.Token, x, y Value) (bool, error) {
|
||||
return CompareDepth(op, x, y, maxdepth)
|
||||
return CompareDepth(op, x, y, CompareLimit)
|
||||
}
|
||||
|
||||
// CompareDepth compares two Starlark values.
|
||||
@@ -1177,6 +1325,14 @@ func CompareDepth(op syntax.Token, x, y Value, depth int) (bool, error) {
|
||||
return xcomp.CompareSameType(op, y, depth)
|
||||
}
|
||||
|
||||
if xcomp, ok := x.(TotallyOrdered); ok {
|
||||
t, err := xcomp.Cmp(y, depth)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return threeway(op, t), nil
|
||||
}
|
||||
|
||||
// use identity comparison
|
||||
switch op {
|
||||
case syntax.EQL:
|
||||
@@ -1193,11 +1349,10 @@ func CompareDepth(op syntax.Token, x, y Value, depth int) (bool, error) {
|
||||
switch x := x.(type) {
|
||||
case Int:
|
||||
if y, ok := y.(Float); ok {
|
||||
if y != y {
|
||||
return false, nil // y is NaN
|
||||
}
|
||||
var cmp int
|
||||
if !math.IsInf(float64(y), 0) {
|
||||
if y != y {
|
||||
cmp = -1 // y is NaN
|
||||
} else if !math.IsInf(float64(y), 0) {
|
||||
cmp = x.rational().Cmp(y.rational()) // y is finite
|
||||
} else if y > 0 {
|
||||
cmp = -1 // y is +Inf
|
||||
@@ -1208,16 +1363,15 @@ func CompareDepth(op syntax.Token, x, y Value, depth int) (bool, error) {
|
||||
}
|
||||
case Float:
|
||||
if y, ok := y.(Int); ok {
|
||||
if x != x {
|
||||
return false, nil // x is NaN
|
||||
}
|
||||
var cmp int
|
||||
if !math.IsInf(float64(x), 0) {
|
||||
if x != x {
|
||||
cmp = +1 // x is NaN
|
||||
} else if !math.IsInf(float64(x), 0) {
|
||||
cmp = x.rational().Cmp(y.rational()) // x is finite
|
||||
} else if x > 0 {
|
||||
cmp = -1 // x is +Inf
|
||||
cmp = +1 // x is +Inf
|
||||
} else {
|
||||
cmp = +1 // x is -Inf
|
||||
cmp = -1 // x is -Inf
|
||||
}
|
||||
return threeway(op, cmp), nil
|
||||
}
|
||||
@@ -1274,6 +1428,8 @@ func Len(x Value) int {
|
||||
switch x := x.(type) {
|
||||
case String:
|
||||
return x.Len()
|
||||
case Indexable:
|
||||
return x.Len()
|
||||
case Sequence:
|
||||
return x.Len()
|
||||
}
|
||||
@@ -1291,3 +1447,54 @@ func Iterate(x Value) Iterator {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Bytes is the type of a Starlark binary string.
|
||||
//
|
||||
// A Bytes encapsulates an immutable sequence of bytes.
|
||||
// It is comparable, indexable, and sliceable, but not direcly iterable;
|
||||
// use bytes.elems() for an iterable view.
|
||||
//
|
||||
// In this Go implementation, the elements of 'string' and 'bytes' are
|
||||
// both bytes, but in other implementations, notably Java, the elements
|
||||
// of a 'string' are UTF-16 codes (Java chars). The spec abstracts text
|
||||
// strings as sequences of UTF-k codes that encode Unicode code points,
|
||||
// and operations that convert from text to binary incur UTF-k-to-UTF-8
|
||||
// transcoding; conversely, conversion from binary to text incurs
|
||||
// UTF-8-to-UTF-k transcoding. Because k=8 for Go, these operations
|
||||
// are the identity function, at least for valid encodings of text.
|
||||
type Bytes string
|
||||
|
||||
var (
|
||||
_ Comparable = Bytes("")
|
||||
_ Sliceable = Bytes("")
|
||||
_ Indexable = Bytes("")
|
||||
)
|
||||
|
||||
func (b Bytes) String() string { return syntax.Quote(string(b), true) }
|
||||
func (b Bytes) Type() string { return "bytes" }
|
||||
func (b Bytes) Freeze() {} // immutable
|
||||
func (b Bytes) Truth() Bool { return len(b) > 0 }
|
||||
func (b Bytes) Hash() (uint32, error) { return String(b).Hash() }
|
||||
func (b Bytes) Len() int { return len(b) }
|
||||
func (b Bytes) Index(i int) Value { return b[i : i+1] }
|
||||
|
||||
func (b Bytes) Attr(name string) (Value, error) { return builtinAttr(b, name, bytesMethods) }
|
||||
func (b Bytes) AttrNames() []string { return builtinAttrNames(bytesMethods) }
|
||||
|
||||
func (b Bytes) Slice(start, end, step int) Value {
|
||||
if step == 1 {
|
||||
return b[start:end]
|
||||
}
|
||||
|
||||
sign := signum(step)
|
||||
var str []byte
|
||||
for i := start; signum(end-i) == sign; i += step {
|
||||
str = append(str, b[i])
|
||||
}
|
||||
return Bytes(str)
|
||||
}
|
||||
|
||||
func (x Bytes) CompareSameType(op syntax.Token, y_ Value, depth int) (bool, error) {
|
||||
y := y_.(Bytes)
|
||||
return threeway(op, strings.Compare(string(x), string(y))), nil
|
||||
}
|
||||
|
||||
9
vendor/go.starlark.net/starlarkstruct/struct.go
generated
vendored
9
vendor/go.starlark.net/starlarkstruct/struct.go
generated
vendored
@@ -66,7 +66,7 @@ func FromKeywords(constructor starlark.Value, kwargs []starlark.Tuple) *Struct {
|
||||
return s
|
||||
}
|
||||
|
||||
// FromStringDict returns a whose elements are those of d.
|
||||
// FromStringDict returns a new struct instance whose elements are those of d.
|
||||
// The constructor parameter specifies the constructor; use Default for an ordinary struct.
|
||||
func FromStringDict(constructor starlark.Value, d starlark.StringDict) *Struct {
|
||||
if constructor == nil {
|
||||
@@ -132,11 +132,12 @@ func (s *Struct) ToStringDict(d starlark.StringDict) {
|
||||
|
||||
func (s *Struct) String() string {
|
||||
buf := new(strings.Builder)
|
||||
if s.constructor == Default {
|
||||
switch constructor := s.constructor.(type) {
|
||||
case starlark.String:
|
||||
// NB: The Java implementation always prints struct
|
||||
// even for Bazel provider instances.
|
||||
buf.WriteString("struct") // avoid String()'s quotation
|
||||
} else {
|
||||
buf.WriteString(constructor.GoString()) // avoid String()'s quotation
|
||||
default:
|
||||
buf.WriteString(s.constructor.String())
|
||||
}
|
||||
buf.WriteByte('(')
|
||||
|
||||
9
vendor/go.starlark.net/syntax/parse.go
generated
vendored
9
vendor/go.starlark.net/syntax/parse.go
generated
vendored
@@ -28,7 +28,7 @@ const (
|
||||
// If src != nil, ParseFile parses the source from src and the filename
|
||||
// is only used when recording position information.
|
||||
// The type of the argument for the src parameter must be string,
|
||||
// []byte, or io.Reader.
|
||||
// []byte, io.Reader, or FilePortion.
|
||||
// If src == nil, ParseFile parses the file specified by filename.
|
||||
func Parse(filename string, src interface{}, mode Mode) (f *File, err error) {
|
||||
in, err := newScanner(filename, src, mode&RetainComments != 0)
|
||||
@@ -771,8 +771,7 @@ func (p *parser) parseArgs() []Expr {
|
||||
}
|
||||
|
||||
// primary = IDENT
|
||||
// | INT | FLOAT
|
||||
// | STRING
|
||||
// | INT | FLOAT | STRING | BYTES
|
||||
// | '[' ... // list literal or comprehension
|
||||
// | '{' ... // dict literal or comprehension
|
||||
// | '(' ... // tuple or parenthesized expression
|
||||
@@ -782,7 +781,7 @@ func (p *parser) parsePrimary() Expr {
|
||||
case IDENT:
|
||||
return p.parseIdent()
|
||||
|
||||
case INT, FLOAT, STRING:
|
||||
case INT, FLOAT, STRING, BYTES:
|
||||
var val interface{}
|
||||
tok := p.tok
|
||||
switch tok {
|
||||
@@ -794,7 +793,7 @@ func (p *parser) parsePrimary() Expr {
|
||||
}
|
||||
case FLOAT:
|
||||
val = p.tokval.float
|
||||
case STRING:
|
||||
case STRING, BYTES:
|
||||
val = p.tokval.string
|
||||
}
|
||||
raw := p.tokval.raw
|
||||
|
||||
202
vendor/go.starlark.net/syntax/quote.go
generated
vendored
202
vendor/go.starlark.net/syntax/quote.go
generated
vendored
@@ -10,6 +10,8 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// unesc maps single-letter chars following \ to their actual values.
|
||||
@@ -40,23 +42,21 @@ var esc = [256]byte{
|
||||
'"': '"',
|
||||
}
|
||||
|
||||
// notEsc is a list of characters that can follow a \ in a string value
|
||||
// without having to escape the \. That is, since ( is in this list, we
|
||||
// quote the Go string "foo\\(bar" as the Python literal "foo\(bar".
|
||||
// This really does happen in BUILD files, especially in strings
|
||||
// being used as shell arguments containing regular expressions.
|
||||
const notEsc = " !#$%&()*+,-./:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~"
|
||||
|
||||
// unquote unquotes the quoted string, returning the actual
|
||||
// string value, whether the original was triple-quoted, and
|
||||
// an error describing invalid input.
|
||||
func unquote(quoted string) (s string, triple bool, err error) {
|
||||
// string value, whether the original was triple-quoted,
|
||||
// whether it was a byte string, and an error describing invalid input.
|
||||
func unquote(quoted string) (s string, triple, isByte bool, err error) {
|
||||
// Check for raw prefix: means don't interpret the inner \.
|
||||
raw := false
|
||||
if strings.HasPrefix(quoted, "r") {
|
||||
raw = true
|
||||
quoted = quoted[1:]
|
||||
}
|
||||
// Check for bytes prefix.
|
||||
if strings.HasPrefix(quoted, "b") {
|
||||
isByte = true
|
||||
quoted = quoted[1:]
|
||||
}
|
||||
|
||||
if len(quoted) < 2 {
|
||||
err = fmt.Errorf("string literal too short")
|
||||
@@ -127,22 +127,25 @@ func unquote(quoted string) (s string, triple bool, err error) {
|
||||
|
||||
switch quoted[1] {
|
||||
default:
|
||||
// In Python, if \z (for some byte z) is not a known escape sequence
|
||||
// then it appears as literal text in the string.
|
||||
buf.WriteString(quoted[:2])
|
||||
quoted = quoted[2:]
|
||||
// In Starlark, like Go, a backslash must escape something.
|
||||
// (Python still treats unnecessary backslashes literally,
|
||||
// but since 3.6 has emitted a deprecation warning.)
|
||||
err = fmt.Errorf("invalid escape sequence \\%c", quoted[1])
|
||||
return
|
||||
|
||||
case '\n':
|
||||
// Ignore the escape and the line break.
|
||||
quoted = quoted[2:]
|
||||
|
||||
case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '\'', '"':
|
||||
// One-char escape
|
||||
// One-char escape.
|
||||
// Escapes are allowed for both kinds of quotation
|
||||
// mark, not just the kind in use.
|
||||
buf.WriteByte(unesc[quoted[1]])
|
||||
quoted = quoted[2:]
|
||||
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7':
|
||||
// Octal escape, up to 3 digits.
|
||||
// Octal escape, up to 3 digits, \OOO.
|
||||
n := int(quoted[1] - '0')
|
||||
quoted = quoted[2:]
|
||||
for i := 1; i < 3; i++ {
|
||||
@@ -152,6 +155,10 @@ func unquote(quoted string) (s string, triple bool, err error) {
|
||||
n = n*8 + int(quoted[0]-'0')
|
||||
quoted = quoted[1:]
|
||||
}
|
||||
if !isByte && n > 127 {
|
||||
err = fmt.Errorf(`non-ASCII octal escape \%o (use \u%04X for the UTF-8 encoding of U+%04X)`, n, n, n)
|
||||
return
|
||||
}
|
||||
if n >= 256 {
|
||||
// NOTE: Python silently discards the high bit,
|
||||
// so that '\541' == '\141' == 'a'.
|
||||
@@ -162,7 +169,7 @@ func unquote(quoted string) (s string, triple bool, err error) {
|
||||
buf.WriteByte(byte(n))
|
||||
|
||||
case 'x':
|
||||
// Hexadecimal escape, exactly 2 digits.
|
||||
// Hexadecimal escape, exactly 2 digits, \xXX. [0-127]
|
||||
if len(quoted) < 4 {
|
||||
err = fmt.Errorf(`truncated escape sequence %s`, quoted)
|
||||
return
|
||||
@@ -172,8 +179,41 @@ func unquote(quoted string) (s string, triple bool, err error) {
|
||||
err = fmt.Errorf(`invalid escape sequence %s`, quoted[:4])
|
||||
return
|
||||
}
|
||||
if !isByte && n > 127 {
|
||||
err = fmt.Errorf(`non-ASCII hex escape %s (use \u%04X for the UTF-8 encoding of U+%04X)`,
|
||||
quoted[:4], n, n)
|
||||
return
|
||||
}
|
||||
buf.WriteByte(byte(n))
|
||||
quoted = quoted[4:]
|
||||
|
||||
case 'u', 'U':
|
||||
// Unicode code point, 4 (\uXXXX) or 8 (\UXXXXXXXX) hex digits.
|
||||
sz := 6
|
||||
if quoted[1] == 'U' {
|
||||
sz = 10
|
||||
}
|
||||
if len(quoted) < sz {
|
||||
err = fmt.Errorf(`truncated escape sequence %s`, quoted)
|
||||
return
|
||||
}
|
||||
n, err1 := strconv.ParseUint(quoted[2:sz], 16, 0)
|
||||
if err1 != nil {
|
||||
err = fmt.Errorf(`invalid escape sequence %s`, quoted[:sz])
|
||||
return
|
||||
}
|
||||
if n > unicode.MaxRune {
|
||||
err = fmt.Errorf(`code point out of range: %s (max \U%08x)`,
|
||||
quoted[:sz], n)
|
||||
return
|
||||
}
|
||||
// As in Go, surrogates are disallowed.
|
||||
if 0xD800 <= n && n < 0xE000 {
|
||||
err = fmt.Errorf(`invalid Unicode code point U+%04X`, n)
|
||||
return
|
||||
}
|
||||
buf.WriteRune(rune(n))
|
||||
quoted = quoted[sz:]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,79 +231,79 @@ func indexByte(s string, b byte) int {
|
||||
return -1
|
||||
}
|
||||
|
||||
// hex is a list of the hexadecimal digits, for use in quoting.
|
||||
// We always print lower-case hexadecimal.
|
||||
const hex = "0123456789abcdef"
|
||||
// Quote returns a Starlark literal that denotes s.
|
||||
// If b, it returns a bytes literal.
|
||||
func Quote(s string, b bool) string {
|
||||
const hex = "0123456789abcdef"
|
||||
var runeTmp [utf8.UTFMax]byte
|
||||
|
||||
// quote returns the quoted form of the string value "x".
|
||||
// If triple is true, quote uses the triple-quoted form """x""".
|
||||
func quote(unquoted string, triple bool) string {
|
||||
q := `"`
|
||||
if triple {
|
||||
q = `"""`
|
||||
buf := make([]byte, 0, 3*len(s)/2)
|
||||
if b {
|
||||
buf = append(buf, 'b')
|
||||
}
|
||||
|
||||
buf := new(strings.Builder)
|
||||
buf.WriteString(q)
|
||||
|
||||
for i := 0; i < len(unquoted); i++ {
|
||||
c := unquoted[i]
|
||||
if c == '"' && triple && (i+1 < len(unquoted) && unquoted[i+1] != '"' || i+2 < len(unquoted) && unquoted[i+2] != '"') {
|
||||
// Can pass up to two quotes through, because they are followed by a non-quote byte.
|
||||
buf.WriteByte(c)
|
||||
if i+1 < len(unquoted) && unquoted[i+1] == '"' {
|
||||
buf.WriteByte(c)
|
||||
i++
|
||||
}
|
||||
buf = append(buf, '"')
|
||||
for width := 0; len(s) > 0; s = s[width:] {
|
||||
r := rune(s[0])
|
||||
width = 1
|
||||
if r >= utf8.RuneSelf {
|
||||
r, width = utf8.DecodeRuneInString(s)
|
||||
}
|
||||
if width == 1 && r == utf8.RuneError {
|
||||
// String (!b) literals accept \xXX escapes only for ASCII,
|
||||
// but we must use them here to represent invalid bytes.
|
||||
// The result is not a legal literal.
|
||||
buf = append(buf, `\x`...)
|
||||
buf = append(buf, hex[s[0]>>4])
|
||||
buf = append(buf, hex[s[0]&0xF])
|
||||
continue
|
||||
}
|
||||
if triple && c == '\n' {
|
||||
// Can allow newline in triple-quoted string.
|
||||
buf.WriteByte(c)
|
||||
if r == '"' || r == '\\' { // always backslashed
|
||||
buf = append(buf, '\\')
|
||||
buf = append(buf, byte(r))
|
||||
continue
|
||||
}
|
||||
if c == '\'' {
|
||||
// Can allow ' since we always use ".
|
||||
buf.WriteByte(c)
|
||||
if strconv.IsPrint(r) {
|
||||
n := utf8.EncodeRune(runeTmp[:], r)
|
||||
buf = append(buf, runeTmp[:n]...)
|
||||
continue
|
||||
}
|
||||
if c == '\\' {
|
||||
if i+1 < len(unquoted) && indexByte(notEsc, unquoted[i+1]) >= 0 {
|
||||
// Can pass \ through when followed by a byte that
|
||||
// known not to be a valid escape sequence and also
|
||||
// that does not trigger an escape sequence of its own.
|
||||
// Use this, because various BUILD files do.
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteByte(unquoted[i+1])
|
||||
i++
|
||||
continue
|
||||
switch r {
|
||||
case '\a':
|
||||
buf = append(buf, `\a`...)
|
||||
case '\b':
|
||||
buf = append(buf, `\b`...)
|
||||
case '\f':
|
||||
buf = append(buf, `\f`...)
|
||||
case '\n':
|
||||
buf = append(buf, `\n`...)
|
||||
case '\r':
|
||||
buf = append(buf, `\r`...)
|
||||
case '\t':
|
||||
buf = append(buf, `\t`...)
|
||||
case '\v':
|
||||
buf = append(buf, `\v`...)
|
||||
default:
|
||||
switch {
|
||||
case r < ' ' || r == 0x7f:
|
||||
buf = append(buf, `\x`...)
|
||||
buf = append(buf, hex[byte(r)>>4])
|
||||
buf = append(buf, hex[byte(r)&0xF])
|
||||
case r > utf8.MaxRune:
|
||||
r = 0xFFFD
|
||||
fallthrough
|
||||
case r < 0x10000:
|
||||
buf = append(buf, `\u`...)
|
||||
for s := 12; s >= 0; s -= 4 {
|
||||
buf = append(buf, hex[r>>uint(s)&0xF])
|
||||
}
|
||||
default:
|
||||
buf = append(buf, `\U`...)
|
||||
for s := 28; s >= 0; s -= 4 {
|
||||
buf = append(buf, hex[r>>uint(s)&0xF])
|
||||
}
|
||||
}
|
||||
}
|
||||
if esc[c] != 0 {
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteByte(esc[c])
|
||||
continue
|
||||
}
|
||||
if c < 0x20 || c >= 0x80 {
|
||||
// BUILD files are supposed to be Latin-1, so escape all control and high bytes.
|
||||
// I'd prefer to use \x here, but Blaze does not implement
|
||||
// \x in quoted strings (b/7272572).
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteByte(hex[c>>6]) // actually octal but reusing hex digits 0-7.
|
||||
buf.WriteByte(hex[(c>>3)&7])
|
||||
buf.WriteByte(hex[c&7])
|
||||
/*
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteByte('x')
|
||||
buf.WriteByte(hex[c>>4])
|
||||
buf.WriteByte(hex[c&0xF])
|
||||
*/
|
||||
continue
|
||||
}
|
||||
buf.WriteByte(c)
|
||||
continue
|
||||
}
|
||||
|
||||
buf.WriteString(q)
|
||||
return buf.String()
|
||||
buf = append(buf, '"')
|
||||
return string(buf)
|
||||
}
|
||||
|
||||
72
vendor/go.starlark.net/syntax/scan.go
generated
vendored
72
vendor/go.starlark.net/syntax/scan.go
generated
vendored
@@ -35,6 +35,7 @@ const (
|
||||
INT // 123
|
||||
FLOAT // 1.23e45
|
||||
STRING // "foo" or 'foo' or '''foo''' or r'foo' or r"foo"
|
||||
BYTES // b"foo", etc
|
||||
|
||||
// Punctuation
|
||||
PLUS // +
|
||||
@@ -182,6 +183,15 @@ var tokenNames = [...]string{
|
||||
WHILE: "while",
|
||||
}
|
||||
|
||||
// A FilePortion describes the content of a portion of a file.
|
||||
// Callers may provide a FilePortion for the src argument of Parse
|
||||
// when the desired initial line and column numbers are not (1, 1),
|
||||
// such as when an expression is parsed from within larger file.
|
||||
type FilePortion struct {
|
||||
Content []byte
|
||||
FirstLine, FirstCol int32
|
||||
}
|
||||
|
||||
// A Position describes the location of a rune of input.
|
||||
type Position struct {
|
||||
file *string // filename (indirect for compactness)
|
||||
@@ -249,13 +259,17 @@ type scanner struct {
|
||||
}
|
||||
|
||||
func newScanner(filename string, src interface{}, keepComments bool) (*scanner, error) {
|
||||
var firstLine, firstCol int32 = 1, 1
|
||||
if portion, ok := src.(FilePortion); ok {
|
||||
firstLine, firstCol = portion.FirstLine, portion.FirstCol
|
||||
}
|
||||
sc := &scanner{
|
||||
pos: Position{file: &filename, Line: 1, Col: 1},
|
||||
pos: MakePosition(&filename, firstLine, firstCol),
|
||||
indentstk: make([]int, 1, 10), // []int{0} + spare capacity
|
||||
lineStart: true,
|
||||
keepComments: keepComments,
|
||||
}
|
||||
sc.readline, _ = src.(func() ([]byte, error)) // REPL only
|
||||
sc.readline, _ = src.(func() ([]byte, error)) // ParseCompoundStmt (REPL) only
|
||||
if sc.readline == nil {
|
||||
data, err := readSource(filename, src)
|
||||
if err != nil {
|
||||
@@ -279,6 +293,8 @@ func readSource(filename string, src interface{}) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
case FilePortion:
|
||||
return src.Content, nil
|
||||
case nil:
|
||||
return ioutil.ReadFile(filename)
|
||||
default:
|
||||
@@ -407,7 +423,7 @@ type tokenValue struct {
|
||||
int int64 // decoded int
|
||||
bigInt *big.Int // decoded integers > int64
|
||||
float float64 // decoded float
|
||||
string string // decoded string
|
||||
string string // decoded string or bytes
|
||||
pos Position // start position of token
|
||||
}
|
||||
|
||||
@@ -627,8 +643,15 @@ start:
|
||||
|
||||
// identifier or keyword
|
||||
if isIdentStart(c) {
|
||||
// raw string literal
|
||||
if c == 'r' && len(sc.rest) > 1 && (sc.rest[1] == '"' || sc.rest[1] == '\'') {
|
||||
if (c == 'r' || c == 'b') && len(sc.rest) > 1 && (sc.rest[1] == '"' || sc.rest[1] == '\'') {
|
||||
// r"..."
|
||||
// b"..."
|
||||
sc.readRune()
|
||||
c = sc.peekRune()
|
||||
return sc.scanString(val, c)
|
||||
} else if c == 'r' && len(sc.rest) > 2 && sc.rest[1] == 'b' && (sc.rest[2] == '"' || sc.rest[2] == '\'') {
|
||||
// rb"..."
|
||||
sc.readRune()
|
||||
sc.readRune()
|
||||
c = sc.peekRune()
|
||||
return sc.scanString(val, c)
|
||||
@@ -805,13 +828,26 @@ func (sc *scanner) scanString(val *tokenValue, quote rune) Token {
|
||||
start := sc.pos
|
||||
triple := len(sc.rest) >= 3 && sc.rest[0] == byte(quote) && sc.rest[1] == byte(quote) && sc.rest[2] == byte(quote)
|
||||
sc.readRune()
|
||||
|
||||
// String literals may contain escaped or unescaped newlines,
|
||||
// causing them to span multiple lines (gulps) of REPL input;
|
||||
// they are the only such token. Thus we cannot call endToken,
|
||||
// as it assumes sc.rest is unchanged since startToken.
|
||||
// Instead, buffer the token here.
|
||||
// TODO(adonovan): opt: buffer only if we encounter a newline.
|
||||
raw := new(strings.Builder)
|
||||
|
||||
// Copy the prefix, e.g. r' or " (see startToken).
|
||||
raw.Write(sc.token[:len(sc.token)-len(sc.rest)])
|
||||
|
||||
if !triple {
|
||||
// Precondition: startToken was already called.
|
||||
// single-quoted string literal
|
||||
for {
|
||||
if sc.eof() {
|
||||
sc.error(val.pos, "unexpected EOF in string")
|
||||
}
|
||||
c := sc.readRune()
|
||||
raw.WriteRune(c)
|
||||
if c == quote {
|
||||
break
|
||||
}
|
||||
@@ -822,22 +858,16 @@ func (sc *scanner) scanString(val *tokenValue, quote rune) Token {
|
||||
if sc.eof() {
|
||||
sc.error(val.pos, "unexpected EOF in string")
|
||||
}
|
||||
sc.readRune()
|
||||
c = sc.readRune()
|
||||
raw.WriteRune(c)
|
||||
}
|
||||
}
|
||||
sc.endToken(val)
|
||||
} else {
|
||||
// triple-quoted string literal
|
||||
sc.readRune()
|
||||
raw.WriteRune(quote)
|
||||
sc.readRune()
|
||||
|
||||
// A triple-quoted string literal may span multiple
|
||||
// gulps of REPL input; it is the only such token.
|
||||
// Thus we must avoid {start,end}Token.
|
||||
raw := new(strings.Builder)
|
||||
|
||||
// Copy the prefix, e.g. r''' or """ (see startToken).
|
||||
raw.Write(sc.token[:len(sc.token)-len(sc.rest)])
|
||||
raw.WriteRune(quote)
|
||||
|
||||
quoteCount := 0
|
||||
for {
|
||||
@@ -862,15 +892,19 @@ func (sc *scanner) scanString(val *tokenValue, quote rune) Token {
|
||||
raw.WriteRune(c)
|
||||
}
|
||||
}
|
||||
val.raw = raw.String()
|
||||
}
|
||||
val.raw = raw.String()
|
||||
|
||||
s, _, err := unquote(val.raw)
|
||||
s, _, isByte, err := unquote(val.raw)
|
||||
if err != nil {
|
||||
sc.error(start, err.Error())
|
||||
}
|
||||
val.string = s
|
||||
return STRING
|
||||
if isByte {
|
||||
return BYTES
|
||||
} else {
|
||||
return STRING
|
||||
}
|
||||
}
|
||||
|
||||
func (sc *scanner) scanNumber(val *tokenValue, c rune) Token {
|
||||
|
||||
8
vendor/go.starlark.net/syntax/syntax.go
generated
vendored
8
vendor/go.starlark.net/syntax/syntax.go
generated
vendored
@@ -251,10 +251,10 @@ func (x *Ident) Span() (start, end Position) {
|
||||
// A Literal represents a literal string or number.
|
||||
type Literal struct {
|
||||
commentsRef
|
||||
Token Token // = STRING | INT
|
||||
Token Token // = STRING | BYTES | INT | FLOAT
|
||||
TokenPos Position
|
||||
Raw string // uninterpreted text
|
||||
Value interface{} // = string | int64 | *big.Int
|
||||
Value interface{} // = string | int64 | *big.Int | float64
|
||||
}
|
||||
|
||||
func (x *Literal) Span() (start, end Position) {
|
||||
@@ -398,10 +398,6 @@ func (x *DictEntry) Span() (start, end Position) {
|
||||
}
|
||||
|
||||
// A LambdaExpr represents an inline function abstraction.
|
||||
//
|
||||
// Although they may be added in future, lambda expressions are not
|
||||
// currently part of the Starlark spec, so their use is controlled by the
|
||||
// resolver.AllowLambda flag.
|
||||
type LambdaExpr struct {
|
||||
commentsRef
|
||||
Lambda Position
|
||||
|
||||
4
vendor/go.starlark.net/syntax/walk.go
generated
vendored
4
vendor/go.starlark.net/syntax/walk.go
generated
vendored
@@ -119,9 +119,7 @@ func Walk(n Node, f func(Node) bool) {
|
||||
|
||||
case *DictExpr:
|
||||
for _, entry := range n.List {
|
||||
entry := entry.(*DictEntry)
|
||||
Walk(entry.Key, f)
|
||||
Walk(entry.Value, f)
|
||||
Walk(entry, f)
|
||||
}
|
||||
|
||||
case *UnaryExpr:
|
||||
|
||||
Reference in New Issue
Block a user