treerack/define.go

210 lines
4.1 KiB
Go
Raw Normal View History

2017-07-15 21:49:08 +02:00
package treerack
2017-06-25 17:51:08 +02:00
import "strconv"
func dropComments(n *Node) *Node {
ncc := *n
nc := &ncc
2017-11-01 00:19:29 +01:00
nc.Nodes = filterNodes(func(n *Node) bool { return n.Name != "comment" }, n.Nodes)
nc.Nodes = mapNodes(dropComments, nc.Nodes)
2017-06-25 17:51:08 +02:00
return nc
}
func flagsToCommitType(n []*Node) CommitType {
var ct CommitType
for _, ni := range n {
switch ni.Name {
case "alias":
ct |= Alias
2017-10-28 22:54:15 +02:00
case "ws":
ct |= Whitespace
case "nows":
ct |= NoWhitespace
2017-06-25 17:51:08 +02:00
case "root":
ct |= Root
}
}
return ct
}
func toRune(c string) rune {
return []rune(c)[0]
}
func nodeChar(n *Node) rune {
s := n.Text()
if s[0] == '\\' {
return unescapeChar(toRune(s[1:]))
}
return toRune(s)
}
2017-10-29 15:12:47 +01:00
func defineMember(s *Syntax, defaultName string, ct CommitType, n *Node) (string, error) {
2017-06-25 23:38:32 +02:00
switch n.Name {
case "symbol":
return n.Text(), nil
default:
2017-10-29 15:12:47 +01:00
return defaultName, defineExpression(s, defaultName, ct, n)
2017-06-25 23:38:32 +02:00
}
}
2017-06-25 17:51:08 +02:00
func defineClass(s *Syntax, name string, ct CommitType, n []*Node) error {
var (
not bool
chars []rune
ranges [][]rune
)
if len(n) > 0 && n[0].Name == "class-not" {
not, n = true, n[1:]
}
for _, c := range n {
switch c.Name {
case "class-char":
chars = append(chars, nodeChar(c))
case "char-range":
ranges = append(ranges, []rune{nodeChar(c.Nodes[0]), nodeChar(c.Nodes[1])})
}
}
2017-10-31 21:53:09 +01:00
return s.class(name, ct, not, chars, ranges)
2017-06-25 17:51:08 +02:00
}
func defineCharSequence(s *Syntax, name string, ct CommitType, charNodes []*Node) error {
var chars []rune
for _, ci := range charNodes {
chars = append(chars, nodeChar(ci))
}
2017-10-31 21:53:09 +01:00
return s.charSequence(name, ct, chars)
2017-06-25 17:51:08 +02:00
}
2017-06-25 23:38:32 +02:00
func getQuantity(n *Node) (min int, max int, err error) {
switch n.Name {
2017-06-25 17:51:08 +02:00
case "count-quantifier":
2017-06-25 23:38:32 +02:00
min, err = strconv.Atoi(n.Nodes[0].Text())
2017-06-25 17:51:08 +02:00
if err != nil {
2017-06-25 23:38:32 +02:00
return
2017-06-25 17:51:08 +02:00
}
max = min
case "range-quantifier":
min = 0
max = -1
2017-06-25 23:38:32 +02:00
for _, rq := range n.Nodes {
2017-06-25 17:51:08 +02:00
switch rq.Name {
case "range-from":
min, err = strconv.Atoi(rq.Text())
if err != nil {
2017-06-25 23:38:32 +02:00
return
2017-06-25 17:51:08 +02:00
}
case "range-to":
max, err = strconv.Atoi(rq.Text())
if err != nil {
2017-06-25 23:38:32 +02:00
return
2017-06-25 17:51:08 +02:00
}
}
}
case "one-or-more":
min, max = 1, -1
case "zero-or-more":
min, max = 0, -1
case "zero-or-one":
min, max = 0, 1
}
2017-06-25 23:38:32 +02:00
return
}
func defineSymbol(s *Syntax, name string, ct CommitType, n *Node) error {
2017-10-31 21:53:09 +01:00
return s.sequence(name, ct, SequenceItem{Name: n.Text()})
2017-06-25 17:51:08 +02:00
}
func defineSequence(s *Syntax, name string, ct CommitType, n ...*Node) error {
2017-10-29 15:12:47 +01:00
nows := ct & NoWhitespace
2017-06-25 23:38:32 +02:00
var items []SequenceItem
for i, ni := range n {
var (
item SequenceItem
err error
)
2017-06-25 17:51:08 +02:00
2017-06-25 23:38:32 +02:00
defaultName := childName(name, i)
2017-10-29 15:12:47 +01:00
item.Name, err = defineMember(s, defaultName, Alias|nows, ni.Nodes[0])
2017-06-25 23:38:32 +02:00
if err != nil {
return err
}
if len(ni.Nodes) == 2 {
item.Min, item.Max, err = getQuantity(ni.Nodes[1])
if err != nil {
return err
}
}
items = append(items, item)
}
2017-06-25 17:51:08 +02:00
2017-10-31 21:53:09 +01:00
return s.sequence(name, ct, items...)
2017-06-25 17:51:08 +02:00
}
func defineChoice(s *Syntax, name string, ct CommitType, n ...*Node) error {
2017-11-01 00:19:29 +01:00
var refs []string
memberCT := ct&NoWhitespace | Alias
for i, ni := range n {
nmi := childName(name, i)
ref, err := defineMember(s, nmi, memberCT, ni)
if err != nil {
return err
}
refs = append(refs, ref)
2017-06-25 17:51:08 +02:00
}
2017-10-31 21:53:09 +01:00
return s.choice(name, ct, refs...)
2017-06-25 17:51:08 +02:00
}
func defineExpression(s *Syntax, name string, ct CommitType, expression *Node) error {
var err error
switch expression.Name {
case "any-char":
2017-10-31 21:53:09 +01:00
err = s.anyChar(name, ct)
2017-06-25 17:51:08 +02:00
case "char-class":
err = defineClass(s, name, ct, expression.Nodes)
case "char-sequence":
err = defineCharSequence(s, name, ct, expression.Nodes)
case "symbol":
2017-06-25 23:38:32 +02:00
err = defineSymbol(s, name, ct, expression)
2017-06-25 17:51:08 +02:00
case "sequence":
err = defineSequence(s, name, ct, expression.Nodes...)
case "choice":
err = defineChoice(s, name, ct, expression.Nodes...)
}
return err
}
func defineDefinition(s *Syntax, n *Node) error {
return defineExpression(
s,
n.Nodes[0].Text(),
flagsToCommitType(n.Nodes[1:len(n.Nodes)-1]),
n.Nodes[len(n.Nodes)-1],
)
}
func define(s *Syntax, n *Node) error {
n = dropComments(n)
for _, ni := range n.Nodes {
if err := defineDefinition(s, ni); err != nil {
return err
}
}
return nil
}