This repository has been archived by the owner on Apr 4, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrules.go
131 lines (110 loc) · 2.32 KB
/
rules.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package alchemist
import (
"strconv"
"strings"
"unicode"
)
// Rule represents a rule in the program.
type Rule struct {
Left LHSRule
Right RHSRule
}
// LHSRule represents the left-hand side of a rule.
type LHSRule map[SimpleAtom]int
// Parse allows an LHSRule to be parsed from a string.
//
// Format (whitespace optional): `\d* (SimpleAtom) (\+ \d* SimpleAtom)*`
func (r *LHSRule) Parse(str string) error {
str = strings.TrimSpace(str)
rule := make(LHSRule)
*r = rule
rest := str
var index int
for {
rest = rest[index:]
firstNonDigit := strings.IndexFunc(rest, func(r rune) bool {
return !unicode.IsDigit(r)
})
var num int
if firstNonDigit > 0 {
digits := rest[:firstNonDigit]
num, _ = strconv.Atoi(digits)
} else {
num = 1
}
var atom SimpleAtom
err := atom.Parse(rest[firstNonDigit:])
if err != nil {
return &SyntaxError{
reason: "unable to parse an atom in " + rest,
}
}
if num == 0 {
rule[atom] = 0
}
for i := 0; i < num; i++ {
rule[atom]++
}
next := strings.IndexRune(rest, '+')
if next < 0 {
break
} else if next == len(rest)-1 {
return &SyntaxError{
reason: "trailing + sign in " + str,
}
} else {
index = next + 1
}
}
return nil
}
// RHSRule represents the right-hand side of a rule.
type RHSRule []Atom
// Parse allows an LHSRule to be parsed from a string.
//
// Format (whitespace optional): `\d* (Atom) (\+ \d* Atom)*`
func (r *RHSRule) Parse(str string) error {
str = strings.TrimSpace(str)
rest := str
var index int
for {
rest = rest[index:]
firstNonDigit := strings.IndexFunc(rest, func(r rune) bool {
return !unicode.IsDigit(r)
})
var num int
if firstNonDigit > 0 {
digits := rest[:firstNonDigit]
num, _ = strconv.Atoi(digits)
} else {
num = 1
}
var simple SimpleAtom
var in InAtom
var outSimple OutSimpleAtom
var outStrLit OutStrLitAtom
var correct Atom
atoms := []Atom{&simple, &in, &outSimple, &outStrLit}
for _, a := range atoms {
err := a.Parse(rest)
if err == nil {
correct = a
break
}
}
for i := 0; i < num; i++ {
*r = append(*r, correct)
}
next := strings.IndexRune(rest, '+')
if next < 0 {
break
} else if next == len(rest)-1 {
return &SyntaxError{
reason: "trailing + sign in " + str,
}
} else {
index = next + 1
}
}
return nil
}