1package syntax
2
3// compact specifies whether we allow spaces between expressions.
4// This is true for let
5func (p *Parser) arithmExpr(compact bool) ArithmExpr {
6 return p.arithmExprComma(compact)
7}
8
9// These function names are inspired by Bash's expr.c
10
11func (p *Parser) arithmExprComma(compact bool) ArithmExpr {
12 return p.arithmExprBinary(compact, p.arithmExprAssign, Comma)
13}
14
15func (p *Parser) arithmExprAssign(compact bool) ArithmExpr {
16 // Assign is different from the other binary operators because it's
17 // right-associative and needs to check that it's placed after a name
18 value := p.arithmExprTernary(compact)
19 switch BinAritOperator(p.tok) {
20 case AddAssgn, SubAssgn, MulAssgn, QuoAssgn, RemAssgn, AndAssgn,
21 OrAssgn, XorAssgn, ShlAssgn, ShrAssgn, Assgn:
22 if compact && p.spaced {
23 return value
24 }
25 if !isArithName(value) {
26 p.posErr(p.pos, "%s must follow a name", p.tok.String())
27 }
28 pos := p.pos
29 tok := p.tok
30 p.nextArithOp(compact)
31 y := p.arithmExprAssign(compact)
32 if y == nil {
33 p.followErrExp(pos, tok.String())
34 }
35 return &BinaryArithm{
36 OpPos: pos,
37 Op: BinAritOperator(tok),
38 X: value,
39 Y: y,
40 }
41 }
42 return value
43}
44
45func (p *Parser) arithmExprTernary(compact bool) ArithmExpr {
46 value := p.arithmExprLor(compact)
47 if BinAritOperator(p.tok) != TernQuest || (compact && p.spaced) {
48 return value
49 }
50
51 if value == nil {
52 p.curErr("%s must follow an expression", p.tok.String())
53 }
54 questPos := p.pos
55 p.nextArithOp(compact)
56 if BinAritOperator(p.tok) == TernColon {
57 p.followErrExp(questPos, TernQuest.String())
58 }
59 trueExpr := p.arithmExpr(compact)
60 if trueExpr == nil {
61 p.followErrExp(questPos, TernQuest.String())
62 }
63 if BinAritOperator(p.tok) != TernColon {
64 p.posErr(questPos, "ternary operator missing : after ?")
65 }
66 colonPos := p.pos
67 p.nextArithOp(compact)
68 falseExpr := p.arithmExprTernary(compact)
69 if falseExpr == nil {
70 p.followErrExp(colonPos, TernColon.String())
71 }
72 return &BinaryArithm{
73 OpPos: questPos,
74 Op: TernQuest,
75 X: value,
76 Y: &BinaryArithm{
77 OpPos: colonPos,
78 Op: TernColon,
79 X: trueExpr,
80 Y: falseExpr,
81 },
82 }
83}
84
85func (p *Parser) arithmExprLor(compact bool) ArithmExpr {
86 return p.arithmExprBinary(compact, p.arithmExprLand, OrArit)
87}
88
89func (p *Parser) arithmExprLand(compact bool) ArithmExpr {
90 return p.arithmExprBinary(compact, p.arithmExprBor, AndArit)
91}
92
93func (p *Parser) arithmExprBor(compact bool) ArithmExpr {
94 return p.arithmExprBinary(compact, p.arithmExprBxor, Or)
95}
96
97func (p *Parser) arithmExprBxor(compact bool) ArithmExpr {
98 return p.arithmExprBinary(compact, p.arithmExprBand, Xor)
99}
100
101func (p *Parser) arithmExprBand(compact bool) ArithmExpr {
102 return p.arithmExprBinary(compact, p.arithmExprEquality, And)
103}
104
105func (p *Parser) arithmExprEquality(compact bool) ArithmExpr {
106 return p.arithmExprBinary(compact, p.arithmExprComparison, Eql, Neq)
107}
108
109func (p *Parser) arithmExprComparison(compact bool) ArithmExpr {
110 return p.arithmExprBinary(compact, p.arithmExprShift, Lss, Gtr, Leq, Geq)
111}
112
113func (p *Parser) arithmExprShift(compact bool) ArithmExpr {
114 return p.arithmExprBinary(compact, p.arithmExprAddition, Shl, Shr)
115}
116
117func (p *Parser) arithmExprAddition(compact bool) ArithmExpr {
118 return p.arithmExprBinary(compact, p.arithmExprMultiplication, Add, Sub)
119}
120
121func (p *Parser) arithmExprMultiplication(compact bool) ArithmExpr {
122 return p.arithmExprBinary(compact, p.arithmExprPower, Mul, Quo, Rem)
123}
124
125func (p *Parser) arithmExprPower(compact bool) ArithmExpr {
126 // Power is different from the other binary operators because it's right-associative
127 value := p.arithmExprUnary(compact)
128 if BinAritOperator(p.tok) != Pow || (compact && p.spaced) {
129 return value
130 }
131
132 if value == nil {
133 p.curErr("%s must follow an expression", p.tok.String())
134 }
135
136 op := p.tok
137 pos := p.pos
138 p.nextArithOp(compact)
139 y := p.arithmExprPower(compact)
140 if y == nil {
141 p.followErrExp(pos, op.String())
142 }
143 return &BinaryArithm{
144 OpPos: pos,
145 Op: BinAritOperator(op),
146 X: value,
147 Y: y,
148 }
149}
150
151func (p *Parser) arithmExprUnary(compact bool) ArithmExpr {
152 if !compact {
153 p.got(_Newl)
154 }
155
156 switch UnAritOperator(p.tok) {
157 case Not, BitNegation, Plus, Minus:
158 ue := &UnaryArithm{OpPos: p.pos, Op: UnAritOperator(p.tok)}
159 p.nextArithOp(compact)
160 if ue.X = p.arithmExprUnary(compact); ue.X == nil {
161 p.followErrExp(ue.OpPos, ue.Op.String())
162 }
163 return ue
164 }
165 return p.arithmExprValue(compact)
166}
167
168func (p *Parser) arithmExprValue(compact bool) ArithmExpr {
169 var x ArithmExpr
170 switch p.tok {
171 case addAdd, subSub:
172 ue := &UnaryArithm{OpPos: p.pos, Op: UnAritOperator(p.tok)}
173 p.nextArith(compact)
174 if p.tok != _LitWord {
175 p.followErr(ue.OpPos, token(ue.Op).String(), "a literal")
176 }
177 ue.X = p.arithmExprValue(compact)
178 return ue
179 case leftParen:
180 pe := &ParenArithm{Lparen: p.pos}
181 p.nextArithOp(compact)
182 pe.X = p.followArithm(leftParen, pe.Lparen)
183 pe.Rparen = p.matched(pe.Lparen, leftParen, rightParen)
184 x = pe
185 case leftBrack:
186 p.curErr("[ must follow a name")
187 case colon:
188 p.curErr("ternary operator missing ? before :")
189 case _LitWord:
190 l := p.getLit()
191 if p.tok != leftBrack {
192 x = p.wordOne(l)
193 break
194 }
195 pe := &ParamExp{Dollar: l.ValuePos, Short: true, Param: l}
196 pe.Index = p.eitherIndex()
197 x = p.wordOne(pe)
198 case bckQuote:
199 if p.quote == arithmExprLet && p.openBquotes > 0 {
200 return nil
201 }
202 fallthrough
203 default:
204 if w := p.getWord(); w != nil {
205 x = w
206 } else {
207 return nil
208 }
209 }
210
211 if compact && p.spaced {
212 return x
213 }
214 if !compact {
215 p.got(_Newl)
216 }
217
218 // we want real nil, not (*Word)(nil) as that
219 // sets the type to non-nil and then x != nil
220 if p.tok == addAdd || p.tok == subSub {
221 if !isArithName(x) {
222 p.curErr("%s must follow a name", p.tok.String())
223 }
224 u := &UnaryArithm{
225 Post: true,
226 OpPos: p.pos,
227 Op: UnAritOperator(p.tok),
228 X: x,
229 }
230 p.nextArith(compact)
231 return u
232 }
233 return x
234}
235
236// nextArith consumes a token.
237// It returns true if compact and the token was followed by spaces
238func (p *Parser) nextArith(compact bool) bool {
239 p.next()
240 if compact && p.spaced {
241 return true
242 }
243 if !compact {
244 p.got(_Newl)
245 }
246 return false
247}
248
249func (p *Parser) nextArithOp(compact bool) {
250 pos := p.pos
251 tok := p.tok
252 if p.nextArith(compact) {
253 p.followErrExp(pos, tok.String())
254 }
255}
256
257// arithmExprBinary is used for all left-associative binary operators
258func (p *Parser) arithmExprBinary(compact bool, nextOp func(bool) ArithmExpr, operators ...BinAritOperator) ArithmExpr {
259 value := nextOp(compact)
260 for {
261 var foundOp BinAritOperator
262 for _, op := range operators {
263 if p.tok == token(op) {
264 foundOp = op
265 break
266 }
267 }
268
269 if token(foundOp) == illegalTok || (compact && p.spaced) {
270 return value
271 }
272
273 if value == nil {
274 p.curErr("%s must follow an expression", p.tok.String())
275 }
276
277 pos := p.pos
278 p.nextArithOp(compact)
279 y := nextOp(compact)
280 if y == nil {
281 p.followErrExp(pos, foundOp.String())
282 }
283
284 value = &BinaryArithm{
285 OpPos: pos,
286 Op: foundOp,
287 X: value,
288 Y: y,
289 }
290 }
291}
292
293func isArithName(left ArithmExpr) bool {
294 w, ok := left.(*Word)
295 if !ok || len(w.Parts) != 1 {
296 return false
297 }
298 switch wp := w.Parts[0].(type) {
299 case *Lit:
300 return ValidName(wp.Value)
301 case *ParamExp:
302 return wp.nakedIndex()
303 default:
304 return false
305 }
306}
307
308func (p *Parser) followArithm(ftok token, fpos Pos) ArithmExpr {
309 x := p.arithmExpr(false)
310 if x == nil {
311 p.followErrExp(fpos, ftok.String())
312 }
313 return x
314}
315
316func (p *Parser) peekArithmEnd() bool {
317 return p.tok == rightParen && p.r == ')'
318}
319
320func (p *Parser) arithmMatchingErr(pos Pos, left, right token) {
321 switch p.tok {
322 case _Lit, _LitWord:
323 p.curErr("not a valid arithmetic operator: %s", p.val)
324 case leftBrack:
325 p.curErr("[ must follow a name")
326 case colon:
327 p.curErr("ternary operator missing ? before :")
328 case rightParen, _EOF:
329 p.matchingErr(pos, left, right)
330 default:
331 if p.quote == arithmExpr {
332 p.curErr("not a valid arithmetic operator: %v", p.tok)
333 }
334 p.matchingErr(pos, left, right)
335 }
336}
337
338func (p *Parser) matchedArithm(lpos Pos, left, right token) {
339 if !p.got(right) {
340 p.arithmMatchingErr(lpos, left, right)
341 }
342}
343
344func (p *Parser) arithmEnd(ltok token, lpos Pos, old saveState) Pos {
345 if !p.peekArithmEnd() {
346 if p.recoverError() {
347 return recoveredPos
348 }
349 p.arithmMatchingErr(lpos, ltok, dblRightParen)
350 }
351 p.rune()
352 p.postNested(old)
353 pos := p.pos
354 p.next()
355 return pos
356}