2016-08-13 12:49:04 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
// Grammar of the language used in the evaluator
|
|
|
|
//
|
|
|
|
// Expr = SetExpr
|
|
|
|
// | MapExpr
|
2017-03-10 15:53:21 +00:00
|
|
|
// | CMapExpr
|
2016-08-13 12:49:04 +00:00
|
|
|
// | CmdExpr
|
|
|
|
// | CallExpr
|
|
|
|
// | ExecExpr
|
|
|
|
// | ListExpr
|
|
|
|
//
|
|
|
|
// SetExpr = 'set' <opt> <val> ';'
|
|
|
|
//
|
2017-11-19 18:55:13 +00:00
|
|
|
// MapExpr = 'map' <keys> Expr
|
2016-08-13 12:49:04 +00:00
|
|
|
//
|
2021-08-25 15:28:47 +00:00
|
|
|
// CMapExpr = 'cmap' <key> Expr
|
2017-03-10 15:53:21 +00:00
|
|
|
//
|
2017-11-19 18:55:13 +00:00
|
|
|
// CmdExpr = 'cmd' <name> Expr
|
2016-08-13 12:49:04 +00:00
|
|
|
//
|
|
|
|
// CallExpr = <name> <args> ';'
|
|
|
|
//
|
2017-11-19 18:55:13 +00:00
|
|
|
// ExecExpr = Prefix <value> '\n'
|
|
|
|
// | Prefix '{{' <value> '}}' ';'
|
2016-08-13 12:49:04 +00:00
|
|
|
//
|
2018-05-20 17:30:41 +00:00
|
|
|
// Prefix = '$' | '%' | '!' | '&'
|
2016-08-13 12:49:04 +00:00
|
|
|
//
|
2017-11-19 18:55:13 +00:00
|
|
|
// ListExpr = ':' Expr ListRest '\n'
|
|
|
|
// | ':' '{{' Expr ListRest '}}' ';'
|
2016-08-13 12:49:04 +00:00
|
|
|
//
|
|
|
|
// ListRest = Nil
|
2017-11-19 18:55:13 +00:00
|
|
|
// | Expr ListExpr
|
2016-08-13 12:49:04 +00:00
|
|
|
|
|
|
|
import (
|
2017-11-04 15:50:31 +00:00
|
|
|
"bytes"
|
2016-08-13 12:49:04 +00:00
|
|
|
"fmt"
|
|
|
|
"io"
|
2017-11-04 15:50:31 +00:00
|
|
|
"strings"
|
2016-08-13 12:49:04 +00:00
|
|
|
)
|
|
|
|
|
2016-12-17 21:47:37 +00:00
|
|
|
type expr interface {
|
2016-08-13 12:49:04 +00:00
|
|
|
String() string
|
2016-12-17 21:47:37 +00:00
|
|
|
eval(app *app, args []string)
|
2016-08-13 12:49:04 +00:00
|
|
|
}
|
|
|
|
|
2016-12-17 21:47:37 +00:00
|
|
|
type setExpr struct {
|
2016-08-13 12:49:04 +00:00
|
|
|
opt string
|
|
|
|
val string
|
|
|
|
}
|
|
|
|
|
2016-12-17 21:47:37 +00:00
|
|
|
func (e *setExpr) String() string { return fmt.Sprintf("set %s %s", e.opt, e.val) }
|
2016-08-13 12:49:04 +00:00
|
|
|
|
2016-12-17 21:47:37 +00:00
|
|
|
type mapExpr struct {
|
2016-08-13 12:49:04 +00:00
|
|
|
keys string
|
2016-12-17 21:47:37 +00:00
|
|
|
expr expr
|
2016-08-13 12:49:04 +00:00
|
|
|
}
|
|
|
|
|
2016-12-17 21:47:37 +00:00
|
|
|
func (e *mapExpr) String() string { return fmt.Sprintf("map %s %s", e.keys, e.expr) }
|
2016-08-13 12:49:04 +00:00
|
|
|
|
2017-03-10 15:53:21 +00:00
|
|
|
type cmapExpr struct {
|
2021-08-25 15:28:47 +00:00
|
|
|
key string
|
|
|
|
expr expr
|
2017-03-10 15:53:21 +00:00
|
|
|
}
|
|
|
|
|
2021-08-25 15:28:47 +00:00
|
|
|
func (e *cmapExpr) String() string { return fmt.Sprintf("cmap %s %s", e.key, e.expr) }
|
2017-03-10 15:53:21 +00:00
|
|
|
|
2016-12-17 21:47:37 +00:00
|
|
|
type cmdExpr struct {
|
2016-08-13 12:49:04 +00:00
|
|
|
name string
|
2016-12-17 21:47:37 +00:00
|
|
|
expr expr
|
2016-08-13 12:49:04 +00:00
|
|
|
}
|
|
|
|
|
2016-12-17 21:47:37 +00:00
|
|
|
func (e *cmdExpr) String() string { return fmt.Sprintf("cmd %s %s", e.name, e.expr) }
|
2016-08-13 12:49:04 +00:00
|
|
|
|
2016-12-17 21:47:37 +00:00
|
|
|
type callExpr struct {
|
2018-04-12 15:04:37 +00:00
|
|
|
name string
|
|
|
|
args []string
|
|
|
|
count int
|
2016-08-13 12:49:04 +00:00
|
|
|
}
|
|
|
|
|
2016-12-17 21:47:37 +00:00
|
|
|
func (e *callExpr) String() string { return fmt.Sprintf("%s -- %s", e.name, e.args) }
|
2016-08-13 12:49:04 +00:00
|
|
|
|
2016-12-17 21:47:37 +00:00
|
|
|
type execExpr struct {
|
2017-11-19 18:55:13 +00:00
|
|
|
prefix string
|
|
|
|
value string
|
2016-08-13 12:49:04 +00:00
|
|
|
}
|
|
|
|
|
2017-11-04 15:50:31 +00:00
|
|
|
func (e *execExpr) String() string {
|
|
|
|
var buf bytes.Buffer
|
|
|
|
|
2017-11-19 18:55:13 +00:00
|
|
|
buf.WriteString(e.prefix)
|
2017-11-04 15:50:31 +00:00
|
|
|
buf.WriteString("{{ ")
|
|
|
|
|
2017-11-19 18:55:13 +00:00
|
|
|
lines := strings.Split(e.value, "\n")
|
2017-11-04 15:50:31 +00:00
|
|
|
|
|
|
|
for _, line := range lines {
|
|
|
|
trimmed := strings.TrimSpace(line)
|
|
|
|
if trimmed == "" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
buf.WriteString(trimmed)
|
|
|
|
|
|
|
|
if len(lines) > 1 {
|
|
|
|
buf.WriteString(" ...")
|
|
|
|
}
|
|
|
|
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
buf.WriteString(" }}")
|
|
|
|
|
|
|
|
return buf.String()
|
|
|
|
}
|
2016-08-13 12:49:04 +00:00
|
|
|
|
2016-12-17 21:47:37 +00:00
|
|
|
type listExpr struct {
|
|
|
|
exprs []expr
|
2020-07-03 15:29:55 +00:00
|
|
|
count int
|
2016-08-13 12:49:04 +00:00
|
|
|
}
|
|
|
|
|
2016-12-17 21:47:37 +00:00
|
|
|
func (e *listExpr) String() string {
|
2017-11-04 15:58:48 +00:00
|
|
|
var buf bytes.Buffer
|
|
|
|
|
|
|
|
buf.WriteString(":{{ ")
|
|
|
|
|
2016-08-13 12:49:04 +00:00
|
|
|
for _, expr := range e.exprs {
|
2017-11-04 15:58:48 +00:00
|
|
|
buf.WriteString(expr.String())
|
|
|
|
buf.WriteString("; ")
|
2016-08-13 12:49:04 +00:00
|
|
|
}
|
2017-11-04 15:58:48 +00:00
|
|
|
|
|
|
|
buf.WriteString("}}")
|
|
|
|
|
|
|
|
return buf.String()
|
2016-08-13 12:49:04 +00:00
|
|
|
}
|
|
|
|
|
2016-12-17 21:47:37 +00:00
|
|
|
type parser struct {
|
|
|
|
scanner *scanner
|
|
|
|
expr expr
|
2016-08-13 12:49:04 +00:00
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
2016-12-17 21:47:37 +00:00
|
|
|
func newParser(r io.Reader) *parser {
|
2016-08-13 12:49:04 +00:00
|
|
|
scanner := newScanner(r)
|
|
|
|
|
|
|
|
scanner.scan()
|
|
|
|
|
2016-12-17 21:47:37 +00:00
|
|
|
return &parser{
|
2016-08-13 12:49:04 +00:00
|
|
|
scanner: scanner,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-17 21:47:37 +00:00
|
|
|
func (p *parser) parseExpr() expr {
|
2016-08-13 12:49:04 +00:00
|
|
|
s := p.scanner
|
|
|
|
|
2016-12-17 21:47:37 +00:00
|
|
|
var result expr
|
2016-08-13 12:49:04 +00:00
|
|
|
|
|
|
|
switch s.typ {
|
2016-12-17 21:47:37 +00:00
|
|
|
case tokenEOF:
|
2016-08-13 12:49:04 +00:00
|
|
|
return nil
|
2016-12-17 21:47:37 +00:00
|
|
|
case tokenIdent:
|
2016-08-13 12:49:04 +00:00
|
|
|
switch s.tok {
|
|
|
|
case "set":
|
2016-10-16 11:19:19 +00:00
|
|
|
var val string
|
|
|
|
|
2016-08-13 12:49:04 +00:00
|
|
|
s.scan()
|
2016-12-17 21:47:37 +00:00
|
|
|
if s.typ != tokenIdent {
|
2016-10-16 11:19:19 +00:00
|
|
|
p.err = fmt.Errorf("expected identifier: %s", s.tok)
|
|
|
|
}
|
2016-08-13 12:49:04 +00:00
|
|
|
opt := s.tok
|
|
|
|
|
|
|
|
s.scan()
|
2016-12-17 21:47:37 +00:00
|
|
|
if s.typ != tokenSemicolon {
|
2016-08-13 12:49:04 +00:00
|
|
|
val = s.tok
|
|
|
|
s.scan()
|
|
|
|
}
|
|
|
|
|
|
|
|
s.scan()
|
|
|
|
|
2016-12-17 21:47:37 +00:00
|
|
|
result = &setExpr{opt, val}
|
2016-08-13 12:49:04 +00:00
|
|
|
case "map":
|
2016-12-17 21:47:37 +00:00
|
|
|
var expr expr
|
2016-10-16 18:23:24 +00:00
|
|
|
|
2016-08-13 12:49:04 +00:00
|
|
|
s.scan()
|
|
|
|
keys := s.tok
|
|
|
|
|
|
|
|
s.scan()
|
2016-12-17 21:47:37 +00:00
|
|
|
if s.typ != tokenSemicolon {
|
2016-10-16 18:23:24 +00:00
|
|
|
expr = p.parseExpr()
|
|
|
|
} else {
|
|
|
|
s.scan()
|
|
|
|
}
|
2016-08-13 12:49:04 +00:00
|
|
|
|
2016-12-17 21:47:37 +00:00
|
|
|
result = &mapExpr{keys, expr}
|
2017-03-10 15:53:21 +00:00
|
|
|
case "cmap":
|
2021-08-25 15:28:47 +00:00
|
|
|
var expr expr
|
2017-03-10 15:53:21 +00:00
|
|
|
|
|
|
|
s.scan()
|
|
|
|
key := s.tok
|
|
|
|
|
|
|
|
s.scan()
|
|
|
|
if s.typ != tokenSemicolon {
|
2021-08-25 15:28:47 +00:00
|
|
|
expr = p.parseExpr()
|
|
|
|
} else {
|
2017-03-10 15:53:21 +00:00
|
|
|
s.scan()
|
|
|
|
}
|
|
|
|
|
2021-08-25 15:28:47 +00:00
|
|
|
result = &cmapExpr{key, expr}
|
2016-08-13 12:49:04 +00:00
|
|
|
case "cmd":
|
2016-12-17 21:47:37 +00:00
|
|
|
var expr expr
|
2016-10-16 18:23:24 +00:00
|
|
|
|
2016-08-13 12:49:04 +00:00
|
|
|
s.scan()
|
|
|
|
name := s.tok
|
|
|
|
|
|
|
|
s.scan()
|
2016-12-17 21:47:37 +00:00
|
|
|
if s.typ != tokenSemicolon {
|
2016-10-16 18:23:24 +00:00
|
|
|
expr = p.parseExpr()
|
|
|
|
} else {
|
|
|
|
s.scan()
|
|
|
|
}
|
2016-08-13 12:49:04 +00:00
|
|
|
|
2016-12-17 21:47:37 +00:00
|
|
|
result = &cmdExpr{name, expr}
|
2016-08-13 12:49:04 +00:00
|
|
|
default:
|
|
|
|
name := s.tok
|
|
|
|
|
|
|
|
var args []string
|
2016-12-17 21:47:37 +00:00
|
|
|
for s.scan() && s.typ != tokenSemicolon {
|
2016-08-13 12:49:04 +00:00
|
|
|
args = append(args, s.tok)
|
|
|
|
}
|
|
|
|
|
|
|
|
s.scan()
|
|
|
|
|
2018-04-12 15:04:37 +00:00
|
|
|
result = &callExpr{name, args, 1}
|
2016-08-13 12:49:04 +00:00
|
|
|
}
|
2016-12-17 21:47:37 +00:00
|
|
|
case tokenColon:
|
2016-08-13 12:49:04 +00:00
|
|
|
s.scan()
|
|
|
|
|
2016-12-17 21:47:37 +00:00
|
|
|
var exprs []expr
|
|
|
|
if s.typ == tokenLBraces {
|
2016-08-13 12:49:04 +00:00
|
|
|
s.scan()
|
|
|
|
for {
|
|
|
|
e := p.parseExpr()
|
|
|
|
if e == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
exprs = append(exprs, e)
|
2016-12-17 21:47:37 +00:00
|
|
|
if s.typ == tokenRBraces {
|
2016-08-13 12:49:04 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s.scan()
|
|
|
|
} else {
|
|
|
|
for {
|
|
|
|
e := p.parseExpr()
|
|
|
|
if e == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
exprs = append(exprs, e)
|
|
|
|
if s.tok == "\n" {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
s.scan()
|
|
|
|
|
2020-07-03 15:29:55 +00:00
|
|
|
result = &listExpr{exprs, 1}
|
2016-12-17 21:47:37 +00:00
|
|
|
case tokenPrefix:
|
2016-10-16 11:19:19 +00:00
|
|
|
var expr string
|
|
|
|
|
2017-11-19 18:55:13 +00:00
|
|
|
prefix := s.tok
|
2016-08-13 12:49:04 +00:00
|
|
|
|
|
|
|
s.scan()
|
2016-12-17 21:47:37 +00:00
|
|
|
if s.typ == tokenLBraces {
|
2016-08-13 12:49:04 +00:00
|
|
|
s.scan()
|
|
|
|
expr = s.tok
|
|
|
|
s.scan()
|
|
|
|
} else {
|
2016-10-16 11:19:19 +00:00
|
|
|
expr = s.tok
|
2016-08-13 12:49:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
s.scan()
|
|
|
|
s.scan()
|
|
|
|
|
2017-11-19 18:55:13 +00:00
|
|
|
result = &execExpr{prefix, expr}
|
2016-08-13 12:49:04 +00:00
|
|
|
default:
|
2016-10-16 11:19:19 +00:00
|
|
|
p.err = fmt.Errorf("unexpected token: %s", s.tok)
|
2016-08-13 12:49:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2016-12-17 21:47:37 +00:00
|
|
|
func (p *parser) parse() bool {
|
2016-08-13 12:49:04 +00:00
|
|
|
if p.expr = p.parseExpr(); p.expr == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|