aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/robertkrimen/otto/parser
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/robertkrimen/otto/parser')
-rw-r--r--vendor/github.com/robertkrimen/otto/parser/Makefile4
-rw-r--r--vendor/github.com/robertkrimen/otto/parser/README.markdown190
-rw-r--r--vendor/github.com/robertkrimen/otto/parser/dbg.go9
-rw-r--r--vendor/github.com/robertkrimen/otto/parser/error.go175
-rw-r--r--vendor/github.com/robertkrimen/otto/parser/expression.go1005
-rw-r--r--vendor/github.com/robertkrimen/otto/parser/lexer.go866
-rw-r--r--vendor/github.com/robertkrimen/otto/parser/parser.go344
-rw-r--r--vendor/github.com/robertkrimen/otto/parser/regexp.go358
-rw-r--r--vendor/github.com/robertkrimen/otto/parser/scope.go44
-rw-r--r--vendor/github.com/robertkrimen/otto/parser/statement.go938
10 files changed, 3933 insertions, 0 deletions
diff --git a/vendor/github.com/robertkrimen/otto/parser/Makefile b/vendor/github.com/robertkrimen/otto/parser/Makefile
new file mode 100644
index 000000000..766fd4d0b
--- /dev/null
+++ b/vendor/github.com/robertkrimen/otto/parser/Makefile
@@ -0,0 +1,4 @@
+.PHONY: test
+
+test:
+ go test
diff --git a/vendor/github.com/robertkrimen/otto/parser/README.markdown b/vendor/github.com/robertkrimen/otto/parser/README.markdown
new file mode 100644
index 000000000..c3cae5b60
--- /dev/null
+++ b/vendor/github.com/robertkrimen/otto/parser/README.markdown
@@ -0,0 +1,190 @@
+# parser
+--
+ import "github.com/robertkrimen/otto/parser"
+
+Package parser implements a parser for JavaScript.
+
+ import (
+ "github.com/robertkrimen/otto/parser"
+ )
+
+Parse and return an AST
+
+ filename := "" // A filename is optional
+ src := `
+ // Sample xyzzy example
+ (function(){
+ if (3.14159 > 0) {
+ console.log("Hello, World.");
+ return;
+ }
+
+ var xyzzy = NaN;
+ console.log("Nothing happens.");
+ return xyzzy;
+ })();
+ `
+
+ // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList
+ program, err := parser.ParseFile(nil, filename, src, 0)
+
+
+### Warning
+
+The parser and AST interfaces are still works-in-progress (particularly where
+node types are concerned) and may change in the future.
+
+## Usage
+
+#### func ParseFile
+
+```go
+func ParseFile(fileSet *file.FileSet, filename string, src interface{}, mode Mode) (*ast.Program, error)
+```
+ParseFile parses the source code of a single JavaScript/ECMAScript source file
+and returns the corresponding ast.Program node.
+
+If fileSet == nil, ParseFile parses source without a FileSet. If fileSet != nil,
+ParseFile first adds filename and src to fileSet.
+
+The filename argument is optional and is used for labelling errors, etc.
+
+src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST
+always be in UTF-8.
+
+ // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList
+ program, err := parser.ParseFile(nil, "", `if (abc > 1) {}`, 0)
+
+#### func ParseFunction
+
+```go
+func ParseFunction(parameterList, body string) (*ast.FunctionLiteral, error)
+```
+ParseFunction parses a given parameter list and body as a function and returns
+the corresponding ast.FunctionLiteral node.
+
+The parameter list, if any, should be a comma-separated list of identifiers.
+
+#### func ReadSource
+
+```go
+func ReadSource(filename string, src interface{}) ([]byte, error)
+```
+
+#### func TransformRegExp
+
+```go
+func TransformRegExp(pattern string) (string, error)
+```
+TransformRegExp transforms a JavaScript pattern into a Go "regexp" pattern.
+
+re2 (Go) cannot do backtracking, so the presence of a lookahead (?=) (?!) or
+backreference (\1, \2, ...) will cause an error.
+
+re2 (Go) has a different definition for \s: [\t\n\f\r ]. The JavaScript
+definition, on the other hand, also includes \v, Unicode "Separator, Space",
+etc.
+
+If the pattern is invalid (not valid even in JavaScript), then this function
+returns the empty string and an error.
+
+If the pattern is valid, but incompatible (contains a lookahead or
+backreference), then this function returns the transformation (a non-empty
+string) AND an error.
+
+#### type Error
+
+```go
+type Error struct {
+ Position file.Position
+ Message string
+}
+```
+
+An Error represents a parsing error. It includes the position where the error
+occurred and a message/description.
+
+#### func (Error) Error
+
+```go
+func (self Error) Error() string
+```
+
+#### type ErrorList
+
+```go
+type ErrorList []*Error
+```
+
+ErrorList is a list of *Errors.
+
+#### func (*ErrorList) Add
+
+```go
+func (self *ErrorList) Add(position file.Position, msg string)
+```
+Add adds an Error with given position and message to an ErrorList.
+
+#### func (ErrorList) Err
+
+```go
+func (self ErrorList) Err() error
+```
+Err returns an error equivalent to this ErrorList. If the list is empty, Err
+returns nil.
+
+#### func (ErrorList) Error
+
+```go
+func (self ErrorList) Error() string
+```
+Error implements the Error interface.
+
+#### func (ErrorList) Len
+
+```go
+func (self ErrorList) Len() int
+```
+
+#### func (ErrorList) Less
+
+```go
+func (self ErrorList) Less(i, j int) bool
+```
+
+#### func (*ErrorList) Reset
+
+```go
+func (self *ErrorList) Reset()
+```
+Reset resets an ErrorList to no errors.
+
+#### func (ErrorList) Sort
+
+```go
+func (self ErrorList) Sort()
+```
+
+#### func (ErrorList) Swap
+
+```go
+func (self ErrorList) Swap(i, j int)
+```
+
+#### type Mode
+
+```go
+type Mode uint
+```
+
+A Mode value is a set of flags (or 0). They control optional parser
+functionality.
+
+```go
+const (
+ IgnoreRegExpErrors Mode = 1 << iota // Ignore RegExp compatibility errors (allow backtracking)
+)
+```
+
+--
+**godocdown** http://github.com/robertkrimen/godocdown
diff --git a/vendor/github.com/robertkrimen/otto/parser/dbg.go b/vendor/github.com/robertkrimen/otto/parser/dbg.go
new file mode 100644
index 000000000..3c5f2f698
--- /dev/null
+++ b/vendor/github.com/robertkrimen/otto/parser/dbg.go
@@ -0,0 +1,9 @@
+// This file was AUTOMATICALLY GENERATED by dbg-import (smuggol) for github.com/robertkrimen/dbg
+
+package parser
+
+import (
+ Dbg "github.com/robertkrimen/otto/dbg"
+)
+
+var dbg, dbgf = Dbg.New()
diff --git a/vendor/github.com/robertkrimen/otto/parser/error.go b/vendor/github.com/robertkrimen/otto/parser/error.go
new file mode 100644
index 000000000..e0f74a5cf
--- /dev/null
+++ b/vendor/github.com/robertkrimen/otto/parser/error.go
@@ -0,0 +1,175 @@
+package parser
+
+import (
+ "fmt"
+ "sort"
+
+ "github.com/robertkrimen/otto/file"
+ "github.com/robertkrimen/otto/token"
+)
+
+const (
+ err_UnexpectedToken = "Unexpected token %v"
+ err_UnexpectedEndOfInput = "Unexpected end of input"
+ err_UnexpectedEscape = "Unexpected escape"
+)
+
+// UnexpectedNumber: 'Unexpected number',
+// UnexpectedString: 'Unexpected string',
+// UnexpectedIdentifier: 'Unexpected identifier',
+// UnexpectedReserved: 'Unexpected reserved word',
+// NewlineAfterThrow: 'Illegal newline after throw',
+// InvalidRegExp: 'Invalid regular expression',
+// UnterminatedRegExp: 'Invalid regular expression: missing /',
+// InvalidLHSInAssignment: 'Invalid left-hand side in assignment',
+// InvalidLHSInForIn: 'Invalid left-hand side in for-in',
+// MultipleDefaultsInSwitch: 'More than one default clause in switch statement',
+// NoCatchOrFinally: 'Missing catch or finally after try',
+// UnknownLabel: 'Undefined label \'%0\'',
+// Redeclaration: '%0 \'%1\' has already been declared',
+// IllegalContinue: 'Illegal continue statement',
+// IllegalBreak: 'Illegal break statement',
+// IllegalReturn: 'Illegal return statement',
+// StrictModeWith: 'Strict mode code may not include a with statement',
+// StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode',
+// StrictVarName: 'Variable name may not be eval or arguments in strict mode',
+// StrictParamName: 'Parameter name eval or arguments is not allowed in strict mode',
+// StrictParamDupe: 'Strict mode function may not have duplicate parameter names',
+// StrictFunctionName: 'Function name may not be eval or arguments in strict mode',
+// StrictOctalLiteral: 'Octal literals are not allowed in strict mode.',
+// StrictDelete: 'Delete of an unqualified identifier in strict mode.',
+// StrictDuplicateProperty: 'Duplicate data property in object literal not allowed in strict mode',
+// AccessorDataProperty: 'Object literal may not have data and accessor property with the same name',
+// AccessorGetSet: 'Object literal may not have multiple get/set accessors with the same name',
+// StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode',
+// StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode',
+// StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode',
+// StrictReservedWord: 'Use of future reserved word in strict mode'
+
+// A SyntaxError is a description of an ECMAScript syntax error.
+
+// An Error represents a parsing error. It includes the position where the error occurred and a message/description.
+type Error struct {
+ Position file.Position
+ Message string
+}
+
+// FIXME Should this be "SyntaxError"?
+
+func (self Error) Error() string {
+ filename := self.Position.Filename
+ if filename == "" {
+ filename = "(anonymous)"
+ }
+ return fmt.Sprintf("%s: Line %d:%d %s",
+ filename,
+ self.Position.Line,
+ self.Position.Column,
+ self.Message,
+ )
+}
+
+func (self *_parser) error(place interface{}, msg string, msgValues ...interface{}) *Error {
+ idx := file.Idx(0)
+ switch place := place.(type) {
+ case int:
+ idx = self.idxOf(place)
+ case file.Idx:
+ if place == 0 {
+ idx = self.idxOf(self.chrOffset)
+ } else {
+ idx = place
+ }
+ default:
+ panic(fmt.Errorf("error(%T, ...)", place))
+ }
+
+ position := self.position(idx)
+ msg = fmt.Sprintf(msg, msgValues...)
+ self.errors.Add(position, msg)
+ return self.errors[len(self.errors)-1]
+}
+
+func (self *_parser) errorUnexpected(idx file.Idx, chr rune) error {
+ if chr == -1 {
+ return self.error(idx, err_UnexpectedEndOfInput)
+ }
+ return self.error(idx, err_UnexpectedToken, token.ILLEGAL)
+}
+
+func (self *_parser) errorUnexpectedToken(tkn token.Token) error {
+ switch tkn {
+ case token.EOF:
+ return self.error(file.Idx(0), err_UnexpectedEndOfInput)
+ }
+ value := tkn.String()
+ switch tkn {
+ case token.BOOLEAN, token.NULL:
+ value = self.literal
+ case token.IDENTIFIER:
+ return self.error(self.idx, "Unexpected identifier")
+ case token.KEYWORD:
+ // TODO Might be a future reserved word
+ return self.error(self.idx, "Unexpected reserved word")
+ case token.NUMBER:
+ return self.error(self.idx, "Unexpected number")
+ case token.STRING:
+ return self.error(self.idx, "Unexpected string")
+ }
+ return self.error(self.idx, err_UnexpectedToken, value)
+}
+
+// ErrorList is a list of *Errors.
+//
+type ErrorList []*Error
+
+// Add adds an Error with given position and message to an ErrorList.
+func (self *ErrorList) Add(position file.Position, msg string) {
+ *self = append(*self, &Error{position, msg})
+}
+
+// Reset resets an ErrorList to no errors.
+func (self *ErrorList) Reset() { *self = (*self)[0:0] }
+
+func (self ErrorList) Len() int { return len(self) }
+func (self ErrorList) Swap(i, j int) { self[i], self[j] = self[j], self[i] }
+func (self ErrorList) Less(i, j int) bool {
+ x := &self[i].Position
+ y := &self[j].Position
+ if x.Filename < y.Filename {
+ return true
+ }
+ if x.Filename == y.Filename {
+ if x.Line < y.Line {
+ return true
+ }
+ if x.Line == y.Line {
+ return x.Column < y.Column
+ }
+ }
+ return false
+}
+
+func (self ErrorList) Sort() {
+ sort.Sort(self)
+}
+
+// Error implements the Error interface.
+func (self ErrorList) Error() string {
+ switch len(self) {
+ case 0:
+ return "no errors"
+ case 1:
+ return self[0].Error()
+ }
+ return fmt.Sprintf("%s (and %d more errors)", self[0].Error(), len(self)-1)
+}
+
+// Err returns an error equivalent to this ErrorList.
+// If the list is empty, Err returns nil.
+func (self ErrorList) Err() error {
+ if len(self) == 0 {
+ return nil
+ }
+ return self
+}
diff --git a/vendor/github.com/robertkrimen/otto/parser/expression.go b/vendor/github.com/robertkrimen/otto/parser/expression.go
new file mode 100644
index 000000000..63a169d40
--- /dev/null
+++ b/vendor/github.com/robertkrimen/otto/parser/expression.go
@@ -0,0 +1,1005 @@
+package parser
+
+import (
+ "regexp"
+
+ "github.com/robertkrimen/otto/ast"
+ "github.com/robertkrimen/otto/file"
+ "github.com/robertkrimen/otto/token"
+)
+
+func (self *_parser) parseIdentifier() *ast.Identifier {
+ literal := self.literal
+ idx := self.idx
+ if self.mode&StoreComments != 0 {
+ self.comments.MarkComments(ast.LEADING)
+ }
+ self.next()
+ exp := &ast.Identifier{
+ Name: literal,
+ Idx: idx,
+ }
+
+ if self.mode&StoreComments != 0 {
+ self.comments.SetExpression(exp)
+ }
+
+ return exp
+}
+
+func (self *_parser) parsePrimaryExpression() ast.Expression {
+ literal := self.literal
+ idx := self.idx
+ switch self.token {
+ case token.IDENTIFIER:
+ self.next()
+ if len(literal) > 1 {
+ tkn, strict := token.IsKeyword(literal)
+ if tkn == token.KEYWORD {
+ if !strict {
+ self.error(idx, "Unexpected reserved word")
+ }
+ }
+ }
+ return &ast.Identifier{
+ Name: literal,
+ Idx: idx,
+ }
+ case token.NULL:
+ self.next()
+ return &ast.NullLiteral{
+ Idx: idx,
+ Literal: literal,
+ }
+ case token.BOOLEAN:
+ self.next()
+ value := false
+ switch literal {
+ case "true":
+ value = true
+ case "false":
+ value = false
+ default:
+ self.error(idx, "Illegal boolean literal")
+ }
+ return &ast.BooleanLiteral{
+ Idx: idx,
+ Literal: literal,
+ Value: value,
+ }
+ case token.STRING:
+ self.next()
+ value, err := parseStringLiteral(literal[1 : len(literal)-1])
+ if err != nil {
+ self.error(idx, err.Error())
+ }
+ return &ast.StringLiteral{
+ Idx: idx,
+ Literal: literal,
+ Value: value,
+ }
+ case token.NUMBER:
+ self.next()
+ value, err := parseNumberLiteral(literal)
+ if err != nil {
+ self.error(idx, err.Error())
+ value = 0
+ }
+ return &ast.NumberLiteral{
+ Idx: idx,
+ Literal: literal,
+ Value: value,
+ }
+ case token.SLASH, token.QUOTIENT_ASSIGN:
+ return self.parseRegExpLiteral()
+ case token.LEFT_BRACE:
+ return self.parseObjectLiteral()
+ case token.LEFT_BRACKET:
+ return self.parseArrayLiteral()
+ case token.LEFT_PARENTHESIS:
+ self.expect(token.LEFT_PARENTHESIS)
+ expression := self.parseExpression()
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.expect(token.RIGHT_PARENTHESIS)
+ return expression
+ case token.THIS:
+ self.next()
+ return &ast.ThisExpression{
+ Idx: idx,
+ }
+ case token.FUNCTION:
+ return self.parseFunction(false)
+ }
+
+ self.errorUnexpectedToken(self.token)
+ self.nextStatement()
+ return &ast.BadExpression{From: idx, To: self.idx}
+}
+
+func (self *_parser) parseRegExpLiteral() *ast.RegExpLiteral {
+
+ offset := self.chrOffset - 1 // Opening slash already gotten
+ if self.token == token.QUOTIENT_ASSIGN {
+ offset -= 1 // =
+ }
+ idx := self.idxOf(offset)
+
+ pattern, err := self.scanString(offset)
+ endOffset := self.chrOffset
+
+ self.next()
+ if err == nil {
+ pattern = pattern[1 : len(pattern)-1]
+ }
+
+ flags := ""
+ if self.token == token.IDENTIFIER { // gim
+
+ flags = self.literal
+ self.next()
+ endOffset = self.chrOffset - 1
+ }
+
+ var value string
+ // TODO 15.10
+ {
+ // Test during parsing that this is a valid regular expression
+ // Sorry, (?=) and (?!) are invalid (for now)
+ pattern, err := TransformRegExp(pattern)
+ if err != nil {
+ if pattern == "" || self.mode&IgnoreRegExpErrors == 0 {
+ self.error(idx, "Invalid regular expression: %s", err.Error())
+ }
+ } else {
+ _, err = regexp.Compile(pattern)
+ if err != nil {
+ // We should not get here, ParseRegExp should catch any errors
+ self.error(idx, "Invalid regular expression: %s", err.Error()[22:]) // Skip redundant "parse regexp error"
+ } else {
+ value = pattern
+ }
+ }
+ }
+
+ literal := self.str[offset:endOffset]
+
+ return &ast.RegExpLiteral{
+ Idx: idx,
+ Literal: literal,
+ Pattern: pattern,
+ Flags: flags,
+ Value: value,
+ }
+}
+
+func (self *_parser) parseVariableDeclaration(declarationList *[]*ast.VariableExpression) ast.Expression {
+
+ if self.token != token.IDENTIFIER {
+ idx := self.expect(token.IDENTIFIER)
+ self.nextStatement()
+ return &ast.BadExpression{From: idx, To: self.idx}
+ }
+
+ literal := self.literal
+ idx := self.idx
+ self.next()
+ node := &ast.VariableExpression{
+ Name: literal,
+ Idx: idx,
+ }
+ if self.mode&StoreComments != 0 {
+ self.comments.SetExpression(node)
+ }
+
+ if declarationList != nil {
+ *declarationList = append(*declarationList, node)
+ }
+
+ if self.token == token.ASSIGN {
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.next()
+ node.Initializer = self.parseAssignmentExpression()
+ }
+
+ return node
+}
+
+func (self *_parser) parseVariableDeclarationList(var_ file.Idx) []ast.Expression {
+
+ var declarationList []*ast.VariableExpression // Avoid bad expressions
+ var list []ast.Expression
+
+ for {
+ if self.mode&StoreComments != 0 {
+ self.comments.MarkComments(ast.LEADING)
+ }
+ decl := self.parseVariableDeclaration(&declarationList)
+ list = append(list, decl)
+ if self.token != token.COMMA {
+ break
+ }
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.next()
+ }
+
+ self.scope.declare(&ast.VariableDeclaration{
+ Var: var_,
+ List: declarationList,
+ })
+
+ return list
+}
+
+func (self *_parser) parseObjectPropertyKey() (string, string) {
+ idx, tkn, literal := self.idx, self.token, self.literal
+ value := ""
+ if self.mode&StoreComments != 0 {
+ self.comments.MarkComments(ast.KEY)
+ }
+ self.next()
+
+ switch tkn {
+ case token.IDENTIFIER:
+ value = literal
+ case token.NUMBER:
+ var err error
+ _, err = parseNumberLiteral(literal)
+ if err != nil {
+ self.error(idx, err.Error())
+ } else {
+ value = literal
+ }
+ case token.STRING:
+ var err error
+ value, err = parseStringLiteral(literal[1 : len(literal)-1])
+ if err != nil {
+ self.error(idx, err.Error())
+ }
+ default:
+ // null, false, class, etc.
+ if matchIdentifier.MatchString(literal) {
+ value = literal
+ }
+ }
+ return literal, value
+}
+
+func (self *_parser) parseObjectProperty() ast.Property {
+ literal, value := self.parseObjectPropertyKey()
+ if literal == "get" && self.token != token.COLON {
+ idx := self.idx
+ _, value := self.parseObjectPropertyKey()
+ parameterList := self.parseFunctionParameterList()
+
+ node := &ast.FunctionLiteral{
+ Function: idx,
+ ParameterList: parameterList,
+ }
+ self.parseFunctionBlock(node)
+ return ast.Property{
+ Key: value,
+ Kind: "get",
+ Value: node,
+ }
+ } else if literal == "set" && self.token != token.COLON {
+ idx := self.idx
+ _, value := self.parseObjectPropertyKey()
+ parameterList := self.parseFunctionParameterList()
+
+ node := &ast.FunctionLiteral{
+ Function: idx,
+ ParameterList: parameterList,
+ }
+ self.parseFunctionBlock(node)
+ return ast.Property{
+ Key: value,
+ Kind: "set",
+ Value: node,
+ }
+ }
+
+ if self.mode&StoreComments != 0 {
+ self.comments.MarkComments(ast.COLON)
+ }
+ self.expect(token.COLON)
+
+ exp := ast.Property{
+ Key: value,
+ Kind: "value",
+ Value: self.parseAssignmentExpression(),
+ }
+
+ if self.mode&StoreComments != 0 {
+ self.comments.SetExpression(exp.Value)
+ }
+ return exp
+}
+
+func (self *_parser) parseObjectLiteral() ast.Expression {
+ var value []ast.Property
+ idx0 := self.expect(token.LEFT_BRACE)
+ for self.token != token.RIGHT_BRACE && self.token != token.EOF {
+ value = append(value, self.parseObjectProperty())
+ if self.token == token.COMMA {
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.next()
+ continue
+ }
+ }
+ if self.mode&StoreComments != 0 {
+ self.comments.MarkComments(ast.FINAL)
+ }
+ idx1 := self.expect(token.RIGHT_BRACE)
+
+ return &ast.ObjectLiteral{
+ LeftBrace: idx0,
+ RightBrace: idx1,
+ Value: value,
+ }
+}
+
+func (self *_parser) parseArrayLiteral() ast.Expression {
+ idx0 := self.expect(token.LEFT_BRACKET)
+ var value []ast.Expression
+ for self.token != token.RIGHT_BRACKET && self.token != token.EOF {
+ if self.token == token.COMMA {
+ // This kind of comment requires a special empty expression node.
+ empty := &ast.EmptyExpression{self.idx, self.idx}
+
+ if self.mode&StoreComments != 0 {
+ self.comments.SetExpression(empty)
+ self.comments.Unset()
+ }
+ value = append(value, empty)
+ self.next()
+ continue
+ }
+
+ exp := self.parseAssignmentExpression()
+
+ value = append(value, exp)
+ if self.token != token.RIGHT_BRACKET {
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.expect(token.COMMA)
+ }
+ }
+ if self.mode&StoreComments != 0 {
+ self.comments.MarkComments(ast.FINAL)
+ }
+ idx1 := self.expect(token.RIGHT_BRACKET)
+
+ return &ast.ArrayLiteral{
+ LeftBracket: idx0,
+ RightBracket: idx1,
+ Value: value,
+ }
+}
+
+func (self *_parser) parseArgumentList() (argumentList []ast.Expression, idx0, idx1 file.Idx) {
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ idx0 = self.expect(token.LEFT_PARENTHESIS)
+ if self.token != token.RIGHT_PARENTHESIS {
+ for {
+ exp := self.parseAssignmentExpression()
+ if self.mode&StoreComments != 0 {
+ self.comments.SetExpression(exp)
+ }
+ argumentList = append(argumentList, exp)
+ if self.token != token.COMMA {
+ break
+ }
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.next()
+ }
+ }
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ idx1 = self.expect(token.RIGHT_PARENTHESIS)
+ return
+}
+
+func (self *_parser) parseCallExpression(left ast.Expression) ast.Expression {
+ argumentList, idx0, idx1 := self.parseArgumentList()
+ exp := &ast.CallExpression{
+ Callee: left,
+ LeftParenthesis: idx0,
+ ArgumentList: argumentList,
+ RightParenthesis: idx1,
+ }
+
+ if self.mode&StoreComments != 0 {
+ self.comments.SetExpression(exp)
+ }
+ return exp
+}
+
+func (self *_parser) parseDotMember(left ast.Expression) ast.Expression {
+ period := self.expect(token.PERIOD)
+
+ literal := self.literal
+ idx := self.idx
+
+ if !matchIdentifier.MatchString(literal) {
+ self.expect(token.IDENTIFIER)
+ self.nextStatement()
+ return &ast.BadExpression{From: period, To: self.idx}
+ }
+
+ self.next()
+
+ return &ast.DotExpression{
+ Left: left,
+ Identifier: &ast.Identifier{
+ Idx: idx,
+ Name: literal,
+ },
+ }
+}
+
+func (self *_parser) parseBracketMember(left ast.Expression) ast.Expression {
+ idx0 := self.expect(token.LEFT_BRACKET)
+ member := self.parseExpression()
+ idx1 := self.expect(token.RIGHT_BRACKET)
+ return &ast.BracketExpression{
+ LeftBracket: idx0,
+ Left: left,
+ Member: member,
+ RightBracket: idx1,
+ }
+}
+
+func (self *_parser) parseNewExpression() ast.Expression {
+ idx := self.expect(token.NEW)
+ callee := self.parseLeftHandSideExpression()
+ node := &ast.NewExpression{
+ New: idx,
+ Callee: callee,
+ }
+ if self.token == token.LEFT_PARENTHESIS {
+ argumentList, idx0, idx1 := self.parseArgumentList()
+ node.ArgumentList = argumentList
+ node.LeftParenthesis = idx0
+ node.RightParenthesis = idx1
+ }
+
+ if self.mode&StoreComments != 0 {
+ self.comments.SetExpression(node)
+ }
+
+ return node
+}
+
+func (self *_parser) parseLeftHandSideExpression() ast.Expression {
+
+ var left ast.Expression
+ if self.token == token.NEW {
+ left = self.parseNewExpression()
+ } else {
+ if self.mode&StoreComments != 0 {
+ self.comments.MarkComments(ast.LEADING)
+ self.comments.MarkPrimary()
+ }
+ left = self.parsePrimaryExpression()
+ }
+
+ if self.mode&StoreComments != 0 {
+ self.comments.SetExpression(left)
+ }
+
+ for {
+ if self.token == token.PERIOD {
+ left = self.parseDotMember(left)
+ } else if self.token == token.LEFT_BRACKET {
+ left = self.parseBracketMember(left)
+ } else {
+ break
+ }
+ }
+
+ return left
+}
+
+func (self *_parser) parseLeftHandSideExpressionAllowCall() ast.Expression {
+
+ allowIn := self.scope.allowIn
+ self.scope.allowIn = true
+ defer func() {
+ self.scope.allowIn = allowIn
+ }()
+
+ var left ast.Expression
+ if self.token == token.NEW {
+ var newComments []*ast.Comment
+ if self.mode&StoreComments != 0 {
+ newComments = self.comments.FetchAll()
+ self.comments.MarkComments(ast.LEADING)
+ self.comments.MarkPrimary()
+ }
+ left = self.parseNewExpression()
+ if self.mode&StoreComments != 0 {
+ self.comments.CommentMap.AddComments(left, newComments, ast.LEADING)
+ }
+ } else {
+ if self.mode&StoreComments != 0 {
+ self.comments.MarkComments(ast.LEADING)
+ self.comments.MarkPrimary()
+ }
+ left = self.parsePrimaryExpression()
+ }
+
+ if self.mode&StoreComments != 0 {
+ self.comments.SetExpression(left)
+ }
+
+ for {
+ if self.token == token.PERIOD {
+ left = self.parseDotMember(left)
+ } else if self.token == token.LEFT_BRACKET {
+ left = self.parseBracketMember(left)
+ } else if self.token == token.LEFT_PARENTHESIS {
+ left = self.parseCallExpression(left)
+ } else {
+ break
+ }
+ }
+
+ return left
+}
+
+func (self *_parser) parsePostfixExpression() ast.Expression {
+ operand := self.parseLeftHandSideExpressionAllowCall()
+
+ switch self.token {
+ case token.INCREMENT, token.DECREMENT:
+ // Make sure there is no line terminator here
+ if self.implicitSemicolon {
+ break
+ }
+ tkn := self.token
+ idx := self.idx
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.next()
+ switch operand.(type) {
+ case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression:
+ default:
+ self.error(idx, "Invalid left-hand side in assignment")
+ self.nextStatement()
+ return &ast.BadExpression{From: idx, To: self.idx}
+ }
+ exp := &ast.UnaryExpression{
+ Operator: tkn,
+ Idx: idx,
+ Operand: operand,
+ Postfix: true,
+ }
+
+ if self.mode&StoreComments != 0 {
+ self.comments.SetExpression(exp)
+ }
+
+ return exp
+ }
+
+ return operand
+}
+
+func (self *_parser) parseUnaryExpression() ast.Expression {
+
+ switch self.token {
+ case token.PLUS, token.MINUS, token.NOT, token.BITWISE_NOT:
+ fallthrough
+ case token.DELETE, token.VOID, token.TYPEOF:
+ tkn := self.token
+ idx := self.idx
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.next()
+
+ return &ast.UnaryExpression{
+ Operator: tkn,
+ Idx: idx,
+ Operand: self.parseUnaryExpression(),
+ }
+ case token.INCREMENT, token.DECREMENT:
+ tkn := self.token
+ idx := self.idx
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.next()
+ operand := self.parseUnaryExpression()
+ switch operand.(type) {
+ case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression:
+ default:
+ self.error(idx, "Invalid left-hand side in assignment")
+ self.nextStatement()
+ return &ast.BadExpression{From: idx, To: self.idx}
+ }
+ return &ast.UnaryExpression{
+ Operator: tkn,
+ Idx: idx,
+ Operand: operand,
+ }
+ }
+
+ return self.parsePostfixExpression()
+}
+
+func (self *_parser) parseMultiplicativeExpression() ast.Expression {
+ next := self.parseUnaryExpression
+ left := next()
+
+ for self.token == token.MULTIPLY || self.token == token.SLASH ||
+ self.token == token.REMAINDER {
+ tkn := self.token
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.next()
+
+ left = &ast.BinaryExpression{
+ Operator: tkn,
+ Left: left,
+ Right: next(),
+ }
+ }
+
+ return left
+}
+
+func (self *_parser) parseAdditiveExpression() ast.Expression {
+ next := self.parseMultiplicativeExpression
+ left := next()
+
+ for self.token == token.PLUS || self.token == token.MINUS {
+ tkn := self.token
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.next()
+
+ left = &ast.BinaryExpression{
+ Operator: tkn,
+ Left: left,
+ Right: next(),
+ }
+ }
+
+ return left
+}
+
+func (self *_parser) parseShiftExpression() ast.Expression {
+ next := self.parseAdditiveExpression
+ left := next()
+
+ for self.token == token.SHIFT_LEFT || self.token == token.SHIFT_RIGHT ||
+ self.token == token.UNSIGNED_SHIFT_RIGHT {
+ tkn := self.token
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.next()
+
+ left = &ast.BinaryExpression{
+ Operator: tkn,
+ Left: left,
+ Right: next(),
+ }
+ }
+
+ return left
+}
+
+func (self *_parser) parseRelationalExpression() ast.Expression {
+ next := self.parseShiftExpression
+ left := next()
+
+ allowIn := self.scope.allowIn
+ self.scope.allowIn = true
+ defer func() {
+ self.scope.allowIn = allowIn
+ }()
+
+ switch self.token {
+ case token.LESS, token.LESS_OR_EQUAL, token.GREATER, token.GREATER_OR_EQUAL:
+ tkn := self.token
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.next()
+
+ exp := &ast.BinaryExpression{
+ Operator: tkn,
+ Left: left,
+ Right: self.parseRelationalExpression(),
+ Comparison: true,
+ }
+ return exp
+ case token.INSTANCEOF:
+ tkn := self.token
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.next()
+
+ exp := &ast.BinaryExpression{
+ Operator: tkn,
+ Left: left,
+ Right: self.parseRelationalExpression(),
+ }
+ return exp
+ case token.IN:
+ if !allowIn {
+ return left
+ }
+ tkn := self.token
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.next()
+
+ exp := &ast.BinaryExpression{
+ Operator: tkn,
+ Left: left,
+ Right: self.parseRelationalExpression(),
+ }
+ return exp
+ }
+
+ return left
+}
+
+func (self *_parser) parseEqualityExpression() ast.Expression {
+ next := self.parseRelationalExpression
+ left := next()
+
+ for self.token == token.EQUAL || self.token == token.NOT_EQUAL ||
+ self.token == token.STRICT_EQUAL || self.token == token.STRICT_NOT_EQUAL {
+ tkn := self.token
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.next()
+
+ left = &ast.BinaryExpression{
+ Operator: tkn,
+ Left: left,
+ Right: next(),
+ Comparison: true,
+ }
+ }
+
+ return left
+}
+
+func (self *_parser) parseBitwiseAndExpression() ast.Expression {
+ next := self.parseEqualityExpression
+ left := next()
+
+ for self.token == token.AND {
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ tkn := self.token
+ self.next()
+
+ left = &ast.BinaryExpression{
+ Operator: tkn,
+ Left: left,
+ Right: next(),
+ }
+ }
+
+ return left
+}
+
+func (self *_parser) parseBitwiseExclusiveOrExpression() ast.Expression {
+ next := self.parseBitwiseAndExpression
+ left := next()
+
+ for self.token == token.EXCLUSIVE_OR {
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ tkn := self.token
+ self.next()
+
+ left = &ast.BinaryExpression{
+ Operator: tkn,
+ Left: left,
+ Right: next(),
+ }
+ }
+
+ return left
+}
+
+func (self *_parser) parseBitwiseOrExpression() ast.Expression {
+ next := self.parseBitwiseExclusiveOrExpression
+ left := next()
+
+ for self.token == token.OR {
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ tkn := self.token
+ self.next()
+
+ left = &ast.BinaryExpression{
+ Operator: tkn,
+ Left: left,
+ Right: next(),
+ }
+ }
+
+ return left
+}
+
+func (self *_parser) parseLogicalAndExpression() ast.Expression {
+ next := self.parseBitwiseOrExpression
+ left := next()
+
+ for self.token == token.LOGICAL_AND {
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ tkn := self.token
+ self.next()
+
+ left = &ast.BinaryExpression{
+ Operator: tkn,
+ Left: left,
+ Right: next(),
+ }
+ }
+
+ return left
+}
+
+func (self *_parser) parseLogicalOrExpression() ast.Expression {
+ next := self.parseLogicalAndExpression
+ left := next()
+
+ for self.token == token.LOGICAL_OR {
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ tkn := self.token
+ self.next()
+
+ left = &ast.BinaryExpression{
+ Operator: tkn,
+ Left: left,
+ Right: next(),
+ }
+ }
+
+ return left
+}
+
+func (self *_parser) parseConditionlExpression() ast.Expression {
+ left := self.parseLogicalOrExpression()
+
+ if self.token == token.QUESTION_MARK {
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.next()
+
+ consequent := self.parseAssignmentExpression()
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.expect(token.COLON)
+ exp := &ast.ConditionalExpression{
+ Test: left,
+ Consequent: consequent,
+ Alternate: self.parseAssignmentExpression(),
+ }
+
+ return exp
+ }
+
+ return left
+}
+
+func (self *_parser) parseAssignmentExpression() ast.Expression {
+ left := self.parseConditionlExpression()
+ var operator token.Token
+ switch self.token {
+ case token.ASSIGN:
+ operator = self.token
+ case token.ADD_ASSIGN:
+ operator = token.PLUS
+ case token.SUBTRACT_ASSIGN:
+ operator = token.MINUS
+ case token.MULTIPLY_ASSIGN:
+ operator = token.MULTIPLY
+ case token.QUOTIENT_ASSIGN:
+ operator = token.SLASH
+ case token.REMAINDER_ASSIGN:
+ operator = token.REMAINDER
+ case token.AND_ASSIGN:
+ operator = token.AND
+ case token.AND_NOT_ASSIGN:
+ operator = token.AND_NOT
+ case token.OR_ASSIGN:
+ operator = token.OR
+ case token.EXCLUSIVE_OR_ASSIGN:
+ operator = token.EXCLUSIVE_OR
+ case token.SHIFT_LEFT_ASSIGN:
+ operator = token.SHIFT_LEFT
+ case token.SHIFT_RIGHT_ASSIGN:
+ operator = token.SHIFT_RIGHT
+ case token.UNSIGNED_SHIFT_RIGHT_ASSIGN:
+ operator = token.UNSIGNED_SHIFT_RIGHT
+ }
+
+ if operator != 0 {
+ idx := self.idx
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.next()
+ switch left.(type) {
+ case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression:
+ default:
+ self.error(left.Idx0(), "Invalid left-hand side in assignment")
+ self.nextStatement()
+ return &ast.BadExpression{From: idx, To: self.idx}
+ }
+
+ exp := &ast.AssignExpression{
+ Left: left,
+ Operator: operator,
+ Right: self.parseAssignmentExpression(),
+ }
+
+ if self.mode&StoreComments != 0 {
+ self.comments.SetExpression(exp)
+ }
+
+ return exp
+ }
+
+ return left
+}
+
+func (self *_parser) parseExpression() ast.Expression {
+ next := self.parseAssignmentExpression
+ left := next()
+
+ if self.token == token.COMMA {
+ sequence := []ast.Expression{left}
+ for {
+ if self.token != token.COMMA {
+ break
+ }
+ self.next()
+ sequence = append(sequence, next())
+ }
+ return &ast.SequenceExpression{
+ Sequence: sequence,
+ }
+ }
+
+ return left
+}
diff --git a/vendor/github.com/robertkrimen/otto/parser/lexer.go b/vendor/github.com/robertkrimen/otto/parser/lexer.go
new file mode 100644
index 000000000..d9d69e124
--- /dev/null
+++ b/vendor/github.com/robertkrimen/otto/parser/lexer.go
@@ -0,0 +1,866 @@
+package parser
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "regexp"
+ "strconv"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+
+ "github.com/robertkrimen/otto/ast"
+ "github.com/robertkrimen/otto/file"
+ "github.com/robertkrimen/otto/token"
+)
+
+type _chr struct {
+ value rune
+ width int
+}
+
+var matchIdentifier = regexp.MustCompile(`^[$_\p{L}][$_\p{L}\d}]*$`)
+
+func isDecimalDigit(chr rune) bool {
+ return '0' <= chr && chr <= '9'
+}
+
+func digitValue(chr rune) int {
+ switch {
+ case '0' <= chr && chr <= '9':
+ return int(chr - '0')
+ case 'a' <= chr && chr <= 'f':
+ return int(chr - 'a' + 10)
+ case 'A' <= chr && chr <= 'F':
+ return int(chr - 'A' + 10)
+ }
+ return 16 // Larger than any legal digit value
+}
+
+func isDigit(chr rune, base int) bool {
+ return digitValue(chr) < base
+}
+
+func isIdentifierStart(chr rune) bool {
+ return chr == '$' || chr == '_' || chr == '\\' ||
+ 'a' <= chr && chr <= 'z' || 'A' <= chr && chr <= 'Z' ||
+ chr >= utf8.RuneSelf && unicode.IsLetter(chr)
+}
+
+func isIdentifierPart(chr rune) bool {
+ return chr == '$' || chr == '_' || chr == '\\' ||
+ 'a' <= chr && chr <= 'z' || 'A' <= chr && chr <= 'Z' ||
+ '0' <= chr && chr <= '9' ||
+ chr >= utf8.RuneSelf && (unicode.IsLetter(chr) || unicode.IsDigit(chr))
+}
+
+func (self *_parser) scanIdentifier() (string, error) {
+ offset := self.chrOffset
+ parse := false
+ for isIdentifierPart(self.chr) {
+ if self.chr == '\\' {
+ distance := self.chrOffset - offset
+ self.read()
+ if self.chr != 'u' {
+ return "", fmt.Errorf("Invalid identifier escape character: %c (%s)", self.chr, string(self.chr))
+ }
+ parse = true
+ var value rune
+ for j := 0; j < 4; j++ {
+ self.read()
+ decimal, ok := hex2decimal(byte(self.chr))
+ if !ok {
+ return "", fmt.Errorf("Invalid identifier escape character: %c (%s)", self.chr, string(self.chr))
+ }
+ value = value<<4 | decimal
+ }
+ if value == '\\' {
+ return "", fmt.Errorf("Invalid identifier escape value: %c (%s)", value, string(value))
+ } else if distance == 0 {
+ if !isIdentifierStart(value) {
+ return "", fmt.Errorf("Invalid identifier escape value: %c (%s)", value, string(value))
+ }
+ } else if distance > 0 {
+ if !isIdentifierPart(value) {
+ return "", fmt.Errorf("Invalid identifier escape value: %c (%s)", value, string(value))
+ }
+ }
+ }
+ self.read()
+ }
+ literal := string(self.str[offset:self.chrOffset])
+ if parse {
+ return parseStringLiteral(literal)
+ }
+ return literal, nil
+}
+
+// 7.2
+func isLineWhiteSpace(chr rune) bool {
+ switch chr {
+ case '\u0009', '\u000b', '\u000c', '\u0020', '\u00a0', '\ufeff':
+ return true
+ case '\u000a', '\u000d', '\u2028', '\u2029':
+ return false
+ case '\u0085':
+ return false
+ }
+ return unicode.IsSpace(chr)
+}
+
+// 7.3
+func isLineTerminator(chr rune) bool {
+ switch chr {
+ case '\u000a', '\u000d', '\u2028', '\u2029':
+ return true
+ }
+ return false
+}
+
+func (self *_parser) scan() (tkn token.Token, literal string, idx file.Idx) {
+
+ self.implicitSemicolon = false
+
+ for {
+ self.skipWhiteSpace()
+
+ idx = self.idxOf(self.chrOffset)
+ insertSemicolon := false
+
+ switch chr := self.chr; {
+ case isIdentifierStart(chr):
+ var err error
+ literal, err = self.scanIdentifier()
+ if err != nil {
+ tkn = token.ILLEGAL
+ break
+ }
+ if len(literal) > 1 {
+ // Keywords are longer than 1 character, avoid lookup otherwise
+ var strict bool
+ tkn, strict = token.IsKeyword(literal)
+
+ switch tkn {
+
+ case 0: // Not a keyword
+ if literal == "true" || literal == "false" {
+ self.insertSemicolon = true
+ tkn = token.BOOLEAN
+ return
+ } else if literal == "null" {
+ self.insertSemicolon = true
+ tkn = token.NULL
+ return
+ }
+
+ case token.KEYWORD:
+ tkn = token.KEYWORD
+ if strict {
+ // TODO If strict and in strict mode, then this is not a break
+ break
+ }
+ return
+
+ case
+ token.THIS,
+ token.BREAK,
+ token.THROW, // A newline after a throw is not allowed, but we need to detect it
+ token.RETURN,
+ token.CONTINUE,
+ token.DEBUGGER:
+ self.insertSemicolon = true
+ return
+
+ default:
+ return
+
+ }
+ }
+ self.insertSemicolon = true
+ tkn = token.IDENTIFIER
+ return
+ case '0' <= chr && chr <= '9':
+ self.insertSemicolon = true
+ tkn, literal = self.scanNumericLiteral(false)
+ return
+ default:
+ self.read()
+ switch chr {
+ case -1:
+ if self.insertSemicolon {
+ self.insertSemicolon = false
+ self.implicitSemicolon = true
+ }
+ tkn = token.EOF
+ case '\r', '\n', '\u2028', '\u2029':
+ self.insertSemicolon = false
+ self.implicitSemicolon = true
+ self.comments.AtLineBreak()
+ continue
+ case ':':
+ tkn = token.COLON
+ case '.':
+ if digitValue(self.chr) < 10 {
+ insertSemicolon = true
+ tkn, literal = self.scanNumericLiteral(true)
+ } else {
+ tkn = token.PERIOD
+ }
+ case ',':
+ tkn = token.COMMA
+ case ';':
+ tkn = token.SEMICOLON
+ case '(':
+ tkn = token.LEFT_PARENTHESIS
+ case ')':
+ tkn = token.RIGHT_PARENTHESIS
+ insertSemicolon = true
+ case '[':
+ tkn = token.LEFT_BRACKET
+ case ']':
+ tkn = token.RIGHT_BRACKET
+ insertSemicolon = true
+ case '{':
+ tkn = token.LEFT_BRACE
+ case '}':
+ tkn = token.RIGHT_BRACE
+ insertSemicolon = true
+ case '+':
+ tkn = self.switch3(token.PLUS, token.ADD_ASSIGN, '+', token.INCREMENT)
+ if tkn == token.INCREMENT {
+ insertSemicolon = true
+ }
+ case '-':
+ tkn = self.switch3(token.MINUS, token.SUBTRACT_ASSIGN, '-', token.DECREMENT)
+ if tkn == token.DECREMENT {
+ insertSemicolon = true
+ }
+ case '*':
+ tkn = self.switch2(token.MULTIPLY, token.MULTIPLY_ASSIGN)
+ case '/':
+ if self.chr == '/' {
+ if self.mode&StoreComments != 0 {
+ literal := string(self.readSingleLineComment())
+ self.comments.AddComment(ast.NewComment(literal, self.idx))
+ continue
+ }
+ self.skipSingleLineComment()
+ continue
+ } else if self.chr == '*' {
+ if self.mode&StoreComments != 0 {
+ literal = string(self.readMultiLineComment())
+ self.comments.AddComment(ast.NewComment(literal, self.idx))
+ continue
+ }
+ self.skipMultiLineComment()
+ continue
+ } else {
+ // Could be division, could be RegExp literal
+ tkn = self.switch2(token.SLASH, token.QUOTIENT_ASSIGN)
+ insertSemicolon = true
+ }
+ case '%':
+ tkn = self.switch2(token.REMAINDER, token.REMAINDER_ASSIGN)
+ case '^':
+ tkn = self.switch2(token.EXCLUSIVE_OR, token.EXCLUSIVE_OR_ASSIGN)
+ case '<':
+ tkn = self.switch4(token.LESS, token.LESS_OR_EQUAL, '<', token.SHIFT_LEFT, token.SHIFT_LEFT_ASSIGN)
+ case '>':
+ tkn = self.switch6(token.GREATER, token.GREATER_OR_EQUAL, '>', token.SHIFT_RIGHT, token.SHIFT_RIGHT_ASSIGN, '>', token.UNSIGNED_SHIFT_RIGHT, token.UNSIGNED_SHIFT_RIGHT_ASSIGN)
+ case '=':
+ tkn = self.switch2(token.ASSIGN, token.EQUAL)
+ if tkn == token.EQUAL && self.chr == '=' {
+ self.read()
+ tkn = token.STRICT_EQUAL
+ }
+ case '!':
+ tkn = self.switch2(token.NOT, token.NOT_EQUAL)
+ if tkn == token.NOT_EQUAL && self.chr == '=' {
+ self.read()
+ tkn = token.STRICT_NOT_EQUAL
+ }
+ case '&':
+ if self.chr == '^' {
+ self.read()
+ tkn = self.switch2(token.AND_NOT, token.AND_NOT_ASSIGN)
+ } else {
+ tkn = self.switch3(token.AND, token.AND_ASSIGN, '&', token.LOGICAL_AND)
+ }
+ case '|':
+ tkn = self.switch3(token.OR, token.OR_ASSIGN, '|', token.LOGICAL_OR)
+ case '~':
+ tkn = token.BITWISE_NOT
+ case '?':
+ tkn = token.QUESTION_MARK
+ case '"', '\'':
+ insertSemicolon = true
+ tkn = token.STRING
+ var err error
+ literal, err = self.scanString(self.chrOffset - 1)
+ if err != nil {
+ tkn = token.ILLEGAL
+ }
+ default:
+ self.errorUnexpected(idx, chr)
+ tkn = token.ILLEGAL
+ }
+ }
+ self.insertSemicolon = insertSemicolon
+ return
+ }
+}
+
+func (self *_parser) switch2(tkn0, tkn1 token.Token) token.Token {
+ if self.chr == '=' {
+ self.read()
+ return tkn1
+ }
+ return tkn0
+}
+
+func (self *_parser) switch3(tkn0, tkn1 token.Token, chr2 rune, tkn2 token.Token) token.Token {
+ if self.chr == '=' {
+ self.read()
+ return tkn1
+ }
+ if self.chr == chr2 {
+ self.read()
+ return tkn2
+ }
+ return tkn0
+}
+
+func (self *_parser) switch4(tkn0, tkn1 token.Token, chr2 rune, tkn2, tkn3 token.Token) token.Token {
+ if self.chr == '=' {
+ self.read()
+ return tkn1
+ }
+ if self.chr == chr2 {
+ self.read()
+ if self.chr == '=' {
+ self.read()
+ return tkn3
+ }
+ return tkn2
+ }
+ return tkn0
+}
+
+func (self *_parser) switch6(tkn0, tkn1 token.Token, chr2 rune, tkn2, tkn3 token.Token, chr3 rune, tkn4, tkn5 token.Token) token.Token {
+ if self.chr == '=' {
+ self.read()
+ return tkn1
+ }
+ if self.chr == chr2 {
+ self.read()
+ if self.chr == '=' {
+ self.read()
+ return tkn3
+ }
+ if self.chr == chr3 {
+ self.read()
+ if self.chr == '=' {
+ self.read()
+ return tkn5
+ }
+ return tkn4
+ }
+ return tkn2
+ }
+ return tkn0
+}
+
+func (self *_parser) chrAt(index int) _chr {
+ value, width := utf8.DecodeRuneInString(self.str[index:])
+ return _chr{
+ value: value,
+ width: width,
+ }
+}
+
+func (self *_parser) _peek() rune {
+ if self.offset+1 < self.length {
+ return rune(self.str[self.offset+1])
+ }
+ return -1
+}
+
+func (self *_parser) read() {
+ if self.offset < self.length {
+ self.chrOffset = self.offset
+ chr, width := rune(self.str[self.offset]), 1
+ if chr >= utf8.RuneSelf { // !ASCII
+ chr, width = utf8.DecodeRuneInString(self.str[self.offset:])
+ if chr == utf8.RuneError && width == 1 {
+ self.error(self.chrOffset, "Invalid UTF-8 character")
+ }
+ }
+ self.offset += width
+ self.chr = chr
+ } else {
+ self.chrOffset = self.length
+ self.chr = -1 // EOF
+ }
+}
+
+// This is here since the functions are so similar
+func (self *_RegExp_parser) read() {
+ if self.offset < self.length {
+ self.chrOffset = self.offset
+ chr, width := rune(self.str[self.offset]), 1
+ if chr >= utf8.RuneSelf { // !ASCII
+ chr, width = utf8.DecodeRuneInString(self.str[self.offset:])
+ if chr == utf8.RuneError && width == 1 {
+ self.error(self.chrOffset, "Invalid UTF-8 character")
+ }
+ }
+ self.offset += width
+ self.chr = chr
+ } else {
+ self.chrOffset = self.length
+ self.chr = -1 // EOF
+ }
+}
+
+func (self *_parser) readSingleLineComment() (result []rune) {
+ for self.chr != -1 {
+ self.read()
+ if isLineTerminator(self.chr) {
+ return
+ }
+ result = append(result, self.chr)
+ }
+
+ // Get rid of the trailing -1
+ result = result[:len(result)-1]
+
+ return
+}
+
+func (self *_parser) readMultiLineComment() (result []rune) {
+ self.read()
+ for self.chr >= 0 {
+ chr := self.chr
+ self.read()
+ if chr == '*' && self.chr == '/' {
+ self.read()
+ return
+ }
+
+ result = append(result, chr)
+ }
+
+ self.errorUnexpected(0, self.chr)
+
+ return
+}
+
+func (self *_parser) skipSingleLineComment() {
+ for self.chr != -1 {
+ self.read()
+ if isLineTerminator(self.chr) {
+ return
+ }
+ }
+}
+
+func (self *_parser) skipMultiLineComment() {
+ self.read()
+ for self.chr >= 0 {
+ chr := self.chr
+ self.read()
+ if chr == '*' && self.chr == '/' {
+ self.read()
+ return
+ }
+ }
+
+ self.errorUnexpected(0, self.chr)
+}
+
+func (self *_parser) skipWhiteSpace() {
+ for {
+ switch self.chr {
+ case ' ', '\t', '\f', '\v', '\u00a0', '\ufeff':
+ self.read()
+ continue
+ case '\r':
+ if self._peek() == '\n' {
+ self.comments.AtLineBreak()
+ self.read()
+ }
+ fallthrough
+ case '\u2028', '\u2029', '\n':
+ if self.insertSemicolon {
+ return
+ }
+ self.comments.AtLineBreak()
+ self.read()
+ continue
+ }
+ if self.chr >= utf8.RuneSelf {
+ if unicode.IsSpace(self.chr) {
+ self.read()
+ continue
+ }
+ }
+ break
+ }
+}
+
+func (self *_parser) skipLineWhiteSpace() {
+ for isLineWhiteSpace(self.chr) {
+ self.read()
+ }
+}
+
+func (self *_parser) scanMantissa(base int) {
+ for digitValue(self.chr) < base {
+ self.read()
+ }
+}
+
+func (self *_parser) scanEscape(quote rune) {
+
+ var length, base uint32
+ switch self.chr {
+ //case '0', '1', '2', '3', '4', '5', '6', '7':
+ // Octal:
+ // length, base, limit = 3, 8, 255
+ case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '"', '\'', '0':
+ self.read()
+ return
+ case '\r', '\n', '\u2028', '\u2029':
+ self.scanNewline()
+ return
+ case 'x':
+ self.read()
+ length, base = 2, 16
+ case 'u':
+ self.read()
+ length, base = 4, 16
+ default:
+ self.read() // Always make progress
+ return
+ }
+
+ var value uint32
+ for ; length > 0 && self.chr != quote && self.chr >= 0; length-- {
+ digit := uint32(digitValue(self.chr))
+ if digit >= base {
+ break
+ }
+ value = value*base + digit
+ self.read()
+ }
+}
+
+func (self *_parser) scanString(offset int) (string, error) {
+ // " ' /
+ quote := rune(self.str[offset])
+
+ for self.chr != quote {
+ chr := self.chr
+ if chr == '\n' || chr == '\r' || chr == '\u2028' || chr == '\u2029' || chr < 0 {
+ goto newline
+ }
+ self.read()
+ if chr == '\\' {
+ if quote == '/' {
+ if self.chr == '\n' || self.chr == '\r' || self.chr == '\u2028' || self.chr == '\u2029' || self.chr < 0 {
+ goto newline
+ }
+ self.read()
+ } else {
+ self.scanEscape(quote)
+ }
+ } else if chr == '[' && quote == '/' {
+ // Allow a slash (/) in a bracket character class ([...])
+ // TODO Fix this, this is hacky...
+ quote = -1
+ } else if chr == ']' && quote == -1 {
+ quote = '/'
+ }
+ }
+
+ // " ' /
+ self.read()
+
+ return string(self.str[offset:self.chrOffset]), nil
+
+newline:
+ self.scanNewline()
+ err := "String not terminated"
+ if quote == '/' {
+ err = "Invalid regular expression: missing /"
+ self.error(self.idxOf(offset), err)
+ }
+ return "", errors.New(err)
+}
+
+func (self *_parser) scanNewline() {
+ if self.chr == '\r' {
+ self.read()
+ if self.chr != '\n' {
+ return
+ }
+ }
+ self.read()
+}
+
+func hex2decimal(chr byte) (value rune, ok bool) {
+ {
+ chr := rune(chr)
+ switch {
+ case '0' <= chr && chr <= '9':
+ return chr - '0', true
+ case 'a' <= chr && chr <= 'f':
+ return chr - 'a' + 10, true
+ case 'A' <= chr && chr <= 'F':
+ return chr - 'A' + 10, true
+ }
+ return
+ }
+}
+
+func parseNumberLiteral(literal string) (value interface{}, err error) {
+ // TODO Is Uint okay? What about -MAX_UINT
+ value, err = strconv.ParseInt(literal, 0, 64)
+ if err == nil {
+ return
+ }
+
+ parseIntErr := err // Save this first error, just in case
+
+ value, err = strconv.ParseFloat(literal, 64)
+ if err == nil {
+ return
+ } else if err.(*strconv.NumError).Err == strconv.ErrRange {
+ // Infinity, etc.
+ return value, nil
+ }
+
+ err = parseIntErr
+
+ if err.(*strconv.NumError).Err == strconv.ErrRange {
+ if len(literal) > 2 && literal[0] == '0' && (literal[1] == 'X' || literal[1] == 'x') {
+ // Could just be a very large number (e.g. 0x8000000000000000)
+ var value float64
+ literal = literal[2:]
+ for _, chr := range literal {
+ digit := digitValue(chr)
+ if digit >= 16 {
+ goto error
+ }
+ value = value*16 + float64(digit)
+ }
+ return value, nil
+ }
+ }
+
+error:
+ return nil, errors.New("Illegal numeric literal")
+}
+
+func parseStringLiteral(literal string) (string, error) {
+ // Best case scenario...
+ if literal == "" {
+ return "", nil
+ }
+
+ // Slightly less-best case scenario...
+ if !strings.ContainsRune(literal, '\\') {
+ return literal, nil
+ }
+
+ str := literal
+ buffer := bytes.NewBuffer(make([]byte, 0, 3*len(literal)/2))
+
+ for len(str) > 0 {
+ switch chr := str[0]; {
+ // We do not explicitly handle the case of the quote
+ // value, which can be: " ' /
+ // This assumes we're already passed a partially well-formed literal
+ case chr >= utf8.RuneSelf:
+ chr, size := utf8.DecodeRuneInString(str)
+ buffer.WriteRune(chr)
+ str = str[size:]
+ continue
+ case chr != '\\':
+ buffer.WriteByte(chr)
+ str = str[1:]
+ continue
+ }
+
+ if len(str) <= 1 {
+ panic("len(str) <= 1")
+ }
+ chr := str[1]
+ var value rune
+ if chr >= utf8.RuneSelf {
+ str = str[1:]
+ var size int
+ value, size = utf8.DecodeRuneInString(str)
+ str = str[size:] // \ + <character>
+ } else {
+ str = str[2:] // \<character>
+ switch chr {
+ case 'b':
+ value = '\b'
+ case 'f':
+ value = '\f'
+ case 'n':
+ value = '\n'
+ case 'r':
+ value = '\r'
+ case 't':
+ value = '\t'
+ case 'v':
+ value = '\v'
+ case 'x', 'u':
+ size := 0
+ switch chr {
+ case 'x':
+ size = 2
+ case 'u':
+ size = 4
+ }
+ if len(str) < size {
+ return "", fmt.Errorf("invalid escape: \\%s: len(%q) != %d", string(chr), str, size)
+ }
+ for j := 0; j < size; j++ {
+ decimal, ok := hex2decimal(str[j])
+ if !ok {
+ return "", fmt.Errorf("invalid escape: \\%s: %q", string(chr), str[:size])
+ }
+ value = value<<4 | decimal
+ }
+ str = str[size:]
+ if chr == 'x' {
+ break
+ }
+ if value > utf8.MaxRune {
+ panic("value > utf8.MaxRune")
+ }
+ case '0':
+ if len(str) == 0 || '0' > str[0] || str[0] > '7' {
+ value = 0
+ break
+ }
+ fallthrough
+ case '1', '2', '3', '4', '5', '6', '7':
+ // TODO strict
+ value = rune(chr) - '0'
+ j := 0
+ for ; j < 2; j++ {
+ if len(str) < j+1 {
+ break
+ }
+ chr := str[j]
+ if '0' > chr || chr > '7' {
+ break
+ }
+ decimal := rune(str[j]) - '0'
+ value = (value << 3) | decimal
+ }
+ str = str[j:]
+ case '\\':
+ value = '\\'
+ case '\'', '"':
+ value = rune(chr)
+ case '\r':
+ if len(str) > 0 {
+ if str[0] == '\n' {
+ str = str[1:]
+ }
+ }
+ fallthrough
+ case '\n':
+ continue
+ default:
+ value = rune(chr)
+ }
+ }
+ buffer.WriteRune(value)
+ }
+
+ return buffer.String(), nil
+}
+
+func (self *_parser) scanNumericLiteral(decimalPoint bool) (token.Token, string) {
+
+ offset := self.chrOffset
+ tkn := token.NUMBER
+
+ if decimalPoint {
+ offset--
+ self.scanMantissa(10)
+ goto exponent
+ }
+
+ if self.chr == '0' {
+ offset := self.chrOffset
+ self.read()
+ if self.chr == 'x' || self.chr == 'X' {
+ // Hexadecimal
+ self.read()
+ if isDigit(self.chr, 16) {
+ self.read()
+ } else {
+ return token.ILLEGAL, self.str[offset:self.chrOffset]
+ }
+ self.scanMantissa(16)
+
+ if self.chrOffset-offset <= 2 {
+ // Only "0x" or "0X"
+ self.error(0, "Illegal hexadecimal number")
+ }
+
+ goto hexadecimal
+ } else if self.chr == '.' {
+ // Float
+ goto float
+ } else {
+ // Octal, Float
+ if self.chr == 'e' || self.chr == 'E' {
+ goto exponent
+ }
+ self.scanMantissa(8)
+ if self.chr == '8' || self.chr == '9' {
+ return token.ILLEGAL, self.str[offset:self.chrOffset]
+ }
+ goto octal
+ }
+ }
+
+ self.scanMantissa(10)
+
+float:
+ if self.chr == '.' {
+ self.read()
+ self.scanMantissa(10)
+ }
+
+exponent:
+ if self.chr == 'e' || self.chr == 'E' {
+ self.read()
+ if self.chr == '-' || self.chr == '+' {
+ self.read()
+ }
+ if isDecimalDigit(self.chr) {
+ self.read()
+ self.scanMantissa(10)
+ } else {
+ return token.ILLEGAL, self.str[offset:self.chrOffset]
+ }
+ }
+
+hexadecimal:
+octal:
+ if isIdentifierStart(self.chr) || isDecimalDigit(self.chr) {
+ return token.ILLEGAL, self.str[offset:self.chrOffset]
+ }
+
+ return tkn, self.str[offset:self.chrOffset]
+}
diff --git a/vendor/github.com/robertkrimen/otto/parser/parser.go b/vendor/github.com/robertkrimen/otto/parser/parser.go
new file mode 100644
index 000000000..75b7c500c
--- /dev/null
+++ b/vendor/github.com/robertkrimen/otto/parser/parser.go
@@ -0,0 +1,344 @@
+/*
+Package parser implements a parser for JavaScript.
+
+ import (
+ "github.com/robertkrimen/otto/parser"
+ )
+
+Parse and return an AST
+
+ filename := "" // A filename is optional
+ src := `
+ // Sample xyzzy example
+ (function(){
+ if (3.14159 > 0) {
+ console.log("Hello, World.");
+ return;
+ }
+
+ var xyzzy = NaN;
+ console.log("Nothing happens.");
+ return xyzzy;
+ })();
+ `
+
+ // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList
+ program, err := parser.ParseFile(nil, filename, src, 0)
+
+Warning
+
+The parser and AST interfaces are still works-in-progress (particularly where
+node types are concerned) and may change in the future.
+
+*/
+package parser
+
+import (
+ "bytes"
+ "encoding/base64"
+ "errors"
+ "io"
+ "io/ioutil"
+
+ "github.com/robertkrimen/otto/ast"
+ "github.com/robertkrimen/otto/file"
+ "github.com/robertkrimen/otto/token"
+ "gopkg.in/sourcemap.v1"
+)
+
+// A Mode value is a set of flags (or 0). They control optional parser functionality.
+type Mode uint
+
+const (
+ IgnoreRegExpErrors Mode = 1 << iota // Ignore RegExp compatibility errors (allow backtracking)
+ StoreComments // Store the comments from source to the comments map
+)
+
+type _parser struct {
+ str string
+ length int
+ base int
+
+ chr rune // The current character
+ chrOffset int // The offset of current character
+ offset int // The offset after current character (may be greater than 1)
+
+ idx file.Idx // The index of token
+ token token.Token // The token
+ literal string // The literal of the token, if any
+
+ scope *_scope
+ insertSemicolon bool // If we see a newline, then insert an implicit semicolon
+ implicitSemicolon bool // An implicit semicolon exists
+
+ errors ErrorList
+
+ recover struct {
+ // Scratch when trying to seek to the next statement, etc.
+ idx file.Idx
+ count int
+ }
+
+ mode Mode
+
+ file *file.File
+
+ comments *ast.Comments
+}
+
+type Parser interface {
+ Scan() (tkn token.Token, literal string, idx file.Idx)
+}
+
+func _newParser(filename, src string, base int, sm *sourcemap.Consumer) *_parser {
+ return &_parser{
+ chr: ' ', // This is set so we can start scanning by skipping whitespace
+ str: src,
+ length: len(src),
+ base: base,
+ file: file.NewFile(filename, src, base).WithSourceMap(sm),
+ comments: ast.NewComments(),
+ }
+}
+
+// Returns a new Parser.
+func NewParser(filename, src string) Parser {
+ return _newParser(filename, src, 1, nil)
+}
+
+func ReadSource(filename string, src interface{}) ([]byte, error) {
+ if src != nil {
+ switch src := src.(type) {
+ case string:
+ return []byte(src), nil
+ case []byte:
+ return src, nil
+ case *bytes.Buffer:
+ if src != nil {
+ return src.Bytes(), nil
+ }
+ case io.Reader:
+ var bfr bytes.Buffer
+ if _, err := io.Copy(&bfr, src); err != nil {
+ return nil, err
+ }
+ return bfr.Bytes(), nil
+ }
+ return nil, errors.New("invalid source")
+ }
+ return ioutil.ReadFile(filename)
+}
+
+func ReadSourceMap(filename string, src interface{}) (*sourcemap.Consumer, error) {
+ if src == nil {
+ return nil, nil
+ }
+
+ switch src := src.(type) {
+ case string:
+ return sourcemap.Parse(filename, []byte(src))
+ case []byte:
+ return sourcemap.Parse(filename, src)
+ case *bytes.Buffer:
+ if src != nil {
+ return sourcemap.Parse(filename, src.Bytes())
+ }
+ case io.Reader:
+ var bfr bytes.Buffer
+ if _, err := io.Copy(&bfr, src); err != nil {
+ return nil, err
+ }
+ return sourcemap.Parse(filename, bfr.Bytes())
+ case *sourcemap.Consumer:
+ return src, nil
+ }
+
+ return nil, errors.New("invalid sourcemap type")
+}
+
+func ParseFileWithSourceMap(fileSet *file.FileSet, filename string, javascriptSource, sourcemapSource interface{}, mode Mode) (*ast.Program, error) {
+ src, err := ReadSource(filename, javascriptSource)
+ if err != nil {
+ return nil, err
+ }
+
+ if sourcemapSource == nil {
+ lines := bytes.Split(src, []byte("\n"))
+ lastLine := lines[len(lines)-1]
+ if bytes.HasPrefix(lastLine, []byte("//# sourceMappingURL=data:application/json")) {
+ bits := bytes.SplitN(lastLine, []byte(","), 2)
+ if len(bits) == 2 {
+ if d, err := base64.StdEncoding.DecodeString(string(bits[1])); err == nil {
+ sourcemapSource = d
+ }
+ }
+ }
+ }
+
+ sm, err := ReadSourceMap(filename, sourcemapSource)
+ if err != nil {
+ return nil, err
+ }
+
+ base := 1
+ if fileSet != nil {
+ base = fileSet.AddFile(filename, string(src))
+ }
+
+ parser := _newParser(filename, string(src), base, sm)
+ parser.mode = mode
+ program, err := parser.parse()
+ program.Comments = parser.comments.CommentMap
+
+ return program, err
+}
+
+// ParseFile parses the source code of a single JavaScript/ECMAScript source file and returns
+// the corresponding ast.Program node.
+//
+// If fileSet == nil, ParseFile parses source without a FileSet.
+// If fileSet != nil, ParseFile first adds filename and src to fileSet.
+//
+// The filename argument is optional and is used for labelling errors, etc.
+//
+// src may be a string, a byte slice, a bytes.Buffer, or an io.Reader, but it MUST always be in UTF-8.
+//
+// // Parse some JavaScript, yielding a *ast.Program and/or an ErrorList
+// program, err := parser.ParseFile(nil, "", `if (abc > 1) {}`, 0)
+//
+func ParseFile(fileSet *file.FileSet, filename string, src interface{}, mode Mode) (*ast.Program, error) {
+ return ParseFileWithSourceMap(fileSet, filename, src, nil, mode)
+}
+
+// ParseFunction parses a given parameter list and body as a function and returns the
+// corresponding ast.FunctionLiteral node.
+//
+// The parameter list, if any, should be a comma-separated list of identifiers.
+//
+func ParseFunction(parameterList, body string) (*ast.FunctionLiteral, error) {
+
+ src := "(function(" + parameterList + ") {\n" + body + "\n})"
+
+ parser := _newParser("", src, 1, nil)
+ program, err := parser.parse()
+ if err != nil {
+ return nil, err
+ }
+
+ return program.Body[0].(*ast.ExpressionStatement).Expression.(*ast.FunctionLiteral), nil
+}
+
+// Scan reads a single token from the source at the current offset, increments the offset and
+// returns the token.Token token, a string literal representing the value of the token (if applicable)
+// and it's current file.Idx index.
+func (self *_parser) Scan() (tkn token.Token, literal string, idx file.Idx) {
+ return self.scan()
+}
+
+func (self *_parser) slice(idx0, idx1 file.Idx) string {
+ from := int(idx0) - self.base
+ to := int(idx1) - self.base
+ if from >= 0 && to <= len(self.str) {
+ return self.str[from:to]
+ }
+
+ return ""
+}
+
+func (self *_parser) parse() (*ast.Program, error) {
+ self.next()
+ program := self.parseProgram()
+ if false {
+ self.errors.Sort()
+ }
+
+ if self.mode&StoreComments != 0 {
+ self.comments.CommentMap.AddComments(program, self.comments.FetchAll(), ast.TRAILING)
+ }
+
+ return program, self.errors.Err()
+}
+
+func (self *_parser) next() {
+ self.token, self.literal, self.idx = self.scan()
+}
+
+func (self *_parser) optionalSemicolon() {
+ if self.token == token.SEMICOLON {
+ self.next()
+ return
+ }
+
+ if self.implicitSemicolon {
+ self.implicitSemicolon = false
+ return
+ }
+
+ if self.token != token.EOF && self.token != token.RIGHT_BRACE {
+ self.expect(token.SEMICOLON)
+ }
+}
+
+func (self *_parser) semicolon() {
+ if self.token != token.RIGHT_PARENTHESIS && self.token != token.RIGHT_BRACE {
+ if self.implicitSemicolon {
+ self.implicitSemicolon = false
+ return
+ }
+
+ self.expect(token.SEMICOLON)
+ }
+}
+
+func (self *_parser) idxOf(offset int) file.Idx {
+ return file.Idx(self.base + offset)
+}
+
+func (self *_parser) expect(value token.Token) file.Idx {
+ idx := self.idx
+ if self.token != value {
+ self.errorUnexpectedToken(self.token)
+ }
+ self.next()
+ return idx
+}
+
+func lineCount(str string) (int, int) {
+ line, last := 0, -1
+ pair := false
+ for index, chr := range str {
+ switch chr {
+ case '\r':
+ line += 1
+ last = index
+ pair = true
+ continue
+ case '\n':
+ if !pair {
+ line += 1
+ }
+ last = index
+ case '\u2028', '\u2029':
+ line += 1
+ last = index + 2
+ }
+ pair = false
+ }
+ return line, last
+}
+
+func (self *_parser) position(idx file.Idx) file.Position {
+ position := file.Position{}
+ offset := int(idx) - self.base
+ str := self.str[:offset]
+ position.Filename = self.file.Name()
+ line, last := lineCount(str)
+ position.Line = 1 + line
+ if last >= 0 {
+ position.Column = offset - last
+ } else {
+ position.Column = 1 + len(str)
+ }
+
+ return position
+}
diff --git a/vendor/github.com/robertkrimen/otto/parser/regexp.go b/vendor/github.com/robertkrimen/otto/parser/regexp.go
new file mode 100644
index 000000000..f614dae74
--- /dev/null
+++ b/vendor/github.com/robertkrimen/otto/parser/regexp.go
@@ -0,0 +1,358 @@
+package parser
+
+import (
+ "bytes"
+ "fmt"
+ "strconv"
+)
+
+type _RegExp_parser struct {
+ str string
+ length int
+
+ chr rune // The current character
+ chrOffset int // The offset of current character
+ offset int // The offset after current character (may be greater than 1)
+
+ errors []error
+ invalid bool // The input is an invalid JavaScript RegExp
+
+ goRegexp *bytes.Buffer
+}
+
+// TransformRegExp transforms a JavaScript pattern into a Go "regexp" pattern.
+//
+// re2 (Go) cannot do backtracking, so the presence of a lookahead (?=) (?!) or
+// backreference (\1, \2, ...) will cause an error.
+//
+// re2 (Go) has a different definition for \s: [\t\n\f\r ].
+// The JavaScript definition, on the other hand, also includes \v, Unicode "Separator, Space", etc.
+//
+// If the pattern is invalid (not valid even in JavaScript), then this function
+// returns the empty string and an error.
+//
+// If the pattern is valid, but incompatible (contains a lookahead or backreference),
+// then this function returns the transformation (a non-empty string) AND an error.
+func TransformRegExp(pattern string) (string, error) {
+
+ if pattern == "" {
+ return "", nil
+ }
+
+ // TODO If without \, if without (?=, (?!, then another shortcut
+
+ parser := _RegExp_parser{
+ str: pattern,
+ length: len(pattern),
+ goRegexp: bytes.NewBuffer(make([]byte, 0, 3*len(pattern)/2)),
+ }
+ parser.read() // Pull in the first character
+ parser.scan()
+ var err error
+ if len(parser.errors) > 0 {
+ err = parser.errors[0]
+ }
+ if parser.invalid {
+ return "", err
+ }
+
+ // Might not be re2 compatible, but is still a valid JavaScript RegExp
+ return parser.goRegexp.String(), err
+}
+
+func (self *_RegExp_parser) scan() {
+ for self.chr != -1 {
+ switch self.chr {
+ case '\\':
+ self.read()
+ self.scanEscape(false)
+ case '(':
+ self.pass()
+ self.scanGroup()
+ case '[':
+ self.pass()
+ self.scanBracket()
+ case ')':
+ self.error(-1, "Unmatched ')'")
+ self.invalid = true
+ self.pass()
+ default:
+ self.pass()
+ }
+ }
+}
+
+// (...)
+func (self *_RegExp_parser) scanGroup() {
+ str := self.str[self.chrOffset:]
+ if len(str) > 1 { // A possibility of (?= or (?!
+ if str[0] == '?' {
+ if str[1] == '=' || str[1] == '!' {
+ self.error(-1, "re2: Invalid (%s) <lookahead>", self.str[self.chrOffset:self.chrOffset+2])
+ }
+ }
+ }
+ for self.chr != -1 && self.chr != ')' {
+ switch self.chr {
+ case '\\':
+ self.read()
+ self.scanEscape(false)
+ case '(':
+ self.pass()
+ self.scanGroup()
+ case '[':
+ self.pass()
+ self.scanBracket()
+ default:
+ self.pass()
+ continue
+ }
+ }
+ if self.chr != ')' {
+ self.error(-1, "Unterminated group")
+ self.invalid = true
+ return
+ }
+ self.pass()
+}
+
+// [...]
+func (self *_RegExp_parser) scanBracket() {
+ for self.chr != -1 {
+ if self.chr == ']' {
+ break
+ } else if self.chr == '\\' {
+ self.read()
+ self.scanEscape(true)
+ continue
+ }
+ self.pass()
+ }
+ if self.chr != ']' {
+ self.error(-1, "Unterminated character class")
+ self.invalid = true
+ return
+ }
+ self.pass()
+}
+
+// \...
+func (self *_RegExp_parser) scanEscape(inClass bool) {
+ offset := self.chrOffset
+
+ var length, base uint32
+ switch self.chr {
+
+ case '0', '1', '2', '3', '4', '5', '6', '7':
+ var value int64
+ size := 0
+ for {
+ digit := int64(digitValue(self.chr))
+ if digit >= 8 {
+ // Not a valid digit
+ break
+ }
+ value = value*8 + digit
+ self.read()
+ size += 1
+ }
+ if size == 1 { // The number of characters read
+ _, err := self.goRegexp.Write([]byte{'\\', byte(value) + '0'})
+ if err != nil {
+ self.errors = append(self.errors, err)
+ }
+ if value != 0 {
+ // An invalid backreference
+ self.error(-1, "re2: Invalid \\%d <backreference>", value)
+ }
+ return
+ }
+ tmp := []byte{'\\', 'x', '0', 0}
+ if value >= 16 {
+ tmp = tmp[0:2]
+ } else {
+ tmp = tmp[0:3]
+ }
+ tmp = strconv.AppendInt(tmp, value, 16)
+ _, err := self.goRegexp.Write(tmp)
+ if err != nil {
+ self.errors = append(self.errors, err)
+ }
+ return
+
+ case '8', '9':
+ size := 0
+ for {
+ digit := digitValue(self.chr)
+ if digit >= 10 {
+ // Not a valid digit
+ break
+ }
+ self.read()
+ size += 1
+ }
+ err := self.goRegexp.WriteByte('\\')
+ if err != nil {
+ self.errors = append(self.errors, err)
+ }
+ _, err = self.goRegexp.WriteString(self.str[offset:self.chrOffset])
+ if err != nil {
+ self.errors = append(self.errors, err)
+ }
+ self.error(-1, "re2: Invalid \\%s <backreference>", self.str[offset:self.chrOffset])
+ return
+
+ case 'x':
+ self.read()
+ length, base = 2, 16
+
+ case 'u':
+ self.read()
+ length, base = 4, 16
+
+ case 'b':
+ if inClass {
+ _, err := self.goRegexp.Write([]byte{'\\', 'x', '0', '8'})
+ if err != nil {
+ self.errors = append(self.errors, err)
+ }
+ self.read()
+ return
+ }
+ fallthrough
+
+ case 'B':
+ fallthrough
+
+ case 'd', 'D', 's', 'S', 'w', 'W':
+ // This is slightly broken, because ECMAScript
+ // includes \v in \s, \S, while re2 does not
+ fallthrough
+
+ case '\\':
+ fallthrough
+
+ case 'f', 'n', 'r', 't', 'v':
+ err := self.goRegexp.WriteByte('\\')
+ if err != nil {
+ self.errors = append(self.errors, err)
+ }
+ self.pass()
+ return
+
+ case 'c':
+ self.read()
+ var value int64
+ if 'a' <= self.chr && self.chr <= 'z' {
+ value = int64(self.chr) - 'a' + 1
+ } else if 'A' <= self.chr && self.chr <= 'Z' {
+ value = int64(self.chr) - 'A' + 1
+ } else {
+ err := self.goRegexp.WriteByte('c')
+ if err != nil {
+ self.errors = append(self.errors, err)
+ }
+ return
+ }
+ tmp := []byte{'\\', 'x', '0', 0}
+ if value >= 16 {
+ tmp = tmp[0:2]
+ } else {
+ tmp = tmp[0:3]
+ }
+ tmp = strconv.AppendInt(tmp, value, 16)
+ _, err := self.goRegexp.Write(tmp)
+ if err != nil {
+ self.errors = append(self.errors, err)
+ }
+ self.read()
+ return
+
+ default:
+ // $ is an identifier character, so we have to have
+ // a special case for it here
+ if self.chr == '$' || !isIdentifierPart(self.chr) {
+ // A non-identifier character needs escaping
+ err := self.goRegexp.WriteByte('\\')
+ if err != nil {
+ self.errors = append(self.errors, err)
+ }
+ } else {
+ // Unescape the character for re2
+ }
+ self.pass()
+ return
+ }
+
+ // Otherwise, we're a \u.... or \x...
+ valueOffset := self.chrOffset
+
+ var value uint32
+ {
+ length := length
+ for ; length > 0; length-- {
+ digit := uint32(digitValue(self.chr))
+ if digit >= base {
+ // Not a valid digit
+ goto skip
+ }
+ value = value*base + digit
+ self.read()
+ }
+ }
+
+ if length == 4 {
+ _, err := self.goRegexp.Write([]byte{
+ '\\',
+ 'x',
+ '{',
+ self.str[valueOffset+0],
+ self.str[valueOffset+1],
+ self.str[valueOffset+2],
+ self.str[valueOffset+3],
+ '}',
+ })
+ if err != nil {
+ self.errors = append(self.errors, err)
+ }
+ } else if length == 2 {
+ _, err := self.goRegexp.Write([]byte{
+ '\\',
+ 'x',
+ self.str[valueOffset+0],
+ self.str[valueOffset+1],
+ })
+ if err != nil {
+ self.errors = append(self.errors, err)
+ }
+ } else {
+ // Should never, ever get here...
+ self.error(-1, "re2: Illegal branch in scanEscape")
+ goto skip
+ }
+
+ return
+
+skip:
+ _, err := self.goRegexp.WriteString(self.str[offset:self.chrOffset])
+ if err != nil {
+ self.errors = append(self.errors, err)
+ }
+}
+
+func (self *_RegExp_parser) pass() {
+ if self.chr != -1 {
+ _, err := self.goRegexp.WriteRune(self.chr)
+ if err != nil {
+ self.errors = append(self.errors, err)
+ }
+ }
+ self.read()
+}
+
+// TODO Better error reporting, use the offset, etc.
+func (self *_RegExp_parser) error(offset int, msg string, msgValues ...interface{}) error {
+ err := fmt.Errorf(msg, msgValues...)
+ self.errors = append(self.errors, err)
+ return err
+}
diff --git a/vendor/github.com/robertkrimen/otto/parser/scope.go b/vendor/github.com/robertkrimen/otto/parser/scope.go
new file mode 100644
index 000000000..e1dbdda13
--- /dev/null
+++ b/vendor/github.com/robertkrimen/otto/parser/scope.go
@@ -0,0 +1,44 @@
+package parser
+
+import (
+ "github.com/robertkrimen/otto/ast"
+)
+
+type _scope struct {
+ outer *_scope
+ allowIn bool
+ inIteration bool
+ inSwitch bool
+ inFunction bool
+ declarationList []ast.Declaration
+
+ labels []string
+}
+
+func (self *_parser) openScope() {
+ self.scope = &_scope{
+ outer: self.scope,
+ allowIn: true,
+ }
+}
+
+func (self *_parser) closeScope() {
+ self.scope = self.scope.outer
+}
+
+func (self *_scope) declare(declaration ast.Declaration) {
+ self.declarationList = append(self.declarationList, declaration)
+}
+
+func (self *_scope) hasLabel(name string) bool {
+ for _, label := range self.labels {
+ if label == name {
+ return true
+ }
+ }
+ if self.outer != nil && !self.inFunction {
+ // Crossing a function boundary to look for a label is verboten
+ return self.outer.hasLabel(name)
+ }
+ return false
+}
diff --git a/vendor/github.com/robertkrimen/otto/parser/statement.go b/vendor/github.com/robertkrimen/otto/parser/statement.go
new file mode 100644
index 000000000..6ff19d975
--- /dev/null
+++ b/vendor/github.com/robertkrimen/otto/parser/statement.go
@@ -0,0 +1,938 @@
+package parser
+
+import (
+ "github.com/robertkrimen/otto/ast"
+ "github.com/robertkrimen/otto/token"
+)
+
+func (self *_parser) parseBlockStatement() *ast.BlockStatement {
+ node := &ast.BlockStatement{}
+
+ // Find comments before the leading brace
+ if self.mode&StoreComments != 0 {
+ self.comments.CommentMap.AddComments(node, self.comments.FetchAll(), ast.LEADING)
+ self.comments.Unset()
+ }
+
+ node.LeftBrace = self.expect(token.LEFT_BRACE)
+ node.List = self.parseStatementList()
+
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ self.comments.CommentMap.AddComments(node, self.comments.FetchAll(), ast.FINAL)
+ self.comments.AfterBlock()
+ }
+
+ node.RightBrace = self.expect(token.RIGHT_BRACE)
+
+ // Find comments after the trailing brace
+ if self.mode&StoreComments != 0 {
+ self.comments.ResetLineBreak()
+ self.comments.CommentMap.AddComments(node, self.comments.Fetch(), ast.TRAILING)
+ }
+
+ return node
+}
+
+func (self *_parser) parseEmptyStatement() ast.Statement {
+ idx := self.expect(token.SEMICOLON)
+ return &ast.EmptyStatement{Semicolon: idx}
+}
+
+func (self *_parser) parseStatementList() (list []ast.Statement) {
+ for self.token != token.RIGHT_BRACE && self.token != token.EOF {
+ statement := self.parseStatement()
+ list = append(list, statement)
+ }
+
+ return
+}
+
+func (self *_parser) parseStatement() ast.Statement {
+
+ if self.token == token.EOF {
+ self.errorUnexpectedToken(self.token)
+ return &ast.BadStatement{From: self.idx, To: self.idx + 1}
+ }
+
+ if self.mode&StoreComments != 0 {
+ self.comments.ResetLineBreak()
+ }
+
+ switch self.token {
+ case token.SEMICOLON:
+ return self.parseEmptyStatement()
+ case token.LEFT_BRACE:
+ return self.parseBlockStatement()
+ case token.IF:
+ return self.parseIfStatement()
+ case token.DO:
+ statement := self.parseDoWhileStatement()
+ self.comments.PostProcessNode(statement)
+ return statement
+ case token.WHILE:
+ return self.parseWhileStatement()
+ case token.FOR:
+ return self.parseForOrForInStatement()
+ case token.BREAK:
+ return self.parseBreakStatement()
+ case token.CONTINUE:
+ return self.parseContinueStatement()
+ case token.DEBUGGER:
+ return self.parseDebuggerStatement()
+ case token.WITH:
+ return self.parseWithStatement()
+ case token.VAR:
+ return self.parseVariableStatement()
+ case token.FUNCTION:
+ return self.parseFunctionStatement()
+ case token.SWITCH:
+ return self.parseSwitchStatement()
+ case token.RETURN:
+ return self.parseReturnStatement()
+ case token.THROW:
+ return self.parseThrowStatement()
+ case token.TRY:
+ return self.parseTryStatement()
+ }
+
+ var comments []*ast.Comment
+ if self.mode&StoreComments != 0 {
+ comments = self.comments.FetchAll()
+ }
+
+ expression := self.parseExpression()
+
+ if identifier, isIdentifier := expression.(*ast.Identifier); isIdentifier && self.token == token.COLON {
+ // LabelledStatement
+ colon := self.idx
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.next() // :
+
+ label := identifier.Name
+ for _, value := range self.scope.labels {
+ if label == value {
+ self.error(identifier.Idx0(), "Label '%s' already exists", label)
+ }
+ }
+ var labelComments []*ast.Comment
+ if self.mode&StoreComments != 0 {
+ labelComments = self.comments.FetchAll()
+ }
+ self.scope.labels = append(self.scope.labels, label) // Push the label
+ statement := self.parseStatement()
+ self.scope.labels = self.scope.labels[:len(self.scope.labels)-1] // Pop the label
+ exp := &ast.LabelledStatement{
+ Label: identifier,
+ Colon: colon,
+ Statement: statement,
+ }
+ if self.mode&StoreComments != 0 {
+ self.comments.CommentMap.AddComments(exp, labelComments, ast.LEADING)
+ }
+
+ return exp
+ }
+
+ self.optionalSemicolon()
+
+ statement := &ast.ExpressionStatement{
+ Expression: expression,
+ }
+
+ if self.mode&StoreComments != 0 {
+ self.comments.CommentMap.AddComments(statement, comments, ast.LEADING)
+ }
+ return statement
+}
+
+func (self *_parser) parseTryStatement() ast.Statement {
+ var tryComments []*ast.Comment
+ if self.mode&StoreComments != 0 {
+ tryComments = self.comments.FetchAll()
+ }
+ node := &ast.TryStatement{
+ Try: self.expect(token.TRY),
+ Body: self.parseBlockStatement(),
+ }
+ if self.mode&StoreComments != 0 {
+ self.comments.CommentMap.AddComments(node, tryComments, ast.LEADING)
+ self.comments.CommentMap.AddComments(node.Body, self.comments.FetchAll(), ast.TRAILING)
+ }
+
+ if self.token == token.CATCH {
+ catch := self.idx
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.next()
+ self.expect(token.LEFT_PARENTHESIS)
+ if self.token != token.IDENTIFIER {
+ self.expect(token.IDENTIFIER)
+ self.nextStatement()
+ return &ast.BadStatement{From: catch, To: self.idx}
+ } else {
+ identifier := self.parseIdentifier()
+ self.expect(token.RIGHT_PARENTHESIS)
+ node.Catch = &ast.CatchStatement{
+ Catch: catch,
+ Parameter: identifier,
+ Body: self.parseBlockStatement(),
+ }
+
+ if self.mode&StoreComments != 0 {
+ self.comments.CommentMap.AddComments(node.Catch.Body, self.comments.FetchAll(), ast.TRAILING)
+ }
+ }
+ }
+
+ if self.token == token.FINALLY {
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.next()
+ if self.mode&StoreComments != 0 {
+ tryComments = self.comments.FetchAll()
+ }
+
+ node.Finally = self.parseBlockStatement()
+
+ if self.mode&StoreComments != 0 {
+ self.comments.CommentMap.AddComments(node.Finally, tryComments, ast.LEADING)
+ }
+ }
+
+ if node.Catch == nil && node.Finally == nil {
+ self.error(node.Try, "Missing catch or finally after try")
+ return &ast.BadStatement{From: node.Try, To: node.Body.Idx1()}
+ }
+
+ return node
+}
+
+func (self *_parser) parseFunctionParameterList() *ast.ParameterList {
+ opening := self.expect(token.LEFT_PARENTHESIS)
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ var list []*ast.Identifier
+ for self.token != token.RIGHT_PARENTHESIS && self.token != token.EOF {
+ if self.token != token.IDENTIFIER {
+ self.expect(token.IDENTIFIER)
+ } else {
+ identifier := self.parseIdentifier()
+ list = append(list, identifier)
+ }
+ if self.token != token.RIGHT_PARENTHESIS {
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.expect(token.COMMA)
+ }
+ }
+ closing := self.expect(token.RIGHT_PARENTHESIS)
+
+ return &ast.ParameterList{
+ Opening: opening,
+ List: list,
+ Closing: closing,
+ }
+}
+
+func (self *_parser) parseParameterList() (list []string) {
+ for self.token != token.EOF {
+ if self.token != token.IDENTIFIER {
+ self.expect(token.IDENTIFIER)
+ }
+ list = append(list, self.literal)
+ self.next()
+ if self.token != token.EOF {
+ self.expect(token.COMMA)
+ }
+ }
+ return
+}
+
+func (self *_parser) parseFunctionStatement() *ast.FunctionStatement {
+ var comments []*ast.Comment
+ if self.mode&StoreComments != 0 {
+ comments = self.comments.FetchAll()
+ }
+ function := &ast.FunctionStatement{
+ Function: self.parseFunction(true),
+ }
+ if self.mode&StoreComments != 0 {
+ self.comments.CommentMap.AddComments(function, comments, ast.LEADING)
+ }
+
+ return function
+}
+
+func (self *_parser) parseFunction(declaration bool) *ast.FunctionLiteral {
+
+ node := &ast.FunctionLiteral{
+ Function: self.expect(token.FUNCTION),
+ }
+
+ var name *ast.Identifier
+ if self.token == token.IDENTIFIER {
+ name = self.parseIdentifier()
+ if declaration {
+ self.scope.declare(&ast.FunctionDeclaration{
+ Function: node,
+ })
+ }
+ } else if declaration {
+ // Use expect error handling
+ self.expect(token.IDENTIFIER)
+ }
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ node.Name = name
+ node.ParameterList = self.parseFunctionParameterList()
+ self.parseFunctionBlock(node)
+ node.Source = self.slice(node.Idx0(), node.Idx1())
+
+ return node
+}
+
+func (self *_parser) parseFunctionBlock(node *ast.FunctionLiteral) {
+ {
+ self.openScope()
+ inFunction := self.scope.inFunction
+ self.scope.inFunction = true
+ defer func() {
+ self.scope.inFunction = inFunction
+ self.closeScope()
+ }()
+ node.Body = self.parseBlockStatement()
+ node.DeclarationList = self.scope.declarationList
+ }
+}
+
+func (self *_parser) parseDebuggerStatement() ast.Statement {
+ idx := self.expect(token.DEBUGGER)
+
+ node := &ast.DebuggerStatement{
+ Debugger: idx,
+ }
+ if self.mode&StoreComments != 0 {
+ self.comments.CommentMap.AddComments(node, self.comments.FetchAll(), ast.TRAILING)
+ }
+
+ self.semicolon()
+ return node
+}
+
+func (self *_parser) parseReturnStatement() ast.Statement {
+ idx := self.expect(token.RETURN)
+ var comments []*ast.Comment
+ if self.mode&StoreComments != 0 {
+ comments = self.comments.FetchAll()
+ }
+
+ if !self.scope.inFunction {
+ self.error(idx, "Illegal return statement")
+ self.nextStatement()
+ return &ast.BadStatement{From: idx, To: self.idx}
+ }
+
+ node := &ast.ReturnStatement{
+ Return: idx,
+ }
+
+ if !self.implicitSemicolon && self.token != token.SEMICOLON && self.token != token.RIGHT_BRACE && self.token != token.EOF {
+ node.Argument = self.parseExpression()
+ }
+ if self.mode&StoreComments != 0 {
+ self.comments.CommentMap.AddComments(node, comments, ast.LEADING)
+ }
+
+ self.semicolon()
+
+ return node
+}
+
+func (self *_parser) parseThrowStatement() ast.Statement {
+ var comments []*ast.Comment
+ if self.mode&StoreComments != 0 {
+ comments = self.comments.FetchAll()
+ }
+ idx := self.expect(token.THROW)
+
+ if self.implicitSemicolon {
+ if self.chr == -1 { // Hackish
+ self.error(idx, "Unexpected end of input")
+ } else {
+ self.error(idx, "Illegal newline after throw")
+ }
+ self.nextStatement()
+ return &ast.BadStatement{From: idx, To: self.idx}
+ }
+
+ node := &ast.ThrowStatement{
+ Argument: self.parseExpression(),
+ }
+ if self.mode&StoreComments != 0 {
+ self.comments.CommentMap.AddComments(node, comments, ast.LEADING)
+ }
+
+ self.semicolon()
+
+ return node
+}
+
+func (self *_parser) parseSwitchStatement() ast.Statement {
+ var comments []*ast.Comment
+ if self.mode&StoreComments != 0 {
+ comments = self.comments.FetchAll()
+ }
+ self.expect(token.SWITCH)
+ if self.mode&StoreComments != 0 {
+ comments = append(comments, self.comments.FetchAll()...)
+ }
+ self.expect(token.LEFT_PARENTHESIS)
+ node := &ast.SwitchStatement{
+ Discriminant: self.parseExpression(),
+ Default: -1,
+ }
+ self.expect(token.RIGHT_PARENTHESIS)
+ if self.mode&StoreComments != 0 {
+ comments = append(comments, self.comments.FetchAll()...)
+ }
+
+ self.expect(token.LEFT_BRACE)
+
+ inSwitch := self.scope.inSwitch
+ self.scope.inSwitch = true
+ defer func() {
+ self.scope.inSwitch = inSwitch
+ }()
+
+ for index := 0; self.token != token.EOF; index++ {
+ if self.token == token.RIGHT_BRACE {
+ self.next()
+ break
+ }
+
+ clause := self.parseCaseStatement()
+ if clause.Test == nil {
+ if node.Default != -1 {
+ self.error(clause.Case, "Already saw a default in switch")
+ }
+ node.Default = index
+ }
+ node.Body = append(node.Body, clause)
+ }
+
+ if self.mode&StoreComments != 0 {
+ self.comments.CommentMap.AddComments(node, comments, ast.LEADING)
+ }
+
+ return node
+}
+
+func (self *_parser) parseWithStatement() ast.Statement {
+ var comments []*ast.Comment
+ if self.mode&StoreComments != 0 {
+ comments = self.comments.FetchAll()
+ }
+ self.expect(token.WITH)
+ var withComments []*ast.Comment
+ if self.mode&StoreComments != 0 {
+ withComments = self.comments.FetchAll()
+ }
+
+ self.expect(token.LEFT_PARENTHESIS)
+
+ node := &ast.WithStatement{
+ Object: self.parseExpression(),
+ }
+ self.expect(token.RIGHT_PARENTHESIS)
+
+ if self.mode&StoreComments != 0 {
+ //comments = append(comments, self.comments.FetchAll()...)
+ self.comments.CommentMap.AddComments(node, comments, ast.LEADING)
+ self.comments.CommentMap.AddComments(node, withComments, ast.WITH)
+ }
+
+ node.Body = self.parseStatement()
+
+ return node
+}
+
+func (self *_parser) parseCaseStatement() *ast.CaseStatement {
+ node := &ast.CaseStatement{
+ Case: self.idx,
+ }
+
+ var comments []*ast.Comment
+ if self.mode&StoreComments != 0 {
+ comments = self.comments.FetchAll()
+ self.comments.Unset()
+ }
+
+ if self.token == token.DEFAULT {
+ self.next()
+ } else {
+ self.expect(token.CASE)
+ node.Test = self.parseExpression()
+ }
+
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.expect(token.COLON)
+
+ for {
+ if self.token == token.EOF ||
+ self.token == token.RIGHT_BRACE ||
+ self.token == token.CASE ||
+ self.token == token.DEFAULT {
+ break
+ }
+ consequent := self.parseStatement()
+ node.Consequent = append(node.Consequent, consequent)
+ }
+
+ // Link the comments to the case statement
+ if self.mode&StoreComments != 0 {
+ self.comments.CommentMap.AddComments(node, comments, ast.LEADING)
+ }
+
+ return node
+}
+
+func (self *_parser) parseIterationStatement() ast.Statement {
+ inIteration := self.scope.inIteration
+ self.scope.inIteration = true
+ defer func() {
+ self.scope.inIteration = inIteration
+ }()
+ return self.parseStatement()
+}
+
+func (self *_parser) parseForIn(into ast.Expression) *ast.ForInStatement {
+
+ // Already have consumed "<into> in"
+
+ source := self.parseExpression()
+ self.expect(token.RIGHT_PARENTHESIS)
+ body := self.parseIterationStatement()
+
+ forin := &ast.ForInStatement{
+ Into: into,
+ Source: source,
+ Body: body,
+ }
+
+ return forin
+}
+
+func (self *_parser) parseFor(initializer ast.Expression) *ast.ForStatement {
+
+ // Already have consumed "<initializer> ;"
+
+ var test, update ast.Expression
+
+ if self.token != token.SEMICOLON {
+ test = self.parseExpression()
+ }
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.expect(token.SEMICOLON)
+
+ if self.token != token.RIGHT_PARENTHESIS {
+ update = self.parseExpression()
+ }
+ self.expect(token.RIGHT_PARENTHESIS)
+ body := self.parseIterationStatement()
+
+ forstatement := &ast.ForStatement{
+ Initializer: initializer,
+ Test: test,
+ Update: update,
+ Body: body,
+ }
+
+ return forstatement
+}
+
+func (self *_parser) parseForOrForInStatement() ast.Statement {
+ var comments []*ast.Comment
+ if self.mode&StoreComments != 0 {
+ comments = self.comments.FetchAll()
+ }
+ idx := self.expect(token.FOR)
+ var forComments []*ast.Comment
+ if self.mode&StoreComments != 0 {
+ forComments = self.comments.FetchAll()
+ }
+ self.expect(token.LEFT_PARENTHESIS)
+
+ var left []ast.Expression
+
+ forIn := false
+ if self.token != token.SEMICOLON {
+
+ allowIn := self.scope.allowIn
+ self.scope.allowIn = false
+ if self.token == token.VAR {
+ var_ := self.idx
+ var varComments []*ast.Comment
+ if self.mode&StoreComments != 0 {
+ varComments = self.comments.FetchAll()
+ self.comments.Unset()
+ }
+ self.next()
+ list := self.parseVariableDeclarationList(var_)
+ if len(list) == 1 && self.token == token.IN {
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.next() // in
+ forIn = true
+ left = []ast.Expression{list[0]} // There is only one declaration
+ } else {
+ left = list
+ }
+ if self.mode&StoreComments != 0 {
+ self.comments.CommentMap.AddComments(left[0], varComments, ast.LEADING)
+ }
+ } else {
+ left = append(left, self.parseExpression())
+ if self.token == token.IN {
+ self.next()
+ forIn = true
+ }
+ }
+ self.scope.allowIn = allowIn
+ }
+
+ if forIn {
+ switch left[0].(type) {
+ case *ast.Identifier, *ast.DotExpression, *ast.BracketExpression, *ast.VariableExpression:
+ // These are all acceptable
+ default:
+ self.error(idx, "Invalid left-hand side in for-in")
+ self.nextStatement()
+ return &ast.BadStatement{From: idx, To: self.idx}
+ }
+
+ forin := self.parseForIn(left[0])
+ if self.mode&StoreComments != 0 {
+ self.comments.CommentMap.AddComments(forin, comments, ast.LEADING)
+ self.comments.CommentMap.AddComments(forin, forComments, ast.FOR)
+ }
+ return forin
+ }
+
+ if self.mode&StoreComments != 0 {
+ self.comments.Unset()
+ }
+ self.expect(token.SEMICOLON)
+ initializer := &ast.SequenceExpression{Sequence: left}
+ forstatement := self.parseFor(initializer)
+ if self.mode&StoreComments != 0 {
+ self.comments.CommentMap.AddComments(forstatement, comments, ast.LEADING)
+ self.comments.CommentMap.AddComments(forstatement, forComments, ast.FOR)
+ }
+ return forstatement
+}
+
+func (self *_parser) parseVariableStatement() *ast.VariableStatement {
+ var comments []*ast.Comment
+ if self.mode&StoreComments != 0 {
+ comments = self.comments.FetchAll()
+ }
+ idx := self.expect(token.VAR)
+
+ list := self.parseVariableDeclarationList(idx)
+
+ statement := &ast.VariableStatement{
+ Var: idx,
+ List: list,
+ }
+ if self.mode&StoreComments != 0 {
+ self.comments.CommentMap.AddComments(statement, comments, ast.LEADING)
+ self.comments.Unset()
+ }
+ self.semicolon()
+
+ return statement
+}
+
+func (self *_parser) parseDoWhileStatement() ast.Statement {
+ inIteration := self.scope.inIteration
+ self.scope.inIteration = true
+ defer func() {
+ self.scope.inIteration = inIteration
+ }()
+
+ var comments []*ast.Comment
+ if self.mode&StoreComments != 0 {
+ comments = self.comments.FetchAll()
+ }
+ self.expect(token.DO)
+ var doComments []*ast.Comment
+ if self.mode&StoreComments != 0 {
+ doComments = self.comments.FetchAll()
+ }
+
+ node := &ast.DoWhileStatement{}
+ if self.token == token.LEFT_BRACE {
+ node.Body = self.parseBlockStatement()
+ } else {
+ node.Body = self.parseStatement()
+ }
+
+ self.expect(token.WHILE)
+ var whileComments []*ast.Comment
+ if self.mode&StoreComments != 0 {
+ whileComments = self.comments.FetchAll()
+ }
+ self.expect(token.LEFT_PARENTHESIS)
+ node.Test = self.parseExpression()
+ self.expect(token.RIGHT_PARENTHESIS)
+
+ if self.mode&StoreComments != 0 {
+ self.comments.CommentMap.AddComments(node, comments, ast.LEADING)
+ self.comments.CommentMap.AddComments(node, doComments, ast.DO)
+ self.comments.CommentMap.AddComments(node, whileComments, ast.WHILE)
+ }
+
+ return node
+}
+
+func (self *_parser) parseWhileStatement() ast.Statement {
+ var comments []*ast.Comment
+ if self.mode&StoreComments != 0 {
+ comments = self.comments.FetchAll()
+ }
+ self.expect(token.WHILE)
+
+ var whileComments []*ast.Comment
+ if self.mode&StoreComments != 0 {
+ whileComments = self.comments.FetchAll()
+ }
+
+ self.expect(token.LEFT_PARENTHESIS)
+ node := &ast.WhileStatement{
+ Test: self.parseExpression(),
+ }
+ self.expect(token.RIGHT_PARENTHESIS)
+ node.Body = self.parseIterationStatement()
+
+ if self.mode&StoreComments != 0 {
+ self.comments.CommentMap.AddComments(node, comments, ast.LEADING)
+ self.comments.CommentMap.AddComments(node, whileComments, ast.WHILE)
+ }
+
+ return node
+}
+
+func (self *_parser) parseIfStatement() ast.Statement {
+ var comments []*ast.Comment
+ if self.mode&StoreComments != 0 {
+ comments = self.comments.FetchAll()
+ }
+ self.expect(token.IF)
+ var ifComments []*ast.Comment
+ if self.mode&StoreComments != 0 {
+ ifComments = self.comments.FetchAll()
+ }
+
+ self.expect(token.LEFT_PARENTHESIS)
+ node := &ast.IfStatement{
+ Test: self.parseExpression(),
+ }
+ self.expect(token.RIGHT_PARENTHESIS)
+ if self.token == token.LEFT_BRACE {
+ node.Consequent = self.parseBlockStatement()
+ } else {
+ node.Consequent = self.parseStatement()
+ }
+
+ if self.token == token.ELSE {
+ self.next()
+ node.Alternate = self.parseStatement()
+ }
+
+ if self.mode&StoreComments != 0 {
+ self.comments.CommentMap.AddComments(node, comments, ast.LEADING)
+ self.comments.CommentMap.AddComments(node, ifComments, ast.IF)
+ }
+
+ return node
+}
+
+func (self *_parser) parseSourceElement() ast.Statement {
+ statement := self.parseStatement()
+ //self.comments.Unset()
+ return statement
+}
+
+func (self *_parser) parseSourceElements() []ast.Statement {
+ body := []ast.Statement(nil)
+
+ for {
+ if self.token != token.STRING {
+ break
+ }
+ body = append(body, self.parseSourceElement())
+ }
+
+ for self.token != token.EOF {
+ body = append(body, self.parseSourceElement())
+ }
+
+ return body
+}
+
+func (self *_parser) parseProgram() *ast.Program {
+ self.openScope()
+ defer self.closeScope()
+ return &ast.Program{
+ Body: self.parseSourceElements(),
+ DeclarationList: self.scope.declarationList,
+ File: self.file,
+ }
+}
+
+func (self *_parser) parseBreakStatement() ast.Statement {
+ var comments []*ast.Comment
+ if self.mode&StoreComments != 0 {
+ comments = self.comments.FetchAll()
+ }
+ idx := self.expect(token.BREAK)
+ semicolon := self.implicitSemicolon
+ if self.token == token.SEMICOLON {
+ semicolon = true
+ self.next()
+ }
+
+ if semicolon || self.token == token.RIGHT_BRACE {
+ self.implicitSemicolon = false
+ if !self.scope.inIteration && !self.scope.inSwitch {
+ goto illegal
+ }
+ breakStatement := &ast.BranchStatement{
+ Idx: idx,
+ Token: token.BREAK,
+ }
+
+ if self.mode&StoreComments != 0 {
+ self.comments.CommentMap.AddComments(breakStatement, comments, ast.LEADING)
+ self.comments.CommentMap.AddComments(breakStatement, self.comments.FetchAll(), ast.TRAILING)
+ }
+
+ return breakStatement
+ }
+
+ if self.token == token.IDENTIFIER {
+ identifier := self.parseIdentifier()
+ if !self.scope.hasLabel(identifier.Name) {
+ self.error(idx, "Undefined label '%s'", identifier.Name)
+ return &ast.BadStatement{From: idx, To: identifier.Idx1()}
+ }
+ self.semicolon()
+ breakStatement := &ast.BranchStatement{
+ Idx: idx,
+ Token: token.BREAK,
+ Label: identifier,
+ }
+ if self.mode&StoreComments != 0 {
+ self.comments.CommentMap.AddComments(breakStatement, comments, ast.LEADING)
+ }
+
+ return breakStatement
+ }
+
+ self.expect(token.IDENTIFIER)
+
+illegal:
+ self.error(idx, "Illegal break statement")
+ self.nextStatement()
+ return &ast.BadStatement{From: idx, To: self.idx}
+}
+
+func (self *_parser) parseContinueStatement() ast.Statement {
+ idx := self.expect(token.CONTINUE)
+ semicolon := self.implicitSemicolon
+ if self.token == token.SEMICOLON {
+ semicolon = true
+ self.next()
+ }
+
+ if semicolon || self.token == token.RIGHT_BRACE {
+ self.implicitSemicolon = false
+ if !self.scope.inIteration {
+ goto illegal
+ }
+ return &ast.BranchStatement{
+ Idx: idx,
+ Token: token.CONTINUE,
+ }
+ }
+
+ if self.token == token.IDENTIFIER {
+ identifier := self.parseIdentifier()
+ if !self.scope.hasLabel(identifier.Name) {
+ self.error(idx, "Undefined label '%s'", identifier.Name)
+ return &ast.BadStatement{From: idx, To: identifier.Idx1()}
+ }
+ if !self.scope.inIteration {
+ goto illegal
+ }
+ self.semicolon()
+ return &ast.BranchStatement{
+ Idx: idx,
+ Token: token.CONTINUE,
+ Label: identifier,
+ }
+ }
+
+ self.expect(token.IDENTIFIER)
+
+illegal:
+ self.error(idx, "Illegal continue statement")
+ self.nextStatement()
+ return &ast.BadStatement{From: idx, To: self.idx}
+}
+
+// Find the next statement after an error (recover)
+func (self *_parser) nextStatement() {
+ for {
+ switch self.token {
+ case token.BREAK, token.CONTINUE,
+ token.FOR, token.IF, token.RETURN, token.SWITCH,
+ token.VAR, token.DO, token.TRY, token.WITH,
+ token.WHILE, token.THROW, token.CATCH, token.FINALLY:
+ // Return only if parser made some progress since last
+ // sync or if it has not reached 10 next calls without
+ // progress. Otherwise consume at least one token to
+ // avoid an endless parser loop
+ if self.idx == self.recover.idx && self.recover.count < 10 {
+ self.recover.count++
+ return
+ }
+ if self.idx > self.recover.idx {
+ self.recover.idx = self.idx
+ self.recover.count = 0
+ return
+ }
+ // Reaching here indicates a parser bug, likely an
+ // incorrect token list in this function, but it only
+ // leads to skipping of possibly correct code if a
+ // previous error is present, and thus is preferred
+ // over a non-terminating parse.
+ case token.EOF:
+ return
+ }
+ self.next()
+ }
+}