Skip to content

Commit

Permalink
implement Scanner for Many and Branch (#69)
Browse files Browse the repository at this point in the history
* feat: add MergeScanners function to merge multiple same-src Scanners

* feat: implement Scanner function for Many and Branch

* chore: bold filename text
  • Loading branch information
ChloePlanet authored Apr 27, 2020
1 parent 63ecbfa commit 4cddf83
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 4 deletions.
42 changes: 40 additions & 2 deletions ast/node_string.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,20 @@ func (c One) Scanner() parser.Scanner {
}

func (c Many) Scanner() parser.Scanner {
panic("Scanner() not valid for Many")
childrenScanners := make([]parser.Scanner, 0, len(c))
for _, n := range c {
childrenScanners = append(childrenScanners, n.Scanner())
}

if len(childrenScanners) > 0 {
manyScanner, err := parser.MergeScanners(childrenScanners...)
if err != nil {
panic(err)
}
return manyScanner
}

return parser.Scanner{}
}

func (c Extra) Scanner() parser.Scanner {
Expand All @@ -130,5 +143,30 @@ func (n Branch) Scanner() parser.Scanner {
if len(n) == 1 && n.oneChild() != nil {
return n.oneChild().Scanner()
}
panic("Scanner() not valid for Branch")

scanners := make([]parser.Scanner, 0)
for childrenName, ch := range n {
if !strings.HasPrefix(childrenName, "@") {
switch c := ch.(type) {
case One:
if s := c.Node.Scanner(); !s.IsNil() {
scanners = append(scanners, s)
}
case Many:
if s := c.Scanner(); !s.IsNil() {
scanners = append(scanners, s)
}
}
}
}

if len(scanners) > 0 {
branchScanner, err := parser.MergeScanners(scanners...)
if err != nil {
panic(err)
}
return branchScanner
}

return parser.Scanner{}
}
78 changes: 78 additions & 0 deletions ast/node_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package ast

import (
"testing"

"github.com/arr-ai/wbnf/parser"
"github.com/stretchr/testify/assert"
)

func TestBranchScanner(t *testing.T) {
str := "one\ntwo\nthree\nfour"

assertBranchScanner(t, parser.NewScannerAt(str, 0, 4), Branch{
"foo": One{
Node: Leaf(*parser.NewScannerAt(str, 0, 4)),
},
})
assertBranchScanner(t, parser.NewScannerAt(str, 0, 6), Branch{
"foo": One{
Node: Leaf(*parser.NewScannerAt(str, 0, 4)),
},
"bar": One{
Node: Leaf(*parser.NewScannerAt(str, 5, 1)),
},
})
assertBranchScanner(t, parser.NewScannerAt(str, 0, 11), Branch{
"foo": One{
Node: Leaf(*parser.NewScannerAt(str, 0, 4)),
},
"bar": One{
Node: Leaf(*parser.NewScannerAt(str, 10, 1)),
},
})
assertBranchScanner(t, parser.NewScannerAt(str, 0, 17), Branch{
"foo": Many{
Leaf(*parser.NewScannerAt(str, 0, 4)),
Leaf(*parser.NewScannerAt(str, 7, 10)),
},
})
assertBranchScanner(t, parser.NewScannerAt(str, 0, 17), Branch{
"foo": Many{
Leaf(*parser.NewScannerAt(str, 0, 4)),
Leaf(*parser.NewScannerAt(str, 7, 10)),
},
"bar": Many{
Leaf(*parser.NewScannerAt(str, 0, 4)),
Leaf(*parser.NewScannerAt(str, 7, 1)),
},
})
assertBranchScanner(t, parser.NewScannerAt(str, 0, 17), Branch{
"foo": Many{
Leaf(*parser.NewScannerAt(str, 0, 4)),
Leaf(*parser.NewScannerAt(str, 7, 10)),
},
"bar": One{
Node: Leaf(*parser.NewScannerAt(str, 5, 1)),
},
})

assertBranchScanner(t, &parser.Scanner{}, Branch{})
assertBranchScanner(t, &parser.Scanner{}, Branch{
"foo": Many{},
})
assertBranchScanner(t, &parser.Scanner{}, Branch{
"foo": Many{},
"bar": Many{},
})
assertBranchScanner(t, parser.NewScannerAt(str, 5, 1), Branch{
"foo": Many{},
"bar": One{
Node: Leaf(*parser.NewScannerAt(str, 5, 1)),
},
})
}

func assertBranchScanner(t *testing.T, s *parser.Scanner, b Branch) {
assert.Equal(t, *s, b.Scanner())
}
39 changes: 37 additions & 2 deletions parser/scanner.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package parser

import (
"errors"
"fmt"
"regexp"
"strings"
Expand Down Expand Up @@ -55,6 +56,10 @@ func (s Scanner) String() string {
return s.slice()
}

func (s Scanner) IsNil() bool {
return s.src == nil
}

func (s Scanner) Format(state fmt.State, c rune) {
if c == 'q' {
_, _ = fmt.Fprintf(state, "%q", s.slice())
Expand All @@ -65,7 +70,7 @@ func (s Scanner) Format(state fmt.State, c rune) {

var (
NoLimit = -1
DefaultLimit = 3
DefaultLimit = 1
)

func (s Scanner) Context(limitLines int) string {
Expand All @@ -85,7 +90,7 @@ func (s Scanner) Context(limitLines int) string {
}
}

return fmt.Sprintf("\n%s:%d:%d:\n\n%s\033[1;31m%s\033[0m%s",
return fmt.Sprintf("\n\033[1;37m%s:%d:%d:\033[0m\n%s\033[1;31m%s\033[0m%s",
s.Filename(),
lineno,
colno,
Expand Down Expand Up @@ -118,6 +123,36 @@ func (s Scanner) Skip(i int) *Scanner {
return &Scanner{s.src, s.sliceStart + i, s.sliceLength - i}
}

func MergeScanners(items ...Scanner) (Scanner, error) {
if len(items) == 0 {
return Scanner{}, errors.New("needs at least one scanner")
}
if len(items) == 1 {
return items[0], nil
}

l, r := items[0].sliceStart, items[0].sliceStart+items[0].sliceLength
src := items[0].src

for _, v := range items[1:] {
if v.src != src {
return Scanner{}, fmt.Errorf("scanners' sources are not the same: %s vs %s", src, v.src)
}
if v.sliceStart < l {
l = v.sliceStart
}
if v.sliceStart+v.sliceLength > r {
r = v.sliceStart + v.sliceLength
}
}

return Scanner{
src: src,
sliceStart: l,
sliceLength: r - l,
}, nil
}

// Eat returns a scanner containing the next i bytes and advances s past them.
func (s *Scanner) Eat(i int, eaten *Scanner) *Scanner {
eaten.src = s.src
Expand Down
29 changes: 29 additions & 0 deletions parser/scanner_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package parser

import (
"errors"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -36,3 +37,31 @@ func assertLineColumn(t *testing.T, scanner *Scanner, line, column int) {
assert.Equal(t, line, l)
assert.Equal(t, column, c)
}

func TestScannerMerge(t *testing.T) {
str := "one\ntwo\nthree\nfour"
src := stringSource{origin: str}

assertMergedScanner(t, src, 0, 5, []Scanner{*NewScannerAt(str, 0, 5)})
assertMergedScanner(t, src, 0, len(str), []Scanner{*NewScanner(str), *NewScanner(str)})
assertMergedScanner(t, src, 0, len(str), []Scanner{*NewScanner(str), *NewScannerAt(str, 0, 1)})
assertMergedScanner(t, src, 0, 11, []Scanner{*NewScannerAt(str, 0, 1), *NewScannerAt(str, 5, 6)})
assertMergedScanner(t, src, 0, 11, []Scanner{*NewScannerAt(str, 0, 1), *NewScannerAt(str, 3, 1), *NewScannerAt(str, 5, 6)})
assertMergedScanner(t, src, 0, 6, []Scanner{*NewScannerAt(str, 0, 1), *NewScannerAt(str, 0, 4), *NewScannerAt(str, 0, 6)})

assertMergedScannerErr(t, errors.New("needs at least one scanner"), []Scanner{})
assertMergedScannerErr(t, errors.New("scanners' sources are not the same: {one\ntwo\nthree\nfour } vs {another src }"), []Scanner{*NewScanner(str), *NewScanner("another src")})
}

func assertMergedScanner(t *testing.T, src source, offset, length int, items []Scanner) {
s, err := MergeScanners(items...)
assert.NoError(t, err)
assert.Equal(t, src, s.src)
assert.Equal(t, offset, s.sliceStart)
assert.Equal(t, length, s.sliceLength)
}

func assertMergedScannerErr(t *testing.T, err error, items []Scanner) {
_, e := MergeScanners(items...)
assert.Equal(t, err, e)
}

0 comments on commit 4cddf83

Please sign in to comment.