diff --git a/assert/assert.go b/assert/assert.go index 208fa56..b09be7c 100644 --- a/assert/assert.go +++ b/assert/assert.go @@ -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() diff --git a/assert/trace.go b/assert/trace.go index 6f4d219..a5a32d9 100644 --- a/assert/trace.go +++ b/assert/trace.go @@ -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) } diff --git a/cmd/tengo/main.go b/cmd/tengo/main.go index acf43c2..777321c 100644 --- a/cmd/tengo/main.go +++ b/cmd/tengo/main.go @@ -248,7 +248,7 @@ func addPrints(file *ast.File) *ast.File { Func: &ast.Ident{ Name: "print", }, - Args: s.Lhs, + Args: s.LHS, }, }) diff --git a/compiler/ast/array_lit.go b/compiler/ast/array_lit.go index 9dcb1d7..f98d69d 100644 --- a/compiler/ast/array_lit.go +++ b/compiler/ast/array_lit.go @@ -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 } diff --git a/compiler/ast/assign_stmt.go b/compiler/ast/assign_stmt.go index 4d9719d..e129114 100644 --- a/compiler/ast/assign_stmt.go +++ b/compiler/ast/assign_stmt.go @@ -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()) } diff --git a/compiler/ast/bad_expr.go b/compiler/ast/bad_expr.go index 121eac8..771f26f 100644 --- a/compiler/ast/bad_expr.go +++ b/compiler/ast/bad_expr.go @@ -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 } diff --git a/compiler/ast/bad_stmt.go b/compiler/ast/bad_stmt.go index b02d812..c2d0ae9 100644 --- a/compiler/ast/bad_stmt.go +++ b/compiler/ast/bad_stmt.go @@ -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 } diff --git a/compiler/ast/binary_expr.go b/compiler/ast/binary_expr.go index c073400..0cc5bba 100644 --- a/compiler/ast/binary_expr.go +++ b/compiler/ast/binary_expr.go @@ -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() + ")" } diff --git a/compiler/ast/block_stmt.go b/compiler/ast/block_stmt.go index d8db888..9bde9fa 100644 --- a/compiler/ast/block_stmt.go +++ b/compiler/ast/block_stmt.go @@ -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 } diff --git a/compiler/ast/bool_lit.go b/compiler/ast/bool_lit.go index 3d7781f..c3dfbf8 100644 --- a/compiler/ast/bool_lit.go +++ b/compiler/ast/bool_lit.go @@ -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)) } diff --git a/compiler/ast/branch_stmt.go b/compiler/ast/branch_stmt.go index 1875f57..f6c7fde 100644 --- a/compiler/ast/branch_stmt.go +++ b/compiler/ast/branch_stmt.go @@ -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() diff --git a/compiler/ast/call_expr.go b/compiler/ast/call_expr.go index c9adb24..0219d7c 100644 --- a/compiler/ast/call_expr.go +++ b/compiler/ast/call_expr.go @@ -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 } diff --git a/compiler/ast/char_lit.go b/compiler/ast/char_lit.go index ae4bb29..592f874 100644 --- a/compiler/ast/char_lit.go +++ b/compiler/ast/char_lit.go @@ -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)) } diff --git a/compiler/ast/empty_stmt.go b/compiler/ast/empty_stmt.go index 569d905..a2ac6ff 100644 --- a/compiler/ast/empty_stmt.go +++ b/compiler/ast/empty_stmt.go @@ -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 diff --git a/compiler/ast/expr.go b/compiler/ast/expr.go index 8b65256..764bace 100644 --- a/compiler/ast/expr.go +++ b/compiler/ast/expr.go @@ -1,5 +1,6 @@ package ast +// Expr represents an expression node in the AST. type Expr interface { Node exprNode() diff --git a/compiler/ast/expr_stmt.go b/compiler/ast/expr_stmt.go index 9a3bb05..095a3ad 100644 --- a/compiler/ast/expr_stmt.go +++ b/compiler/ast/expr_stmt.go @@ -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() } diff --git a/compiler/ast/file.go b/compiler/ast/file.go index 38daa06..7ea18c5 100644 --- a/compiler/ast/file.go +++ b/compiler/ast/file.go @@ -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()) } diff --git a/compiler/ast/float_lit.go b/compiler/ast/float_lit.go index ca219c7..670f744 100644 --- a/compiler/ast/float_lit.go +++ b/compiler/ast/float_lit.go @@ -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)) } diff --git a/compiler/ast/for_in_stmt.go b/compiler/ast/for_in_stmt.go index 3bed403..18020b5 100644 --- a/compiler/ast/for_in_stmt.go +++ b/compiler/ast/for_in_stmt.go @@ -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() } diff --git a/compiler/ast/for_stmt.go b/compiler/ast/for_stmt.go index 1d2e11d..c6cb5e3 100644 --- a/compiler/ast/for_stmt.go +++ b/compiler/ast/for_stmt.go @@ -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() } diff --git a/compiler/ast/func_lit.go b/compiler/ast/func_lit.go index b61cb91..2e90ed2 100644 --- a/compiler/ast/func_lit.go +++ b/compiler/ast/func_lit.go @@ -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() } diff --git a/compiler/ast/func_type.go b/compiler/ast/func_type.go index fac46ef..ba9aa20 100644 --- a/compiler/ast/func_type.go +++ b/compiler/ast/func_type.go @@ -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() } diff --git a/compiler/ast/ident.go b/compiler/ast/ident.go index ad0dc3e..3c7a2f7 100644 --- a/compiler/ast/ident.go +++ b/compiler/ast/ident.go @@ -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)) } diff --git a/compiler/ast/ident_list.go b/compiler/ast/ident_list.go index e93100b..604431c 100644 --- a/compiler/ast/ident_list.go +++ b/compiler/ast/ident_list.go @@ -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 diff --git a/compiler/ast/if_stmt.go b/compiler/ast/if_stmt.go index 6f071d3..b3d6560 100644 --- a/compiler/ast/if_stmt.go +++ b/compiler/ast/if_stmt.go @@ -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() diff --git a/compiler/ast/inc_dec_stmt.go b/compiler/ast/inc_dec_stmt.go index 46f9165..e4e7f92 100644 --- a/compiler/ast/inc_dec_stmt.go +++ b/compiler/ast/inc_dec_stmt.go @@ -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) } diff --git a/compiler/ast/index_expr.go b/compiler/ast/index_expr.go index bca7ce2..bc0992a 100644 --- a/compiler/ast/index_expr.go +++ b/compiler/ast/index_expr.go @@ -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 } diff --git a/compiler/ast/int_lit.go b/compiler/ast/int_lit.go index 39314a5..3f52eb3 100644 --- a/compiler/ast/int_lit.go +++ b/compiler/ast/int_lit.go @@ -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)) } diff --git a/compiler/ast/map_element_lit.go b/compiler/ast/map_element_lit.go index 8d4b001..3d7fca9 100644 --- a/compiler/ast/map_element_lit.go +++ b/compiler/ast/map_element_lit.go @@ -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() } diff --git a/compiler/ast/map_lit.go b/compiler/ast/map_lit.go index f74fb8e..2a3a542 100644 --- a/compiler/ast/map_lit.go +++ b/compiler/ast/map_lit.go @@ -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 } diff --git a/compiler/ast/node.go b/compiler/ast/node.go index 6777442..44677b4 100644 --- a/compiler/ast/node.go +++ b/compiler/ast/node.go @@ -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 } diff --git a/compiler/ast/paren_expr.go b/compiler/ast/paren_expr.go index abc35a0..8db4ac0 100644 --- a/compiler/ast/paren_expr.go +++ b/compiler/ast/paren_expr.go @@ -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 } diff --git a/compiler/ast/return_stmt.go b/compiler/ast/return_stmt.go index 88d2a24..f82f8f0 100644 --- a/compiler/ast/return_stmt.go +++ b/compiler/ast/return_stmt.go @@ -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() diff --git a/compiler/ast/selector_expr.go b/compiler/ast/selector_expr.go index 02e320a..31d2e6d 100644 --- a/compiler/ast/selector_expr.go +++ b/compiler/ast/selector_expr.go @@ -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() } diff --git a/compiler/ast/slice_expr.go b/compiler/ast/slice_expr.go index ba6faf6..e7e2e05 100644 --- a/compiler/ast/slice_expr.go +++ b/compiler/ast/slice_expr.go @@ -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 } diff --git a/compiler/ast/stmt.go b/compiler/ast/stmt.go index d68ead0..6b26ba8 100644 --- a/compiler/ast/stmt.go +++ b/compiler/ast/stmt.go @@ -1,5 +1,6 @@ package ast +// Stmt represents a statement in the AST. type Stmt interface { Node stmtNode() diff --git a/compiler/ast/string_lit.go b/compiler/ast/string_lit.go index 057a69a..2119d34 100644 --- a/compiler/ast/string_lit.go +++ b/compiler/ast/string_lit.go @@ -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)) } diff --git a/compiler/ast/unary_expr.go b/compiler/ast/unary_expr.go index 525bb8e..5323614 100644 --- a/compiler/ast/unary_expr.go +++ b/compiler/ast/unary_expr.go @@ -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() } diff --git a/compiler/ast/undefined_lit.go b/compiler/ast/undefined_lit.go index d4f536b..8e51b11 100644 --- a/compiler/ast/undefined_lit.go +++ b/compiler/ast/undefined_lit.go @@ -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 } diff --git a/compiler/bytecode.go b/compiler/bytecode.go index baf01bf..234f277 100644 --- a/compiler/bytecode.go +++ b/compiler/bytecode.go @@ -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) diff --git a/compiler/compilation_scope.go b/compiler/compilation_scope.go index d82f837..03f86de 100644 --- a/compiler/compilation_scope.go +++ b/compiler/compilation_scope.go @@ -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 diff --git a/compiler/compiler.go b/compiler/compiler.go index 4b57e2e..b532587 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -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(), diff --git a/compiler/compiler_assign.go b/compiler/compiler_assign.go index 30b912e..3aceb0f 100644 --- a/compiler/compiler_assign.go +++ b/compiler/compiler_assign.go @@ -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 } diff --git a/compiler/compiler_logical.go b/compiler/compiler_logical.go index 5dfff28..ba12507 100644 --- a/compiler/compiler_logical.go +++ b/compiler/compiler_logical.go @@ -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 } diff --git a/compiler/definitions.go b/compiler/definitions.go index 1b1cd80..a84410f 100644 --- a/compiler/definitions.go +++ b/compiler/definitions.go @@ -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] diff --git a/compiler/emitted_instruction.go b/compiler/emitted_instruction.go index c6bb074..8572fb0 100644 --- a/compiler/emitted_instruction.go +++ b/compiler/emitted_instruction.go @@ -1,5 +1,7 @@ package compiler +// EmittedInstruction represents an opcode +// with its emitted position. type EmittedInstruction struct { Opcode Opcode Position int diff --git a/compiler/instructions.go b/compiler/instructions.go index b233610..fc5d9bc 100644 --- a/compiler/instructions.go +++ b/compiler/instructions.go @@ -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 diff --git a/compiler/loop.go b/compiler/loop.go index 8592012..e27cb09 100644 --- a/compiler/loop.go +++ b/compiler/loop.go @@ -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 diff --git a/compiler/opcodes.go b/compiler/opcodes.go index 5d97042..6783ed5 100644 --- a/compiler/opcodes.go +++ b/compiler/opcodes.go @@ -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 ) diff --git a/compiler/operands.go b/compiler/operands.go index 5734154..a442857 100644 --- a/compiler/operands.go +++ b/compiler/operands.go @@ -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]) } diff --git a/compiler/parser/error.go b/compiler/parser/error.go index f2db31f..dd72e58 100644 --- a/compiler/parser/error.go +++ b/compiler/parser/error.go @@ -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 diff --git a/compiler/parser/error_list.go b/compiler/parser/error_list.go index 6ff3f19..7b804c7 100644 --- a/compiler/parser/error_list.go +++ b/compiler/parser/error_list.go @@ -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 diff --git a/compiler/parser/parse_file.go b/compiler/parser/parse_file.go index 0ff9731..0482c77 100644 --- a/compiler/parser/parse_file.go +++ b/compiler/parser/parse_file.go @@ -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) diff --git a/compiler/parser/parser.go b/compiler/parser/parser.go index 528fa2a..bc23c5c 100644 --- a/compiler/parser/parser.go +++ b/compiler/parser/parser.go @@ -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, } diff --git a/compiler/parser/parser_test.go b/compiler/parser/parser_test.go index 42c1d21..fa90904 100644 --- a/compiler/parser/parser_test.go +++ b/compiler/parser/parser_test.go @@ -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: diff --git a/compiler/scanner/error_handler.go b/compiler/scanner/error_handler.go index baae0ac..379f019 100644 --- a/compiler/scanner/error_handler.go +++ b/compiler/scanner/error_handler.go @@ -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) diff --git a/compiler/scanner/mode.go b/compiler/scanner/mode.go index af83abf..f67ceaf 100644 --- a/compiler/scanner/mode.go +++ b/compiler/scanner/mode.go @@ -1,7 +1,9 @@ package scanner +// Mode represents a scanner mode. type Mode int +// List of scanner modes. const ( ScanComments Mode = 1 << iota DontInsertSemis diff --git a/compiler/scanner/scanner.go b/compiler/scanner/scanner.go index b5deb5c..e4e86b6 100644 --- a/compiler/scanner/scanner.go +++ b/compiler/scanner/scanner.go @@ -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)) diff --git a/compiler/source/file.go b/compiler/source/file.go index 0859aae..754742b 100644 --- a/compiler/source/file.go +++ b/compiler/source/file.go @@ -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 { diff --git a/compiler/source/file_pos.go b/compiler/source/file_pos.go index 0ee322d..4055fe6 100644 --- a/compiler/source/file_pos.go +++ b/compiler/source/file_pos.go @@ -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 } diff --git a/compiler/source/file_set.go b/compiler/source/file_set.go index 8e0e144..45dc529 100644 --- a/compiler/source/file_set.go +++ b/compiler/source/file_set.go @@ -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 { diff --git a/compiler/source/pos.go b/compiler/source/pos.go index 03bbef4..72128b1 100644 --- a/compiler/source/pos.go +++ b/compiler/source/pos.go @@ -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 } diff --git a/compiler/symbol.go b/compiler/symbol.go index 61fd2d7..21b9508 100644 --- a/compiler/symbol.go +++ b/compiler/symbol.go @@ -1,5 +1,6 @@ package compiler +// Symbol represents a symbol in the symbol table. type Symbol struct { Name string Scope SymbolScope diff --git a/compiler/symbol_scopes.go b/compiler/symbol_scopes.go index 4726397..15204b3 100644 --- a/compiler/symbol_scopes.go +++ b/compiler/symbol_scopes.go @@ -1,7 +1,9 @@ package compiler +// SymbolScope represents a symbol scope. type SymbolScope string +// List of symbol scopes const ( ScopeGlobal SymbolScope = "GLOBAL" ScopeLocal = "LOCAL" diff --git a/compiler/symbol_table.go b/compiler/symbol_table.go index e12a450..eb83f8c 100644 --- a/compiler/symbol_table.go +++ b/compiler/symbol_table.go @@ -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 { diff --git a/compiler/token/keywords.go b/compiler/token/keywords.go index 6ad8433..fd6e9d0 100644 --- a/compiler/token/keywords.go +++ b/compiler/token/keywords.go @@ -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 diff --git a/compiler/token/tokens.go b/compiler/token/tokens.go index 19e2f19..77246e8 100644 --- a/compiler/token/tokens.go +++ b/compiler/token/tokens.go @@ -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 } diff --git a/objects/array.go b/objects/array.go index 5539a8e..5df9d81 100644 --- a/objects/array.go +++ b/objects/array.go @@ -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") diff --git a/objects/bool.go b/objects/bool.go index ef0b025..719e48c 100644 --- a/objects/bool.go +++ b/objects/bool.go @@ -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 { diff --git a/objects/break.go b/objects/break.go index deedfb7..cd473a8 100644 --- a/objects/break.go +++ b/objects/break.go @@ -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 "" } +// 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 } diff --git a/objects/builtin_function.go b/objects/builtin_function.go index 847c885..5c084df 100644 --- a/objects/builtin_function.go +++ b/objects/builtin_function.go @@ -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 "" } +// 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...) } diff --git a/objects/builtins.go b/objects/builtins.go index 6dad33b..845ecb4 100644 --- a/objects/builtins.go +++ b/objects/builtins.go @@ -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 diff --git a/objects/callable.go b/objects/callable.go index 2ca920c..96e3733 100644 --- a/objects/callable.go +++ b/objects/callable.go @@ -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, diff --git a/objects/char.go b/objects/char.go index 183d543..7a57142 100644 --- a/objects/char.go +++ b/objects/char.go @@ -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 { diff --git a/objects/closure.go b/objects/closure.go index 15eaffe..d4915a5 100644 --- a/objects/closure.go +++ b/objects/closure.go @@ -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 "" } +// 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 } diff --git a/objects/compiled_function.go b/objects/compiled_function.go index 92e9185..a112d1d 100644 --- a/objects/compiled_function.go +++ b/objects/compiled_function.go @@ -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 "" } +// 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 } diff --git a/objects/continue.go b/objects/continue.go index 2337d76..8094e68 100644 --- a/objects/continue.go +++ b/objects/continue.go @@ -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 "" } +// 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 } diff --git a/objects/errors.go b/objects/errors.go index 8cd1c99..110a769 100644 --- a/objects/errors.go +++ b/objects/errors.go @@ -2,4 +2,5 @@ package objects import "errors" +// ErrInvalidOperator represents an error for invalid operator usage. var ErrInvalidOperator = errors.New("invalid operator") diff --git a/objects/float.go b/objects/float.go index 2d2d718..6599730 100644 --- a/objects/float.go +++ b/objects/float.go @@ -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 { diff --git a/objects/int.go b/objects/int.go index ed1a033..ecf9477 100644 --- a/objects/int.go +++ b/objects/int.go @@ -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 { diff --git a/objects/iterator.go b/objects/iterator.go index 425555b..e7fa67c 100644 --- a/objects/iterator.go +++ b/objects/iterator.go @@ -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 "" } +// 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 "" } +// 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 "" } +// 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]} } diff --git a/objects/map.go b/objects/map.go index fc95ca9..f7438f7 100644 --- a/objects/map.go +++ b/objects/map.go @@ -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 { diff --git a/objects/object.go b/objects/object.go index eea2127..4c5aa7a 100644 --- a/objects/object.go +++ b/objects/object.go @@ -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 diff --git a/objects/objects.go b/objects/objects.go index c7bb942..eaa29dd 100644 --- a/objects/objects.go +++ b/objects/objects.go @@ -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{} ) diff --git a/objects/return_value.go b/objects/return_value.go index d936370..eb9680a 100644 --- a/objects/return_value.go +++ b/objects/return_value.go @@ -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 "" } +// 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 } diff --git a/objects/string.go b/objects/string.go index 382bedc..deca219 100644 --- a/objects/string.go +++ b/objects/string.go @@ -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 { diff --git a/objects/undefined.go b/objects/undefined.go index 44802ae..1da9eab 100644 --- a/objects/undefined.go +++ b/objects/undefined.go @@ -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 "" } +// 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) diff --git a/runtime/errors.go b/runtime/errors.go index d29a194..f5f201c 100644 --- a/runtime/errors.go +++ b/runtime/errors.go @@ -4,4 +4,5 @@ import ( "errors" ) +// ErrStackOverflow is a stack overflow error. var ErrStackOverflow = errors.New("stack overflow") diff --git a/runtime/frame.go b/runtime/frame.go index f7c3017..90151a1 100644 --- a/runtime/frame.go +++ b/runtime/frame.go @@ -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 diff --git a/runtime/vm.go b/runtime/vm.go index 500c45e..14ff18b 100644 --- a/runtime/vm.go +++ b/runtime/vm.go @@ -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) }