generated from okp4/template-oss
-
Notifications
You must be signed in to change notification settings - Fork 119
/
assert.go
182 lines (162 loc) · 5.71 KB
/
assert.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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
package prolog
import (
"strings"
"unicode/utf8"
"github.com/ichiban/prolog/engine"
"github.com/samber/lo"
"github.com/axone-protocol/axoned/v8/x/logic/util"
)
// PredicateMatches returns a function that matches the given predicate against the given other predicate.
// If the other predicate contains a slash, it is matched as is. Otherwise, the other predicate is matched against the
// first part of the given predicate.
// For example:
// - matchPredicate("foo/0")("foo/0") -> true
// - matchPredicate("foo/0")("foo/1") -> false
// - matchPredicate("foo/0")("foo") -> true
// - matchPredicate("foo/0")("bar") -> false
//
// The function is curried, and is a binary relation that is reflexive, associative (but not commutative).
func PredicateMatches(this string) func(string) bool {
return func(that string) bool {
if strings.Contains(that, "/") {
return this == that
}
return strings.Split(this, "/")[0] == that
}
}
// IsList returns true if the given term is a list.
func IsList(term engine.Term, env *engine.Env) bool {
_, err := AssertList(term, env)
return err == nil
}
// IsEmptyList returns true if the given term is an empty list.
func IsEmptyList(term engine.Term, env *engine.Env) bool {
if v, ok := env.Resolve(term).(engine.Atom); ok {
return v == AtomEmptyList
}
return false
}
// IsGround returns true if the given term holds no free variables.
func IsGround(term engine.Term, env *engine.Env) bool {
_, err := AssertIsGround(term, env)
return err == nil
}
func AreGround(terms []engine.Term, env *engine.Env) bool {
return lo.EveryBy(terms, func(t engine.Term) bool {
return IsGround(t, env)
})
}
// AssertIsGround resolves a term and returns it if it is ground.
// If the term is not ground, the function returns nil and the instantiation error.
func AssertIsGround(term engine.Term, env *engine.Env) (engine.Term, error) {
switch term := env.Resolve(term).(type) {
case engine.Variable:
return nil, engine.InstantiationError(env)
case engine.Compound:
args := make([]engine.Term, term.Arity())
for i := 0; i < term.Arity(); i++ {
arg, err := AssertIsGround(term.Arg(i), env)
if err != nil {
return nil, err
}
args[i] = arg
}
return term.Functor().Apply(args...), nil
default:
return term, nil
}
}
// AssertAtom resolves a term and attempts to convert it into an engine.Atom if possible.
// If conversion fails, the function returns the empty atom and the error.
func AssertAtom(term engine.Term, env *engine.Env) (engine.Atom, error) {
switch term := env.Resolve(term).(type) {
case engine.Atom:
return term, nil
case engine.Variable:
return AtomEmpty, engine.InstantiationError(env)
default:
return AtomEmpty, engine.TypeError(AtomTypeAtom, term, env)
}
}
// AssertCharacterCode resolves a term and attempts to convert it into a rune if possible.
// If conversion fails, the function returns the zero value and the error.
func AssertCharacterCode(term engine.Term, env *engine.Env) (rune, error) {
switch term := env.Resolve(term).(type) {
case engine.Integer:
if term >= 0 && term <= utf8.MaxRune {
return rune(term), nil
}
case engine.Variable:
return utf8.RuneError, engine.InstantiationError(env)
}
return utf8.RuneError, engine.TypeError(AtomTypeCharacterCode, term, env)
}
// AssertCharacter resolves a term and attempts to convert it into an engine.Atom if possible.
// If conversion fails, the function returns the empty atom and the error.
func AssertCharacter(term engine.Term, env *engine.Env) (rune, error) {
switch term := env.Resolve(term).(type) {
case engine.Atom:
runes := []rune(term.String())
if len(runes) == 1 {
return runes[0], nil
}
case engine.Variable:
return utf8.RuneError, engine.InstantiationError(env)
}
return utf8.RuneError, engine.TypeError(AtomTypeCharacter, term, env)
}
// AssertByte resolves a term and attempts to convert it into a byte if possible.
// If conversion fails, the function returns the zero value and the error.
func AssertByte(term engine.Term, env *engine.Env) (byte, error) {
switch term := env.Resolve(term).(type) {
case engine.Integer:
if term >= 0 && term <= 255 {
return byte(term), nil
}
case engine.Variable:
return 0, engine.InstantiationError(env)
}
return 0, engine.TypeError(AtomTypeByte, term, env)
}
// AssertList resolves a term as a list and returns it as a engine.Compound.
// If conversion fails, the function returns nil and the error.
func AssertList(term engine.Term, env *engine.Env) (engine.Term, error) {
switch term := env.Resolve(term).(type) {
case engine.Compound:
if term.Functor() == AtomDot && term.Arity() == 2 {
return term, nil
}
case engine.Atom:
if term == AtomEmptyList {
return term, nil
}
}
return nil, engine.TypeError(AtomTypeList, term, env)
}
// AssertPair resolves a term as a pair and returns the pair components.
// If conversion fails, the function returns nil and the error.
func AssertPair(term engine.Term, env *engine.Env) (engine.Term, engine.Term, error) {
term, err := AssertIsGround(term, env)
if err != nil {
return nil, nil, err
}
if term, ok := term.(engine.Compound); ok && term.Functor() == AtomPair && term.Arity() == 2 {
return term.Arg(0), term.Arg(1), nil
}
return nil, nil, engine.TypeError(AtomTypePair, term, env)
}
// AssertURIComponent resolves a term as a URI component and returns it as an URIComponent.
func AssertURIComponent(term engine.Term, env *engine.Env) (util.URIComponent, error) {
switch v := env.Resolve(term); v {
case AtomQueryValue:
return util.QueryValueComponent, nil
case AtomFragment:
return util.FragmentComponent, nil
case AtomPath:
return util.PathComponent, nil
case AtomSegment:
return util.SegmentComponent, nil
default:
return 0, engine.TypeError(AtomTypeURIComponent, term, env)
}
}