package parser

import (
	"strings"

	"g3pix.com.br/axonasp/jscript/ast"
	"g3pix.com.br/axonasp/jscript/file"
	"g3pix.com.br/axonasp/jscript/token"
	"g3pix.com.br/axonasp/jscript/unistring"
)

func (self *_parser) parseIdentifier() *ast.Identifier {
	literal := self.parsedLiteral
	idx := self.idx
	self.next()
	return &ast.Identifier{
		Name: literal,
		Idx:  idx,
	}
}

func (self *_parser) parsePrimaryExpression() ast.Expression {
	literal, parsedLiteral := self.literal, self.parsedLiteral
	idx := self.idx
	switch self.token {
	case token.IDENTIFIER:
		self.next()
		return &ast.Identifier{
			Name: parsedLiteral,
			Idx:  idx,
		}
	case token.NULL:
		self.next()
		return &ast.NullLiteral{
			Idx:     idx,
			Literal: literal,
		}
	case token.BOOLEAN:
		self.next()
		value := false
		switch parsedLiteral {
		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()
		return &ast.StringLiteral{
			Idx:     idx,
			Literal: literal,
			Value:   parsedLiteral,
		}
	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:
		return self.parseParenthesisedExpression()
	case token.BACKTICK:
		return self.parseTemplateLiteral(false)
	case token.THIS:
		self.next()
		return &ast.ThisExpression{
			Idx: idx,
		}
	case token.SUPER:
		return self.parseSuperProperty()
	case token.ASYNC:
		if f := self.parseMaybeAsyncFunction(false); f != nil {
			return f
		}
	case token.FUNCTION:
		return self.parseFunction(false, false, idx)
	case token.CLASS:
		return &ast.ClassExpression{
			Class: self.parseClass(false),
		}
	}

	if self.isBindingId(self.token) {
		self.next()
		return &ast.Identifier{
			Name: parsedLiteral,
			Idx:  idx,
		}
	}

	self.errorUnexpectedToken(self.token)
	self.nextStatement()
	return &ast.BadExpression{From: idx, To: self.idx}
}

func (self *_parser) parseSuperProperty() ast.Expression {
	idx := self.idx
	self.next()
	switch self.token {
	case token.PERIOD:
		self.next()
		if !token.IsId(self.token) {
			self.expect(token.IDENTIFIER)
			self.nextStatement()
			return &ast.BadExpression{From: idx, To: self.idx}
		}
		idIdx := self.idx
		parsedLiteral := self.parsedLiteral
		self.next()
		return &ast.DotExpression{
			Left: &ast.SuperExpression{
				Idx: idx,
			},
			Identifier: ast.Identifier{
				Name: parsedLiteral,
				Idx:  idIdx,
			},
		}
	case token.LEFT_BRACKET:
		return self.parseBracketMember(&ast.SuperExpression{
			Idx: idx,
		})
	case token.LEFT_PARENTHESIS:
		return self.parseCallExpression(&ast.SuperExpression{
			Idx: idx,
		})
	default:
		self.error(idx, "'super' keyword unexpected here")
		self.nextStatement()
		return &ast.BadExpression{From: idx, To: self.idx}
	}
}

func (self *_parser) reinterpretSequenceAsArrowFuncParams(list []ast.Expression) *ast.ParameterList {
	firstRestIdx := -1
	params := make([]*ast.Binding, 0, len(list))
	for i, item := range list {
		if _, ok := item.(*ast.SpreadElement); ok {
			if firstRestIdx == -1 {
				firstRestIdx = i
				continue
			}
		}
		if firstRestIdx != -1 {
			self.error(list[firstRestIdx].Idx0(), "Rest parameter must be last formal parameter")
			return &ast.ParameterList{}
		}
		params = append(params, self.reinterpretAsBinding(item))
	}
	var rest ast.Expression
	if firstRestIdx != -1 {
		rest = self.reinterpretAsBindingRestElement(list[firstRestIdx])
	}
	return &ast.ParameterList{
		List: params,
		Rest: rest,
	}
}

func (self *_parser) parseParenthesisedExpression() ast.Expression {
	opening := self.idx
	self.expect(token.LEFT_PARENTHESIS)
	var list []ast.Expression
	if self.token != token.RIGHT_PARENTHESIS {
		for {
			if self.token == token.ELLIPSIS {
				start := self.idx
				self.errorUnexpectedToken(token.ELLIPSIS)
				self.next()
				expr := self.parseAssignmentExpression()
				list = append(list, &ast.BadExpression{
					From: start,
					To:   expr.Idx1(),
				})
			} else {
				list = append(list, self.parseAssignmentExpression())
			}
			if self.token != token.COMMA {
				break
			}
			self.next()
			if self.token == token.RIGHT_PARENTHESIS {
				self.errorUnexpectedToken(token.RIGHT_PARENTHESIS)
				break
			}
		}
	}
	self.expect(token.RIGHT_PARENTHESIS)
	if len(list) == 1 && len(self.errors) == 0 {
		return list[0]
	}
	if len(list) == 0 {
		self.errorUnexpectedToken(token.RIGHT_PARENTHESIS)
		return &ast.BadExpression{
			From: opening,
			To:   self.idx,
		}
	}
	return &ast.SequenceExpression{
		Sequence: list,
	}
}

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, false)
	endOffset := self.chrOffset

	if err == "" {
		pattern = pattern[1 : len(pattern)-1]
	}

	flags := ""
	if !isLineTerminator(self.chr) && !isLineWhiteSpace(self.chr) {
		self.next()

		if self.token == token.IDENTIFIER { // gim

			flags = self.literal
			self.next()
			endOffset = self.chrOffset - 1
		}
	} else {
		self.next()
	}

	literal := self.str[offset:endOffset]

	return &ast.RegExpLiteral{
		Idx:     idx,
		Literal: literal,
		Pattern: pattern,
		Flags:   flags,
	}
}

func (self *_parser) isBindingId(tok token.Token) bool {
	if tok == token.IDENTIFIER {
		return true
	}

	if tok == token.AWAIT {
		return !self.scope.allowAwait
	}
	if tok == token.YIELD {
		return !self.scope.allowYield
	}

	if token.IsUnreservedWord(tok) {
		return true
	}
	return false
}

func (self *_parser) tokenToBindingId() {
	if self.isBindingId(self.token) {
		self.token = token.IDENTIFIER
	}
}

func (self *_parser) parseBindingTarget() (target ast.BindingTarget) {
	self.tokenToBindingId()
	switch self.token {
	case token.IDENTIFIER:
		target = &ast.Identifier{
			Name: self.parsedLiteral,
			Idx:  self.idx,
		}
		self.next()
	case token.LEFT_BRACKET:
		target = self.parseArrayBindingPattern()
	case token.LEFT_BRACE:
		target = self.parseObjectBindingPattern()
	default:
		idx := self.expect(token.IDENTIFIER)
		self.nextStatement()
		target = &ast.BadExpression{From: idx, To: self.idx}
	}

	return
}

func (self *_parser) parseVariableDeclaration(declarationList *[]*ast.Binding) *ast.Binding {
	node := &ast.Binding{
		Target: self.parseBindingTarget(),
	}

	if declarationList != nil {
		*declarationList = append(*declarationList, node)
	}

	if self.token == token.ASSIGN {
		self.next()
		node.Initializer = self.parseAssignmentExpression()
	}

	return node
}

func (self *_parser) parseVariableDeclarationList() (declarationList []*ast.Binding) {
	for {
		self.parseVariableDeclaration(&declarationList)
		if self.token != token.COMMA {
			break
		}
		self.next()
	}
	return
}

func (self *_parser) parseVarDeclarationList(var_ file.Idx) []*ast.Binding {
	declarationList := self.parseVariableDeclarationList()

	self.scope.declare(&ast.VariableDeclaration{
		Var:  var_,
		List: declarationList,
	})

	return declarationList
}

func (self *_parser) parseObjectPropertyKey() (string, unistring.String, ast.Expression, token.Token) {
	if self.token == token.LEFT_BRACKET {
		self.next()
		expr := self.parseAssignmentExpression()
		self.expect(token.RIGHT_BRACKET)
		return "", "", expr, token.ILLEGAL
	}
	idx, tkn, literal, parsedLiteral := self.idx, self.token, self.literal, self.parsedLiteral
	var value ast.Expression
	self.next()
	switch tkn {
	case token.IDENTIFIER, token.STRING, token.KEYWORD, token.ESCAPED_RESERVED_WORD:
		value = &ast.StringLiteral{
			Idx:     idx,
			Literal: literal,
			Value:   parsedLiteral,
		}
	case token.NUMBER:
		num, err := parseNumberLiteral(literal)
		if err != nil {
			self.error(idx, err.Error())
		} else {
			value = &ast.NumberLiteral{
				Idx:     idx,
				Literal: literal,
				Value:   num,
			}
		}
	case token.PRIVATE_IDENTIFIER:
		value = &ast.PrivateIdentifier{
			Identifier: ast.Identifier{
				Idx:  idx,
				Name: parsedLiteral,
			},
		}
	default:
		// null, false, class, etc.
		if token.IsId(tkn) {
			value = &ast.StringLiteral{
				Idx:     idx,
				Literal: literal,
				Value:   unistring.String(literal),
			}
		} else {
			self.errorUnexpectedToken(tkn)
		}
	}
	return literal, parsedLiteral, value, tkn
}

func (self *_parser) parseObjectProperty() ast.Property {
	if self.token == token.ELLIPSIS {
		self.next()
		return &ast.SpreadElement{
			Expression: self.parseAssignmentExpression(),
		}
	}
	keyStartIdx := self.idx
	generator := false
	if self.token == token.MULTIPLY {
		generator = true
		self.next()
	}
	literal, parsedLiteral, value, tkn := self.parseObjectPropertyKey()
	if value == nil {
		return nil
	}
	if token.IsId(tkn) || tkn == token.STRING || tkn == token.NUMBER || tkn == token.ILLEGAL {
		if generator {
			return &ast.PropertyKeyed{
				Key:      value,
				Kind:     ast.PropertyKindMethod,
				Value:    self.parseMethodDefinition(keyStartIdx, ast.PropertyKindMethod, true, false),
				Computed: tkn == token.ILLEGAL,
			}
		}
		switch {
		case self.token == token.LEFT_PARENTHESIS:
			return &ast.PropertyKeyed{
				Key:      value,
				Kind:     ast.PropertyKindMethod,
				Value:    self.parseMethodDefinition(keyStartIdx, ast.PropertyKindMethod, false, false),
				Computed: tkn == token.ILLEGAL,
			}
		case self.token == token.COMMA || self.token == token.RIGHT_BRACE || self.token == token.ASSIGN: // shorthand property
			if self.isBindingId(tkn) {
				var initializer ast.Expression
				if self.token == token.ASSIGN {
					// allow the initializer syntax here in case the object literal
					// needs to be reinterpreted as an assignment pattern, enforce later if it doesn't.
					self.next()
					initializer = self.parseAssignmentExpression()
				}
				return &ast.PropertyShort{
					Name: ast.Identifier{
						Name: parsedLiteral,
						Idx:  value.Idx0(),
					},
					Initializer: initializer,
				}
			} else {
				self.errorUnexpectedToken(self.token)
			}
		case (literal == "get" || literal == "set" || tkn == token.ASYNC) && self.token != token.COLON:
			_, _, keyValue, tkn1 := self.parseObjectPropertyKey()
			if keyValue == nil {
				return nil
			}

			var kind ast.PropertyKind
			var async bool
			if tkn == token.ASYNC {
				async = true
				kind = ast.PropertyKindMethod
			} else if literal == "get" {
				kind = ast.PropertyKindGet
			} else {
				kind = ast.PropertyKindSet
			}

			return &ast.PropertyKeyed{
				Key:      keyValue,
				Kind:     kind,
				Value:    self.parseMethodDefinition(keyStartIdx, kind, false, async),
				Computed: tkn1 == token.ILLEGAL,
			}
		}
	}

	self.expect(token.COLON)
	return &ast.PropertyKeyed{
		Key:      value,
		Kind:     ast.PropertyKindValue,
		Value:    self.parseAssignmentExpression(),
		Computed: tkn == token.ILLEGAL,
	}
}

func (self *_parser) parseMethodDefinition(keyStartIdx file.Idx, kind ast.PropertyKind, generator, async bool) *ast.FunctionLiteral {
	idx1 := self.idx
	if generator != self.scope.allowYield {
		self.scope.allowYield = generator
		defer func() {
			self.scope.allowYield = !generator
		}()
	}
	if async != self.scope.allowAwait {
		self.scope.allowAwait = async
		defer func() {
			self.scope.allowAwait = !async
		}()
	}
	parameterList := self.parseFunctionParameterList()
	switch kind {
	case ast.PropertyKindGet:
		if len(parameterList.List) > 0 || parameterList.Rest != nil {
			self.error(idx1, "Getter must not have any formal parameters.")
		}
	case ast.PropertyKindSet:
		if len(parameterList.List) != 1 || parameterList.Rest != nil {
			self.error(idx1, "Setter must have exactly one formal parameter.")
		}
	}
	node := &ast.FunctionLiteral{
		Function:      keyStartIdx,
		ParameterList: parameterList,
		Generator:     generator,
		Async:         async,
	}
	node.Body, node.DeclarationList = self.parseFunctionBlock(async, async, generator)
	node.Source = self.slice(keyStartIdx, node.Body.Idx1())
	return node
}

func (self *_parser) parseObjectLiteral() *ast.ObjectLiteral {
	var value []ast.Property
	idx0 := self.expect(token.LEFT_BRACE)
	for self.token != token.RIGHT_BRACE && self.token != token.EOF {
		property := self.parseObjectProperty()
		if property != nil {
			value = append(value, property)
		}
		if self.token != token.RIGHT_BRACE {
			self.expect(token.COMMA)
		} else {
			break
		}
	}
	idx1 := self.expect(token.RIGHT_BRACE)

	return &ast.ObjectLiteral{
		LeftBrace:  idx0,
		RightBrace: idx1,
		Value:      value,
	}
}

func (self *_parser) parseArrayLiteral() *ast.ArrayLiteral {

	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 {
			self.next()
			value = append(value, nil)
			continue
		}
		if self.token == token.ELLIPSIS {
			self.next()
			value = append(value, &ast.SpreadElement{
				Expression: self.parseAssignmentExpression(),
			})
		} else {
			value = append(value, self.parseAssignmentExpression())
		}
		if self.token != token.RIGHT_BRACKET {
			self.expect(token.COMMA)
		}
	}
	idx1 := self.expect(token.RIGHT_BRACKET)

	return &ast.ArrayLiteral{
		LeftBracket:  idx0,
		RightBracket: idx1,
		Value:        value,
	}
}

func (self *_parser) parseTemplateLiteral(tagged bool) *ast.TemplateLiteral {
	res := &ast.TemplateLiteral{
		OpenQuote: self.idx,
	}
	for {
		start := self.offset
		literal, parsed, finished, parseErr, err := self.parseTemplateCharacters()
		if err != "" {
			self.error(self.offset, err)
		}
		res.Elements = append(res.Elements, &ast.TemplateElement{
			Idx:     self.idxOf(start),
			Literal: literal,
			Parsed:  parsed,
			Valid:   parseErr == "",
		})
		if !tagged && parseErr != "" {
			self.error(self.offset, parseErr)
		}
		end := self.chrOffset - 1
		self.next()
		if finished {
			res.CloseQuote = self.idxOf(end)
			break
		}
		expr := self.parseExpression()
		res.Expressions = append(res.Expressions, expr)
		if self.token != token.RIGHT_BRACE {
			self.errorUnexpectedToken(self.token)
		}
	}
	return res
}

func (self *_parser) parseTaggedTemplateLiteral(tag ast.Expression) *ast.TemplateLiteral {
	l := self.parseTemplateLiteral(true)
	l.Tag = tag
	return l
}

func (self *_parser) parseArgumentList() (argumentList []ast.Expression, idx0, idx1 file.Idx) {
	idx0 = self.expect(token.LEFT_PARENTHESIS)
	for self.token != token.RIGHT_PARENTHESIS {
		var item ast.Expression
		if self.token == token.ELLIPSIS {
			self.next()
			item = &ast.SpreadElement{
				Expression: self.parseAssignmentExpression(),
			}
		} else {
			item = self.parseAssignmentExpression()
		}
		argumentList = append(argumentList, item)
		if self.token != token.COMMA {
			break
		}
		self.next()
	}
	idx1 = self.expect(token.RIGHT_PARENTHESIS)
	return
}

func (self *_parser) parseCallExpression(left ast.Expression) ast.Expression {
	argumentList, idx0, idx1 := self.parseArgumentList()
	return &ast.CallExpression{
		Callee:           left,
		LeftParenthesis:  idx0,
		ArgumentList:     argumentList,
		RightParenthesis: idx1,
	}
}

func (self *_parser) parseDotMember(left ast.Expression) ast.Expression {
	period := self.idx
	self.next()

	literal := self.parsedLiteral
	idx := self.idx

	if self.token == token.PRIVATE_IDENTIFIER {
		self.next()
		return &ast.PrivateDotExpression{
			Left: left,
			Identifier: ast.PrivateIdentifier{
				Identifier: ast.Identifier{
					Idx:  idx,
					Name: literal,
				},
			},
		}
	}

	if !token.IsId(self.token) {
		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)
	if self.token == token.PERIOD {
		self.next()
		if self.literal == "target" {
			return &ast.MetaProperty{
				Meta: &ast.Identifier{
					Name: unistring.String(token.NEW.String()),
					Idx:  idx,
				},
				Property: self.parseIdentifier(),
			}
		}
		self.errorUnexpectedToken(token.IDENTIFIER)
	}
	callee := self.parseLeftHandSideExpression()
	if bad, ok := callee.(*ast.BadExpression); ok {
		bad.From = idx
		return bad
	}
	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
	}
	return node
}

func (self *_parser) parseLeftHandSideExpression() ast.Expression {

	var left ast.Expression
	if self.token == token.NEW {
		left = self.parseNewExpression()
	} else {
		left = self.parsePrimaryExpression()
	}
L:
	for {
		switch self.token {
		case token.PERIOD:
			left = self.parseDotMember(left)
		case token.LEFT_BRACKET:
			left = self.parseBracketMember(left)
		case token.BACKTICK:
			left = self.parseTaggedTemplateLiteral(left)
		default:
			break L
		}
	}

	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
	start := self.idx
	if self.token == token.NEW {
		left = self.parseNewExpression()
	} else {
		left = self.parsePrimaryExpression()
	}

	optionalChain := false
L:
	for {
		switch self.token {
		case token.PERIOD:
			left = self.parseDotMember(left)
		case token.LEFT_BRACKET:
			left = self.parseBracketMember(left)
		case token.LEFT_PARENTHESIS:
			left = self.parseCallExpression(left)
		case token.BACKTICK:
			if optionalChain {
				self.error(self.idx, "Invalid template literal on optional chain")
				self.nextStatement()
				return &ast.BadExpression{From: start, To: self.idx}
			}
			left = self.parseTaggedTemplateLiteral(left)
		case token.QUESTION_DOT:
			optionalChain = true
			left = &ast.Optional{Expression: left}

			switch self.peek() {
			case token.LEFT_BRACKET, token.LEFT_PARENTHESIS, token.BACKTICK:
				self.next()
			default:
				left = self.parseDotMember(left)
			}
		default:
			break L
		}
	}

	if optionalChain {
		left = &ast.OptionalChain{Expression: left}
	}
	return left
}

func (self *_parser) parseUpdateExpression() ast.Expression {
	switch self.token {
	case token.INCREMENT, token.DECREMENT:
		tkn := self.token
		idx := self.idx
		self.next()
		operand := self.parseUnaryExpression()
		switch operand.(type) {
		case *ast.Identifier, *ast.DotExpression, *ast.PrivateDotExpression, *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,
		}
	default:
		operand := self.parseLeftHandSideExpressionAllowCall()
		if self.token == token.INCREMENT || self.token == token.DECREMENT {
			// Make sure there is no line terminator here
			if self.implicitSemicolon {
				return operand
			}
			tkn := self.token
			idx := self.idx
			self.next()
			switch operand.(type) {
			case *ast.Identifier, *ast.DotExpression, *ast.PrivateDotExpression, *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,
				Postfix:  true,
			}
		}
		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
		self.next()
		return &ast.UnaryExpression{
			Operator: tkn,
			Idx:      idx,
			Operand:  self.parseUnaryExpression(),
		}
	case token.AWAIT:
		if self.scope.allowAwait {
			idx := self.idx
			self.next()
			if !self.scope.inAsync && self.scope.outer != nil {
				self.errorUnexpectedToken(token.AWAIT)
				return &ast.BadExpression{
					From: idx,
					To:   self.idx,
				}
			}
			if self.scope.inFuncParams {
				self.error(idx, "Illegal await-expression in formal parameters of async function")
			}
			return &ast.AwaitExpression{
				Await:    idx,
				Argument: self.parseUnaryExpression(),
			}
		}
	}

	return self.parseUpdateExpression()
}

func (self *_parser) parseExponentiationExpression() ast.Expression {
	parenthesis := self.token == token.LEFT_PARENTHESIS

	left := self.parseUnaryExpression()

	if self.token == token.EXPONENT {
		if !parenthesis {
			if u, isUnary := left.(*ast.UnaryExpression); isUnary && u.Operator != token.INCREMENT && u.Operator != token.DECREMENT {
				self.error(self.idx, "Unary operator used immediately before exponentiation expression. Parenthesis must be used to disambiguate operator precedence")
			}
		}
		for {
			self.next()
			left = &ast.BinaryExpression{
				Operator: token.EXPONENT,
				Left:     left,
				Right:    self.parseExponentiationExpression(),
			}
			if self.token != token.EXPONENT {
				break
			}
		}
	}

	return left
}

func (self *_parser) parseMultiplicativeExpression() ast.Expression {
	left := self.parseExponentiationExpression()

	for self.token == token.MULTIPLY || self.token == token.SLASH ||
		self.token == token.REMAINDER {
		tkn := self.token
		self.next()
		left = &ast.BinaryExpression{
			Operator: tkn,
			Left:     left,
			Right:    self.parseExponentiationExpression(),
		}
	}

	return left
}

func (self *_parser) parseAdditiveExpression() ast.Expression {
	left := self.parseMultiplicativeExpression()

	for self.token == token.PLUS || self.token == token.MINUS {
		tkn := self.token
		self.next()
		left = &ast.BinaryExpression{
			Operator: tkn,
			Left:     left,
			Right:    self.parseMultiplicativeExpression(),
		}
	}

	return left
}

func (self *_parser) parseShiftExpression() ast.Expression {
	left := self.parseAdditiveExpression()

	for self.token == token.SHIFT_LEFT || self.token == token.SHIFT_RIGHT ||
		self.token == token.UNSIGNED_SHIFT_RIGHT {
		tkn := self.token
		self.next()
		left = &ast.BinaryExpression{
			Operator: tkn,
			Left:     left,
			Right:    self.parseAdditiveExpression(),
		}
	}

	return left
}

func (self *_parser) parseRelationalExpression() ast.Expression {
	if self.scope.allowIn && self.token == token.PRIVATE_IDENTIFIER {
		left := &ast.PrivateIdentifier{
			Identifier: ast.Identifier{
				Idx:  self.idx,
				Name: self.parsedLiteral,
			},
		}
		self.next()
		if self.token == token.IN {
			self.next()
			return &ast.BinaryExpression{
				Operator: self.token,
				Left:     left,
				Right:    self.parseShiftExpression(),
			}
		}
		return left
	}
	left := self.parseShiftExpression()

	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
		self.next()
		return &ast.BinaryExpression{
			Operator:   tkn,
			Left:       left,
			Right:      self.parseRelationalExpression(),
			Comparison: true,
		}
	case token.INSTANCEOF:
		tkn := self.token
		self.next()
		return &ast.BinaryExpression{
			Operator: tkn,
			Left:     left,
			Right:    self.parseRelationalExpression(),
		}
	case token.IN:
		if !allowIn {
			return left
		}
		tkn := self.token
		self.next()
		return &ast.BinaryExpression{
			Operator: tkn,
			Left:     left,
			Right:    self.parseRelationalExpression(),
		}
	}

	return left
}

func (self *_parser) parseEqualityExpression() ast.Expression {
	left := self.parseRelationalExpression()

	for self.token == token.EQUAL || self.token == token.NOT_EQUAL ||
		self.token == token.STRICT_EQUAL || self.token == token.STRICT_NOT_EQUAL {
		tkn := self.token
		self.next()
		left = &ast.BinaryExpression{
			Operator:   tkn,
			Left:       left,
			Right:      self.parseRelationalExpression(),
			Comparison: true,
		}
	}

	return left
}

func (self *_parser) parseBitwiseAndExpression() ast.Expression {
	left := self.parseEqualityExpression()

	for self.token == token.AND {
		tkn := self.token
		self.next()
		left = &ast.BinaryExpression{
			Operator: tkn,
			Left:     left,
			Right:    self.parseEqualityExpression(),
		}
	}

	return left
}

func (self *_parser) parseBitwiseExclusiveOrExpression() ast.Expression {
	left := self.parseBitwiseAndExpression()

	for self.token == token.EXCLUSIVE_OR {
		tkn := self.token
		self.next()
		left = &ast.BinaryExpression{
			Operator: tkn,
			Left:     left,
			Right:    self.parseBitwiseAndExpression(),
		}
	}

	return left
}

func (self *_parser) parseBitwiseOrExpression() ast.Expression {
	left := self.parseBitwiseExclusiveOrExpression()

	for self.token == token.OR {
		tkn := self.token
		self.next()
		left = &ast.BinaryExpression{
			Operator: tkn,
			Left:     left,
			Right:    self.parseBitwiseExclusiveOrExpression(),
		}
	}

	return left
}

func (self *_parser) parseLogicalAndExpression() ast.Expression {
	left := self.parseBitwiseOrExpression()

	for self.token == token.LOGICAL_AND {
		tkn := self.token
		self.next()
		left = &ast.BinaryExpression{
			Operator: tkn,
			Left:     left,
			Right:    self.parseBitwiseOrExpression(),
		}
	}

	return left
}

func isLogicalAndExpr(expr ast.Expression) bool {
	if bexp, ok := expr.(*ast.BinaryExpression); ok && bexp.Operator == token.LOGICAL_AND {
		return true
	}
	return false
}

func (self *_parser) parseLogicalOrExpression() ast.Expression {
	var idx file.Idx
	parenthesis := self.token == token.LEFT_PARENTHESIS
	left := self.parseLogicalAndExpression()

	if self.token == token.LOGICAL_OR || !parenthesis && isLogicalAndExpr(left) {
		for {
			switch self.token {
			case token.LOGICAL_OR:
				self.next()
				left = &ast.BinaryExpression{
					Operator: token.LOGICAL_OR,
					Left:     left,
					Right:    self.parseLogicalAndExpression(),
				}
			case token.COALESCE:
				idx = self.idx
				goto mixed
			default:
				return left
			}
		}
	} else {
		for {
			switch self.token {
			case token.COALESCE:
				idx = self.idx
				self.next()

				parenthesis := self.token == token.LEFT_PARENTHESIS
				right := self.parseLogicalAndExpression()
				if !parenthesis && isLogicalAndExpr(right) {
					goto mixed
				}

				left = &ast.BinaryExpression{
					Operator: token.COALESCE,
					Left:     left,
					Right:    right,
				}
			case token.LOGICAL_OR:
				idx = self.idx
				goto mixed
			default:
				return left
			}
		}
	}

mixed:
	self.error(idx, "Logical expressions and coalesce expressions cannot be mixed. Wrap either by parentheses")
	return left
}

func (self *_parser) parseConditionalExpression() ast.Expression {
	left := self.parseLogicalOrExpression()

	if self.token == token.QUESTION_MARK {
		self.next()
		allowIn := self.scope.allowIn
		self.scope.allowIn = true
		consequent := self.parseAssignmentExpression()
		self.scope.allowIn = allowIn
		self.expect(token.COLON)
		return &ast.ConditionalExpression{
			Test:       left,
			Consequent: consequent,
			Alternate:  self.parseAssignmentExpression(),
		}
	}

	return left
}

func (self *_parser) parseArrowFunction(start file.Idx, paramList *ast.ParameterList, async bool) ast.Expression {
	self.expect(token.ARROW)
	node := &ast.ArrowFunctionLiteral{
		Start:         start,
		ParameterList: paramList,
		Async:         async,
	}
	node.Body, node.DeclarationList = self.parseArrowFunctionBody(async)
	node.Source = self.slice(start, node.Body.Idx1())
	return node
}

func (self *_parser) parseSingleArgArrowFunction(start file.Idx, async bool) ast.Expression {
	if async != self.scope.allowAwait {
		self.scope.allowAwait = async
		defer func() {
			self.scope.allowAwait = !async
		}()
	}
	self.tokenToBindingId()
	if self.token != token.IDENTIFIER {
		self.errorUnexpectedToken(self.token)
		self.next()
		return &ast.BadExpression{
			From: start,
			To:   self.idx,
		}
	}

	id := self.parseIdentifier()

	paramList := &ast.ParameterList{
		Opening: id.Idx,
		Closing: id.Idx1(),
		List: []*ast.Binding{{
			Target: id,
		}},
	}

	return self.parseArrowFunction(start, paramList, async)
}

func (self *_parser) parseAssignmentExpression() ast.Expression {
	start := self.idx
	parenthesis := false
	async := false
	var state parserState
	switch self.token {
	case token.LEFT_PARENTHESIS:
		self.mark(&state)
		parenthesis = true
	case token.ASYNC:
		tok := self.peek()
		if self.isBindingId(tok) {
			// async x => ...
			self.next()
			return self.parseSingleArgArrowFunction(start, true)
		} else if tok == token.LEFT_PARENTHESIS {
			self.mark(&state)
			async = true
		}
	case token.YIELD:
		if self.scope.allowYield {
			return self.parseYieldExpression()
		}
		fallthrough
	default:
		self.tokenToBindingId()
	}
	left := self.parseConditionalExpression()
	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.EXPONENT_ASSIGN:
		operator = token.EXPONENT
	case token.QUOTIENT_ASSIGN:
		operator = token.SLASH
	case token.REMAINDER_ASSIGN:
		operator = token.REMAINDER
	case token.AND_ASSIGN:
		operator = token.AND
	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
	case token.LOGICAL_AND_ASSIGN:
		operator = token.LOGICAL_AND
	case token.LOGICAL_OR_ASSIGN:
		operator = token.LOGICAL_OR
	case token.COALESCE_ASSIGN:
		operator = token.COALESCE
	case token.ARROW:
		var paramList *ast.ParameterList
		if id, ok := left.(*ast.Identifier); ok {
			paramList = &ast.ParameterList{
				Opening: id.Idx,
				Closing: id.Idx1() - 1,
				List: []*ast.Binding{{
					Target: id,
				}},
			}
		} else if parenthesis {
			if seq, ok := left.(*ast.SequenceExpression); ok && len(self.errors) == 0 {
				paramList = self.reinterpretSequenceAsArrowFuncParams(seq.Sequence)
			} else {
				self.restore(&state)
				paramList = self.parseFunctionParameterList()
			}
		} else if async {
			// async (x, y) => ...
			if !self.scope.allowAwait {
				self.scope.allowAwait = true
				defer func() {
					self.scope.allowAwait = false
				}()
			}
			if _, ok := left.(*ast.CallExpression); ok {
				self.restore(&state)
				self.next() // skip "async"
				paramList = self.parseFunctionParameterList()
			}
		}
		if paramList == nil {
			self.error(left.Idx0(), "Malformed arrow function parameter list")
			return &ast.BadExpression{From: left.Idx0(), To: left.Idx1()}
		}
		return self.parseArrowFunction(start, paramList, async)
	}

	if operator != 0 {
		idx := self.idx
		self.next()
		ok := false
		switch l := left.(type) {
		case *ast.Identifier, *ast.DotExpression, *ast.PrivateDotExpression, *ast.BracketExpression:
			ok = true
		case *ast.ArrayLiteral:
			if !parenthesis && operator == token.ASSIGN {
				left = self.reinterpretAsArrayAssignmentPattern(l)
				ok = true
			}
		case *ast.ObjectLiteral:
			if !parenthesis && operator == token.ASSIGN {
				left = self.reinterpretAsObjectAssignmentPattern(l)
				ok = true
			}
		}
		if ok {
			return &ast.AssignExpression{
				Left:     left,
				Operator: operator,
				Right:    self.parseAssignmentExpression(),
			}
		}
		self.error(left.Idx0(), "Invalid left-hand side in assignment")
		self.nextStatement()
		return &ast.BadExpression{From: idx, To: self.idx}
	}

	return left
}

func (self *_parser) parseYieldExpression() ast.Expression {
	idx := self.expect(token.YIELD)

	if self.scope.inFuncParams {
		self.error(idx, "Yield expression not allowed in formal parameter")
	}

	node := &ast.YieldExpression{
		Yield: idx,
	}

	if !self.implicitSemicolon && self.token == token.MULTIPLY {
		node.Delegate = true
		self.next()
	}

	if !self.implicitSemicolon && self.token != token.SEMICOLON && self.token != token.RIGHT_BRACE && self.token != token.EOF {
		var state parserState
		self.mark(&state)
		expr := self.parseAssignmentExpression()
		if _, bad := expr.(*ast.BadExpression); bad {
			expr = nil
			self.restore(&state)
		}
		node.Argument = expr
	}

	return node
}

func (self *_parser) parseExpression() ast.Expression {
	left := self.parseAssignmentExpression()

	if self.token == token.COMMA {
		sequence := []ast.Expression{left}
		for {
			if self.token != token.COMMA {
				break
			}
			self.next()
			sequence = append(sequence, self.parseAssignmentExpression())
		}
		return &ast.SequenceExpression{
			Sequence: sequence,
		}
	}

	return left
}

func (self *_parser) checkComma(from, to file.Idx) {
	if pos := strings.IndexByte(self.str[int(from)-self.base:int(to)-self.base], ','); pos >= 0 {
		self.error(from+file.Idx(pos), "Comma is not allowed here")
	}
}

func (self *_parser) reinterpretAsArrayAssignmentPattern(left *ast.ArrayLiteral) ast.Expression {
	value := left.Value
	var rest ast.Expression
	for i, item := range value {
		if spread, ok := item.(*ast.SpreadElement); ok {
			if i != len(value)-1 {
				self.error(item.Idx0(), "Rest element must be last element")
				return &ast.BadExpression{From: left.Idx0(), To: left.Idx1()}
			}
			self.checkComma(spread.Expression.Idx1(), left.RightBracket)
			rest = self.reinterpretAsDestructAssignTarget(spread.Expression)
			value = value[:len(value)-1]
		} else {
			value[i] = self.reinterpretAsAssignmentElement(item)
		}
	}
	return &ast.ArrayPattern{
		LeftBracket:  left.LeftBracket,
		RightBracket: left.RightBracket,
		Elements:     value,
		Rest:         rest,
	}
}

func (self *_parser) reinterpretArrayAssignPatternAsBinding(pattern *ast.ArrayPattern) *ast.ArrayPattern {
	for i, item := range pattern.Elements {
		pattern.Elements[i] = self.reinterpretAsDestructBindingTarget(item)
	}
	if pattern.Rest != nil {
		pattern.Rest = self.reinterpretAsDestructBindingTarget(pattern.Rest)
	}
	return pattern
}

func (self *_parser) reinterpretAsArrayBindingPattern(left *ast.ArrayLiteral) ast.BindingTarget {
	value := left.Value
	var rest ast.Expression
	for i, item := range value {
		if spread, ok := item.(*ast.SpreadElement); ok {
			if i != len(value)-1 {
				self.error(item.Idx0(), "Rest element must be last element")
				return &ast.BadExpression{From: left.Idx0(), To: left.Idx1()}
			}
			self.checkComma(spread.Expression.Idx1(), left.RightBracket)
			rest = self.reinterpretAsDestructBindingTarget(spread.Expression)
			value = value[:len(value)-1]
		} else {
			value[i] = self.reinterpretAsAssignmentElement(item)
		}
	}
	return &ast.ArrayPattern{
		LeftBracket:  left.LeftBracket,
		RightBracket: left.RightBracket,
		Elements:     value,
		Rest:         rest,
	}
}

func (self *_parser) parseArrayBindingPattern() ast.BindingTarget {
	return self.reinterpretAsArrayBindingPattern(self.parseArrayLiteral())
}

func (self *_parser) parseObjectBindingPattern() ast.BindingTarget {
	return self.reinterpretAsObjectBindingPattern(self.parseObjectLiteral())
}

func (self *_parser) reinterpretArrayObjectPatternAsBinding(pattern *ast.ObjectPattern) *ast.ObjectPattern {
	for _, prop := range pattern.Properties {
		if keyed, ok := prop.(*ast.PropertyKeyed); ok {
			keyed.Value = self.reinterpretAsBindingElement(keyed.Value)
		}
	}
	if pattern.Rest != nil {
		pattern.Rest = self.reinterpretAsBindingRestElement(pattern.Rest)
	}
	return pattern
}

func (self *_parser) reinterpretAsObjectBindingPattern(expr *ast.ObjectLiteral) ast.BindingTarget {
	var rest ast.Expression
	value := expr.Value
	for i, prop := range value {
		ok := false
		switch prop := prop.(type) {
		case *ast.PropertyKeyed:
			if prop.Kind == ast.PropertyKindValue {
				prop.Value = self.reinterpretAsBindingElement(prop.Value)
				ok = true
			}
		case *ast.PropertyShort:
			ok = true
		case *ast.SpreadElement:
			if i != len(expr.Value)-1 {
				self.error(prop.Idx0(), "Rest element must be last element")
				return &ast.BadExpression{From: expr.Idx0(), To: expr.Idx1()}
			}
			// TODO make sure there is no trailing comma
			rest = self.reinterpretAsBindingRestElement(prop.Expression)
			value = value[:i]
			ok = true
		}
		if !ok {
			self.error(prop.Idx0(), "Invalid destructuring binding target")
			return &ast.BadExpression{From: expr.Idx0(), To: expr.Idx1()}
		}
	}
	return &ast.ObjectPattern{
		LeftBrace:  expr.LeftBrace,
		RightBrace: expr.RightBrace,
		Properties: value,
		Rest:       rest,
	}
}

func (self *_parser) reinterpretAsObjectAssignmentPattern(l *ast.ObjectLiteral) ast.Expression {
	var rest ast.Expression
	value := l.Value
	for i, prop := range value {
		ok := false
		switch prop := prop.(type) {
		case *ast.PropertyKeyed:
			if prop.Kind == ast.PropertyKindValue {
				prop.Value = self.reinterpretAsAssignmentElement(prop.Value)
				ok = true
			}
		case *ast.PropertyShort:
			ok = true
		case *ast.SpreadElement:
			if i != len(l.Value)-1 {
				self.error(prop.Idx0(), "Rest element must be last element")
				return &ast.BadExpression{From: l.Idx0(), To: l.Idx1()}
			}
			// TODO make sure there is no trailing comma
			rest = prop.Expression
			value = value[:i]
			ok = true
		}
		if !ok {
			self.error(prop.Idx0(), "Invalid destructuring assignment target")
			return &ast.BadExpression{From: l.Idx0(), To: l.Idx1()}
		}
	}
	return &ast.ObjectPattern{
		LeftBrace:  l.LeftBrace,
		RightBrace: l.RightBrace,
		Properties: value,
		Rest:       rest,
	}
}

func (self *_parser) reinterpretAsAssignmentElement(expr ast.Expression) ast.Expression {
	switch expr := expr.(type) {
	case *ast.AssignExpression:
		if expr.Operator == token.ASSIGN {
			expr.Left = self.reinterpretAsDestructAssignTarget(expr.Left)
			return expr
		} else {
			self.error(expr.Idx0(), "Invalid destructuring assignment target")
			return &ast.BadExpression{From: expr.Idx0(), To: expr.Idx1()}
		}
	default:
		return self.reinterpretAsDestructAssignTarget(expr)
	}
}

func (self *_parser) reinterpretAsBindingElement(expr ast.Expression) ast.Expression {
	switch expr := expr.(type) {
	case *ast.AssignExpression:
		if expr.Operator == token.ASSIGN {
			expr.Left = self.reinterpretAsDestructBindingTarget(expr.Left)
			return expr
		} else {
			self.error(expr.Idx0(), "Invalid destructuring assignment target")
			return &ast.BadExpression{From: expr.Idx0(), To: expr.Idx1()}
		}
	default:
		return self.reinterpretAsDestructBindingTarget(expr)
	}
}

func (self *_parser) reinterpretAsBinding(expr ast.Expression) *ast.Binding {
	switch expr := expr.(type) {
	case *ast.AssignExpression:
		if expr.Operator == token.ASSIGN {
			return &ast.Binding{
				Target:      self.reinterpretAsDestructBindingTarget(expr.Left),
				Initializer: expr.Right,
			}
		} else {
			self.error(expr.Idx0(), "Invalid destructuring assignment target")
			return &ast.Binding{
				Target: &ast.BadExpression{From: expr.Idx0(), To: expr.Idx1()},
			}
		}
	default:
		return &ast.Binding{
			Target: self.reinterpretAsDestructBindingTarget(expr),
		}
	}
}

func (self *_parser) reinterpretAsDestructAssignTarget(item ast.Expression) ast.Expression {
	switch item := item.(type) {
	case nil:
		return nil
	case *ast.ArrayLiteral:
		return self.reinterpretAsArrayAssignmentPattern(item)
	case *ast.ObjectLiteral:
		return self.reinterpretAsObjectAssignmentPattern(item)
	case ast.Pattern, *ast.Identifier, *ast.DotExpression, *ast.PrivateDotExpression, *ast.BracketExpression:
		return item
	}
	self.error(item.Idx0(), "Invalid destructuring assignment target")
	return &ast.BadExpression{From: item.Idx0(), To: item.Idx1()}
}

func (self *_parser) reinterpretAsDestructBindingTarget(item ast.Expression) ast.BindingTarget {
	switch item := item.(type) {
	case nil:
		return nil
	case *ast.ArrayPattern:
		return self.reinterpretArrayAssignPatternAsBinding(item)
	case *ast.ObjectPattern:
		return self.reinterpretArrayObjectPatternAsBinding(item)
	case *ast.ArrayLiteral:
		return self.reinterpretAsArrayBindingPattern(item)
	case *ast.ObjectLiteral:
		return self.reinterpretAsObjectBindingPattern(item)
	case *ast.Identifier:
		if !self.scope.allowAwait || item.Name != "await" {
			return item
		}
	}
	self.error(item.Idx0(), "Invalid destructuring binding target")
	return &ast.BadExpression{From: item.Idx0(), To: item.Idx1()}
}

func (self *_parser) reinterpretAsBindingRestElement(expr ast.Expression) ast.Expression {
	if _, ok := expr.(*ast.Identifier); ok {
		return expr
	}
	self.error(expr.Idx0(), "Invalid binding rest")
	return &ast.BadExpression{From: expr.Idx0(), To: expr.Idx1()}
}
