This repository has been archived by the owner on Jan 29, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
comp.go
152 lines (140 loc) · 3.82 KB
/
comp.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
package std
import (
"github.com/mb0/xelf/cor"
"github.com/mb0/xelf/exp"
"github.com/mb0/xelf/lit"
"github.com/mb0/xelf/typ"
)
// eqSpec returns a bool whether the arguments are equivalent literals.
// The result is negated, if the expression symbol is 'ne'.
var eqSpec = core.add(SpecDXX("<form eq any plain; bool>",
func(x CallCtx) (exp.El, error) {
return evalBinaryComp(x, true, lit.Equiv)
}))
var neSpec = core.add(SpecDXX("<form ne any plain; bool>",
func(x CallCtx) (exp.El, error) {
res, err := evalBinaryComp(x, true, lit.Equiv)
if err != nil {
return nil, err
}
a := res.(*exp.Atom)
a.Lit = !a.Lit.(lit.Bool)
return a, nil
}))
// equalSpec returns a bool whether the arguments are same types or same literals.
var equalSpec = core.add(SpecDXX("<form equal @1 plain:list|@1 bool>",
func(x CallCtx) (exp.El, error) {
return evalBinaryComp(x, true, lit.Equal)
}))
var inSpec = core.add(SpecDXX("<form in @1 list|@1 bool>",
func(x CallCtx) (exp.El, error) {
return inOrNi(x, false)
}))
var niSpec = core.add(SpecDXX("<form ni @1 list|@1 bool>",
func(x CallCtx) (exp.El, error) {
return inOrNi(x, true)
}))
func inOrNi(x CallCtx, neg bool) (exp.El, error) {
err := x.Layout.Eval(x.Prog, x.Env, x.Hint)
if err != nil {
return nil, err
}
a := x.Arg(0).(*exp.Atom)
b := x.Arg(1).(*exp.Atom)
list, ok := b.Lit.(lit.Indexer)
if !ok {
return nil, cor.Errorf("expect idxer got %s", b.Typ())
}
var found bool
err = list.IterIdx(func(idx int, el lit.Lit) error {
if found = lit.Equal(el, a.Lit); found {
return lit.BreakIter
}
return nil
})
if err != nil {
return nil, err
}
if neg {
found = !found
}
return &exp.Atom{Lit: lit.Bool(found)}, nil
}
// ltSpec returns a bool whether the arguments are monotonic increasing literals.
// Or the inverse, if the expression symbol is 'ge'.
var ltSpec = core.add(SpecDXX("<form lt @1 plain:list|@1 bool>",
func(x CallCtx) (exp.El, error) {
return evalBinaryComp(x, false, func(a, b lit.Lit) bool {
res, ok := lit.Less(a, b)
return ok && res
})
}))
var geSpec = core.add(SpecDXX("<form ge @1 plain:list|@1 bool>",
func(x CallCtx) (exp.El, error) {
return evalBinaryComp(x, false, func(a, b lit.Lit) bool {
res, ok := lit.Less(a, b)
return ok && !res
})
}))
// gtSpec returns a bool whether the arguments are monotonic decreasing literals.
// Or the inverse, if the expression symbol is 'le'.
var gtSpec = core.add(SpecDXX("<form gt @1 plain:list|@1 bool>",
func(x CallCtx) (exp.El, error) {
return evalBinaryComp(x, false, func(a, b lit.Lit) bool {
res, ok := lit.Less(b, a)
return ok && res
})
}))
var leSpec = core.add(SpecDXX("<form le @1 plain:list|@1 bool>",
func(x CallCtx) (exp.El, error) {
return evalBinaryComp(x, false, func(a, b lit.Lit) bool {
res, ok := lit.Less(b, a)
return ok && !res
})
}))
type cmpf = func(a, b lit.Lit) bool
func evalBinaryComp(x CallCtx, sym bool, cmp cmpf) (exp.El, error) {
err := x.Layout.Eval(x.Prog, x.Env, x.Hint)
if err == exp.ErrUnres {
err = nil
}
if err != nil {
return nil, err
}
var res, init bool
var unres []exp.El
var last *exp.Atom
for _, args := range x.Groups {
for _, arg := range args {
if arg.Typ().Kind&typ.KindAny == 0 {
if len(unres) == 0 {
unres = make([]exp.El, 0, 1+len(x.Groups[1]))
if res {
init = true
unres = append(unres, last)
}
}
res = false
unres = append(unres, arg)
continue
}
el := arg.(*exp.Atom)
if last != nil {
if !cmp(last.Lit, el.Lit) {
return &exp.Atom{Lit: lit.False}, nil
}
}
if !res && ((!sym || !init) && len(unres) > 0) || len(unres) == 1 {
unres = append(unres, el)
}
last = el
res = true
}
}
if len(unres) != 0 {
call := *x.Call
call.Groups = [][]exp.El{unres[:1], unres[1:]}
return &call, exp.ErrUnres
}
return &exp.Atom{Lit: lit.True}, nil
}