UNPKG

23 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const impossible_1 = require("@calculemus/impossible");
4function 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}
49exports.restrictType = restrictType;
50function 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}
271exports.restrictExpression = restrictExpression;
272function 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}
331exports.restrictLValue = restrictLValue;
332function 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}
509exports.restrictStatement = restrictStatement;
510function 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}
527function 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}
534function 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}
550function 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}
577exports.restrictDeclaration = restrictDeclaration;
578//# sourceMappingURL=restrictsyntax.js.map
\No newline at end of file