package syntax

// compact specifies whether we allow spaces between expressions.
// This is true for let
func ( *Parser) ( bool) ArithmExpr {
	return .arithmExprComma()
}

// These function names are inspired by Bash's expr.c

func ( *Parser) ( bool) ArithmExpr {
	return .arithmExprBinary(, .arithmExprAssign, Comma)
}

func ( *Parser) ( bool) ArithmExpr {
	// Assign is different from the other binary operators because it's
	// right-associative and needs to check that it's placed after a name
	 := .arithmExprTernary()
	switch BinAritOperator(.tok) {
	case AddAssgn, SubAssgn, MulAssgn, QuoAssgn, RemAssgn, AndAssgn,
		OrAssgn, XorAssgn, ShlAssgn, ShrAssgn, Assgn:
		if  && .spaced {
			return 
		}
		if !isArithName() {
			.posErr(.pos, "%s must follow a name", .tok.String())
		}
		 := .pos
		 := .tok
		.nextArithOp()
		 := .()
		if  == nil {
			.followErrExp(, .String())
		}
		return &BinaryArithm{
			OpPos: ,
			Op:    BinAritOperator(),
			X:     ,
			Y:     ,
		}
	}
	return 
}

func ( *Parser) ( bool) ArithmExpr {
	 := .arithmExprLor()
	if BinAritOperator(.tok) != TernQuest || ( && .spaced) {
		return 
	}

	if  == nil {
		.curErr("%s must follow an expression", .tok.String())
	}
	 := .pos
	.nextArithOp()
	if BinAritOperator(.tok) == TernColon {
		.followErrExp(, TernQuest.String())
	}
	 := .arithmExpr()
	if  == nil {
		.followErrExp(, TernQuest.String())
	}
	if BinAritOperator(.tok) != TernColon {
		.posErr(, "ternary operator missing : after ?")
	}
	 := .pos
	.nextArithOp()
	 := .()
	if  == nil {
		.followErrExp(, TernColon.String())
	}
	return &BinaryArithm{
		OpPos: ,
		Op:    TernQuest,
		X:     ,
		Y: &BinaryArithm{
			OpPos: ,
			Op:    TernColon,
			X:     ,
			Y:     ,
		},
	}
}

func ( *Parser) ( bool) ArithmExpr {
	return .arithmExprBinary(, .arithmExprLand, OrArit)
}

func ( *Parser) ( bool) ArithmExpr {
	return .arithmExprBinary(, .arithmExprBor, AndArit)
}

func ( *Parser) ( bool) ArithmExpr {
	return .arithmExprBinary(, .arithmExprBxor, Or)
}

func ( *Parser) ( bool) ArithmExpr {
	return .arithmExprBinary(, .arithmExprBand, Xor)
}

func ( *Parser) ( bool) ArithmExpr {
	return .arithmExprBinary(, .arithmExprEquality, And)
}

func ( *Parser) ( bool) ArithmExpr {
	return .arithmExprBinary(, .arithmExprComparison, Eql, Neq)
}

func ( *Parser) ( bool) ArithmExpr {
	return .arithmExprBinary(, .arithmExprShift, Lss, Gtr, Leq, Geq)
}

func ( *Parser) ( bool) ArithmExpr {
	return .arithmExprBinary(, .arithmExprAddition, Shl, Shr)
}

func ( *Parser) ( bool) ArithmExpr {
	return .arithmExprBinary(, .arithmExprMultiplication, Add, Sub)
}

func ( *Parser) ( bool) ArithmExpr {
	return .arithmExprBinary(, .arithmExprPower, Mul, Quo, Rem)
}

func ( *Parser) ( bool) ArithmExpr {
	// Power is different from the other binary operators because it's right-associative
	 := .arithmExprUnary()
	if BinAritOperator(.tok) != Pow || ( && .spaced) {
		return 
	}

	if  == nil {
		.curErr("%s must follow an expression", .tok.String())
	}

	 := .tok
	 := .pos
	.nextArithOp()
	 := .()
	if  == nil {
		.followErrExp(, .String())
	}
	return &BinaryArithm{
		OpPos: ,
		Op:    BinAritOperator(),
		X:     ,
		Y:     ,
	}
}

func ( *Parser) ( bool) ArithmExpr {
	if ! {
		.got(_Newl)
	}

	switch UnAritOperator(.tok) {
	case Not, BitNegation, Plus, Minus:
		 := &UnaryArithm{OpPos: .pos, Op: UnAritOperator(.tok)}
		.nextArithOp()
		if .X = .(); .X == nil {
			.followErrExp(.OpPos, .Op.String())
		}
		return 
	}
	return .arithmExprValue()
}

func ( *Parser) ( bool) ArithmExpr {
	var  ArithmExpr
	switch .tok {
	case addAdd, subSub:
		 := &UnaryArithm{OpPos: .pos, Op: UnAritOperator(.tok)}
		.nextArith()
		if .tok != _LitWord {
			.followErr(.OpPos, token(.Op).String(), "a literal")
		}
		.X = .()
		return 
	case leftParen:
		 := &ParenArithm{Lparen: .pos}
		.nextArithOp()
		.X = .followArithm(leftParen, .Lparen)
		.Rparen = .matched(.Lparen, leftParen, rightParen)
		 = 
	case leftBrack:
		.curErr("[ must follow a name")
	case colon:
		.curErr("ternary operator missing ? before :")
	case _LitWord:
		 := .getLit()
		if .tok != leftBrack {
			 = .wordOne()
			break
		}
		 := &ParamExp{Dollar: .ValuePos, Short: true, Param: }
		.Index = .eitherIndex()
		 = .wordOne()
	case bckQuote:
		if .quote == arithmExprLet && .openBquotes > 0 {
			return nil
		}
		fallthrough
	default:
		if  := .getWord();  != nil {
			 = 
		} else {
			return nil
		}
	}

	if  && .spaced {
		return 
	}
	if ! {
		.got(_Newl)
	}

	// we want real nil, not (*Word)(nil) as that
	// sets the type to non-nil and then x != nil
	if .tok == addAdd || .tok == subSub {
		if !isArithName() {
			.curErr("%s must follow a name", .tok.String())
		}
		 := &UnaryArithm{
			Post:  true,
			OpPos: .pos,
			Op:    UnAritOperator(.tok),
			X:     ,
		}
		.nextArith()
		return 
	}
	return 
}

// nextArith consumes a token.
// It returns true if compact and the token was followed by spaces
func ( *Parser) ( bool) bool {
	.next()
	if  && .spaced {
		return true
	}
	if ! {
		.got(_Newl)
	}
	return false
}

func ( *Parser) ( bool) {
	 := .pos
	 := .tok
	if .nextArith() {
		.followErrExp(, .String())
	}
}

// arithmExprBinary is used for all left-associative binary operators
func ( *Parser) ( bool,  func(bool) ArithmExpr,  ...BinAritOperator) ArithmExpr {
	 := ()
	for {
		var  BinAritOperator
		for ,  := range  {
			if .tok == token() {
				 = 
				break
			}
		}

		if token() == illegalTok || ( && .spaced) {
			return 
		}

		if  == nil {
			.curErr("%s must follow an expression", .tok.String())
		}

		 := .pos
		.nextArithOp()
		 := ()
		if  == nil {
			.followErrExp(, .String())
		}

		 = &BinaryArithm{
			OpPos: ,
			Op:    ,
			X:     ,
			Y:     ,
		}
	}
}

func isArithName( ArithmExpr) bool {
	,  := .(*Word)
	if ! || len(.Parts) != 1 {
		return false
	}
	switch x := .Parts[0].(type) {
	case *Lit:
		return ValidName(.Value)
	case *ParamExp:
		return .nakedIndex()
	default:
		return false
	}
}

func ( *Parser) ( token,  Pos) ArithmExpr {
	 := .arithmExpr(false)
	if  == nil {
		.followErrExp(, .String())
	}
	return 
}

func ( *Parser) () bool {
	return .tok == rightParen && .r == ')'
}

func ( *Parser) ( Pos, ,  token) {
	switch .tok {
	case _Lit, _LitWord:
		.curErr("not a valid arithmetic operator: %s", .val)
	case leftBrack:
		.curErr("[ must follow a name")
	case colon:
		.curErr("ternary operator missing ? before :")
	case rightParen, _EOF:
		.matchingErr(, , )
	default:
		if .quote == arithmExpr {
			.curErr("not a valid arithmetic operator: %v", .tok)
		}
		.matchingErr(, , )
	}
}

func ( *Parser) ( Pos, ,  token) {
	if !.got() {
		.arithmMatchingErr(, , )
	}
}

func ( *Parser) ( token,  Pos,  saveState) Pos {
	if !.peekArithmEnd() {
		.arithmMatchingErr(, , dblRightParen)
	}
	.rune()
	.postNested()
	 := .pos
	.next()
	return 
}