2017-07-15 21:49:08 +02:00
|
|
|
package treerack
|
2017-06-25 17:51:08 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"os"
|
|
|
|
"strconv"
|
2017-06-25 23:38:32 +02:00
|
|
|
"strings"
|
2017-06-25 17:51:08 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
var errInvalidDefinition = errors.New("invalid syntax definition")
|
|
|
|
|
|
|
|
func stringToCommitType(s string) CommitType {
|
|
|
|
switch s {
|
|
|
|
case "alias":
|
|
|
|
return Alias
|
2017-10-29 02:49:54 +02:00
|
|
|
case "ws":
|
|
|
|
return Whitespace
|
|
|
|
case "nows":
|
|
|
|
return NoWhitespace
|
2017-06-25 17:51:08 +02:00
|
|
|
case "doc":
|
|
|
|
return Documentation
|
|
|
|
case "root":
|
|
|
|
return Root
|
|
|
|
default:
|
|
|
|
return None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkBootDefinitionLength(d []string) error {
|
|
|
|
if len(d) < 3 {
|
|
|
|
return errInvalidDefinition
|
|
|
|
}
|
|
|
|
|
|
|
|
switch d[0] {
|
2017-10-29 16:46:17 +01:00
|
|
|
case "chars", "class", "sequence", "choice":
|
2017-06-25 17:51:08 +02:00
|
|
|
if len(d) < 4 {
|
|
|
|
return errInvalidDefinition
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-10-29 16:46:17 +01:00
|
|
|
func parseClass(class []rune) (not bool, chars []rune, ranges [][]rune, err error) {
|
|
|
|
if class[0] == '^' {
|
2017-06-25 17:51:08 +02:00
|
|
|
not = true
|
2017-10-29 16:46:17 +01:00
|
|
|
class = class[1:]
|
2017-06-25 17:51:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
for {
|
2017-10-29 16:46:17 +01:00
|
|
|
if len(class) == 0 {
|
2017-06-25 17:51:08 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var c0 rune
|
2017-10-29 16:46:17 +01:00
|
|
|
c0, class = class[0], class[1:]
|
2017-06-25 17:51:08 +02:00
|
|
|
switch c0 {
|
|
|
|
case '[', ']', '^', '-':
|
|
|
|
err = errInvalidDefinition
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if c0 == '\\' {
|
2017-10-29 16:46:17 +01:00
|
|
|
if len(class) == 0 {
|
2017-06-25 17:51:08 +02:00
|
|
|
err = errInvalidDefinition
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-10-29 16:46:17 +01:00
|
|
|
c0, class = unescapeChar(class[0]), class[1:]
|
2017-06-25 17:51:08 +02:00
|
|
|
}
|
|
|
|
|
2017-10-29 16:46:17 +01:00
|
|
|
if len(class) < 2 || class[0] != '-' {
|
2017-06-25 17:51:08 +02:00
|
|
|
chars = append(chars, c0)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
var c1 rune
|
2017-10-29 16:46:17 +01:00
|
|
|
c1, class = class[1], class[2:]
|
|
|
|
switch c1 {
|
|
|
|
case '[', ']', '^', '-':
|
|
|
|
err = errInvalidDefinition
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-06-25 17:51:08 +02:00
|
|
|
if c1 == '\\' {
|
2017-10-29 16:46:17 +01:00
|
|
|
if len(class) == 0 {
|
2017-06-25 17:51:08 +02:00
|
|
|
err = errInvalidDefinition
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-10-29 16:46:17 +01:00
|
|
|
c1, class = unescapeChar(class[0]), class[1:]
|
2017-06-25 17:51:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ranges = append(ranges, []rune{c0, c1})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func defineBootAnything(s *Syntax, d []string) error {
|
|
|
|
ct := stringToCommitType(d[2])
|
|
|
|
return s.AnyChar(d[1], ct)
|
|
|
|
}
|
|
|
|
|
|
|
|
func defineBootClass(s *Syntax, d []string) error {
|
2017-10-29 16:46:17 +01:00
|
|
|
name := d[1]
|
2017-06-25 17:51:08 +02:00
|
|
|
ct := stringToCommitType(d[2])
|
|
|
|
|
|
|
|
not, chars, ranges, err := parseClass([]rune(d[3]))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-10-29 16:46:17 +01:00
|
|
|
return s.Class(name, ct, not, chars, ranges)
|
2017-06-25 17:51:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func defineBootCharSequence(s *Syntax, d []string) error {
|
2017-10-29 16:46:17 +01:00
|
|
|
name := d[1]
|
2017-06-25 17:51:08 +02:00
|
|
|
ct := stringToCommitType(d[2])
|
|
|
|
|
2017-10-29 16:46:17 +01:00
|
|
|
chars, err := unescapeCharSequence(d[3])
|
2017-06-25 17:51:08 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-10-29 16:46:17 +01:00
|
|
|
return s.CharSequence(name, ct, chars)
|
2017-06-25 17:51:08 +02:00
|
|
|
}
|
|
|
|
|
2017-10-29 16:46:17 +01:00
|
|
|
func splitQuantifiedSymbol(s string) (string, int, int) {
|
|
|
|
ssplit := strings.Split(s, ":")
|
|
|
|
if len(ssplit) != 3 {
|
|
|
|
return s, 0, 0
|
|
|
|
}
|
2017-06-25 17:51:08 +02:00
|
|
|
|
2017-10-29 16:46:17 +01:00
|
|
|
name := ssplit[0]
|
|
|
|
|
|
|
|
min, err := strconv.Atoi(ssplit[1])
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
2017-06-25 17:51:08 +02:00
|
|
|
}
|
|
|
|
|
2017-10-29 16:46:17 +01:00
|
|
|
max, err := strconv.Atoi(ssplit[2])
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return name, min, max
|
2017-06-25 23:38:32 +02:00
|
|
|
}
|
2017-06-25 17:51:08 +02:00
|
|
|
|
2017-10-29 16:46:17 +01:00
|
|
|
func namesToSequenceItemsQuantify(n []string) []SequenceItem {
|
|
|
|
si := make([]SequenceItem, len(n))
|
|
|
|
for i, ni := range n {
|
|
|
|
name, min, max := splitQuantifiedSymbol(ni)
|
|
|
|
si[i] = SequenceItem{Name: name, Min: min, Max: max}
|
|
|
|
}
|
|
|
|
|
|
|
|
return si
|
2017-06-25 17:51:08 +02:00
|
|
|
}
|
|
|
|
|
2017-10-29 16:46:17 +01:00
|
|
|
func defineBootSequence(s *Syntax, defs []string) error {
|
|
|
|
name := defs[1]
|
|
|
|
ct := stringToCommitType(defs[2])
|
|
|
|
items := namesToSequenceItemsQuantify(defs[3:])
|
|
|
|
return s.Sequence(name, ct, items...)
|
2017-06-25 17:51:08 +02:00
|
|
|
}
|
|
|
|
|
2017-10-29 16:46:17 +01:00
|
|
|
func defineBootChoice(s *Syntax, defs []string) error {
|
|
|
|
name := defs[1]
|
|
|
|
ct := stringToCommitType(defs[2])
|
|
|
|
items := defs[3:]
|
|
|
|
return s.Choice(name, ct, items...)
|
2017-06-25 17:51:08 +02:00
|
|
|
}
|
|
|
|
|
2017-10-29 16:46:17 +01:00
|
|
|
func defineBoot(s *Syntax, defs []string) error {
|
|
|
|
switch defs[0] {
|
2017-06-25 17:51:08 +02:00
|
|
|
case "anything":
|
2017-10-29 16:46:17 +01:00
|
|
|
return defineBootAnything(s, defs)
|
2017-06-25 17:51:08 +02:00
|
|
|
case "class":
|
2017-10-29 16:46:17 +01:00
|
|
|
return defineBootClass(s, defs)
|
2017-06-25 17:51:08 +02:00
|
|
|
case "chars":
|
2017-10-29 16:46:17 +01:00
|
|
|
return defineBootCharSequence(s, defs)
|
2017-06-25 17:51:08 +02:00
|
|
|
case "sequence":
|
2017-10-29 16:46:17 +01:00
|
|
|
return defineBootSequence(s, defs)
|
2017-06-25 17:51:08 +02:00
|
|
|
case "choice":
|
2017-10-29 16:46:17 +01:00
|
|
|
return defineBootChoice(s, defs)
|
2017-06-25 17:51:08 +02:00
|
|
|
default:
|
|
|
|
return errInvalidDefinition
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func defineAllBoot(s *Syntax, defs [][]string) error {
|
|
|
|
for _, d := range defs {
|
|
|
|
if err := defineBoot(s, d); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-10-27 17:25:20 +02:00
|
|
|
func createBoot() (*Syntax, error) {
|
2017-06-26 00:20:54 +02:00
|
|
|
s := NewSyntax()
|
2017-10-27 17:25:20 +02:00
|
|
|
if err := defineAllBoot(s, bootSyntaxDefs); err != nil {
|
2017-06-25 17:51:08 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return s, s.Init()
|
|
|
|
}
|
|
|
|
|
2017-06-26 00:20:54 +02:00
|
|
|
func bootSyntax() (*Syntax, error) {
|
2017-10-27 17:25:20 +02:00
|
|
|
b, err := createBoot()
|
2017-06-25 17:51:08 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-10-29 20:01:33 +01:00
|
|
|
f, err := os.Open("treerack.treerack")
|
2017-06-25 17:51:08 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
doc, err := b.Parse(f)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-06-26 00:20:54 +02:00
|
|
|
s := NewSyntax()
|
2017-06-25 17:51:08 +02:00
|
|
|
return s, define(s, doc)
|
|
|
|
}
|