func (a *exprCompiler) genBinOpAndNot(l *exprCompiler, r *exprCompiler)
func (a *exprCompiler) genBinOpShl(l *exprCompiler, r *exprCompiler)
func (a *exprCompiler) genBinOpShr(l *exprCompiler, r *exprCompiler)
+func genAssign(lt Type, r *exprCompiler) (func(lv Value, f *Frame))
func (a *exprCompiler) copy() *exprCompiler {
ec := newExprCompiler(a.exprContext, a.pos);
a.diag("illegal operand types for '%v' operator\n\t%v\n\t%v", op, lt, rt);
}
+/*
+ * "As" functions. These retrieve evaluator functions from an
+ * exprCompiler, panicking if the requested evaluator is nil.
+ */
+
func (a *exprCompiler) asBool() (func(f *Frame) bool) {
if a.evalBool == nil {
log.Crashf("tried to get %v node as boolType", a.t);
return a.evalPtr;
}
-// TODO(austin) Move convertTo somewhere more reasonable
-func (a *exprCompiler) convertTo(t Type) *exprCompiler
+/*
+ * Common expression manipulations
+ */
+
+// a.convertTo(t) converts the value of the analyzed expression a,
+// which must be a constant, ideal number, to a new analyzed
+// expression with a constant value of type t.
+func (a *exprCompiler) convertTo(t Type) *exprCompiler {
+ if !a.t.isIdeal() {
+ log.Crashf("attempted to convert from %v, expected ideal", a.t);
+ }
+
+ var rat *bignum.Rational;
+
+ // XXX(Spec) The spec says "It is erroneous".
+ //
+ // It is an error to assign a value with a non-zero fractional
+ // part to an integer, or if the assignment would overflow or
+ // underflow, or in general if the value cannot be represented
+ // by the type of the variable.
+ switch a.t {
+ case IdealFloatType:
+ rat = a.asIdealFloat()();
+ if t.isInteger() && !rat.IsInt() {
+ a.diag("constant %v truncated to integer", ratToString(rat));
+ return nil;
+ }
+ case IdealIntType:
+ i := a.asIdealInt()();
+ rat = bignum.MakeRat(i, bignum.Nat(1));
+ default:
+ log.Crashf("unexpected ideal type %v", a.t);
+ }
+
+ // Check bounds
+ if t, ok := t.(BoundedType); ok {
+ if rat.Cmp(t.minVal()) < 0 {
+ a.diag("constant %v underflows %v", ratToString(rat), t);
+ return nil;
+ }
+ if rat.Cmp(t.maxVal()) > 0 {
+ a.diag("constant %v overflows %v", ratToString(rat), t);
+ return nil;
+ }
+ }
+
+ // Convert rat to type t.
+ res := a.copy();
+ res.t = t;
+ switch t := t.(type) {
+ case *uintType:
+ n, d := rat.Value();
+ f := n.Quo(bignum.MakeInt(false, d));
+ v := f.Abs().Value();
+ res.evalUint = func(*Frame) uint64 { return v };
+ case *intType:
+ n, d := rat.Value();
+ f := n.Quo(bignum.MakeInt(false, d));
+ v := f.Value();
+ res.evalInt = func(*Frame) int64 { return v };
+ case *idealIntType:
+ n, d := rat.Value();
+ f := n.Quo(bignum.MakeInt(false, d));
+ res.evalIdealInt = func() *bignum.Integer { return f };
+ case *floatType:
+ n, d := rat.Value();
+ v := float64(n.Value())/float64(d.Value());
+ res.evalFloat = func(*Frame) float64 { return v };
+ case *idealFloatType:
+ res.evalIdealFloat = func() *bignum.Rational { return rat };
+ default:
+ log.Crashf("cannot convert to type %T", t);
+ }
+
+ return res;
+}
+
+// mkAssign takes an optional expected l-value type, lt, and an
+// r-value expression compiler, r, and returns the expected l-value
+// type and a function that evaluates the r-value and assigns it to
+// the l-value lv.
+//
+// If lt is non-nil, the returned l-value type will always be lt. If
+// lt is nil, mkAssign will infer and return the appropriate l-value
+// type, or produce an error.
+//
+// errOp specifies the operation name to use for error messages, such
+// as "assignment", or "function call". errPos, if non-zero,
+// specifies the position of this assignment (for tuple assignments or
+// function arguments). errPosName specifies the name to use for
+// positions.
+//
+// If the assignment fails to typecheck, this generates an error
+// message and returns nil, nil.
+func mkAssign(lt Type, r *exprCompiler, errOp string, errPos int, errPosName string) (Type, func(lv Value, f *Frame)) {
+ // However, when [an ideal is] (used in an expression)
+ // assigned to a variable or typed constant, the destination
+ // must be able to represent the assigned value.
+ if r.t.isIdeal() && (lt == nil || lt.isInteger() || lt.isFloat()) {
+ // If the type is absent and the corresponding
+ // expression is a constant expression of ideal
+ // integer or ideal float type, the type of the
+ // declared variable is int or float respectively.
+ if lt == nil {
+ switch {
+ case r.t.isInteger():
+ lt = IntType;
+ case r.t.isFloat():
+ lt = FloatType;
+ default:
+ log.Crashf("unexpected ideal type %v", r.t);
+ }
+ }
+ r = r.convertTo(lt);
+ if r == nil {
+ return nil, nil;
+ }
+ }
+
+ // TOOD(austin) Deal with assignment special cases
+
+ if lt == nil {
+ lt = r.t;
+ } else {
+ // Values of any type may always be assigned to
+ // variables of compatible static type.
+ if !lt.compatible(r.t) {
+ if errPos == 0 {
+ r.diag("illegal operand types for %s\n\t%v\n\t%v", errOp, lt, r.t);
+ } else {
+ r.diag("illegal operand types in %s %d of %s\n\t%v\n\t%v", errPosName, errPos, errOp, lt, r.t);
+ }
+ return nil, nil;
+ }
+ }
+
+ // Compile
+ return lt, genAssign(lt, r);
+}
+
+/*
+ * Expression visitors
+ */
func (a *exprCompiler) DoBadExpr(x *ast.BadExpr) {
// Do nothing. Already reported by parser.
}
}
-// a.convertTo(t) converts the value of the analyzed expression a,
-// which must be a constant, ideal number, to a new analyzed
-// expression with a constant value of type t.
-func (a *exprCompiler) convertTo(t Type) *exprCompiler {
- if !a.t.isIdeal() {
- log.Crashf("attempted to convert from %v, expected ideal", a.t);
- }
-
- var rat *bignum.Rational;
-
- // It is erroneous to assign a value with a non-zero
- // fractional part to an integer, or if the assignment would
- // overflow or underflow, or in general if the value cannot be
- // represented by the type of the variable.
- switch a.t {
- case IdealFloatType:
- rat = a.asIdealFloat()();
- if t.isInteger() && !rat.IsInt() {
- a.diag("constant %v truncated to integer", ratToString(rat));
- return nil;
- }
- case IdealIntType:
- i := a.asIdealInt()();
- rat = bignum.MakeRat(i, bignum.Nat(1));
- default:
- log.Crashf("unexpected ideal type %v", a.t);
- }
-
- // Check bounds
- if t, ok := t.(BoundedType); ok {
- if rat.Cmp(t.minVal()) < 0 {
- a.diag("constant %v underflows %v", ratToString(rat), t);
- return nil;
- }
- if rat.Cmp(t.maxVal()) > 0 {
- a.diag("constant %v overflows %v", ratToString(rat), t);
- return nil;
- }
- }
-
- // Convert rat to type t.
- res := a.copy();
- res.t = t;
- switch t := t.(type) {
- case *uintType:
- n, d := rat.Value();
- f := n.Quo(bignum.MakeInt(false, d));
- v := f.Abs().Value();
- res.evalUint = func(*Frame) uint64 { return v };
- case *intType:
- n, d := rat.Value();
- f := n.Quo(bignum.MakeInt(false, d));
- v := f.Value();
- res.evalInt = func(*Frame) int64 { return v };
- case *idealIntType:
- n, d := rat.Value();
- f := n.Quo(bignum.MakeInt(false, d));
- res.evalIdealInt = func() *bignum.Integer { return f };
- case *floatType:
- n, d := rat.Value();
- v := float64(n.Value())/float64(d.Value());
- res.evalFloat = func(*Frame) float64 { return v };
- case *idealFloatType:
- res.evalIdealFloat = func() *bignum.Rational { return rat };
- default:
- log.Crashf("cannot convert to type %T", t);
- }
-
- return res;
-}
-
var binOpDescs = make(map[token.Token] string)
-func (a *exprCompiler) DoBinaryExpr(x *ast.BinaryExpr) {
- l, r := a.copyVisit(x.X), a.copyVisit(x.Y);
- if l.t == nil || r.t == nil {
- return;
- }
-
+func (a *exprCompiler) doBinaryExpr(op token.Token, l, r *exprCompiler) {
// Save the original types of l.t and r.t for error messages.
origlt := l.t;
origrt := r.t;
// relevant only for / and %? If I add an ideal int and an
// ideal float, I get an ideal float.
- if x.Op != token.SHL && x.Op != token.SHR {
+ if op != token.SHL && op != token.SHR {
// Except in shift expressions, if one operand has
// numeric type and the other operand is an ideal
// number, the ideal number is converted to match the
};
// Type check
- switch x.Op {
+ switch op {
case token.ADD:
if !compat() || (!integers() && !floats() && !strings()) {
- a.diagOpTypes(x.Op, origlt, origrt);
+ a.diagOpTypes(op, origlt, origrt);
return;
}
a.t = l.t;
case token.SUB, token.MUL, token.QUO:
if !compat() || (!integers() && !floats()) {
- a.diagOpTypes(x.Op, origlt, origrt);
+ a.diagOpTypes(op, origlt, origrt);
return;
}
a.t = l.t;
case token.REM, token.AND, token.OR, token.XOR, token.AND_NOT:
if !compat() || !integers() {
- a.diagOpTypes(x.Op, origlt, origrt);
+ a.diagOpTypes(op, origlt, origrt);
return;
}
a.t = l.t;
// (§Arithmetic operators)" suggests so and 6g agrees.
if !l.t.isInteger() || !(r.t.isInteger() || r.t.isIdeal()) {
- a.diagOpTypes(x.Op, origlt, origrt);
+ a.diagOpTypes(op, origlt, origrt);
return;
}
// ... booleans may be compared only for equality or
// inequality.
if l.t.literal() == BoolType || r.t.literal() == BoolType {
- a.diagOpTypes(x.Op, origlt, origrt);
+ a.diagOpTypes(op, origlt, origrt);
return;
}
// "except bools" is really weird here, since this is
// actually explained in the Comparison compatibility
// section.
- log.Crashf("Binary op %v not implemented", x.Op);
+ log.Crashf("Binary op %v not implemented", op);
// TODO(austin) Unnamed bool? Named bool?
a.t = BoolType;
default:
- log.Crashf("unknown binary operator %v", x.Op);
+ log.Crashf("unknown binary operator %v", op);
}
var ok bool;
- a.desc, ok = binOpDescs[x.Op];
+ a.desc, ok = binOpDescs[op];
if !ok {
- a.desc = x.Op.String() + " expression";
- binOpDescs[x.Op] = a.desc;
+ a.desc = op.String() + " expression";
+ binOpDescs[op] = a.desc;
}
// Compile
- switch x.Op {
+ switch op {
case token.ADD:
a.genBinOpAdd(l, r);
}
default:
- log.Crashf("Compilation of binary op %v not implemented", x.Op);
+ log.Crashf("Compilation of binary op %v not implemented", op);
+ }
+}
+
+func (a *exprCompiler) DoBinaryExpr(x *ast.BinaryExpr) {
+ l, r := a.copyVisit(x.X), a.copyVisit(x.Y);
+ if l.t == nil || r.t == nil {
+ return;
}
+
+ a.doBinaryExpr(x.Op, l, r);
}
func (a *exprCompiler) DoKeyValueExpr(x *ast.KeyValueExpr) {
log.Crashf("unexpected result type %v at %v", l.t.literal(), a.pos);
}
}
+
+func genAssign(lt Type, r *exprCompiler) (func(lv Value, f *Frame)) {
+ switch _ := lt.literal().(type) {
+ case *boolType:
+ rf := r.asBool();
+ return func(lv Value, f *Frame) { lv.(BoolValue).Set(rf(f)) };
+ case *uintType:
+ rf := r.asUint();
+ return func(lv Value, f *Frame) { lv.(UintValue).Set(rf(f)) };
+ case *intType:
+ rf := r.asInt();
+ return func(lv Value, f *Frame) { lv.(IntValue).Set(rf(f)) };
+ case *floatType:
+ rf := r.asFloat();
+ return func(lv Value, f *Frame) { lv.(FloatValue).Set(rf(f)) };
+ case *stringType:
+ rf := r.asString();
+ return func(lv Value, f *Frame) { lv.(StringValue).Set(rf(f)) };
+ case *ArrayType:
+ rf := r.asArray();
+ return func(lv Value, f *Frame) { lv.Assign(rf(f)) };
+ case *PtrType:
+ rf := r.asPtr();
+ return func(lv Value, f *Frame) { lv.(PtrValue).Set(rf(f)) };
+ default:
+ log.Crashf("unexpected left operand type %v at %v", lt.literal(), r.pos);
+ }
+ panic();
+}
--- /dev/null
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package eval
+
+import (
+ "eval";
+ "fmt";
+ "log";
+ "os";
+ "go/ast";
+ "go/scanner";
+ "go/token";
+)
+
+type stmtCompiler struct {
+ scope *Scope;
+ errors scanner.ErrorHandler;
+ pos token.Position;
+ f func (f *Frame);
+}
+
+func (a *stmtCompiler) diagAt(pos token.Position, format string, args ...) {
+ a.errors.Error(pos, fmt.Sprintf(format, args));
+}
+
+func (a *stmtCompiler) diag(format string, args ...) {
+ a.diagAt(a.pos, format, args);
+}
+
+/*
+ * Statement visitors
+ */
+
+func (a *stmtCompiler) DoBadStmt(s *ast.BadStmt) {
+ // Do nothing. Already reported by parser.
+}
+
+func (a *stmtCompiler) DoDeclStmt(s *ast.DeclStmt) {
+ log.Crash("Not implemented");
+}
+
+func (a *stmtCompiler) DoEmptyStmt(s *ast.EmptyStmt) {
+ log.Crash("Not implemented");
+}
+
+func (a *stmtCompiler) DoLabeledStmt(s *ast.LabeledStmt) {
+ log.Crash("Not implemented");
+}
+
+func (a *stmtCompiler) DoExprStmt(s *ast.ExprStmt) {
+ log.Crash("Not implemented");
+}
+
+func (a *stmtCompiler) DoIncDecStmt(s *ast.IncDecStmt) {
+ log.Crash("Not implemented");
+}
+
+func (a *stmtCompiler) doAssign(s *ast.AssignStmt) {
+ if len(s.Lhs) != len(s.Rhs) {
+ log.Crashf("Unbalanced assignment not implemented %v %v %v", len(s.Lhs), s.Tok, len(s.Rhs));
+ }
+
+ bad := false;
+
+ // Compile right side first so we have the types when
+ // compiling the left side and so we don't see definitions
+ // made on the left side.
+ rs := make([]*exprCompiler, len(s.Rhs));
+ for i, re := range s.Rhs {
+ rs[i] = compileExpr(re, a.scope, a.errors);
+ if rs[i] == nil {
+ bad = true;
+ }
+ }
+
+ // Compile left side and generate assigners
+ ls := make([]*exprCompiler, len(s.Lhs));
+ as := make([]func(lv Value, f *Frame), len(s.Lhs));
+ nDefs := 0;
+ for i, le := range s.Lhs {
+ errPos := i + 1;
+ if len(s.Lhs) == 1 {
+ errPos = 0;
+ }
+
+ if s.Tok == token.DEFINE {
+ // Check that it's an identifier
+ ident, ok := le.(*ast.Ident);
+ if !ok {
+ a.diagAt(le.Pos(), "left side of := must be a name");
+ bad = true;
+ continue;
+ }
+
+ // Is this simply an assignment?
+ if _, ok := a.scope.defs[ident.Value]; ok {
+ goto assignment;
+ }
+
+ if rs[i] == nil {
+ // TODO(austin) Define a placeholder.
+ continue;
+ }
+
+ // Generate assigner and get type
+ var lt Type;
+ lt, as[i] = mkAssign(nil, rs[i], "assignment", errPos, "position");
+ if lt == nil {
+ bad = true;
+ continue;
+ }
+
+ // Define identifier
+ v := a.scope.DefineVar(ident.Value, lt);
+ nDefs++;
+ if v == nil {
+ log.Crashf("Failed to define %s", ident.Value);
+ }
+ }
+
+ assignment:
+ ls[i] = compileExpr(le, a.scope, a.errors);
+ if ls[i] == nil {
+ bad = true;
+ continue;
+ }
+
+ if ls[i].evalAddr == nil {
+ ls[i].diag("cannot assign to %s", ls[i].desc);
+ bad = true;
+ continue;
+ }
+
+ // Generate assigner
+ if as[i] == nil {
+ var lt Type;
+ lt, as[i] = mkAssign(ls[i].t, rs[i], "assignment", errPos, "position");
+ if lt == nil {
+ bad = true;
+ continue;
+ }
+ }
+ }
+
+ if bad {
+ return;
+ }
+
+
+ // A short variable declaration may redeclare variables
+ // provided they were originally declared in the same block
+ // with the same type, and at least one of the variables is
+ // new.
+ if s.Tok == token.DEFINE && nDefs == 0 {
+ a.diag("at least one new variable must be declared");
+ return;
+ }
+
+ n := len(s.Lhs);
+ if n == 1 {
+ lf := ls[0].evalAddr;
+ assign := as[0];
+ a.f = func(f *Frame) { assign(lf(f), f) };
+ } else {
+ a.f = func(f *Frame) {
+ temps := make([]Value, n);
+ // Assign to temporaries
+ for i := 0; i < n; i++ {
+ // TODO(austin) Don't capture ls
+ temps[i] = ls[i].t.Zero();
+ as[i](temps[i], f);
+ }
+ // Copy to destination
+ for i := 0; i < n; i++ {
+ ls[i].evalAddr(f).Assign(temps[i]);
+ }
+ }
+ }
+}
+
+var assignOpToOp = map[token.Token] token.Token {
+ token.ADD_ASSIGN : token.ADD,
+ token.SUB_ASSIGN : token.SUB,
+ token.MUL_ASSIGN : token.MUL,
+ token.QUO_ASSIGN : token.QUO,
+ token.REM_ASSIGN : token.REM,
+
+ token.AND_ASSIGN : token.AND,
+ token.OR_ASSIGN : token.OR,
+ token.XOR_ASSIGN : token.XOR,
+ token.SHL_ASSIGN : token.SHL,
+ token.SHR_ASSIGN : token.SHR,
+ token.AND_NOT_ASSIGN : token.AND_NOT,
+}
+
+func (a *stmtCompiler) doAssignOp(s *ast.AssignStmt) {
+ if len(s.Lhs) != 1 || len(s.Rhs) != 1 {
+ a.diag("tuple assignment cannot be combined with an arithmetic operation");
+ return;
+ }
+
+ l := compileExpr(s.Lhs[0], a.scope, a.errors);
+ r := compileExpr(s.Rhs[0], a.scope, a.errors);
+ if l == nil || r == nil {
+ return;
+ }
+
+ if l.evalAddr == nil {
+ l.diag("cannot assign to %s", l.desc);
+ return;
+ }
+
+ ec := r.copy();
+ ec.pos = s.TokPos;
+ ec.doBinaryExpr(assignOpToOp[s.Tok], l, r);
+ if ec.t == nil {
+ return;
+ }
+
+ lf := l.evalAddr;
+ _, assign := mkAssign(l.t, r, "assignment", 0, "");
+ if assign == nil {
+ return;
+ }
+ a.f = func(f *Frame) { assign(lf(f), f) };
+}
+
+func (a *stmtCompiler) DoAssignStmt(s *ast.AssignStmt) {
+ switch s.Tok {
+ case token.ASSIGN, token.DEFINE:
+ a.doAssign(s);
+
+ default:
+ a.doAssignOp(s);
+ }
+}
+
+func (a *stmtCompiler) DoGoStmt(s *ast.GoStmt) {
+ log.Crash("Not implemented");
+}
+
+func (a *stmtCompiler) DoDeferStmt(s *ast.DeferStmt) {
+ log.Crash("Not implemented");
+}
+
+func (a *stmtCompiler) DoReturnStmt(s *ast.ReturnStmt) {
+ log.Crash("Not implemented");
+}
+
+func (a *stmtCompiler) DoBranchStmt(s *ast.BranchStmt) {
+ log.Crash("Not implemented");
+}
+
+func (a *stmtCompiler) DoBlockStmt(s *ast.BlockStmt) {
+ log.Crash("Not implemented");
+}
+
+func (a *stmtCompiler) DoIfStmt(s *ast.IfStmt) {
+ log.Crash("Not implemented");
+}
+
+func (a *stmtCompiler) DoCaseClause(s *ast.CaseClause) {
+ log.Crash("Not implemented");
+}
+
+func (a *stmtCompiler) DoSwitchStmt(s *ast.SwitchStmt) {
+ log.Crash("Not implemented");
+}
+
+func (a *stmtCompiler) DoTypeCaseClause(s *ast.TypeCaseClause) {
+ log.Crash("Not implemented");
+}
+
+func (a *stmtCompiler) DoTypeSwitchStmt(s *ast.TypeSwitchStmt) {
+ log.Crash("Not implemented");
+}
+
+func (a *stmtCompiler) DoCommClause(s *ast.CommClause) {
+ log.Crash("Not implemented");
+}
+
+func (a *stmtCompiler) DoSelectStmt(s *ast.SelectStmt) {
+ log.Crash("Not implemented");
+}
+
+func (a *stmtCompiler) DoForStmt(s *ast.ForStmt) {
+ log.Crash("Not implemented");
+}
+
+func (a *stmtCompiler) DoRangeStmt(s *ast.RangeStmt) {
+ log.Crash("Not implemented");
+}
+
+/*
+ * Public interface
+ */
+
+type Stmt struct {
+ f func (f *Frame);
+}
+
+func (s *Stmt) Exec(f *Frame) {
+ s.f(f);
+}
+
+func CompileStmt(stmt ast.Stmt, scope *Scope) (*Stmt, os.Error) {
+ errors := scanner.NewErrorVector();
+ sc := &stmtCompiler{scope, errors, stmt.Pos(), nil};
+ stmt.Visit(sc);
+ if sc.f == nil {
+ return nil, errors.GetError(scanner.Sorted);
+ }
+ return &Stmt{sc.f}, nil;
+}