blob: 61c2c8634bd07be9cd5701b472a262eccb666304 [file] [log] [blame]
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +09001package main
2
3import (
4 "bytes"
5 "errors"
6 "fmt"
7 "io"
Fumitoshi Ukaif0a2ba72015-04-19 00:02:32 +09008 "strconv"
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +09009 "strings"
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +090010 "sync"
Fumitoshi Ukai6ac7f692015-04-15 17:13:51 +090011 "time"
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +090012)
13
14var (
15 errEndOfInput = errors.New("parse: unexpected end of input")
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +090016
17 bufFree = sync.Pool{
18 New: func() interface{} { return new(bytes.Buffer) },
19 }
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +090020)
21
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +090022func newBuf() *bytes.Buffer {
23 buf := bufFree.Get().(*bytes.Buffer)
24 return buf
25}
26
27func freeBuf(buf *bytes.Buffer) {
Fumitoshi Ukai7ff5b232015-05-08 00:44:12 +090028 if cap(buf.Bytes()) > 1024 {
29 return
30 }
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +090031 buf.Reset()
32 bufFree.Put(buf)
33}
34
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +090035type Value interface {
36 String() string
37 Eval(w io.Writer, ev *Evaluator)
Shinichiro Hamajic8bc7312015-04-28 02:48:03 +090038 Serialize() SerializableVar
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +090039}
40
Fumitoshi Ukai6ac7f692015-04-15 17:13:51 +090041type Valuer interface {
42 Value() []byte
43}
44
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +090045// literal is literal value.
Fumitoshi Ukaid2bcf662015-04-11 01:02:27 +090046// TODO(ukai): always use []byte?
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +090047type literal string
48
49func (s literal) String() string { return string(s) }
50func (s literal) Eval(w io.Writer, ev *Evaluator) {
Fumitoshi Ukai5541c7e2015-04-18 22:47:03 +090051 io.WriteString(w, string(s))
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +090052}
Shinichiro Hamajic8bc7312015-04-28 02:48:03 +090053func (s literal) Serialize() SerializableVar {
54 return SerializableVar{Type: "literal", V: string(s)}
55}
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +090056
57// tmpval is temporary value.
Fumitoshi Ukaid2bcf662015-04-11 01:02:27 +090058// TODO(ukai): Values() returns []Value? (word list?)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +090059type tmpval []byte
60
61func (t tmpval) String() string { return string(t) }
62func (t tmpval) Eval(w io.Writer, ev *Evaluator) {
63 w.Write(t)
64}
Fumitoshi Ukai6ac7f692015-04-15 17:13:51 +090065func (t tmpval) Value() []byte { return []byte(t) }
Shinichiro Hamajic8bc7312015-04-28 02:48:03 +090066func (t tmpval) Serialize() SerializableVar {
67 return SerializableVar{Type: "tmpval", V: string(t)}
68}
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +090069
70// Expr is a list of values.
71type Expr []Value
72
73func (e Expr) String() string {
74 var s []string
75 for _, v := range e {
76 s = append(s, v.String())
77 }
78 return strings.Join(s, "")
79}
80
81func (e Expr) Eval(w io.Writer, ev *Evaluator) {
82 for _, v := range e {
83 v.Eval(w, ev)
84 }
85}
86
Shinichiro Hamajic8bc7312015-04-28 02:48:03 +090087func (e Expr) Serialize() SerializableVar {
88 r := SerializableVar{Type: "expr"}
89 for _, v := range e {
90 r.Children = append(r.Children, v.Serialize())
91 }
92 return r
93}
94
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +090095func compactExpr(e Expr) Value {
96 if len(e) == 1 {
97 return e[0]
98 }
99 // TODO(ukai): concat literal
100 return e
101}
102
103// varref is variable reference. e.g. ${foo}.
104type varref struct {
105 varname Value
106}
107
108func (v varref) String() string {
109 varname := v.varname.String()
110 if len(varname) == 1 {
111 return fmt.Sprintf("$%s", varname)
112 }
113 return fmt.Sprintf("${%s}", varname)
114}
115
116func (v varref) Eval(w io.Writer, ev *Evaluator) {
Fumitoshi Ukai6ac7f692015-04-15 17:13:51 +0900117 t := time.Now()
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +0900118 buf := newBuf()
119 v.varname.Eval(buf, ev)
120 vv := ev.LookupVar(buf.String())
121 freeBuf(buf)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900122 vv.Eval(w, ev)
Fumitoshi Ukai6ac7f692015-04-15 17:13:51 +0900123 addStats("var", v, t)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900124}
125
Shinichiro Hamajic8bc7312015-04-28 02:48:03 +0900126func (v varref) Serialize() SerializableVar {
127 return SerializableVar{
Shinichiro Hamaji3d6d0aa2015-04-28 16:18:44 +0900128 Type: "varref",
Shinichiro Hamajic8bc7312015-04-28 02:48:03 +0900129 Children: []SerializableVar{v.varname.Serialize()},
130 }
131}
132
Fumitoshi Ukaif0a2ba72015-04-19 00:02:32 +0900133// paramref is parameter reference e.g. $1.
134type paramref int
135
136func (p paramref) String() string {
137 return fmt.Sprintf("$%d", int(p))
138}
139
140func (p paramref) Eval(w io.Writer, ev *Evaluator) {
141 t := time.Now()
142 n := int(p)
143 if n < len(ev.paramVars) {
144 ev.paramVars[n].Eval(w, ev)
145 } else {
146 // out of range?
147 // panic(fmt.Sprintf("out of range %d: %d", n, len(ev.paramVars)))
148 }
149 addStats("param", p, t)
150}
151
Shinichiro Hamajic8bc7312015-04-28 02:48:03 +0900152func (p paramref) Serialize() SerializableVar {
153 return SerializableVar{Type: "paramref", V: strconv.Itoa(int(p))}
154}
155
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900156// varsubst is variable substitutaion. e.g. ${var:pat=subst}.
157type varsubst struct {
158 varname Value
159 pat Value
160 subst Value
161}
162
163func (v varsubst) String() string {
164 return fmt.Sprintf("${%s:%s=%s}", v.varname, v.pat, v.subst)
165}
166
167func (v varsubst) Eval(w io.Writer, ev *Evaluator) {
Fumitoshi Ukai6ac7f692015-04-15 17:13:51 +0900168 t := time.Now()
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +0900169 buf := newBuf()
170 params := ev.args(buf, v.varname, v.pat, v.subst)
171 vname := string(params[0])
172 pat := string(params[1])
173 subst := string(params[2])
174 buf.Reset()
175 vv := ev.LookupVar(vname)
176 vv.Eval(buf, ev)
177 vals := splitSpaces(buf.String())
178 freeBuf(buf)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900179 space := false
180 for _, val := range vals {
181 if space {
Fumitoshi Ukai5541c7e2015-04-18 22:47:03 +0900182 io.WriteString(w, " ")
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900183 }
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +0900184 io.WriteString(w, substRef(pat, subst, val))
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900185 space = true
186 }
Fumitoshi Ukai6ac7f692015-04-15 17:13:51 +0900187 addStats("varsubst", v, t)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900188}
189
Shinichiro Hamajic8bc7312015-04-28 02:48:03 +0900190func (p varsubst) Serialize() SerializableVar {
191 return SerializableVar{
192 Type: "varsubst",
193 Children: []SerializableVar{
194 p.varname.Serialize(),
195 p.pat.Serialize(),
196 p.subst.Serialize(),
197 },
198 }
199}
200
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900201// parseExpr parses expression in `in` until it finds any byte in term.
202// if term is nil, it will parse to end of input.
203// if term is not nil, and it reaches to end of input, return errEndOfInput.
204// it returns parsed value, and parsed length `n`, so in[n-1] is any byte of
205// term, and in[n:] is next input.
206func parseExpr(in, term []byte) (Value, int, error) {
207 var expr Expr
Fumitoshi Ukaid8d84252015-04-19 17:30:13 +0900208 buf := make([]byte, 0, len(in))
Fumitoshi Ukai00178d12015-04-18 00:11:05 +0900209 b := 0
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900210 i := 0
211 var saveParen byte
212 parenDepth := 0
213Loop:
214 for i < len(in) {
215 ch := in[i]
Fumitoshi Ukai96c79f12015-04-18 22:58:13 +0900216 if term != nil && bytes.IndexByte(term, ch) >= 0 {
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900217 break Loop
218 }
219 switch ch {
220 case '$':
221 if i+1 >= len(in) {
222 break Loop
223 }
224 if in[i+1] == '$' {
Fumitoshi Ukaid8d84252015-04-19 17:30:13 +0900225 buf = append(buf, in[b:i+1]...)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900226 i += 2
Fumitoshi Ukai00178d12015-04-18 00:11:05 +0900227 b = i
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900228 continue
229 }
230 if bytes.IndexByte(term, in[i+1]) >= 0 {
Fumitoshi Ukaid8d84252015-04-19 17:30:13 +0900231 buf = append(buf, in[b:i]...)
232 if len(buf) > 0 {
233 expr = append(expr, literal(string(buf)))
234 buf = buf[:0]
Fumitoshi Ukai00178d12015-04-18 00:11:05 +0900235 }
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900236 expr = append(expr, varref{varname: literal("")})
237 i++
Fumitoshi Ukai00178d12015-04-18 00:11:05 +0900238 b = i
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900239 break Loop
240 }
Fumitoshi Ukaid8d84252015-04-19 17:30:13 +0900241 buf = append(buf, in[b:i]...)
242 if len(buf) > 0 {
243 expr = append(expr, literal(string(buf)))
244 buf = buf[:0]
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900245 }
246 v, n, err := parseDollar(in[i:])
247 if err != nil {
248 return nil, 0, err
249 }
250 i += n
Fumitoshi Ukai00178d12015-04-18 00:11:05 +0900251 b = i
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900252 expr = append(expr, v)
253 continue
254 case '(', '{':
255 cp := closeParen(ch)
256 if i := bytes.IndexByte(term, cp); i >= 0 {
257 parenDepth++
258 saveParen = cp
259 term[i] = 0
260 } else if cp == saveParen {
261 parenDepth++
262 }
263 case saveParen:
264 parenDepth--
265 if parenDepth == 0 {
266 i := bytes.IndexByte(term, 0)
267 term[i] = saveParen
268 saveParen = 0
269 }
270 }
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900271 i++
272 }
Fumitoshi Ukaid8d84252015-04-19 17:30:13 +0900273 buf = append(buf, in[b:i]...)
274 if len(buf) > 0 {
275 expr = append(expr, literal(string(buf)))
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900276 }
277 if i == len(in) && term != nil {
278 return expr, i, errEndOfInput
279 }
280 return compactExpr(expr), i, nil
281}
282
283func closeParen(ch byte) byte {
284 switch ch {
285 case '(':
286 return ')'
287 case '{':
288 return '}'
289 }
290 return 0
291}
292
293// parseDollar parses
294// $(func expr[, expr...]) # func = literal SP
295// $(expr:expr=expr)
296// $(expr)
297// $x
298// it returns parsed value and parsed length.
299func parseDollar(in []byte) (Value, int, error) {
300 if len(in) <= 1 {
301 return nil, 0, errors.New("empty expr")
302 }
303 if in[0] != '$' {
304 return nil, 0, errors.New("should starts with $")
305 }
306 if in[1] == '$' {
307 return nil, 0, errors.New("should handle $$ as literal $")
308 }
309 paren := closeParen(in[1])
310 if paren == 0 {
311 // $x case.
Fumitoshi Ukaif0a2ba72015-04-19 00:02:32 +0900312 if in[1] >= '0' && in[1] <= '9' {
313 return paramref(in[1] - '0'), 2, nil
314 }
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900315 return varref{varname: literal(string(in[1]))}, 2, nil
316 }
317 term := []byte{paren, ':', ' '}
318 var varname Expr
319 i := 2
320Again:
321 for {
322 e, n, err := parseExpr(in[i:], term)
323 if err != nil {
324 return nil, 0, err
325 }
326 varname = append(varname, e)
327 i += n
328 switch in[i] {
329 case paren:
330 // ${expr}
Fumitoshi Ukaif0a2ba72015-04-19 00:02:32 +0900331 vname := compactExpr(varname)
332 if vname, ok := vname.(literal); ok {
333 n, err := strconv.ParseInt(string(vname), 10, 64)
334 if err == nil {
335 // ${n}
336 return paramref(n), i + 1, nil
337 }
338 }
339 return varref{varname: vname}, i + 1, nil
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900340 case ' ':
341 // ${e ...}
342 if token, ok := e.(literal); ok {
Shinichiro Hamaji2216dd62015-04-11 13:44:39 +0900343 funcName := string(token)
344 if f, ok := funcMap[funcName]; ok {
345 return parseFunc(f(), in, i+1, term[:1], funcName)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900346 }
347 }
348 term = term[:2] // drop ' '
349 continue Again
350 case ':':
351 // ${varname:...}
352 term = term[:2]
353 term[1] = '=' // term={paren, '='}.
354 e, n, err := parseExpr(in[i+1:], term)
355 if err != nil {
356 return nil, 0, err
357 }
358 i += 1 + n
359 if in[i] == paren {
360 varname = append(varname, literal(string(":")), e)
361 return varref{varname: varname}, i + 1, nil
362 }
363 // ${varname:xx=...}
364 pat := e
365 subst, n, err := parseExpr(in[i+1:], term[:1])
366 if err != nil {
367 return nil, 0, err
368 }
369 i += 1 + n
370 // ${first:pat=e}
371 return varsubst{
372 varname: compactExpr(varname),
373 pat: pat,
374 subst: subst,
375 }, i + 1, nil
376 default:
377 panic(fmt.Sprintf("unexpected char"))
378 }
379 }
380}
381
382// skipSpaces skips spaces at front of `in` before any bytes in term.
383// in[n] will be the first non white space in in.
384func skipSpaces(in, term []byte) int {
385 for i := 0; i < len(in); i++ {
386 if bytes.IndexByte(term, in[i]) >= 0 {
387 return i
388 }
389 switch in[i] {
390 case ' ', '\t':
391 default:
392 return i
393 }
394 }
395 return len(in)
396}
397
Fumitoshi Ukaiee5c6fc2015-04-16 13:13:10 +0900398// trimLiteralSpace trims literal space around v.
399func trimLiteralSpace(v Value) Value {
400 switch v := v.(type) {
401 case literal:
402 return literal(strings.TrimSpace(string(v)))
403 case tmpval:
404 b := bytes.TrimSpace([]byte(v))
405 if len(b) == 0 {
406 return literal("")
407 }
408 return tmpval(b)
409 case Expr:
410 if len(v) == 0 {
411 return v
412 }
413 switch s := v[0].(type) {
414 case literal, tmpval:
415 t := trimLiteralSpace(s)
416 if t == literal("") {
417 v = v[1:]
418 } else {
419 v[0] = t
420 }
421 }
422 switch s := v[len(v)-1].(type) {
423 case literal, tmpval:
424 t := trimLiteralSpace(s)
425 if t == literal("") {
426 v = v[:len(v)-1]
427 } else {
428 v[len(v)-1] = t
429 }
430 }
431 return compactExpr(v)
432 }
433 return v
434}
435
436// concatLine concatinates line with "\\\n" in function expression.
437func concatLine(v Value) Value {
438 switch v := v.(type) {
439 case literal:
440 for {
441 s := string(v)
442 i := strings.Index(s, "\\\n")
443 if i < 0 {
444 return v
445 }
446 v = literal(s[:i] + strings.TrimLeft(s[i+2:], " \t"))
447 }
448 case tmpval:
449 for {
450 b := []byte(v)
451 i := bytes.Index(b, []byte{'\\', '\n'})
452 if i < 0 {
453 return v
454 }
455 var buf bytes.Buffer
456 buf.Write(b[:i])
457 buf.Write(bytes.TrimLeft(b[i+2:], " \t"))
Fumitoshi Ukaid8d84252015-04-19 17:30:13 +0900458 v = tmpval(buf.Bytes())
Fumitoshi Ukaiee5c6fc2015-04-16 13:13:10 +0900459 }
460 case Expr:
461 for i := range v {
462 switch vv := v[i].(type) {
463 case literal, tmpval:
464 v[i] = concatLine(vv)
465 }
466 }
467 return v
468 }
469 return v
470}
471
Fumitoshi Ukaiebf945c2015-04-10 17:30:04 +0900472// parseFunc parses function arguments from in[s:] for f.
Fumitoshi Ukaib2670d92015-04-16 10:28:27 +0900473// in[0] is '$' and in[s] is space just after func name.
Fumitoshi Ukaiebf945c2015-04-10 17:30:04 +0900474// in[:n] will be "${func args...}"
Shinichiro Hamaji2216dd62015-04-11 13:44:39 +0900475func parseFunc(f Func, in []byte, s int, term []byte, funcName string) (Value, int, error) {
Fumitoshi Ukaib2670d92015-04-16 10:28:27 +0900476 f.AddArg(literal(string(in[1 : s-1])))
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900477 arity := f.Arity()
478 term = append(term, ',')
Fumitoshi Ukaiebf945c2015-04-10 17:30:04 +0900479 i := skipSpaces(in[s:], term)
480 i = s + i
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900481 if i == len(in) {
482 return f, i, nil
483 }
484 narg := 1
485 for {
486 if arity != 0 && narg >= arity {
487 // final arguments.
488 term = term[:1] // drop ','
489 }
Fumitoshi Ukaiee5c6fc2015-04-16 13:13:10 +0900490 v, n, err := parseExpr(in[i:], term)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900491 if err != nil {
492 return nil, 0, err
493 }
Fumitoshi Ukaiee5c6fc2015-04-16 13:13:10 +0900494 v = concatLine(v)
Fumitoshi Ukaida7f2552015-04-16 13:33:37 +0900495 // TODO(ukai): do this in funcIf, funcAnd, or funcOr's compactor?
Fumitoshi Ukaiee5c6fc2015-04-16 13:13:10 +0900496 if (narg == 1 && funcName == "if") || funcName == "and" || funcName == "or" {
497 v = trimLiteralSpace(v)
498 }
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900499 f.AddArg(v)
500 i += n
501 narg++
502 if in[i] == term[0] {
503 i++
504 break
505 }
506 i++ // should be ','
507 if i == len(in) {
508 break
509 }
510 }
Fumitoshi Ukai9f6b6352015-04-16 16:25:09 +0900511 var fv Value
512 fv = f
Fumitoshi Ukaida7f2552015-04-16 13:33:37 +0900513 if compactor, ok := f.(Compactor); ok {
Fumitoshi Ukai9f6b6352015-04-16 16:25:09 +0900514 fv = compactor.Compact()
Fumitoshi Ukaida7f2552015-04-16 13:33:37 +0900515 }
Fumitoshi Ukai6ac7f692015-04-15 17:13:51 +0900516 if katiStatsFlag {
Fumitoshi Ukai9f6b6352015-04-16 16:25:09 +0900517 fv = funcstats{fv}
Fumitoshi Ukai6ac7f692015-04-15 17:13:51 +0900518 }
Fumitoshi Ukai9f6b6352015-04-16 16:25:09 +0900519 return fv, i, nil
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900520}
Fumitoshi Ukai6ac7f692015-04-15 17:13:51 +0900521
Fumitoshi Ukaida7f2552015-04-16 13:33:37 +0900522type Compactor interface {
Fumitoshi Ukai9f6b6352015-04-16 16:25:09 +0900523 Compact() Value
Fumitoshi Ukaida7f2552015-04-16 13:33:37 +0900524}
525
Fumitoshi Ukai6ac7f692015-04-15 17:13:51 +0900526type funcstats struct {
Fumitoshi Ukai9f6b6352015-04-16 16:25:09 +0900527 Value
Fumitoshi Ukai6ac7f692015-04-15 17:13:51 +0900528}
529
530func (f funcstats) Eval(w io.Writer, ev *Evaluator) {
531 t := time.Now()
Fumitoshi Ukai9f6b6352015-04-16 16:25:09 +0900532 f.Value.Eval(w, ev)
Fumitoshi Ukai6ac7f692015-04-15 17:13:51 +0900533 // TODO(ukai): per functype?
534 addStats("func", f, t)
535}