parser_arithm.go

  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}