summaryrefslogtreecommitdiff
path: root/libgo/go/exp/eval/world.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/exp/eval/world.go')
-rw-r--r--libgo/go/exp/eval/world.go188
1 files changed, 188 insertions, 0 deletions
diff --git a/libgo/go/exp/eval/world.go b/libgo/go/exp/eval/world.go
new file mode 100644
index 000000000..02d18bd79
--- /dev/null
+++ b/libgo/go/exp/eval/world.go
@@ -0,0 +1,188 @@
+// 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.
+
+// This package 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
+}