parent
1c92fff07d
commit
b79fd4f7ef
90 changed files with 495 additions and 137 deletions
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
// NoError asserts err is not an error.
|
||||
func NoError(t *testing.T, err error, msg ...interface{}) bool {
|
||||
t.Helper()
|
||||
|
||||
|
@ -23,6 +24,7 @@ func NoError(t *testing.T, err error, msg ...interface{}) bool {
|
|||
return failExpectedActual(t, "no error", err, msg...)
|
||||
}
|
||||
|
||||
// Error asserts err is an error.
|
||||
func Error(t *testing.T, err error, msg ...interface{}) bool {
|
||||
t.Helper()
|
||||
|
||||
|
@ -33,6 +35,7 @@ func Error(t *testing.T, err error, msg ...interface{}) bool {
|
|||
return failExpectedActual(t, "error", err, msg...)
|
||||
}
|
||||
|
||||
// Nil asserts v is nil.
|
||||
func Nil(t *testing.T, v interface{}, msg ...interface{}) bool {
|
||||
t.Helper()
|
||||
|
||||
|
@ -43,6 +46,7 @@ func Nil(t *testing.T, v interface{}, msg ...interface{}) bool {
|
|||
return failExpectedActual(t, "nil", v, msg...)
|
||||
}
|
||||
|
||||
// True asserts v is true.
|
||||
func True(t *testing.T, v bool, msg ...interface{}) bool {
|
||||
t.Helper()
|
||||
|
||||
|
@ -53,6 +57,7 @@ func True(t *testing.T, v bool, msg ...interface{}) bool {
|
|||
return failExpectedActual(t, "true", v, msg...)
|
||||
}
|
||||
|
||||
// False asserts vis false.
|
||||
func False(t *testing.T, v bool, msg ...interface{}) bool {
|
||||
t.Helper()
|
||||
|
||||
|
@ -63,6 +68,7 @@ func False(t *testing.T, v bool, msg ...interface{}) bool {
|
|||
return failExpectedActual(t, "false", v, msg...)
|
||||
}
|
||||
|
||||
// NotNil asserts v is not nil.
|
||||
func NotNil(t *testing.T, v interface{}, msg ...interface{}) bool {
|
||||
t.Helper()
|
||||
|
||||
|
@ -73,6 +79,7 @@ func NotNil(t *testing.T, v interface{}, msg ...interface{}) bool {
|
|||
return failExpectedActual(t, "not nil", v, msg...)
|
||||
}
|
||||
|
||||
// IsType asserts expected and actual are of the same type.
|
||||
func IsType(t *testing.T, expected, actual interface{}, msg ...interface{}) bool {
|
||||
t.Helper()
|
||||
|
||||
|
@ -83,6 +90,7 @@ func IsType(t *testing.T, expected, actual interface{}, msg ...interface{}) bool
|
|||
return failExpectedActual(t, reflect.TypeOf(expected), reflect.TypeOf(actual), msg...)
|
||||
}
|
||||
|
||||
// Equal asserts expected and actual are equal.
|
||||
func Equal(t *testing.T, expected, actual interface{}, msg ...interface{}) bool {
|
||||
t.Helper()
|
||||
|
||||
|
@ -172,6 +180,7 @@ func Equal(t *testing.T, expected, actual interface{}, msg ...interface{}) bool
|
|||
return true
|
||||
}
|
||||
|
||||
// Fail marks the function as having failed but continues execution.
|
||||
func Fail(t *testing.T, msg ...interface{}) bool {
|
||||
t.Helper()
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ func isTest(name, prefix string) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
rune_, _ := utf8.DecodeRuneInString(name[len(prefix):])
|
||||
r, _ := utf8.DecodeRuneInString(name[len(prefix):])
|
||||
|
||||
return !unicode.IsLower(rune_)
|
||||
return !unicode.IsLower(r)
|
||||
}
|
||||
|
|
|
@ -248,7 +248,7 @@ func addPrints(file *ast.File) *ast.File {
|
|||
Func: &ast.Ident{
|
||||
Name: "print",
|
||||
},
|
||||
Args: s.Lhs,
|
||||
Args: s.LHS,
|
||||
},
|
||||
})
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/d5/tengo/compiler/source"
|
||||
)
|
||||
|
||||
// ArrayLit represents an array literal.
|
||||
type ArrayLit struct {
|
||||
Elements []Expr
|
||||
LBrack source.Pos
|
||||
|
@ -14,10 +15,12 @@ type ArrayLit struct {
|
|||
|
||||
func (e *ArrayLit) exprNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (e *ArrayLit) Pos() source.Pos {
|
||||
return e.LBrack
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (e *ArrayLit) End() source.Pos {
|
||||
return e.RBrack + 1
|
||||
}
|
||||
|
|
|
@ -7,29 +7,32 @@ import (
|
|||
"github.com/d5/tengo/compiler/token"
|
||||
)
|
||||
|
||||
// AssignStmt represents an assignment statement.
|
||||
type AssignStmt struct {
|
||||
Lhs []Expr
|
||||
Rhs []Expr
|
||||
LHS []Expr
|
||||
RHS []Expr
|
||||
Token token.Token
|
||||
TokenPos source.Pos
|
||||
}
|
||||
|
||||
func (s *AssignStmt) stmtNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (s *AssignStmt) Pos() source.Pos {
|
||||
return s.Lhs[0].Pos()
|
||||
return s.LHS[0].Pos()
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (s *AssignStmt) End() source.Pos {
|
||||
return s.Rhs[len(s.Rhs)-1].End()
|
||||
return s.RHS[len(s.RHS)-1].End()
|
||||
}
|
||||
|
||||
func (s *AssignStmt) String() string {
|
||||
var lhs, rhs []string
|
||||
for _, e := range s.Lhs {
|
||||
for _, e := range s.LHS {
|
||||
lhs = append(lhs, e.String())
|
||||
}
|
||||
for _, e := range s.Rhs {
|
||||
for _, e := range s.RHS {
|
||||
rhs = append(rhs, e.String())
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package ast
|
|||
|
||||
import "github.com/d5/tengo/compiler/source"
|
||||
|
||||
// BadExpr represents a bad expression.
|
||||
type BadExpr struct {
|
||||
From source.Pos
|
||||
To source.Pos
|
||||
|
@ -9,10 +10,12 @@ type BadExpr struct {
|
|||
|
||||
func (e *BadExpr) exprNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (e *BadExpr) Pos() source.Pos {
|
||||
return e.From
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (e *BadExpr) End() source.Pos {
|
||||
return e.To
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package ast
|
|||
|
||||
import "github.com/d5/tengo/compiler/source"
|
||||
|
||||
// BadStmt represents a bad statement.
|
||||
type BadStmt struct {
|
||||
From source.Pos
|
||||
To source.Pos
|
||||
|
@ -9,10 +10,12 @@ type BadStmt struct {
|
|||
|
||||
func (s *BadStmt) stmtNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (s *BadStmt) Pos() source.Pos {
|
||||
return s.From
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (s *BadStmt) End() source.Pos {
|
||||
return s.To
|
||||
}
|
||||
|
|
|
@ -5,23 +5,26 @@ import (
|
|||
"github.com/d5/tengo/compiler/token"
|
||||
)
|
||||
|
||||
// BinaryExpr represents a binary operator expression.
|
||||
type BinaryExpr struct {
|
||||
Lhs Expr
|
||||
Rhs Expr
|
||||
LHS Expr
|
||||
RHS Expr
|
||||
Token token.Token
|
||||
TokenPos source.Pos
|
||||
}
|
||||
|
||||
func (e *BinaryExpr) exprNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (e *BinaryExpr) Pos() source.Pos {
|
||||
return e.Lhs.Pos()
|
||||
return e.LHS.Pos()
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (e *BinaryExpr) End() source.Pos {
|
||||
return e.Rhs.End()
|
||||
return e.RHS.End()
|
||||
}
|
||||
|
||||
func (e *BinaryExpr) String() string {
|
||||
return "(" + e.Lhs.String() + " " + e.Token.String() + " " + e.Rhs.String() + ")"
|
||||
return "(" + e.LHS.String() + " " + e.Token.String() + " " + e.RHS.String() + ")"
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/d5/tengo/compiler/source"
|
||||
)
|
||||
|
||||
// BlockStmt represents a block statement.
|
||||
type BlockStmt struct {
|
||||
Stmts []Stmt
|
||||
LBrace source.Pos
|
||||
|
@ -14,10 +15,12 @@ type BlockStmt struct {
|
|||
|
||||
func (s *BlockStmt) stmtNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (s *BlockStmt) Pos() source.Pos {
|
||||
return s.LBrace
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (s *BlockStmt) End() source.Pos {
|
||||
return s.RBrace + 1
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package ast
|
|||
|
||||
import "github.com/d5/tengo/compiler/source"
|
||||
|
||||
// BoolLit represetns a boolean literal.
|
||||
type BoolLit struct {
|
||||
Value bool
|
||||
ValuePos source.Pos
|
||||
|
@ -10,10 +11,12 @@ type BoolLit struct {
|
|||
|
||||
func (e *BoolLit) exprNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (e *BoolLit) Pos() source.Pos {
|
||||
return e.ValuePos
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (e *BoolLit) End() source.Pos {
|
||||
return source.Pos(int(e.ValuePos) + len(e.Literal))
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"github.com/d5/tengo/compiler/token"
|
||||
)
|
||||
|
||||
// BranchStmt represents a branch statement.
|
||||
type BranchStmt struct {
|
||||
Token token.Token
|
||||
TokenPos source.Pos
|
||||
|
@ -13,10 +14,12 @@ type BranchStmt struct {
|
|||
|
||||
func (s *BranchStmt) stmtNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (s *BranchStmt) Pos() source.Pos {
|
||||
return s.TokenPos
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (s *BranchStmt) End() source.Pos {
|
||||
if s.Label != nil {
|
||||
return s.Label.End()
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/d5/tengo/compiler/source"
|
||||
)
|
||||
|
||||
// CallExpr represents a function call expression.
|
||||
type CallExpr struct {
|
||||
Func Expr
|
||||
LParen source.Pos
|
||||
|
@ -15,10 +16,12 @@ type CallExpr struct {
|
|||
|
||||
func (e *CallExpr) exprNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (e *CallExpr) Pos() source.Pos {
|
||||
return e.Func.Pos()
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (e *CallExpr) End() source.Pos {
|
||||
return e.RParen + 1
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package ast
|
|||
|
||||
import "github.com/d5/tengo/compiler/source"
|
||||
|
||||
// CharLit represents a character literal.
|
||||
type CharLit struct {
|
||||
Value rune
|
||||
ValuePos source.Pos
|
||||
|
@ -10,10 +11,12 @@ type CharLit struct {
|
|||
|
||||
func (e *CharLit) exprNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (e *CharLit) Pos() source.Pos {
|
||||
return e.ValuePos
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (e *CharLit) End() source.Pos {
|
||||
return source.Pos(int(e.ValuePos) + len(e.Literal))
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package ast
|
|||
|
||||
import "github.com/d5/tengo/compiler/source"
|
||||
|
||||
// EmptyStmt represents an empty statement.
|
||||
type EmptyStmt struct {
|
||||
Semicolon source.Pos
|
||||
Implicit bool
|
||||
|
@ -9,10 +10,12 @@ type EmptyStmt struct {
|
|||
|
||||
func (s *EmptyStmt) stmtNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (s *EmptyStmt) Pos() source.Pos {
|
||||
return s.Semicolon
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (s *EmptyStmt) End() source.Pos {
|
||||
if s.Implicit {
|
||||
return s.Semicolon
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ast
|
||||
|
||||
// Expr represents an expression node in the AST.
|
||||
type Expr interface {
|
||||
Node
|
||||
exprNode()
|
||||
|
|
|
@ -2,16 +2,19 @@ package ast
|
|||
|
||||
import "github.com/d5/tengo/compiler/source"
|
||||
|
||||
// ExprStmt represents an expression statement.
|
||||
type ExprStmt struct {
|
||||
Expr Expr
|
||||
}
|
||||
|
||||
func (s *ExprStmt) stmtNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (s *ExprStmt) Pos() source.Pos {
|
||||
return s.Expr.Pos()
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (s *ExprStmt) End() source.Pos {
|
||||
return s.Expr.End()
|
||||
}
|
||||
|
|
|
@ -6,15 +6,18 @@ import (
|
|||
"github.com/d5/tengo/compiler/source"
|
||||
)
|
||||
|
||||
// File represents a file unit.
|
||||
type File struct {
|
||||
InputFile *source.File
|
||||
Stmts []Stmt
|
||||
}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (n *File) Pos() source.Pos {
|
||||
return source.Pos(n.InputFile.Base())
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (n *File) End() source.Pos {
|
||||
return source.Pos(n.InputFile.Base() + n.InputFile.Size())
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package ast
|
|||
|
||||
import "github.com/d5/tengo/compiler/source"
|
||||
|
||||
// FloatLit represents a floating point literal.
|
||||
type FloatLit struct {
|
||||
Value float64
|
||||
ValuePos source.Pos
|
||||
|
@ -10,10 +11,12 @@ type FloatLit struct {
|
|||
|
||||
func (e *FloatLit) exprNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (e *FloatLit) Pos() source.Pos {
|
||||
return e.ValuePos
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (e *FloatLit) End() source.Pos {
|
||||
return source.Pos(int(e.ValuePos) + len(e.Literal))
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package ast
|
|||
|
||||
import "github.com/d5/tengo/compiler/source"
|
||||
|
||||
// ForInStmt represents a for-in statement.
|
||||
type ForInStmt struct {
|
||||
ForPos source.Pos
|
||||
Key *Ident
|
||||
|
@ -12,10 +13,12 @@ type ForInStmt struct {
|
|||
|
||||
func (s *ForInStmt) stmtNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (s *ForInStmt) Pos() source.Pos {
|
||||
return s.ForPos
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (s *ForInStmt) End() source.Pos {
|
||||
return s.Body.End()
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package ast
|
|||
|
||||
import "github.com/d5/tengo/compiler/source"
|
||||
|
||||
// ForStmt represetns a for statement.
|
||||
type ForStmt struct {
|
||||
ForPos source.Pos
|
||||
Init Stmt
|
||||
|
@ -12,10 +13,12 @@ type ForStmt struct {
|
|||
|
||||
func (s *ForStmt) stmtNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (s *ForStmt) Pos() source.Pos {
|
||||
return s.ForPos
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (s *ForStmt) End() source.Pos {
|
||||
return s.Body.End()
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package ast
|
|||
|
||||
import "github.com/d5/tengo/compiler/source"
|
||||
|
||||
// FuncLit represents a function literal.
|
||||
type FuncLit struct {
|
||||
Type *FuncType
|
||||
Body *BlockStmt
|
||||
|
@ -9,10 +10,12 @@ type FuncLit struct {
|
|||
|
||||
func (e *FuncLit) exprNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (e *FuncLit) Pos() source.Pos {
|
||||
return e.Type.Pos()
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (e *FuncLit) End() source.Pos {
|
||||
return e.Body.End()
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package ast
|
|||
|
||||
import "github.com/d5/tengo/compiler/source"
|
||||
|
||||
// FuncType represetns a function type definition.
|
||||
type FuncType struct {
|
||||
FuncPos source.Pos
|
||||
Params *IdentList
|
||||
|
@ -9,10 +10,12 @@ type FuncType struct {
|
|||
|
||||
func (e *FuncType) exprNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (e *FuncType) Pos() source.Pos {
|
||||
return e.FuncPos
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (e *FuncType) End() source.Pos {
|
||||
return e.Params.End()
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package ast
|
|||
|
||||
import "github.com/d5/tengo/compiler/source"
|
||||
|
||||
// Ident represetns an identifier.
|
||||
type Ident struct {
|
||||
Name string
|
||||
NamePos source.Pos
|
||||
|
@ -9,10 +10,12 @@ type Ident struct {
|
|||
|
||||
func (e *Ident) exprNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (e *Ident) Pos() source.Pos {
|
||||
return e.NamePos
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (e *Ident) End() source.Pos {
|
||||
return source.Pos(int(e.NamePos) + len(e.Name))
|
||||
}
|
||||
|
|
|
@ -6,12 +6,14 @@ import (
|
|||
"github.com/d5/tengo/compiler/source"
|
||||
)
|
||||
|
||||
// IdentList represetns a list of identifiers.
|
||||
type IdentList struct {
|
||||
LParen source.Pos
|
||||
List []*Ident
|
||||
RParen source.Pos
|
||||
}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (n *IdentList) Pos() source.Pos {
|
||||
if n.LParen.IsValid() {
|
||||
return n.LParen
|
||||
|
@ -24,6 +26,7 @@ func (n *IdentList) Pos() source.Pos {
|
|||
return source.NoPos
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (n *IdentList) End() source.Pos {
|
||||
if n.RParen.IsValid() {
|
||||
return n.RParen + 1
|
||||
|
@ -36,6 +39,7 @@ func (n *IdentList) End() source.Pos {
|
|||
return source.NoPos
|
||||
}
|
||||
|
||||
// NumFields returns the number of fields.
|
||||
func (n *IdentList) NumFields() int {
|
||||
if n == nil {
|
||||
return 0
|
||||
|
|
|
@ -2,6 +2,7 @@ package ast
|
|||
|
||||
import "github.com/d5/tengo/compiler/source"
|
||||
|
||||
// IfStmt represents an if statement.
|
||||
type IfStmt struct {
|
||||
IfPos source.Pos
|
||||
Init Stmt
|
||||
|
@ -12,10 +13,12 @@ type IfStmt struct {
|
|||
|
||||
func (s *IfStmt) stmtNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (s *IfStmt) Pos() source.Pos {
|
||||
return s.IfPos
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (s *IfStmt) End() source.Pos {
|
||||
if s.Else != nil {
|
||||
return s.Else.End()
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"github.com/d5/tengo/compiler/token"
|
||||
)
|
||||
|
||||
// IncDecStmt represents increment or decrement statement.
|
||||
type IncDecStmt struct {
|
||||
Expr Expr
|
||||
Token token.Token
|
||||
|
@ -13,10 +14,12 @@ type IncDecStmt struct {
|
|||
|
||||
func (s *IncDecStmt) stmtNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (s *IncDecStmt) Pos() source.Pos {
|
||||
return s.Expr.Pos()
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (s *IncDecStmt) End() source.Pos {
|
||||
return source.Pos(int(s.TokenPos) + 2)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package ast
|
|||
|
||||
import "github.com/d5/tengo/compiler/source"
|
||||
|
||||
// IndexExpr represents an index expression.
|
||||
type IndexExpr struct {
|
||||
Expr Expr
|
||||
LBrack source.Pos
|
||||
|
@ -11,10 +12,12 @@ type IndexExpr struct {
|
|||
|
||||
func (e *IndexExpr) exprNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (e *IndexExpr) Pos() source.Pos {
|
||||
return e.Expr.Pos()
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (e *IndexExpr) End() source.Pos {
|
||||
return e.RBrack + 1
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package ast
|
|||
|
||||
import "github.com/d5/tengo/compiler/source"
|
||||
|
||||
// IntLit represetns an integer literal.
|
||||
type IntLit struct {
|
||||
Value int64
|
||||
ValuePos source.Pos
|
||||
|
@ -10,10 +11,12 @@ type IntLit struct {
|
|||
|
||||
func (e *IntLit) exprNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (e *IntLit) Pos() source.Pos {
|
||||
return e.ValuePos
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (e *IntLit) End() source.Pos {
|
||||
return source.Pos(int(e.ValuePos) + len(e.Literal))
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package ast
|
|||
|
||||
import "github.com/d5/tengo/compiler/source"
|
||||
|
||||
// MapElementLit represents a map element.
|
||||
type MapElementLit struct {
|
||||
Key string
|
||||
KeyPos source.Pos
|
||||
|
@ -11,10 +12,12 @@ type MapElementLit struct {
|
|||
|
||||
func (e *MapElementLit) exprNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (e *MapElementLit) Pos() source.Pos {
|
||||
return e.KeyPos
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (e *MapElementLit) End() source.Pos {
|
||||
return e.Value.End()
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/d5/tengo/compiler/source"
|
||||
)
|
||||
|
||||
// MapLit represents a map literal.
|
||||
type MapLit struct {
|
||||
LBrace source.Pos
|
||||
Elements []*MapElementLit
|
||||
|
@ -14,10 +15,12 @@ type MapLit struct {
|
|||
|
||||
func (e *MapLit) exprNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (e *MapLit) Pos() source.Pos {
|
||||
return e.LBrace
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (e *MapLit) End() source.Pos {
|
||||
return e.RBrace + 1
|
||||
}
|
||||
|
|
|
@ -2,8 +2,12 @@ package ast
|
|||
|
||||
import "github.com/d5/tengo/compiler/source"
|
||||
|
||||
// Node represents a node in the AST.
|
||||
type Node interface {
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
Pos() source.Pos
|
||||
// End returns the position of first character immediately after the node.
|
||||
End() source.Pos
|
||||
// String returns a string representation of the node.
|
||||
String() string
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package ast
|
|||
|
||||
import "github.com/d5/tengo/compiler/source"
|
||||
|
||||
// ParenExpr represents a parenthesis wrapped expression.
|
||||
type ParenExpr struct {
|
||||
Expr Expr
|
||||
LParen source.Pos
|
||||
|
@ -10,10 +11,12 @@ type ParenExpr struct {
|
|||
|
||||
func (e *ParenExpr) exprNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (e *ParenExpr) Pos() source.Pos {
|
||||
return e.LParen
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (e *ParenExpr) End() source.Pos {
|
||||
return e.RParen + 1
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/d5/tengo/compiler/source"
|
||||
)
|
||||
|
||||
// ReturnStmt represents a return statement.
|
||||
type ReturnStmt struct {
|
||||
ReturnPos source.Pos
|
||||
Results []Expr
|
||||
|
@ -13,10 +14,12 @@ type ReturnStmt struct {
|
|||
|
||||
func (s *ReturnStmt) stmtNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (s *ReturnStmt) Pos() source.Pos {
|
||||
return s.ReturnPos
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (s *ReturnStmt) End() source.Pos {
|
||||
if n := len(s.Results); n > 0 {
|
||||
return s.Results[n-1].End()
|
||||
|
|
|
@ -2,6 +2,7 @@ package ast
|
|||
|
||||
import "github.com/d5/tengo/compiler/source"
|
||||
|
||||
// SelectorExpr represents a selector expression.
|
||||
type SelectorExpr struct {
|
||||
Expr Expr
|
||||
Sel Expr
|
||||
|
@ -9,10 +10,12 @@ type SelectorExpr struct {
|
|||
|
||||
func (e *SelectorExpr) exprNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (e *SelectorExpr) Pos() source.Pos {
|
||||
return e.Expr.Pos()
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (e *SelectorExpr) End() source.Pos {
|
||||
return e.Sel.End()
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package ast
|
|||
|
||||
import "github.com/d5/tengo/compiler/source"
|
||||
|
||||
// SliceExpr represents a slice expression.
|
||||
type SliceExpr struct {
|
||||
Expr Expr
|
||||
LBrack source.Pos
|
||||
|
@ -12,10 +13,12 @@ type SliceExpr struct {
|
|||
|
||||
func (e *SliceExpr) exprNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (e *SliceExpr) Pos() source.Pos {
|
||||
return e.Expr.Pos()
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (e *SliceExpr) End() source.Pos {
|
||||
return e.RBrack + 1
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ast
|
||||
|
||||
// Stmt represents a statement in the AST.
|
||||
type Stmt interface {
|
||||
Node
|
||||
stmtNode()
|
||||
|
|
|
@ -2,6 +2,7 @@ package ast
|
|||
|
||||
import "github.com/d5/tengo/compiler/source"
|
||||
|
||||
// StringLit represents a string literal.
|
||||
type StringLit struct {
|
||||
Value string
|
||||
ValuePos source.Pos
|
||||
|
@ -10,10 +11,12 @@ type StringLit struct {
|
|||
|
||||
func (e *StringLit) exprNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (e *StringLit) Pos() source.Pos {
|
||||
return e.ValuePos
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (e *StringLit) End() source.Pos {
|
||||
return source.Pos(int(e.ValuePos) + len(e.Literal))
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"github.com/d5/tengo/compiler/token"
|
||||
)
|
||||
|
||||
// UnaryExpr represents an unary operator expression.
|
||||
type UnaryExpr struct {
|
||||
Expr Expr
|
||||
Token token.Token
|
||||
|
@ -13,10 +14,12 @@ type UnaryExpr struct {
|
|||
|
||||
func (e *UnaryExpr) exprNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (e *UnaryExpr) Pos() source.Pos {
|
||||
return e.Expr.Pos()
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (e *UnaryExpr) End() source.Pos {
|
||||
return e.Expr.End()
|
||||
}
|
||||
|
|
|
@ -2,16 +2,19 @@ package ast
|
|||
|
||||
import "github.com/d5/tengo/compiler/source"
|
||||
|
||||
// UndefinedLit represents an undefined literal.
|
||||
type UndefinedLit struct {
|
||||
TokenPos source.Pos
|
||||
}
|
||||
|
||||
func (e *UndefinedLit) exprNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (e *UndefinedLit) Pos() source.Pos {
|
||||
return e.TokenPos
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (e *UndefinedLit) End() source.Pos {
|
||||
return e.TokenPos + 9 // len(undefined) == 9
|
||||
}
|
||||
|
|
|
@ -7,11 +7,13 @@ import (
|
|||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
// Bytecode is a compiled instructions and constants.
|
||||
type Bytecode struct {
|
||||
Instructions []byte
|
||||
Constants []objects.Object
|
||||
}
|
||||
|
||||
// Decode reads Bytecode data from the reader.
|
||||
func (b *Bytecode) Decode(r io.Reader) error {
|
||||
dec := gob.NewDecoder(r)
|
||||
|
||||
|
@ -26,6 +28,7 @@ func (b *Bytecode) Decode(r io.Reader) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Encode writes Bytecode data to the writer.
|
||||
func (b *Bytecode) Encode(w io.Writer) error {
|
||||
enc := gob.NewEncoder(w)
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package compiler
|
||||
|
||||
// CompilationScope represents a compiled instructions
|
||||
// and the last two instructions that were emitted.
|
||||
type CompilationScope struct {
|
||||
instructions []byte
|
||||
lastInstructions [2]EmittedInstruction
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
// Compiler compiles the AST into a bytecode.
|
||||
type Compiler struct {
|
||||
constants []objects.Object
|
||||
symbolTable *SymbolTable
|
||||
|
@ -21,6 +22,7 @@ type Compiler struct {
|
|||
indent int
|
||||
}
|
||||
|
||||
// NewCompiler creates a Compiler.
|
||||
func NewCompiler(symbolTable *SymbolTable, trace io.Writer) *Compiler {
|
||||
mainScope := CompilationScope{
|
||||
instructions: make([]byte, 0),
|
||||
|
@ -43,6 +45,7 @@ func NewCompiler(symbolTable *SymbolTable, trace io.Writer) *Compiler {
|
|||
}
|
||||
}
|
||||
|
||||
// Compile compiles the AST node.
|
||||
func (c *Compiler) Compile(node ast.Node) error {
|
||||
if c.trace != nil {
|
||||
if node != nil {
|
||||
|
@ -85,11 +88,11 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
}
|
||||
|
||||
if node.Token == token.Less {
|
||||
if err := c.Compile(node.Rhs); err != nil {
|
||||
if err := c.Compile(node.RHS); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.Compile(node.Lhs); err != nil {
|
||||
if err := c.Compile(node.LHS); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -97,10 +100,10 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
|
||||
return nil
|
||||
} else if node.Token == token.LessEq {
|
||||
if err := c.Compile(node.Rhs); err != nil {
|
||||
if err := c.Compile(node.RHS); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Compile(node.Lhs); err != nil {
|
||||
if err := c.Compile(node.LHS); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -109,10 +112,10 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
if err := c.Compile(node.Lhs); err != nil {
|
||||
if err := c.Compile(node.LHS); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Compile(node.Rhs); err != nil {
|
||||
if err := c.Compile(node.RHS); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -258,7 +261,7 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
}
|
||||
case *ast.AssignStmt:
|
||||
|
||||
if err := c.compileAssign(node.Lhs, node.Rhs, node.Token); err != nil {
|
||||
if err := c.compileAssign(node.LHS, node.RHS, node.Token); err != nil {
|
||||
return err
|
||||
}
|
||||
case *ast.Ident:
|
||||
|
@ -414,6 +417,7 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Bytecode returns a compiled bytecode.
|
||||
func (c *Compiler) Bytecode() *Bytecode {
|
||||
return &Bytecode{
|
||||
Instructions: c.currentInstructions(),
|
||||
|
|
|
@ -9,21 +9,21 @@ import (
|
|||
)
|
||||
|
||||
func (c *Compiler) compileAssign(lhs, rhs []ast.Expr, op token.Token) error {
|
||||
numLhs, numRhs := len(lhs), len(rhs)
|
||||
if numLhs < numRhs {
|
||||
numLHS, numRHS := len(lhs), len(rhs)
|
||||
if numLHS < numRHS {
|
||||
// # of LHS must be >= # of RHS
|
||||
return fmt.Errorf("assigntment count error: %d < %d", numLhs, numRhs)
|
||||
return fmt.Errorf("assigntment count error: %d < %d", numLHS, numRHS)
|
||||
}
|
||||
if numLhs > 1 {
|
||||
if numLHS > 1 {
|
||||
// TODO: until we fully implement the tuple assignment
|
||||
return fmt.Errorf("tuple assignment not implemented")
|
||||
}
|
||||
//if numLhs > 1 && op != token.Assign && op != token.Define {
|
||||
//if numLHS > 1 && op != token.Assign && op != token.Define {
|
||||
// return fmt.Errorf("invalid operator for tuple assignment: %s", op.String())
|
||||
//}
|
||||
|
||||
// resolve and compile left-hand side
|
||||
ident, selectors, err := resolveAssignLhs(lhs[0])
|
||||
ident, selectors, err := resolveAssignLHS(lhs[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -124,10 +124,10 @@ func (c *Compiler) compileAssign(lhs, rhs []ast.Expr, op token.Token) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func resolveAssignLhs(expr ast.Expr) (name string, selectors []ast.Expr, err error) {
|
||||
func resolveAssignLHS(expr ast.Expr) (name string, selectors []ast.Expr, err error) {
|
||||
switch term := expr.(type) {
|
||||
case *ast.SelectorExpr:
|
||||
name, selectors, err = resolveAssignLhs(term.Expr)
|
||||
name, selectors, err = resolveAssignLHS(term.Expr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ func resolveAssignLhs(expr ast.Expr) (name string, selectors []ast.Expr, err err
|
|||
|
||||
return
|
||||
case *ast.IndexExpr:
|
||||
name, selectors, err = resolveAssignLhs(term.Expr)
|
||||
name, selectors, err = resolveAssignLHS(term.Expr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
|
||||
func (c *Compiler) compileLogical(node *ast.BinaryExpr) error {
|
||||
// left side term
|
||||
if err := c.Compile(node.Lhs); err != nil {
|
||||
if err := c.Compile(node.LHS); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ func (c *Compiler) compileLogical(node *ast.BinaryExpr) error {
|
|||
}
|
||||
|
||||
// right side term
|
||||
if err := c.Compile(node.Rhs); err != nil {
|
||||
if err := c.Compile(node.RHS); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package compiler
|
||||
|
||||
// Definition represents an Opcode name and
|
||||
// the number of operands.
|
||||
type Definition struct {
|
||||
Name string
|
||||
Operands []int
|
||||
|
@ -58,6 +60,7 @@ var definitions = map[Opcode]*Definition{
|
|||
OpIteratorValue: {Name: "ITVAL", Operands: []int{}},
|
||||
}
|
||||
|
||||
// Lookup returns a Definition of a given opcode.
|
||||
func Lookup(opcode Opcode) (def *Definition, ok bool) {
|
||||
def, ok = definitions[opcode]
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package compiler
|
||||
|
||||
// EmittedInstruction represents an opcode
|
||||
// with its emitted position.
|
||||
type EmittedInstruction struct {
|
||||
Opcode Opcode
|
||||
Position int
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
)
|
||||
|
||||
// MakeInstruction returns a bytecode for an opcode and the operands.
|
||||
func MakeInstruction(opcode Opcode, operands ...int) []byte {
|
||||
def, ok := Lookup(opcode)
|
||||
if !ok {
|
||||
|
@ -35,6 +36,8 @@ func MakeInstruction(opcode Opcode, operands ...int) []byte {
|
|||
return instruction
|
||||
}
|
||||
|
||||
// FormatInstructions returns string representation of
|
||||
// bytecode instructions.
|
||||
func FormatInstructions(b []byte, posOffset int) []string {
|
||||
var out []string
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package compiler
|
||||
|
||||
// Loop represents a loop construct that
|
||||
// the compiler uses to track the current loop.
|
||||
type Loop struct {
|
||||
Continues []int
|
||||
Breaks []int
|
||||
|
|
|
@ -1,56 +1,58 @@
|
|||
package compiler
|
||||
|
||||
// Opcode represents a single byte operation code.
|
||||
type Opcode byte
|
||||
|
||||
// List of opcodes
|
||||
const (
|
||||
OpConstant Opcode = iota
|
||||
OpAdd
|
||||
OpSub
|
||||
OpMul
|
||||
OpDiv
|
||||
OpRem
|
||||
OpBAnd
|
||||
OpBOr
|
||||
OpBXor
|
||||
OpBShiftLeft
|
||||
OpBShiftRight
|
||||
OpBAndNot
|
||||
OpBComplement
|
||||
OpPop
|
||||
OpTrue
|
||||
OpFalse
|
||||
OpEqual
|
||||
OpNotEqual
|
||||
OpGreaterThan
|
||||
OpGreaterThanEqual
|
||||
OpMinus
|
||||
OpLNot
|
||||
OpJumpFalsy
|
||||
OpAndJump
|
||||
OpOrJump
|
||||
OpJump
|
||||
OpNull
|
||||
OpArray
|
||||
OpMap
|
||||
OpIndex
|
||||
OpSliceIndex
|
||||
OpCall
|
||||
OpReturn
|
||||
OpReturnValue
|
||||
OpGetGlobal
|
||||
OpSetGlobal
|
||||
OpSetSelGlobal
|
||||
OpGetLocal
|
||||
OpSetLocal
|
||||
OpDefineLocal
|
||||
OpSetSelLocal
|
||||
OpGetFree
|
||||
OpSetFree
|
||||
OpSetSelFree
|
||||
OpGetBuiltin
|
||||
OpClosure
|
||||
OpIteratorInit
|
||||
OpIteratorNext
|
||||
OpIteratorKey
|
||||
OpIteratorValue
|
||||
OpConstant Opcode = iota // Load constant
|
||||
OpAdd // Add
|
||||
OpSub // Sub
|
||||
OpMul // Multiply
|
||||
OpDiv // Divide
|
||||
OpRem // Remainder
|
||||
OpBAnd // bitwise AND
|
||||
OpBOr // bitwise OR
|
||||
OpBXor // bitwise XOR
|
||||
OpBShiftLeft // bitwise shift left
|
||||
OpBShiftRight // bitwise shift right
|
||||
OpBAndNot // bitwise AND NOT
|
||||
OpBComplement // bitwise complement
|
||||
OpPop // Pop
|
||||
OpTrue // Push true
|
||||
OpFalse // Push false
|
||||
OpEqual // Equal ==
|
||||
OpNotEqual // Not equal !=
|
||||
OpGreaterThan // Greater than >=
|
||||
OpGreaterThanEqual // Greater than or equal to >=
|
||||
OpMinus // Minus -
|
||||
OpLNot // Logical not !
|
||||
OpJumpFalsy // Jump if falsy
|
||||
OpAndJump // Logical AND jump
|
||||
OpOrJump // Logical OR jump
|
||||
OpJump // Jump
|
||||
OpNull // Push null
|
||||
OpArray // Array literal
|
||||
OpMap // Map literal
|
||||
OpIndex // Index operation
|
||||
OpSliceIndex // Slice operation
|
||||
OpCall // Call function
|
||||
OpReturn // Return
|
||||
OpReturnValue // Return value
|
||||
OpGetGlobal // Get global variable
|
||||
OpSetGlobal // Set global variable
|
||||
OpSetSelGlobal // Set global variable using selectors
|
||||
OpGetLocal // Get local variable
|
||||
OpSetLocal // Set local variable
|
||||
OpDefineLocal // Define local variable
|
||||
OpSetSelLocal // Set local variable using selectors
|
||||
OpGetFree // Get free variables
|
||||
OpSetFree // Set free variables
|
||||
OpSetSelFree // Set free variables using selectors
|
||||
OpGetBuiltin // Get builtin function
|
||||
OpClosure // Push closure
|
||||
OpIteratorInit // Iterator init
|
||||
OpIteratorNext // Iterator next
|
||||
OpIteratorKey // Iterator key
|
||||
OpIteratorValue // Iterator value
|
||||
)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package compiler
|
||||
|
||||
// ReadOperands reads operands from the bytecode.
|
||||
func ReadOperands(def *Definition, ins []byte) (operands []int, offset int) {
|
||||
for _, width := range def.Operands {
|
||||
switch width {
|
||||
|
@ -15,10 +16,12 @@ func ReadOperands(def *Definition, ins []byte) (operands []int, offset int) {
|
|||
return
|
||||
}
|
||||
|
||||
// ReadUint16 reads uint16 from the byte slice.
|
||||
func ReadUint16(b []byte) uint16 {
|
||||
return uint16(b[1]) | uint16(b[0])<<8
|
||||
}
|
||||
|
||||
// ReadUint8 reads uint8 from the byte slice.
|
||||
func ReadUint8(b []byte) uint8 {
|
||||
return uint8(b[0])
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package parser
|
|||
|
||||
import "github.com/d5/tengo/compiler/source"
|
||||
|
||||
// Error represents a parser error.
|
||||
type Error struct {
|
||||
Pos source.FilePos
|
||||
Msg string
|
||||
|
|
|
@ -7,16 +7,20 @@ import (
|
|||
"github.com/d5/tengo/compiler/source"
|
||||
)
|
||||
|
||||
// ErrorList is a collection of parser errors.
|
||||
type ErrorList []*Error
|
||||
|
||||
// Add adds a new parser error to the collection.
|
||||
func (p *ErrorList) Add(pos source.FilePos, msg string) {
|
||||
*p = append(*p, &Error{pos, msg})
|
||||
}
|
||||
|
||||
// Reset clears the collection.
|
||||
func (p *ErrorList) Reset() {
|
||||
*p = (*p)[0:0]
|
||||
}
|
||||
|
||||
// Len returns the number of elements in the collection.
|
||||
func (p ErrorList) Len() int {
|
||||
return len(p)
|
||||
}
|
||||
|
@ -44,27 +48,11 @@ func (p ErrorList) Less(i, j int) bool {
|
|||
return p[i].Msg < p[j].Msg
|
||||
}
|
||||
|
||||
// Sort sorts the collection.
|
||||
func (p ErrorList) Sort() {
|
||||
sort.Sort(p)
|
||||
}
|
||||
|
||||
func (p *ErrorList) RemoveMultiples() {
|
||||
sort.Sort(p)
|
||||
|
||||
var last source.FilePos // initial last.Line is != any legal error line
|
||||
|
||||
i := 0
|
||||
for _, e := range *p {
|
||||
if e.Pos.Filename != last.Filename || e.Pos.Line != last.Line {
|
||||
last = e.Pos
|
||||
(*p)[i] = e
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
*p = (*p)[0:i]
|
||||
}
|
||||
|
||||
func (p ErrorList) Error() string {
|
||||
switch len(p) {
|
||||
case 0:
|
||||
|
@ -75,6 +63,7 @@ func (p ErrorList) Error() string {
|
|||
return fmt.Sprintf("%s (and %d more errors)", p[0], len(p)-1)
|
||||
}
|
||||
|
||||
// Err returns an error.
|
||||
func (p ErrorList) Err() error {
|
||||
if len(p) == 0 {
|
||||
return nil
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/d5/tengo/compiler/source"
|
||||
)
|
||||
|
||||
// ParseFile parses a file with a given src.
|
||||
func ParseFile(file *source.File, src []byte, trace io.Writer) (res *ast.File, err error) {
|
||||
p := NewParser(file, src, trace)
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
|
||||
type bailout struct{}
|
||||
|
||||
// Parser parses the Tengo source files.
|
||||
type Parser struct {
|
||||
file *source.File
|
||||
errors ErrorList
|
||||
|
@ -38,6 +39,7 @@ type Parser struct {
|
|||
traceOut io.Writer
|
||||
}
|
||||
|
||||
// NewParser creates a Parser.
|
||||
func NewParser(file *source.File, src []byte, trace io.Writer) *Parser {
|
||||
p := &Parser{
|
||||
file: file,
|
||||
|
@ -54,6 +56,7 @@ func NewParser(file *source.File, src []byte, trace io.Writer) *Parser {
|
|||
return p
|
||||
}
|
||||
|
||||
// ParseFile parses the source and returns an AST file unit.
|
||||
func (p *Parser) ParseFile() (*ast.File, error) {
|
||||
if p.trace {
|
||||
defer un(trace(p, "File"))
|
||||
|
@ -99,8 +102,8 @@ func (p *Parser) parseBinaryExpr(prec1 int) ast.Expr {
|
|||
|
||||
y := p.parseBinaryExpr(prec + 1)
|
||||
x = &ast.BinaryExpr{
|
||||
Lhs: x,
|
||||
Rhs: y,
|
||||
LHS: x,
|
||||
RHS: y,
|
||||
Token: op,
|
||||
TokenPos: pos,
|
||||
}
|
||||
|
@ -785,8 +788,8 @@ func (p *Parser) parseSimpleStmt(forIn bool) ast.Stmt {
|
|||
y := p.parseExprList()
|
||||
|
||||
return &ast.AssignStmt{
|
||||
Lhs: x,
|
||||
Rhs: y,
|
||||
LHS: x,
|
||||
RHS: y,
|
||||
Token: tok,
|
||||
TokenPos: pos,
|
||||
}
|
||||
|
@ -844,8 +847,8 @@ func (p *Parser) parseSimpleStmt(forIn bool) ast.Stmt {
|
|||
y := p.parseExpr()
|
||||
|
||||
return &ast.AssignStmt{
|
||||
Lhs: []ast.Expr{x[0]},
|
||||
Rhs: []ast.Expr{y},
|
||||
LHS: []ast.Expr{x[0]},
|
||||
RHS: []ast.Expr{y},
|
||||
Token: tok,
|
||||
TokenPos: pos,
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ func exprStmt(x ast.Expr) *ast.ExprStmt {
|
|||
}
|
||||
|
||||
func assignStmt(lhs, rhs []ast.Expr, token token.Token, pos source.Pos) *ast.AssignStmt {
|
||||
return &ast.AssignStmt{Lhs: lhs, Rhs: rhs, Token: token, TokenPos: pos}
|
||||
return &ast.AssignStmt{LHS: lhs, RHS: rhs, Token: token, TokenPos: pos}
|
||||
}
|
||||
|
||||
func emptyStmt(implicit bool, pos source.Pos) *ast.EmptyStmt {
|
||||
|
@ -184,7 +184,7 @@ func identList(opening, closing source.Pos, list ...*ast.Ident) *ast.IdentList {
|
|||
}
|
||||
|
||||
func binaryExpr(x, y ast.Expr, op token.Token, pos source.Pos) *ast.BinaryExpr {
|
||||
return &ast.BinaryExpr{Lhs: x, Rhs: y, Token: op, TokenPos: pos}
|
||||
return &ast.BinaryExpr{LHS: x, RHS: y, Token: op, TokenPos: pos}
|
||||
}
|
||||
|
||||
func unaryExpr(x ast.Expr, op token.Token, pos source.Pos) *ast.UnaryExpr {
|
||||
|
@ -273,8 +273,8 @@ func equalStmt(t *testing.T, expected, actual ast.Stmt) bool {
|
|||
assert.Equal(t, expected.RBrace, actual.(*ast.BlockStmt).RBrace) &&
|
||||
equalStmts(t, expected.Stmts, actual.(*ast.BlockStmt).Stmts)
|
||||
case *ast.AssignStmt:
|
||||
return equalExprs(t, expected.Lhs, actual.(*ast.AssignStmt).Lhs) &&
|
||||
equalExprs(t, expected.Rhs, actual.(*ast.AssignStmt).Rhs) &&
|
||||
return equalExprs(t, expected.LHS, actual.(*ast.AssignStmt).LHS) &&
|
||||
equalExprs(t, expected.RHS, actual.(*ast.AssignStmt).RHS) &&
|
||||
assert.Equal(t, int(expected.Token), int(actual.(*ast.AssignStmt).Token)) &&
|
||||
assert.Equal(t, int(expected.TokenPos), int(actual.(*ast.AssignStmt).TokenPos))
|
||||
case *ast.IfStmt:
|
||||
|
@ -350,8 +350,8 @@ func equalExpr(t *testing.T, expected, actual ast.Expr) bool {
|
|||
assert.Equal(t, expected.RBrace, actual.(*ast.MapLit).RBrace) &&
|
||||
equalMapElements(t, expected.Elements, actual.(*ast.MapLit).Elements)
|
||||
case *ast.BinaryExpr:
|
||||
return equalExpr(t, expected.Lhs, actual.(*ast.BinaryExpr).Lhs) &&
|
||||
equalExpr(t, expected.Rhs, actual.(*ast.BinaryExpr).Rhs) &&
|
||||
return equalExpr(t, expected.LHS, actual.(*ast.BinaryExpr).LHS) &&
|
||||
equalExpr(t, expected.RHS, actual.(*ast.BinaryExpr).RHS) &&
|
||||
assert.Equal(t, expected.Token, actual.(*ast.BinaryExpr).Token) &&
|
||||
assert.Equal(t, expected.TokenPos, actual.(*ast.BinaryExpr).TokenPos)
|
||||
case *ast.UnaryExpr:
|
||||
|
|
|
@ -2,4 +2,5 @@ package scanner
|
|||
|
||||
import "github.com/d5/tengo/compiler/source"
|
||||
|
||||
// ErrorHandler is an error handler for the scanner.
|
||||
type ErrorHandler func(pos source.FilePos, msg string)
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package scanner
|
||||
|
||||
// Mode represents a scanner mode.
|
||||
type Mode int
|
||||
|
||||
// List of scanner modes.
|
||||
const (
|
||||
ScanComments Mode = 1 << iota
|
||||
DontInsertSemis
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
// byte order mark
|
||||
const bom = 0xFEFF
|
||||
|
||||
// Scanner reads the Tengo source text.
|
||||
type Scanner struct {
|
||||
file *source.File // source file handle
|
||||
src []byte // source
|
||||
|
@ -35,6 +36,7 @@ type Scanner struct {
|
|||
mode Mode
|
||||
}
|
||||
|
||||
// NewScanner creates a Scanner.
|
||||
func NewScanner(file *source.File, src []byte, errorHandler ErrorHandler, mode Mode) *Scanner {
|
||||
if file.Size() != len(src) {
|
||||
panic(fmt.Sprintf("file size (%d) does not match src len (%d)", file.Size(), len(src)))
|
||||
|
@ -56,10 +58,12 @@ func NewScanner(file *source.File, src []byte, errorHandler ErrorHandler, mode M
|
|||
return s
|
||||
}
|
||||
|
||||
// ErrorCount returns the number of errors.
|
||||
func (s *Scanner) ErrorCount() int {
|
||||
return s.errorCount
|
||||
}
|
||||
|
||||
// Scan returns a token, token literal and its position.
|
||||
func (s *Scanner) Scan() (tok token.Token, literal string, pos source.Pos) {
|
||||
s.skipWhitespace()
|
||||
|
||||
|
@ -593,6 +597,7 @@ func (s *Scanner) scanRawString() string {
|
|||
return string(lit)
|
||||
}
|
||||
|
||||
// StripCR removes carriage return characters.
|
||||
func StripCR(b []byte, comment bool) []byte {
|
||||
c := make([]byte, len(b))
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"sync"
|
||||
)
|
||||
|
||||
// File represents a source file.
|
||||
type File struct {
|
||||
set *FileSet
|
||||
name string // file name as provided to AddFile
|
||||
|
@ -13,18 +14,22 @@ type File struct {
|
|||
lines []int // lines contains the offset of the first character for each line (the first entry is always 0)
|
||||
}
|
||||
|
||||
// Name returns the file name.
|
||||
func (f *File) Name() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
// Base returns the base position of the file.
|
||||
func (f *File) Base() int {
|
||||
return f.base
|
||||
}
|
||||
|
||||
// Size returns the size of the file.
|
||||
func (f *File) Size() int {
|
||||
return f.size
|
||||
}
|
||||
|
||||
// LineCount returns the current number of lines.
|
||||
func (f *File) LineCount() int {
|
||||
f.mutex.Lock()
|
||||
n := len(f.lines)
|
||||
|
@ -33,6 +38,7 @@ func (f *File) LineCount() int {
|
|||
return n
|
||||
}
|
||||
|
||||
// AddLine adds a new line.
|
||||
func (f *File) AddLine(offset int) {
|
||||
f.mutex.Lock()
|
||||
if i := len(f.lines); (i == 0 || f.lines[i-1] < offset) && offset < f.size {
|
||||
|
@ -41,6 +47,7 @@ func (f *File) AddLine(offset int) {
|
|||
f.mutex.Unlock()
|
||||
}
|
||||
|
||||
// LineStart returns the position of the first character in the line.
|
||||
func (f *File) LineStart(line int) Pos {
|
||||
if line < 1 {
|
||||
panic("illegal line number (line numbering starts at 1)")
|
||||
|
@ -56,6 +63,7 @@ func (f *File) LineStart(line int) Pos {
|
|||
return Pos(f.base + f.lines[line-1])
|
||||
}
|
||||
|
||||
// FileSetPos returns the position in the file set.
|
||||
func (f *File) FileSetPos(offset int) Pos {
|
||||
if offset > f.size {
|
||||
panic("illegal file offset")
|
||||
|
@ -64,6 +72,7 @@ func (f *File) FileSetPos(offset int) Pos {
|
|||
return Pos(f.base + offset)
|
||||
}
|
||||
|
||||
// Offset translates the file set position into the file offset.
|
||||
func (f *File) Offset(p Pos) int {
|
||||
if int(p) < f.base || int(p) > f.base+f.size {
|
||||
panic("illegal Pos value")
|
||||
|
@ -72,10 +81,7 @@ func (f *File) Offset(p Pos) int {
|
|||
return int(p) - f.base
|
||||
}
|
||||
|
||||
func (f *File) Line(p Pos) int {
|
||||
return f.Position(p).Line
|
||||
}
|
||||
|
||||
// Position translates the file set position into the file position.
|
||||
func (f *File) Position(p Pos) (pos FilePos) {
|
||||
if p != NoPos {
|
||||
if int(p) < f.base || int(p) > f.base+f.size {
|
||||
|
|
|
@ -2,6 +2,7 @@ package source
|
|||
|
||||
import "fmt"
|
||||
|
||||
// FilePos represents a position information in the file.
|
||||
type FilePos struct {
|
||||
Filename string // filename, if any
|
||||
Offset int // offset, starting at 0
|
||||
|
@ -9,6 +10,7 @@ type FilePos struct {
|
|||
Column int // column number, starting at 1 (byte count)
|
||||
}
|
||||
|
||||
// IsValid returns true if the position is valid.
|
||||
func (p FilePos) IsValid() bool {
|
||||
return p.Line > 0
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"sync"
|
||||
)
|
||||
|
||||
// FileSet represents a set of source files.
|
||||
type FileSet struct {
|
||||
mutex sync.RWMutex // protects the file set
|
||||
base int // base offset for the next file
|
||||
|
@ -12,12 +13,14 @@ type FileSet struct {
|
|||
last *File // cache of last file looked up
|
||||
}
|
||||
|
||||
// NewFileSet creates a new file set.
|
||||
func NewFileSet() *FileSet {
|
||||
return &FileSet{
|
||||
base: 1, // 0 == NoPos
|
||||
}
|
||||
}
|
||||
|
||||
// Base returns the current base position of the file set.
|
||||
func (s *FileSet) Base() int {
|
||||
s.mutex.RLock()
|
||||
b := s.base
|
||||
|
@ -26,6 +29,7 @@ func (s *FileSet) Base() int {
|
|||
return b
|
||||
}
|
||||
|
||||
// AddFile adds a new file in the file set.
|
||||
func (s *FileSet) AddFile(filename string, base, size int) *File {
|
||||
s.mutex.Lock()
|
||||
defer s.mutex.Unlock()
|
||||
|
@ -70,7 +74,7 @@ func (s *FileSet) File(p Pos) (f *File) {
|
|||
return
|
||||
}
|
||||
|
||||
// PositionFor converts a Pos p in the fileset into a FilePos value.
|
||||
// Position converts a Pos p in the fileset into a FilePos value.
|
||||
func (s *FileSet) Position(p Pos) (pos FilePos) {
|
||||
if p != NoPos {
|
||||
if f := s.file(p); f != nil {
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
package source
|
||||
|
||||
// Pos represents a position in the file set.
|
||||
type Pos int
|
||||
|
||||
// NoPos represents an invalid position.
|
||||
const NoPos Pos = 0
|
||||
|
||||
// IsValid returns true if the position is valid.
|
||||
func (p Pos) IsValid() bool {
|
||||
return p != NoPos
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package compiler
|
||||
|
||||
// Symbol represents a symbol in the symbol table.
|
||||
type Symbol struct {
|
||||
Name string
|
||||
Scope SymbolScope
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package compiler
|
||||
|
||||
// SymbolScope represents a symbol scope.
|
||||
type SymbolScope string
|
||||
|
||||
// List of symbol scopes
|
||||
const (
|
||||
ScopeGlobal SymbolScope = "GLOBAL"
|
||||
ScopeLocal = "LOCAL"
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package compiler
|
||||
|
||||
// SymbolTable represents a symbol table.
|
||||
type SymbolTable struct {
|
||||
parent *SymbolTable
|
||||
block bool
|
||||
|
@ -9,12 +10,14 @@ type SymbolTable struct {
|
|||
freeSymbols []Symbol
|
||||
}
|
||||
|
||||
// NewSymbolTable creates a SymbolTable.
|
||||
func NewSymbolTable() *SymbolTable {
|
||||
return &SymbolTable{
|
||||
store: make(map[string]Symbol),
|
||||
}
|
||||
}
|
||||
|
||||
// Define adds a new symbol in the current scope.
|
||||
func (t *SymbolTable) Define(name string) Symbol {
|
||||
symbol := Symbol{Name: name, Index: t.nextIndex()}
|
||||
t.numDefinition++
|
||||
|
@ -32,6 +35,7 @@ func (t *SymbolTable) Define(name string) Symbol {
|
|||
return symbol
|
||||
}
|
||||
|
||||
// DefineBuiltin adds a symbol for builtin function.
|
||||
func (t *SymbolTable) DefineBuiltin(index int, name string) Symbol {
|
||||
symbol := Symbol{
|
||||
Name: name,
|
||||
|
@ -44,6 +48,7 @@ func (t *SymbolTable) DefineBuiltin(index int, name string) Symbol {
|
|||
return symbol
|
||||
}
|
||||
|
||||
// Resolve resolves a symbol with a given name.
|
||||
func (t *SymbolTable) Resolve(name string) (symbol Symbol, depth int, ok bool) {
|
||||
symbol, ok = t.store[name]
|
||||
if !ok && t.parent != nil {
|
||||
|
@ -53,7 +58,7 @@ func (t *SymbolTable) Resolve(name string) (symbol Symbol, depth int, ok bool) {
|
|||
}
|
||||
|
||||
if !t.block {
|
||||
depth += 1
|
||||
depth++
|
||||
}
|
||||
|
||||
// if symbol is defined in parent table and if it's not global/builtin
|
||||
|
@ -68,6 +73,7 @@ func (t *SymbolTable) Resolve(name string) (symbol Symbol, depth int, ok bool) {
|
|||
return
|
||||
}
|
||||
|
||||
// Fork creates a new symbol table for a new scope.
|
||||
func (t *SymbolTable) Fork(block bool) *SymbolTable {
|
||||
return &SymbolTable{
|
||||
store: make(map[string]Symbol),
|
||||
|
@ -76,6 +82,7 @@ func (t *SymbolTable) Fork(block bool) *SymbolTable {
|
|||
}
|
||||
}
|
||||
|
||||
// Parent returns the outer scope of the current symbol table.
|
||||
func (t *SymbolTable) Parent(skipBlock bool) *SymbolTable {
|
||||
if skipBlock && t.block {
|
||||
return t.parent.Parent(skipBlock)
|
||||
|
@ -84,14 +91,17 @@ func (t *SymbolTable) Parent(skipBlock bool) *SymbolTable {
|
|||
return t.parent
|
||||
}
|
||||
|
||||
// MaxSymbols returns the total number of symbols defined in the scope.
|
||||
func (t *SymbolTable) MaxSymbols() int {
|
||||
return t.maxDefinition
|
||||
}
|
||||
|
||||
// FreeSymbols returns free symbols for the scope.
|
||||
func (t *SymbolTable) FreeSymbols() []Symbol {
|
||||
return t.freeSymbols
|
||||
}
|
||||
|
||||
// Names returns the name of all the symbols.
|
||||
func (t *SymbolTable) Names() []string {
|
||||
var names []string
|
||||
for name := range t.store {
|
||||
|
|
|
@ -9,6 +9,7 @@ func init() {
|
|||
}
|
||||
}
|
||||
|
||||
// Lookup returns corresponding keyword if ident is a keyword.
|
||||
func Lookup(ident string) Token {
|
||||
if tok, isKeyword := keywords[ident]; isKeyword {
|
||||
return tok
|
||||
|
|
|
@ -2,8 +2,10 @@ package token
|
|||
|
||||
import "strconv"
|
||||
|
||||
// Token represents a token.
|
||||
type Token int
|
||||
|
||||
// List of tokens
|
||||
const (
|
||||
Illegal Token = iota
|
||||
EOF
|
||||
|
@ -168,10 +170,10 @@ func (tok Token) String() string {
|
|||
return s
|
||||
}
|
||||
|
||||
const (
|
||||
LowestPrec = 0 // non-operators
|
||||
)
|
||||
// LowestPrec represents lowest operator precedence.
|
||||
const LowestPrec = 0
|
||||
|
||||
// Precedence returns the precedence for the operator token.
|
||||
func (tok Token) Precedence() int {
|
||||
switch tok {
|
||||
case LOr:
|
||||
|
@ -188,14 +190,17 @@ func (tok Token) Precedence() int {
|
|||
return LowestPrec
|
||||
}
|
||||
|
||||
// IsLiteral returns true if the token is a literal.
|
||||
func (tok Token) IsLiteral() bool {
|
||||
return _literalBeg < tok && tok < _literalEnd
|
||||
}
|
||||
|
||||
// IsOperator returns true if the token is an operator.
|
||||
func (tok Token) IsOperator() bool {
|
||||
return _operatorBeg < tok && tok < _operatorEnd
|
||||
}
|
||||
|
||||
// IsKeyword returns true if the token is a keyword.
|
||||
func (tok Token) IsKeyword() bool {
|
||||
return _keywordBeg < tok && tok < _keywordEnd
|
||||
}
|
||||
|
|
|
@ -8,10 +8,12 @@ import (
|
|||
"github.com/d5/tengo/compiler/token"
|
||||
)
|
||||
|
||||
// Array represents an array of objects.
|
||||
type Array struct {
|
||||
Value []Object
|
||||
}
|
||||
|
||||
// TypeName returns the name of the type.
|
||||
func (o *Array) TypeName() string {
|
||||
return "array"
|
||||
}
|
||||
|
@ -25,6 +27,8 @@ func (o *Array) String() string {
|
|||
return fmt.Sprintf("[%s]", strings.Join(elements, ", "))
|
||||
}
|
||||
|
||||
// BinaryOp returns another object that is the result of
|
||||
// a given binary operator and a right-hand side object.
|
||||
func (o *Array) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||
if rhs, ok := rhs.(*Array); ok {
|
||||
switch op {
|
||||
|
@ -39,6 +43,7 @@ func (o *Array) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
|||
return nil, ErrInvalidOperator
|
||||
}
|
||||
|
||||
// Copy returns a copy of the type.
|
||||
func (o *Array) Copy() Object {
|
||||
var c []Object
|
||||
for _, elem := range o.Value {
|
||||
|
@ -48,10 +53,13 @@ func (o *Array) Copy() Object {
|
|||
return &Array{Value: c}
|
||||
}
|
||||
|
||||
// IsFalsy returns true if the value of the type is falsy.
|
||||
func (o *Array) IsFalsy() bool {
|
||||
return len(o.Value) == 0
|
||||
}
|
||||
|
||||
// Equals returns true if the value of the type
|
||||
// is equal to the value of another object.
|
||||
func (o *Array) Equals(x Object) bool {
|
||||
t, ok := x.(*Array)
|
||||
if !ok {
|
||||
|
@ -71,6 +79,7 @@ func (o *Array) Equals(x Object) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// Get returns an element at a given index.
|
||||
func (o *Array) Get(index int) (Object, error) {
|
||||
if index < 0 || index >= len(o.Value) {
|
||||
return nil, errors.New("array index out of bounds")
|
||||
|
@ -79,6 +88,7 @@ func (o *Array) Get(index int) (Object, error) {
|
|||
return o.Value[index], nil
|
||||
}
|
||||
|
||||
// Set sets an element at a given index.
|
||||
func (o *Array) Set(index int, value Object) error {
|
||||
if index < 0 || index >= len(o.Value) {
|
||||
return errors.New("array index out of bounds")
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"github.com/d5/tengo/compiler/token"
|
||||
)
|
||||
|
||||
// Bool represents a boolean value.
|
||||
type Bool struct {
|
||||
Value bool
|
||||
}
|
||||
|
@ -16,23 +17,30 @@ func (o *Bool) String() string {
|
|||
return "false"
|
||||
}
|
||||
|
||||
// TypeName returns the name of the type.
|
||||
func (o *Bool) TypeName() string {
|
||||
return "bool"
|
||||
}
|
||||
|
||||
// BinaryOp returns another object that is the result of
|
||||
// a given binary operator and a right-hand side object.
|
||||
func (o *Bool) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||
return nil, ErrInvalidOperator
|
||||
}
|
||||
|
||||
// Copy returns a copy of the type.
|
||||
func (o *Bool) Copy() Object {
|
||||
v := Bool(*o)
|
||||
return &v
|
||||
}
|
||||
|
||||
// IsFalsy returns true if the value of the type is falsy.
|
||||
func (o *Bool) IsFalsy() bool {
|
||||
return !o.Value
|
||||
}
|
||||
|
||||
// Equals returns true if the value of the type
|
||||
// is equal to the value of another object.
|
||||
func (o *Bool) Equals(x Object) bool {
|
||||
t, ok := x.(*Bool)
|
||||
if !ok {
|
||||
|
|
|
@ -2,8 +2,10 @@ package objects
|
|||
|
||||
import "github.com/d5/tengo/compiler/token"
|
||||
|
||||
// Break represents a break statement.
|
||||
type Break struct{}
|
||||
|
||||
// TypeName returns the name of the type.
|
||||
func (o *Break) TypeName() string {
|
||||
return "break"
|
||||
}
|
||||
|
@ -12,18 +14,24 @@ func (o *Break) String() string {
|
|||
return "<break>"
|
||||
}
|
||||
|
||||
// BinaryOp returns another object that is the result of
|
||||
// a given binary operator and a right-hand side object.
|
||||
func (o *Break) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||
return nil, ErrInvalidOperator
|
||||
}
|
||||
|
||||
// Copy returns a copy of the type.
|
||||
func (o *Break) Copy() Object {
|
||||
return &Break{}
|
||||
}
|
||||
|
||||
// IsFalsy returns true if the value of the type is falsy.
|
||||
func (o *Break) IsFalsy() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Equals returns true if the value of the type
|
||||
// is equal to the value of another object.
|
||||
func (o *Break) Equals(x Object) bool {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -4,10 +4,12 @@ import (
|
|||
"github.com/d5/tengo/compiler/token"
|
||||
)
|
||||
|
||||
// BuiltinFunction represents a builtin function.
|
||||
type BuiltinFunction struct {
|
||||
Value BuiltinFunc
|
||||
}
|
||||
|
||||
// TypeName returns the name of the type.
|
||||
func (o *BuiltinFunction) TypeName() string {
|
||||
return "builtin-function"
|
||||
}
|
||||
|
@ -16,22 +18,29 @@ func (o *BuiltinFunction) String() string {
|
|||
return "<builtin-function>"
|
||||
}
|
||||
|
||||
// BinaryOp returns another object that is the result of
|
||||
// a given binary operator and a right-hand side object.
|
||||
func (o *BuiltinFunction) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||
return nil, ErrInvalidOperator
|
||||
}
|
||||
|
||||
// Copy returns a copy of the type.
|
||||
func (o *BuiltinFunction) Copy() Object {
|
||||
return &BuiltinFunction{Value: o.Value}
|
||||
}
|
||||
|
||||
// IsFalsy returns true if the value of the type is falsy.
|
||||
func (o *BuiltinFunction) IsFalsy() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Equals returns true if the value of the type
|
||||
// is equal to the value of another object.
|
||||
func (o *BuiltinFunction) Equals(x Object) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Call executes a builtin function.
|
||||
func (o *BuiltinFunction) Call(args ...Object) (Object, error) {
|
||||
return o.Value(args...)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package objects
|
||||
|
||||
// BuiltinFunc is a function signature for the builtin functions.
|
||||
type BuiltinFunc func(args ...Object) (ret Object, err error)
|
||||
|
||||
// Builtins contains all known builtin functions.
|
||||
var Builtins = []struct {
|
||||
Name string
|
||||
Func BuiltinFunc
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package objects
|
||||
|
||||
// Callable repesents an object that can be called like a function.
|
||||
type Callable interface {
|
||||
// Call should take an arbitrary number of arguments
|
||||
// and returns a return value and/or an error,
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"github.com/d5/tengo/compiler/token"
|
||||
)
|
||||
|
||||
// Char represents a character value.
|
||||
type Char struct {
|
||||
Value rune
|
||||
}
|
||||
|
@ -12,22 +13,29 @@ func (o *Char) String() string {
|
|||
return string(o.Value)
|
||||
}
|
||||
|
||||
// TypeName returns the name of the type.
|
||||
func (o *Char) TypeName() string {
|
||||
return "char"
|
||||
}
|
||||
|
||||
// BinaryOp returns another object that is the result of
|
||||
// a given binary operator and a right-hand side object.
|
||||
func (o *Char) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||
return nil, ErrInvalidOperator
|
||||
}
|
||||
|
||||
// Copy returns a copy of the type.
|
||||
func (o *Char) Copy() Object {
|
||||
return &Char{Value: o.Value}
|
||||
}
|
||||
|
||||
// IsFalsy returns true if the value of the type is falsy.
|
||||
func (o *Char) IsFalsy() bool {
|
||||
return o.Value == 0
|
||||
}
|
||||
|
||||
// Equals returns true if the value of the type
|
||||
// is equal to the value of another object.
|
||||
func (o *Char) Equals(x Object) bool {
|
||||
t, ok := x.(*Char)
|
||||
if !ok {
|
||||
|
|
|
@ -4,11 +4,13 @@ import (
|
|||
"github.com/d5/tengo/compiler/token"
|
||||
)
|
||||
|
||||
// Closure represents a function closure.
|
||||
type Closure struct {
|
||||
Fn *CompiledFunction
|
||||
Free []*Object
|
||||
}
|
||||
|
||||
// TypeName returns the name of the type.
|
||||
func (o *Closure) TypeName() string {
|
||||
return "closure"
|
||||
}
|
||||
|
@ -17,10 +19,13 @@ func (o *Closure) String() string {
|
|||
return "<closure>"
|
||||
}
|
||||
|
||||
// BinaryOp returns another object that is the result of
|
||||
// a given binary operator and a right-hand side object.
|
||||
func (o *Closure) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||
return nil, ErrInvalidOperator
|
||||
}
|
||||
|
||||
// Copy returns a copy of the type.
|
||||
func (o *Closure) Copy() Object {
|
||||
return &Closure{
|
||||
Fn: o.Fn.Copy().(*CompiledFunction),
|
||||
|
@ -28,10 +33,13 @@ func (o *Closure) Copy() Object {
|
|||
}
|
||||
}
|
||||
|
||||
// IsFalsy returns true if the value of the type is falsy.
|
||||
func (o *Closure) IsFalsy() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Equals returns true if the value of the type
|
||||
// is equal to the value of another object.
|
||||
func (o *Closure) Equals(x Object) bool {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -4,12 +4,14 @@ import (
|
|||
"github.com/d5/tengo/compiler/token"
|
||||
)
|
||||
|
||||
// CompiledFunction represents a compiled function.
|
||||
type CompiledFunction struct {
|
||||
Instructions []byte
|
||||
NumLocals int
|
||||
NumParameters int
|
||||
}
|
||||
|
||||
// TypeName returns the name of the type.
|
||||
func (o *CompiledFunction) TypeName() string {
|
||||
return "compiled-function"
|
||||
}
|
||||
|
@ -18,10 +20,13 @@ func (o *CompiledFunction) String() string {
|
|||
return "<compiled-function>"
|
||||
}
|
||||
|
||||
// BinaryOp returns another object that is the result of
|
||||
// a given binary operator and a right-hand side object.
|
||||
func (o *CompiledFunction) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||
return nil, ErrInvalidOperator
|
||||
}
|
||||
|
||||
// Copy returns a copy of the type.
|
||||
func (o *CompiledFunction) Copy() Object {
|
||||
return &CompiledFunction{
|
||||
Instructions: append([]byte{}, o.Instructions...),
|
||||
|
@ -30,10 +35,13 @@ func (o *CompiledFunction) Copy() Object {
|
|||
}
|
||||
}
|
||||
|
||||
// IsFalsy returns true if the value of the type is falsy.
|
||||
func (o *CompiledFunction) IsFalsy() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Equals returns true if the value of the type
|
||||
// is equal to the value of another object.
|
||||
func (o *CompiledFunction) Equals(x Object) bool {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -2,9 +2,11 @@ package objects
|
|||
|
||||
import "github.com/d5/tengo/compiler/token"
|
||||
|
||||
// Continue represents a continue statement.
|
||||
type Continue struct {
|
||||
}
|
||||
|
||||
// TypeName returns the name of the type.
|
||||
func (o *Continue) TypeName() string {
|
||||
return "continue"
|
||||
}
|
||||
|
@ -13,18 +15,24 @@ func (o *Continue) String() string {
|
|||
return "<continue>"
|
||||
}
|
||||
|
||||
// BinaryOp returns another object that is the result of
|
||||
// a given binary operator and a right-hand side object.
|
||||
func (o *Continue) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||
return nil, ErrInvalidOperator
|
||||
}
|
||||
|
||||
// Copy returns a copy of the type.
|
||||
func (o *Continue) Copy() Object {
|
||||
return &Continue{}
|
||||
}
|
||||
|
||||
// IsFalsy returns true if the value of the type is falsy.
|
||||
func (o *Continue) IsFalsy() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Equals returns true if the value of the type
|
||||
// is equal to the value of another object.
|
||||
func (o *Continue) Equals(x Object) bool {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -2,4 +2,5 @@ package objects
|
|||
|
||||
import "errors"
|
||||
|
||||
// ErrInvalidOperator represents an error for invalid operator usage.
|
||||
var ErrInvalidOperator = errors.New("invalid operator")
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/d5/tengo/compiler/token"
|
||||
)
|
||||
|
||||
// Float represents a floating point number value.
|
||||
type Float struct {
|
||||
Value float64
|
||||
}
|
||||
|
@ -15,10 +16,13 @@ func (o *Float) String() string {
|
|||
return strconv.FormatFloat(o.Value, 'f', -1, 64)
|
||||
}
|
||||
|
||||
// TypeName returns the name of the type.
|
||||
func (o *Float) TypeName() string {
|
||||
return "float"
|
||||
}
|
||||
|
||||
// BinaryOp returns another object that is the result of
|
||||
// a given binary operator and a right-hand side object.
|
||||
func (o *Float) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||
switch rhs := rhs.(type) {
|
||||
case *Float:
|
||||
|
@ -120,14 +124,18 @@ func (o *Float) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
|||
return nil, ErrInvalidOperator
|
||||
}
|
||||
|
||||
// Copy returns a copy of the type.
|
||||
func (o *Float) Copy() Object {
|
||||
return &Float{Value: o.Value}
|
||||
}
|
||||
|
||||
// IsFalsy returns true if the value of the type is falsy.
|
||||
func (o *Float) IsFalsy() bool {
|
||||
return math.IsNaN(o.Value)
|
||||
}
|
||||
|
||||
// Equals returns true if the value of the type
|
||||
// is equal to the value of another object.
|
||||
func (o *Float) Equals(x Object) bool {
|
||||
t, ok := x.(*Float)
|
||||
if !ok {
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/d5/tengo/compiler/token"
|
||||
)
|
||||
|
||||
// Int represents an integer value.
|
||||
type Int struct {
|
||||
Value int64
|
||||
}
|
||||
|
@ -14,10 +15,13 @@ func (o *Int) String() string {
|
|||
return strconv.FormatInt(o.Value, 10)
|
||||
}
|
||||
|
||||
// TypeName returns the name of the type.
|
||||
func (o *Int) TypeName() string {
|
||||
return "int"
|
||||
}
|
||||
|
||||
// BinaryOp returns another object that is the result of
|
||||
// a given binary operator and a right-hand side object.
|
||||
func (o *Int) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||
switch rhs := rhs.(type) {
|
||||
case *Int:
|
||||
|
@ -145,14 +149,18 @@ func (o *Int) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
|||
return nil, ErrInvalidOperator
|
||||
}
|
||||
|
||||
// Copy returns a copy of the type.
|
||||
func (o *Int) Copy() Object {
|
||||
return &Int{o.Value}
|
||||
}
|
||||
|
||||
// IsFalsy returns true if the value of the type is falsy.
|
||||
func (o *Int) IsFalsy() bool {
|
||||
return o.Value == 0
|
||||
}
|
||||
|
||||
// Equals returns true if the value of the type
|
||||
// is equal to the value of another object.
|
||||
func (o *Int) Equals(x Object) bool {
|
||||
t, ok := x.(*Int)
|
||||
if !ok {
|
||||
|
|
|
@ -2,19 +2,28 @@ package objects
|
|||
|
||||
import "github.com/d5/tengo/compiler/token"
|
||||
|
||||
// Iterator represents an iterator for underlying data type.
|
||||
type Iterator interface {
|
||||
Object
|
||||
|
||||
// Next returns true if there are more elements to iterate.
|
||||
Next() bool
|
||||
|
||||
// Key returns the key or index value of the current element.
|
||||
Key() Object
|
||||
|
||||
// Value returns the value of the current element.
|
||||
Value() Object
|
||||
}
|
||||
|
||||
// ArrayIterator is an iterator for an array.
|
||||
type ArrayIterator struct {
|
||||
v []Object
|
||||
i int
|
||||
l int
|
||||
}
|
||||
|
||||
// NewArrayIterator creates an ArrayIterator.
|
||||
func NewArrayIterator(v *Array) Iterator {
|
||||
return &ArrayIterator{
|
||||
v: v.Value,
|
||||
|
@ -22,6 +31,7 @@ func NewArrayIterator(v *Array) Iterator {
|
|||
}
|
||||
}
|
||||
|
||||
// TypeName returns the name of the type.
|
||||
func (i *ArrayIterator) TypeName() string {
|
||||
return "array-iterator"
|
||||
}
|
||||
|
@ -30,35 +40,45 @@ func (i *ArrayIterator) String() string {
|
|||
return "<array-iterator>"
|
||||
}
|
||||
|
||||
// BinaryOp returns another object that is the result of
|
||||
// a given binary operator and a right-hand side object.
|
||||
func (i *ArrayIterator) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||
return nil, ErrInvalidOperator
|
||||
}
|
||||
|
||||
// IsFalsy returns true if the value of the type is falsy.
|
||||
func (i *ArrayIterator) IsFalsy() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Equals returns true if the value of the type
|
||||
// is equal to the value of another object.
|
||||
func (i *ArrayIterator) Equals(Object) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Copy returns a copy of the type.
|
||||
func (i *ArrayIterator) Copy() Object {
|
||||
return &ArrayIterator{v: i.v, i: i.i, l: i.l}
|
||||
}
|
||||
|
||||
// Next returns true if there are more elements to iterate.
|
||||
func (i *ArrayIterator) Next() bool {
|
||||
i.i++
|
||||
return i.i <= i.l
|
||||
}
|
||||
|
||||
// Key returns the key or index value of the current element.
|
||||
func (i *ArrayIterator) Key() Object {
|
||||
return &Int{int64(i.i - 1)}
|
||||
}
|
||||
|
||||
// Value returns the value of the current element.
|
||||
func (i *ArrayIterator) Value() Object {
|
||||
return i.v[i.i-1]
|
||||
}
|
||||
|
||||
// MapIterator represents an iterator for the map.
|
||||
type MapIterator struct {
|
||||
v map[string]Object
|
||||
k []string
|
||||
|
@ -66,6 +86,7 @@ type MapIterator struct {
|
|||
l int
|
||||
}
|
||||
|
||||
// NewMapIterator creates a map iterator.
|
||||
func NewMapIterator(v *Map) Iterator {
|
||||
var keys []string
|
||||
for k := range v.Value {
|
||||
|
@ -79,6 +100,7 @@ func NewMapIterator(v *Map) Iterator {
|
|||
}
|
||||
}
|
||||
|
||||
// TypeName returns the name of the type.
|
||||
func (i *MapIterator) TypeName() string {
|
||||
return "map-iterator"
|
||||
}
|
||||
|
@ -87,45 +109,56 @@ func (i *MapIterator) String() string {
|
|||
return "<map-iterator>"
|
||||
}
|
||||
|
||||
// BinaryOp returns another object that is the result of
|
||||
// a given binary operator and a right-hand side object.
|
||||
func (i *MapIterator) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||
return nil, ErrInvalidOperator
|
||||
}
|
||||
|
||||
// IsFalsy returns true if the value of the type is falsy.
|
||||
func (i *MapIterator) IsFalsy() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Equals returns true if the value of the type
|
||||
// is equal to the value of another object.
|
||||
func (i *MapIterator) Equals(Object) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Copy returns a copy of the type.
|
||||
func (i *MapIterator) Copy() Object {
|
||||
return &MapIterator{v: i.v, k: i.k, i: i.i, l: i.l}
|
||||
}
|
||||
|
||||
// Next returns true if there are more elements to iterate.
|
||||
func (i *MapIterator) Next() bool {
|
||||
i.i++
|
||||
return i.i <= i.l
|
||||
}
|
||||
|
||||
// Key returns the key or index value of the current element.
|
||||
func (i *MapIterator) Key() Object {
|
||||
k := i.k[i.i-1]
|
||||
|
||||
return &String{Value: k}
|
||||
}
|
||||
|
||||
// Value returns the value of the current element.
|
||||
func (i *MapIterator) Value() Object {
|
||||
k := i.k[i.i-1]
|
||||
|
||||
return i.v[k]
|
||||
}
|
||||
|
||||
// StringIterator represents an iterator for a string.
|
||||
type StringIterator struct {
|
||||
v []rune
|
||||
i int
|
||||
l int
|
||||
}
|
||||
|
||||
// NewStringIterator creates a string iterator.
|
||||
func NewStringIterator(v *String) Iterator {
|
||||
r := []rune(v.Value)
|
||||
|
||||
|
@ -135,6 +168,7 @@ func NewStringIterator(v *String) Iterator {
|
|||
}
|
||||
}
|
||||
|
||||
// TypeName returns the name of the type.
|
||||
func (i *StringIterator) TypeName() string {
|
||||
return "string-iterator"
|
||||
}
|
||||
|
@ -143,31 +177,40 @@ func (i *StringIterator) String() string {
|
|||
return "<string-iterator>"
|
||||
}
|
||||
|
||||
// BinaryOp returns another object that is the result of
|
||||
// a given binary operator and a right-hand side object.
|
||||
func (i *StringIterator) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||
return nil, ErrInvalidOperator
|
||||
}
|
||||
|
||||
// IsFalsy returns true if the value of the type is falsy.
|
||||
func (i *StringIterator) IsFalsy() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Equals returns true if the value of the type
|
||||
// is equal to the value of another object.
|
||||
func (i *StringIterator) Equals(Object) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Copy returns a copy of the type.
|
||||
func (i *StringIterator) Copy() Object {
|
||||
return &StringIterator{v: i.v, i: i.i, l: i.l}
|
||||
}
|
||||
|
||||
// Next returns true if there are more elements to iterate.
|
||||
func (i *StringIterator) Next() bool {
|
||||
i.i++
|
||||
return i.i <= i.l
|
||||
}
|
||||
|
||||
// Key returns the key or index value of the current element.
|
||||
func (i *StringIterator) Key() Object {
|
||||
return &Int{int64(i.i - 1)}
|
||||
}
|
||||
|
||||
// Value returns the value of the current element.
|
||||
func (i *StringIterator) Value() Object {
|
||||
return &Char{Value: i.v[i.i-1]}
|
||||
}
|
||||
|
|
|
@ -7,10 +7,12 @@ import (
|
|||
"github.com/d5/tengo/compiler/token"
|
||||
)
|
||||
|
||||
// Map represents a map of objects.
|
||||
type Map struct {
|
||||
Value map[string]Object
|
||||
}
|
||||
|
||||
// TypeName returns the name of the type.
|
||||
func (o *Map) TypeName() string {
|
||||
return "map"
|
||||
}
|
||||
|
@ -24,10 +26,13 @@ func (o *Map) String() string {
|
|||
return fmt.Sprintf("{%s}", strings.Join(pairs, ", "))
|
||||
}
|
||||
|
||||
// BinaryOp returns another object that is the result of
|
||||
// a given binary operator and a right-hand side object.
|
||||
func (o *Map) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||
return nil, ErrInvalidOperator
|
||||
}
|
||||
|
||||
// Copy returns a copy of the type.
|
||||
func (o *Map) Copy() Object {
|
||||
c := make(map[string]Object)
|
||||
for k, v := range o.Value {
|
||||
|
@ -37,20 +42,25 @@ func (o *Map) Copy() Object {
|
|||
return &Map{Value: c}
|
||||
}
|
||||
|
||||
// IsFalsy returns true if the value of the type is falsy.
|
||||
func (o *Map) IsFalsy() bool {
|
||||
return len(o.Value) == 0
|
||||
}
|
||||
|
||||
// Get returns the value for the given key.
|
||||
func (o *Map) Get(key string) (Object, bool) {
|
||||
val, ok := o.Value[key]
|
||||
|
||||
return val, ok
|
||||
}
|
||||
|
||||
// Set sets the value for the given key.
|
||||
func (o *Map) Set(key string, value Object) {
|
||||
o.Value[key] = value
|
||||
}
|
||||
|
||||
// Equals returns true if the value of the type
|
||||
// is equal to the value of another object.
|
||||
func (o *Map) Equals(x Object) bool {
|
||||
t, ok := x.(*Map)
|
||||
if !ok {
|
||||
|
|
|
@ -2,6 +2,7 @@ package objects
|
|||
|
||||
import "github.com/d5/tengo/compiler/token"
|
||||
|
||||
// Object represents an object in the VM.
|
||||
type Object interface {
|
||||
// TypeName should return the name of the type.
|
||||
TypeName() string
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
package objects
|
||||
|
||||
var (
|
||||
TrueValue Object = &Bool{Value: true}
|
||||
FalseValue Object = &Bool{Value: false}
|
||||
// TrueValue represents a true value.
|
||||
TrueValue Object = &Bool{Value: true}
|
||||
|
||||
// FalseValue represents a false value.
|
||||
FalseValue Object = &Bool{Value: false}
|
||||
|
||||
// UndefinedValue represents an undefined value.
|
||||
UndefinedValue Object = &Undefined{}
|
||||
)
|
||||
|
|
|
@ -2,10 +2,12 @@ package objects
|
|||
|
||||
import "github.com/d5/tengo/compiler/token"
|
||||
|
||||
// ReturnValue represents a value that is being returned.
|
||||
type ReturnValue struct {
|
||||
Value Object
|
||||
}
|
||||
|
||||
// TypeName returns the name of the type.
|
||||
func (o *ReturnValue) TypeName() string {
|
||||
return "return-value"
|
||||
}
|
||||
|
@ -14,18 +16,24 @@ func (o *ReturnValue) String() string {
|
|||
return "<return>"
|
||||
}
|
||||
|
||||
// BinaryOp returns another object that is the result of
|
||||
// a given binary operator and a right-hand side object.
|
||||
func (o *ReturnValue) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||
return nil, ErrInvalidOperator
|
||||
}
|
||||
|
||||
// Copy returns a copy of the type.
|
||||
func (o *ReturnValue) Copy() Object {
|
||||
return &ReturnValue{Value: o.Copy()}
|
||||
}
|
||||
|
||||
// IsFalsy returns true if the value of the type is falsy.
|
||||
func (o *ReturnValue) IsFalsy() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Equals returns true if the value of the type
|
||||
// is equal to the value of another object.
|
||||
func (o *ReturnValue) Equals(x Object) bool {
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -6,10 +6,12 @@ import (
|
|||
"github.com/d5/tengo/compiler/token"
|
||||
)
|
||||
|
||||
// String represents a string value.
|
||||
type String struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
// TypeName returns the name of the type.
|
||||
func (o *String) TypeName() string {
|
||||
return "string"
|
||||
}
|
||||
|
@ -18,6 +20,8 @@ func (o *String) String() string {
|
|||
return fmt.Sprintf("%q", o.Value)
|
||||
}
|
||||
|
||||
// BinaryOp returns another object that is the result of
|
||||
// a given binary operator and a right-hand side object.
|
||||
func (o *String) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||
switch rhs := rhs.(type) {
|
||||
case *String:
|
||||
|
@ -38,14 +42,18 @@ func (o *String) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
|||
return nil, ErrInvalidOperator
|
||||
}
|
||||
|
||||
// IsFalsy returns true if the value of the type is falsy.
|
||||
func (o *String) IsFalsy() bool {
|
||||
return len(o.Value) == 0
|
||||
}
|
||||
|
||||
// Copy returns a copy of the type.
|
||||
func (o *String) Copy() Object {
|
||||
return &String{Value: o.Value}
|
||||
}
|
||||
|
||||
// Equals returns true if the value of the type
|
||||
// is equal to the value of another object.
|
||||
func (o *String) Equals(x Object) bool {
|
||||
t, ok := x.(*String)
|
||||
if !ok {
|
||||
|
|
|
@ -2,8 +2,10 @@ package objects
|
|||
|
||||
import "github.com/d5/tengo/compiler/token"
|
||||
|
||||
// Undefined represents an undefined value.
|
||||
type Undefined struct{}
|
||||
|
||||
// TypeName returns the name of the type.
|
||||
func (o Undefined) TypeName() string {
|
||||
return "undefined"
|
||||
}
|
||||
|
@ -12,18 +14,24 @@ func (o Undefined) String() string {
|
|||
return "<undefined>"
|
||||
}
|
||||
|
||||
// BinaryOp returns another object that is the result of
|
||||
// a given binary operator and a right-hand side object.
|
||||
func (o Undefined) BinaryOp(op token.Token, rhs Object) (Object, error) {
|
||||
return nil, ErrInvalidOperator
|
||||
}
|
||||
|
||||
// Copy returns a copy of the type.
|
||||
func (o Undefined) Copy() Object {
|
||||
return Undefined{}
|
||||
}
|
||||
|
||||
// IsFalsy returns true if the value of the type is falsy.
|
||||
func (o Undefined) IsFalsy() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Equals returns true if the value of the type
|
||||
// is equal to the value of another object.
|
||||
func (o Undefined) Equals(x Object) bool {
|
||||
_, ok := x.(*Undefined)
|
||||
|
||||
|
|
|
@ -4,4 +4,5 @@ import (
|
|||
"errors"
|
||||
)
|
||||
|
||||
// ErrStackOverflow is a stack overflow error.
|
||||
var ErrStackOverflow = errors.New("stack overflow")
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"github.com/d5/tengo/objects"
|
||||
)
|
||||
|
||||
// Frame represents a function call frame.
|
||||
type Frame struct {
|
||||
fn *objects.CompiledFunction
|
||||
freeVars []*objects.Object
|
||||
|
|
|
@ -9,9 +9,14 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
StackSize = 2048
|
||||
// StackSize is the maximum stack size.
|
||||
StackSize = 2048
|
||||
|
||||
// GlobalsSize is the maximum number of global variables.
|
||||
GlobalsSize = 1024
|
||||
MaxFrames = 1024
|
||||
|
||||
// MaxFrames is the maximum number of function frames.
|
||||
MaxFrames = 1024
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -21,6 +26,7 @@ var (
|
|||
builtinFuncs []objects.Object
|
||||
)
|
||||
|
||||
// VM is a virtual machine that executes the bytecode compiled by Compiler.
|
||||
type VM struct {
|
||||
constants []objects.Object
|
||||
stack []*objects.Object
|
||||
|
@ -34,6 +40,7 @@ type VM struct {
|
|||
aborting bool
|
||||
}
|
||||
|
||||
// NewVM creates a VM.
|
||||
func NewVM(bytecode *compiler.Bytecode, globals []*objects.Object) *VM {
|
||||
if globals == nil {
|
||||
globals = make([]*objects.Object, GlobalsSize)
|
||||
|
@ -62,10 +69,12 @@ func NewVM(bytecode *compiler.Bytecode, globals []*objects.Object) *VM {
|
|||
}
|
||||
}
|
||||
|
||||
// Abort aborts the execution.
|
||||
func (v *VM) Abort() {
|
||||
v.aborting = true
|
||||
}
|
||||
|
||||
// Run starts the execution.
|
||||
func (v *VM) Run() error {
|
||||
var ip int
|
||||
|
||||
|
@ -544,13 +553,13 @@ func (v *VM) Run() error {
|
|||
}
|
||||
v.sp -= numElements
|
||||
|
||||
var map_ objects.Object = &objects.Map{Value: kv}
|
||||
var m objects.Object = &objects.Map{Value: kv}
|
||||
|
||||
if v.sp >= StackSize {
|
||||
return ErrStackOverflow
|
||||
}
|
||||
|
||||
v.stack[v.sp] = &map_
|
||||
v.stack[v.sp] = &m
|
||||
v.sp++
|
||||
|
||||
case compiler.OpIndex:
|
||||
|
@ -703,7 +712,7 @@ func (v *VM) Run() error {
|
|||
|
||||
case compiler.OpCall:
|
||||
numArgs := int(compiler.ReadUint8(v.curInsts[ip+1:]))
|
||||
v.curFrame.ip += 1
|
||||
v.curFrame.ip++
|
||||
|
||||
callee := *v.stack[v.sp-1-numArgs]
|
||||
|
||||
|
@ -748,7 +757,7 @@ func (v *VM) Run() error {
|
|||
case compiler.OpReturnValue:
|
||||
//numRets := int(compiler.ReadUint8(v.curInsts[ip+1:]))
|
||||
_ = int(compiler.ReadUint8(v.curInsts[ip+1:]))
|
||||
v.curFrame.ip += 1
|
||||
v.curFrame.ip++
|
||||
|
||||
// TODO: multi-value return is not fully implemented yet
|
||||
//var rets []*objects.Object
|
||||
|
@ -798,7 +807,7 @@ func (v *VM) Run() error {
|
|||
|
||||
case compiler.OpDefineLocal:
|
||||
localIndex := compiler.ReadUint8(v.curInsts[ip+1:])
|
||||
v.curFrame.ip += 1
|
||||
v.curFrame.ip++
|
||||
|
||||
sp := v.curFrame.basePointer + int(localIndex)
|
||||
|
||||
|
@ -811,7 +820,7 @@ func (v *VM) Run() error {
|
|||
|
||||
case compiler.OpSetLocal:
|
||||
localIndex := compiler.ReadUint8(v.curInsts[ip+1:])
|
||||
v.curFrame.ip += 1
|
||||
v.curFrame.ip++
|
||||
|
||||
sp := v.curFrame.basePointer + int(localIndex)
|
||||
|
||||
|
@ -855,7 +864,7 @@ func (v *VM) Run() error {
|
|||
|
||||
case compiler.OpGetLocal:
|
||||
localIndex := compiler.ReadUint8(v.curInsts[ip+1:])
|
||||
v.curFrame.ip += 1
|
||||
v.curFrame.ip++
|
||||
|
||||
val := v.stack[v.curFrame.basePointer+int(localIndex)]
|
||||
|
||||
|
@ -868,7 +877,7 @@ func (v *VM) Run() error {
|
|||
|
||||
case compiler.OpGetBuiltin:
|
||||
builtinIndex := compiler.ReadUint8(v.curInsts[ip+1:])
|
||||
v.curFrame.ip += 1
|
||||
v.curFrame.ip++
|
||||
|
||||
if v.sp >= StackSize {
|
||||
return ErrStackOverflow
|
||||
|
@ -888,7 +897,7 @@ func (v *VM) Run() error {
|
|||
|
||||
case compiler.OpGetFree:
|
||||
freeIndex := compiler.ReadUint8(v.curInsts[ip+1:])
|
||||
v.curFrame.ip += 1
|
||||
v.curFrame.ip++
|
||||
|
||||
val := v.curFrame.freeVars[freeIndex]
|
||||
|
||||
|
@ -930,7 +939,7 @@ func (v *VM) Run() error {
|
|||
|
||||
case compiler.OpSetFree:
|
||||
freeIndex := compiler.ReadUint8(v.curInsts[ip+1:])
|
||||
v.curFrame.ip += 1
|
||||
v.curFrame.ip++
|
||||
|
||||
val := v.stack[v.sp-1]
|
||||
v.sp--
|
||||
|
@ -1016,10 +1025,12 @@ func (v *VM) Run() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Globals returns the global variables.
|
||||
func (v *VM) Globals() []*objects.Object {
|
||||
return v.globals
|
||||
}
|
||||
|
||||
// FrameInfo returns the current function call frame information.
|
||||
func (v *VM) FrameInfo() (frameIndex int, ip int) {
|
||||
return v.framesIndex - 1, v.frames[v.framesIndex-1].ip
|
||||
}
|
||||
|
@ -1150,17 +1161,17 @@ func selectorAssign(dst, src *objects.Object, selectors []interface{}) error {
|
|||
for idx := 0; idx < numSel; idx++ {
|
||||
switch sel := selectors[idx].(type) {
|
||||
case string:
|
||||
map_, isMap := (*dst).(*objects.Map)
|
||||
m, isMap := (*dst).(*objects.Map)
|
||||
if !isMap {
|
||||
return fmt.Errorf("invalid map object for selector '%s'", sel)
|
||||
}
|
||||
|
||||
if idx == numSel-1 {
|
||||
map_.Set(sel, *src)
|
||||
m.Set(sel, *src)
|
||||
return nil
|
||||
}
|
||||
|
||||
nxt, found := map_.Get(sel)
|
||||
nxt, found := m.Get(sel)
|
||||
if !found {
|
||||
return fmt.Errorf("key not found '%s'", sel)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue