1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const impossible_1 = require("@calculemus/impossible");
|
4 | function restrictType(lang, syn) {
|
5 | switch (syn.tag) {
|
6 | case "IntType":
|
7 | return syn;
|
8 | case "BoolType":
|
9 | if (lang === "L1")
|
10 | throw new Error(`The type 'bool' is not a part of ${lang}`);
|
11 | return syn;
|
12 | case "StringType":
|
13 | if (lang === "L1" || lang === "L2" || lang === "L3" || lang === "L4")
|
14 | throw new Error(`The type 'string' is not a part of ${lang}`);
|
15 | return syn;
|
16 | case "CharType":
|
17 | if (lang === "L1" || lang === "L2" || lang === "L3" || lang === "L4")
|
18 | throw new Error(`The type 'char' is not a part of ${lang}`);
|
19 | case "VoidType":
|
20 | if (lang === "L1")
|
21 | throw new Error(`The type 'void' is not a part of ${lang}`);
|
22 | return syn;
|
23 | case "PointerType":
|
24 | if (lang === "L1" || lang === "L2" || lang === "L3")
|
25 | throw new Error(`Pointer types are not a part of ${lang}`);
|
26 | return {
|
27 | tag: "PointerType",
|
28 | argument: restrictType(lang, syn.argument)
|
29 | };
|
30 | case "ArrayType":
|
31 | if (lang === "L1" || lang === "L2" || lang === "L3")
|
32 | throw new Error(`Array types are not a part of ${lang}`);
|
33 | return {
|
34 | tag: "ArrayType",
|
35 | argument: restrictType(lang, syn.argument)
|
36 | };
|
37 | case "StructType":
|
38 | if (lang === "L1" || lang === "L2" || lang === "L3")
|
39 | throw new Error(`Struct types are not a part of ${lang}`);
|
40 | return syn;
|
41 | case "Identifier":
|
42 | if (lang === "L1" || lang === "L2")
|
43 | throw new Error(`Defined types are not a part of ${lang}`);
|
44 | return syn;
|
45 | default:
|
46 | return impossible_1.impossible(syn);
|
47 | }
|
48 | }
|
49 | exports.restrictType = restrictType;
|
50 | function restrictExpression(lang, syn) {
|
51 | switch (syn.tag) {
|
52 | case "StringLiteral": {
|
53 | if (lang === "L1" || lang === "L2" || lang === "L3" || lang === "L4")
|
54 | throw new Error(`String and char literals are not a part of ${lang}`);
|
55 | syn.raw.map(x => {
|
56 | if (x.length === 2 && x[0] === "\\") {
|
57 | if (!x.match(/\\[ntvbrfa\\'"]/))
|
58 | throw new Error(`Invalid escape '${x}' in string`);
|
59 | }
|
60 | else if (!x.match(/\\[ntvbrfa\\'"]+/)) {
|
61 | if (!x.match(/[ !#-~]+/))
|
62 | throw new Error(`Invalid character in string '${x}'`);
|
63 | }
|
64 | });
|
65 | return {
|
66 | tag: "StringLiteral",
|
67 | value: syn.raw.join(""),
|
68 | raw: `"${syn.raw.join("")}"`
|
69 | };
|
70 | }
|
71 | case "CharLiteral": {
|
72 | if (lang === "L1" || lang === "L2" || lang === "L3" || lang === "L4")
|
73 | throw new Error(`String and char literals are not a part of ${lang}`);
|
74 | if (syn.raw.length === 1) {
|
75 | if (!syn.raw.match(/[ !#-~]/))
|
76 | throw new Error(`Invalid character '${syn.raw}'`);
|
77 | }
|
78 | else {
|
79 | if (!syn.raw.match(/\\[ntvbrfa\\'"0]/))
|
80 | throw new Error(`Invalid escape character '${syn.raw}'`);
|
81 | }
|
82 | return {
|
83 | tag: "CharLiteral",
|
84 | value: syn.raw,
|
85 | raw: `'${syn.raw}'`
|
86 | };
|
87 | }
|
88 | case "BoolLiteral":
|
89 | if (lang === "L1")
|
90 | throw new Error(`Boolean literals 'true' and 'false' are not part of ${lang}`);
|
91 | case "NullLiteral":
|
92 | if (lang === "L1" || lang === "L2" || lang === "L3")
|
93 | throw new Error(`'NULL' is not a part of ${lang}`);
|
94 | case "Identifier":
|
95 | return syn;
|
96 | case "IntLiteral":
|
97 | if (syn.raw.startsWith("0x") || syn.raw.startsWith("0X")) {
|
98 | const hex = syn.raw.substring(2);
|
99 | if (hex.length > 8)
|
100 | throw new Error(`Hex constant too large: ${syn.raw}`);
|
101 | return {
|
102 | tag: "IntLiteral",
|
103 | raw: syn.raw,
|
104 | value: parseInt(hex, 16)
|
105 | };
|
106 | }
|
107 | else {
|
108 | if (syn.raw.length > 10)
|
109 | throw new Error(`Decimal constant too large: ${syn.raw}`);
|
110 | const dec = parseInt(syn.raw, 10);
|
111 | if (dec > 2147483648)
|
112 | throw new Error(`Decimal constant too large: ${syn.raw}`);
|
113 | return {
|
114 | tag: "IntLiteral",
|
115 | raw: syn.raw,
|
116 | value: dec
|
117 | };
|
118 | }
|
119 | case "ArrayMemberExpression": {
|
120 | if (lang === "L1" || lang === "L2" || lang === "L3")
|
121 | throw new Error(`Array access is not a part of ${lang}`);
|
122 | return {
|
123 | tag: "ArrayMemberExpression",
|
124 | object: restrictExpression(lang, syn.object),
|
125 | index: restrictExpression(lang, syn.index)
|
126 | };
|
127 | }
|
128 | case "StructMemberExpression": {
|
129 | if (lang === "L1" || lang === "L2" || lang === "L3")
|
130 | throw new Error(`Struct access is not a part of ${lang}`);
|
131 | return {
|
132 | tag: "StructMemberExpression",
|
133 | deref: syn.deref,
|
134 | object: restrictExpression(lang, syn.object),
|
135 | field: syn.field
|
136 | };
|
137 | }
|
138 | case "CallExpression": {
|
139 | if (lang === "L1" || lang === "L2")
|
140 | throw new Error(`Functions are not a part of ${lang}`);
|
141 | return {
|
142 | tag: "CallExpression",
|
143 | callee: syn.callee,
|
144 | arguments: syn.arguments.map(x => restrictExpression(lang, x))
|
145 | };
|
146 | }
|
147 | case "IndirectCallExpression": {
|
148 | if (lang !== "C1")
|
149 | throw new Error(`Calls from function pointers not a part of ${lang}`);
|
150 | return {
|
151 | tag: "IndirectCallExpression",
|
152 | callee: restrictExpression(lang, syn.callee),
|
153 | arguments: syn.arguments.map(x => restrictExpression(lang, x))
|
154 | };
|
155 | }
|
156 | case "CastExpression": {
|
157 | if (lang !== "C1")
|
158 | throw new Error(`Casts not a part of ${lang}`);
|
159 | return {
|
160 | tag: "CastExpression",
|
161 | kind: restrictType(lang, syn.kind),
|
162 | argument: restrictExpression(lang, syn.argument)
|
163 | };
|
164 | }
|
165 | case "UnaryExpression": {
|
166 | if (syn.operator === "&" && lang !== "C1")
|
167 | throw new Error(`Address-of not a part of ${lang}`);
|
168 | if (syn.operator === "!" && lang === "L1")
|
169 | throw new Error(`Boolean negation not a part of ${lang}`);
|
170 | if (syn.operator === "*" && (lang === "L1" || lang === "L2" || lang === "L3"))
|
171 | throw new Error(`Pointer dereference not a part of ${lang}`);
|
172 | return {
|
173 | tag: "UnaryExpression",
|
174 | operator: syn.operator,
|
175 | argument: restrictExpression(lang, syn.argument)
|
176 | };
|
177 | }
|
178 | case "BinaryExpression": {
|
179 | if (lang === "L1") {
|
180 | switch (syn.operator) {
|
181 | case "*":
|
182 | case "/":
|
183 | case "%":
|
184 | case "+":
|
185 | case "-":
|
186 | break;
|
187 | default:
|
188 | throw new Error(`Operator ${syn.operator} not a part of ${lang}`);
|
189 | }
|
190 | }
|
191 | return {
|
192 | tag: "BinaryExpression",
|
193 | operator: syn.operator,
|
194 | left: restrictExpression(lang, syn.left),
|
195 | right: restrictExpression(lang, syn.right)
|
196 | };
|
197 | }
|
198 | case "LogicalExpression": {
|
199 | if (lang === "L1")
|
200 | throw new Error(`Logical operators not a part of ${lang}`);
|
201 | return {
|
202 | tag: "LogicalExpression",
|
203 | operator: syn.operator,
|
204 | left: restrictExpression(lang, syn.left),
|
205 | right: restrictExpression(lang, syn.right)
|
206 | };
|
207 | }
|
208 | case "ConditionalExpression": {
|
209 | if (lang === "L1")
|
210 | throw new Error(`Conditional expression is not a part of ${lang}`);
|
211 | return {
|
212 | tag: "ConditionalExpression",
|
213 | test: restrictExpression(lang, syn.test),
|
214 | consequent: restrictExpression(lang, syn.consequent),
|
215 | alternate: restrictExpression(lang, syn.alternate)
|
216 | };
|
217 | }
|
218 | case "AllocExpression": {
|
219 | if (lang === "L1" || lang === "L2" || lang === "L3")
|
220 | throw new Error(`Allocation not a part of ${lang}`);
|
221 | return {
|
222 | tag: "AllocExpression",
|
223 | kind: restrictType(lang, syn.kind)
|
224 | };
|
225 | }
|
226 | case "AllocArrayExpression": {
|
227 | if (lang === "L1" || lang === "L2" || lang === "L3")
|
228 | throw new Error(`Allocation not a part of ${lang}`);
|
229 | return {
|
230 | tag: "AllocArrayExpression",
|
231 | kind: restrictType(lang, syn.kind),
|
232 | size: restrictExpression(lang, syn.size)
|
233 | };
|
234 | }
|
235 | case "ResultExpression": {
|
236 | if (lang === "L1" || lang === "L2" || lang === "L3" || lang === "L4")
|
237 | throw new Error(`Contracts not a part of ${lang}`);
|
238 | return {
|
239 | tag: "ResultExpression"
|
240 | };
|
241 | }
|
242 | case "LengthExpression": {
|
243 | if (lang === "L1" || lang === "L2" || lang === "L3" || lang === "L4")
|
244 | throw new Error(`Contracts not a part of ${lang}`);
|
245 | return {
|
246 | tag: "LengthExpression",
|
247 | argument: restrictExpression(lang, syn.argument)
|
248 | };
|
249 | }
|
250 | case "HasTagExpression": {
|
251 | if (lang !== "C1")
|
252 | throw new Error(`Tag contracts not a part of ${lang}`);
|
253 | return {
|
254 | tag: "HasTagExpression",
|
255 | kind: restrictType(lang, syn.kind),
|
256 | argument: restrictExpression(lang, syn.argument)
|
257 | };
|
258 | }
|
259 | case "AssignmentExpression":
|
260 | throw new Error(`Assignments 'x ${syn.operator} e2' must be used as statements, and not inside of expressions.`);
|
261 | case "UpdateExpression":
|
262 | throw new Error(`Increment/decrement operations 'e${syn.operator}' must be used as statements, and not inside of expressions.`);
|
263 | case "AssertExpression":
|
264 | throw new Error(`The 'assert()' function must be used as a statement, and not inside of expressions.`);
|
265 | case "ErrorExpression":
|
266 | throw new Error(`The 'error()' function must be used as a statement, and not inside of expressions.`);
|
267 | default:
|
268 | return impossible_1.impossible(syn);
|
269 | }
|
270 | }
|
271 | exports.restrictExpression = restrictExpression;
|
272 | function restrictLValue(lang, syn) {
|
273 | switch (syn.tag) {
|
274 | case "Identifier":
|
275 | return syn;
|
276 | case "StructMemberExpression": {
|
277 | if (lang === "L1" || lang === "L2" || lang === "L3")
|
278 | throw new Error(`Struct access not a part of ${lang}`);
|
279 | return {
|
280 | tag: "StructMemberExpression",
|
281 | deref: syn.deref,
|
282 | object: restrictLValue(lang, syn.object),
|
283 | field: syn.field
|
284 | };
|
285 | }
|
286 | case "UnaryExpression": {
|
287 | if (syn.operator !== "*")
|
288 | throw new Error(`Not an LValue`);
|
289 | if (lang == "L1" || lang === "L2" || lang === "L3")
|
290 | throw new Error(`Pointer dereference not a part of ${lang}`);
|
291 | return {
|
292 | tag: "UnaryExpression",
|
293 | operator: "*",
|
294 | argument: restrictLValue(lang, syn.argument)
|
295 | };
|
296 | }
|
297 | case "ArrayMemberExpression": {
|
298 | if (lang === "L1" || lang === "L2" || lang === "L3")
|
299 | throw new Error(`Array access not a part of ${lang}`);
|
300 | return {
|
301 | tag: "ArrayMemberExpression",
|
302 | object: restrictLValue(lang, syn.object),
|
303 | index: restrictExpression(lang, syn.index)
|
304 | };
|
305 | }
|
306 | case "IntLiteral":
|
307 | case "StringLiteral":
|
308 | case "CharLiteral":
|
309 | case "BoolLiteral":
|
310 | case "NullLiteral":
|
311 | case "CallExpression":
|
312 | case "IndirectCallExpression":
|
313 | case "CastExpression":
|
314 | case "BinaryExpression":
|
315 | case "LogicalExpression":
|
316 | case "ConditionalExpression":
|
317 | case "AllocExpression":
|
318 | case "AllocArrayExpression":
|
319 | case "ResultExpression":
|
320 | case "LengthExpression":
|
321 | case "HasTagExpression":
|
322 | case "UpdateExpression":
|
323 | case "AssignmentExpression":
|
324 | case "AssertExpression":
|
325 | case "ErrorExpression":
|
326 | throw new Error(`Not a valid LValue ${JSON.stringify(syn)}`);
|
327 | default:
|
328 | return impossible_1.impossible(syn);
|
329 | }
|
330 | }
|
331 | exports.restrictLValue = restrictLValue;
|
332 | function restrictStatement(lang, syn) {
|
333 | switch (syn.tag) {
|
334 | case "AnnoStatement": {
|
335 | if (syn.anno.tag !== "assert")
|
336 | throw new Error(`Only assert annotations are allowed here, ${syn.anno.tag} is not permitted.`);
|
337 | return {
|
338 | tag: "AssertStatement",
|
339 | contract: true,
|
340 | test: restrictExpression(lang, syn.anno.test)
|
341 | };
|
342 | }
|
343 | case "ExpressionStatement": {
|
344 | switch (syn.expression.tag) {
|
345 | case "AssignmentExpression": {
|
346 | if (lang === "L1") {
|
347 | switch (syn.expression.operator) {
|
348 | case "=":
|
349 | case "*=":
|
350 | case "/=":
|
351 | case "%=":
|
352 | case "+=":
|
353 | case "-=":
|
354 | break;
|
355 | default:
|
356 | throw new Error(`Assignment operator ${syn.expression.operator} not a part of ${lang}`);
|
357 | }
|
358 | }
|
359 | return {
|
360 | tag: "AssignmentStatement",
|
361 | operator: syn.expression.operator,
|
362 | left: restrictLValue(lang, syn.expression.left),
|
363 | right: restrictExpression(lang, syn.expression.right)
|
364 | };
|
365 | }
|
366 | case "UpdateExpression": {
|
367 | if (lang === "L1")
|
368 | throw new Error(`Postfix update 'x${syn.expression.operator}' not a part of ${lang}`);
|
369 | return {
|
370 | tag: "UpdateStatement",
|
371 | operator: syn.expression.operator,
|
372 | argument: restrictExpression(lang, syn.expression.argument)
|
373 | };
|
374 | }
|
375 | case "AssertExpression": {
|
376 | if (lang === "L1" || lang === "L2" || lang === "L3" || lang === "L4") {
|
377 | throw new Error(`Assertions not a part of ${lang}`);
|
378 | }
|
379 | return {
|
380 | tag: "AssertStatement",
|
381 | contract: false,
|
382 | test: restrictExpression(lang, syn.expression.test)
|
383 | };
|
384 | }
|
385 | case "ErrorExpression": {
|
386 | if (lang === "L1" || lang === "L2" || lang === "L3" || lang === "L4") {
|
387 | throw new Error(`The 'error()' function is not a part of ${lang}`);
|
388 | }
|
389 | return {
|
390 | tag: "ErrorStatement",
|
391 | argument: restrictExpression(lang, syn.expression.argument)
|
392 | };
|
393 | }
|
394 | default: {
|
395 | return {
|
396 | tag: "ExpressionStatement",
|
397 | expression: restrictExpression(lang, syn.expression)
|
398 | };
|
399 | }
|
400 | }
|
401 | }
|
402 | case "VariableDeclaration": {
|
403 | return {
|
404 | tag: "VariableDeclaration",
|
405 | kind: restrictType(lang, syn.kind),
|
406 | id: syn.id,
|
407 | init: syn.init ? restrictExpression(lang, syn.init) : null
|
408 | };
|
409 | }
|
410 | case "IfStatement": {
|
411 | if (lang === "L1")
|
412 | throw new Error(`Conditionals not a part of ${lang}`);
|
413 | if (!syn.alternate) {
|
414 | return {
|
415 | tag: "IfStatement",
|
416 | test: restrictExpression(lang, syn.test),
|
417 | consequent: restrictAssert(lang, syn.consequent)
|
418 | };
|
419 | }
|
420 | else {
|
421 | return {
|
422 | tag: "IfStatement",
|
423 | test: restrictExpression(lang, syn.test),
|
424 | consequent: restrictAssert(lang, syn.consequent),
|
425 | alternate: restrictAssert(lang, syn.consequent)
|
426 | };
|
427 | }
|
428 | }
|
429 | case "WhileStatement": {
|
430 | if (lang === "L1")
|
431 | throw new Error(`Loops not a part of ${lang}`);
|
432 | return {
|
433 | tag: "WhileStatement",
|
434 | invariants: restrictLoopInvariants(lang, syn.annos),
|
435 | test: restrictExpression(lang, syn.test),
|
436 | body: restrictStatement(lang, syn.body)
|
437 | };
|
438 | }
|
439 | case "ForStatement": {
|
440 | if (lang === "L1")
|
441 | throw new Error(`Loops not a part of ${lang}`);
|
442 | let init;
|
443 | let update;
|
444 | if (syn.init === null) {
|
445 | init = null;
|
446 | }
|
447 | else {
|
448 | const candidate = restrictStatement(lang, syn.init);
|
449 | switch (candidate.tag) {
|
450 | case "AssignmentStatement":
|
451 | case "UpdateStatement":
|
452 | case "ExpressionStatement":
|
453 | case "VariableDeclaration":
|
454 | init = candidate;
|
455 | break;
|
456 | default:
|
457 | throw new Error(`A ${candidate.tag} is not allowed as the first argument of a for statement`);
|
458 | }
|
459 | }
|
460 | if (syn.update === null) {
|
461 | update = null;
|
462 | }
|
463 | else {
|
464 | const candidate = restrictStatement(lang, {
|
465 | tag: "ExpressionStatement",
|
466 | expression: syn.update
|
467 | });
|
468 | switch (candidate.tag) {
|
469 | case "AssignmentStatement":
|
470 | case "UpdateStatement":
|
471 | case "ExpressionStatement":
|
472 | update = candidate;
|
473 | break;
|
474 | default:
|
475 | throw new Error(`A ${candidate.tag} is not allowed as the third argument of a for statement`);
|
476 | }
|
477 | }
|
478 | return {
|
479 | tag: "ForStatement",
|
480 | invariants: restrictLoopInvariants(lang, syn.annos),
|
481 | init: init,
|
482 | test: restrictExpression(lang, syn.test),
|
483 | update: update,
|
484 | body: restrictStatement(lang, syn.body)
|
485 | };
|
486 | }
|
487 | case "ReturnStatement": {
|
488 | return {
|
489 | tag: "ReturnStatement",
|
490 | argument: syn.argument ? restrictExpression(lang, syn.argument) : null
|
491 | };
|
492 | }
|
493 | case "BlockStatement": {
|
494 | return {
|
495 | tag: "BlockStatement",
|
496 | body: syn.body.map(x => restrictStatement(lang, x))
|
497 | };
|
498 | }
|
499 | case "BreakStatement":
|
500 | case "ContinueStatement": {
|
501 | if (lang !== "C1")
|
502 | throw new Error(`Control with 'break' and 'continue' not a part of ${lang}`);
|
503 | return syn;
|
504 | }
|
505 | default:
|
506 | return impossible_1.impossible(syn);
|
507 | }
|
508 | }
|
509 | exports.restrictStatement = restrictStatement;
|
510 | function restrictAssert(lang, [annos, stm]) {
|
511 | if (annos.length === 0)
|
512 | return restrictStatement(lang, stm);
|
513 | const asserts = annos.map((x) => {
|
514 | if (x.tag !== "assert")
|
515 | throw new Error(`The only annotations allowed with if-statements are assertions, ${x.tag} is not permitted`);
|
516 | return {
|
517 | tag: "AssertStatement",
|
518 | contract: true,
|
519 | test: restrictExpression(lang, x.test)
|
520 | };
|
521 | });
|
522 | return {
|
523 | tag: "BlockStatement",
|
524 | body: asserts.concat([restrictStatement(lang, stm)])
|
525 | };
|
526 | }
|
527 | function restrictLoopInvariants(lang, annos) {
|
528 | return annos.map(x => {
|
529 | if (x.tag !== "loop_invariant")
|
530 | throw new Error(`The only annotations allowed are loop invariants, ${x.tag} is not permitted`);
|
531 | return restrictExpression(lang, x.test);
|
532 | });
|
533 | }
|
534 | function restrictFunctionAnnos(lang, annos) {
|
535 | const preconditions = [];
|
536 | const postconditions = [];
|
537 | annos.map(x => {
|
538 | if (x.tag === "requires") {
|
539 | preconditions.push(restrictExpression(lang, x.test));
|
540 | }
|
541 | else if (x.tag === "ensures") {
|
542 | postconditions.push(restrictExpression(lang, x.test));
|
543 | }
|
544 | else {
|
545 | throw new Error(`The only annotations allowed are requires and ensures, ${x.tag} is not permitted`);
|
546 | }
|
547 | });
|
548 | return { pre: preconditions, post: postconditions };
|
549 | }
|
550 | function restrictDeclaration(lang, decl) {
|
551 | if (typeof decl === "string")
|
552 | return decl;
|
553 | switch (decl.tag) {
|
554 | case "FunctionDeclaration": {
|
555 | const annos = restrictFunctionAnnos(lang, decl.annos);
|
556 | return {
|
557 | tag: "FunctionDeclaration",
|
558 | returns: restrictType(lang, decl.returns),
|
559 | id: decl.id,
|
560 | params: decl.params,
|
561 | preconditions: annos.pre,
|
562 | postconditions: annos.post,
|
563 | body: decl.body === null
|
564 | ? null
|
565 | : {
|
566 | tag: "BlockStatement",
|
567 | body: decl.body.body.map(x => restrictStatement(lang, x))
|
568 | }
|
569 | };
|
570 | }
|
571 | case "BlockStatement":
|
572 | throw new Error("Fake case");
|
573 | default:
|
574 | return impossible_1.impossible(decl);
|
575 | }
|
576 | }
|
577 | exports.restrictDeclaration = restrictDeclaration;
|
578 |
|
\ | No newline at end of file |