You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
135 lines
2.4 KiB
Go
135 lines
2.4 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
const (
|
|
e_no_error parseErrorType = iota
|
|
e_lex_error
|
|
e_unexpected_eof
|
|
e_unexpected_token
|
|
)
|
|
|
|
type parseErrorType int
|
|
|
|
type parseError struct {
|
|
t parseErrorType
|
|
m string
|
|
}
|
|
|
|
func (p parseError) Error() string {
|
|
return fmt.Sprintf("parse error: %s", p.m)
|
|
}
|
|
|
|
func parseErrorf(t parseErrorType, tpl string, args ...interface{}) error {
|
|
return parseError{t: t, m: fmt.Sprintf(tpl, args...)}
|
|
}
|
|
|
|
type parseFn func(*parser) (parseFn, error)
|
|
|
|
func parseRoot(p *parser) (parseFn, error) {
|
|
if err := p.next(); err != nil {
|
|
return nil, err
|
|
}
|
|
switch p.cur.t {
|
|
case t_name:
|
|
return parseAfterName(p.cur.s), nil
|
|
default:
|
|
return nil, parseErrorf(e_unexpected_token, "unexpected %s token in parseRoot", p.cur.t)
|
|
}
|
|
}
|
|
|
|
func parseAfterName(name string) parseFn {
|
|
return func(p *parser) (parseFn, error) {
|
|
switch err := p.next(); err {
|
|
case io.EOF:
|
|
return nil, parseErrorf(e_unexpected_eof, "unexpected eof after name %s", name)
|
|
case nil:
|
|
default:
|
|
return nil, err
|
|
}
|
|
|
|
switch p.cur.t {
|
|
case t_equals:
|
|
return parseAssign(name), nil
|
|
default:
|
|
return nil, parseErrorf(e_unexpected_token, "unexpected %s token in parseAfterName", p.cur.t)
|
|
}
|
|
}
|
|
}
|
|
|
|
func parseAssign(name string) parseFn {
|
|
return func(p *parser) (parseFn, error) {
|
|
switch err := p.next(); err {
|
|
case io.EOF:
|
|
return nil, parseErrorf(e_unexpected_eof, "unexpected eof when trying to parse value for name %s", name)
|
|
case nil:
|
|
default:
|
|
return nil, err
|
|
}
|
|
|
|
switch p.cur.t {
|
|
case t_string:
|
|
p.out.setUnique(name, p.cur.s)
|
|
return parseRoot, nil
|
|
default:
|
|
return nil, parseErrorf(e_unexpected_token, "unexpected %s token in parseAssign", p.cur.t)
|
|
}
|
|
}
|
|
}
|
|
|
|
type parser struct {
|
|
in chan token
|
|
cur token
|
|
out *Config
|
|
}
|
|
|
|
func (p *parser) next() error {
|
|
t, ok := <-p.in
|
|
if !ok {
|
|
return io.EOF
|
|
}
|
|
if t.t == t_error {
|
|
return parseError{e_lex_error, t.s}
|
|
}
|
|
p.cur = t
|
|
return nil
|
|
}
|
|
|
|
func (p *parser) run() error {
|
|
fn := parseRoot
|
|
var err error
|
|
for {
|
|
fn, err = fn(p)
|
|
switch err {
|
|
case io.EOF:
|
|
return nil
|
|
case nil:
|
|
default:
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
type assignment struct {
|
|
name string
|
|
value interface{}
|
|
}
|
|
|
|
func parse(in chan token) (*Config, error) {
|
|
p := &parser{
|
|
in: in,
|
|
out: new(Config),
|
|
}
|
|
if err := p.run(); err != nil {
|
|
return nil, err
|
|
}
|
|
return p.out, nil
|
|
}
|
|
|
|
func parseString(in string) (*Config, error) {
|
|
return parse(lexString(in))
|
|
}
|