generic cleanup

This commit is contained in:
Arpad Ryszka 2017-06-26 01:21:46 +02:00
parent 2d8d1ae4ef
commit 5d38caf222
16 changed files with 699 additions and 733 deletions

View File

@ -197,7 +197,7 @@ func initBoot(definitions [][]string) (*Syntax, error) {
}
func bootSyntax() (*Syntax, error) {
b, err := initBoot(bootDefinitions)
b, err := initBoot(bootSyntaxDefs)
if err != nil {
return nil, err
}

View File

@ -6,7 +6,7 @@ import (
)
func TestBoot(t *testing.T) {
b, err := initBoot(bootDefinitions)
b, err := initBoot(bootSyntaxDefs)
if err != nil {
t.Error(err)
return

View File

@ -1,6 +1,6 @@
package parse
var bootDefinitions = [][]string{{
var bootSyntaxDefs = [][]string{{
"chars", "space", "alias", " ",
}, {
"chars", "tab", "alias", "\\t",

5
buzz.txt Normal file
View File

@ -0,0 +1,5 @@
generator, in-process init or command line
syntax from file or in-memory
simple syntax with recursion
no lexer required
utf8, 8bit or custom tokens

View File

@ -1,5 +1,7 @@
package parse
// TODO: rename to store
type cacheItem struct {
name string
node *Node
@ -39,11 +41,11 @@ func (c *cache) get(offset int, name string) (*Node, bool, bool) {
return nil, false, false
}
func (c *cache) setOne(offset int, name string, n *Node) {
}
func (c *cache) set(offset int, name string, n *Node) {
if len(c.tokens) <= offset {
var tc *tokenCache
if len(c.tokens) > offset {
tc = c.tokens[offset]
} else {
if cap(c.tokens) > offset {
c.tokens = c.tokens[:offset+1]
} else {
@ -52,10 +54,7 @@ func (c *cache) set(offset int, name string, n *Node) {
c.tokens = append(c.tokens, nil)
}
}
}
tc := c.tokens[offset]
if tc == nil {
tc = &tokenCache{}
c.tokens[offset] = tc
}

View File

@ -89,7 +89,7 @@ func (p *charParser) parse(t Trace, c *context) {
if tok, ok := c.token(); ok && p.match(tok) {
// t.Out1("success", string(tok))
n := newNode(p.name, p.commit, c.offset, c.offset+1)
n := newNode(p.name, c.offset, c.offset+1, p.commit)
c.cache.set(c.offset, p.name, n)
for _, includedBy := range p.includedBy {
includedBy.cacheIncluded(c, n)

View File

@ -79,13 +79,13 @@ func (p *choiceParser) setIncludedBy(includedBy parser, path []string) {
}
func (p *choiceParser) cacheIncluded(c *context, n *Node) {
if !c.excluded(n.from, p.name) {
if !c.excluded(n.From, p.name) {
return
}
nc := newNode(p.name, p.commit, n.from, n.to)
nc := newNode(p.name, n.From, n.To, p.commit)
nc.append(n)
c.cache.set(nc.from, p.name, nc)
c.cache.set(nc.From, p.name, nc)
for _, includedBy := range p.includedBy {
includedBy.cacheIncluded(c, nc)
@ -116,7 +116,7 @@ func (p *choiceParser) parse(t Trace, c *context) {
c.exclude(c.offset, p.name)
defer c.include(c.offset, p.name)
node := newNode(p.name, p.commit, c.offset, c.offset)
node := newNode(p.name, c.offset, c.offset, p.commit)
var match bool
for {
@ -126,7 +126,7 @@ func (p *choiceParser) parse(t Trace, c *context) {
for len(elements) > 0 {
elements[0].parse(t, c)
elements = elements[1:]
c.offset = node.from
c.offset = node.From
if !c.match || match && c.node.tokenLength() <= node.tokenLength() {
continue
@ -134,10 +134,10 @@ func (p *choiceParser) parse(t Trace, c *context) {
match = true
foundMatch = true
node = newNode(p.name, p.commit, c.offset, c.offset)
node = newNode(p.name, c.offset, c.offset, p.commit)
node.append(c.node)
c.cache.set(node.from, p.name, node)
c.cache.set(node.From, p.name, node)
for _, includedBy := range p.includedBy {
includedBy.cacheIncluded(c, node)
}
@ -155,6 +155,6 @@ func (p *choiceParser) parse(t Trace, c *context) {
}
// t.Out1("fail")
c.cache.set(node.from, p.name, nil)
c.fail(node.from)
c.cache.set(node.From, p.name, nil)
c.fail(node.From)
}

View File

@ -73,6 +73,10 @@ func (c *context) excluded(offset int, name string) bool {
}
func (c *context) exclude(offset int, name string) {
if c.excluded(offset, name) {
return
}
if len(c.isExcluded) <= offset {
c.isExcluded = append(c.isExcluded, nil)
if cap(c.isExcluded) > offset {
@ -96,6 +100,7 @@ func (c *context) include(offset int, name string) {
for i := len(c.isExcluded[offset]) - 1; i >= 0; i-- {
if c.isExcluded[offset][i] == name {
c.isExcluded[offset] = append(c.isExcluded[offset][:i], c.isExcluded[offset][i+1:]...)
return
}
}
}
@ -117,7 +122,7 @@ func (c *context) fromCache(name string) (bool, bool) {
func (c *context) success(n *Node) {
c.node = n
c.offset = n.to
c.offset = n.To
c.match = true
}
@ -127,7 +132,7 @@ func (c *context) fail(offset int) {
}
func (c *context) finalize() error {
if c.node.to < c.readOffset {
if c.node.To < c.readOffset {
return ErrUnexpectedCharacter
}
@ -142,11 +147,6 @@ func (c *context) finalize() error {
}
}
c.node.commit()
if c.node.commitType&Alias != 0 {
return nil
}
c.node.applyTokens(c.tokens)
c.node.commit(c.tokens)
return nil
}

View File

@ -13,13 +13,13 @@ func TestKeyVal(t *testing.T) {
text: "a key",
nodes: []*Node{{
Name: "key-val",
to: 5,
To: 5,
Nodes: []*Node{{
Name: "key",
to: 5,
To: 5,
Nodes: []*Node{{
Name: "symbol",
to: 5,
To: 5,
}},
}},
}},
@ -28,16 +28,16 @@ func TestKeyVal(t *testing.T) {
text: " a key",
nodes: []*Node{{
Name: "key-val",
from: 1,
to: 6,
From: 1,
To: 6,
Nodes: []*Node{{
Name: "key",
from: 1,
to: 6,
From: 1,
To: 6,
Nodes: []*Node{{
Name: "symbol",
from: 1,
to: 6,
From: 1,
To: 6,
}},
}},
}},
@ -50,16 +50,16 @@ func TestKeyVal(t *testing.T) {
`,
nodes: []*Node{{
Name: "key-val",
from: 20,
to: 25,
From: 20,
To: 25,
Nodes: []*Node{{
Name: "key",
from: 20,
to: 25,
From: 20,
To: 25,
Nodes: []*Node{{
Name: "symbol",
from: 20,
to: 25,
From: 20,
To: 25,
}},
}},
}},
@ -68,18 +68,18 @@ func TestKeyVal(t *testing.T) {
text: "a key = a value",
nodes: []*Node{{
Name: "key-val",
to: 15,
To: 15,
Nodes: []*Node{{
Name: "key",
to: 5,
To: 5,
Nodes: []*Node{{
Name: "symbol",
to: 5,
To: 5,
}},
}, {
Name: "value",
from: 8,
to: 15,
From: 8,
To: 15,
}},
}},
}, {
@ -90,39 +90,39 @@ func TestKeyVal(t *testing.T) {
`,
nodes: []*Node{{
Name: "key-val",
from: 11,
to: 32,
From: 11,
To: 32,
Nodes: []*Node{{
Name: "key",
from: 11,
to: 16,
From: 11,
To: 16,
Nodes: []*Node{{
Name: "symbol",
from: 11,
to: 16,
From: 11,
To: 16,
}},
}, {
Name: "value",
from: 25,
to: 32,
From: 25,
To: 32,
}},
}, {
Name: "key-val",
from: 61,
to: 88,
From: 61,
To: 88,
Nodes: []*Node{{
Name: "key",
from: 61,
to: 72,
From: 61,
To: 72,
Nodes: []*Node{{
Name: "symbol",
from: 61,
to: 72,
From: 61,
To: 72,
}},
}, {
Name: "value",
from: 75,
to: 88,
From: 75,
To: 88,
}},
}},
}, {
@ -130,11 +130,11 @@ func TestKeyVal(t *testing.T) {
text: "= a value",
nodes: []*Node{{
Name: "key-val",
to: 9,
To: 9,
Nodes: []*Node{{
Name: "value",
from: 2,
to: 9,
From: 2,
To: 9,
}},
}},
}, {
@ -145,25 +145,25 @@ func TestKeyVal(t *testing.T) {
`,
nodes: []*Node{{
Name: "key-val",
from: 4,
to: 34,
From: 4,
To: 34,
Nodes: []*Node{{
Name: "comment",
from: 4,
to: 15,
From: 4,
To: 15,
}, {
Name: "key",
from: 19,
to: 24,
From: 19,
To: 24,
Nodes: []*Node{{
Name: "symbol",
from: 19,
to: 24,
From: 19,
To: 24,
}},
}, {
Name: "value",
from: 27,
to: 34,
From: 27,
To: 34,
}},
}},
}, {
@ -171,32 +171,32 @@ func TestKeyVal(t *testing.T) {
text: "a key . with.multiple.symbols=a value",
nodes: []*Node{{
Name: "key-val",
to: 37,
To: 37,
Nodes: []*Node{{
Name: "key",
from: 0,
to: 29,
From: 0,
To: 29,
Nodes: []*Node{{
Name: "symbol",
from: 0,
to: 5,
From: 0,
To: 5,
}, {
Name: "symbol",
from: 8,
to: 12,
From: 8,
To: 12,
}, {
Name: "symbol",
from: 13,
to: 21,
From: 13,
To: 21,
}, {
Name: "symbol",
from: 22,
to: 29,
From: 22,
To: 29,
}},
}, {
Name: "value",
from: 30,
to: 37,
From: 30,
To: 37,
}},
}},
}, {
@ -207,20 +207,20 @@ func TestKeyVal(t *testing.T) {
`,
nodes: []*Node{{
Name: "group-key",
from: 4,
to: 38,
From: 4,
To: 38,
Nodes: []*Node{{
Name: "comment",
from: 4,
to: 15,
From: 4,
To: 15,
}, {
Name: "symbol",
from: 20,
to: 31,
From: 20,
To: 31,
}, {
Name: "symbol",
from: 32,
to: 37,
From: 32,
To: 37,
}},
}},
}, {

File diff suppressed because it is too large Load Diff

50
node.go
View File

@ -5,67 +5,43 @@ import "fmt"
type Node struct {
Name string
Nodes []*Node
From, To int
commitType CommitType
from, to int
tokens []rune
}
func newNode(name string, ct CommitType, from, to int) *Node {
func newNode(name string, from, to int, ct CommitType) *Node {
return &Node{
Name: name,
From: from,
To: to,
commitType: ct,
from: from,
to: to,
}
}
func (n *Node) tokenLength() int {
return n.to - n.from
return n.To - n.From
}
func (n *Node) nodeLength() int {
return len(n.Nodes)
}
func findNode(in, n *Node) {
if n == in {
panic(fmt.Errorf("found self in %s", in.Name))
}
for _, ni := range n.Nodes {
findNode(in, ni)
}
}
func (n *Node) append(p *Node) {
findNode(n, p)
n.Nodes = append(n.Nodes, p)
// TODO: check rather if n.from <= p.from??? or panic if less? or check rather node length and commit
// happens in the end anyway?
if n.from == 0 && n.to == 0 {
n.from = p.from
if n.tokenLength() == 0 {
n.From = p.From
}
n.to = p.to
n.To = p.To
}
func (n *Node) clear() {
n.from = 0
n.to = 0
n.Nodes = nil
}
func (n *Node) applyTokens(t []rune) {
func (n *Node) commit(t []rune) {
n.tokens = t
for _, ni := range n.Nodes {
ni.applyTokens(t)
}
}
func (n *Node) commit() {
var nodes []*Node
for _, ni := range n.Nodes {
ni.commit()
ni.commit(t)
if ni.commitType&Alias != 0 {
nodes = append(nodes, ni.Nodes...)
} else {
@ -77,13 +53,13 @@ func (n *Node) commit() {
}
func (n *Node) String() string {
if n.from >= len(n.tokens) || n.to > len(n.tokens) {
if n.From >= len(n.tokens) || n.To > len(n.tokens) {
return n.Name + ":incomplete"
}
return fmt.Sprintf("%s:%d:%d:%s", n.Name, n.from, n.to, n.Text())
return fmt.Sprintf("%s:%d:%d:%s", n.Name, n.From, n.To, n.Text())
}
func (n *Node) Text() string {
return string(n.tokens[n.from:n.to])
return string(n.tokens[n.From:n.To])
}

7
notes.txt Normal file
View File

@ -0,0 +1,7 @@
cleanup
error reporting
custom tokens
indentation
benchmarking
code generation go
code generation js

View File

@ -33,23 +33,6 @@ func stringsContain(ss []string, s string) bool {
return false
}
func copyIncludes(to, from map[string]CommitType) {
if from == nil {
return
}
for name, ct := range from {
to[name] = ct
}
}
func mergeIncludes(left, right map[string]CommitType) map[string]CommitType {
m := make(map[string]CommitType)
copyIncludes(m, left)
copyIncludes(m, right)
return m
}
func parse(t Trace, p parser, c *context) (*Node, error) {
p.parse(t, c)
if c.readErr != nil {

View File

@ -28,7 +28,7 @@ func testSyntaxReader(r io.Reader, traceLevel int) (*Syntax, error) {
return nil, err
}
var trace Trace = NopTrace{}
var trace Trace
if traceLevel >= 0 {
trace = NewTrace(traceLevel)
}
@ -45,10 +45,6 @@ func testSyntaxReader(r io.Reader, traceLevel int) (*Syntax, error) {
return s, nil
}
func testSyntaxString(s string, traceLevel int) (*Syntax, error) {
return testSyntaxReader(bytes.NewBufferString(s), traceLevel)
}
func testSyntax(file string, traceLevel int) (*Syntax, error) {
f, err := os.Open(file)
if err != nil {
@ -90,13 +86,13 @@ func checkNodePosition(t *testing.T, left, right *Node, position bool) {
return
}
if position && left.from != right.from {
t.Error("from doesn't match", left.Name, left.from, right.from)
if position && left.From != right.From {
t.Error("from doesn't match", left.Name, left.From, right.From)
return
}
if position && left.to != right.to {
t.Error("to doesn't match", left.Name, left.to, right.to)
if position && left.To != right.To {
t.Error("to doesn't match", left.Name, left.To, right.To)
return
}
@ -177,8 +173,7 @@ func testReaderTrace(t *testing.T, r io.Reader, rootName string, traceLevel int,
} else {
cn(t, n, &Node{
Name: rootName,
from: 0,
to: len(ti.text),
To: len(ti.text),
Nodes: ti.nodes,
})
}
@ -294,7 +289,7 @@ func TestRecursion(t *testing.T) {
text: "aaa",
node: &Node{
Name: "A",
to: 3,
To: 3,
},
}},
)
@ -307,7 +302,7 @@ func TestRecursion(t *testing.T) {
text: "aaa",
node: &Node{
Name: "A",
to: 3,
To: 3,
},
}},
)
@ -320,7 +315,7 @@ func TestRecursion(t *testing.T) {
text: "aaa",
node: &Node{
Name: "A",
to: 3,
To: 3,
},
}},
)
@ -333,7 +328,7 @@ func TestRecursion(t *testing.T) {
text: "aaa",
node: &Node{
Name: "A",
to: 3,
To: 3,
},
}},
)
@ -348,14 +343,14 @@ func TestSequence(t *testing.T) {
text: "abb",
node: &Node{
Name: "AB",
to: 3,
To: 3,
},
}, {
msg: "sequence with optional items, none",
text: "bb",
node: &Node{
Name: "AB",
to: 2,
To: 2,
},
}},
)
@ -388,7 +383,7 @@ func TestSequence(t *testing.T) {
text: "aaa",
node: &Node{
Name: "A",
to: 3,
To: 3,
},
}},
)
@ -403,7 +398,7 @@ func TestQuantifiers(t *testing.T) {
text: "aba",
node: &Node{
Name: "A",
to: 3,
To: 3,
},
}, {
msg: "zero, fail",
@ -424,7 +419,7 @@ func TestQuantifiers(t *testing.T) {
text: "aba",
node: &Node{
Name: "A",
to: 3,
To: 3,
},
}, {
msg: "one, too much",
@ -445,7 +440,7 @@ func TestQuantifiers(t *testing.T) {
text: "abbba",
node: &Node{
Name: "A",
to: 5,
To: 5,
},
}, {
msg: "three, too much",
@ -462,14 +457,14 @@ func TestQuantifiers(t *testing.T) {
text: "aa",
node: &Node{
Name: "A",
to: 2,
To: 2,
},
}, {
msg: "zero or one explicit",
text: "aba",
node: &Node{
Name: "A",
to: 3,
To: 3,
},
}, {
msg: "zero or one explicit, too much",
@ -486,14 +481,14 @@ func TestQuantifiers(t *testing.T) {
text: "aa",
node: &Node{
Name: "A",
to: 2,
To: 2,
},
}, {
msg: "zero or one explicit, omit zero",
text: "aba",
node: &Node{
Name: "A",
to: 3,
To: 3,
},
}, {
msg: "zero or one explicit, omit zero, too much",
@ -510,14 +505,14 @@ func TestQuantifiers(t *testing.T) {
text: "aa",
node: &Node{
Name: "A",
to: 2,
To: 2,
},
}, {
msg: "zero or one explicit, shortcut",
text: "aba",
node: &Node{
Name: "A",
to: 3,
To: 3,
},
}, {
msg: "zero or one explicit, shortcut, too much",
@ -534,21 +529,21 @@ func TestQuantifiers(t *testing.T) {
text: "aa",
node: &Node{
Name: "A",
to: 2,
To: 2,
},
}, {
msg: "zero or three",
text: "abba",
node: &Node{
Name: "A",
to: 4,
To: 4,
},
}, {
msg: "zero or three",
text: "abbba",
node: &Node{
Name: "A",
to: 5,
To: 5,
},
}, {
msg: "zero or three, too much",
@ -565,21 +560,21 @@ func TestQuantifiers(t *testing.T) {
text: "aa",
node: &Node{
Name: "A",
to: 2,
To: 2,
},
}, {
msg: "zero or three, omit zero",
text: "abba",
node: &Node{
Name: "A",
to: 4,
To: 4,
},
}, {
msg: "zero or three, omit zero",
text: "abbba",
node: &Node{
Name: "A",
to: 5,
To: 5,
},
}, {
msg: "zero or three, omit zero, too much",
@ -600,14 +595,14 @@ func TestQuantifiers(t *testing.T) {
text: "abba",
node: &Node{
Name: "A",
to: 4,
To: 4,
},
}, {
msg: "one or three",
text: "abbba",
node: &Node{
Name: "A",
to: 5,
To: 5,
},
}, {
msg: "one or three, too much",
@ -628,14 +623,14 @@ func TestQuantifiers(t *testing.T) {
text: "abbbba",
node: &Node{
Name: "A",
to: 6,
To: 6,
},
}, {
msg: "three or five",
text: "abbbbba",
node: &Node{
Name: "A",
to: 7,
To: 7,
},
}, {
msg: "three or five, too much",
@ -652,14 +647,14 @@ func TestQuantifiers(t *testing.T) {
text: "aa",
node: &Node{
Name: "A",
to: 2,
To: 2,
},
}, {
msg: "zero or more, explicit",
text: "abba",
node: &Node{
Name: "A",
to: 4,
To: 4,
},
}},
)
@ -672,14 +667,14 @@ func TestQuantifiers(t *testing.T) {
text: "aa",
node: &Node{
Name: "A",
to: 2,
To: 2,
},
}, {
msg: "zero or more, shortcut",
text: "abba",
node: &Node{
Name: "A",
to: 4,
To: 4,
},
}},
)
@ -696,7 +691,7 @@ func TestQuantifiers(t *testing.T) {
text: "abba",
node: &Node{
Name: "A",
to: 4,
To: 4,
},
}},
)
@ -713,7 +708,7 @@ func TestQuantifiers(t *testing.T) {
text: "abba",
node: &Node{
Name: "A",
to: 4,
To: 4,
},
}},
)
@ -730,7 +725,7 @@ func TestQuantifiers(t *testing.T) {
text: "abbbba",
node: &Node{
Name: "A",
to: 6,
To: 6,
},
}},
)

View File

@ -100,13 +100,13 @@ func (p *sequenceParser) setIncludedBy(includedBy parser, path []string) {
}
func (p *sequenceParser) cacheIncluded(c *context, n *Node) {
if !c.excluded(n.from, p.name) {
if !c.excluded(n.From, p.name) {
return
}
nc := newNode(p.name, p.commit, n.from, n.to)
nc := newNode(p.name, n.From, n.To, p.commit)
nc.append(n)
c.cache.set(nc.from, p.name, nc)
c.cache.set(nc.From, p.name, nc)
for _, includedBy := range p.includedBy {
includedBy.cacheIncluded(c, nc)
@ -135,7 +135,7 @@ func (p *sequenceParser) parse(t Trace, c *context) {
items := p.items
ranges := p.ranges
var currentCount int
node := newNode(p.name, p.commit, c.offset, c.offset)
node := newNode(p.name, c.offset, c.offset, p.commit)
for len(items) > 0 {
m, ok := c.fromCache(items[0].nodeName())
@ -149,8 +149,8 @@ func (p *sequenceParser) parse(t Trace, c *context) {
if !m {
if currentCount < ranges[0][0] {
// t.Out1("fail, item failed")
c.cache.set(node.from, p.name, nil)
c.fail(node.from)
c.cache.set(node.From, p.name, nil)
c.fail(node.From)
return
}
@ -174,7 +174,7 @@ func (p *sequenceParser) parse(t Trace, c *context) {
// t.Out1("success, items parsed")
c.cache.set(node.from, p.name, node)
c.cache.set(node.From, p.name, node)
for _, includedBy := range p.includedBy {
includedBy.cacheIncluded(c, node)
}

View File

@ -36,10 +36,11 @@ var (
ErrInitFailed = errors.New("init failed")
ErrNoParsersDefined = errors.New("no parsers defined")
ErrInvalidInput = errors.New("invalid input")
ErrInvalidCharacter = errors.New("invalid character") // two use cases: utf8 and boot
ErrInvalidCharacter = errors.New("invalid character") // TODO: fix two use cases, utf8 and boot
ErrUnexpectedCharacter = errors.New("unexpected character")
ErrInvalidSyntax = errors.New("invalid syntax")
ErrRootAlias = errors.New("root node cannot be an alias")
ErrNotImplemented = errors.New("not implemented")
)
func duplicateDefinition(name string) error {
@ -114,7 +115,7 @@ func (s *Syntax) Read(r io.Reader) error {
return ErrSyntaxInitialized
}
return nil
return ErrNotImplemented
}
func (s *Syntax) Init() error {
@ -150,7 +151,7 @@ func (s *Syntax) Generate(w io.Writer) error {
return err
}
return nil
return ErrNotImplemented
}
func (s *Syntax) Parse(r io.Reader) (*Node, error) {