diff --git a/char.go b/char.go index 3bb0ae6..24dfd9a 100644 --- a/char.go +++ b/char.go @@ -7,7 +7,7 @@ type charParser struct { not bool chars []rune ranges [][]rune - includedBy []parser + includedBy []int } func newChar( @@ -30,6 +30,13 @@ func (p *charParser) nodeName() string { return p.name } func (p *charParser) nodeID() int { return p.id } func (p *charParser) setID(id int) { p.id = id } +func (p *charParser) init(r *registry) error { return nil } + +func (p *charParser) setIncludedBy(r *registry, includedBy int, parsers *idSet) error { + p.includedBy = appendIfMissing(p.includedBy, includedBy) + return nil +} + func (p *charParser) parser(r *registry, parsers *idSet) (parser, error) { if parsers.has(p.id) { panic(cannotIncludeParsers(p.name)) @@ -47,14 +54,6 @@ func (p *charParser) commitType() CommitType { return p.commit } -func (p *charParser) setIncludedBy(includedBy parser, parsers *idSet) { - if parsers.has(p.id) { - panic(cannotIncludeParsers(p.name)) - } - - p.includedBy = append(p.includedBy, includedBy) -} - func (p *charParser) storeIncluded(*context, int, int) { panic(cannotIncludeParsers(p.name)) } @@ -83,6 +82,6 @@ func (p *charParser) parse(t Trace, c *context) { c.success(c.offset + 1) for _, includedBy := range p.includedBy { - includedBy.storeIncluded(c, c.offset, c.offset+1) + c.store.setMatch(c.offset, includedBy, c.offset+1) } } diff --git a/choice.go b/choice.go index 55df20b..a0f9ce0 100644 --- a/choice.go +++ b/choice.go @@ -1,10 +1,11 @@ package treerack type choiceDefinition struct { - name string - id int - commit CommitType - elements []string + name string + id int + commit CommitType + elements []string + includedBy []int } type choiceParser struct { @@ -12,7 +13,7 @@ type choiceParser struct { id int commit CommitType elements []parser - includedBy []parser + includedBy []int } func newChoice(name string, ct CommitType, elements []string) *choiceDefinition { @@ -27,6 +28,22 @@ func (d *choiceDefinition) nodeName() string { return d.name } func (d *choiceDefinition) nodeID() int { return d.id } func (d *choiceDefinition) setID(id int) { d.id = id } +func (d *choiceDefinition) init(r *registry) error { + parsers := &idSet{} + parsers.set(d.id) + return setItemsIncludedBy(r, d.elements, d.id, parsers) +} + +func (d *choiceDefinition) setIncludedBy(r *registry, includedBy int, parsers *idSet) error { + if parsers.has(d.id) { + return nil + } + + d.includedBy = appendIfMissing(d.includedBy, includedBy) + parsers.set(d.id) + return setItemsIncludedBy(r, d.elements, includedBy, parsers) +} + func (d *choiceDefinition) parser(r *registry, parsers *idSet) (parser, error) { p, ok := r.parser(d.name) if ok { @@ -34,9 +51,10 @@ func (d *choiceDefinition) parser(r *registry, parsers *idSet) (parser, error) { } cp := &choiceParser{ - name: d.name, - id: d.id, - commit: d.commit, + name: d.name, + id: d.id, + commit: d.commit, + includedBy: d.includedBy, } r.setParser(cp) @@ -48,7 +66,6 @@ func (d *choiceDefinition) parser(r *registry, parsers *idSet) (parser, error) { element, ok := r.parser(e) if ok { elements = append(elements, element) - element.setIncludedBy(cp, parsers) continue } @@ -62,7 +79,6 @@ func (d *choiceDefinition) parser(r *registry, parsers *idSet) (parser, error) { return nil, err } - element.setIncludedBy(cp, parsers) elements = append(elements, element) } @@ -77,26 +93,6 @@ func (d *choiceDefinition) commitType() CommitType { func (p *choiceParser) nodeName() string { return p.name } func (p *choiceParser) nodeID() int { return p.id } -func (p *choiceParser) setIncludedBy(includedBy parser, parsers *idSet) { - if parsers.has(p.id) { - return - } - - p.includedBy = append(p.includedBy, includedBy) -} - -func (p *choiceParser) storeIncluded(c *context, from, to int) { - if !c.excluded(from, p.id) { - return - } - - c.store.setMatch(from, p.id, to) - - for _, includedBy := range p.includedBy { - includedBy.storeIncluded(c, from, to) - } -} - func (p *choiceParser) parse(t Trace, c *context) { if p.commit&Documentation != 0 { c.fail(c.offset) @@ -140,7 +136,7 @@ func (p *choiceParser) parse(t Trace, c *context) { c.store.setMatch(from, p.id, to) for _, includedBy := range p.includedBy { - includedBy.storeIncluded(c, from, to) + c.store.setMatch(from, includedBy, to) } } diff --git a/parse.go b/parse.go index 91bb52d..d7d859d 100644 --- a/parse.go +++ b/parse.go @@ -6,6 +6,8 @@ type definition interface { nodeName() string nodeID() int setID(int) + init(*registry) error + setIncludedBy(*registry, int, *idSet) error parser(*registry, *idSet) (parser, error) commitType() CommitType // builder() builder @@ -14,8 +16,6 @@ type definition interface { type parser interface { nodeName() string nodeID() int - setIncludedBy(parser, *idSet) - storeIncluded(*context, int, int) // can be just an id set, taking what's excluded from the context parse(Trace, *context) } @@ -33,6 +33,46 @@ func cannotIncludeParsers(name string) error { return fmt.Errorf("parser: %s cannot include other parsers", name) } +func intsContain(is []int, i int) bool { + for _, ii := range is { + if ii == i { + return true + } + } + + return false +} + +func appendIfMissing(is []int, i int) []int { + if intsContain(is, i) { + return is + } + + return append(is, i) +} + +func setItemsIncludedBy(r *registry, items []string, includedBy int, parsers *idSet) error { + for _, item := range items { + di, ok := r.definition(item) + if !ok { + return ErrNoParsersDefined + } + + di.setIncludedBy(r, includedBy, parsers) + } + + return nil +} + +func sequenceItemNames(items []SequenceItem) []string { + names := make([]string, len(items)) + for i := range items { + names[i] = items[i].Name + } + + return names +} + func parse(t Trace, p parser, c *context) (*Node, error) { p.parse(t, c) if c.readErr != nil { diff --git a/sequence.go b/sequence.go index eecaa1d..c30582a 100644 --- a/sequence.go +++ b/sequence.go @@ -1,10 +1,11 @@ package treerack type sequenceDefinition struct { - name string - id int - commit CommitType - items []SequenceItem + name string + id int + commit CommitType + items []SequenceItem + includedBy []int } type sequenceParser struct { @@ -13,7 +14,7 @@ type sequenceParser struct { commit CommitType items []parser ranges [][]int - includedBy []parser + includedBy []int } func newSequence(name string, ct CommitType, items []SequenceItem) *sequenceDefinition { @@ -28,6 +29,42 @@ func (d *sequenceDefinition) nodeName() string { return d.name } func (d *sequenceDefinition) nodeID() int { return d.id } func (d *sequenceDefinition) setID(id int) { d.id = id } +func (d *sequenceDefinition) includeItems() bool { + return len(d.items) == 1 && d.items[0].Min == 1 && d.items[0].Max == 1 +} + +func (d *sequenceDefinition) init(r *registry) error { + for _, item := range d.items { + if item.Min == 0 && item.Max == 0 { + item.Min, item.Max = 1, 1 + } else if item.Max == 0 { + item.Max = -1 + } + } + + if !d.includeItems() { + return nil + } + + parsers := &idSet{} + parsers.set(d.id) + return setItemsIncludedBy(r, sequenceItemNames(d.items), d.id, parsers) +} + +func (d *sequenceDefinition) setIncludedBy(r *registry, includedBy int, parsers *idSet) error { + if parsers.has(d.id) { + return nil + } + + d.includedBy = appendIfMissing(d.includedBy, includedBy) + if !d.includeItems() { + return nil + } + + parsers.set(d.id) + return setItemsIncludedBy(r, sequenceItemNames(d.items), includedBy, parsers) +} + func (d *sequenceDefinition) parser(r *registry, parsers *idSet) (parser, error) { if parsers.has(d.id) { panic(cannotIncludeParsers(d.name)) @@ -39,9 +76,10 @@ func (d *sequenceDefinition) parser(r *registry, parsers *idSet) (parser, error) } sp := &sequenceParser{ - name: d.name, - id: d.id, - commit: d.commit, + name: d.name, + id: d.id, + commit: d.commit, + includedBy: d.includedBy, } r.setParser(sp) @@ -81,11 +119,6 @@ func (d *sequenceDefinition) parser(r *registry, parsers *idSet) (parser, error) ranges = append(ranges, []int{item.Min, item.Max}) } - // for single items, acts like a choice - if len(items) == 1 && ranges[0][0] == 1 && ranges[0][1] == 1 { - items[0].setIncludedBy(sp, parsers) - } - sp.items = items sp.ranges = ranges return sp, nil @@ -98,26 +131,6 @@ func (d *sequenceDefinition) commitType() CommitType { func (p *sequenceParser) nodeName() string { return p.name } func (p *sequenceParser) nodeID() int { return p.id } -func (p *sequenceParser) setIncludedBy(includedBy parser, parsers *idSet) { - if parsers.has(p.id) { - return - } - - p.includedBy = append(p.includedBy, includedBy) -} - -func (p *sequenceParser) storeIncluded(c *context, from, to int) { - if !c.excluded(from, p.id) { - return - } - - c.store.setMatch(from, p.id, to) - - for _, includedBy := range p.includedBy { - includedBy.storeIncluded(c, from, to) - } -} - func (p *sequenceParser) parse(t Trace, c *context) { if p.commit&Documentation != 0 { c.fail(c.offset) @@ -169,7 +182,7 @@ func (p *sequenceParser) parse(t Trace, c *context) { } for _, includedBy := range p.includedBy { - includedBy.storeIncluded(c, from, to) + c.store.setMatch(from, includedBy, to) } c.store.setMatch(from, p.id, to) diff --git a/syntax.go b/syntax.go index 22cef17..424b6b3 100644 --- a/syntax.go +++ b/syntax.go @@ -143,6 +143,10 @@ func (s *Syntax) Init() error { return ErrRootAlias } + for _, p := range s.registry.definitions { + p.init(s.registry) + } + var err error s.parser, err = s.root.parser(s.registry, &idSet{}) if err != nil {