treerack/define.go

293 lines
5.1 KiB
Go
Raw Normal View History

2017-06-25 17:51:08 +02:00
package parse
import "strconv"
func runesContain(rs []rune, r rune) bool {
for _, ri := range rs {
if ri == r {
return true
}
}
return false
}
func unescapeChar(c rune) rune {
switch c {
case 'n':
return '\n'
case 't':
return '\t'
case 'b':
return '\b'
case 'f':
return '\f'
case 'r':
return '\r'
case 'v':
return '\v'
default:
return c
}
}
func unescape(escape rune, banned []rune, chars []rune) ([]rune, error) {
var (
unescaped []rune
escaped bool
)
for _, ci := range chars {
if escaped {
unescaped = append(unescaped, unescapeChar(ci))
escaped = false
continue
}
switch {
case ci == escape:
escaped = true
case runesContain(banned, ci):
return nil, ErrInvalidCharacter
default:
unescaped = append(unescaped, ci)
}
}
if escaped {
return nil, ErrInvalidCharacter
}
return unescaped, nil
}
func dropComments(n *Node) *Node {
ncc := *n
nc := &ncc
nc.Nodes = nil
for _, ni := range n.Nodes {
if ni.Name == "comment" {
continue
}
nc.Nodes = append(nc.Nodes, dropComments(ni))
}
return nc
}
func flagsToCommitType(n []*Node) CommitType {
var ct CommitType
for _, ni := range n {
switch ni.Name {
case "alias":
ct |= Alias
case "doc":
ct |= Documentation
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-06-25 23:38:32 +02:00
func defineMember(s *Syntax, defaultName string, n *Node) (string, error) {
switch n.Name {
case "symbol":
return n.Text(), nil
default:
return defaultName, defineExpression(s, defaultName, Alias, n)
}
}
2017-06-25 17:51:08 +02:00
func defineMembers(s *Syntax, name string, n ...*Node) ([]string, error) {
var refs []string
for i, ni := range n {
nmi := childName(name, i)
2017-06-25 23:38:32 +02:00
ref, err := defineMember(s, nmi, ni)
if err != nil {
return nil, err
2017-06-25 17:51:08 +02:00
}
2017-06-25 23:38:32 +02:00
refs = append(refs, ref)
2017-06-25 17:51:08 +02:00
}
return refs, nil
}
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])})
}
}
return s.Class(name, ct, not, chars, ranges)
}
func defineCharSequence(s *Syntax, name string, ct CommitType, charNodes []*Node) error {
var chars []rune
for _, ci := range charNodes {
chars = append(chars, nodeChar(ci))
}
return s.CharSequence(name, ct, chars)
}
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
}
default:
2017-06-25 23:38:32 +02:00
err = ErrInvalidSyntax
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 {
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-06-25 23:38:32 +02:00
var items []SequenceItem
for i, ni := range n {
if ni.Name != "item" || len(ni.Nodes) == 0 {
return ErrInvalidSyntax
}
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)
item.Name, err = defineMember(s, defaultName, ni.Nodes[0])
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-06-25 23:38:32 +02: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 {
refs, err := defineMembers(s, name, n...)
if err != nil {
return err
}
return s.Choice(name, ct, refs...)
}
func defineExpression(s *Syntax, name string, ct CommitType, expression *Node) error {
var err error
switch expression.Name {
case "any-char":
err = s.AnyChar(name, ct)
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 {
if n.Name != "syntax" {
return ErrInvalidSyntax
}
n = dropComments(n)
for _, ni := range n.Nodes {
if err := defineDefinition(s, ni); err != nil {
return err
}
}
return nil
}