2017-07-15 21:49:08 +02:00
|
|
|
package treerack
|
2017-06-25 17:51:08 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
)
|
|
|
|
|
|
|
|
type CommitType int
|
|
|
|
|
|
|
|
const (
|
|
|
|
None CommitType = 0
|
|
|
|
Alias CommitType = 1 << iota
|
|
|
|
Documentation
|
|
|
|
Root
|
|
|
|
)
|
|
|
|
|
2017-06-25 23:38:32 +02:00
|
|
|
type SequenceItem struct {
|
|
|
|
Name string
|
|
|
|
Min, Max int // 0,0 considered as 1,1, x,0 considered as x,-1
|
|
|
|
}
|
|
|
|
|
2017-06-25 17:51:08 +02:00
|
|
|
type Syntax struct {
|
2017-07-15 21:49:08 +02:00
|
|
|
trace Trace
|
|
|
|
registry *registry
|
|
|
|
initialized bool
|
|
|
|
initFailed bool
|
|
|
|
explicitRoot bool
|
|
|
|
root definition
|
|
|
|
parser parser
|
2017-06-25 17:51:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
2017-07-15 19:04:04 +02:00
|
|
|
ErrSyntaxInitialized = errors.New("syntax initialized")
|
|
|
|
ErrInitFailed = errors.New("init failed")
|
|
|
|
ErrNoParsersDefined = errors.New("no parsers defined")
|
|
|
|
ErrInvalidInput = errors.New("invalid input")
|
|
|
|
ErrInvalidUnicodeCharacter = errors.New("invalid unicode character")
|
|
|
|
ErrInvalidEscapeCharacter = errors.New("invalid escape character")
|
|
|
|
ErrUnexpectedCharacter = errors.New("unexpected character")
|
|
|
|
ErrInvalidSyntax = errors.New("invalid syntax")
|
|
|
|
ErrRootAlias = errors.New("root node cannot be an alias")
|
|
|
|
ErrNotImplemented = errors.New("not implemented")
|
2017-06-25 17:51:08 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
func duplicateDefinition(name string) error {
|
|
|
|
return fmt.Errorf("duplicate definition: %s", name)
|
|
|
|
}
|
|
|
|
|
2017-06-26 00:20:54 +02:00
|
|
|
func NewSyntax() *Syntax {
|
|
|
|
return NewSyntaxTrace(nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewSyntaxTrace(t Trace) *Syntax {
|
2017-06-25 17:51:08 +02:00
|
|
|
if t == nil {
|
2017-06-26 00:20:54 +02:00
|
|
|
t = NopTrace{}
|
2017-06-25 17:51:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return &Syntax{
|
|
|
|
trace: t,
|
|
|
|
registry: newRegistry(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Syntax) register(d definition) error {
|
|
|
|
if s.initialized {
|
|
|
|
return ErrSyntaxInitialized
|
|
|
|
}
|
|
|
|
|
|
|
|
if d.commitType()&Root != 0 {
|
|
|
|
s.root = d
|
2017-07-15 21:49:08 +02:00
|
|
|
s.explicitRoot = true
|
|
|
|
} else if !s.explicitRoot {
|
2017-06-25 17:51:08 +02:00
|
|
|
s.root = d
|
|
|
|
}
|
|
|
|
|
|
|
|
return s.registry.setDefinition(d)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Syntax) AnyChar(name string, ct CommitType) error {
|
2017-06-25 23:38:32 +02:00
|
|
|
return s.Class(name, ct, true, nil, nil)
|
2017-06-25 17:51:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Syntax) Class(name string, ct CommitType, not bool, chars []rune, ranges [][]rune) error {
|
2017-06-25 23:38:32 +02:00
|
|
|
return s.register(newChar(name, ct, not, chars, ranges))
|
2017-06-25 17:51:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func childName(name string, childIndex int) string {
|
|
|
|
return fmt.Sprintf("%s:%d", name, childIndex)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Syntax) CharSequence(name string, ct CommitType, chars []rune) error {
|
|
|
|
var refs []string
|
|
|
|
for i, ci := range chars {
|
|
|
|
ref := childName(name, i)
|
|
|
|
refs = append(refs, ref)
|
2017-06-25 23:38:32 +02:00
|
|
|
if err := s.register(newChar(ref, Alias, false, []rune{ci}, nil)); err != nil {
|
2017-06-25 17:51:08 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-25 23:38:32 +02:00
|
|
|
return s.Sequence(name, ct, namesToSequenceItems(refs)...)
|
2017-06-25 17:51:08 +02:00
|
|
|
}
|
|
|
|
|
2017-06-25 23:38:32 +02:00
|
|
|
func (s *Syntax) Sequence(name string, ct CommitType, items ...SequenceItem) error {
|
2017-06-25 17:51:08 +02:00
|
|
|
return s.register(newSequence(name, ct, items))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Syntax) Choice(name string, ct CommitType, elements ...string) error {
|
|
|
|
return s.register(newChoice(name, ct, elements))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Syntax) Read(r io.Reader) error {
|
|
|
|
if s.initialized {
|
|
|
|
return ErrSyntaxInitialized
|
|
|
|
}
|
|
|
|
|
2017-06-26 01:21:46 +02:00
|
|
|
return ErrNotImplemented
|
2017-06-25 17:51:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Syntax) Init() error {
|
|
|
|
if s.initFailed {
|
|
|
|
return ErrInitFailed
|
|
|
|
}
|
|
|
|
|
|
|
|
if s.initialized {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if s.root == nil {
|
|
|
|
return ErrNoParsersDefined
|
|
|
|
}
|
|
|
|
|
|
|
|
if s.root.commitType()&Alias != 0 {
|
|
|
|
return ErrRootAlias
|
|
|
|
}
|
|
|
|
|
|
|
|
var err error
|
2017-07-15 22:12:01 +02:00
|
|
|
s.parser, err = s.root.parser(s.registry, &idSet{})
|
2017-06-25 17:51:08 +02:00
|
|
|
if err != nil {
|
|
|
|
s.initFailed = true
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
s.initialized = true
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Syntax) Generate(w io.Writer) error {
|
|
|
|
if err := s.Init(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-06-26 01:21:46 +02:00
|
|
|
return ErrNotImplemented
|
2017-06-25 17:51:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Syntax) Parse(r io.Reader) (*Node, error) {
|
|
|
|
if err := s.Init(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
c := newContext(bufio.NewReader(r))
|
|
|
|
return parse(s.trace, s.parser, c)
|
|
|
|
}
|