blob: a5f6ac7e5e788aeb0c00180ad3d157593b7429bf [file] [log] [blame]
// 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 is the beginning of an interpreter for Go.
// It can run simple Go programs but does not implement
// interface values or packages.
package eval
import (
"go/ast"
"go/parser"
"go/scanner"
"go/token"
"os"
)
type World struct {
scope *Scope
frame *Frame
}
func NewWorld() *World {
w := new(World)
w.scope = universe.ChildScope()
w.scope.global = true // this block's vars allocate directly
return w
}
type Code interface {
// The type of the value Run returns, or nil if Run returns nil.
Type() Type
// Run runs the code; if the code is a single expression
// with a value, it returns the value; otherwise it returns nil.
Run() (Value, os.Error)
}
type stmtCode struct {
w *World
code code
}
func (w *World) CompileStmtList(fset *token.FileSet, stmts []ast.Stmt) (Code, os.Error) {
if len(stmts) == 1 {
if s, ok := stmts[0].(*ast.ExprStmt); ok {
return w.CompileExpr(fset, s.X)
}
}
errors := new(scanner.ErrorVector)
cc := &compiler{fset, errors, 0, 0}
cb := newCodeBuf()
fc := &funcCompiler{
compiler: cc,
fnType: nil,
outVarsNamed: false,
codeBuf: cb,
flow: newFlowBuf(cb),
labels: make(map[string]*label),
}
bc := &blockCompiler{
funcCompiler: fc,
block: w.scope.block,
}
nerr := cc.numError()
for _, stmt := range stmts {
bc.compileStmt(stmt)
}
fc.checkLabels()
if nerr != cc.numError() {
return nil, errors.GetError(scanner.Sorted)
}
return &stmtCode{w, fc.get()}, nil
}
func (w *World) CompileDeclList(fset *token.FileSet, decls []ast.Decl) (Code, os.Error) {
stmts := make([]ast.Stmt, len(decls))
for i, d := range decls {
stmts[i] = &ast.DeclStmt{d}
}
return w.CompileStmtList(fset, stmts)
}
func (s *stmtCode) Type() Type { return nil }
func (s *stmtCode) Run() (Value, os.Error) {
t := new(Thread)
t.f = s.w.scope.NewFrame(nil)
return nil, t.Try(func(t *Thread) { s.code.exec(t) })
}
type exprCode struct {
w *World
e *expr
eval func(Value, *Thread)
}
func (w *World) CompileExpr(fset *token.FileSet, e ast.Expr) (Code, os.Error) {
errors := new(scanner.ErrorVector)
cc := &compiler{fset, errors, 0, 0}
ec := cc.compileExpr(w.scope.block, false, e)
if ec == nil {
return nil, errors.GetError(scanner.Sorted)
}
var eval func(Value, *Thread)
switch t := ec.t.(type) {
case *idealIntType:
// nothing
case *idealFloatType:
// nothing
default:
if tm, ok := t.(*MultiType); ok && len(tm.Elems) == 0 {
return &stmtCode{w, code{ec.exec}}, nil
}
eval = genAssign(ec.t, ec)
}
return &exprCode{w, ec, eval}, nil
}
func (e *exprCode) Type() Type { return e.e.t }
func (e *exprCode) Run() (Value, os.Error) {
t := new(Thread)
t.f = e.w.scope.NewFrame(nil)
switch e.e.t.(type) {
case *idealIntType:
return &idealIntV{e.e.asIdealInt()()}, nil
case *idealFloatType:
return &idealFloatV{e.e.asIdealFloat()()}, nil
}
v := e.e.t.Zero()
eval := e.eval
err := t.Try(func(t *Thread) { eval(v, t) })
return v, err
}
func (w *World) Compile(fset *token.FileSet, text string) (Code, os.Error) {
stmts, err := parser.ParseStmtList(fset, "input", text)
if err == nil {
return w.CompileStmtList(fset, stmts)
}
// Otherwise try as DeclList.
decls, err1 := parser.ParseDeclList(fset, "input", text)
if err1 == nil {
return w.CompileDeclList(fset, decls)
}
// Have to pick an error.
// Parsing as statement list admits more forms,
// its error is more likely to be useful.
return nil, err
}
type RedefinitionError struct {
Name string
Prev Def
}
func (e *RedefinitionError) String() string {
res := "identifier " + e.Name + " redeclared"
pos := e.Prev.Pos()
if pos.IsValid() {
// TODO: fix this - currently this code is not reached by the tests
// need to get a file set (fset) from somewhere
//res += "; previous declaration at " + fset.Position(pos).String()
panic(0)
}
return res
}
func (w *World) DefineConst(name string, t Type, val Value) os.Error {
_, prev := w.scope.DefineConst(name, token.NoPos, t, val)
if prev != nil {
return &RedefinitionError{name, prev}
}
return nil
}
func (w *World) DefineVar(name string, t Type, val Value) os.Error {
v, prev := w.scope.DefineVar(name, token.NoPos, t)
if prev != nil {
return &RedefinitionError{name, prev}
}
v.Init = val
return nil
}