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

View file

@ -66,7 +66,7 @@ func isTest(name, prefix string) bool {
return true 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{ Func: &ast.Ident{
Name: "print", Name: "print",
}, },
Args: s.Lhs, Args: s.LHS,
}, },
}) })

View file

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

View file

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

View file

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

View file

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

View file

@ -5,23 +5,26 @@ import (
"github.com/d5/tengo/compiler/token" "github.com/d5/tengo/compiler/token"
) )
// BinaryExpr represents a binary operator expression.
type BinaryExpr struct { type BinaryExpr struct {
Lhs Expr LHS Expr
Rhs Expr RHS Expr
Token token.Token Token token.Token
TokenPos source.Pos TokenPos source.Pos
} }
func (e *BinaryExpr) exprNode() {} func (e *BinaryExpr) exprNode() {}
// Pos returns the position of first character belonging to the node.
func (e *BinaryExpr) Pos() source.Pos { 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 { func (e *BinaryExpr) End() source.Pos {
return e.Rhs.End() return e.RHS.End()
} }
func (e *BinaryExpr) String() string { 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" "github.com/d5/tengo/compiler/source"
) )
// BlockStmt represents a block statement.
type BlockStmt struct { type BlockStmt struct {
Stmts []Stmt Stmts []Stmt
LBrace source.Pos LBrace source.Pos
@ -14,10 +15,12 @@ type BlockStmt struct {
func (s *BlockStmt) stmtNode() {} func (s *BlockStmt) stmtNode() {}
// Pos returns the position of first character belonging to the node.
func (s *BlockStmt) Pos() source.Pos { func (s *BlockStmt) Pos() source.Pos {
return s.LBrace return s.LBrace
} }
// End returns the position of first character immediately after the node.
func (s *BlockStmt) End() source.Pos { func (s *BlockStmt) End() source.Pos {
return s.RBrace + 1 return s.RBrace + 1
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -7,6 +7,7 @@ import (
"github.com/d5/tengo/compiler/source" "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) { func ParseFile(file *source.File, src []byte, trace io.Writer) (res *ast.File, err error) {
p := NewParser(file, src, trace) p := NewParser(file, src, trace)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -4,6 +4,7 @@ import (
"github.com/d5/tengo/compiler/token" "github.com/d5/tengo/compiler/token"
) )
// Bool represents a boolean value.
type Bool struct { type Bool struct {
Value bool Value bool
} }
@ -16,23 +17,30 @@ func (o *Bool) String() string {
return "false" return "false"
} }
// TypeName returns the name of the type.
func (o *Bool) TypeName() string { func (o *Bool) TypeName() string {
return "bool" 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) { func (o *Bool) BinaryOp(op token.Token, rhs Object) (Object, error) {
return nil, ErrInvalidOperator return nil, ErrInvalidOperator
} }
// Copy returns a copy of the type.
func (o *Bool) Copy() Object { func (o *Bool) Copy() Object {
v := Bool(*o) v := Bool(*o)
return &v return &v
} }
// IsFalsy returns true if the value of the type is falsy.
func (o *Bool) IsFalsy() bool { func (o *Bool) IsFalsy() bool {
return !o.Value 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 { func (o *Bool) Equals(x Object) bool {
t, ok := x.(*Bool) t, ok := x.(*Bool)
if !ok { if !ok {

View file

@ -2,8 +2,10 @@ package objects
import "github.com/d5/tengo/compiler/token" import "github.com/d5/tengo/compiler/token"
// Break represents a break statement.
type Break struct{} type Break struct{}
// TypeName returns the name of the type.
func (o *Break) TypeName() string { func (o *Break) TypeName() string {
return "break" return "break"
} }
@ -12,18 +14,24 @@ func (o *Break) String() string {
return "<break>" 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) { func (o *Break) BinaryOp(op token.Token, rhs Object) (Object, error) {
return nil, ErrInvalidOperator return nil, ErrInvalidOperator
} }
// Copy returns a copy of the type.
func (o *Break) Copy() Object { func (o *Break) Copy() Object {
return &Break{} return &Break{}
} }
// IsFalsy returns true if the value of the type is falsy.
func (o *Break) IsFalsy() bool { func (o *Break) IsFalsy() bool {
return false 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 { func (o *Break) Equals(x Object) bool {
return false return false
} }

View file

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

View file

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

View file

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

View file

@ -4,6 +4,7 @@ import (
"github.com/d5/tengo/compiler/token" "github.com/d5/tengo/compiler/token"
) )
// Char represents a character value.
type Char struct { type Char struct {
Value rune Value rune
} }
@ -12,22 +13,29 @@ func (o *Char) String() string {
return string(o.Value) return string(o.Value)
} }
// TypeName returns the name of the type.
func (o *Char) TypeName() string { func (o *Char) TypeName() string {
return "char" 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) { func (o *Char) BinaryOp(op token.Token, rhs Object) (Object, error) {
return nil, ErrInvalidOperator return nil, ErrInvalidOperator
} }
// Copy returns a copy of the type.
func (o *Char) Copy() Object { func (o *Char) Copy() Object {
return &Char{Value: o.Value} return &Char{Value: o.Value}
} }
// IsFalsy returns true if the value of the type is falsy.
func (o *Char) IsFalsy() bool { func (o *Char) IsFalsy() bool {
return o.Value == 0 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 { func (o *Char) Equals(x Object) bool {
t, ok := x.(*Char) t, ok := x.(*Char)
if !ok { if !ok {

View file

@ -4,11 +4,13 @@ import (
"github.com/d5/tengo/compiler/token" "github.com/d5/tengo/compiler/token"
) )
// Closure represents a function closure.
type Closure struct { type Closure struct {
Fn *CompiledFunction Fn *CompiledFunction
Free []*Object Free []*Object
} }
// TypeName returns the name of the type.
func (o *Closure) TypeName() string { func (o *Closure) TypeName() string {
return "closure" return "closure"
} }
@ -17,10 +19,13 @@ func (o *Closure) String() string {
return "<closure>" 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) { func (o *Closure) BinaryOp(op token.Token, rhs Object) (Object, error) {
return nil, ErrInvalidOperator return nil, ErrInvalidOperator
} }
// Copy returns a copy of the type.
func (o *Closure) Copy() Object { func (o *Closure) Copy() Object {
return &Closure{ return &Closure{
Fn: o.Fn.Copy().(*CompiledFunction), 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 { func (o *Closure) IsFalsy() bool {
return false 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 { func (o *Closure) Equals(x Object) bool {
return false return false
} }

View file

@ -4,12 +4,14 @@ import (
"github.com/d5/tengo/compiler/token" "github.com/d5/tengo/compiler/token"
) )
// CompiledFunction represents a compiled function.
type CompiledFunction struct { type CompiledFunction struct {
Instructions []byte Instructions []byte
NumLocals int NumLocals int
NumParameters int NumParameters int
} }
// TypeName returns the name of the type.
func (o *CompiledFunction) TypeName() string { func (o *CompiledFunction) TypeName() string {
return "compiled-function" return "compiled-function"
} }
@ -18,10 +20,13 @@ func (o *CompiledFunction) String() string {
return "<compiled-function>" 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) { func (o *CompiledFunction) BinaryOp(op token.Token, rhs Object) (Object, error) {
return nil, ErrInvalidOperator return nil, ErrInvalidOperator
} }
// Copy returns a copy of the type.
func (o *CompiledFunction) Copy() Object { func (o *CompiledFunction) Copy() Object {
return &CompiledFunction{ return &CompiledFunction{
Instructions: append([]byte{}, o.Instructions...), 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 { func (o *CompiledFunction) IsFalsy() bool {
return false 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 { func (o *CompiledFunction) Equals(x Object) bool {
return false return false
} }

View file

@ -2,9 +2,11 @@ package objects
import "github.com/d5/tengo/compiler/token" import "github.com/d5/tengo/compiler/token"
// Continue represents a continue statement.
type Continue struct { type Continue struct {
} }
// TypeName returns the name of the type.
func (o *Continue) TypeName() string { func (o *Continue) TypeName() string {
return "continue" return "continue"
} }
@ -13,18 +15,24 @@ func (o *Continue) String() string {
return "<continue>" 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) { func (o *Continue) BinaryOp(op token.Token, rhs Object) (Object, error) {
return nil, ErrInvalidOperator return nil, ErrInvalidOperator
} }
// Copy returns a copy of the type.
func (o *Continue) Copy() Object { func (o *Continue) Copy() Object {
return &Continue{} return &Continue{}
} }
// IsFalsy returns true if the value of the type is falsy.
func (o *Continue) IsFalsy() bool { func (o *Continue) IsFalsy() bool {
return false 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 { func (o *Continue) Equals(x Object) bool {
return false return false
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -6,10 +6,12 @@ import (
"github.com/d5/tengo/compiler/token" "github.com/d5/tengo/compiler/token"
) )
// String represents a string value.
type String struct { type String struct {
Value string Value string
} }
// TypeName returns the name of the type.
func (o *String) TypeName() string { func (o *String) TypeName() string {
return "string" return "string"
} }
@ -18,6 +20,8 @@ func (o *String) String() string {
return fmt.Sprintf("%q", o.Value) 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) { func (o *String) BinaryOp(op token.Token, rhs Object) (Object, error) {
switch rhs := rhs.(type) { switch rhs := rhs.(type) {
case *String: case *String:
@ -38,14 +42,18 @@ func (o *String) BinaryOp(op token.Token, rhs Object) (Object, error) {
return nil, ErrInvalidOperator return nil, ErrInvalidOperator
} }
// IsFalsy returns true if the value of the type is falsy.
func (o *String) IsFalsy() bool { func (o *String) IsFalsy() bool {
return len(o.Value) == 0 return len(o.Value) == 0
} }
// Copy returns a copy of the type.
func (o *String) Copy() Object { func (o *String) Copy() Object {
return &String{Value: o.Value} 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 { func (o *String) Equals(x Object) bool {
t, ok := x.(*String) t, ok := x.(*String)
if !ok { if !ok {

View file

@ -2,8 +2,10 @@ package objects
import "github.com/d5/tengo/compiler/token" import "github.com/d5/tengo/compiler/token"
// Undefined represents an undefined value.
type Undefined struct{} type Undefined struct{}
// TypeName returns the name of the type.
func (o Undefined) TypeName() string { func (o Undefined) TypeName() string {
return "undefined" return "undefined"
} }
@ -12,18 +14,24 @@ func (o Undefined) String() string {
return "<undefined>" 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) { func (o Undefined) BinaryOp(op token.Token, rhs Object) (Object, error) {
return nil, ErrInvalidOperator return nil, ErrInvalidOperator
} }
// Copy returns a copy of the type.
func (o Undefined) Copy() Object { func (o Undefined) Copy() Object {
return Undefined{} return Undefined{}
} }
// IsFalsy returns true if the value of the type is falsy.
func (o Undefined) IsFalsy() bool { func (o Undefined) IsFalsy() bool {
return true 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 { func (o Undefined) Equals(x Object) bool {
_, ok := x.(*Undefined) _, ok := x.(*Undefined)

View file

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

View file

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

View file

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