1
0
mirror of https://github.com/golang/go synced 2024-11-12 07:00:21 -07:00

interpreter checkpoint.

* generate different versions of binary operators
    for each size of int and float, so that proper
    truncating happens after each operation to
    simulate the various sized ops.
  * add slice expressions
  * publish World.CompileStmtList, CompileDeclList, CompileExpr
  * handle type-less expressions in CompileExpr

R=austin
DELTA=1459  (1327 added, 11 deleted, 121 changed)
OCL=34382
CL=35581
This commit is contained in:
Russ Cox 2009-10-11 02:35:53 -07:00
parent 1620023d03
commit e98412290e
8 changed files with 1447 additions and 132 deletions

View File

@ -58,6 +58,14 @@ func (e IndexError) String() string {
return fmt.Sprintf("index %d exceeds length %d", e.Idx, e.Len);
}
type SliceError struct {
Lo, Hi, Cap int64;
}
func (e SliceError) String() string {
return fmt.Sprintf("slice [%d:%d]; cap %d", e.Lo, e.Hi, e.Cap);
}
type KeyError struct {
Key interface {};
}

View File

@ -65,7 +65,7 @@ func (a test) run(t *testing.T, name string) {
t.Errorf("%s: Compile %s succeeded; want %s", name, src, j.cerr);
break;
}
val, err := code.Run();
if err != nil {
if j.rterr == "" {
@ -82,7 +82,7 @@ func (a test) run(t *testing.T, name string) {
t.Errorf("%s: Run %s succeeded; want %s", name, src, j.rterr);
break;
}
if !j.noval && !reflect.DeepEqual(val, j.val) {
t.Errorf("%s: Run %s = %T(%v) want %T(%v)", name, src, val, val, j.val, j.val);
}
@ -122,6 +122,13 @@ func Run(stmts string) test {
return test([]job{job{code: stmts, noval: true}})
}
// Two statements without error.
// TODO(rsc): Should be possible with Run but the parser
// won't let us do both top-level and non-top-level statements.
func Run2(stmt1, stmt2 string) test {
return test([]job{job{code: stmt1, noval: true}, job{code: stmt2, noval: true}})
}
// Statement runs and test one expression's value
func Val1(stmts string, expr1 string, val1 interface{}) test {
return test([]job{

View File

@ -11,6 +11,7 @@ import (
"log";
"strconv";
"strings";
"os";
)
// An expr is the result of compiling an expression. It stores the
@ -160,7 +161,11 @@ func (a *expr) convertToInt(max int64, negErr string, errOp string) *expr {
a.diag("negative %s: %s", negErr, val);
return nil;
}
if max != -1 && val.Cmp(bignum.Int(max)) >= 0 {
bound := max;
if negErr == "slice" {
bound++;
}
if max != -1 && val.Cmp(bignum.Int(bound)) >= 0 {
a.diag("index %s exceeds length %d", val, max);
return nil;
}
@ -289,7 +294,7 @@ func (a *assignCompiler) allowMapForms(nls int) {
a.allowMap = true;
// Update unpacking info if this is r, ok = a[x]
if nls == 2 && len(a.rs) == 1 && a.rs[0].evalMapValue != nil {
if nls == 2 && len(a.rs) == 1 && a.rs[0] != nil && a.rs[0].evalMapValue != nil {
a.isUnpack = true;
a.rmt = NewMultiType([]Type {a.rs[0].t, BoolType});
a.isMapUnpack = true;
@ -551,7 +556,7 @@ func (a *exprCompiler) compile(x ast.Expr, callCtx bool) *expr {
args := make([]*expr, len(x.Args));
bad := false;
for i, arg := range x.Args {
if i == 0 && l.t == Type(makeType) {
if i == 0 && l != nil && (l.t == Type(makeType) || l.t == Type(newType)) {
argei := &exprInfo{a.compiler, arg.Pos()};
args[i] = argei.exprFromType(a.compileType(a.block, arg));
} else {
@ -561,7 +566,7 @@ func (a *exprCompiler) compile(x ast.Expr, callCtx bool) *expr {
bad = true;
}
}
if l == nil || bad {
if bad || l == nil {
return nil;
}
if a.constant {
@ -583,8 +588,13 @@ func (a *exprCompiler) compile(x ast.Expr, callCtx bool) *expr {
case *ast.IndexExpr:
if x.End != nil {
a.diagAt(x, "slice expression not implemented");
return nil;
arr := a.compile(x.X, false);
lo := a.compile(x.Index, false);
hi := a.compile(x.End, false);
if arr == nil || lo == nil || hi == nil {
return nil;
}
return ei.compileSliceExpr(arr, lo, hi);
}
l, r := a.compile(x.X, false), a.compile(x.Index, false);
if l == nil || r == nil {
@ -926,6 +936,86 @@ func (a *exprInfo) compileSelectorExpr(v *expr, name string) *expr {
return builder(v);
}
func (a *exprInfo) compileSliceExpr(arr, lo, hi *expr) *expr {
// Type check object
arr = arr.derefArray();
var at Type;
var maxIndex int64 = -1;
switch lt := arr.t.lit().(type) {
case *ArrayType:
at = NewSliceType(lt.Elem);
maxIndex = lt.Len;
case *SliceType:
at = lt;
case *stringType:
at = lt;
default:
a.diag("cannot slice %v", arr.t);
return nil;
}
// Type check index and convert to int
// XXX(Spec) It's unclear if ideal floats with no
// fractional part are allowed here. 6g allows it. I
// believe that's wrong.
lo = lo.convertToInt(maxIndex, "slice", "slice");
hi = hi.convertToInt(maxIndex, "slice", "slice");
if lo == nil || hi == nil {
return nil;
}
expr := a.newExpr(at, "slice expression");
// Compile
lof := lo.asInt();
hif := hi.asInt();
switch lt := arr.t.lit().(type) {
case *ArrayType:
arrf := arr.asArray();
bound := lt.Len;
expr.eval = func(t *Thread) Slice {
arr, lo, hi := arrf(t), lof(t), hif(t);
if lo > hi || hi > bound || lo < 0 {
t.Abort(SliceError{lo, hi, bound});
}
return Slice{arr.Sub(lo, bound - lo), hi - lo, bound - lo}
};
case *SliceType:
arrf := arr.asSlice();
expr.eval = func(t *Thread) Slice {
arr, lo, hi := arrf(t), lof(t), hif(t);
if lo > hi || hi > arr.Cap || lo < 0 {
t.Abort(SliceError{lo, hi, arr.Cap});
}
return Slice{arr.Base.Sub(lo, arr.Cap - lo), hi - lo, arr.Cap - lo}
};
case *stringType:
arrf := arr.asString();
// TODO(austin) This pulls over the whole string in a
// remote setting, instead of creating a substring backed
// by remote memory.
expr.eval = func(t *Thread) string {
arr, lo, hi := arrf(t), lof(t), hif(t);
if lo > hi || hi > int64(len(arr)) || lo < 0 {
t.Abort(SliceError{lo, hi, int64(len(arr))});
}
return arr[lo:hi];
}
default:
log.Crashf("unexpected left operand type %T", arr.t.lit());
}
return expr;
}
func (a *exprInfo) compileIndexExpr(l, r *expr) *expr {
// Type check object
l = l.derefArray();
@ -1297,9 +1387,66 @@ func (a *exprInfo) compileBuiltinCallExpr(b *block, ft *FuncType, as []*expr) *e
return nil;
}
case closeType, closedType, newType, panicType, paniclnType, printType, printlnType:
case closeType, closedType:
a.diag("built-in function %s not implemented", ft.builtin);
return nil;
case newType:
if !checkCount(1, 1) {
return nil;
}
t := as[0].valType;
expr := a.newExpr(NewPtrType(t), "new");
expr.eval = func(*Thread) Value {
return t.Zero();
};
return expr;
case panicType, paniclnType, printType, printlnType:
evals := make([]func(*Thread)interface{}, len(as));
for i, x := range as {
evals[i] = x.asInterface();
}
spaces := ft == paniclnType || ft == printlnType;
newline := ft != printType;
printer := func(t *Thread) {
for i, eval := range evals {
if i > 0 && spaces {
print(" ");
}
v := eval(t);
type stringer interface { String() string }
switch v1 := v.(type) {
case bool:
print(v1);
case uint64:
print(v1);
case int64:
print(v1);
case float64:
print(v1);
case string:
print(v1);
case stringer:
print(v1.String());
default:
print("???");
}
}
if newline {
print("\n");
}
};
expr := a.newExpr(EmptyType, "print");
expr.exec = printer;
if ft == panicType || ft == paniclnType {
expr.exec = func(t *Thread) {
printer(t);
t.Abort(os.NewError("panic"));
}
}
return expr;
}
log.Crashf("unexpected built-in function '%s'", ft.builtin);
@ -1680,13 +1827,9 @@ func (a *exprInfo) compileBinaryExpr(op token.Token, l, r *expr) *expr {
expr.genBinOpMul(l, r);
case token.QUO:
// TODO(austin) Clear higher bits that may have
// accumulated in our temporary.
expr.genBinOpQuo(l, r);
case token.REM:
// TODO(austin) Clear higher bits that may have
// accumulated in our temporary.
expr.genBinOpRem(l, r);
case token.AND:
@ -1745,6 +1888,12 @@ func (a *exprInfo) compileBinaryExpr(op token.Token, l, r *expr) *expr {
case token.NEQ:
expr.genBinOpNeq(l, r);
case token.LAND:
expr.genBinOpLogAnd(l, r);
case token.LOR:
expr.genBinOpLogOr(l, r);
default:
log.Crashf("Compilation of binary op %v not implemented", op);
}

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,11 @@ type Op struct {
Types []*Type;
}
type Size struct {
Bits int;
Sized string;
}
type Type struct {
Repr string;
Value string;
@ -29,14 +34,21 @@ type Type struct {
As string;
IsIdeal bool;
HasAssign bool;
Sizes []Size;
}
var (
boolType = &Type{ Repr: "*boolType", Value: "BoolValue", Native: "bool", As: "asBool" };
uintType = &Type{ Repr: "*uintType", Value: "UintValue", Native: "uint64", As: "asUint" };
intType = &Type{ Repr: "*intType", Value: "IntValue", Native: "int64", As: "asInt" };
uintType = &Type{ Repr: "*uintType", Value: "UintValue", Native: "uint64", As: "asUint",
Sizes: []Size{ Size{8, "uint8"}, Size{16, "uint16"}, Size{32, "uint32"}, Size{64, "uint64"}, Size{0, "uint"}}
};
intType = &Type{ Repr: "*intType", Value: "IntValue", Native: "int64", As: "asInt",
Sizes: []Size{Size{8, "int8"}, Size{16, "int16"}, Size{32, "int32"}, Size{64, "int64"}, Size{0, "int"}}
};
idealIntType = &Type{ Repr: "*idealIntType", Value: "IdealIntValue", Native: "*bignum.Integer", As: "asIdealInt", IsIdeal: true };
floatType = &Type{ Repr: "*floatType", Value: "FloatValue", Native: "float64", As: "asFloat" };
floatType = &Type{ Repr: "*floatType", Value: "FloatValue", Native: "float64", As: "asFloat",
Sizes: []Size{Size{32, "float32"}, Size{64, "float64"}, Size{0, "float"}}
};
idealFloatType = &Type{ Repr: "*idealFloatType", Value: "IdealFloatValue", Native: "*bignum.Rational", As: "asIdealFloat", IsIdeal: true };
stringType = &Type{ Repr: "*stringType", Value: "StringValue", Native: "string", As: "asString" };
arrayType = &Type{ Repr: "*ArrayType", Value: "ArrayValue", Native: "ArrayValue", As: "asArray", HasAssign: true };
@ -91,12 +103,12 @@ var binOps = []Op{
Op{ Name: "Sub", Expr: "l - r", ConstExpr: "l.Sub(r)", Types: numbers },
Op{ Name: "Mul", Expr: "l * r", ConstExpr: "l.Mul(r)", Types: numbers },
Op{ Name: "Quo",
Body: "if r == 0 { t.Abort(DivByZeroError{}) } return l / r",
Body: "if r == 0 { t.Abort(DivByZeroError{}) } ret = l / r",
ConstExpr: "l.Quo(r)",
Types: numbers,
},
Op{ Name: "Rem",
Body: "if r == 0 { t.Abort(DivByZeroError{}) } return l % r",
Body: "if r == 0 { t.Abort(DivByZeroError{}) } ret = l % r",
ConstExpr: "l.Rem(r)",
Types: integers,
},
@ -163,10 +175,11 @@ func (a *expr) asMulti() (func(*Thread) []Value) {
func (a *expr) asInterface() (func(*Thread) interface{}) {
switch sf := a.eval.(type) {
«.repeated section Types»
case func(*Thread)«Native»:
«.section IsIdeal»
return func(t *Thread) interface{} { return sf(t) }
case func()«Native»:
return func(*Thread) interface{} { return sf() }
«.or»
case func(t *Thread)«Native»:
return func(t *Thread) interface{} { return sf(t) }
«.end»
«.end»
@ -263,26 +276,68 @@ func (a *expr) genUnaryOp«Name»(v *expr) {
}
«.end»
func (a *expr) genBinOpLogAnd(l, r *expr) {
lf := l.asBool();
rf := r.asBool();
a.eval = func(t *Thread) bool { return lf(t) && rf(t) }
}
func (a *expr) genBinOpLogOr(l, r *expr) {
lf := l.asBool();
rf := r.asBool();
a.eval = func(t *Thread) bool { return lf(t) || rf(t) }
}
«.repeated section BinaryOps»
func (a *expr) genBinOp«Name»(l, r *expr) {
switch l.t.lit().(type) {
switch t := l.t.lit().(type) {
«.repeated section Types»
case «Repr»:
«.section IsIdeal»
«.section IsIdeal»
l := l.«As»()();
r := r.«As»()();
val := «ConstExpr»;
«.section ReturnType»
«.section ReturnType»
a.eval = func(t *Thread) «ReturnType» { return val }
«.or»
«.or»
a.eval = func() «Native» { return val }
«.end»
«.or»
«.end»
«.or»
lf := l.«As»();
rf := r.«.section AsRightName»«@»«.or»«As»«.end»();
a.eval = func(t *Thread) «.section ReturnType»«@»«.or»«Native»«.end» { l, r := lf(t), rf(t); «.section Body»«Body»«.or»return «Expr»«.end» }
«.end»
«.end»
«.section ReturnType»
a.eval = func(t *Thread) «@» {
l, r := lf(t), rf(t);
return «Expr»
}
«.or»
«.section Sizes»
switch t.Bits {
«.repeated section @»
case «Bits»:
a.eval = func(t *Thread) «Native» {
l, r := lf(t), rf(t);
var ret «Native»;
«.section Body»
«Body»;
«.or»
ret = «Expr»;
«.end»
return «Native»(«Sized»(ret))
}
«.end»
default:
log.Crashf("unexpected size %d in type %v at %v", t.Bits, t, a.pos);
}
«.or»
a.eval = func(t *Thread) «Native» {
l, r := lf(t), rf(t);
return «Expr»
}
«.end»
«.end»
«.end»
«.end»
default:
log.Crashf("unexpected type %v at %v", l.t, a.pos);
}

View File

@ -7,11 +7,66 @@ package main
import (
"./_obj/eval";
"bufio";
"flag";
"go/parser";
"go/scanner";
"io";
"os";
)
var filename = flag.String("f", "", "file to run");
func main() {
flag.Parse();
w := eval.NewWorld();
if *filename != "" {
data, err := io.ReadFile(*filename);
if err != nil {
println(err.String());
os.Exit(1);
}
file, err := parser.ParseFile(*filename, data, 0);
if err != nil {
println(err.String());
os.Exit(1);
}
code, err := w.CompileDeclList(file.Decls);
if err != nil {
if list, ok := err.(scanner.ErrorList); ok {
for _, e := range list {
println(e.String());
}
} else {
println(err.String());
}
os.Exit(1);
}
_, err := code.Run();
if err != nil {
println(err.String());
os.Exit(1);
}
code, err = w.Compile("init()");
if code != nil {
_, err := code.Run();
if err != nil {
println(err.String());
os.Exit(1);
}
}
code, err = w.Compile("main()");
if err != nil {
println(err.String());
os.Exit(1);
}
_, err = code.Run();
if err != nil {
println(err.String());
os.Exit(1);
}
os.Exit(0);
}
r := bufio.NewReader(os.Stdin);
for {
print("; ");

View File

@ -331,9 +331,11 @@ var stmtTests = []test {
CErr("x := make(map[int] int); (func(a,b int){})(x[0])", "not enough"),
CErr("x := make(map[int] int); x[1] = oneTwo()", "too many"),
RErr("x := make(map[int] int); i = x[1]", "key '1' not found"),
// Functions
Val2("func fib(n int) int { if n <= 2 { return n } return fib(n-1) + fib(n-2) }", "fib(4)", 5, "fib(10)", 89),
Run("func f1(){}"),
Run2("func f1(){}", "f1()"),
}
func TestStmt(t *testing.T) {

View File

@ -12,9 +12,6 @@ import (
"os";
)
// TODO: Make CompileExpr and CompileStmts
// methods on World.
type World struct {
scope *Scope;
frame *Frame;
@ -41,10 +38,10 @@ type stmtCode struct {
code code;
}
func (w *World) compileStmts(stmts []ast.Stmt) (Code, os.Error) {
func (w *World) CompileStmtList(stmts []ast.Stmt) (Code, os.Error) {
if len(stmts) == 1 {
if s, ok := stmts[0].(*ast.ExprStmt); ok {
return w.compileExpr(s.X);
return w.CompileExpr(s.X);
}
}
errors := scanner.NewErrorVector();
@ -73,12 +70,12 @@ func (w *World) compileStmts(stmts []ast.Stmt) (Code, os.Error) {
return &stmtCode{w, fc.get()}, nil;
}
func (w *World) compileDecls(decls []ast.Decl) (Code, os.Error) {
func (w *World) CompileDeclList(decls []ast.Decl) (Code, os.Error) {
stmts := make([]ast.Stmt, len(decls));
for i, d := range decls {
stmts[i] = &ast.DeclStmt{d};
}
return w.compileStmts(stmts);
return w.CompileStmtList(stmts);
}
func (s *stmtCode) Type() Type {
@ -97,7 +94,7 @@ type exprCode struct {
eval func(Value, *Thread);
}
func (w *World) compileExpr(e ast.Expr) (Code, os.Error) {
func (w *World) CompileExpr(e ast.Expr) (Code, os.Error) {
errors := scanner.NewErrorVector();
cc := &compiler{errors, 0, 0};
@ -144,13 +141,13 @@ func (e *exprCode) Run() (Value, os.Error) {
func (w *World) Compile(text string) (Code, os.Error) {
stmts, err := parser.ParseStmtList("input", text);
if err == nil {
return w.compileStmts(stmts);
return w.CompileStmtList(stmts);
}
// Otherwise try as DeclList.
decls, err1 := parser.ParseDeclList("input", text);
if err1 == nil {
return w.compileDecls(decls);
return w.CompileDeclList(decls);
}
// Have to pick an error.