lf/parse.go

241 lines
3.7 KiB
Go
Raw Normal View History

2016-08-13 12:49:04 +00:00
package main
// Grammar of the language used in the evaluator
//
// Expr = SetExpr
// | MapExpr
// | CmdExpr
// | CallExpr
// | ExecExpr
// | ListExpr
//
// SetExpr = 'set' <opt> <val> ';'
//
// MapExpr = 'map' <keys> Expr ';'
//
// CmdExpr = 'cmd' <name> Expr ';'
//
// CallExpr = <name> <args> ';'
//
// ExecExpr = Prefix <expr> '\n'
// | Prefix '{{' <expr> '}}' ';'
//
// Prefix = '$' | '!' | '&' | '/' | '?'
//
// ListExpr = ':' ListRest '\n'
2016-08-13 12:49:04 +00:00
// | ':' '{{' ListRest '}}' ';'
//
// ListRest = Nil
// | Expr ListRest
import (
"fmt"
"io"
)
type Expr interface {
String() string
eval(app *App, args []string)
// TODO: add a bind method to avoid passing args in eval
}
type SetExpr struct {
opt string
val string
}
func (e *SetExpr) String() string { return fmt.Sprintf("set %s %s", e.opt, e.val) }
type MapExpr struct {
keys string
expr Expr
}
func (e *MapExpr) String() string { return fmt.Sprintf("map %s %s", e.keys, e.expr) }
type CmdExpr struct {
name string
expr Expr
}
func (e *CmdExpr) String() string { return fmt.Sprintf("cmd %s %s", e.name, e.expr) }
type CallExpr struct {
name string
args []string
}
func (e *CallExpr) String() string { return fmt.Sprintf("%s -- %s", e.name, e.args) }
type ExecExpr struct {
pref string
expr string
}
func (e *ExecExpr) String() string { return fmt.Sprintf("%s %s", e.pref, e.expr) }
type ListExpr struct {
exprs []Expr
}
func (e *ListExpr) String() string {
buf := []byte{':', '{', '{', ' '}
for _, expr := range e.exprs {
buf = append(buf, expr.String()...)
buf = append(buf, ';', ' ')
}
buf = append(buf, '}', '}')
return string(buf)
}
type Parser struct {
scanner *Scanner
expr Expr
err error
}
func newParser(r io.Reader) *Parser {
scanner := newScanner(r)
scanner.scan()
return &Parser{
scanner: scanner,
}
}
func (p *Parser) parseExpr() Expr {
s := p.scanner
var result Expr
switch s.typ {
case TokenEOF:
return nil
case TokenIdent:
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-10-16 11:19:19 +00:00
if s.typ != TokenIdent {
p.err = fmt.Errorf("expected identifier: %s", s.tok)
}
2016-08-13 12:49:04 +00:00
opt := s.tok
s.scan()
if s.typ != TokenSemicolon {
val = s.tok
s.scan()
}
s.scan()
result = &SetExpr{opt, val}
case "map":
var expr Expr
2016-08-13 12:49:04 +00:00
s.scan()
keys := s.tok
s.scan()
if s.typ != TokenSemicolon {
expr = p.parseExpr()
} else {
s.scan()
}
2016-08-13 12:49:04 +00:00
result = &MapExpr{keys, expr}
case "cmd":
var expr Expr
2016-08-13 12:49:04 +00:00
s.scan()
name := s.tok
s.scan()
if s.typ != TokenSemicolon {
expr = p.parseExpr()
} else {
s.scan()
}
2016-08-13 12:49:04 +00:00
result = &CmdExpr{name, expr}
default:
name := s.tok
var args []string
for s.scan() && s.typ != TokenSemicolon {
args = append(args, s.tok)
}
s.scan()
result = &CallExpr{name, args}
}
case TokenColon:
s.scan()
var exprs []Expr
if s.typ == TokenLBraces {
s.scan()
for {
e := p.parseExpr()
if e == nil {
return nil
}
exprs = append(exprs, e)
if s.typ == TokenRBraces {
break
}
}
s.scan()
} else {
for {
e := p.parseExpr()
if e == nil {
return nil
}
exprs = append(exprs, e)
if s.tok == "\n" {
break
}
}
}
s.scan()
result = &ListExpr{exprs}
case TokenPrefix:
2016-10-16 11:19:19 +00:00
var expr string
2016-08-13 12:49:04 +00:00
pref := s.tok
s.scan()
if s.typ == TokenLBraces {
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()
result = &ExecExpr{pref, expr}
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
}
func (p *Parser) parse() bool {
if p.expr = p.parseExpr(); p.expr == nil {
return false
}
return true
}