diff options
Diffstat (limited to 'vendor/github.com/robertkrimen/otto/parser/expression.go')
-rw-r--r-- | vendor/github.com/robertkrimen/otto/parser/expression.go | 1005 |
1 files changed, 1005 insertions, 0 deletions
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 +} |