-
Notifications
You must be signed in to change notification settings - Fork 15
/
pattern_tuple.go
144 lines (129 loc) · 3.39 KB
/
pattern_tuple.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
132
133
134
135
136
137
138
139
140
141
142
143
144
package rel
import (
"bytes"
"fmt"
)
type TuplePatternAttr struct {
name string
pattern FallbackPattern
}
func NewTuplePatternAttr(name string, pattern FallbackPattern) TuplePatternAttr {
return TuplePatternAttr{
name: name,
pattern: pattern,
}
}
func (a TuplePatternAttr) String() string {
if a.pattern.fallback == nil {
return fmt.Sprintf("%s: %s", a.name, a.pattern)
}
return fmt.Sprintf("%s?: %s", a.name, a.pattern)
}
func (a *TuplePatternAttr) IsWildcard() bool {
return a.name == "*"
}
type TuplePattern struct {
attrs []TuplePatternAttr
}
func NewTuplePattern(attrs ...TuplePatternAttr) TuplePattern {
names := make(map[string]bool)
for _, attr := range attrs {
if names[attr.name] {
panic(fmt.Sprintf("name %s is duplicated in tuple", attr.name))
}
}
return TuplePattern{attrs}
}
func (p TuplePattern) Bind(local Scope, value Value) (Scope, error) {
tuple, is := value.(Tuple)
if !is {
return EmptyScope, fmt.Errorf("%s is not a tuple", value)
}
extraElements := make(map[int]int)
for i, attr := range p.attrs {
if _, is := attr.pattern.pattern.(ExtraElementPattern); is {
if len(extraElements) == 1 {
return EmptyScope, fmt.Errorf("non-deterministic pattern is not supported yet")
}
extraElements[i] = tuple.Count() - len(p.attrs)
}
if attr.pattern.fallback != nil {
if len(extraElements) == 1 {
return EmptyScope, fmt.Errorf("non-deterministic pattern is not supported yet")
}
extraElements[i] = tuple.Count() - len(p.attrs)
}
}
if len(p.attrs) > tuple.Count()+len(extraElements) {
return EmptyScope, fmt.Errorf("length of tuple %s shorter than tuple pattern %s", tuple, p)
}
if len(extraElements) == 0 && len(p.attrs) < tuple.Count() {
return EmptyScope, fmt.Errorf("length of tuple %s longer than tuple pattern %s", tuple, p)
}
result := EmptyScope
names := tuple.Names()
for _, attr := range p.attrs {
var tupleValue Value
if _, is := attr.pattern.pattern.(ExtraElementPattern); is {
tupleValue = tuple.Project(names)
if tupleValue == nil {
return EmptyScope, fmt.Errorf("tuple %s cannot match tuple pattern %s", tuple, p)
}
} else {
var found bool
tupleValue, found = tuple.Get(attr.name)
if !found {
if attr.pattern.fallback == nil {
return EmptyScope, fmt.Errorf("couldn't find %s in tuple %s", attr.name, tuple)
}
var err error
tupleValue, err = attr.pattern.fallback.Eval(local)
if err != nil {
return EmptyScope, err
}
}
}
scope, err := attr.pattern.Bind(local, tupleValue)
if err != nil {
return EmptyScope, err
}
result, err = result.MatchedUpdate(scope)
if err != nil {
return EmptyScope, err
}
names = names.Without(attr.name)
}
return result, nil
}
func (p TuplePattern) String() string { //nolint:dupl
var b bytes.Buffer
b.WriteByte('(')
for i, attr := range p.attrs {
if i > 0 {
b.WriteString(", ")
}
if attr.IsWildcard() {
isDot := false
if exprpat, is := attr.pattern.pattern.(ExprPattern); is {
if ident, is := exprpat.Expr.(IdentExpr); is {
isDot = ident.Ident() == "."
}
}
if !isDot {
b.WriteString(attr.pattern.String())
}
b.WriteString(".*")
} else {
b.WriteString(attr.String())
}
}
b.WriteByte(')')
return b.String()
}
func (p TuplePattern) Bindings() []string {
bindings := make([]string, len(p.attrs))
for i, v := range p.attrs {
bindings[i] = v.pattern.String()
}
return bindings
}