Fix lint issues (#2)

* addressing golint issues

* fix all lint issues.
This commit is contained in:
Daniel Kang 2019-01-14 22:24:33 -08:00 committed by GitHub
parent 1c92fff07d
commit b79fd4f7ef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
90 changed files with 495 additions and 137 deletions

View file

@ -13,6 +13,7 @@ import (
"github.com/d5/tengo/objects"
)
// NoError asserts err is not an error.
func NoError(t *testing.T, err error, msg ...interface{}) bool {
t.Helper()
@ -23,6 +24,7 @@ func NoError(t *testing.T, err error, msg ...interface{}) bool {
return failExpectedActual(t, "no error", err, msg...)
}
// Error asserts err is an error.
func Error(t *testing.T, err error, msg ...interface{}) bool {
t.Helper()
@ -33,6 +35,7 @@ func Error(t *testing.T, err error, msg ...interface{}) bool {
return failExpectedActual(t, "error", err, msg...)
}
// Nil asserts v is nil.
func Nil(t *testing.T, v interface{}, msg ...interface{}) bool {
t.Helper()
@ -43,6 +46,7 @@ func Nil(t *testing.T, v interface{}, msg ...interface{}) bool {
return failExpectedActual(t, "nil", v, msg...)
}
// True asserts v is true.
func True(t *testing.T, v bool, msg ...interface{}) bool {
t.Helper()
@ -53,6 +57,7 @@ func True(t *testing.T, v bool, msg ...interface{}) bool {
return failExpectedActual(t, "true", v, msg...)
}
// False asserts vis false.
func False(t *testing.T, v bool, msg ...interface{}) bool {
t.Helper()
@ -63,6 +68,7 @@ func False(t *testing.T, v bool, msg ...interface{}) bool {
return failExpectedActual(t, "false", v, msg...)
}
// NotNil asserts v is not nil.
func NotNil(t *testing.T, v interface{}, msg ...interface{}) bool {
t.Helper()
@ -73,6 +79,7 @@ func NotNil(t *testing.T, v interface{}, msg ...interface{}) bool {
return failExpectedActual(t, "not nil", v, msg...)
}
// IsType asserts expected and actual are of the same type.
func IsType(t *testing.T, expected, actual interface{}, msg ...interface{}) bool {
t.Helper()
@ -83,6 +90,7 @@ func IsType(t *testing.T, expected, actual interface{}, msg ...interface{}) bool
return failExpectedActual(t, reflect.TypeOf(expected), reflect.TypeOf(actual), msg...)
}
// Equal asserts expected and actual are equal.
func Equal(t *testing.T, expected, actual interface{}, msg ...interface{}) bool {
t.Helper()
@ -172,6 +180,7 @@ func Equal(t *testing.T, expected, actual interface{}, msg ...interface{}) bool
return true
}
// Fail marks the function as having failed but continues execution.
func Fail(t *testing.T, msg ...interface{}) bool {
t.Helper()

View file

@ -66,7 +66,7 @@ func isTest(name, prefix string) bool {
return true
}
rune_, _ := utf8.DecodeRuneInString(name[len(prefix):])
r, _ := utf8.DecodeRuneInString(name[len(prefix):])
return !unicode.IsLower(rune_)
return !unicode.IsLower(r)
}

View file

@ -248,7 +248,7 @@ func addPrints(file *ast.File) *ast.File {
Func: &ast.Ident{
Name: "print",
},
Args: s.Lhs,
Args: s.LHS,
},
})

View file

@ -6,6 +6,7 @@ import (
"github.com/d5/tengo/compiler/source"
)
// ArrayLit represents an array literal.
type ArrayLit struct {
Elements []Expr
LBrack source.Pos
@ -14,10 +15,12 @@ type ArrayLit struct {
func (e *ArrayLit) exprNode() {}
// Pos returns the position of first character belonging to the node.
func (e *ArrayLit) Pos() source.Pos {
return e.LBrack
}
// End returns the position of first character immediately after the node.
func (e *ArrayLit) End() source.Pos {
return e.RBrack + 1
}

View file

@ -7,29 +7,32 @@ import (
"github.com/d5/tengo/compiler/token"
)
// AssignStmt represents an assignment statement.
type AssignStmt struct {
Lhs []Expr
Rhs []Expr
LHS []Expr
RHS []Expr
Token token.Token
TokenPos source.Pos
}
func (s *AssignStmt) stmtNode() {}
// Pos returns the position of first character belonging to the node.
func (s *AssignStmt) Pos() source.Pos {
return s.Lhs[0].Pos()
return s.LHS[0].Pos()
}
// End returns the position of first character immediately after the node.
func (s *AssignStmt) End() source.Pos {
return s.Rhs[len(s.Rhs)-1].End()
return s.RHS[len(s.RHS)-1].End()
}
func (s *AssignStmt) String() string {
var lhs, rhs []string
for _, e := range s.Lhs {
for _, e := range s.LHS {
lhs = append(lhs, e.String())
}
for _, e := range s.Rhs {
for _, e := range s.RHS {
rhs = append(rhs, e.String())
}

View file

@ -2,6 +2,7 @@ package ast
import "github.com/d5/tengo/compiler/source"
// BadExpr represents a bad expression.
type BadExpr struct {
From source.Pos
To source.Pos
@ -9,10 +10,12 @@ type BadExpr struct {
func (e *BadExpr) exprNode() {}
// Pos returns the position of first character belonging to the node.
func (e *BadExpr) Pos() source.Pos {
return e.From
}
// End returns the position of first character immediately after the node.
func (e *BadExpr) End() source.Pos {
return e.To
}

View file

@ -2,6 +2,7 @@ package ast
import "github.com/d5/tengo/compiler/source"
// BadStmt represents a bad statement.
type BadStmt struct {
From source.Pos
To source.Pos
@ -9,10 +10,12 @@ type BadStmt struct {
func (s *BadStmt) stmtNode() {}
// Pos returns the position of first character belonging to the node.
func (s *BadStmt) Pos() source.Pos {
return s.From
}
// End returns the position of first character immediately after the node.
func (s *BadStmt) End() source.Pos {
return s.To
}

View file

@ -5,23 +5,26 @@ import (
"github.com/d5/tengo/compiler/token"
)
// BinaryExpr represents a binary operator expression.
type BinaryExpr struct {
Lhs Expr
Rhs Expr
LHS Expr
RHS Expr
Token token.Token
TokenPos source.Pos
}
func (e *BinaryExpr) exprNode() {}
// Pos returns the position of first character belonging to the node.
func (e *BinaryExpr) Pos() source.Pos {
return e.Lhs.Pos()
return e.LHS.Pos()
}
// End returns the position of first character immediately after the node.
func (e *BinaryExpr) End() source.Pos {
return e.Rhs.End()
return e.RHS.End()
}
func (e *BinaryExpr) String() string {
return "(" + e.Lhs.String() + " " + e.Token.String() + " " + e.Rhs.String() + ")"
return "(" + e.LHS.String() + " " + e.Token.String() + " " + e.RHS.String() + ")"
}

View file

@ -6,6 +6,7 @@ import (
"github.com/d5/tengo/compiler/source"
)
// BlockStmt represents a block statement.
type BlockStmt struct {
Stmts []Stmt
LBrace source.Pos
@ -14,10 +15,12 @@ type BlockStmt struct {
func (s *BlockStmt) stmtNode() {}
// Pos returns the position of first character belonging to the node.
func (s *BlockStmt) Pos() source.Pos {
return s.LBrace
}
// End returns the position of first character immediately after the node.
func (s *BlockStmt) End() source.Pos {
return s.RBrace + 1
}

View file

@ -2,6 +2,7 @@ package ast
import "github.com/d5/tengo/compiler/source"
// BoolLit represetns a boolean literal.
type BoolLit struct {
Value bool
ValuePos source.Pos
@ -10,10 +11,12 @@ type BoolLit struct {
func (e *BoolLit) exprNode() {}
// Pos returns the position of first character belonging to the node.
func (e *BoolLit) Pos() source.Pos {
return e.ValuePos
}
// End returns the position of first character immediately after the node.
func (e *BoolLit) End() source.Pos {
return source.Pos(int(e.ValuePos) + len(e.Literal))
}

View file

@ -5,6 +5,7 @@ import (
"github.com/d5/tengo/compiler/token"
)
// BranchStmt represents a branch statement.
type BranchStmt struct {
Token token.Token
TokenPos source.Pos
@ -13,10 +14,12 @@ type BranchStmt struct {
func (s *BranchStmt) stmtNode() {}
// Pos returns the position of first character belonging to the node.
func (s *BranchStmt) Pos() source.Pos {
return s.TokenPos
}
// End returns the position of first character immediately after the node.
func (s *BranchStmt) End() source.Pos {
if s.Label != nil {
return s.Label.End()

View file

@ -6,6 +6,7 @@ import (
"github.com/d5/tengo/compiler/source"
)
// CallExpr represents a function call expression.
type CallExpr struct {
Func Expr
LParen source.Pos
@ -15,10 +16,12 @@ type CallExpr struct {
func (e *CallExpr) exprNode() {}
// Pos returns the position of first character belonging to the node.
func (e *CallExpr) Pos() source.Pos {
return e.Func.Pos()
}
// End returns the position of first character immediately after the node.
func (e *CallExpr) End() source.Pos {
return e.RParen + 1
}

View file

@ -2,6 +2,7 @@ package ast
import "github.com/d5/tengo/compiler/source"
// CharLit represents a character literal.
type CharLit struct {
Value rune
ValuePos source.Pos
@ -10,10 +11,12 @@ type CharLit struct {
func (e *CharLit) exprNode() {}
// Pos returns the position of first character belonging to the node.
func (e *CharLit) Pos() source.Pos {
return e.ValuePos
}
// End returns the position of first character immediately after the node.
func (e *CharLit) End() source.Pos {
return source.Pos(int(e.ValuePos) + len(e.Literal))
}

View file

@ -2,6 +2,7 @@ package ast
import "github.com/d5/tengo/compiler/source"
// EmptyStmt represents an empty statement.
type EmptyStmt struct {
Semicolon source.Pos
Implicit bool
@ -9,10 +10,12 @@ type EmptyStmt struct {
func (s *EmptyStmt) stmtNode() {}
// Pos returns the position of first character belonging to the node.
func (s *EmptyStmt) Pos() source.Pos {
return s.Semicolon
}
// End returns the position of first character immediately after the node.
func (s *EmptyStmt) End() source.Pos {
if s.Implicit {
return s.Semicolon

View file

@ -1,5 +1,6 @@
package ast
// Expr represents an expression node in the AST.
type Expr interface {
Node
exprNode()

View file

@ -2,16 +2,19 @@ package ast
import "github.com/d5/tengo/compiler/source"
// ExprStmt represents an expression statement.
type ExprStmt struct {
Expr Expr
}
func (s *ExprStmt) stmtNode() {}
// Pos returns the position of first character belonging to the node.
func (s *ExprStmt) Pos() source.Pos {
return s.Expr.Pos()
}
// End returns the position of first character immediately after the node.
func (s *ExprStmt) End() source.Pos {
return s.Expr.End()
}

View file

@ -6,15 +6,18 @@ import (
"github.com/d5/tengo/compiler/source"
)
// File represents a file unit.
type File struct {
InputFile *source.File
Stmts []Stmt
}
// Pos returns the position of first character belonging to the node.
func (n *File) Pos() source.Pos {
return source.Pos(n.InputFile.Base())
}
// End returns the position of first character immediately after the node.
func (n *File) End() source.Pos {
return source.Pos(n.InputFile.Base() + n.InputFile.Size())
}

View file

@ -2,6 +2,7 @@ package ast
import "github.com/d5/tengo/compiler/source"
// FloatLit represents a floating point literal.
type FloatLit struct {
Value float64
ValuePos source.Pos
@ -10,10 +11,12 @@ type FloatLit struct {
func (e *FloatLit) exprNode() {}
// Pos returns the position of first character belonging to the node.
func (e *FloatLit) Pos() source.Pos {
return e.ValuePos
}
// End returns the position of first character immediately after the node.
func (e *FloatLit) End() source.Pos {
return source.Pos(int(e.ValuePos) + len(e.Literal))
}

View file

@ -2,6 +2,7 @@ package ast
import "github.com/d5/tengo/compiler/source"
// ForInStmt represents a for-in statement.
type ForInStmt struct {
ForPos source.Pos
Key *Ident
@ -12,10 +13,12 @@ type ForInStmt struct {
func (s *ForInStmt) stmtNode() {}
// Pos returns the position of first character belonging to the node.
func (s *ForInStmt) Pos() source.Pos {
return s.ForPos
}
// End returns the position of first character immediately after the node.
func (s *ForInStmt) End() source.Pos {
return s.Body.End()
}

View file

@ -2,6 +2,7 @@ package ast
import "github.com/d5/tengo/compiler/source"
// ForStmt represetns a for statement.
type ForStmt struct {
ForPos source.Pos
Init Stmt
@ -12,10 +13,12 @@ type ForStmt struct {
func (s *ForStmt) stmtNode() {}
// Pos returns the position of first character belonging to the node.
func (s *ForStmt) Pos() source.Pos {
return s.ForPos
}
// End returns the position of first character immediately after the node.
func (s *ForStmt) End() source.Pos {
return s.Body.End()
}

View file

@ -2,6 +2,7 @@ package ast
import "github.com/d5/tengo/compiler/source"
// FuncLit represents a function literal.
type FuncLit struct {
Type *FuncType
Body *BlockStmt
@ -9,10 +10,12 @@ type FuncLit struct {
func (e *FuncLit) exprNode() {}
// Pos returns the position of first character belonging to the node.
func (e *FuncLit) Pos() source.Pos {
return e.Type.Pos()
}
// End returns the position of first character immediately after the node.
func (e *FuncLit) End() source.Pos {
return e.Body.End()
}

View file

@ -2,6 +2,7 @@ package ast
import "github.com/d5/tengo/compiler/source"
// FuncType represetns a function type definition.
type FuncType struct {
FuncPos source.Pos
Params *IdentList
@ -9,10 +10,12 @@ type FuncType struct {
func (e *FuncType) exprNode() {}
// Pos returns the position of first character belonging to the node.
func (e *FuncType) Pos() source.Pos {
return e.FuncPos
}
// End returns the position of first character immediately after the node.
func (e *FuncType) End() source.Pos {
return e.Params.End()
}

View file

@ -2,6 +2,7 @@ package ast
import "github.com/d5/tengo/compiler/source"
// Ident represetns an identifier.
type Ident struct {
Name string
NamePos source.Pos
@ -9,10 +10,12 @@ type Ident struct {
func (e *Ident) exprNode() {}
// Pos returns the position of first character belonging to the node.
func (e *Ident) Pos() source.Pos {
return e.NamePos
}
// End returns the position of first character immediately after the node.
func (e *Ident) End() source.Pos {
return source.Pos(int(e.NamePos) + len(e.Name))
}

View file

@ -6,12 +6,14 @@ import (
"github.com/d5/tengo/compiler/source"
)
// IdentList represetns a list of identifiers.
type IdentList struct {
LParen source.Pos
List []*Ident
RParen source.Pos
}
// Pos returns the position of first character belonging to the node.
func (n *IdentList) Pos() source.Pos {
if n.LParen.IsValid() {
return n.LParen
@ -24,6 +26,7 @@ func (n *IdentList) Pos() source.Pos {
return source.NoPos
}
// End returns the position of first character immediately after the node.
func (n *IdentList) End() source.Pos {
if n.RParen.IsValid() {
return n.RParen + 1
@ -36,6 +39,7 @@ func (n *IdentList) End() source.Pos {
return source.NoPos
}
// NumFields returns the number of fields.
func (n *IdentList) NumFields() int {
if n == nil {
return 0

View file

@ -2,6 +2,7 @@ package ast
import "github.com/d5/tengo/compiler/source"
// IfStmt represents an if statement.
type IfStmt struct {
IfPos source.Pos
Init Stmt
@ -12,10 +13,12 @@ type IfStmt struct {
func (s *IfStmt) stmtNode() {}
// Pos returns the position of first character belonging to the node.
func (s *IfStmt) Pos() source.Pos {
return s.IfPos
}
// End returns the position of first character immediately after the node.
func (s *IfStmt) End() source.Pos {
if s.Else != nil {
return s.Else.End()

View file

@ -5,6 +5,7 @@ import (
"github.com/d5/tengo/compiler/token"
)
// IncDecStmt represents increment or decrement statement.
type IncDecStmt struct {
Expr Expr
Token token.Token
@ -13,10 +14,12 @@ type IncDecStmt struct {
func (s *IncDecStmt) stmtNode() {}
// Pos returns the position of first character belonging to the node.
func (s *IncDecStmt) Pos() source.Pos {
return s.Expr.Pos()
}
// End returns the position of first character immediately after the node.
func (s *IncDecStmt) End() source.Pos {
return source.Pos(int(s.TokenPos) + 2)
}

View file

@ -2,6 +2,7 @@ package ast
import "github.com/d5/tengo/compiler/source"
// IndexExpr represents an index expression.
type IndexExpr struct {
Expr Expr
LBrack source.Pos
@ -11,10 +12,12 @@ type IndexExpr struct {
func (e *IndexExpr) exprNode() {}
// Pos returns the position of first character belonging to the node.
func (e *IndexExpr) Pos() source.Pos {
return e.Expr.Pos()
}
// End returns the position of first character immediately after the node.
func (e *IndexExpr) End() source.Pos {
return e.RBrack + 1
}

View file

@ -2,6 +2,7 @@ package ast
import "github.com/d5/tengo/compiler/source"
// IntLit represetns an integer literal.
type IntLit struct {
Value int64
ValuePos source.Pos
@ -10,10 +11,12 @@ type IntLit struct {
func (e *IntLit) exprNode() {}
// Pos returns the position of first character belonging to the node.
func (e *IntLit) Pos() source.Pos {
return e.ValuePos
}
// End returns the position of first character immediately after the node.
func (e *IntLit) End() source.Pos {
return source.Pos(int(e.ValuePos) + len(e.Literal))
}

View file

@ -2,6 +2,7 @@ package ast
import "github.com/d5/tengo/compiler/source"
// MapElementLit represents a map element.
type MapElementLit struct {
Key string
KeyPos source.Pos
@ -11,10 +12,12 @@ type MapElementLit struct {
func (e *MapElementLit) exprNode() {}
// Pos returns the position of first character belonging to the node.
func (e *MapElementLit) Pos() source.Pos {
return e.KeyPos
}
// End returns the position of first character immediately after the node.
func (e *MapElementLit) End() source.Pos {
return e.Value.End()
}

View file

@ -6,6 +6,7 @@ import (
"github.com/d5/tengo/compiler/source"
)
// MapLit represents a map literal.
type MapLit struct {
LBrace source.Pos
Elements []*MapElementLit
@ -14,10 +15,12 @@ type MapLit struct {
func (e *MapLit) exprNode() {}
// Pos returns the position of first character belonging to the node.
func (e *MapLit) Pos() source.Pos {
return e.LBrace
}
// End returns the position of first character immediately after the node.
func (e *MapLit) End() source.Pos {
return e.RBrace + 1
}

View file

@ -2,8 +2,12 @@ package ast
import "github.com/d5/tengo/compiler/source"
// Node represents a node in the AST.
type Node interface {
// Pos returns the position of first character belonging to the node.
Pos() source.Pos
// End returns the position of first character immediately after the node.
End() source.Pos
// String returns a string representation of the node.
String() string
}

View file

@ -2,6 +2,7 @@ package ast
import "github.com/d5/tengo/compiler/source"
// ParenExpr represents a parenthesis wrapped expression.
type ParenExpr struct {
Expr Expr
LParen source.Pos
@ -10,10 +11,12 @@ type ParenExpr struct {
func (e *ParenExpr) exprNode() {}
// Pos returns the position of first character belonging to the node.
func (e *ParenExpr) Pos() source.Pos {
return e.LParen
}
// End returns the position of first character immediately after the node.
func (e *ParenExpr) End() source.Pos {
return e.RParen + 1
}

View file

@ -6,6 +6,7 @@ import (
"github.com/d5/tengo/compiler/source"
)
// ReturnStmt represents a return statement.
type ReturnStmt struct {
ReturnPos source.Pos
Results []Expr
@ -13,10 +14,12 @@ type ReturnStmt struct {
func (s *ReturnStmt) stmtNode() {}
// Pos returns the position of first character belonging to the node.
func (s *ReturnStmt) Pos() source.Pos {
return s.ReturnPos
}
// End returns the position of first character immediately after the node.
func (s *ReturnStmt) End() source.Pos {
if n := len(s.Results); n > 0 {
return s.Results[n-1].End()

View file

@ -2,6 +2,7 @@ package ast
import "github.com/d5/tengo/compiler/source"
// SelectorExpr represents a selector expression.
type SelectorExpr struct {
Expr Expr
Sel Expr
@ -9,10 +10,12 @@ type SelectorExpr struct {
func (e *SelectorExpr) exprNode() {}
// Pos returns the position of first character belonging to the node.
func (e *SelectorExpr) Pos() source.Pos {
return e.Expr.Pos()
}
// End returns the position of first character immediately after the node.
func (e *SelectorExpr) End() source.Pos {
return e.Sel.End()
}

View file

@ -2,6 +2,7 @@ package ast
import "github.com/d5/tengo/compiler/source"
// SliceExpr represents a slice expression.
type SliceExpr struct {
Expr Expr
LBrack source.Pos
@ -12,10 +13,12 @@ type SliceExpr struct {
func (e *SliceExpr) exprNode() {}
// Pos returns the position of first character belonging to the node.
func (e *SliceExpr) Pos() source.Pos {
return e.Expr.Pos()
}
// End returns the position of first character immediately after the node.
func (e *SliceExpr) End() source.Pos {
return e.RBrack + 1
}

View file

@ -1,5 +1,6 @@
package ast
// Stmt represents a statement in the AST.
type Stmt interface {
Node
stmtNode()

View file

@ -2,6 +2,7 @@ package ast
import "github.com/d5/tengo/compiler/source"
// StringLit represents a string literal.
type StringLit struct {
Value string
ValuePos source.Pos
@ -10,10 +11,12 @@ type StringLit struct {
func (e *StringLit) exprNode() {}
// Pos returns the position of first character belonging to the node.
func (e *StringLit) Pos() source.Pos {
return e.ValuePos
}
// End returns the position of first character immediately after the node.
func (e *StringLit) End() source.Pos {
return source.Pos(int(e.ValuePos) + len(e.Literal))
}

View file

@ -5,6 +5,7 @@ import (
"github.com/d5/tengo/compiler/token"
)
// UnaryExpr represents an unary operator expression.
type UnaryExpr struct {
Expr Expr
Token token.Token
@ -13,10 +14,12 @@ type UnaryExpr struct {
func (e *UnaryExpr) exprNode() {}
// Pos returns the position of first character belonging to the node.
func (e *UnaryExpr) Pos() source.Pos {
return e.Expr.Pos()
}
// End returns the position of first character immediately after the node.
func (e *UnaryExpr) End() source.Pos {
return e.Expr.End()
}

View file

@ -2,16 +2,19 @@ package ast
import "github.com/d5/tengo/compiler/source"
// UndefinedLit represents an undefined literal.
type UndefinedLit struct {
TokenPos source.Pos
}
func (e *UndefinedLit) exprNode() {}
// Pos returns the position of first character belonging to the node.
func (e *UndefinedLit) Pos() source.Pos {
return e.TokenPos
}
// End returns the position of first character immediately after the node.
func (e *UndefinedLit) End() source.Pos {
return e.TokenPos + 9 // len(undefined) == 9
}

View file

@ -7,11 +7,13 @@ import (
"github.com/d5/tengo/objects"
)
// Bytecode is a compiled instructions and constants.
type Bytecode struct {
Instructions []byte
Constants []objects.Object
}
// Decode reads Bytecode data from the reader.
func (b *Bytecode) Decode(r io.Reader) error {
dec := gob.NewDecoder(r)
@ -26,6 +28,7 @@ func (b *Bytecode) Decode(r io.Reader) error {
return nil
}
// Encode writes Bytecode data to the writer.
func (b *Bytecode) Encode(w io.Writer) error {
enc := gob.NewEncoder(w)

View file

@ -1,5 +1,7 @@
package compiler
// CompilationScope represents a compiled instructions
// and the last two instructions that were emitted.
type CompilationScope struct {
instructions []byte
lastInstructions [2]EmittedInstruction

View file

@ -10,6 +10,7 @@ import (
"github.com/d5/tengo/objects"
)
// Compiler compiles the AST into a bytecode.
type Compiler struct {
constants []objects.Object
symbolTable *SymbolTable
@ -21,6 +22,7 @@ type Compiler struct {
indent int
}
// NewCompiler creates a Compiler.
func NewCompiler(symbolTable *SymbolTable, trace io.Writer) *Compiler {
mainScope := CompilationScope{
instructions: make([]byte, 0),
@ -43,6 +45,7 @@ func NewCompiler(symbolTable *SymbolTable, trace io.Writer) *Compiler {
}
}
// Compile compiles the AST node.
func (c *Compiler) Compile(node ast.Node) error {
if c.trace != nil {
if node != nil {
@ -85,11 +88,11 @@ func (c *Compiler) Compile(node ast.Node) error {
}
if node.Token == token.Less {
if err := c.Compile(node.Rhs); err != nil {
if err := c.Compile(node.RHS); err != nil {
return err
}
if err := c.Compile(node.Lhs); err != nil {
if err := c.Compile(node.LHS); err != nil {
return err
}
@ -97,10 +100,10 @@ func (c *Compiler) Compile(node ast.Node) error {
return nil
} else if node.Token == token.LessEq {
if err := c.Compile(node.Rhs); err != nil {
if err := c.Compile(node.RHS); err != nil {
return err
}
if err := c.Compile(node.Lhs); err != nil {
if err := c.Compile(node.LHS); err != nil {
return err
}
@ -109,10 +112,10 @@ func (c *Compiler) Compile(node ast.Node) error {
return nil
}
if err := c.Compile(node.Lhs); err != nil {
if err := c.Compile(node.LHS); err != nil {
return err
}
if err := c.Compile(node.Rhs); err != nil {
if err := c.Compile(node.RHS); err != nil {
return err
}
@ -258,7 +261,7 @@ func (c *Compiler) Compile(node ast.Node) error {
}
case *ast.AssignStmt:
if err := c.compileAssign(node.Lhs, node.Rhs, node.Token); err != nil {
if err := c.compileAssign(node.LHS, node.RHS, node.Token); err != nil {
return err
}
case *ast.Ident:
@ -414,6 +417,7 @@ func (c *Compiler) Compile(node ast.Node) error {
return nil
}
// Bytecode returns a compiled bytecode.
func (c *Compiler) Bytecode() *Bytecode {
return &Bytecode{
Instructions: c.currentInstructions(),

View file

@ -9,21 +9,21 @@ import (
)
func (c *Compiler) compileAssign(lhs, rhs []ast.Expr, op token.Token) error {
numLhs, numRhs := len(lhs), len(rhs)
if numLhs < numRhs {
numLHS, numRHS := len(lhs), len(rhs)
if numLHS < numRHS {
// # of LHS must be >= # of RHS
return fmt.Errorf("assigntment count error: %d < %d", numLhs, numRhs)
return fmt.Errorf("assigntment count error: %d < %d", numLHS, numRHS)
}
if numLhs > 1 {
if numLHS > 1 {
// TODO: until we fully implement the tuple assignment
return fmt.Errorf("tuple assignment not implemented")
}
//if numLhs > 1 && op != token.Assign && op != token.Define {
//if numLHS > 1 && op != token.Assign && op != token.Define {
// return fmt.Errorf("invalid operator for tuple assignment: %s", op.String())
//}
// resolve and compile left-hand side
ident, selectors, err := resolveAssignLhs(lhs[0])
ident, selectors, err := resolveAssignLHS(lhs[0])
if err != nil {
return err
}
@ -124,10 +124,10 @@ func (c *Compiler) compileAssign(lhs, rhs []ast.Expr, op token.Token) error {
return nil
}
func resolveAssignLhs(expr ast.Expr) (name string, selectors []ast.Expr, err error) {
func resolveAssignLHS(expr ast.Expr) (name string, selectors []ast.Expr, err error) {
switch term := expr.(type) {
case *ast.SelectorExpr:
name, selectors, err = resolveAssignLhs(term.Expr)
name, selectors, err = resolveAssignLHS(term.Expr)
if err != nil {
return
}
@ -136,7 +136,7 @@ func resolveAssignLhs(expr ast.Expr) (name string, selectors []ast.Expr, err err
return
case *ast.IndexExpr:
name, selectors, err = resolveAssignLhs(term.Expr)
name, selectors, err = resolveAssignLHS(term.Expr)
if err != nil {
return
}

View file

@ -7,7 +7,7 @@ import (
func (c *Compiler) compileLogical(node *ast.BinaryExpr) error {
// left side term
if err := c.Compile(node.Lhs); err != nil {
if err := c.Compile(node.LHS); err != nil {
return err
}
@ -20,7 +20,7 @@ func (c *Compiler) compileLogical(node *ast.BinaryExpr) error {
}
// right side term
if err := c.Compile(node.Rhs); err != nil {
if err := c.Compile(node.RHS); err != nil {
return err
}

View file

@ -1,5 +1,7 @@
package compiler
// Definition represents an Opcode name and
// the number of operands.
type Definition struct {
Name string
Operands []int
@ -58,6 +60,7 @@ var definitions = map[Opcode]*Definition{
OpIteratorValue: {Name: "ITVAL", Operands: []int{}},
}
// Lookup returns a Definition of a given opcode.
func Lookup(opcode Opcode) (def *Definition, ok bool) {
def, ok = definitions[opcode]

View file

@ -1,5 +1,7 @@
package compiler
// EmittedInstruction represents an opcode
// with its emitted position.
type EmittedInstruction struct {
Opcode Opcode
Position int

View file

@ -4,6 +4,7 @@ import (
"fmt"
)
// MakeInstruction returns a bytecode for an opcode and the operands.
func MakeInstruction(opcode Opcode, operands ...int) []byte {
def, ok := Lookup(opcode)
if !ok {
@ -35,6 +36,8 @@ func MakeInstruction(opcode Opcode, operands ...int) []byte {
return instruction
}
// FormatInstructions returns string representation of
// bytecode instructions.
func FormatInstructions(b []byte, posOffset int) []string {
var out []string

View file

@ -1,5 +1,7 @@
package compiler
// Loop represents a loop construct that
// the compiler uses to track the current loop.
type Loop struct {
Continues []int
Breaks []int

View file

@ -1,56 +1,58 @@
package compiler
// Opcode represents a single byte operation code.
type Opcode byte
// List of opcodes
const (
OpConstant Opcode = iota
OpAdd
OpSub
OpMul
OpDiv
OpRem
OpBAnd
OpBOr
OpBXor
OpBShiftLeft
OpBShiftRight
OpBAndNot
OpBComplement
OpPop
OpTrue
OpFalse
OpEqual
OpNotEqual
OpGreaterThan
OpGreaterThanEqual
OpMinus
OpLNot
OpJumpFalsy
OpAndJump
OpOrJump
OpJump
OpNull
OpArray
OpMap
OpIndex
OpSliceIndex
OpCall
OpReturn
OpReturnValue
OpGetGlobal
OpSetGlobal
OpSetSelGlobal
OpGetLocal
OpSetLocal
OpDefineLocal
OpSetSelLocal
OpGetFree
OpSetFree
OpSetSelFree
OpGetBuiltin
OpClosure
OpIteratorInit
OpIteratorNext
OpIteratorKey
OpIteratorValue
OpConstant Opcode = iota // Load constant
OpAdd // Add
OpSub // Sub
OpMul // Multiply
OpDiv // Divide
OpRem // Remainder
OpBAnd // bitwise AND
OpBOr // bitwise OR
OpBXor // bitwise XOR
OpBShiftLeft // bitwise shift left
OpBShiftRight // bitwise shift right
OpBAndNot // bitwise AND NOT
OpBComplement // bitwise complement
OpPop // Pop
OpTrue // Push true
OpFalse // Push false
OpEqual // Equal ==
OpNotEqual // Not equal !=
OpGreaterThan // Greater than >=
OpGreaterThanEqual // Greater than or equal to >=
OpMinus // Minus -
OpLNot // Logical not !
OpJumpFalsy // Jump if falsy
OpAndJump // Logical AND jump
OpOrJump // Logical OR jump
OpJump // Jump
OpNull // Push null
OpArray // Array literal
OpMap // Map literal
OpIndex // Index operation
OpSliceIndex // Slice operation
OpCall // Call function
OpReturn // Return
OpReturnValue // Return value
OpGetGlobal // Get global variable
OpSetGlobal // Set global variable
OpSetSelGlobal // Set global variable using selectors
OpGetLocal // Get local variable
OpSetLocal // Set local variable
OpDefineLocal // Define local variable
OpSetSelLocal // Set local variable using selectors
OpGetFree // Get free variables
OpSetFree // Set free variables
OpSetSelFree // Set free variables using selectors
OpGetBuiltin // Get builtin function
OpClosure // Push closure
OpIteratorInit // Iterator init
OpIteratorNext // Iterator next
OpIteratorKey // Iterator key
OpIteratorValue // Iterator value
)

View file

@ -1,5 +1,6 @@
package compiler
// ReadOperands reads operands from the bytecode.
func ReadOperands(def *Definition, ins []byte) (operands []int, offset int) {
for _, width := range def.Operands {
switch width {
@ -15,10 +16,12 @@ func ReadOperands(def *Definition, ins []byte) (operands []int, offset int) {
return
}
// ReadUint16 reads uint16 from the byte slice.
func ReadUint16(b []byte) uint16 {
return uint16(b[1]) | uint16(b[0])<<8
}
// ReadUint8 reads uint8 from the byte slice.
func ReadUint8(b []byte) uint8 {
return uint8(b[0])
}

View file

@ -2,6 +2,7 @@ package parser
import "github.com/d5/tengo/compiler/source"
// Error represents a parser error.
type Error struct {
Pos source.FilePos
Msg string

View file

@ -7,16 +7,20 @@ import (
"github.com/d5/tengo/compiler/source"
)
// ErrorList is a collection of parser errors.
type ErrorList []*Error
// Add adds a new parser error to the collection.
func (p *ErrorList) Add(pos source.FilePos, msg string) {
*p = append(*p, &Error{pos, msg})
}
// Reset clears the collection.
func (p *ErrorList) Reset() {
*p = (*p)[0:0]
}
// Len returns the number of elements in the collection.
func (p ErrorList) Len() int {
return len(p)
}
@ -44,27 +48,11 @@ func (p ErrorList) Less(i, j int) bool {
return p[i].Msg < p[j].Msg
}
// Sort sorts the collection.
func (p ErrorList) Sort() {
sort.Sort(p)
}
func (p *ErrorList) RemoveMultiples() {
sort.Sort(p)
var last source.FilePos // initial last.Line is != any legal error line
i := 0
for _, e := range *p {
if e.Pos.Filename != last.Filename || e.Pos.Line != last.Line {
last = e.Pos
(*p)[i] = e
i++
}
}
*p = (*p)[0:i]
}
func (p ErrorList) Error() string {
switch len(p) {
case 0:
@ -75,6 +63,7 @@ func (p ErrorList) Error() string {
return fmt.Sprintf("%s (and %d more errors)", p[0], len(p)-1)
}
// Err returns an error.
func (p ErrorList) Err() error {
if len(p) == 0 {
return nil

View file

@ -7,6 +7,7 @@ import (
"github.com/d5/tengo/compiler/source"
)
// ParseFile parses a file with a given src.
func ParseFile(file *source.File, src []byte, trace io.Writer) (res *ast.File, err error) {
p := NewParser(file, src, trace)

View file

@ -23,6 +23,7 @@ import (
type bailout struct{}
// Parser parses the Tengo source files.
type Parser struct {
file *source.File
errors ErrorList
@ -38,6 +39,7 @@ type Parser struct {
traceOut io.Writer
}
// NewParser creates a Parser.
func NewParser(file *source.File, src []byte, trace io.Writer) *Parser {
p := &Parser{
file: file,
@ -54,6 +56,7 @@ func NewParser(file *source.File, src []byte, trace io.Writer) *Parser {
return p
}
// ParseFile parses the source and returns an AST file unit.
func (p *Parser) ParseFile() (*ast.File, error) {
if p.trace {
defer un(trace(p, "File"))
@ -99,8 +102,8 @@ func (p *Parser) parseBinaryExpr(prec1 int) ast.Expr {
y := p.parseBinaryExpr(prec + 1)
x = &ast.BinaryExpr{
Lhs: x,
Rhs: y,
LHS: x,
RHS: y,
Token: op,
TokenPos: pos,
}
@ -785,8 +788,8 @@ func (p *Parser) parseSimpleStmt(forIn bool) ast.Stmt {
y := p.parseExprList()
return &ast.AssignStmt{
Lhs: x,
Rhs: y,
LHS: x,
RHS: y,
Token: tok,
TokenPos: pos,
}
@ -844,8 +847,8 @@ func (p *Parser) parseSimpleStmt(forIn bool) ast.Stmt {
y := p.parseExpr()
return &ast.AssignStmt{
Lhs: []ast.Expr{x[0]},
Rhs: []ast.Expr{y},
LHS: []ast.Expr{x[0]},
RHS: []ast.Expr{y},
Token: tok,
TokenPos: pos,
}

View file

@ -140,7 +140,7 @@ func exprStmt(x ast.Expr) *ast.ExprStmt {
}
func assignStmt(lhs, rhs []ast.Expr, token token.Token, pos source.Pos) *ast.AssignStmt {
return &ast.AssignStmt{Lhs: lhs, Rhs: rhs, Token: token, TokenPos: pos}
return &ast.AssignStmt{LHS: lhs, RHS: rhs, Token: token, TokenPos: pos}
}
func emptyStmt(implicit bool, pos source.Pos) *ast.EmptyStmt {
@ -184,7 +184,7 @@ func identList(opening, closing source.Pos, list ...*ast.Ident) *ast.IdentList {
}
func binaryExpr(x, y ast.Expr, op token.Token, pos source.Pos) *ast.BinaryExpr {
return &ast.BinaryExpr{Lhs: x, Rhs: y, Token: op, TokenPos: pos}
return &ast.BinaryExpr{LHS: x, RHS: y, Token: op, TokenPos: pos}
}
func unaryExpr(x ast.Expr, op token.Token, pos source.Pos) *ast.UnaryExpr {
@ -273,8 +273,8 @@ func equalStmt(t *testing.T, expected, actual ast.Stmt) bool {
assert.Equal(t, expected.RBrace, actual.(*ast.BlockStmt).RBrace) &&
equalStmts(t, expected.Stmts, actual.(*ast.BlockStmt).Stmts)
case *ast.AssignStmt:
return equalExprs(t, expected.Lhs, actual.(*ast.AssignStmt).Lhs) &&
equalExprs(t, expected.Rhs, actual.(*ast.AssignStmt).Rhs) &&
return equalExprs(t, expected.LHS, actual.(*ast.AssignStmt).LHS) &&
equalExprs(t, expected.RHS, actual.(*ast.AssignStmt).RHS) &&
assert.Equal(t, int(expected.Token), int(actual.(*ast.AssignStmt).Token)) &&
assert.Equal(t, int(expected.TokenPos), int(actual.(*ast.AssignStmt).TokenPos))
case *ast.IfStmt:
@ -350,8 +350,8 @@ func equalExpr(t *testing.T, expected, actual ast.Expr) bool {
assert.Equal(t, expected.RBrace, actual.(*ast.MapLit).RBrace) &&
equalMapElements(t, expected.Elements, actual.(*ast.MapLit).Elements)
case *ast.BinaryExpr:
return equalExpr(t, expected.Lhs, actual.(*ast.BinaryExpr).Lhs) &&
equalExpr(t, expected.Rhs, actual.(*ast.BinaryExpr).Rhs) &&
return equalExpr(t, expected.LHS, actual.(*ast.BinaryExpr).LHS) &&
equalExpr(t, expected.RHS, actual.(*ast.BinaryExpr).RHS) &&
assert.Equal(t, expected.Token, actual.(*ast.BinaryExpr).Token) &&
assert.Equal(t, expected.TokenPos, actual.(*ast.BinaryExpr).TokenPos)
case *ast.UnaryExpr:

View file

@ -2,4 +2,5 @@ package scanner
import "github.com/d5/tengo/compiler/source"
// ErrorHandler is an error handler for the scanner.
type ErrorHandler func(pos source.FilePos, msg string)

View file

@ -1,7 +1,9 @@
package scanner
// Mode represents a scanner mode.
type Mode int
// List of scanner modes.
const (
ScanComments Mode = 1 << iota
DontInsertSemis

View file

@ -22,6 +22,7 @@ import (
// byte order mark
const bom = 0xFEFF
// Scanner reads the Tengo source text.
type Scanner struct {
file *source.File // source file handle
src []byte // source
@ -35,6 +36,7 @@ type Scanner struct {
mode Mode
}
// NewScanner creates a Scanner.
func NewScanner(file *source.File, src []byte, errorHandler ErrorHandler, mode Mode) *Scanner {
if file.Size() != len(src) {
panic(fmt.Sprintf("file size (%d) does not match src len (%d)", file.Size(), len(src)))
@ -56,10 +58,12 @@ func NewScanner(file *source.File, src []byte, errorHandler ErrorHandler, mode M
return s
}
// ErrorCount returns the number of errors.
func (s *Scanner) ErrorCount() int {
return s.errorCount
}
// Scan returns a token, token literal and its position.
func (s *Scanner) Scan() (tok token.Token, literal string, pos source.Pos) {
s.skipWhitespace()
@ -593,6 +597,7 @@ func (s *Scanner) scanRawString() string {
return string(lit)
}
// StripCR removes carriage return characters.
func StripCR(b []byte, comment bool) []byte {
c := make([]byte, len(b))

View file

@ -4,6 +4,7 @@ import (
"sync"
)
// File represents a source file.
type File struct {
set *FileSet
name string // file name as provided to AddFile
@ -13,18 +14,22 @@ type File struct {
lines []int // lines contains the offset of the first character for each line (the first entry is always 0)
}
// Name returns the file name.
func (f *File) Name() string {
return f.name
}
// Base returns the base position of the file.
func (f *File) Base() int {
return f.base
}
// Size returns the size of the file.
func (f *File) Size() int {
return f.size
}
// LineCount returns the current number of lines.
func (f *File) LineCount() int {
f.mutex.Lock()
n := len(f.lines)
@ -33,6 +38,7 @@ func (f *File) LineCount() int {
return n
}
// AddLine adds a new line.
func (f *File) AddLine(offset int) {
f.mutex.Lock()
if i := len(f.lines); (i == 0 || f.lines[i-1] < offset) && offset < f.size {
@ -41,6 +47,7 @@ func (f *File) AddLine(offset int) {
f.mutex.Unlock()
}
// LineStart returns the position of the first character in the line.
func (f *File) LineStart(line int) Pos {
if line < 1 {
panic("illegal line number (line numbering starts at 1)")
@ -56,6 +63,7 @@ func (f *File) LineStart(line int) Pos {
return Pos(f.base + f.lines[line-1])
}
// FileSetPos returns the position in the file set.
func (f *File) FileSetPos(offset int) Pos {
if offset > f.size {
panic("illegal file offset")
@ -64,6 +72,7 @@ func (f *File) FileSetPos(offset int) Pos {
return Pos(f.base + offset)
}
// Offset translates the file set position into the file offset.
func (f *File) Offset(p Pos) int {
if int(p) < f.base || int(p) > f.base+f.size {
panic("illegal Pos value")
@ -72,10 +81,7 @@ func (f *File) Offset(p Pos) int {
return int(p) - f.base
}
func (f *File) Line(p Pos) int {
return f.Position(p).Line
}
// Position translates the file set position into the file position.
func (f *File) Position(p Pos) (pos FilePos) {
if p != NoPos {
if int(p) < f.base || int(p) > f.base+f.size {

View file

@ -2,6 +2,7 @@ package source
import "fmt"
// FilePos represents a position information in the file.
type FilePos struct {
Filename string // filename, if any
Offset int // offset, starting at 0
@ -9,6 +10,7 @@ type FilePos struct {
Column int // column number, starting at 1 (byte count)
}
// IsValid returns true if the position is valid.
func (p FilePos) IsValid() bool {
return p.Line > 0
}

View file

@ -5,6 +5,7 @@ import (
"sync"
)
// FileSet represents a set of source files.
type FileSet struct {
mutex sync.RWMutex // protects the file set
base int // base offset for the next file
@ -12,12 +13,14 @@ type FileSet struct {
last *File // cache of last file looked up
}
// NewFileSet creates a new file set.
func NewFileSet() *FileSet {
return &FileSet{
base: 1, // 0 == NoPos
}
}
// Base returns the current base position of the file set.
func (s *FileSet) Base() int {
s.mutex.RLock()
b := s.base
@ -26,6 +29,7 @@ func (s *FileSet) Base() int {
return b
}
// AddFile adds a new file in the file set.
func (s *FileSet) AddFile(filename string, base, size int) *File {
s.mutex.Lock()
defer s.mutex.Unlock()
@ -70,7 +74,7 @@ func (s *FileSet) File(p Pos) (f *File) {
return
}
// PositionFor converts a Pos p in the fileset into a FilePos value.
// Position converts a Pos p in the fileset into a FilePos value.
func (s *FileSet) Position(p Pos) (pos FilePos) {
if p != NoPos {
if f := s.file(p); f != nil {

View file

@ -1,9 +1,12 @@
package source
// Pos represents a position in the file set.
type Pos int
// NoPos represents an invalid position.
const NoPos Pos = 0
// IsValid returns true if the position is valid.
func (p Pos) IsValid() bool {
return p != NoPos
}

View file

@ -1,5 +1,6 @@
package compiler
// Symbol represents a symbol in the symbol table.
type Symbol struct {
Name string
Scope SymbolScope

View file

@ -1,7 +1,9 @@
package compiler
// SymbolScope represents a symbol scope.
type SymbolScope string
// List of symbol scopes
const (
ScopeGlobal SymbolScope = "GLOBAL"
ScopeLocal = "LOCAL"

View file

@ -1,5 +1,6 @@
package compiler
// SymbolTable represents a symbol table.
type SymbolTable struct {
parent *SymbolTable
block bool
@ -9,12 +10,14 @@ type SymbolTable struct {
freeSymbols []Symbol
}
// NewSymbolTable creates a SymbolTable.
func NewSymbolTable() *SymbolTable {
return &SymbolTable{
store: make(map[string]Symbol),
}
}
// Define adds a new symbol in the current scope.
func (t *SymbolTable) Define(name string) Symbol {
symbol := Symbol{Name: name, Index: t.nextIndex()}
t.numDefinition++
@ -32,6 +35,7 @@ func (t *SymbolTable) Define(name string) Symbol {
return symbol
}
// DefineBuiltin adds a symbol for builtin function.
func (t *SymbolTable) DefineBuiltin(index int, name string) Symbol {
symbol := Symbol{
Name: name,
@ -44,6 +48,7 @@ func (t *SymbolTable) DefineBuiltin(index int, name string) Symbol {
return symbol
}
// Resolve resolves a symbol with a given name.
func (t *SymbolTable) Resolve(name string) (symbol Symbol, depth int, ok bool) {
symbol, ok = t.store[name]
if !ok && t.parent != nil {
@ -53,7 +58,7 @@ func (t *SymbolTable) Resolve(name string) (symbol Symbol, depth int, ok bool) {
}
if !t.block {
depth += 1
depth++
}
// if symbol is defined in parent table and if it's not global/builtin
@ -68,6 +73,7 @@ func (t *SymbolTable) Resolve(name string) (symbol Symbol, depth int, ok bool) {
return
}
// Fork creates a new symbol table for a new scope.
func (t *SymbolTable) Fork(block bool) *SymbolTable {
return &SymbolTable{
store: make(map[string]Symbol),
@ -76,6 +82,7 @@ func (t *SymbolTable) Fork(block bool) *SymbolTable {
}
}
// Parent returns the outer scope of the current symbol table.
func (t *SymbolTable) Parent(skipBlock bool) *SymbolTable {
if skipBlock && t.block {
return t.parent.Parent(skipBlock)
@ -84,14 +91,17 @@ func (t *SymbolTable) Parent(skipBlock bool) *SymbolTable {
return t.parent
}
// MaxSymbols returns the total number of symbols defined in the scope.
func (t *SymbolTable) MaxSymbols() int {
return t.maxDefinition
}
// FreeSymbols returns free symbols for the scope.
func (t *SymbolTable) FreeSymbols() []Symbol {
return t.freeSymbols
}
// Names returns the name of all the symbols.
func (t *SymbolTable) Names() []string {
var names []string
for name := range t.store {

View file

@ -9,6 +9,7 @@ func init() {
}
}
// Lookup returns corresponding keyword if ident is a keyword.
func Lookup(ident string) Token {
if tok, isKeyword := keywords[ident]; isKeyword {
return tok

View file

@ -2,8 +2,10 @@ package token
import "strconv"
// Token represents a token.
type Token int
// List of tokens
const (
Illegal Token = iota
EOF
@ -168,10 +170,10 @@ func (tok Token) String() string {
return s
}
const (
LowestPrec = 0 // non-operators
)
// LowestPrec represents lowest operator precedence.
const LowestPrec = 0
// Precedence returns the precedence for the operator token.
func (tok Token) Precedence() int {
switch tok {
case LOr:
@ -188,14 +190,17 @@ func (tok Token) Precedence() int {
return LowestPrec
}
// IsLiteral returns true if the token is a literal.
func (tok Token) IsLiteral() bool {
return _literalBeg < tok && tok < _literalEnd
}
// IsOperator returns true if the token is an operator.
func (tok Token) IsOperator() bool {
return _operatorBeg < tok && tok < _operatorEnd
}
// IsKeyword returns true if the token is a keyword.
func (tok Token) IsKeyword() bool {
return _keywordBeg < tok && tok < _keywordEnd
}

View file

@ -8,10 +8,12 @@ import (
"github.com/d5/tengo/compiler/token"
)
// Array represents an array of objects.
type Array struct {
Value []Object
}
// TypeName returns the name of the type.
func (o *Array) TypeName() string {
return "array"
}
@ -25,6 +27,8 @@ func (o *Array) String() string {
return fmt.Sprintf("[%s]", strings.Join(elements, ", "))
}
// BinaryOp returns another object that is the result of
// a given binary operator and a right-hand side object.
func (o *Array) BinaryOp(op token.Token, rhs Object) (Object, error) {
if rhs, ok := rhs.(*Array); ok {
switch op {
@ -39,6 +43,7 @@ func (o *Array) BinaryOp(op token.Token, rhs Object) (Object, error) {
return nil, ErrInvalidOperator
}
// Copy returns a copy of the type.
func (o *Array) Copy() Object {
var c []Object
for _, elem := range o.Value {
@ -48,10 +53,13 @@ func (o *Array) Copy() Object {
return &Array{Value: c}
}
// IsFalsy returns true if the value of the type is falsy.
func (o *Array) IsFalsy() bool {
return len(o.Value) == 0
}
// Equals returns true if the value of the type
// is equal to the value of another object.
func (o *Array) Equals(x Object) bool {
t, ok := x.(*Array)
if !ok {
@ -71,6 +79,7 @@ func (o *Array) Equals(x Object) bool {
return true
}
// Get returns an element at a given index.
func (o *Array) Get(index int) (Object, error) {
if index < 0 || index >= len(o.Value) {
return nil, errors.New("array index out of bounds")
@ -79,6 +88,7 @@ func (o *Array) Get(index int) (Object, error) {
return o.Value[index], nil
}
// Set sets an element at a given index.
func (o *Array) Set(index int, value Object) error {
if index < 0 || index >= len(o.Value) {
return errors.New("array index out of bounds")

View file

@ -4,6 +4,7 @@ import (
"github.com/d5/tengo/compiler/token"
)
// Bool represents a boolean value.
type Bool struct {
Value bool
}
@ -16,23 +17,30 @@ func (o *Bool) String() string {
return "false"
}
// TypeName returns the name of the type.
func (o *Bool) TypeName() string {
return "bool"
}
// BinaryOp returns another object that is the result of
// a given binary operator and a right-hand side object.
func (o *Bool) BinaryOp(op token.Token, rhs Object) (Object, error) {
return nil, ErrInvalidOperator
}
// Copy returns a copy of the type.
func (o *Bool) Copy() Object {
v := Bool(*o)
return &v
}
// IsFalsy returns true if the value of the type is falsy.
func (o *Bool) IsFalsy() bool {
return !o.Value
}
// Equals returns true if the value of the type
// is equal to the value of another object.
func (o *Bool) Equals(x Object) bool {
t, ok := x.(*Bool)
if !ok {

View file

@ -2,8 +2,10 @@ package objects
import "github.com/d5/tengo/compiler/token"
// Break represents a break statement.
type Break struct{}
// TypeName returns the name of the type.
func (o *Break) TypeName() string {
return "break"
}
@ -12,18 +14,24 @@ func (o *Break) String() string {
return "<break>"
}
// BinaryOp returns another object that is the result of
// a given binary operator and a right-hand side object.
func (o *Break) BinaryOp(op token.Token, rhs Object) (Object, error) {
return nil, ErrInvalidOperator
}
// Copy returns a copy of the type.
func (o *Break) Copy() Object {
return &Break{}
}
// IsFalsy returns true if the value of the type is falsy.
func (o *Break) IsFalsy() bool {
return false
}
// Equals returns true if the value of the type
// is equal to the value of another object.
func (o *Break) Equals(x Object) bool {
return false
}

View file

@ -4,10 +4,12 @@ import (
"github.com/d5/tengo/compiler/token"
)
// BuiltinFunction represents a builtin function.
type BuiltinFunction struct {
Value BuiltinFunc
}
// TypeName returns the name of the type.
func (o *BuiltinFunction) TypeName() string {
return "builtin-function"
}
@ -16,22 +18,29 @@ func (o *BuiltinFunction) String() string {
return "<builtin-function>"
}
// BinaryOp returns another object that is the result of
// a given binary operator and a right-hand side object.
func (o *BuiltinFunction) BinaryOp(op token.Token, rhs Object) (Object, error) {
return nil, ErrInvalidOperator
}
// Copy returns a copy of the type.
func (o *BuiltinFunction) Copy() Object {
return &BuiltinFunction{Value: o.Value}
}
// IsFalsy returns true if the value of the type is falsy.
func (o *BuiltinFunction) IsFalsy() bool {
return false
}
// Equals returns true if the value of the type
// is equal to the value of another object.
func (o *BuiltinFunction) Equals(x Object) bool {
return false
}
// Call executes a builtin function.
func (o *BuiltinFunction) Call(args ...Object) (Object, error) {
return o.Value(args...)
}

View file

@ -1,7 +1,9 @@
package objects
// BuiltinFunc is a function signature for the builtin functions.
type BuiltinFunc func(args ...Object) (ret Object, err error)
// Builtins contains all known builtin functions.
var Builtins = []struct {
Name string
Func BuiltinFunc

View file

@ -1,5 +1,6 @@
package objects
// Callable repesents an object that can be called like a function.
type Callable interface {
// Call should take an arbitrary number of arguments
// and returns a return value and/or an error,

View file

@ -4,6 +4,7 @@ import (
"github.com/d5/tengo/compiler/token"
)
// Char represents a character value.
type Char struct {
Value rune
}
@ -12,22 +13,29 @@ func (o *Char) String() string {
return string(o.Value)
}
// TypeName returns the name of the type.
func (o *Char) TypeName() string {
return "char"
}
// BinaryOp returns another object that is the result of
// a given binary operator and a right-hand side object.
func (o *Char) BinaryOp(op token.Token, rhs Object) (Object, error) {
return nil, ErrInvalidOperator
}
// Copy returns a copy of the type.
func (o *Char) Copy() Object {
return &Char{Value: o.Value}
}
// IsFalsy returns true if the value of the type is falsy.
func (o *Char) IsFalsy() bool {
return o.Value == 0
}
// Equals returns true if the value of the type
// is equal to the value of another object.
func (o *Char) Equals(x Object) bool {
t, ok := x.(*Char)
if !ok {

View file

@ -4,11 +4,13 @@ import (
"github.com/d5/tengo/compiler/token"
)
// Closure represents a function closure.
type Closure struct {
Fn *CompiledFunction
Free []*Object
}
// TypeName returns the name of the type.
func (o *Closure) TypeName() string {
return "closure"
}
@ -17,10 +19,13 @@ func (o *Closure) String() string {
return "<closure>"
}
// BinaryOp returns another object that is the result of
// a given binary operator and a right-hand side object.
func (o *Closure) BinaryOp(op token.Token, rhs Object) (Object, error) {
return nil, ErrInvalidOperator
}
// Copy returns a copy of the type.
func (o *Closure) Copy() Object {
return &Closure{
Fn: o.Fn.Copy().(*CompiledFunction),
@ -28,10 +33,13 @@ func (o *Closure) Copy() Object {
}
}
// IsFalsy returns true if the value of the type is falsy.
func (o *Closure) IsFalsy() bool {
return false
}
// Equals returns true if the value of the type
// is equal to the value of another object.
func (o *Closure) Equals(x Object) bool {
return false
}

View file

@ -4,12 +4,14 @@ import (
"github.com/d5/tengo/compiler/token"
)
// CompiledFunction represents a compiled function.
type CompiledFunction struct {
Instructions []byte
NumLocals int
NumParameters int
}
// TypeName returns the name of the type.
func (o *CompiledFunction) TypeName() string {
return "compiled-function"
}
@ -18,10 +20,13 @@ func (o *CompiledFunction) String() string {
return "<compiled-function>"
}
// BinaryOp returns another object that is the result of
// a given binary operator and a right-hand side object.
func (o *CompiledFunction) BinaryOp(op token.Token, rhs Object) (Object, error) {
return nil, ErrInvalidOperator
}
// Copy returns a copy of the type.
func (o *CompiledFunction) Copy() Object {
return &CompiledFunction{
Instructions: append([]byte{}, o.Instructions...),
@ -30,10 +35,13 @@ func (o *CompiledFunction) Copy() Object {
}
}
// IsFalsy returns true if the value of the type is falsy.
func (o *CompiledFunction) IsFalsy() bool {
return false
}
// Equals returns true if the value of the type
// is equal to the value of another object.
func (o *CompiledFunction) Equals(x Object) bool {
return false
}

View file

@ -2,9 +2,11 @@ package objects
import "github.com/d5/tengo/compiler/token"
// Continue represents a continue statement.
type Continue struct {
}
// TypeName returns the name of the type.
func (o *Continue) TypeName() string {
return "continue"
}
@ -13,18 +15,24 @@ func (o *Continue) String() string {
return "<continue>"
}
// BinaryOp returns another object that is the result of
// a given binary operator and a right-hand side object.
func (o *Continue) BinaryOp(op token.Token, rhs Object) (Object, error) {
return nil, ErrInvalidOperator
}
// Copy returns a copy of the type.
func (o *Continue) Copy() Object {
return &Continue{}
}
// IsFalsy returns true if the value of the type is falsy.
func (o *Continue) IsFalsy() bool {
return false
}
// Equals returns true if the value of the type
// is equal to the value of another object.
func (o *Continue) Equals(x Object) bool {
return false
}

View file

@ -2,4 +2,5 @@ package objects
import "errors"
// ErrInvalidOperator represents an error for invalid operator usage.
var ErrInvalidOperator = errors.New("invalid operator")

View file

@ -7,6 +7,7 @@ import (
"github.com/d5/tengo/compiler/token"
)
// Float represents a floating point number value.
type Float struct {
Value float64
}
@ -15,10 +16,13 @@ func (o *Float) String() string {
return strconv.FormatFloat(o.Value, 'f', -1, 64)
}
// TypeName returns the name of the type.
func (o *Float) TypeName() string {
return "float"
}
// BinaryOp returns another object that is the result of
// a given binary operator and a right-hand side object.
func (o *Float) BinaryOp(op token.Token, rhs Object) (Object, error) {
switch rhs := rhs.(type) {
case *Float:
@ -120,14 +124,18 @@ func (o *Float) BinaryOp(op token.Token, rhs Object) (Object, error) {
return nil, ErrInvalidOperator
}
// Copy returns a copy of the type.
func (o *Float) Copy() Object {
return &Float{Value: o.Value}
}
// IsFalsy returns true if the value of the type is falsy.
func (o *Float) IsFalsy() bool {
return math.IsNaN(o.Value)
}
// Equals returns true if the value of the type
// is equal to the value of another object.
func (o *Float) Equals(x Object) bool {
t, ok := x.(*Float)
if !ok {

View file

@ -6,6 +6,7 @@ import (
"github.com/d5/tengo/compiler/token"
)
// Int represents an integer value.
type Int struct {
Value int64
}
@ -14,10 +15,13 @@ func (o *Int) String() string {
return strconv.FormatInt(o.Value, 10)
}
// TypeName returns the name of the type.
func (o *Int) TypeName() string {
return "int"
}
// BinaryOp returns another object that is the result of
// a given binary operator and a right-hand side object.
func (o *Int) BinaryOp(op token.Token, rhs Object) (Object, error) {
switch rhs := rhs.(type) {
case *Int:
@ -145,14 +149,18 @@ func (o *Int) BinaryOp(op token.Token, rhs Object) (Object, error) {
return nil, ErrInvalidOperator
}
// Copy returns a copy of the type.
func (o *Int) Copy() Object {
return &Int{o.Value}
}
// IsFalsy returns true if the value of the type is falsy.
func (o *Int) IsFalsy() bool {
return o.Value == 0
}
// Equals returns true if the value of the type
// is equal to the value of another object.
func (o *Int) Equals(x Object) bool {
t, ok := x.(*Int)
if !ok {

View file

@ -2,19 +2,28 @@ package objects
import "github.com/d5/tengo/compiler/token"
// Iterator represents an iterator for underlying data type.
type Iterator interface {
Object
// Next returns true if there are more elements to iterate.
Next() bool
// Key returns the key or index value of the current element.
Key() Object
// Value returns the value of the current element.
Value() Object
}
// ArrayIterator is an iterator for an array.
type ArrayIterator struct {
v []Object
i int
l int
}
// NewArrayIterator creates an ArrayIterator.
func NewArrayIterator(v *Array) Iterator {
return &ArrayIterator{
v: v.Value,
@ -22,6 +31,7 @@ func NewArrayIterator(v *Array) Iterator {
}
}
// TypeName returns the name of the type.
func (i *ArrayIterator) TypeName() string {
return "array-iterator"
}
@ -30,35 +40,45 @@ func (i *ArrayIterator) String() string {
return "<array-iterator>"
}
// BinaryOp returns another object that is the result of
// a given binary operator and a right-hand side object.
func (i *ArrayIterator) BinaryOp(op token.Token, rhs Object) (Object, error) {
return nil, ErrInvalidOperator
}
// IsFalsy returns true if the value of the type is falsy.
func (i *ArrayIterator) IsFalsy() bool {
return true
}
// Equals returns true if the value of the type
// is equal to the value of another object.
func (i *ArrayIterator) Equals(Object) bool {
return false
}
// Copy returns a copy of the type.
func (i *ArrayIterator) Copy() Object {
return &ArrayIterator{v: i.v, i: i.i, l: i.l}
}
// Next returns true if there are more elements to iterate.
func (i *ArrayIterator) Next() bool {
i.i++
return i.i <= i.l
}
// Key returns the key or index value of the current element.
func (i *ArrayIterator) Key() Object {
return &Int{int64(i.i - 1)}
}
// Value returns the value of the current element.
func (i *ArrayIterator) Value() Object {
return i.v[i.i-1]
}
// MapIterator represents an iterator for the map.
type MapIterator struct {
v map[string]Object
k []string
@ -66,6 +86,7 @@ type MapIterator struct {
l int
}
// NewMapIterator creates a map iterator.
func NewMapIterator(v *Map) Iterator {
var keys []string
for k := range v.Value {
@ -79,6 +100,7 @@ func NewMapIterator(v *Map) Iterator {
}
}
// TypeName returns the name of the type.
func (i *MapIterator) TypeName() string {
return "map-iterator"
}
@ -87,45 +109,56 @@ func (i *MapIterator) String() string {
return "<map-iterator>"
}
// BinaryOp returns another object that is the result of
// a given binary operator and a right-hand side object.
func (i *MapIterator) BinaryOp(op token.Token, rhs Object) (Object, error) {
return nil, ErrInvalidOperator
}
// IsFalsy returns true if the value of the type is falsy.
func (i *MapIterator) IsFalsy() bool {
return true
}
// Equals returns true if the value of the type
// is equal to the value of another object.
func (i *MapIterator) Equals(Object) bool {
return false
}
// Copy returns a copy of the type.
func (i *MapIterator) Copy() Object {
return &MapIterator{v: i.v, k: i.k, i: i.i, l: i.l}
}
// Next returns true if there are more elements to iterate.
func (i *MapIterator) Next() bool {
i.i++
return i.i <= i.l
}
// Key returns the key or index value of the current element.
func (i *MapIterator) Key() Object {
k := i.k[i.i-1]
return &String{Value: k}
}
// Value returns the value of the current element.
func (i *MapIterator) Value() Object {
k := i.k[i.i-1]
return i.v[k]
}
// StringIterator represents an iterator for a string.
type StringIterator struct {
v []rune
i int
l int
}
// NewStringIterator creates a string iterator.
func NewStringIterator(v *String) Iterator {
r := []rune(v.Value)
@ -135,6 +168,7 @@ func NewStringIterator(v *String) Iterator {
}
}
// TypeName returns the name of the type.
func (i *StringIterator) TypeName() string {
return "string-iterator"
}
@ -143,31 +177,40 @@ func (i *StringIterator) String() string {
return "<string-iterator>"
}
// BinaryOp returns another object that is the result of
// a given binary operator and a right-hand side object.
func (i *StringIterator) BinaryOp(op token.Token, rhs Object) (Object, error) {
return nil, ErrInvalidOperator
}
// IsFalsy returns true if the value of the type is falsy.
func (i *StringIterator) IsFalsy() bool {
return true
}
// Equals returns true if the value of the type
// is equal to the value of another object.
func (i *StringIterator) Equals(Object) bool {
return false
}
// Copy returns a copy of the type.
func (i *StringIterator) Copy() Object {
return &StringIterator{v: i.v, i: i.i, l: i.l}
}
// Next returns true if there are more elements to iterate.
func (i *StringIterator) Next() bool {
i.i++
return i.i <= i.l
}
// Key returns the key or index value of the current element.
func (i *StringIterator) Key() Object {
return &Int{int64(i.i - 1)}
}
// Value returns the value of the current element.
func (i *StringIterator) Value() Object {
return &Char{Value: i.v[i.i-1]}
}

View file

@ -7,10 +7,12 @@ import (
"github.com/d5/tengo/compiler/token"
)
// Map represents a map of objects.
type Map struct {
Value map[string]Object
}
// TypeName returns the name of the type.
func (o *Map) TypeName() string {
return "map"
}
@ -24,10 +26,13 @@ func (o *Map) String() string {
return fmt.Sprintf("{%s}", strings.Join(pairs, ", "))
}
// BinaryOp returns another object that is the result of
// a given binary operator and a right-hand side object.
func (o *Map) BinaryOp(op token.Token, rhs Object) (Object, error) {
return nil, ErrInvalidOperator
}
// Copy returns a copy of the type.
func (o *Map) Copy() Object {
c := make(map[string]Object)
for k, v := range o.Value {
@ -37,20 +42,25 @@ func (o *Map) Copy() Object {
return &Map{Value: c}
}
// IsFalsy returns true if the value of the type is falsy.
func (o *Map) IsFalsy() bool {
return len(o.Value) == 0
}
// Get returns the value for the given key.
func (o *Map) Get(key string) (Object, bool) {
val, ok := o.Value[key]
return val, ok
}
// Set sets the value for the given key.
func (o *Map) Set(key string, value Object) {
o.Value[key] = value
}
// Equals returns true if the value of the type
// is equal to the value of another object.
func (o *Map) Equals(x Object) bool {
t, ok := x.(*Map)
if !ok {

View file

@ -2,6 +2,7 @@ package objects
import "github.com/d5/tengo/compiler/token"
// Object represents an object in the VM.
type Object interface {
// TypeName should return the name of the type.
TypeName() string

View file

@ -1,7 +1,12 @@
package objects
var (
TrueValue Object = &Bool{Value: true}
FalseValue Object = &Bool{Value: false}
// TrueValue represents a true value.
TrueValue Object = &Bool{Value: true}
// FalseValue represents a false value.
FalseValue Object = &Bool{Value: false}
// UndefinedValue represents an undefined value.
UndefinedValue Object = &Undefined{}
)

View file

@ -2,10 +2,12 @@ package objects
import "github.com/d5/tengo/compiler/token"
// ReturnValue represents a value that is being returned.
type ReturnValue struct {
Value Object
}
// TypeName returns the name of the type.
func (o *ReturnValue) TypeName() string {
return "return-value"
}
@ -14,18 +16,24 @@ func (o *ReturnValue) String() string {
return "<return>"
}
// BinaryOp returns another object that is the result of
// a given binary operator and a right-hand side object.
func (o *ReturnValue) BinaryOp(op token.Token, rhs Object) (Object, error) {
return nil, ErrInvalidOperator
}
// Copy returns a copy of the type.
func (o *ReturnValue) Copy() Object {
return &ReturnValue{Value: o.Copy()}
}
// IsFalsy returns true if the value of the type is falsy.
func (o *ReturnValue) IsFalsy() bool {
return false
}
// Equals returns true if the value of the type
// is equal to the value of another object.
func (o *ReturnValue) Equals(x Object) bool {
return false
}

View file

@ -6,10 +6,12 @@ import (
"github.com/d5/tengo/compiler/token"
)
// String represents a string value.
type String struct {
Value string
}
// TypeName returns the name of the type.
func (o *String) TypeName() string {
return "string"
}
@ -18,6 +20,8 @@ func (o *String) String() string {
return fmt.Sprintf("%q", o.Value)
}
// BinaryOp returns another object that is the result of
// a given binary operator and a right-hand side object.
func (o *String) BinaryOp(op token.Token, rhs Object) (Object, error) {
switch rhs := rhs.(type) {
case *String:
@ -38,14 +42,18 @@ func (o *String) BinaryOp(op token.Token, rhs Object) (Object, error) {
return nil, ErrInvalidOperator
}
// IsFalsy returns true if the value of the type is falsy.
func (o *String) IsFalsy() bool {
return len(o.Value) == 0
}
// Copy returns a copy of the type.
func (o *String) Copy() Object {
return &String{Value: o.Value}
}
// Equals returns true if the value of the type
// is equal to the value of another object.
func (o *String) Equals(x Object) bool {
t, ok := x.(*String)
if !ok {

View file

@ -2,8 +2,10 @@ package objects
import "github.com/d5/tengo/compiler/token"
// Undefined represents an undefined value.
type Undefined struct{}
// TypeName returns the name of the type.
func (o Undefined) TypeName() string {
return "undefined"
}
@ -12,18 +14,24 @@ func (o Undefined) String() string {
return "<undefined>"
}
// BinaryOp returns another object that is the result of
// a given binary operator and a right-hand side object.
func (o Undefined) BinaryOp(op token.Token, rhs Object) (Object, error) {
return nil, ErrInvalidOperator
}
// Copy returns a copy of the type.
func (o Undefined) Copy() Object {
return Undefined{}
}
// IsFalsy returns true if the value of the type is falsy.
func (o Undefined) IsFalsy() bool {
return true
}
// Equals returns true if the value of the type
// is equal to the value of another object.
func (o Undefined) Equals(x Object) bool {
_, ok := x.(*Undefined)

View file

@ -4,4 +4,5 @@ import (
"errors"
)
// ErrStackOverflow is a stack overflow error.
var ErrStackOverflow = errors.New("stack overflow")

View file

@ -4,6 +4,7 @@ import (
"github.com/d5/tengo/objects"
)
// Frame represents a function call frame.
type Frame struct {
fn *objects.CompiledFunction
freeVars []*objects.Object

View file

@ -9,9 +9,14 @@ import (
)
const (
StackSize = 2048
// StackSize is the maximum stack size.
StackSize = 2048
// GlobalsSize is the maximum number of global variables.
GlobalsSize = 1024
MaxFrames = 1024
// MaxFrames is the maximum number of function frames.
MaxFrames = 1024
)
var (
@ -21,6 +26,7 @@ var (
builtinFuncs []objects.Object
)
// VM is a virtual machine that executes the bytecode compiled by Compiler.
type VM struct {
constants []objects.Object
stack []*objects.Object
@ -34,6 +40,7 @@ type VM struct {
aborting bool
}
// NewVM creates a VM.
func NewVM(bytecode *compiler.Bytecode, globals []*objects.Object) *VM {
if globals == nil {
globals = make([]*objects.Object, GlobalsSize)
@ -62,10 +69,12 @@ func NewVM(bytecode *compiler.Bytecode, globals []*objects.Object) *VM {
}
}
// Abort aborts the execution.
func (v *VM) Abort() {
v.aborting = true
}
// Run starts the execution.
func (v *VM) Run() error {
var ip int
@ -544,13 +553,13 @@ func (v *VM) Run() error {
}
v.sp -= numElements
var map_ objects.Object = &objects.Map{Value: kv}
var m objects.Object = &objects.Map{Value: kv}
if v.sp >= StackSize {
return ErrStackOverflow
}
v.stack[v.sp] = &map_
v.stack[v.sp] = &m
v.sp++
case compiler.OpIndex:
@ -703,7 +712,7 @@ func (v *VM) Run() error {
case compiler.OpCall:
numArgs := int(compiler.ReadUint8(v.curInsts[ip+1:]))
v.curFrame.ip += 1
v.curFrame.ip++
callee := *v.stack[v.sp-1-numArgs]
@ -748,7 +757,7 @@ func (v *VM) Run() error {
case compiler.OpReturnValue:
//numRets := int(compiler.ReadUint8(v.curInsts[ip+1:]))
_ = int(compiler.ReadUint8(v.curInsts[ip+1:]))
v.curFrame.ip += 1
v.curFrame.ip++
// TODO: multi-value return is not fully implemented yet
//var rets []*objects.Object
@ -798,7 +807,7 @@ func (v *VM) Run() error {
case compiler.OpDefineLocal:
localIndex := compiler.ReadUint8(v.curInsts[ip+1:])
v.curFrame.ip += 1
v.curFrame.ip++
sp := v.curFrame.basePointer + int(localIndex)
@ -811,7 +820,7 @@ func (v *VM) Run() error {
case compiler.OpSetLocal:
localIndex := compiler.ReadUint8(v.curInsts[ip+1:])
v.curFrame.ip += 1
v.curFrame.ip++
sp := v.curFrame.basePointer + int(localIndex)
@ -855,7 +864,7 @@ func (v *VM) Run() error {
case compiler.OpGetLocal:
localIndex := compiler.ReadUint8(v.curInsts[ip+1:])
v.curFrame.ip += 1
v.curFrame.ip++
val := v.stack[v.curFrame.basePointer+int(localIndex)]
@ -868,7 +877,7 @@ func (v *VM) Run() error {
case compiler.OpGetBuiltin:
builtinIndex := compiler.ReadUint8(v.curInsts[ip+1:])
v.curFrame.ip += 1
v.curFrame.ip++
if v.sp >= StackSize {
return ErrStackOverflow
@ -888,7 +897,7 @@ func (v *VM) Run() error {
case compiler.OpGetFree:
freeIndex := compiler.ReadUint8(v.curInsts[ip+1:])
v.curFrame.ip += 1
v.curFrame.ip++
val := v.curFrame.freeVars[freeIndex]
@ -930,7 +939,7 @@ func (v *VM) Run() error {
case compiler.OpSetFree:
freeIndex := compiler.ReadUint8(v.curInsts[ip+1:])
v.curFrame.ip += 1
v.curFrame.ip++
val := v.stack[v.sp-1]
v.sp--
@ -1016,10 +1025,12 @@ func (v *VM) Run() error {
return nil
}
// Globals returns the global variables.
func (v *VM) Globals() []*objects.Object {
return v.globals
}
// FrameInfo returns the current function call frame information.
func (v *VM) FrameInfo() (frameIndex int, ip int) {
return v.framesIndex - 1, v.frames[v.framesIndex-1].ip
}
@ -1150,17 +1161,17 @@ func selectorAssign(dst, src *objects.Object, selectors []interface{}) error {
for idx := 0; idx < numSel; idx++ {
switch sel := selectors[idx].(type) {
case string:
map_, isMap := (*dst).(*objects.Map)
m, isMap := (*dst).(*objects.Map)
if !isMap {
return fmt.Errorf("invalid map object for selector '%s'", sel)
}
if idx == numSel-1 {
map_.Set(sel, *src)
m.Set(sel, *src)
return nil
}
nxt, found := map_.Get(sel)
nxt, found := m.Get(sel)
if !found {
return fmt.Errorf("key not found '%s'", sel)
}