Commit d3888fe8 authored by Austin Clements's avatar Austin Clements

Implement single-valued, non-variadic function literals and

function calling.  Implement a type compiler and named types.
Implement a universal scope containing built-in named types,
and some built-in constants.  Implement a simple virtual
machine for executing statements and single-valued return.

Fix many places that incorrectly dealt with named types.  In
particular, the Type.Zero methods now use the type's bit count
to determine the appropriate value representation.  As a
result, a bit count of 0 now means architecture-dependent and
bounded types use unsafe.Sizeof to determine the correct
bounds.  Previously, the bounds on a 32-bit machine would have
been wrong.

Eliminated Type.compatible, since the implementation is
equivalent for all types.  Added Type.rep that shallowly
strips named types.  Replaced almost all uses of Type.literal
with Type.rep.

Fix implementation of assign-op's so it only evaluates the
left side once.  As part of this, there is now a generic way
to separate out the effect and value of an expression.

R=rsc
APPROVED=rsc
DELTA=1530  (1244 added, 68 deleted, 218 changed)
OCL=32184
CL=32230
parent ca017169
// 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";
"go/ast";
"go/scanner";
"go/token";
)
type positioned interface {
Pos() token.Position;
}
// A compiler captures information used throughout an entire
// compilation. Currently it includes only the error handler.
//
// TODO(austin) This might actually represent package level, in which
// case it should be package compiler.
type compiler struct {
errors scanner.ErrorHandler;
}
func (a *compiler) diagAt(pos positioned, format string, args ...) {
a.errors.Error(pos.Pos(), fmt.Sprintf(format, args));
}
type FuncDecl struct
func (a *compiler) compileFunc(scope *Scope, decl *FuncDecl, body *ast.BlockStmt) (func (f *Frame) Func)
type exprCompiler struct
func (a *compiler) compileExpr(scope *Scope, expr ast.Expr, constant bool) *exprCompiler
func (a *compiler) compileType(scope *Scope, typ ast.Expr) Type
func (a *compiler) compileFuncType(scope *Scope, typ *ast.FuncType) *FuncDecl
func (a *compiler) compileArrayLen(scope *Scope, expr ast.Expr) (int64, bool)
type codeBuf struct
// A funcCompiler captures information used throughout the compilation
// of a single function body.
type funcCompiler struct {
*compiler;
outVars []*Variable;
// Whether the out variables are named. This affects what
// kinds of return statements are legal.
outVarsNamed bool;
*codeBuf;
err bool;
}
// A blockCompiler captures information used throughout the compilation
// of a single block within a function.
type blockCompiler struct {
*funcCompiler;
scope *Scope;
returned bool;
}
func (a *blockCompiler) compileBlock(body *ast.BlockStmt)
// An exprContext stores information used throughout the compilation
// of a single expression. It does not embed funcCompiler because
// expressions can appear at top level.
//
// TODO(austin) Rename exprCompiler to exprNodeCompiler and rename
// this to exprCompiler.
type exprContext struct {
*compiler;
scope *Scope;
constant bool;
}
......@@ -16,12 +16,14 @@ type Value interface
type Type interface {
// literal returns this type with all names recursively
// stripped.
// TODO(austin) Eliminate the need for this
// stripped. This should only be used when determining
// assignment compatibility. To strip a named type for use in
// a type switch, use .rep().
literal() Type;
// compatible returns true if this type is compatible with o.
// XXX Assignment versus comparison compatibility?
compatible(o Type) bool;
// rep returns the representative type. If this is a named
// type, this is the unnamed underlying type. Otherwise, this
// is an identity operation.
rep() Type;
// isBoolean returns true if this is a boolean type.
isBoolean() bool;
// isInteger returns true if this is an integer type.
......@@ -112,6 +114,13 @@ type PtrValue interface {
Set(Value);
}
type Func interface
type FuncValue interface {
Value;
Get() Func;
Set(Func);
}
/*
* Scopes
*/
......@@ -134,17 +143,21 @@ type Def interface {}
type Scope struct {
outer *Scope;
defs map[string] Def;
temps map[int] *Variable;
numVars int;
varTypes []Type;
}
func NewRootScope() *Scope
func (s *Scope) Fork() *Scope
func (s *Scope) DefineVar(name string, t Type) *Variable
func (s *Scope) DefineTemp(t Type) *Variable
func (s *Scope) DefineConst(name string, t Type, v Value) *Constant
func (s *Scope) DefineType(name string, t Type) bool
func (s *Scope) DefineType(name string, t Type) Type
func (s *Scope) Lookup(name string) (Def, *Scope)
// The universal scope
var universe = &Scope{defs: make(map[string] Def), temps: make(map[int] *Variable)};
/*
* Frames
*/
......@@ -156,5 +169,15 @@ type Frame struct {
}
func (f *Frame) Get(s *Scope, index int) Value
func (f *Frame) String() string
func (s *Scope) NewFrame(outer *Frame) *Frame
/*
* Functions
*/
type Func interface {
NewFrame() *Frame;
Call(*Frame);
}
This diff is collapsed.
// 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 (
"container/vector";
"eval";
)
/*
* Virtual machine
*/
type vm struct {
pc uint;
// The current execution frame. If execution is within a
// block, this may be a child of the original function
// activation frame.
f *Frame;
// The original function activation frame. This is used to
// access function out args.
activation *Frame;
}
type code []func(*vm)
func (i code) exec(fr *Frame) {
v := vm{0, fr, fr};
l := uint(len(i));
for v.pc < l {
pc := v.pc;
v.pc++;
i[pc](&v);
}
}
/*
* Code buffer
*/
type codeBuf struct {
instrs code;
}
func newCodeBuf() *codeBuf {
return &codeBuf{make(code, 0, 16)};
}
func (b codeBuf) push(instr func(*vm)) {
n := len(b.instrs);
if n >= cap(b.instrs) {
a := make(code, n, n*2);
for i := range b.instrs {
a[i] = b.instrs[i];
}
b.instrs = a;
}
b.instrs = b.instrs[0:n+1];
b.instrs[n] = instr;
}
func (b codeBuf) get() code {
// Freeze this buffer into an array of exactly the right size
a := make(code, len(b.instrs));
for i := range b.instrs {
a[i] = b.instrs[i];
}
return code(a);
}
/*
* User-defined functions
*/
type evalFunc struct {
sc *Scope;
fr *Frame;
code code;
}
func (f *evalFunc) NewFrame() *Frame {
return f.sc.NewFrame(f.fr);
}
func (f *evalFunc) Call(fr *Frame) {
f.code.exec(fr);
}
......@@ -6,14 +6,15 @@ package eval
import (
"eval";
"fmt";
)
func NewRootScope() *Scope {
return &Scope{defs: make(map[string] Def)};
}
func (s *Scope) Fork() *Scope {
return &Scope{outer: s, defs: make(map[string] Def)};
return &Scope{
outer: s,
defs: make(map[string] Def),
temps: make(map[int] *Variable)
};
}
func (s *Scope) DefineVar(name string, t Type) *Variable {
......@@ -21,8 +22,15 @@ func (s *Scope) DefineVar(name string, t Type) *Variable {
return nil;
}
v := &Variable{s.numVars, t};
s.numVars++;
s.defs[name] = v;
s.numVars++;
return v;
}
func (s *Scope) DefineTemp(t Type) *Variable {
v := &Variable{s.numVars, t};
s.temps[s.numVars] = v;
s.numVars++;
return v;
}
......@@ -35,12 +43,15 @@ func (s *Scope) DefineConst(name string, t Type, v Value) *Constant {
return c;
}
func (s *Scope) DefineType(name string, t Type) bool {
func (s *Scope) DefineType(name string, t Type) Type {
if _, ok := s.defs[name]; ok {
return false;
return nil;
}
s.defs[name] = t;
return true;
// We take the representative type of t because multiple
// levels of naming are useless.
nt := &NamedType{s, name, t.rep()};
s.defs[name] = nt;
return nt;
}
func (s *Scope) Lookup(name string) (Def, *Scope) {
......@@ -60,13 +71,23 @@ func (s *Scope) NewFrame(outer *Frame) *Frame {
ts := make([]Type, s.numVars);
for _, d := range s.defs {
if v, ok := d.(*Variable); ok {
ts[v.Index] = v.Type;
// Record the representative type to
// avoid indirecting through named
// types every time we drop a frame.
ts[v.Index] = v.Type.rep();
}
}
for _, v := range s.temps {
ts[v.Index] = v.Type.rep();
}
s.varTypes = ts;
}
// Create frame
//
// TODO(austin) This is probably rather expensive. All values
// require heap allocation and the Zero method typically
// requires some computation.
vars := make([]Value, s.numVars);
for i, t := range s.varTypes {
vars[i] = t.Zero();
......@@ -80,3 +101,36 @@ func (f *Frame) Get(s *Scope, index int) Value {
}
return f.Vars[index];
}
func stringFrame(f *Frame) (string, string) {
res := "";
indent := "";
if f.Outer != nil {
res, indent = stringFrame(f.Outer);
}
names := make([]string, f.Scope.numVars);
types := make([]Type, f.Scope.numVars);
for name, def := range f.Scope.defs {
def, ok := def.(*Variable);
if !ok {
continue;
}
names[def.Index] = name;
types[def.Index] = def.Type;
}
for _, def := range f.Scope.temps {
names[def.Index] = "(temp)";
types[def.Index] = def.Type;
}
for i, val := range f.Vars {
res += fmt.Sprintf("%s%-10s %-10s %s\n", indent, names[i], types[i], val);
}
return res, indent + " ";
}
func (f *Frame) String() string {
res, _ := stringFrame(f);
return res;
}
This diff is collapsed.
This diff is collapsed.
// 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";
"go/ast";
"log";
)
/*
* Type compiler
*/
// TODO(austin) Without this, I get a "conflicting definitions for
// eval.compiler" when gopack'ing typec.6 from gobuild.
// Interestingly, if I create the Makefile with this line, then
// comment it out and use the Makefile, things work.
type exprCompiler struct
type typeCompiler struct {
*compiler;
scope *Scope;
}
func (a *typeCompiler) compileType(x ast.Expr) Type
func (a *typeCompiler) compileIdent(x *ast.Ident) Type {
def, dscope := a.scope.Lookup(x.Value);
if def == nil {
a.diagAt(x, "%s: undefined", x.Value);
return nil;
}
switch def := def.(type) {
case *Constant:
a.diagAt(x, "constant %v used as type", x.Value);
return nil;
case *Variable:
a.diagAt(x, "variable %v used as type", x.Value);
return nil;
case Type:
return def;
}
log.Crashf("name %s has unknown type %T", x.Value, def);
return nil;
}
func (a *typeCompiler) compileArrayType(x *ast.ArrayType) *ArrayType {
// Compile length expression
if x.Len == nil {
a.diagAt(x, "slice types not implemented");
return nil;
}
if _, ok := x.Len.(*ast.Ellipsis); ok {
a.diagAt(x.Len, "... array initailizers not implemented");
return nil;
}
l, ok := a.compileArrayLen(a.scope, x.Len);
// Compile element type
elem := a.compileType(x.Elt);
if !ok {
return nil;
}
if l < 0 {
a.diagAt(x.Len, "array length must be non-negative");
return nil;
}
if elem == nil {
return nil;
}
return NewArrayType(l, elem);
}
func (a *typeCompiler) compilePtrType(x *ast.StarExpr) *PtrType {
elem := a.compileType(x.X);
if elem == nil {
return nil;
}
return NewPtrType(elem);
}
func countFields(fs []*ast.Field) int {
n := 0;
for _, f := range fs {
if f.Names == nil {
n++;
} else {
n += len(f.Names);
}
}
return n;
}
func (a *typeCompiler) compileFields(fs []*ast.Field) ([]Type, []*ast.Ident) {
n := countFields(fs);
ts := make([]Type, n);
ns := make([]*ast.Ident, n);
bad := false;
i := 0;
for fi, f := range fs {
t := a.compileType(f.Type);
if t == nil {
bad = true;
}
if f.Names == nil {
// TODO(austin) In a struct, this has an
// implicit name. However, this also triggers
// for function return values, which should
// not be given names.
ns[i] = nil;
ts[i] = t;
i++;
continue;
}
for _, n := range f.Names {
ns[i] = n;
ts[i] = t;
i++;
}
}
if bad {
return nil, nil;
}
return ts, ns;
}
func (a *typeCompiler) compileFuncType(x *ast.FuncType) *FuncDecl {
// TODO(austin) Variadic function types
bad := false;
in, inNames := a.compileFields(x.Params);
out, outNames := a.compileFields(x.Results);
if in == nil || out == nil {
return nil;
}
return &FuncDecl{NewFuncType(in, false, out), nil, inNames, outNames};
}
func (a *typeCompiler) compileType(x ast.Expr) Type {
switch x := x.(type) {
case *ast.Ident:
return a.compileIdent(x);
case *ast.ArrayType:
return a.compileArrayType(x);
case *ast.StructType:
goto notimpl;
case *ast.StarExpr:
return a.compilePtrType(x);
case *ast.FuncType:
return a.compileFuncType(x).Type;
case *ast.InterfaceType:
goto notimpl;
case *ast.MapType:
goto notimpl;
case *ast.ChanType:
goto notimpl;
case *ast.ParenExpr:
return a.compileType(x.X);
case *ast.Ellipsis:
a.diagAt(x, "illegal use of ellipsis");
return nil;
}
a.diagAt(x, "expression used as type");
return nil;
notimpl:
a.diagAt(x, "compileType: %T not implemented", x);
return nil;
}
/*
* Type compiler interface
*/
func (a *compiler) compileType(scope *Scope, typ ast.Expr) Type {
tc := &typeCompiler{a, scope};
return tc.compileType(typ);
}
func (a *compiler) compileFuncType(scope *Scope, typ *ast.FuncType) *FuncDecl {
tc := &typeCompiler{a, scope};
return tc.compileFuncType(typ);
}
......@@ -150,30 +150,29 @@ func (v *uintptrV) Set(x uint64) {
}
func (t *uintType) Zero() Value {
// TODO(austin) t may be a named type instead of one of the
// base types.
switch Type(t) {
case Uint8Type:
switch t.Bits {
case 0:
if t.Ptr {
res := uintptrV(0);
return &res;
} else {
res := uintV(0);
return &res;
}
case 8:
res := uint8V(0);
return &res;
case Uint16Type:
case 16:
res := uint16V(0);
return &res;
case Uint32Type:
case 32:
res := uint32V(0);
return &res;
case Uint64Type:
case 64:
res := uint64V(0);
return &res;
case UintType:
res := uintV(0);
return &res;
case UintptrType:
res := uintptrV(0);
return &res;
}
panic("unknown uint type ", t.String());
panic("unexpected uint bit count: ", t.Bits);
}
/*
......@@ -271,25 +270,25 @@ func (v *intV) Set(x int64) {
}
func (t *intType) Zero() Value {
switch Type(t) {
case Int8Type:
switch t.Bits {
case 8:
res := int8V(0);
return &res;
case Int16Type:
case 16:
res := int16V(0);
return &res;
case Int32Type:
case 32:
res := int32V(0);
return &res;
case Int64Type:
case 64:
res := int64V(0);
return &res;
case IntType:
case 0:
res := intV(0);
return &res;
}
panic("unknown int type ", t.String());
panic("unexpected int bit count: ", t.Bits);
}
/*
......@@ -375,18 +374,18 @@ func (v *floatV) Set(x float64) {
}
func (t *floatType) Zero() Value {
switch Type(t) {
case Float32Type:
switch t.Bits {
case 32:
res := float32V(0);
return &res;
case Float64Type:
case 64:
res := float64V(0);
return &res;
case FloatType:
case 0:
res := floatV(0);
return &res;
}
panic("unknown float type ", t.String());
panic("unexpected float bit count: ", t.Bits);
}
/*
......@@ -505,6 +504,49 @@ func (v *ptrV) Set(x Value) {
}
func (t *PtrType) Zero() Value {
res := ptrV{nil};
return &res;
return &ptrV{nil};
}
/*
* Functions
*/
type funcV struct {
target Func;
}
func (v *funcV) String() string {
// TODO(austin) Rob wants to see the definition
return "func {...}";
}
func (v *funcV) Assign(o Value) {
v.target = o.(FuncValue).Get();
}
func (v *funcV) Get() Func {
return v.target;
}
func (v *funcV) Set(x Func) {
v.target = x;
}
func (t *FuncType) Zero() Value {
return &funcV{nil};
}
/*
* Universal constants
*/
// TODO(austin) Nothing complains if I accidentally define init with
// arguments. Is this intentional?
func init() {
s := universe;
true := boolV(true);
s.DefineConst("true", BoolType, &true);
false := boolV(false);
s.DefineConst("false", BoolType, &false);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment