lemmascript 0.1.0 → 0.3.0

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.
@@ -2,7 +2,7 @@
2
2
  * Spec expression parser.
3
3
  * Parses //@ annotation expressions into RawExpr AST nodes.
4
4
  */
5
- const MULTI_OPS = ["==>", "===", "!==", ">=", "<=", "&&", "||"];
5
+ const MULTI_OPS = ["==>", "===", "!==", "==", "!=", ">=", "<=", "&&", "||"];
6
6
  function tokenize(input) {
7
7
  const tokens = [];
8
8
  let i = 0;
@@ -28,10 +28,23 @@ function tokenize(input) {
28
28
  continue;
29
29
  }
30
30
  if (/[0-9]/.test(input[i])) {
31
- let n = "";
32
- while (i < input.length && /[0-9]/.test(input[i]))
33
- n += input[i++];
34
- tokens.push({ type: "num", value: parseInt(n) });
31
+ let value;
32
+ if (input[i] === "0" && i + 1 < input.length && input[i + 1] === "x") {
33
+ i += 2;
34
+ let hex = "";
35
+ while (i < input.length && /[0-9a-fA-F]/.test(input[i]))
36
+ hex += input[i++];
37
+ value = parseInt(hex, 16);
38
+ }
39
+ else {
40
+ let dec = "";
41
+ while (i < input.length && /[0-9]/.test(input[i]))
42
+ dec += input[i++];
43
+ value = parseInt(dec, 10);
44
+ }
45
+ if (i < input.length && input[i] === "n")
46
+ i++;
47
+ tokens.push({ type: "num", value });
35
48
  continue;
36
49
  }
37
50
  if (/[a-zA-Z_]/.test(input[i])) {
@@ -116,9 +129,16 @@ class Parser {
116
129
  parseCmp() {
117
130
  const left = this.parseAdd();
118
131
  const t = this.peek();
119
- if (t?.type === "op" && ["===", "!==", ">=", "<=", ">", "<"].includes(t.value)) {
132
+ // 'in' as infix membership operator (set/seq/map): x in S
133
+ if (t?.type === "ident" && t.value === "in") {
134
+ this.advance();
135
+ return { kind: "binop", op: "in", left, right: this.parseAdd() };
136
+ }
137
+ if (t?.type === "op" && ["===", "!==", "==", "!=", ">=", "<=", ">", "<"].includes(t.value)) {
120
138
  this.advance();
121
- return { kind: "binop", op: t.value, left, right: this.parseAdd() };
139
+ // Normalize == to ===, != to !== so downstream sees one spelling
140
+ const op = t.value === "==" ? "===" : t.value === "!=" ? "!==" : t.value;
141
+ return { kind: "binop", op, left, right: this.parseAdd() };
122
142
  }
123
143
  return left;
124
144
  }
@@ -201,6 +221,34 @@ class Parser {
201
221
  this.advance();
202
222
  return { kind: "bool", value: false };
203
223
  }
224
+ // new Set<T>() / new Map<K,V>()
225
+ if (t.value === "new") {
226
+ this.advance();
227
+ const name = this.expect("ident").value;
228
+ if (name !== "Set" && name !== "Map")
229
+ throw new Error(`Unsupported constructor: new ${name}`);
230
+ // Skip <T> or <K,V> type arguments
231
+ let tsType = name;
232
+ if (this.match("op", "<")) {
233
+ let depth = 1;
234
+ let typeArgs = "";
235
+ while (depth > 0) {
236
+ const next = this.advance();
237
+ if (next.value === "<")
238
+ depth++;
239
+ else if (next.value === ">") {
240
+ depth--;
241
+ if (depth === 0)
242
+ break;
243
+ }
244
+ typeArgs += next.value;
245
+ }
246
+ tsType = `${name}<${typeArgs}>`;
247
+ }
248
+ this.expect("punc", "(");
249
+ this.expect("punc", ")");
250
+ return { kind: "emptyCollection", collectionType: name, tsType };
251
+ }
204
252
  if (t.value === "forall" || t.value === "exists") {
205
253
  const q = t.value;
206
254
  this.advance();
@@ -209,8 +257,6 @@ class Parser {
209
257
  let varType = "int";
210
258
  if (this.match("punc", ":")) {
211
259
  const ty = this.expect("ident").value;
212
- if (ty !== "nat" && ty !== "int")
213
- throw new Error(`Unknown type '${ty}'`);
214
260
  varType = ty;
215
261
  }
216
262
  this.expect("punc", ",");
@@ -227,6 +273,17 @@ class Parser {
227
273
  this.expect("punc", ")");
228
274
  return expr;
229
275
  }
276
+ if (t.type === "punc" && t.value === "[") {
277
+ this.advance();
278
+ const elems = [];
279
+ if (!this.match("punc", "]")) {
280
+ elems.push(this.parseImplies());
281
+ while (this.match("punc", ","))
282
+ elems.push(this.parseImplies());
283
+ this.expect("punc", "]");
284
+ }
285
+ return { kind: "arrayLiteral", elems };
286
+ }
230
287
  if (t.type === "punc" && t.value === "{") {
231
288
  this.advance();
232
289
  const fields = [];