aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--eval.go45
-rw-r--r--internal/compile/compile.go64
-rw-r--r--interp.go53
-rw-r--r--value.go16
4 files changed, 68 insertions, 110 deletions
diff --git a/eval.go b/eval.go
index 1575279..fbe26e5 100644
--- a/eval.go
+++ b/eval.go
@@ -8,7 +8,9 @@ import (
"bytes"
"fmt"
"io"
+ "log"
"math"
+ "math/big"
"sort"
"strings"
"unicode"
@@ -266,11 +268,7 @@ func CompiledProgram(in io.Reader) (*Program, error) {
// executes the toplevel code of the specified program,
// and returns a new, unfrozen dictionary of the globals.
func (prog *Program) Init(thread *Thread, predeclared StringDict) (StringDict, error) {
- toplevel := &Function{
- funcode: prog.compiled.Toplevel,
- predeclared: predeclared,
- globals: make([]Value, len(prog.compiled.Globals)),
- }
+ toplevel := makeToplevelFunction(prog.compiled.Toplevel, predeclared)
_, err := toplevel.Call(thread, nil, nil)
@@ -279,6 +277,34 @@ func (prog *Program) Init(thread *Thread, predeclared StringDict) (StringDict, e
return toplevel.Globals(), err
}
+func makeToplevelFunction(funcode *compile.Funcode, predeclared StringDict) *Function {
+ // Create the Skylark value denoted by each program constant c.
+ constants := make([]Value, len(funcode.Prog.Constants))
+ for i, c := range funcode.Prog.Constants {
+ var v Value
+ switch c := c.(type) {
+ case int64:
+ v = MakeInt64(c)
+ case *big.Int:
+ v = Int{c}
+ case string:
+ v = String(c)
+ case float64:
+ v = Float(c)
+ default:
+ log.Fatalf("unexpected constant %T: %v", c, c)
+ }
+ constants[i] = v
+ }
+
+ return &Function{
+ funcode: funcode,
+ predeclared: predeclared,
+ globals: make([]Value, len(funcode.Prog.Globals)),
+ constants: constants,
+ }
+}
+
// Eval parses, resolves, and evaluates an expression within the
// specified (predeclared) environment.
//
@@ -300,12 +326,9 @@ func Eval(thread *Thread, filename string, src interface{}, env StringDict) (Val
return nil, err
}
- f := &Function{
- funcode: compile.Expr(expr, locals),
- predeclared: env,
- globals: nil,
- }
- return f.Call(thread, nil, nil)
+ fn := makeToplevelFunction(compile.Expr(expr, locals), env)
+
+ return fn.Call(thread, nil, nil)
}
// The following functions are primitive operations of the byte code interpreter.
diff --git a/internal/compile/compile.go b/internal/compile/compile.go
index 01d33a1..6d86b4b 100644
--- a/internal/compile/compile.go
+++ b/internal/compile/compile.go
@@ -26,9 +26,9 @@ import (
"bytes"
"fmt"
"log"
- "math/big"
"os"
"path/filepath"
+ "strconv"
"github.com/google/skylark/resolve"
"github.com/google/skylark/syntax"
@@ -37,7 +37,7 @@ import (
const debug = false // TODO(adonovan): use a bitmap of options; and regexp to match files
// Increment this to force recompilation of saved bytecode files.
-const Version = 1
+const Version = 2
type Opcode uint8
@@ -106,10 +106,7 @@ const (
ITERJMP // - ITERJMP<addr> elem (and fall through) [acts on topmost iterator]
// or: - ITERJMP<addr> - (and jump)
- INT // - INT<constant> value
- FLOAT // - FLOAT<constant> value
- BIGINT // - BIGINT<constant> value
- STRING // - STRING<constant> value
+ CONSTANT // - CONSTANT<constant> value
MAKETUPLE // x1 ... xn MAKETUPLE<n> tuple
MAKELIST // x1 ... xn MAKELIST<n> list
MAKEFUNC // args kwargs MAKEFUNC<func> fn
@@ -141,17 +138,16 @@ var opcodeNames = [...]string{
AMP: "amp",
APPEND: "append",
ATTR: "attr",
- BIGINT: "bigint",
CALL: "call",
CALL_KW: "call_kw ",
CALL_VAR: "call_var",
CALL_VAR_KW: "call_var_kw",
CJMP: "cjmp",
- DUP: "dup",
+ CONSTANT: "constant",
DUP2: "dup2",
+ DUP: "dup",
EQL: "eql",
FALSE: "false",
- FLOAT: "float",
FREE: "free",
GE: "ge",
GLOBAL: "global",
@@ -159,7 +155,6 @@ var opcodeNames = [...]string{
IN: "in",
INDEX: "index",
INPLACE_ADD: "inplace_add",
- INT: "int",
ITERJMP: "iterjmp",
ITERPOP: "iterpop",
ITERPUSH: "iterpush",
@@ -193,7 +188,6 @@ var opcodeNames = [...]string{
SLASHSLASH: "slashslash",
SLICE: "slice",
STAR: "star",
- STRING: "string",
TRUE: "true",
UMINUS: "uminus",
UNIVERSAL: "universal",
@@ -209,17 +203,16 @@ var stackEffect = [...]int8{
AMP: -1,
APPEND: -2,
ATTR: 0,
- BIGINT: +1,
CALL: variableStackEffect,
CALL_KW: variableStackEffect,
CALL_VAR: variableStackEffect,
CALL_VAR_KW: variableStackEffect,
CJMP: -1,
- DUP: +1,
+ CONSTANT: +1,
DUP2: +2,
+ DUP: +1,
EQL: -1,
FALSE: +1,
- FLOAT: +1,
FREE: +1,
GE: -1,
GLOBAL: +1,
@@ -227,10 +220,9 @@ var stackEffect = [...]int8{
IN: -1,
INDEX: -1,
INPLACE_ADD: -1,
- INT: +1,
ITERJMP: variableStackEffect,
- ITERPUSH: -1,
ITERPOP: 0,
+ ITERPUSH: -1,
JMP: 0,
LE: -1,
LOAD: -1,
@@ -246,10 +238,10 @@ var stackEffect = [...]int8{
NOP: 0,
NOT: 0,
PERCENT: -1,
- PREDECLARED: +1,
PIPE: -1,
PLUS: -1,
POP: -1,
+ PREDECLARED: +1,
RETURN: -1,
SETDICT: -3,
SETDICTUNIQ: -3,
@@ -261,10 +253,9 @@ var stackEffect = [...]int8{
SLASHSLASH: -1,
SLICE: -3,
STAR: -1,
- STRING: +1,
TRUE: +1,
- UNPACK: variableStackEffect,
UNIVERSAL: +1,
+ UNPACK: variableStackEffect,
}
func (op Opcode) String() string {
@@ -281,7 +272,7 @@ func (op Opcode) String() string {
type Program struct {
Loads []Ident // name (really, string) and position of each load stmt
Names []string // names of attributes and predeclared variables
- Constants []interface{} // = string | int64 | float64
+ Constants []interface{} // = string | int64 | float64 | *big.Int
Functions []*Funcode
Globals []Ident // for error messages and tracing
Toplevel *Funcode // module initialization function
@@ -741,10 +732,13 @@ func PrintOp(fn *Funcode, pc uint32, op Opcode, arg uint32) {
var comment string
switch op {
- case INT, FLOAT, BIGINT:
- comment = fmt.Sprint(fn.Prog.Constants[arg])
- case STRING:
- comment = fmt.Sprintf("%q", fn.Prog.Constants[arg])
+ case CONSTANT:
+ switch x := fn.Prog.Constants[arg].(type) {
+ case string:
+ comment = strconv.Quote(x)
+ default:
+ comment = fmt.Sprint(x)
+ }
case MAKEFUNC:
comment = fn.Prog.Functions[arg].Name
case SETLOCAL, LOCAL:
@@ -856,12 +850,7 @@ func (pcomp *pcomp) functionIndex(fn *Funcode) uint32 {
// string emits code to push the specified string.
func (fcomp *fcomp) string(s string) {
- fcomp.emit1(STRING, fcomp.pcomp.constantIndex(s))
-}
-
-// int64 emits code to push the specified integer.
-func (fcomp *fcomp) int64(i int64) {
- fcomp.emit1(INT, fcomp.pcomp.constantIndex(i))
+ fcomp.emit1(CONSTANT, fcomp.pcomp.constantIndex(s))
}
// setPos sets the current source position.
@@ -1137,19 +1126,8 @@ func (fcomp *fcomp) expr(e syntax.Expr) {
fcomp.lookup(e)
case *syntax.Literal:
- switch e.Token {
- case syntax.INT:
- switch e.Value.(type) {
- case int64:
- fcomp.int64(e.Value.(int64))
- case *big.Int:
- fcomp.emit1(BIGINT, fcomp.pcomp.constantIndex(e.Value.(*big.Int).String()))
- }
- case syntax.FLOAT:
- fcomp.emit1(FLOAT, fcomp.pcomp.constantIndex(e.Value.(float64)))
- case syntax.STRING:
- fcomp.string(e.Value.(string))
- }
+ // e.Value is int64, float64, *bigInt, or string.
+ fcomp.emit1(CONSTANT, fcomp.pcomp.constantIndex(e.Value))
case *syntax.ListExpr:
for _, x := range e.List {
diff --git a/interp.go b/interp.go
index 6854ebe..8b41cf5 100644
--- a/interp.go
+++ b/interp.go
@@ -4,9 +4,7 @@ package skylark
import (
"fmt"
- "math/big"
"os"
- "unsafe"
"github.com/google/skylark/internal/compile"
"github.com/google/skylark/syntax"
@@ -433,29 +431,8 @@ loop:
}
sp--
- case compile.INT:
- stack[sp] = MakeInt64(f.Prog.Constants[arg].(int64))
- sp++
-
- case compile.BIGINT:
- s := f.Prog.Constants[arg].(string)
- bigint := new(big.Int)
- if _, ok := bigint.SetString(s, 10); !ok {
- err = fmt.Errorf("internal error: failed to parse compiled large integer constant")
- break loop
- }
- stack[sp] = Int{bigint}
- sp++
-
- case compile.FLOAT:
- stack[sp] = Float(f.Prog.Constants[arg].(float64))
- sp++
-
- case compile.STRING:
- // TODO(adonovan): opt: avoid allocation here
- // by prematerializing String values.
- // stack[sp] = String(f.Prog.Constants[arg].(string))
- stack[sp] = string2String(f.Prog.Constants[arg])
+ case compile.CONSTANT:
+ stack[sp] = fr.fn.constants[arg]
sp++
case compile.MAKETUPLE:
@@ -483,6 +460,7 @@ loop:
funcode: funcode,
predeclared: fr.fn.predeclared,
globals: fr.fn.globals,
+ constants: fr.fn.constants,
defaults: defaults,
freevars: freevars,
}
@@ -585,28 +563,3 @@ loop:
}
return result, err
}
-
-// string2String converts an empty interface containing a string into a
-// Value containing a String, without allocating a new string header;
-// see github.com/golang/go/issues/24582.
-// TODO(adonovan): do this safely by preconverting constants a priori.
-// This requires that Program provide a field for use by the interpreter.
-func string2String(x interface{}) (y Value) {
- // Equivalent to:
- // return String(x.(string))
- // but avoids allocation.
-
- _ = x.(string)
- type iface struct {
- typ, data unsafe.Pointer
- }
- xi := (*iface)((unsafe.Pointer)(&x))
- yi := (*iface)((unsafe.Pointer)(&y))
- si := (*iface)((unsafe.Pointer)(&dummystr))
-
- yi.typ = si.typ
- yi.data = xi.data
- return y
-}
-
-var dummystr Value = String("")
diff --git a/value.go b/value.go
index 25a0dbe..8e8940f 100644
--- a/value.go
+++ b/value.go
@@ -464,13 +464,17 @@ func (it *stringIterator) Next(p *Value) bool {
func (*stringIterator) Done() {}
-// A Function is a function defined by a Skylark def statement.
+// A Function is a function defined by a Skylark def statement or lambda expression.
+// The initialization behavior of a Skylark module is also represented by a Function.
type Function struct {
- funcode *compile.Funcode
- predeclared StringDict // names predeclared in the current module
- globals []Value // globals of the current module
- defaults Tuple
- freevars Tuple
+ funcode *compile.Funcode
+ defaults Tuple
+ freevars Tuple
+
+ // These fields are shared by all functions in a module.
+ predeclared StringDict
+ globals []Value
+ constants []Value
}
func (fn *Function) Name() string { return fn.funcode.Name } // "lambda" for anonymous functions