yukigo-prolog-parser 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.mocharc.json +7 -7
- package/CHANGELOG.md +6 -0
- package/dist/index.d.ts +13 -1
- package/dist/index.js +72 -1
- package/dist/index.js.map +1 -1
- package/dist/parser/grammar.js +74 -26
- package/dist/parser/grammar.js.map +1 -1
- package/dist/parser/lexer.js +6 -5
- package/dist/parser/lexer.js.map +1 -1
- package/dist/std.d.ts +1 -0
- package/dist/std.js +133 -0
- package/dist/std.js.map +1 -0
- package/package.json +8 -2
- package/src/index.ts +82 -1
- package/src/parser/grammar.ne +223 -166
- package/src/parser/grammar.ts +88 -33
- package/src/parser/lexer.ts +40 -39
- package/src/{std.pl → std.ts} +132 -119
- package/tests/parser.spec.ts +1084 -830
- package/tests/tests.spec.ts +82 -0
- package/tsconfig.json +26 -18
- package/tsconfig.tsbuildinfo +1 -0
- package/dist/parser/parser.d.ts +0 -5
- package/dist/parser/parser.js +0 -31
- package/dist/parser/parser.js.map +0 -1
- package/src/parser/parser.ts +0 -35
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yukigo-prolog-parser",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -16,7 +16,13 @@
|
|
|
16
16
|
"moo": "^0.5.2",
|
|
17
17
|
"moo-ignore": "^2.5.3",
|
|
18
18
|
"nearley": "^2.20.1",
|
|
19
|
-
"yukigo-ast": "0.
|
|
19
|
+
"yukigo-ast": "^0.2.0"
|
|
20
|
+
},
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"import": "./dist/index.js",
|
|
24
|
+
"types": "./dist/index.d.ts"
|
|
25
|
+
}
|
|
20
26
|
},
|
|
21
27
|
"devDependencies": {
|
|
22
28
|
"@types/chai": "^5.2.2",
|
package/src/index.ts
CHANGED
|
@@ -1 +1,82 @@
|
|
|
1
|
-
|
|
1
|
+
import { AST, Expression, Rule, YukigoParser } from "yukigo-ast";
|
|
2
|
+
import nearley from "nearley";
|
|
3
|
+
import grammar from "./parser/grammar.js";
|
|
4
|
+
import { Token } from "moo";
|
|
5
|
+
import { stdCode } from "./std.js";
|
|
6
|
+
|
|
7
|
+
class UnexpectedToken extends Error {
|
|
8
|
+
constructor(token: Token) {
|
|
9
|
+
super(
|
|
10
|
+
`Parser: Unexpected '${token.type}' token '${token.value}' at line ${token.line} col ${token.col}.`
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
class AmbiguityError extends Error {
|
|
15
|
+
constructor(amountAST: number) {
|
|
16
|
+
super(
|
|
17
|
+
`Parser: Too much ambiguity. ${amountAST} ASTs parsed. Output not generated.`
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type HaskellConfig = {
|
|
23
|
+
typecheck: boolean;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export class YukigoPrologParser implements YukigoParser {
|
|
27
|
+
public errors: string[] = [];
|
|
28
|
+
private std: AST;
|
|
29
|
+
constructor(std: string = stdCode) {
|
|
30
|
+
this.errors = [];
|
|
31
|
+
this.std = this.feedParser(std);
|
|
32
|
+
}
|
|
33
|
+
public parse(code: string): AST {
|
|
34
|
+
const result = this.feedParser(code);
|
|
35
|
+
const ast = groupRuleDeclarations(this.std.concat(result));
|
|
36
|
+
return ast;
|
|
37
|
+
}
|
|
38
|
+
public parseExpression(code: string): Expression {
|
|
39
|
+
const expr = this.feedParser(code)[0];
|
|
40
|
+
return expr;
|
|
41
|
+
}
|
|
42
|
+
private feedParser(code: string): any {
|
|
43
|
+
const parser = new nearley.Parser(nearley.Grammar.fromCompiled(grammar));
|
|
44
|
+
try {
|
|
45
|
+
parser.feed(code);
|
|
46
|
+
parser.finish();
|
|
47
|
+
} catch (error) {
|
|
48
|
+
if ("token" in error) throw new UnexpectedToken(error.token);
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
const { results } = parser;
|
|
52
|
+
if (results.length > 1) throw new AmbiguityError(results.length);
|
|
53
|
+
if (results.length == 0) return [];
|
|
54
|
+
return results[0];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function groupRuleDeclarations(ast: AST): AST {
|
|
59
|
+
const groups: Record<string, Rule[]> = {};
|
|
60
|
+
const others: AST = [];
|
|
61
|
+
|
|
62
|
+
for (const node of ast) {
|
|
63
|
+
if (node instanceof Rule) {
|
|
64
|
+
const name = node.identifier.value;
|
|
65
|
+
if (!groups[name]) {
|
|
66
|
+
groups[name] = [];
|
|
67
|
+
}
|
|
68
|
+
groups[name].push(node);
|
|
69
|
+
} else {
|
|
70
|
+
others.push(node);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Merge equations for each rule
|
|
75
|
+
const ruleGroups: Rule[] = Object.values(groups).map((rules) => {
|
|
76
|
+
const identifier = rules[0].identifier;
|
|
77
|
+
const allEquations = rules.flatMap((r) => r.equations);
|
|
78
|
+
return new Rule(identifier, allEquations, identifier.loc);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return [...others, ...ruleGroups];
|
|
82
|
+
}
|
package/src/parser/grammar.ne
CHANGED
|
@@ -1,166 +1,223 @@
|
|
|
1
|
-
@{%
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
ConsExpression,
|
|
9
|
-
ArithmeticBinaryOperation,
|
|
10
|
-
ArithmeticUnaryOperation,
|
|
11
|
-
ComparisonOperation,
|
|
12
|
-
Exist,
|
|
13
|
-
Not,
|
|
14
|
-
Findall,
|
|
15
|
-
Sequence,
|
|
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
|
-
|
|
1
|
+
@{% import {
|
|
2
|
+
NumberPrimitive,
|
|
3
|
+
StringPrimitive,
|
|
4
|
+
Rule,
|
|
5
|
+
Fact,
|
|
6
|
+
Query,
|
|
7
|
+
ListPrimitive,
|
|
8
|
+
ConsExpression,
|
|
9
|
+
ArithmeticBinaryOperation,
|
|
10
|
+
ArithmeticUnaryOperation,
|
|
11
|
+
ComparisonOperation,
|
|
12
|
+
Exist,
|
|
13
|
+
Not,
|
|
14
|
+
Findall,
|
|
15
|
+
Sequence,
|
|
16
|
+
UnguardedBody,
|
|
17
|
+
Equation,
|
|
18
|
+
If,
|
|
19
|
+
Call,
|
|
20
|
+
Forall,
|
|
21
|
+
SymbolPrimitive,
|
|
22
|
+
AssignOperation,
|
|
23
|
+
UnifyOperation,
|
|
24
|
+
VariablePattern,
|
|
25
|
+
FunctorPattern,
|
|
26
|
+
ConsPattern,
|
|
27
|
+
ListPattern,
|
|
28
|
+
LogicConstraint,
|
|
29
|
+
LiteralPattern,
|
|
30
|
+
WildcardPattern,
|
|
31
|
+
TuplePattern,
|
|
32
|
+
Test,
|
|
33
|
+
Assert,
|
|
34
|
+
Truth
|
|
35
|
+
} from "yukigo-ast"
|
|
36
|
+
|
|
37
|
+
import { PrologLexer } from "./lexer.js"
|
|
38
|
+
|
|
39
|
+
const asSequence = (d) => {
|
|
40
|
+
if (d instanceof Sequence) return d;
|
|
41
|
+
if (Array.isArray(d)) return d.length === 1 ? d[0] : new Sequence(d);
|
|
42
|
+
return new Sequence([d]);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
%}
|
|
46
|
+
@preprocessor typescript
|
|
47
|
+
@lexer PrologLexer
|
|
48
|
+
|
|
49
|
+
program -> _ (clause _):* {% (d) => d[1].map(x => x[0]).filter(x => x !== null).flat(Infinity) %}
|
|
50
|
+
|
|
51
|
+
clause -> (fact | rule | query | test_rule) {% (d) => d[0][0] %}
|
|
52
|
+
|
|
53
|
+
fact -> any_atom arguments:? _ %period {% (d) => new Fact(d[0], d[1] ?? []) %}
|
|
54
|
+
|
|
55
|
+
rule -> any_atom equation _ %period {% (d) => new Rule(d[0], [d[1]]) %}
|
|
56
|
+
|
|
57
|
+
test_rule -> %testKeyword %lparen _ structural_literal test_args:? _ %rparen equation _ %period {% (d) => new Test(d[3], d[7].body.sequence, d[4] ? [d[4]] : []) %}
|
|
58
|
+
|
|
59
|
+
test_args -> _ %comma _ pattern {% (d) => d[3] %}
|
|
60
|
+
|
|
61
|
+
equation -> arguments:? _ %colonDash _ body {% (d) => new Equation(d[0] || [], new UnguardedBody(new Sequence(d[4])))%}
|
|
62
|
+
|
|
63
|
+
query -> %queryOp _ body _ %period {% (d) => new Query(d[2]) %}
|
|
64
|
+
|
|
65
|
+
body -> expression (_ (%comma | %semicolon) _ expression):* {% (d) => [d[0], ...d[1].map(x => x[3])] %}
|
|
66
|
+
|
|
67
|
+
expression ->
|
|
68
|
+
(conditional | forall | findall | unification | assignment | comparison | not | exist | assertion) {% (d) => d[0][0] %}
|
|
69
|
+
| "(" _ body _ ")" {% (d) => new Sequence(d[2]) %}
|
|
70
|
+
|
|
71
|
+
conditional -> "(" _ body _ "->" _ body _ %semicolon _ body _ ")" {% (d) =>
|
|
72
|
+
new If(
|
|
73
|
+
asSequence(d[2]),
|
|
74
|
+
asSequence(d[6]),
|
|
75
|
+
asSequence(d[10])
|
|
76
|
+
)
|
|
77
|
+
%}
|
|
78
|
+
|
|
79
|
+
forall -> %forallRule "(" _ expression _ %comma _ expression _ ")" {% (d) => new Forall(d[3], d[7]) %}
|
|
80
|
+
|
|
81
|
+
findall -> %findallRule "(" _ pattern _ %comma _ expression _ %comma _ pattern _ ")" {% (d) => new Findall(d[3], d[7], d[11]) %}
|
|
82
|
+
|
|
83
|
+
not ->
|
|
84
|
+
"not" "(" _ expression _ ")" {% (d) => new Not(asSequence(d[3])) %}
|
|
85
|
+
| "\\+" _ expression {% (d) => new Not(d[2]) %}
|
|
86
|
+
|
|
87
|
+
exist ->
|
|
88
|
+
"call" %lparen _ pattern_list _ %rparen {% (d) => {
|
|
89
|
+
const [callee, ...rest] = d[3]
|
|
90
|
+
return new Call(callee.name, rest)
|
|
91
|
+
} %}
|
|
92
|
+
| any_atom arguments:? {% (d, l, reject) => {
|
|
93
|
+
const val = d[0].value;
|
|
94
|
+
if (["not", "\\+", "call", "assertion"].includes(val)) return reject;
|
|
95
|
+
return new Exist(d[0], d[1] ?? [])
|
|
96
|
+
} %}
|
|
97
|
+
| variable {% (d) => new Exist(d[0], []) %}
|
|
98
|
+
|
|
99
|
+
assertion -> "assertion" %lparen _ expression _ %rparen {% (d) =>
|
|
100
|
+
new Assert(null, new Truth(asSequence(d[3])))
|
|
101
|
+
%}
|
|
102
|
+
|
|
103
|
+
assignment ->
|
|
104
|
+
addition _ "is" _ addition {% (d) =>
|
|
105
|
+
new LogicConstraint(new AssignOperation("Assign", d[0], d[4]))
|
|
106
|
+
%}
|
|
107
|
+
|
|
108
|
+
unification -> addition _ "=" _ addition {% (d) => new LogicConstraint(new UnifyOperation("Unify", d[0], d[4])) %}
|
|
109
|
+
|
|
110
|
+
comparison ->
|
|
111
|
+
addition _ comparison_op _ addition {% (d) =>
|
|
112
|
+
new LogicConstraint(new ComparisonOperation(d[2], d[0], d[4]))
|
|
113
|
+
%}
|
|
114
|
+
|
|
115
|
+
addition ->
|
|
116
|
+
multiplication _ "+" _ addition {% (d) => new ArithmeticBinaryOperation("Plus", d[0], d[4]) %}
|
|
117
|
+
| multiplication _ "-" _ addition {% (d) => new ArithmeticBinaryOperation("Minus", d[0], d[4]) %}
|
|
118
|
+
| multiplication {% id %}
|
|
119
|
+
|
|
120
|
+
multiplication ->
|
|
121
|
+
primary _ "*" _ multiplication {% (d) => new ArithmeticBinaryOperation("Multiply", d[0], d[4]) %}
|
|
122
|
+
| primary _ "/" _ multiplication {% (d) => new ArithmeticBinaryOperation("Divide", d[0], d[4]) %}
|
|
123
|
+
| primary {% id %}
|
|
124
|
+
|
|
125
|
+
primary ->
|
|
126
|
+
arithmetic_literal {% id %}
|
|
127
|
+
| variable {% id %}
|
|
128
|
+
| "-" _ primary {% (d) => new ArithmeticUnaryOperation("Negation", d[2]) %}
|
|
129
|
+
| strict_atom arguments {% (d, l, reject) => {
|
|
130
|
+
const val = d[0].value;
|
|
131
|
+
if (["abs", "round", "sqrt", "max", "assertion"].includes(val)) return reject;
|
|
132
|
+
return new Exist(d[0], d[1] ?? [])
|
|
133
|
+
} %}
|
|
134
|
+
| primitiveOperation {% id %}
|
|
135
|
+
| cons_expr {% id %}
|
|
136
|
+
| list_expr {% id %}
|
|
137
|
+
| "(" _ addition _ ")" {% d => d[2] %}
|
|
138
|
+
|
|
139
|
+
list_expr -> "[" _ primary_list:? _ "]" {% (d) => new ListPrimitive(d[2] || []) %}
|
|
140
|
+
|
|
141
|
+
cons_expr -> "[" _ primary_list _ %consOp _ addition _ "]" {% (d) => {
|
|
142
|
+
const heads = d[2];
|
|
143
|
+
const tail = d[6];
|
|
144
|
+
let current = tail;
|
|
145
|
+
for (let i = heads.length - 1; i >= 0; i--) {
|
|
146
|
+
current = new ConsExpression(heads[i], current);
|
|
147
|
+
}
|
|
148
|
+
return current;
|
|
149
|
+
} %}
|
|
150
|
+
|
|
151
|
+
primitiveOperation ->
|
|
152
|
+
"round" __ addition {% d => new ArithmeticUnaryOperation("Round", d[2]) %}
|
|
153
|
+
| "abs" __ addition {% d => new ArithmeticUnaryOperation("Absolute", d[2]) %}
|
|
154
|
+
| "sqrt" __ addition {% d => new ArithmeticUnaryOperation("Sqrt", d[2]) %}
|
|
155
|
+
| "max" "(" _ addition _ "," _ addition _ ")" {% d => new ArithmeticBinaryOperation("Max", d[3], d[7]) %}
|
|
156
|
+
|
|
157
|
+
primitiveArguments -> %lparen _ primary_list _ %rparen {% (d) => d[2] %}
|
|
158
|
+
primary_list -> addition (_ %comma _ addition):* {% (d) => [d[0], ...d[1].map(x => x[3])] %}
|
|
159
|
+
|
|
160
|
+
arguments -> %lparen _ pattern_list _ %rparen {% (d) => d[2] %}
|
|
161
|
+
|
|
162
|
+
pattern_list -> pattern (_ %comma _ pattern):* {% (d) => [d[0], ...d[1].map(x => x[3])] %}
|
|
163
|
+
|
|
164
|
+
pattern ->
|
|
165
|
+
infix_pattern {% id %}
|
|
166
|
+
| primary_pattern {% id %}
|
|
167
|
+
|
|
168
|
+
primary_pattern ->
|
|
169
|
+
variable {% (d) => new VariablePattern(d[0]) %}
|
|
170
|
+
| structural_literal {% (d) => new LiteralPattern(d[0]) %}
|
|
171
|
+
| %wildcard {% (d) => new WildcardPattern() %}
|
|
172
|
+
| any_atom arguments {% (d) => new FunctorPattern(d[0], d[1]) %}
|
|
173
|
+
| "(" _ pattern_list _ ")" {% (d) => new TuplePattern(d[2]) %}
|
|
174
|
+
| "[" _ pattern_list _ %consOp _ pattern _ "]" {% (d) => {
|
|
175
|
+
const heads = d[2];
|
|
176
|
+
const tail = d[6];
|
|
177
|
+
let current = tail;
|
|
178
|
+
for (let i = heads.length - 1; i >= 0; i--) {
|
|
179
|
+
current = new ConsPattern(heads[i], current);
|
|
180
|
+
}
|
|
181
|
+
return current;
|
|
182
|
+
} %}
|
|
183
|
+
| "[" _ pattern_list:? _ "]" {% (d) => new ListPattern(d[2] ? d[2] : []) %}
|
|
184
|
+
|
|
185
|
+
infix_pattern -> primary_pattern _ any_atom _ pattern {% (d) => new FunctorPattern(d[2], [d[0], d[4]]) %}
|
|
186
|
+
|
|
187
|
+
variable -> %variable {% (d) => new SymbolPrimitive(d[0].value) %}
|
|
188
|
+
|
|
189
|
+
strict_atom -> %atom {% (d) => new SymbolPrimitive(d[0].value) %}
|
|
190
|
+
| %primitiveOperator {% (d) => new SymbolPrimitive(d[0].value) %}
|
|
191
|
+
|
|
192
|
+
any_atom -> strict_atom {% id %}
|
|
193
|
+
| (%op | %comparisonOp) {% (d) => new SymbolPrimitive(d[0][0].value) %}
|
|
194
|
+
|
|
195
|
+
arithmetic_literal ->
|
|
196
|
+
strict_atom {% id %}
|
|
197
|
+
| %number {% (d) => new NumberPrimitive(Number(d[0].value)) %}
|
|
198
|
+
| %string {% (d) => new StringPrimitive(d[0].value) %}
|
|
199
|
+
|
|
200
|
+
structural_literal ->
|
|
201
|
+
any_atom {% id %}
|
|
202
|
+
| %number {% (d) => new NumberPrimitive(Number(d[0].value)) %}
|
|
203
|
+
| %string {% (d) => new StringPrimitive(d[0].value) %}
|
|
204
|
+
|
|
205
|
+
comparison_op ->
|
|
206
|
+
"=:=" {% d => "Equal" %}
|
|
207
|
+
| "=\\=" {% d => "NotEqual" %}
|
|
208
|
+
| "==" {% d => "Same" %}
|
|
209
|
+
| "\\==" {% d => "NotSame" %}
|
|
210
|
+
| "\\=" {% d => "NotSame" %}
|
|
211
|
+
| "@<" {% d => "LessThan" %}
|
|
212
|
+
| "@=<" {% d => "LessOrEqualThan" %}
|
|
213
|
+
| "@>" {% d => "GreaterThan" %}
|
|
214
|
+
| "@>=" {% d => "GreaterOrEqualThan" %}
|
|
215
|
+
| "<" {% d => "LessThan" %}
|
|
216
|
+
| "=<" {% d => "LessOrEqualThan" %}
|
|
217
|
+
| ">" {% d => "GreaterThan" %}
|
|
218
|
+
| ">=" {% d => "GreaterOrEqualThan" %}
|
|
219
|
+
| "=@=" {% d => "Same" %}
|
|
220
|
+
| "\\=@=" {% d => "NotSame" %}
|
|
221
|
+
|
|
222
|
+
_ -> %WS:*
|
|
223
|
+
__ -> %WS:?
|