| // 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 | |
| } |