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 }