forked from golang/hotime
1006 lines
21 KiB
Go
1006 lines
21 KiB
Go
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{Begin: self.idx, End: 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
|
|
}
|