From 73585dd07db65998c53c8668350e74af5b253b57 Mon Sep 17 00:00:00 2001 From: Arpad Ryszka Date: Sat, 29 Jul 2017 18:40:22 +0200 Subject: [PATCH] separate build phase for boot --- boot_test.go | 92 ++++++++++++++++++++---------------------- char.go | 6 +-- choice.go | 80 ++++++++++++++++++++++++++++++++----- context.go | 3 -- parse.go | 20 +++++++--- sequence.go | 111 +++++++++++++++++++++++++++++++++++++++++---------- store.go | 44 +++++++++++++++++--- syntax.go | 8 +++- 8 files changed, 268 insertions(+), 96 deletions(-) diff --git a/boot_test.go b/boot_test.go index 7bde6c6..ff5a192 100644 --- a/boot_test.go +++ b/boot_test.go @@ -13,7 +13,7 @@ func TestBoot(t *testing.T) { return } - f, err := os.Open("mml.parser") + f, err := os.Open("syntax.parser") if err != nil { t.Error(err) return @@ -23,6 +23,7 @@ func TestBoot(t *testing.T) { var d time.Duration const n = 120 + var n0 *Node for i := 0; i < n; i++ { if _, err := f.Seek(0, 0); err != nil { t.Error(err) @@ -30,10 +31,10 @@ func TestBoot(t *testing.T) { } start := time.Now() - _, err = b.Parse(f) + n0, err = b.Parse(f) d += time.Now().Sub(start) - if err != ErrNotImplemented { + if err != nil { t.Error(err) return } @@ -41,57 +42,52 @@ func TestBoot(t *testing.T) { t.Log("duration:", d/n) - // if err != nil { - // t.Error(err) - // return - // } + s0 := NewSyntax() + if err := define(s0, n0); err != nil { + t.Error(err) + return + } - // s0 := NewSyntax() - // if err := define(s0, n0); err != nil { - // t.Error(err) - // return - // } + _, err = f.Seek(0, 0) + if err != nil { + t.Error(err) + return + } - // _, err = f.Seek(0, 0) - // if err != nil { - // t.Error(err) - // return - // } + err = s0.Init() + if err != nil { + t.Error(err) + return + } - // err = s0.Init() - // if err != nil { - // t.Error(err) - // return - // } + n1, err := s0.Parse(f) + if err != nil { + t.Error(err) + return + } - // n1, err := s0.Parse(f) - // if err != nil { - // t.Error(err) - // return - // } + checkNode(t, n1, n0) + if t.Failed() { + return + } - // checkNode(t, n1, n0) - // if t.Failed() { - // return - // } + s1 := NewSyntax() + if err := define(s1, n1); err != nil { + t.Error(err) + return + } - // s1 := NewSyntax() - // if err := define(s1, n1); err != nil { - // t.Error(err) - // return - // } + _, err = f.Seek(0, 0) + if err != nil { + t.Error(err) + return + } - // _, err = f.Seek(0, 0) - // if err != nil { - // t.Error(err) - // return - // } + n2, err := s1.Parse(f) + if err != nil { + t.Error(err) + return + } - // n2, err := s1.Parse(f) - // if err != nil { - // t.Error(err) - // return - // } - - // checkNode(t, n2, n1) + checkNode(t, n2, n1) } diff --git a/char.go b/char.go index 4f588dd..54e562c 100644 --- a/char.go +++ b/char.go @@ -23,9 +23,9 @@ func newChar( } } -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) 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) commitType() CommitType { return Alias } func (p *charParser) init(r *registry) error { return nil } diff --git a/choice.go b/choice.go index 4377cf0..07e4f88 100644 --- a/choice.go +++ b/choice.go @@ -6,6 +6,7 @@ type choiceDefinition struct { commit CommitType elements []string includedBy []int + cbuilder *choiceBuilder } type choiceParser struct { @@ -17,9 +18,10 @@ type choiceParser struct { } type choiceBuilder struct { - name string - id int - commit CommitType + name string + id int + commit CommitType + elements []builder } func newChoice(name string, ct CommitType, elements []string) *choiceDefinition { @@ -30,12 +32,29 @@ func newChoice(name string, ct CommitType, elements []string) *choiceDefinition } } -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) 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) commitType() CommitType { return d.commit } func (d *choiceDefinition) init(r *registry) error { + if d.cbuilder == nil { + d.cbuilder = &choiceBuilder{ + name: d.name, + id: d.id, + commit: d.commit, + } + } + + for _, e := range d.elements { + def, ok := r.definition(e) + if !ok { + return parserNotFound(e) + } + + d.cbuilder.elements = append(d.cbuilder.elements, def.builder()) + } + parsers := &idSet{} parsers.set(d.id) return setItemsIncludedBy(r, d.elements, d.id, parsers) @@ -98,7 +117,15 @@ func (d *choiceDefinition) parser(r *registry, parsers *idSet) (parser, error) { } func (d *choiceDefinition) builder() builder { - return &choiceBuilder{} + if d.cbuilder == nil { + d.cbuilder = &choiceBuilder{ + name: d.name, + id: d.id, + commit: d.commit, + } + } + + return d.cbuilder } func (p *choiceParser) nodeName() string { return p.name } @@ -170,6 +197,41 @@ func (p *choiceParser) parse(t Trace, c *context) { func (b *choiceBuilder) nodeName() string { return b.name } func (b *choiceBuilder) nodeID() int { return b.id } -func (b *choiceBuilder) build(*context) ([]*Node, bool) { - return nil, false +func (b *choiceBuilder) build(c *context) ([]*Node, bool) { + to, ok := c.store.takeMatch(c.offset, b.id) + if !ok { + return nil, false + } + + var element builder + for _, e := range b.elements { + elementTo, match, _ := c.store.getMatch(c.offset, e.nodeID()) + if match && elementTo == to { + element = e + break + } + } + + if element == nil { + panic("damaged parse result") + } + + from := c.offset + + n, ok := element.build(c) + if !ok { + panic("damaged parse result") + } + + if b.commit&Alias != 0 { + return n, true + } + + return []*Node{{ + Name: b.name, + From: from, + To: to, + Nodes: n, + tokens: c.tokens, + }}, true } diff --git a/context.go b/context.go index 10bdb3f..b9ea63e 100644 --- a/context.go +++ b/context.go @@ -145,8 +145,5 @@ func (c *context) finalize(root parser) error { } } - return ErrNotImplemented - - c.node.commit(c.tokens) return nil } diff --git a/parse.go b/parse.go index e646a68..958b505 100644 --- a/parse.go +++ b/parse.go @@ -73,19 +73,29 @@ func sequenceItemNames(items []SequenceItem) []string { return names } -func parse(t Trace, p parser, c *context) (*Node, error) { +func parse(t Trace, p parser, c *context) error { p.parse(t, c) if c.readErr != nil { - return nil, c.readErr + return c.readErr } if !c.match { - return nil, ErrInvalidInput + return ErrInvalidInput } if err := c.finalize(p); err != nil { - return nil, err + return err } - return c.node, nil + return nil +} + +func build(b builder, c *context) *Node { + c.offset = 0 + n, ok := b.build(c) + if !ok || len(n) != 1 { + panic("damaged parse result") + } + + return n[0] } diff --git a/sequence.go b/sequence.go index 4daf082..4473644 100644 --- a/sequence.go +++ b/sequence.go @@ -6,6 +6,8 @@ type sequenceDefinition struct { commit CommitType items []SequenceItem includedBy []int + ranges [][]int + sbuilder *sequenceBuilder } type sequenceParser struct { @@ -18,9 +20,11 @@ type sequenceParser struct { } type sequenceBuilder struct { - name string - id int + name string + id int commit CommitType + items []builder + ranges [][]int } func newSequence(name string, ct CommitType, items []SequenceItem) *sequenceDefinition { @@ -31,9 +35,9 @@ func newSequence(name string, ct CommitType, items []SequenceItem) *sequenceDefi } } -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) 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) commitType() CommitType { return d.commit } func (d *sequenceDefinition) includeItems() bool { @@ -41,14 +45,33 @@ func (d *sequenceDefinition) includeItems() bool { } func (d *sequenceDefinition) init(r *registry) error { + if d.sbuilder == nil { + d.sbuilder = &sequenceBuilder{ + name: d.name, + id: d.id, + commit: d.commit, + } + } + 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 } + + d.ranges = append(d.ranges, []int{item.Min, item.Max}) + + def, ok := r.definition(item.Name) + if !ok { + return parserNotFound(item.Name) + } + + d.sbuilder.items = append(d.sbuilder.items, def.builder()) } + d.sbuilder.ranges = d.ranges + if !d.includeItems() { return nil } @@ -92,24 +115,13 @@ func (d *sequenceDefinition) parser(r *registry, parsers *idSet) (parser, error) r.setParser(sp) - var ( - items []parser - ranges [][]int - ) - + var items []parser parsers.set(d.id) defer parsers.unset(d.id) 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 - } - pi, ok := r.parser(item.Name) if ok { items = append(items, pi) - ranges = append(ranges, []int{item.Min, item.Max}) continue } @@ -124,16 +136,23 @@ func (d *sequenceDefinition) parser(r *registry, parsers *idSet) (parser, error) } items = append(items, pi) - ranges = append(ranges, []int{item.Min, item.Max}) } sp.items = items - sp.ranges = ranges + sp.ranges = d.ranges return sp, nil } func (d *sequenceDefinition) builder() builder { - return &sequenceBuilder{} + if d.sbuilder == nil { + d.sbuilder = &sequenceBuilder{ + name: d.name, + id: d.id, + commit: d.commit, + } + } + + return d.sbuilder } func (p *sequenceParser) nodeName() string { return p.name } @@ -162,6 +181,7 @@ func (p *sequenceParser) parse(t Trace, c *context) { to := c.offset for itemIndex < len(p.items) { + // TODO: is it ok to parse before max range check? what if max=0 p.items[itemIndex].parse(t, c) if !c.match { if currentCount < p.ranges[itemIndex][0] { @@ -201,6 +221,53 @@ func (p *sequenceParser) parse(t Trace, c *context) { func (b *sequenceBuilder) nodeName() string { return b.name } func (b *sequenceBuilder) nodeID() int { return b.id } -func (b *sequenceBuilder) build(*context) ([]*Node, bool) { - return nil, false +func (b *sequenceBuilder) build(c *context) ([]*Node, bool) { + to, ok := c.store.takeMatch(c.offset, b.id) + if !ok { + return nil, false + } + + from := c.offset + var ( + itemIndex int + currentCount int + nodes []*Node + ) + + for itemIndex < len(b.items) { + itemFrom := c.offset + n, ok := b.items[itemIndex].build(c) + if !ok { + if currentCount < b.ranges[itemIndex][0] { + panic("damaged parse result") + } + + itemIndex++ + currentCount = 0 + continue + } + + parsed := c.offset > itemFrom + if parsed { + nodes = append(nodes, n...) + currentCount++ + } + + if !parsed || b.ranges[itemIndex][1] >= 0 && currentCount == b.ranges[itemIndex][1] { + itemIndex++ + currentCount = 0 + } + } + + if b.commit&Alias != 0 { + return nodes, true + } + + return []*Node{{ + Name: b.name, + From: from, + To: to, + Nodes: nodes, + tokens: c.tokens, + }}, true } diff --git a/store.go b/store.go index dafeb67..9d53797 100644 --- a/store.go +++ b/store.go @@ -23,8 +23,8 @@ func (s *store) getMatch(offset, id int) (int, bool, bool) { } var ( - found bool - length int + found bool + to int ) for i := 0; i < len(s.match[offset]); i += 2 { @@ -33,12 +33,46 @@ func (s *store) getMatch(offset, id int) (int, bool, bool) { } found = true - if s.match[offset][i+1] > length { - length = s.match[offset][i+1] + if s.match[offset][i+1] > to { + to = s.match[offset][i+1] } } - return length, found, found + return to, found, found +} + +func (s *store) takeMatch(offset, id int) (int, bool) { + if s.hasNoMatch(offset, id) { + return 0, false + } + + if len(s.match) <= offset { + return 0, false + } + + var ( + found bool + to int + index int + ) + + for i := 0; i < len(s.match[offset]); i += 2 { + if s.match[offset][i] != id { + continue + } + + found = true + if s.match[offset][i+1] > to { + to = s.match[offset][i+1] + index = i + } + } + + if found { + s.match[offset][index] = -1 + } + + return to, found } func (s *store) ensureOffset(offset int) { diff --git a/syntax.go b/syntax.go index 2e2d6d8..d6275a6 100644 --- a/syntax.go +++ b/syntax.go @@ -29,6 +29,7 @@ type Syntax struct { explicitRoot bool root definition parser parser + builder builder } var ( @@ -154,6 +155,7 @@ func (s *Syntax) Init() error { return err } + s.builder = s.root.builder() s.initialized = true return nil } @@ -174,5 +176,9 @@ func (s *Syntax) Parse(r io.Reader) (*Node, error) { } c := newContext(bufio.NewReader(r)) - return parse(s.trace, s.parser, c) + if err := parse(s.trace, s.parser, c); err != nil { + return nil, err + } + + return build(s.builder, c), nil }