blob: 5d94db2cabbbba9844e51a4f84defebbbb4cbe42 [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{
Fumitoshi Ukaid0bf88c2015-05-08 10:54:32 +090018 New: func() interface{} { return new(buffer) },
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +090019 }
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +090020)
21
Fumitoshi Ukaid0bf88c2015-05-08 10:54:32 +090022type buffer struct {
23 bytes.Buffer
24 args [][]byte
25}
26
27func newBuf() *buffer {
28 buf := bufFree.Get().(*buffer)
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +090029 return buf
30}
31
Fumitoshi Ukaid0bf88c2015-05-08 10:54:32 +090032func freeBuf(buf *buffer) {
Fumitoshi Ukai7ff5b232015-05-08 00:44:12 +090033 if cap(buf.Bytes()) > 1024 {
34 return
35 }
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +090036 buf.Reset()
Fumitoshi Ukaid0bf88c2015-05-08 10:54:32 +090037 buf.args = buf.args[:0]
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +090038 bufFree.Put(buf)
39}
40
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +090041type Value interface {
42 String() string
43 Eval(w io.Writer, ev *Evaluator)
Shinichiro Hamajic8bc7312015-04-28 02:48:03 +090044 Serialize() SerializableVar
Shinichiro Hamaji723f56a2015-05-15 17:12:55 +090045 Dump(w io.Writer)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +090046}
47
Fumitoshi Ukai6ac7f692015-04-15 17:13:51 +090048type Valuer interface {
49 Value() []byte
50}
51
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +090052// literal is literal value.
Fumitoshi Ukaid2bcf662015-04-11 01:02:27 +090053// TODO(ukai): always use []byte?
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +090054type literal string
55
56func (s literal) String() string { return string(s) }
57func (s literal) Eval(w io.Writer, ev *Evaluator) {
Fumitoshi Ukai5541c7e2015-04-18 22:47:03 +090058 io.WriteString(w, string(s))
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +090059}
Shinichiro Hamajic8bc7312015-04-28 02:48:03 +090060func (s literal) Serialize() SerializableVar {
61 return SerializableVar{Type: "literal", V: string(s)}
62}
Shinichiro Hamaji723f56a2015-05-15 17:12:55 +090063func (s literal) Dump(w io.Writer) {
64 dumpByte(w, VALUE_TYPE_LITERAL)
65 dumpBytes(w, []byte(s))
66}
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +090067
68// tmpval is temporary value.
Fumitoshi Ukaid2bcf662015-04-11 01:02:27 +090069// TODO(ukai): Values() returns []Value? (word list?)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +090070type tmpval []byte
71
72func (t tmpval) String() string { return string(t) }
73func (t tmpval) Eval(w io.Writer, ev *Evaluator) {
74 w.Write(t)
75}
Fumitoshi Ukai6ac7f692015-04-15 17:13:51 +090076func (t tmpval) Value() []byte { return []byte(t) }
Shinichiro Hamajic8bc7312015-04-28 02:48:03 +090077func (t tmpval) Serialize() SerializableVar {
78 return SerializableVar{Type: "tmpval", V: string(t)}
79}
Shinichiro Hamaji723f56a2015-05-15 17:12:55 +090080func (t tmpval) Dump(w io.Writer) {
81 dumpByte(w, VALUE_TYPE_TMPVAL)
82 dumpBytes(w, t)
83}
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +090084
85// Expr is a list of values.
86type Expr []Value
87
88func (e Expr) String() string {
89 var s []string
90 for _, v := range e {
91 s = append(s, v.String())
92 }
93 return strings.Join(s, "")
94}
95
96func (e Expr) Eval(w io.Writer, ev *Evaluator) {
97 for _, v := range e {
98 v.Eval(w, ev)
99 }
100}
101
Shinichiro Hamajic8bc7312015-04-28 02:48:03 +0900102func (e Expr) Serialize() SerializableVar {
103 r := SerializableVar{Type: "expr"}
104 for _, v := range e {
105 r.Children = append(r.Children, v.Serialize())
106 }
107 return r
108}
Shinichiro Hamaji723f56a2015-05-15 17:12:55 +0900109func (e Expr) Dump(w io.Writer) {
110 dumpByte(w, VALUE_TYPE_EXPR)
111 dumpInt(w, len(e))
112 for _, v := range e {
113 v.Dump(w)
114 }
115}
Shinichiro Hamajic8bc7312015-04-28 02:48:03 +0900116
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900117func compactExpr(e Expr) Value {
118 if len(e) == 1 {
119 return e[0]
120 }
121 // TODO(ukai): concat literal
122 return e
123}
124
125// varref is variable reference. e.g. ${foo}.
126type varref struct {
127 varname Value
128}
129
130func (v varref) String() string {
131 varname := v.varname.String()
132 if len(varname) == 1 {
133 return fmt.Sprintf("$%s", varname)
134 }
135 return fmt.Sprintf("${%s}", varname)
136}
137
138func (v varref) Eval(w io.Writer, ev *Evaluator) {
Fumitoshi Ukai6ac7f692015-04-15 17:13:51 +0900139 t := time.Now()
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +0900140 buf := newBuf()
141 v.varname.Eval(buf, ev)
142 vv := ev.LookupVar(buf.String())
143 freeBuf(buf)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900144 vv.Eval(w, ev)
Fumitoshi Ukai6ac7f692015-04-15 17:13:51 +0900145 addStats("var", v, t)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900146}
147
Shinichiro Hamajic8bc7312015-04-28 02:48:03 +0900148func (v varref) Serialize() SerializableVar {
149 return SerializableVar{
Shinichiro Hamaji3d6d0aa2015-04-28 16:18:44 +0900150 Type: "varref",
Shinichiro Hamajic8bc7312015-04-28 02:48:03 +0900151 Children: []SerializableVar{v.varname.Serialize()},
152 }
153}
Shinichiro Hamaji723f56a2015-05-15 17:12:55 +0900154func (v varref) Dump(w io.Writer) {
155 dumpByte(w, VALUE_TYPE_VARREF)
156 v.varname.Dump(w)
157}
Shinichiro Hamajic8bc7312015-04-28 02:48:03 +0900158
Fumitoshi Ukaif0a2ba72015-04-19 00:02:32 +0900159// paramref is parameter reference e.g. $1.
160type paramref int
161
162func (p paramref) String() string {
163 return fmt.Sprintf("$%d", int(p))
164}
165
166func (p paramref) Eval(w io.Writer, ev *Evaluator) {
167 t := time.Now()
168 n := int(p)
169 if n < len(ev.paramVars) {
170 ev.paramVars[n].Eval(w, ev)
171 } else {
172 // out of range?
173 // panic(fmt.Sprintf("out of range %d: %d", n, len(ev.paramVars)))
174 }
175 addStats("param", p, t)
176}
177
Shinichiro Hamajic8bc7312015-04-28 02:48:03 +0900178func (p paramref) Serialize() SerializableVar {
179 return SerializableVar{Type: "paramref", V: strconv.Itoa(int(p))}
180}
181
Shinichiro Hamaji723f56a2015-05-15 17:12:55 +0900182func (p paramref) Dump(w io.Writer) {
183 dumpByte(w, VALUE_TYPE_PARAMREF)
184 dumpInt(w, int(p))
185}
186
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900187// varsubst is variable substitutaion. e.g. ${var:pat=subst}.
188type varsubst struct {
189 varname Value
190 pat Value
191 subst Value
192}
193
194func (v varsubst) String() string {
195 return fmt.Sprintf("${%s:%s=%s}", v.varname, v.pat, v.subst)
196}
197
198func (v varsubst) Eval(w io.Writer, ev *Evaluator) {
Fumitoshi Ukai6ac7f692015-04-15 17:13:51 +0900199 t := time.Now()
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +0900200 buf := newBuf()
201 params := ev.args(buf, v.varname, v.pat, v.subst)
202 vname := string(params[0])
203 pat := string(params[1])
204 subst := string(params[2])
205 buf.Reset()
206 vv := ev.LookupVar(vname)
207 vv.Eval(buf, ev)
208 vals := splitSpaces(buf.String())
209 freeBuf(buf)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900210 space := false
211 for _, val := range vals {
212 if space {
Fumitoshi Ukai5541c7e2015-04-18 22:47:03 +0900213 io.WriteString(w, " ")
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900214 }
Fumitoshi Ukaib06cd9d2015-05-07 12:56:12 +0900215 io.WriteString(w, substRef(pat, subst, val))
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900216 space = true
217 }
Fumitoshi Ukai6ac7f692015-04-15 17:13:51 +0900218 addStats("varsubst", v, t)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900219}
220
Shinichiro Hamaji723f56a2015-05-15 17:12:55 +0900221func (v varsubst) Serialize() SerializableVar {
Shinichiro Hamajic8bc7312015-04-28 02:48:03 +0900222 return SerializableVar{
223 Type: "varsubst",
224 Children: []SerializableVar{
Shinichiro Hamaji723f56a2015-05-15 17:12:55 +0900225 v.varname.Serialize(),
226 v.pat.Serialize(),
227 v.subst.Serialize(),
Shinichiro Hamajic8bc7312015-04-28 02:48:03 +0900228 },
229 }
230}
231
Shinichiro Hamaji723f56a2015-05-15 17:12:55 +0900232func (v varsubst) Dump(w io.Writer) {
233 dumpByte(w, VALUE_TYPE_VARSUBST)
234 v.varname.Dump(w)
235 v.pat.Dump(w)
236 v.subst.Dump(w)
237}
238
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900239// parseExpr parses expression in `in` until it finds any byte in term.
240// if term is nil, it will parse to end of input.
241// if term is not nil, and it reaches to end of input, return errEndOfInput.
242// it returns parsed value, and parsed length `n`, so in[n-1] is any byte of
243// term, and in[n:] is next input.
244func parseExpr(in, term []byte) (Value, int, error) {
245 var expr Expr
Fumitoshi Ukaid8d84252015-04-19 17:30:13 +0900246 buf := make([]byte, 0, len(in))
Fumitoshi Ukai00178d12015-04-18 00:11:05 +0900247 b := 0
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900248 i := 0
249 var saveParen byte
250 parenDepth := 0
251Loop:
252 for i < len(in) {
253 ch := in[i]
Fumitoshi Ukai96c79f12015-04-18 22:58:13 +0900254 if term != nil && bytes.IndexByte(term, ch) >= 0 {
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900255 break Loop
256 }
257 switch ch {
258 case '$':
259 if i+1 >= len(in) {
260 break Loop
261 }
262 if in[i+1] == '$' {
Fumitoshi Ukaid8d84252015-04-19 17:30:13 +0900263 buf = append(buf, in[b:i+1]...)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900264 i += 2
Fumitoshi Ukai00178d12015-04-18 00:11:05 +0900265 b = i
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900266 continue
267 }
268 if bytes.IndexByte(term, in[i+1]) >= 0 {
Fumitoshi Ukaid8d84252015-04-19 17:30:13 +0900269 buf = append(buf, in[b:i]...)
270 if len(buf) > 0 {
271 expr = append(expr, literal(string(buf)))
272 buf = buf[:0]
Fumitoshi Ukai00178d12015-04-18 00:11:05 +0900273 }
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900274 expr = append(expr, varref{varname: literal("")})
275 i++
Fumitoshi Ukai00178d12015-04-18 00:11:05 +0900276 b = i
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900277 break Loop
278 }
Fumitoshi Ukaid8d84252015-04-19 17:30:13 +0900279 buf = append(buf, in[b:i]...)
280 if len(buf) > 0 {
281 expr = append(expr, literal(string(buf)))
282 buf = buf[:0]
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900283 }
284 v, n, err := parseDollar(in[i:])
285 if err != nil {
286 return nil, 0, err
287 }
288 i += n
Fumitoshi Ukai00178d12015-04-18 00:11:05 +0900289 b = i
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900290 expr = append(expr, v)
291 continue
292 case '(', '{':
293 cp := closeParen(ch)
294 if i := bytes.IndexByte(term, cp); i >= 0 {
295 parenDepth++
296 saveParen = cp
297 term[i] = 0
298 } else if cp == saveParen {
299 parenDepth++
300 }
301 case saveParen:
302 parenDepth--
303 if parenDepth == 0 {
304 i := bytes.IndexByte(term, 0)
305 term[i] = saveParen
306 saveParen = 0
307 }
308 }
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900309 i++
310 }
Fumitoshi Ukaid8d84252015-04-19 17:30:13 +0900311 buf = append(buf, in[b:i]...)
312 if len(buf) > 0 {
313 expr = append(expr, literal(string(buf)))
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900314 }
315 if i == len(in) && term != nil {
316 return expr, i, errEndOfInput
317 }
318 return compactExpr(expr), i, nil
319}
320
321func closeParen(ch byte) byte {
322 switch ch {
323 case '(':
324 return ')'
325 case '{':
326 return '}'
327 }
328 return 0
329}
330
331// parseDollar parses
332// $(func expr[, expr...]) # func = literal SP
333// $(expr:expr=expr)
334// $(expr)
335// $x
336// it returns parsed value and parsed length.
337func parseDollar(in []byte) (Value, int, error) {
338 if len(in) <= 1 {
339 return nil, 0, errors.New("empty expr")
340 }
341 if in[0] != '$' {
342 return nil, 0, errors.New("should starts with $")
343 }
344 if in[1] == '$' {
345 return nil, 0, errors.New("should handle $$ as literal $")
346 }
347 paren := closeParen(in[1])
348 if paren == 0 {
349 // $x case.
Fumitoshi Ukaif0a2ba72015-04-19 00:02:32 +0900350 if in[1] >= '0' && in[1] <= '9' {
351 return paramref(in[1] - '0'), 2, nil
352 }
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900353 return varref{varname: literal(string(in[1]))}, 2, nil
354 }
355 term := []byte{paren, ':', ' '}
356 var varname Expr
357 i := 2
358Again:
359 for {
360 e, n, err := parseExpr(in[i:], term)
361 if err != nil {
362 return nil, 0, err
363 }
364 varname = append(varname, e)
365 i += n
366 switch in[i] {
367 case paren:
368 // ${expr}
Fumitoshi Ukaif0a2ba72015-04-19 00:02:32 +0900369 vname := compactExpr(varname)
370 if vname, ok := vname.(literal); ok {
371 n, err := strconv.ParseInt(string(vname), 10, 64)
372 if err == nil {
373 // ${n}
374 return paramref(n), i + 1, nil
375 }
376 }
377 return varref{varname: vname}, i + 1, nil
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900378 case ' ':
379 // ${e ...}
380 if token, ok := e.(literal); ok {
Shinichiro Hamaji2216dd62015-04-11 13:44:39 +0900381 funcName := string(token)
382 if f, ok := funcMap[funcName]; ok {
383 return parseFunc(f(), in, i+1, term[:1], funcName)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900384 }
385 }
386 term = term[:2] // drop ' '
387 continue Again
388 case ':':
389 // ${varname:...}
390 term = term[:2]
391 term[1] = '=' // term={paren, '='}.
392 e, n, err := parseExpr(in[i+1:], term)
393 if err != nil {
394 return nil, 0, err
395 }
396 i += 1 + n
397 if in[i] == paren {
398 varname = append(varname, literal(string(":")), e)
399 return varref{varname: varname}, i + 1, nil
400 }
401 // ${varname:xx=...}
402 pat := e
403 subst, n, err := parseExpr(in[i+1:], term[:1])
404 if err != nil {
405 return nil, 0, err
406 }
407 i += 1 + n
408 // ${first:pat=e}
409 return varsubst{
410 varname: compactExpr(varname),
411 pat: pat,
412 subst: subst,
413 }, i + 1, nil
414 default:
415 panic(fmt.Sprintf("unexpected char"))
416 }
417 }
418}
419
420// skipSpaces skips spaces at front of `in` before any bytes in term.
421// in[n] will be the first non white space in in.
422func skipSpaces(in, term []byte) int {
423 for i := 0; i < len(in); i++ {
424 if bytes.IndexByte(term, in[i]) >= 0 {
425 return i
426 }
427 switch in[i] {
428 case ' ', '\t':
429 default:
430 return i
431 }
432 }
433 return len(in)
434}
435
Fumitoshi Ukaiee5c6fc2015-04-16 13:13:10 +0900436// trimLiteralSpace trims literal space around v.
437func trimLiteralSpace(v Value) Value {
438 switch v := v.(type) {
439 case literal:
440 return literal(strings.TrimSpace(string(v)))
441 case tmpval:
442 b := bytes.TrimSpace([]byte(v))
443 if len(b) == 0 {
444 return literal("")
445 }
446 return tmpval(b)
447 case Expr:
448 if len(v) == 0 {
449 return v
450 }
451 switch s := v[0].(type) {
452 case literal, tmpval:
453 t := trimLiteralSpace(s)
454 if t == literal("") {
455 v = v[1:]
456 } else {
457 v[0] = t
458 }
459 }
460 switch s := v[len(v)-1].(type) {
461 case literal, tmpval:
462 t := trimLiteralSpace(s)
463 if t == literal("") {
464 v = v[:len(v)-1]
465 } else {
466 v[len(v)-1] = t
467 }
468 }
469 return compactExpr(v)
470 }
471 return v
472}
473
474// concatLine concatinates line with "\\\n" in function expression.
475func concatLine(v Value) Value {
476 switch v := v.(type) {
477 case literal:
478 for {
479 s := string(v)
480 i := strings.Index(s, "\\\n")
481 if i < 0 {
482 return v
483 }
484 v = literal(s[:i] + strings.TrimLeft(s[i+2:], " \t"))
485 }
486 case tmpval:
487 for {
488 b := []byte(v)
489 i := bytes.Index(b, []byte{'\\', '\n'})
490 if i < 0 {
491 return v
492 }
493 var buf bytes.Buffer
494 buf.Write(b[:i])
495 buf.Write(bytes.TrimLeft(b[i+2:], " \t"))
Fumitoshi Ukaid8d84252015-04-19 17:30:13 +0900496 v = tmpval(buf.Bytes())
Fumitoshi Ukaiee5c6fc2015-04-16 13:13:10 +0900497 }
498 case Expr:
499 for i := range v {
500 switch vv := v[i].(type) {
501 case literal, tmpval:
502 v[i] = concatLine(vv)
503 }
504 }
505 return v
506 }
507 return v
508}
509
Fumitoshi Ukaiebf945c2015-04-10 17:30:04 +0900510// parseFunc parses function arguments from in[s:] for f.
Fumitoshi Ukaib2670d92015-04-16 10:28:27 +0900511// in[0] is '$' and in[s] is space just after func name.
Fumitoshi Ukaiebf945c2015-04-10 17:30:04 +0900512// in[:n] will be "${func args...}"
Shinichiro Hamaji2216dd62015-04-11 13:44:39 +0900513func parseFunc(f Func, in []byte, s int, term []byte, funcName string) (Value, int, error) {
Fumitoshi Ukaib2670d92015-04-16 10:28:27 +0900514 f.AddArg(literal(string(in[1 : s-1])))
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900515 arity := f.Arity()
516 term = append(term, ',')
Fumitoshi Ukaiebf945c2015-04-10 17:30:04 +0900517 i := skipSpaces(in[s:], term)
518 i = s + i
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900519 if i == len(in) {
520 return f, i, nil
521 }
522 narg := 1
523 for {
524 if arity != 0 && narg >= arity {
525 // final arguments.
526 term = term[:1] // drop ','
527 }
Fumitoshi Ukaiee5c6fc2015-04-16 13:13:10 +0900528 v, n, err := parseExpr(in[i:], term)
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900529 if err != nil {
530 return nil, 0, err
531 }
Fumitoshi Ukaiee5c6fc2015-04-16 13:13:10 +0900532 v = concatLine(v)
Fumitoshi Ukaida7f2552015-04-16 13:33:37 +0900533 // TODO(ukai): do this in funcIf, funcAnd, or funcOr's compactor?
Fumitoshi Ukaiee5c6fc2015-04-16 13:13:10 +0900534 if (narg == 1 && funcName == "if") || funcName == "and" || funcName == "or" {
535 v = trimLiteralSpace(v)
536 }
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900537 f.AddArg(v)
538 i += n
539 narg++
540 if in[i] == term[0] {
541 i++
542 break
543 }
544 i++ // should be ','
545 if i == len(in) {
546 break
547 }
548 }
Fumitoshi Ukai9f6b6352015-04-16 16:25:09 +0900549 var fv Value
550 fv = f
Fumitoshi Ukaida7f2552015-04-16 13:33:37 +0900551 if compactor, ok := f.(Compactor); ok {
Fumitoshi Ukai9f6b6352015-04-16 16:25:09 +0900552 fv = compactor.Compact()
Fumitoshi Ukaida7f2552015-04-16 13:33:37 +0900553 }
Fumitoshi Ukai6ac7f692015-04-15 17:13:51 +0900554 if katiStatsFlag {
Fumitoshi Ukai9f6b6352015-04-16 16:25:09 +0900555 fv = funcstats{fv}
Fumitoshi Ukai6ac7f692015-04-15 17:13:51 +0900556 }
Fumitoshi Ukai9f6b6352015-04-16 16:25:09 +0900557 return fv, i, nil
Fumitoshi Ukaib36f3872015-04-10 15:06:38 +0900558}
Fumitoshi Ukai6ac7f692015-04-15 17:13:51 +0900559
Fumitoshi Ukaida7f2552015-04-16 13:33:37 +0900560type Compactor interface {
Fumitoshi Ukai9f6b6352015-04-16 16:25:09 +0900561 Compact() Value
Fumitoshi Ukaida7f2552015-04-16 13:33:37 +0900562}
563
Fumitoshi Ukai6ac7f692015-04-15 17:13:51 +0900564type funcstats struct {
Fumitoshi Ukai9f6b6352015-04-16 16:25:09 +0900565 Value
Fumitoshi Ukai6ac7f692015-04-15 17:13:51 +0900566}
567
568func (f funcstats) Eval(w io.Writer, ev *Evaluator) {
569 t := time.Now()
Fumitoshi Ukai9f6b6352015-04-16 16:25:09 +0900570 f.Value.Eval(w, ev)
Fumitoshi Ukai6ac7f692015-04-15 17:13:51 +0900571 // TODO(ukai): per functype?
572 addStats("func", f, t)
573}