lemmascript 0.0.1 → 0.1.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.
- package/LICENSE +21 -0
- package/README.md +101 -3
- package/package.json +30 -20
- package/tools/dist/dafny-commands.js +104 -0
- package/tools/dist/dafny-emit.js +449 -0
- package/tools/dist/emit.js +253 -0
- package/tools/dist/extract.js +435 -0
- package/tools/dist/ir.js +7 -0
- package/tools/dist/lsc.js +118 -0
- package/tools/dist/rawir.js +10 -0
- package/tools/dist/resolve.js +451 -0
- package/tools/dist/specparser.js +251 -0
- package/tools/dist/transform.js +745 -0
- package/tools/dist/typedir.js +7 -0
- package/tools/dist/types.js +38 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -4
- package/src/index.ts +0 -1
- package/tsconfig.json +0 -14
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec expression parser.
|
|
3
|
+
* Parses //@ annotation expressions into RawExpr AST nodes.
|
|
4
|
+
*/
|
|
5
|
+
const MULTI_OPS = ["==>", "===", "!==", ">=", "<=", "&&", "||"];
|
|
6
|
+
function tokenize(input) {
|
|
7
|
+
const tokens = [];
|
|
8
|
+
let i = 0;
|
|
9
|
+
while (i < input.length) {
|
|
10
|
+
if (/\s/.test(input[i])) {
|
|
11
|
+
i++;
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
if (input[i] === "\\" && input.slice(i + 1, i + 7) === "result") {
|
|
15
|
+
tokens.push({ type: "result", value: undefined });
|
|
16
|
+
i += 7;
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
if (input[i] === '"' || input[i] === "'") {
|
|
20
|
+
const quote = input[i];
|
|
21
|
+
i++;
|
|
22
|
+
let s = "";
|
|
23
|
+
while (i < input.length && input[i] !== quote)
|
|
24
|
+
s += input[i++];
|
|
25
|
+
if (i < input.length)
|
|
26
|
+
i++;
|
|
27
|
+
tokens.push({ type: "str", value: s });
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
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) });
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (/[a-zA-Z_]/.test(input[i])) {
|
|
38
|
+
let id = "";
|
|
39
|
+
while (i < input.length && /[a-zA-Z_0-9]/.test(input[i]))
|
|
40
|
+
id += input[i++];
|
|
41
|
+
tokens.push({ type: "ident", value: id });
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
let matched = false;
|
|
45
|
+
for (const op of MULTI_OPS) {
|
|
46
|
+
if (input.slice(i, i + op.length) === op) {
|
|
47
|
+
tokens.push({ type: "op", value: op });
|
|
48
|
+
i += op.length;
|
|
49
|
+
matched = true;
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (matched)
|
|
54
|
+
continue;
|
|
55
|
+
const ch = input[i];
|
|
56
|
+
if ("+-*/%><!".includes(ch)) {
|
|
57
|
+
tokens.push({ type: "op", value: ch });
|
|
58
|
+
}
|
|
59
|
+
else if ("()[],:.{}".includes(ch)) {
|
|
60
|
+
tokens.push({ type: "punc", value: ch });
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
throw new Error(`Unexpected '${ch}' at ${i} in: ${input}`);
|
|
64
|
+
}
|
|
65
|
+
i++;
|
|
66
|
+
}
|
|
67
|
+
return tokens;
|
|
68
|
+
}
|
|
69
|
+
// ── Parser ───────────────────────────────────────────────────
|
|
70
|
+
class Parser {
|
|
71
|
+
tokens;
|
|
72
|
+
pos = 0;
|
|
73
|
+
constructor(tokens) {
|
|
74
|
+
this.tokens = tokens;
|
|
75
|
+
}
|
|
76
|
+
peek() { return this.tokens[this.pos]; }
|
|
77
|
+
advance() { return this.tokens[this.pos++]; }
|
|
78
|
+
expect(type, value) {
|
|
79
|
+
const t = this.advance();
|
|
80
|
+
if (!t || t.type !== type || (value !== undefined && t.value !== value))
|
|
81
|
+
throw new Error(`Expected ${type}${value ? ` '${value}'` : ""}, got ${t ? JSON.stringify(t) : "EOF"}`);
|
|
82
|
+
return t;
|
|
83
|
+
}
|
|
84
|
+
match(type, value) {
|
|
85
|
+
const t = this.peek();
|
|
86
|
+
if (t && t.type === type && (value === undefined || t.value === value)) {
|
|
87
|
+
this.pos++;
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
parse() {
|
|
93
|
+
const r = this.parseImplies();
|
|
94
|
+
if (this.pos < this.tokens.length)
|
|
95
|
+
throw new Error(`Unexpected: ${JSON.stringify(this.peek())}`);
|
|
96
|
+
return r;
|
|
97
|
+
}
|
|
98
|
+
parseImplies() {
|
|
99
|
+
const left = this.parseOr();
|
|
100
|
+
if (this.match("op", "==>"))
|
|
101
|
+
return { kind: "binop", op: "==>", left, right: this.parseImplies() };
|
|
102
|
+
return left;
|
|
103
|
+
}
|
|
104
|
+
parseOr() {
|
|
105
|
+
let left = this.parseAnd();
|
|
106
|
+
while (this.match("op", "||"))
|
|
107
|
+
left = { kind: "binop", op: "||", left, right: this.parseAnd() };
|
|
108
|
+
return left;
|
|
109
|
+
}
|
|
110
|
+
parseAnd() {
|
|
111
|
+
let left = this.parseCmp();
|
|
112
|
+
while (this.match("op", "&&"))
|
|
113
|
+
left = { kind: "binop", op: "&&", left, right: this.parseCmp() };
|
|
114
|
+
return left;
|
|
115
|
+
}
|
|
116
|
+
parseCmp() {
|
|
117
|
+
const left = this.parseAdd();
|
|
118
|
+
const t = this.peek();
|
|
119
|
+
if (t?.type === "op" && ["===", "!==", ">=", "<=", ">", "<"].includes(t.value)) {
|
|
120
|
+
this.advance();
|
|
121
|
+
return { kind: "binop", op: t.value, left, right: this.parseAdd() };
|
|
122
|
+
}
|
|
123
|
+
return left;
|
|
124
|
+
}
|
|
125
|
+
parseAdd() {
|
|
126
|
+
let left = this.parseMul();
|
|
127
|
+
while (this.peek()?.type === "op" && ["+", "-"].includes(this.peek().value)) {
|
|
128
|
+
const op = this.advance().value;
|
|
129
|
+
left = { kind: "binop", op, left, right: this.parseMul() };
|
|
130
|
+
}
|
|
131
|
+
return left;
|
|
132
|
+
}
|
|
133
|
+
parseMul() {
|
|
134
|
+
let left = this.parseUnary();
|
|
135
|
+
while (this.peek()?.type === "op" && ["*", "/", "%"].includes(this.peek().value)) {
|
|
136
|
+
const op = this.advance().value;
|
|
137
|
+
left = { kind: "binop", op, left, right: this.parseUnary() };
|
|
138
|
+
}
|
|
139
|
+
return left;
|
|
140
|
+
}
|
|
141
|
+
parseUnary() {
|
|
142
|
+
if (this.match("op", "!"))
|
|
143
|
+
return { kind: "unop", op: "!", expr: this.parseUnary() };
|
|
144
|
+
if (this.peek()?.type === "op" && this.peek().value === "-") {
|
|
145
|
+
const prev = this.pos > 0 ? this.tokens[this.pos - 1] : undefined;
|
|
146
|
+
if (!prev || prev.type === "op" || (prev.type === "punc" && prev.value !== ")")) {
|
|
147
|
+
this.advance();
|
|
148
|
+
return { kind: "unop", op: "-", expr: this.parseUnary() };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return this.parsePostfix();
|
|
152
|
+
}
|
|
153
|
+
parsePostfix() {
|
|
154
|
+
let expr = this.parseAtom();
|
|
155
|
+
while (true) {
|
|
156
|
+
if (this.match("punc", ".")) {
|
|
157
|
+
expr = { kind: "field", obj: expr, field: this.expect("ident").value };
|
|
158
|
+
}
|
|
159
|
+
else if (this.match("punc", "[")) {
|
|
160
|
+
const idx = this.parseImplies();
|
|
161
|
+
this.expect("punc", "]");
|
|
162
|
+
expr = { kind: "index", obj: expr, idx };
|
|
163
|
+
}
|
|
164
|
+
else if (this.match("punc", "(")) {
|
|
165
|
+
const args = [];
|
|
166
|
+
if (!this.match("punc", ")")) {
|
|
167
|
+
args.push(this.parseImplies());
|
|
168
|
+
while (this.match("punc", ","))
|
|
169
|
+
args.push(this.parseImplies());
|
|
170
|
+
this.expect("punc", ")");
|
|
171
|
+
}
|
|
172
|
+
expr = { kind: "call", fn: expr, args };
|
|
173
|
+
}
|
|
174
|
+
else
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
return expr;
|
|
178
|
+
}
|
|
179
|
+
parseAtom() {
|
|
180
|
+
const t = this.peek();
|
|
181
|
+
if (!t)
|
|
182
|
+
throw new Error("Unexpected end of expression");
|
|
183
|
+
if (t.type === "result") {
|
|
184
|
+
this.advance();
|
|
185
|
+
return { kind: "result" };
|
|
186
|
+
}
|
|
187
|
+
if (t.type === "num") {
|
|
188
|
+
this.advance();
|
|
189
|
+
return { kind: "num", value: t.value };
|
|
190
|
+
}
|
|
191
|
+
if (t.type === "str") {
|
|
192
|
+
this.advance();
|
|
193
|
+
return { kind: "str", value: t.value };
|
|
194
|
+
}
|
|
195
|
+
if (t.type === "ident") {
|
|
196
|
+
if (t.value === "true") {
|
|
197
|
+
this.advance();
|
|
198
|
+
return { kind: "bool", value: true };
|
|
199
|
+
}
|
|
200
|
+
if (t.value === "false") {
|
|
201
|
+
this.advance();
|
|
202
|
+
return { kind: "bool", value: false };
|
|
203
|
+
}
|
|
204
|
+
if (t.value === "forall" || t.value === "exists") {
|
|
205
|
+
const q = t.value;
|
|
206
|
+
this.advance();
|
|
207
|
+
this.expect("punc", "(");
|
|
208
|
+
const v = this.expect("ident").value;
|
|
209
|
+
let varType = "int";
|
|
210
|
+
if (this.match("punc", ":")) {
|
|
211
|
+
const ty = this.expect("ident").value;
|
|
212
|
+
if (ty !== "nat" && ty !== "int")
|
|
213
|
+
throw new Error(`Unknown type '${ty}'`);
|
|
214
|
+
varType = ty;
|
|
215
|
+
}
|
|
216
|
+
this.expect("punc", ",");
|
|
217
|
+
const body = this.parseImplies();
|
|
218
|
+
this.expect("punc", ")");
|
|
219
|
+
return { kind: q, var: v, varType, body };
|
|
220
|
+
}
|
|
221
|
+
this.advance();
|
|
222
|
+
return { kind: "var", name: t.value };
|
|
223
|
+
}
|
|
224
|
+
if (t.type === "punc" && t.value === "(") {
|
|
225
|
+
this.advance();
|
|
226
|
+
const expr = this.parseImplies();
|
|
227
|
+
this.expect("punc", ")");
|
|
228
|
+
return expr;
|
|
229
|
+
}
|
|
230
|
+
if (t.type === "punc" && t.value === "{") {
|
|
231
|
+
this.advance();
|
|
232
|
+
const fields = [];
|
|
233
|
+
if (!this.match("punc", "}")) {
|
|
234
|
+
const name = this.expect("ident").value;
|
|
235
|
+
this.expect("punc", ":");
|
|
236
|
+
fields.push({ name, value: this.parseImplies() });
|
|
237
|
+
while (this.match("punc", ",")) {
|
|
238
|
+
const n = this.expect("ident").value;
|
|
239
|
+
this.expect("punc", ":");
|
|
240
|
+
fields.push({ name: n, value: this.parseImplies() });
|
|
241
|
+
}
|
|
242
|
+
this.expect("punc", "}");
|
|
243
|
+
}
|
|
244
|
+
return { kind: "record", spread: null, fields };
|
|
245
|
+
}
|
|
246
|
+
throw new Error(`Unexpected: ${JSON.stringify(t)}`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
export function parseExpr(input) {
|
|
250
|
+
return new Parser(tokenize(input)).parse();
|
|
251
|
+
}
|