How to use evalResult method in apickli

Best JavaScript code snippet using apickli

evaluator.test.ts

Source:evaluator.test.ts Github

copy

Full Screen

1import "jest";2import { isRight, isLeft } from "fp-ts/lib/Either";3import { evaluateProgram, NativeFunctionImplementations } from "../src/evaluator";4import { NATIVE_MODULE_NAME, Value } from "../src/evaluator_types";5import { Identifier, identifierIso } from "../src/universal_types";6import { Block, Module } from "../src/parser_types";7const testModuleName = identifierIso.wrap("Main");8const wrapBlock = (block: Block): Array<Module> => {9 return [10 {11 name: testModuleName,12 body: block,13 exports: [],14 },15 ];16};17const nativeFunctionsTestImplementations: NativeFunctionImplementations = {18 clock: () => 0,19 // eslint-disable-next-line @typescript-eslint/no-empty-function20 print: () => {},21 parseNum: () => new Map<Identifier, Value>(),22 readString: () => "",23};24const evaluateTestProgram = evaluateProgram(nativeFunctionsTestImplementations);25describe("Evaluator", () => {26 describe("Successful evaluations", () => {27 describe("Simple programs with no functions or variables", () => {28 it("Evaluates { return 1; } to 1 (evaluating numeric literals)", () => {29 // Arrange30 const ast: Block = [31 {32 statementKind: "return",33 returnedValue: {34 expressionKind: "numberLit",35 value: 1,36 },37 },38 ];39 // Act40 const evalResult = evaluateTestProgram(wrapBlock(ast));41 // Assert42 if (!isRight(evalResult)) {43 throw new Error("Evaluation failed, should have succeeded");44 }45 if (evalResult.right.valueKind !== "number") {46 throw new Error("Evaluated to non-numeric value");47 }48 expect(evalResult.right.value).toBe(1);49 });50 it("Evaluates { return 2; } to 2 (evaluating numeric literals)", () => {51 // Arrange52 const ast: Block = [53 {54 statementKind: "return",55 returnedValue: {56 expressionKind: "numberLit",57 value: 2,58 },59 },60 ];61 // Act62 const evalResult = evaluateTestProgram(wrapBlock(ast));63 // Assert64 if (!isRight(evalResult)) {65 throw new Error("Evaluation failed, should have succeeded");66 }67 if (evalResult.right.valueKind !== "number") {68 throw new Error("Evaluated to non-numeric value");69 }70 expect(evalResult.right.value).toBe(2);71 });72 it("Evaluates { return true; } to true (evaluating boolean literals)", () => {73 // Arrange74 const ast: Block = [75 {76 statementKind: "return",77 returnedValue: {78 expressionKind: "booleanLit",79 isTrue: true,80 },81 },82 ];83 // Act84 const evalResult = evaluateTestProgram(wrapBlock(ast));85 // Assert86 if (!isRight(evalResult)) {87 throw new Error("Evaluation failed, should have succeeded");88 }89 if (evalResult.right.valueKind !== "boolean") {90 throw new Error("Evaluated to non-boolean value");91 }92 expect(evalResult.right.isTrue).toBe(true);93 });94 it("Evaluates { return false; } to false (evaluating boolean literals)", () => {95 // Arrange96 const ast: Block = [97 {98 statementKind: "return",99 returnedValue: {100 expressionKind: "booleanLit",101 isTrue: false,102 },103 },104 ];105 // Act106 const evalResult = evaluateTestProgram(wrapBlock(ast));107 // Assert108 if (!isRight(evalResult)) {109 throw new Error("Evaluation failed, should have succeeded");110 }111 if (evalResult.right.valueKind !== "boolean") {112 throw new Error("Evaluated to non-boolean value");113 }114 expect(evalResult.right.isTrue).toBe(false);115 });116 it("Evaluates { return null; } to null (evaluating null literals)", () => {117 // Arrange118 const ast: Block = [119 {120 statementKind: "return",121 returnedValue: {122 expressionKind: "nullLit",123 },124 },125 ];126 // Act127 const evalResult = evaluateTestProgram(wrapBlock(ast));128 // Assert129 if (!isRight(evalResult)) {130 throw new Error("Evaluation failed, should have succeeded");131 }132 expect(evalResult.right.valueKind).toBe("null");133 });134 it('Evaluates { return "test" } to "test" (evaluating string literals)', () => {135 // Arrange136 const ast: Block = [137 {138 statementKind: "return",139 returnedValue: {140 expressionKind: "stringLit",141 value: "test",142 },143 },144 ];145 // Act146 const evalResult = evaluateTestProgram(wrapBlock(ast));147 // Assert148 if (!isRight(evalResult)) {149 throw new Error("Evaluation failed, should have succeeded");150 }151 if (evalResult.right.valueKind !== "string") {152 throw new Error("Evaluated to non-string value");153 }154 expect(evalResult.right.value).toBe("test");155 });156 it("Evaluates { return 1 + 2; } to 3 (evaluating addition)", () => {157 // Arrange158 const ast: Block = [159 {160 statementKind: "return",161 returnedValue: {162 expressionKind: "binOp",163 binOp: "add",164 leftOperand: {165 expressionKind: "numberLit",166 value: 1,167 },168 rightOperand: {169 expressionKind: "numberLit",170 value: 2,171 },172 },173 },174 ];175 // Act176 const evalResult = evaluateTestProgram(wrapBlock(ast));177 // Assert178 if (!isRight(evalResult)) {179 throw new Error("Evaluation failed, should have succeeded");180 }181 if (evalResult.right.valueKind !== "number") {182 throw new Error("Evaluated to non-numeric value");183 }184 expect(evalResult.right.value).toBe(3);185 });186 it("Evaluates { return 3 - 4; } to -1 (evaluating subtraction)", () => {187 // Arrange188 const ast: Block = [189 {190 statementKind: "return",191 returnedValue: {192 expressionKind: "binOp",193 binOp: "subtract",194 leftOperand: {195 expressionKind: "numberLit",196 value: 3,197 },198 rightOperand: {199 expressionKind: "numberLit",200 value: 4,201 },202 },203 },204 ];205 // Act206 const evalResult = evaluateTestProgram(wrapBlock(ast));207 // Assert208 if (!isRight(evalResult)) {209 throw new Error("Evaluation failed, should have succeeded");210 }211 if (evalResult.right.valueKind !== "number") {212 throw new Error("Evaluated to non-numeric value");213 }214 expect(evalResult.right.value).toBe(-1);215 });216 it("Evaluates { return 5 * 6; } to 30 (evaluating multiplication)", () => {217 // Arrange218 const ast: Block = [219 {220 statementKind: "return",221 returnedValue: {222 expressionKind: "binOp",223 binOp: "multiply",224 leftOperand: {225 expressionKind: "numberLit",226 value: 5,227 },228 rightOperand: {229 expressionKind: "numberLit",230 value: 6,231 },232 },233 },234 ];235 // Act236 const evalResult = evaluateTestProgram(wrapBlock(ast));237 // Assert238 if (!isRight(evalResult)) {239 throw new Error("Evaluation failed, should have succeeded");240 }241 if (evalResult.right.valueKind !== "number") {242 throw new Error("Evaluated to non-numeric value");243 }244 expect(evalResult.right.value).toBe(30);245 });246 it("Evaluates { return 8 / 2; } to 4 (evaluating division)", () => {247 // Arrange248 const ast: Block = [249 {250 statementKind: "return",251 returnedValue: {252 expressionKind: "binOp",253 binOp: "divide",254 leftOperand: {255 expressionKind: "numberLit",256 value: 8,257 },258 rightOperand: {259 expressionKind: "numberLit",260 value: 2,261 },262 },263 },264 ];265 // Act266 const evalResult = evaluateTestProgram(wrapBlock(ast));267 // Assert268 if (!isRight(evalResult)) {269 throw new Error("Evaluation failed, should have succeeded");270 }271 if (evalResult.right.valueKind !== "number") {272 throw new Error("Evaluated to non-numeric value");273 }274 expect(evalResult.right.value).toBe(4);275 });276 it("Evaluates { return -1; } to -1 (evaluating unary negation)", () => {277 // Arrange278 const ast: Block = [279 {280 statementKind: "return",281 returnedValue: {282 expressionKind: "unaryOp",283 unaryOp: "negative",284 operand: {285 expressionKind: "numberLit",286 value: 1,287 },288 },289 },290 ];291 // Act292 const evalResult = evaluateTestProgram(wrapBlock(ast));293 // Assert294 if (!isRight(evalResult)) {295 throw new Error("Evaluation failed, should have succeeded");296 }297 if (evalResult.right.valueKind !== "number") {298 throw new Error("Evaluated to non-numeric value");299 }300 expect(evalResult.right.value).toBe(-1);301 });302 it("Evaluates { return 2 - -3; } to 5 (evaluating unary negation as part of a larger expression)", () => {303 // Arrange304 const ast: Block = [305 {306 statementKind: "return",307 returnedValue: {308 expressionKind: "binOp",309 binOp: "subtract",310 leftOperand: {311 expressionKind: "numberLit",312 value: 2,313 },314 rightOperand: {315 expressionKind: "unaryOp",316 unaryOp: "negative",317 operand: {318 expressionKind: "numberLit",319 value: 3,320 },321 },322 },323 },324 ];325 // Act326 const evalResult = evaluateTestProgram(wrapBlock(ast));327 // Assert328 if (!isRight(evalResult)) {329 throw new Error("Evaluation failed, should have succeeded");330 }331 if (evalResult.right.valueKind !== "number") {332 throw new Error("Evaluated to non-numeric value");333 }334 expect(evalResult.right.value).toBe(5);335 });336 it("Evaluates { return 1 + 2 + 3; } to 6 (evaluating multiple additions in one expression)", () => {337 // Arrange338 const ast: Block = [339 {340 statementKind: "return",341 returnedValue: {342 expressionKind: "binOp",343 binOp: "add",344 leftOperand: {345 expressionKind: "binOp",346 binOp: "add",347 leftOperand: {348 expressionKind: "numberLit",349 value: 1,350 },351 rightOperand: {352 expressionKind: "numberLit",353 value: 2,354 },355 },356 rightOperand: {357 expressionKind: "numberLit",358 value: 3,359 },360 },361 },362 ];363 // Act364 const evalResult = evaluateTestProgram(wrapBlock(ast));365 // Assert366 if (!isRight(evalResult)) {367 throw new Error("Evaluation failed, should have succeeded");368 }369 if (evalResult.right.valueKind !== "number") {370 throw new Error("Evaluated to non-numeric value");371 }372 expect(evalResult.right.value).toBe(6);373 });374 it("Evaluates { return 4 + 5 * 6; } to 34 (evaluating expressions with different precedence)", () => {375 // Arrange376 const ast: Block = [377 {378 statementKind: "return",379 returnedValue: {380 expressionKind: "binOp",381 binOp: "add",382 leftOperand: {383 expressionKind: "numberLit",384 value: 4,385 },386 rightOperand: {387 expressionKind: "binOp",388 binOp: "multiply",389 leftOperand: {390 expressionKind: "numberLit",391 value: 5,392 },393 rightOperand: {394 expressionKind: "numberLit",395 value: 6,396 },397 },398 },399 },400 ];401 // Act402 const evalResult = evaluateTestProgram(wrapBlock(ast));403 // Assert404 if (!isRight(evalResult)) {405 throw new Error("Evaluation failed, should have succeeded");406 }407 if (evalResult.right.valueKind !== "number") {408 throw new Error("Evaluated to non-numeric value");409 }410 expect(evalResult.right.value).toBe(34);411 });412 it("Evaluates { return 7 * 8 - 9; } to 47 (evaluating expressions with different precedence)", () => {413 // Arrange414 const ast: Block = [415 {416 statementKind: "return",417 returnedValue: {418 expressionKind: "binOp",419 binOp: "subtract",420 leftOperand: {421 expressionKind: "binOp",422 binOp: "multiply",423 leftOperand: {424 expressionKind: "numberLit",425 value: 7,426 },427 rightOperand: {428 expressionKind: "numberLit",429 value: 8,430 },431 },432 rightOperand: {433 expressionKind: "numberLit",434 value: 9,435 },436 },437 },438 ];439 // Act440 const evalResult = evaluateTestProgram(wrapBlock(ast));441 // Assert442 if (!isRight(evalResult)) {443 throw new Error("Evaluation failed, should have succeeded");444 }445 if (evalResult.right.valueKind !== "number") {446 throw new Error("Evaluated to non-numeric value");447 }448 expect(evalResult.right.value).toBe(47);449 });450 it("Evaluates { return true & false; } to false (evaluating logical and)", () => {451 // Arrange452 const ast: Block = [453 {454 statementKind: "return",455 returnedValue: {456 expressionKind: "binOp",457 binOp: "and",458 leftOperand: {459 expressionKind: "booleanLit",460 isTrue: true,461 },462 rightOperand: {463 expressionKind: "booleanLit",464 isTrue: false,465 },466 },467 },468 ];469 // Act470 const evalResult = evaluateTestProgram(wrapBlock(ast));471 // Assert472 if (!isRight(evalResult)) {473 throw new Error("Evaluation failed, should have succeeded");474 }475 if (evalResult.right.valueKind !== "boolean") {476 throw new Error("Evaluated to non-boolean value");477 }478 expect(evalResult.right.isTrue).toBe(false);479 });480 it("Evaluates { return false | true; } to true (evaluating logical or)", () => {481 // Arrange482 const ast: Block = [483 {484 statementKind: "return",485 returnedValue: {486 expressionKind: "binOp",487 binOp: "or",488 leftOperand: {489 expressionKind: "booleanLit",490 isTrue: false,491 },492 rightOperand: {493 expressionKind: "booleanLit",494 isTrue: true,495 },496 },497 },498 ];499 // Act500 const evalResult = evaluateTestProgram(wrapBlock(ast));501 // Assert502 if (!isRight(evalResult)) {503 throw new Error("Evaluation failed, should have succeeded");504 }505 if (evalResult.right.valueKind !== "boolean") {506 throw new Error("Evaluated to non-boolean value");507 }508 expect(evalResult.right.isTrue).toBe(true);509 });510 it("Evaluates { return !true; } to false (evaluating logical not)", () => {511 // Arrange512 const ast: Block = [513 {514 statementKind: "return",515 returnedValue: {516 expressionKind: "unaryOp",517 unaryOp: "not",518 operand: {519 expressionKind: "booleanLit",520 isTrue: true,521 },522 },523 },524 ];525 // Act526 const evalResult = evaluateTestProgram(wrapBlock(ast));527 // Assert528 if (!isRight(evalResult)) {529 throw new Error("Evaluation failed, should have succeeded");530 }531 if (evalResult.right.valueKind !== "boolean") {532 throw new Error("Evaluated to non-boolean value");533 }534 expect(evalResult.right.isTrue).toBe(false);535 });536 it("Evaluates { return true | true & false } to true (evaluating logical expressions with correct precedence)", () => {537 // Arrange538 const ast: Block = [539 {540 statementKind: "return",541 returnedValue: {542 expressionKind: "binOp",543 binOp: "or",544 leftOperand: {545 expressionKind: "booleanLit",546 isTrue: true,547 },548 rightOperand: {549 expressionKind: "binOp",550 binOp: "and",551 leftOperand: {552 expressionKind: "booleanLit",553 isTrue: true,554 },555 rightOperand: {556 expressionKind: "booleanLit",557 isTrue: false,558 },559 },560 },561 },562 ];563 // Act564 const evalResult = evaluateTestProgram(wrapBlock(ast));565 // Assert566 if (!isRight(evalResult)) {567 throw new Error("Evaluation failed, should have succeeded");568 }569 if (evalResult.right.valueKind !== "boolean") {570 throw new Error("Evaluated to non-boolean value");571 }572 expect(evalResult.right.isTrue).toBe(true);573 });574 it("Evaluates { return 1 < 2; } to true (evaluating less-than operator)", () => {575 // Arrange576 const ast: Block = [577 {578 statementKind: "return",579 returnedValue: {580 expressionKind: "binOp",581 binOp: "lessThan",582 leftOperand: {583 expressionKind: "numberLit",584 value: 1,585 },586 rightOperand: {587 expressionKind: "numberLit",588 value: 2,589 },590 },591 },592 ];593 // Act594 const evalResult = evaluateTestProgram(wrapBlock(ast));595 // Assert596 if (!isRight(evalResult)) {597 throw new Error("Evaluation failed, should have succeeded");598 }599 if (evalResult.right.valueKind !== "boolean") {600 throw new Error("Evaluated to non-boolean value");601 }602 expect(evalResult.right.isTrue).toBe(true);603 });604 it("Evaluates { return 3 > 4; } to false (evaluating greater-than operator)", () => {605 // Arrange606 const ast: Block = [607 {608 statementKind: "return",609 returnedValue: {610 expressionKind: "binOp",611 binOp: "greaterThan",612 leftOperand: {613 expressionKind: "numberLit",614 value: 3,615 },616 rightOperand: {617 expressionKind: "numberLit",618 value: 4,619 },620 },621 },622 ];623 // Act624 const evalResult = evaluateTestProgram(wrapBlock(ast));625 // Assert626 if (!isRight(evalResult)) {627 throw new Error("Evaluation failed, should have succeeded");628 }629 if (evalResult.right.valueKind !== "boolean") {630 throw new Error("Evaluated to non-boolean value");631 }632 expect(evalResult.right.isTrue).toBe(false);633 });634 it("Evaluates { return 5 <= 5; } to true (evaluating less-than-or-equals operator)", () => {635 // Arrange636 const ast: Block = [637 {638 statementKind: "return",639 returnedValue: {640 expressionKind: "binOp",641 binOp: "lessThanEquals",642 leftOperand: {643 expressionKind: "numberLit",644 value: 5,645 },646 rightOperand: {647 expressionKind: "numberLit",648 value: 5,649 },650 },651 },652 ];653 // Act654 const evalResult = evaluateTestProgram(wrapBlock(ast));655 // Assert656 if (!isRight(evalResult)) {657 throw new Error("Evaluation failed, should have succeeded");658 }659 if (evalResult.right.valueKind !== "boolean") {660 throw new Error("Evaluated to non-boolean value");661 }662 expect(evalResult.right.isTrue).toBe(true);663 });664 it("Evaluates { return 6 >= 6; } to true (evaluating greater-than-or-equals operator)", () => {665 // Arrange666 const ast: Block = [667 {668 statementKind: "return",669 returnedValue: {670 expressionKind: "binOp",671 binOp: "greaterThanEquals",672 leftOperand: {673 expressionKind: "numberLit",674 value: 6,675 },676 rightOperand: {677 expressionKind: "numberLit",678 value: 6,679 },680 },681 },682 ];683 // Act684 const evalResult = evaluateTestProgram(wrapBlock(ast));685 // Assert686 if (!isRight(evalResult)) {687 throw new Error("Evaluation failed, should have succeeded");688 }689 if (evalResult.right.valueKind !== "boolean") {690 throw new Error("Evaluated to non-boolean value");691 }692 expect(evalResult.right.isTrue).toBe(true);693 });694 it("Evaluates { return 7 == 8; } to false (evaluating equals operator with numbers)", () => {695 // Arrange696 const ast: Block = [697 {698 statementKind: "return",699 returnedValue: {700 expressionKind: "binOp",701 binOp: "equals",702 leftOperand: {703 expressionKind: "numberLit",704 value: 7,705 },706 rightOperand: {707 expressionKind: "numberLit",708 value: 8,709 },710 },711 },712 ];713 // Act714 const evalResult = evaluateTestProgram(wrapBlock(ast));715 // Assert716 if (!isRight(evalResult)) {717 throw new Error("Evaluation failed, should have succeeded");718 }719 if (evalResult.right.valueKind !== "boolean") {720 throw new Error("Evaluated to non-boolean value");721 }722 expect(evalResult.right.isTrue).toBe(false);723 });724 it("Evaluates { return 9 /= 10; } to true (evaluating not-equals operator with numbers)", () => {725 // Arrange726 const ast: Block = [727 {728 statementKind: "return",729 returnedValue: {730 expressionKind: "binOp",731 binOp: "notEqual",732 leftOperand: {733 expressionKind: "numberLit",734 value: 9,735 },736 rightOperand: {737 expressionKind: "numberLit",738 value: 10,739 },740 },741 },742 ];743 // Act744 const evalResult = evaluateTestProgram(wrapBlock(ast));745 // Assert746 if (!isRight(evalResult)) {747 throw new Error("Evaluation failed, should have succeeded");748 }749 if (evalResult.right.valueKind !== "boolean") {750 throw new Error("Evaluated to non-boolean value");751 }752 expect(evalResult.right.isTrue).toBe(true);753 });754 it("Evaluates { return true == true; } to true (evaluating equals operator with booleans)", () => {755 // Arrange756 const ast: Block = [757 {758 statementKind: "return",759 returnedValue: {760 expressionKind: "binOp",761 binOp: "equals",762 leftOperand: {763 expressionKind: "booleanLit",764 isTrue: true,765 },766 rightOperand: {767 expressionKind: "booleanLit",768 isTrue: true,769 },770 },771 },772 ];773 // Act774 const evalResult = evaluateTestProgram(wrapBlock(ast));775 // Assert776 if (!isRight(evalResult)) {777 throw new Error("Evaluation failed, should have succeeded");778 }779 if (evalResult.right.valueKind !== "boolean") {780 throw new Error("Evaluated to non-boolean value");781 }782 expect(evalResult.right.isTrue).toBe(true);783 });784 it("Evaluates { return false /= true; } to true (evaluating not-equals operator with booleans)", () => {785 // Arrange786 const ast: Block = [787 {788 statementKind: "return",789 returnedValue: {790 expressionKind: "binOp",791 binOp: "notEqual",792 leftOperand: {793 expressionKind: "booleanLit",794 isTrue: false,795 },796 rightOperand: {797 expressionKind: "booleanLit",798 isTrue: true,799 },800 },801 },802 ];803 // Act804 const evalResult = evaluateTestProgram(wrapBlock(ast));805 // Assert806 if (!isRight(evalResult)) {807 throw new Error("Evaluation failed, should have succeeded");808 }809 if (evalResult.right.valueKind !== "boolean") {810 throw new Error("Evaluated to non-boolean value");811 }812 expect(evalResult.right.isTrue).toBe(true);813 });814 it("Evaluates { return null == null; } to true (evaluating equals operator with null)", () => {815 // Arrange816 const ast: Block = [817 {818 statementKind: "return",819 returnedValue: {820 expressionKind: "binOp",821 binOp: "equals",822 leftOperand: {823 expressionKind: "nullLit",824 },825 rightOperand: {826 expressionKind: "nullLit",827 },828 },829 },830 ];831 // Act832 const evalResult = evaluateTestProgram(wrapBlock(ast));833 // Assert834 if (!isRight(evalResult)) {835 throw new Error("Evaluation failed, should have succeeded");836 }837 if (evalResult.right.valueKind !== "boolean") {838 throw new Error("Evaluated to non-boolean value");839 }840 expect(evalResult.right.isTrue).toBe(true);841 });842 it("Evaluates { return null /= { a: 1 }; } to true (evaluating not-equals operator with null)", () => {843 // Arrange844 const ast: Block = [845 {846 statementKind: "return",847 returnedValue: {848 expressionKind: "binOp",849 binOp: "notEqual",850 leftOperand: {851 expressionKind: "nullLit",852 },853 rightOperand: {854 expressionKind: "objectLit",855 fields: [856 {857 fieldName: identifierIso.wrap("a"),858 fieldValue: {859 expressionKind: "numberLit",860 value: 1,861 },862 },863 ],864 },865 },866 },867 ];868 // Act869 const evalResult = evaluateTestProgram(wrapBlock(ast));870 // Assert871 if (!isRight(evalResult)) {872 throw new Error("Evaluation failed, should have succeeded");873 }874 if (evalResult.right.valueKind !== "boolean") {875 throw new Error("Evaluated to non-boolean value");876 }877 expect(evalResult.right.isTrue).toBe(true);878 });879 it("Evaluates { return { a: 1 } == null; } to false (evaluating equals operator with object and null", () => {880 // Arrange881 const ast: Block = [882 {883 statementKind: "return",884 returnedValue: {885 expressionKind: "binOp",886 binOp: "equals",887 leftOperand: {888 expressionKind: "objectLit",889 fields: [890 {891 fieldName: identifierIso.wrap("a"),892 fieldValue: {893 expressionKind: "numberLit",894 value: 1,895 },896 },897 ],898 },899 rightOperand: {900 expressionKind: "nullLit",901 },902 },903 },904 ];905 // Act906 const evalResult = evaluateTestProgram(wrapBlock(ast));907 // Assert908 if (!isRight(evalResult)) {909 throw new Error("Evaluation failed, should have succeeded");910 }911 if (evalResult.right.valueKind !== "boolean") {912 throw new Error("Evaluated to non-boolean value");913 }914 expect(evalResult.right.isTrue).toBe(false);915 });916 it("Evaluates { return 1 == null; } to false (comparison of null to number)", () => {917 // Arrange918 const ast: Block = [919 {920 statementKind: "return",921 returnedValue: {922 expressionKind: "binOp",923 binOp: "equals",924 leftOperand: {925 expressionKind: "numberLit",926 value: 1,927 },928 rightOperand: {929 expressionKind: "nullLit",930 },931 },932 },933 ];934 // Act935 const evalResult = evaluateTestProgram(wrapBlock(ast));936 // Assert937 if (!isRight(evalResult)) {938 throw new Error("Evaluation failed, should have succeeded");939 }940 if (evalResult.right.valueKind !== "boolean") {941 throw new Error("Evaluated to non-boolean value");942 }943 expect(evalResult.right.isTrue).toBe(false);944 });945 it("Evalutes { return null == false; } to false (comparison of null to boolean)", () => {946 // Arrange947 const ast: Block = [948 {949 statementKind: "return",950 returnedValue: {951 expressionKind: "binOp",952 binOp: "equals",953 leftOperand: {954 expressionKind: "nullLit",955 },956 rightOperand: {957 expressionKind: "numberLit",958 value: 1,959 },960 },961 },962 ];963 // Act964 const evalResult = evaluateTestProgram(wrapBlock(ast));965 // Assert966 if (!isRight(evalResult)) {967 throw new Error("Evaluation failed, should have succeeded");968 }969 if (evalResult.right.valueKind !== "boolean") {970 throw new Error("Evaluated to non-boolean value");971 }972 expect(evalResult.right.isTrue).toBe(false);973 });974 it('Evaluates { return "" == null; } to false (comparison of null to string)', () => {975 // Arrange976 const ast: Block = [977 {978 statementKind: "return",979 returnedValue: {980 expressionKind: "binOp",981 binOp: "equals",982 leftOperand: {983 expressionKind: "stringLit",984 value: "",985 },986 rightOperand: {987 expressionKind: "nullLit",988 },989 },990 },991 ];992 // Act993 const evalResult = evaluateTestProgram(wrapBlock(ast));994 // Assert995 if (!isRight(evalResult)) {996 throw new Error("Evaluation failed, should have succeeded");997 }998 if (evalResult.right.valueKind !== "boolean") {999 throw new Error("Evaluated to non-boolean value");1000 }1001 expect(evalResult.right.isTrue).toBe(false);1002 });1003 it('Evaluates { return "A" == "B"; } to false (evaluating equals operator with strings', () => {1004 // Arrange1005 const ast: Block = [1006 {1007 statementKind: "return",1008 returnedValue: {1009 expressionKind: "binOp",1010 binOp: "equals",1011 leftOperand: {1012 expressionKind: "stringLit",1013 value: "A",1014 },1015 rightOperand: {1016 expressionKind: "stringLit",1017 value: "B",1018 },1019 },1020 },1021 ];1022 // Act1023 const evalResult = evaluateTestProgram(wrapBlock(ast));1024 // Assert1025 if (!isRight(evalResult)) {1026 throw new Error("Evaluation failed, should have succeeded");1027 }1028 if (evalResult.right.valueKind !== "boolean") {1029 throw new Error("Evaluated to non-boolean value");1030 }1031 expect(evalResult.right.isTrue).toBe(false);1032 });1033 it('Evaluates { return "C" /= "C"; } to false (evaluating not-equals operator with strings', () => {1034 // Arrange1035 const ast: Block = [1036 {1037 statementKind: "return",1038 returnedValue: {1039 expressionKind: "binOp",1040 binOp: "notEqual",1041 leftOperand: {1042 expressionKind: "stringLit",1043 value: "C",1044 },1045 rightOperand: {1046 expressionKind: "stringLit",1047 value: "C",1048 },1049 },1050 },1051 ];1052 // Act1053 const evalResult = evaluateTestProgram(wrapBlock(ast));1054 // Assert1055 if (!isRight(evalResult)) {1056 throw new Error("Evaluation failed, should have succeeded");1057 }1058 if (evalResult.right.valueKind !== "boolean") {1059 throw new Error("Evaluated to non-boolean value");1060 }1061 expect(evalResult.right.isTrue).toBe(false);1062 });1063 it("Evaluates { return; } to null (evaluating top-level empty returns)", () => {1064 // Arrange1065 const ast: Block = [1066 {1067 statementKind: "return",1068 },1069 ];1070 // Act1071 const evalResult = evaluateTestProgram(wrapBlock(ast));1072 // Assert1073 if (!isRight(evalResult)) {1074 throw new Error("Evaluation failed, should have succeeded");1075 }1076 expect(evalResult.right.valueKind).toBe("null");1077 });1078 it("Evaluates { } to null (evaluating lack of top-level explicit return)", () => {1079 // Arrange1080 const ast: Block = [];1081 // Act1082 const evalResult = evaluateTestProgram(wrapBlock(ast));1083 // Assert1084 if (!isRight(evalResult)) {1085 throw new Error("Evaluation failed, should have succeeded");1086 }1087 expect(evalResult.right.valueKind).toBe("null");1088 });1089 });1090 describe("Programs with simple variable use", () => {1091 it("Evaluates { let x; x = 1; return x; } to 1 (assigning numeric literal expression to a variable)", () => {1092 // Arrange1093 const ast: Block = [1094 {1095 statementKind: "varDecl",1096 variableName: identifierIso.wrap("x"),1097 },1098 {1099 statementKind: "assignment",1100 variableName: identifierIso.wrap("x"),1101 variableValue: {1102 expressionKind: "numberLit",1103 value: 1,1104 },1105 },1106 {1107 statementKind: "return",1108 returnedValue: {1109 expressionKind: "variableRef",1110 variableName: identifierIso.wrap("x"),1111 },1112 },1113 ];1114 // Act1115 const evalResult = evaluateTestProgram(wrapBlock(ast));1116 // Assert1117 if (!isRight(evalResult)) {1118 throw new Error("Evaluation failed, should have succeeded");1119 }1120 if (evalResult.right.valueKind !== "number") {1121 throw new Error("Evaluated to non-numeric value");1122 }1123 expect(evalResult.right.value).toBe(1);1124 });1125 it("Evaluates { let x; x = 2; return x; } to 2 (assigning numeric literal expression to a variable)", () => {1126 // Arrange1127 const ast: Block = [1128 {1129 statementKind: "varDecl",1130 variableName: identifierIso.wrap("x"),1131 },1132 {1133 statementKind: "assignment",1134 variableName: identifierIso.wrap("x"),1135 variableValue: {1136 expressionKind: "numberLit",1137 value: 2,1138 },1139 },1140 {1141 statementKind: "return",1142 returnedValue: {1143 expressionKind: "variableRef",1144 variableName: identifierIso.wrap("x"),1145 },1146 },1147 ];1148 // Act1149 const evalResult = evaluateTestProgram(wrapBlock(ast));1150 // Assert1151 if (!isRight(evalResult)) {1152 throw new Error("Evaluation failed, should have succeeded");1153 }1154 if (evalResult.right.valueKind !== "number") {1155 throw new Error("Evaluated to non-numeric value");1156 }1157 expect(evalResult.right.value).toBe(2);1158 });1159 it("Evaluates { let x; x = 1; let y; y = 2; return x; } to 1 (checking that the correct variable's value is used)", () => {1160 // Arrange1161 const ast: Block = [1162 {1163 statementKind: "varDecl",1164 variableName: identifierIso.wrap("x"),1165 },1166 {1167 statementKind: "assignment",1168 variableName: identifierIso.wrap("x"),1169 variableValue: {1170 expressionKind: "numberLit",1171 value: 1,1172 },1173 },1174 {1175 statementKind: "varDecl",1176 variableName: identifierIso.wrap("y"),1177 },1178 {1179 statementKind: "assignment",1180 variableName: identifierIso.wrap("y"),1181 variableValue: {1182 expressionKind: "numberLit",1183 value: 2,1184 },1185 },1186 {1187 statementKind: "return",1188 returnedValue: {1189 expressionKind: "variableRef",1190 variableName: identifierIso.wrap("x"),1191 },1192 },1193 ];1194 // Act1195 const evalResult = evaluateTestProgram(wrapBlock(ast));1196 // Assert1197 if (!isRight(evalResult)) {1198 throw new Error("Evaluation failed, should have succeeded");1199 }1200 if (evalResult.right.valueKind !== "number") {1201 throw new Error("Evaluated to non-numeric value");1202 }1203 expect(evalResult.right.value).toBe(1);1204 });1205 it("Evaluates { let x; x = 1; let y; y = 2; return y; } to 2 (checking that the correct variable's value is used)", () => {1206 // Arrange1207 const ast: Block = [1208 {1209 statementKind: "varDecl",1210 variableName: identifierIso.wrap("x"),1211 },1212 {1213 statementKind: "assignment",1214 variableName: identifierIso.wrap("x"),1215 variableValue: {1216 expressionKind: "numberLit",1217 value: 1,1218 },1219 },1220 {1221 statementKind: "varDecl",1222 variableName: identifierIso.wrap("y"),1223 },1224 {1225 statementKind: "assignment",1226 variableName: identifierIso.wrap("y"),1227 variableValue: {1228 expressionKind: "numberLit",1229 value: 2,1230 },1231 },1232 {1233 statementKind: "return",1234 returnedValue: {1235 expressionKind: "variableRef",1236 variableName: identifierIso.wrap("y"),1237 },1238 },1239 ];1240 // Act1241 const evalResult = evaluateTestProgram(wrapBlock(ast));1242 // Assert1243 if (!isRight(evalResult)) {1244 throw new Error("Evaluation failed, should have succeeded");1245 }1246 if (evalResult.right.valueKind !== "number") {1247 throw new Error("Evaluated to non-numeric value");1248 }1249 expect(evalResult.right.value).toBe(2);1250 });1251 it("Evaluates { let x; x = 1; let y; y = 2; return x + y; } to 3 (checking operations with variables)", () => {1252 // Arrange1253 const ast: Block = [1254 {1255 statementKind: "varDecl",1256 variableName: identifierIso.wrap("x"),1257 },1258 {1259 statementKind: "assignment",1260 variableName: identifierIso.wrap("x"),1261 variableValue: {1262 expressionKind: "numberLit",1263 value: 1,1264 },1265 },1266 {1267 statementKind: "varDecl",1268 variableName: identifierIso.wrap("y"),1269 },1270 {1271 statementKind: "assignment",1272 variableName: identifierIso.wrap("y"),1273 variableValue: {1274 expressionKind: "numberLit",1275 value: 2,1276 },1277 },1278 {1279 statementKind: "return",1280 returnedValue: {1281 expressionKind: "binOp",1282 binOp: "add",1283 leftOperand: {1284 expressionKind: "variableRef",1285 variableName: identifierIso.wrap("x"),1286 },1287 rightOperand: {1288 expressionKind: "variableRef",1289 variableName: identifierIso.wrap("y"),1290 },1291 },1292 },1293 ];1294 // Act1295 const evalResult = evaluateTestProgram(wrapBlock(ast));1296 // Assert1297 if (!isRight(evalResult)) {1298 throw new Error("Evaluation failed, should have succeeded");1299 }1300 if (evalResult.right.valueKind !== "number") {1301 throw new Error("Evaluated to non-numeric value");1302 }1303 expect(evalResult.right.value).toBe(3);1304 });1305 });1306 describe("Programs with simple first-order functions", () => {1307 it("Evaluates { function f() { return 1; } return f(); } to 1 (checking single function call evaluation)", () => {1308 // Arrange1309 const ast: Block = [1310 {1311 statementKind: "funcDecl",1312 functionName: identifierIso.wrap("f"),1313 argNames: [],1314 body: [1315 {1316 statementKind: "return",1317 returnedValue: {1318 expressionKind: "numberLit",1319 value: 1,1320 },1321 },1322 ],1323 },1324 {1325 statementKind: "return",1326 returnedValue: {1327 expressionKind: "funcCall",1328 callee: {1329 expressionKind: "variableRef",1330 variableName: identifierIso.wrap("f"),1331 },1332 args: [],1333 },1334 },1335 ];1336 // Act1337 const evalResult = evaluateTestProgram(wrapBlock(ast));1338 // Assert1339 if (!isRight(evalResult)) {1340 throw new Error("Evaluation failed, should have succeeded");1341 }1342 if (evalResult.right.valueKind !== "number") {1343 throw new Error("Evaluated to non-numeric value");1344 }1345 expect(evalResult.right.value).toBe(1);1346 });1347 it("Evaluates { function f() { return 1; } function g() { return 2; } return f() + g(); } to 3 (checking evaluation of expression with multiple function calls)", () => {1348 // Arrange1349 const ast: Block = [1350 {1351 statementKind: "funcDecl",1352 functionName: identifierIso.wrap("f"),1353 argNames: [],1354 body: [1355 {1356 statementKind: "return",1357 returnedValue: {1358 expressionKind: "numberLit",1359 value: 1,1360 },1361 },1362 ],1363 },1364 {1365 statementKind: "funcDecl",1366 functionName: identifierIso.wrap("g"),1367 argNames: [],1368 body: [1369 {1370 statementKind: "return",1371 returnedValue: {1372 expressionKind: "numberLit",1373 value: 2,1374 },1375 },1376 ],1377 },1378 {1379 statementKind: "return",1380 returnedValue: {1381 expressionKind: "binOp",1382 binOp: "add",1383 leftOperand: {1384 expressionKind: "funcCall",1385 callee: {1386 expressionKind: "variableRef",1387 variableName: identifierIso.wrap("f"),1388 },1389 args: [],1390 },1391 rightOperand: {1392 expressionKind: "funcCall",1393 callee: {1394 expressionKind: "variableRef",1395 variableName: identifierIso.wrap("g"),1396 },1397 args: [],1398 },1399 },1400 },1401 ];1402 // Act1403 const evalResult = evaluateTestProgram(wrapBlock(ast));1404 // Assert1405 if (!isRight(evalResult)) {1406 throw new Error("Evaluation failed, should have succeeded");1407 }1408 if (evalResult.right.valueKind !== "number") {1409 throw new Error("Evaluated to non-numeric value");1410 }1411 expect(evalResult.right.value).toBe(3);1412 });1413 it("Evaluates { let x; x = 1; function f(y) { return y; } return f(x); } to 1 (checking function called with a variable as argument)", () => {1414 // Arrange1415 const ast: Block = [1416 {1417 statementKind: "varDecl",1418 variableName: identifierIso.wrap("x"),1419 },1420 {1421 statementKind: "assignment",1422 variableName: identifierIso.wrap("x"),1423 variableValue: {1424 expressionKind: "numberLit",1425 value: 1,1426 },1427 },1428 {1429 statementKind: "funcDecl",1430 functionName: identifierIso.wrap("f"),1431 argNames: [identifierIso.wrap("y")],1432 body: [1433 {1434 statementKind: "return",1435 returnedValue: {1436 expressionKind: "variableRef",1437 variableName: identifierIso.wrap("y"),1438 },1439 },1440 ],1441 },1442 {1443 statementKind: "return",1444 returnedValue: {1445 expressionKind: "funcCall",1446 callee: {1447 expressionKind: "variableRef",1448 variableName: identifierIso.wrap("f"),1449 },1450 args: [1451 {1452 expressionKind: "variableRef",1453 variableName: identifierIso.wrap("x"),1454 },1455 ],1456 },1457 },1458 ];1459 // Act1460 const evalResult = evaluateTestProgram(wrapBlock(ast));1461 // Assert1462 if (!isRight(evalResult)) {1463 throw new Error("Evaluation failed, should have succeeded");1464 }1465 if (evalResult.right.valueKind !== "number") {1466 throw new Error("Evaluated to non-numeric value");1467 }1468 expect(evalResult.right.value).toBe(1);1469 });1470 it("Evaluates { let x; x = 1; function f(y) { return x + y; } return f(2); } to 3 (checking calling function which uses an operation)", () => {1471 // Arrange1472 const ast: Block = [1473 {1474 statementKind: "varDecl",1475 variableName: identifierIso.wrap("x"),1476 },1477 {1478 statementKind: "assignment",1479 variableName: identifierIso.wrap("x"),1480 variableValue: {1481 expressionKind: "numberLit",1482 value: 1,1483 },1484 },1485 {1486 statementKind: "funcDecl",1487 functionName: identifierIso.wrap("f"),1488 argNames: [identifierIso.wrap("y")],1489 body: [1490 {1491 statementKind: "return",1492 returnedValue: {1493 expressionKind: "binOp",1494 binOp: "add",1495 leftOperand: {1496 expressionKind: "variableRef",1497 variableName: identifierIso.wrap("x"),1498 },1499 rightOperand: {1500 expressionKind: "variableRef",1501 variableName: identifierIso.wrap("y"),1502 },1503 },1504 },1505 ],1506 },1507 {1508 statementKind: "return",1509 returnedValue: {1510 expressionKind: "funcCall",1511 callee: {1512 expressionKind: "variableRef",1513 variableName: identifierIso.wrap("f"),1514 },1515 args: [1516 {1517 expressionKind: "numberLit",1518 value: 2,1519 },1520 ],1521 },1522 },1523 ];1524 // Act1525 const evalResult = evaluateTestProgram(wrapBlock(ast));1526 // Assert1527 if (!isRight(evalResult)) {1528 throw new Error("Evaluation failed, should have succeeded");1529 }1530 if (evalResult.right.valueKind !== "number") {1531 throw new Error("Evaluated to non-numeric value");1532 }1533 expect(evalResult.right.value).toBe(3);1534 });1535 it("Evaluates { function f() { return; } return f(); } to null (checking evaluation of empty returns from functions)", () => {1536 // Arrange1537 const ast: Block = [1538 {1539 statementKind: "funcDecl",1540 functionName: identifierIso.wrap("f"),1541 argNames: [],1542 body: [1543 {1544 statementKind: "return",1545 },1546 ],1547 },1548 {1549 statementKind: "return",1550 returnedValue: {1551 expressionKind: "funcCall",1552 callee: {1553 expressionKind: "variableRef",1554 variableName: identifierIso.wrap("f"),1555 },1556 args: [],1557 },1558 },1559 ];1560 // Act1561 const evalResult = evaluateTestProgram(wrapBlock(ast));1562 // Assert1563 if (!isRight(evalResult)) {1564 throw new Error("Evaluation failed, should have succeeded");1565 }1566 expect(evalResult.right.valueKind).toBe("null");1567 });1568 it("Evaluates { import print from Native; function f() { print(1); return; } f(); return 2; } to 2 (Evaluates programs with standalone function calls)", () => {1569 // Arrange1570 const ast: Block = [1571 {1572 statementKind: "import",1573 moduleName: NATIVE_MODULE_NAME,1574 imports: [identifierIso.wrap("print")],1575 },1576 {1577 statementKind: "funcDecl",1578 functionName: identifierIso.wrap("f"),1579 argNames: [],1580 body: [1581 {1582 statementKind: "expression",1583 expression: {1584 expressionKind: "funcCall",1585 args: [1586 {1587 expressionKind: "numberLit",1588 value: 1,1589 },1590 ],1591 callee: {1592 expressionKind: "variableRef",1593 variableName: identifierIso.wrap("print"),1594 },1595 },1596 },1597 {1598 statementKind: "return",1599 },1600 ],1601 },1602 {1603 statementKind: "expression",1604 expression: {1605 expressionKind: "funcCall",1606 args: [],1607 callee: {1608 expressionKind: "variableRef",1609 variableName: identifierIso.wrap("f"),1610 },1611 },1612 },1613 {1614 statementKind: "return",1615 returnedValue: {1616 expressionKind: "numberLit",1617 value: 2,1618 },1619 },1620 ];1621 // Act1622 const evalResult = evaluateTestProgram(wrapBlock(ast));1623 // Assert1624 if (!isRight(evalResult)) {1625 console.error(evalResult.left);1626 throw new Error("Evaluation failed, should have succeeded");1627 }1628 if (evalResult.right.valueKind !== "number") {1629 throw new Error("Evaluated to non-numeric value");1630 }1631 expect(evalResult.right.value).toBe(2);1632 });1633 it("Evaluates { import print from Native; function f() { print(3); } f(); return 4; } to 4 (Evaluates programs with function calls with no explicit return)", () => {1634 // Arrange1635 const ast: Block = [1636 {1637 statementKind: "import",1638 moduleName: NATIVE_MODULE_NAME,1639 imports: [identifierIso.wrap("print")],1640 },1641 {1642 statementKind: "funcDecl",1643 functionName: identifierIso.wrap("f"),1644 argNames: [],1645 body: [1646 {1647 statementKind: "expression",1648 expression: {1649 expressionKind: "funcCall",1650 args: [1651 {1652 expressionKind: "numberLit",1653 value: 3,1654 },1655 ],1656 callee: {1657 expressionKind: "variableRef",1658 variableName: identifierIso.wrap("print"),1659 },1660 },1661 },1662 ],1663 },1664 {1665 statementKind: "expression",1666 expression: {1667 expressionKind: "funcCall",1668 args: [],1669 callee: {1670 expressionKind: "variableRef",1671 variableName: identifierIso.wrap("f"),1672 },1673 },1674 },1675 {1676 statementKind: "return",1677 returnedValue: {1678 expressionKind: "numberLit",1679 value: 4,1680 },1681 },1682 ];1683 // Act1684 const evalResult = evaluateTestProgram(wrapBlock(ast));1685 // Assert1686 if (!isRight(evalResult)) {1687 throw new Error("Evaluation failed, should have succeeded");1688 }1689 if (evalResult.right.valueKind !== "number") {1690 throw new Error("Evaluated to non-numeric value");1691 }1692 expect(evalResult.right.value).toBe(4);1693 });1694 });1695 describe("Programs with higher-order functions", () => {1696 it("Evaluates { function f() { function g() { return 1; } return g; } return f()(); } to 1 (checking call of higher-order function in single statement)", () => {1697 // Arrange1698 const ast: Block = [1699 {1700 statementKind: "funcDecl",1701 functionName: identifierIso.wrap("f"),1702 argNames: [],1703 body: [1704 {1705 statementKind: "funcDecl",1706 functionName: identifierIso.wrap("g"),1707 argNames: [],1708 body: [1709 {1710 statementKind: "return",1711 returnedValue: {1712 expressionKind: "numberLit",1713 value: 1,1714 },1715 },1716 ],1717 },1718 {1719 statementKind: "return",1720 returnedValue: {1721 expressionKind: "variableRef",1722 variableName: identifierIso.wrap("g"),1723 },1724 },1725 ],1726 },1727 {1728 statementKind: "return",1729 returnedValue: {1730 expressionKind: "funcCall",1731 args: [],1732 callee: {1733 expressionKind: "funcCall",1734 args: [],1735 callee: {1736 expressionKind: "variableRef",1737 variableName: identifierIso.wrap("f"),1738 },1739 },1740 },1741 },1742 ];1743 // Act1744 const evalResult = evaluateTestProgram(wrapBlock(ast));1745 // Assert1746 if (!isRight(evalResult)) {1747 throw new Error("Evaluation failed, should have succeeded");1748 }1749 if (evalResult.right.valueKind !== "number") {1750 throw new Error("Evaluated to non-numeric value");1751 }1752 expect(evalResult.right.value).toBe(1);1753 });1754 it("Evaluates { function makeAdder(x) { function adder(y) { return x + y; } return adder; } let addOne; addOne = makeAdder(1); return addOne(2); } to 3 (checking call of higher-order function over multiple statements", () => {1755 // Arrange1756 const ast: Block = [1757 {1758 statementKind: "funcDecl",1759 functionName: identifierIso.wrap("makeAdder"),1760 argNames: [identifierIso.wrap("x")],1761 body: [1762 {1763 statementKind: "funcDecl",1764 functionName: identifierIso.wrap("adder"),1765 argNames: [identifierIso.wrap("y")],1766 body: [1767 {1768 statementKind: "return",1769 returnedValue: {1770 expressionKind: "binOp",1771 binOp: "add",1772 leftOperand: {1773 expressionKind: "variableRef",1774 variableName: identifierIso.wrap("x"),1775 },1776 rightOperand: {1777 expressionKind: "variableRef",1778 variableName: identifierIso.wrap("y"),1779 },1780 },1781 },1782 ],1783 },1784 {1785 statementKind: "return",1786 returnedValue: {1787 expressionKind: "variableRef",1788 variableName: identifierIso.wrap("adder"),1789 },1790 },1791 ],1792 },1793 {1794 statementKind: "varDecl",1795 variableName: identifierIso.wrap("addOne"),1796 },1797 {1798 statementKind: "assignment",1799 variableName: identifierIso.wrap("addOne"),1800 variableValue: {1801 expressionKind: "funcCall",1802 args: [1803 {1804 expressionKind: "numberLit",1805 value: 1,1806 },1807 ],1808 callee: {1809 expressionKind: "variableRef",1810 variableName: identifierIso.wrap("makeAdder"),1811 },1812 },1813 },1814 {1815 statementKind: "return",1816 returnedValue: {1817 expressionKind: "funcCall",1818 args: [1819 {1820 expressionKind: "numberLit",1821 value: 2,1822 },1823 ],1824 callee: {1825 expressionKind: "variableRef",1826 variableName: identifierIso.wrap("addOne"),1827 },1828 },1829 },1830 ];1831 // Act1832 const evalResult = evaluateTestProgram(wrapBlock(ast));1833 // Assert1834 if (!isRight(evalResult)) {1835 throw new Error("Evaluation failed, should have succeeded");1836 }1837 if (evalResult.right.valueKind !== "number") {1838 throw new Error("Evaluated to non-numeric value");1839 }1840 expect(evalResult.right.value).toBe(3);1841 });1842 });1843 describe("Programs with if statements", () => {1844 it("Evaluates { if (true) { return 1; } else { return 2; } } to 1 (evaluating true block of if statements)", () => {1845 // Arrange1846 const ast: Block = [1847 {1848 statementKind: "if",1849 condition: {1850 expressionKind: "booleanLit",1851 isTrue: true,1852 },1853 trueBody: [1854 {1855 statementKind: "return",1856 returnedValue: {1857 expressionKind: "numberLit",1858 value: 1,1859 },1860 },1861 ],1862 falseBody: [1863 {1864 statementKind: "return",1865 returnedValue: {1866 expressionKind: "numberLit",1867 value: 2,1868 },1869 },1870 ],1871 },1872 ];1873 // Act1874 const evalResult = evaluateTestProgram(wrapBlock(ast));1875 // Assert1876 if (!isRight(evalResult)) {1877 throw new Error("Evaluation failed, should have succeeded");1878 }1879 if (evalResult.right.valueKind !== "number") {1880 throw new Error("Evaluated to non-numeric value");1881 }1882 expect(evalResult.right.value).toBe(1);1883 });1884 it("Evaluates { if (false) { return 1; } else { return 2; } } to 2 (evaluating false block of if statements)", () => {1885 // Arrange1886 const ast: Block = [1887 {1888 statementKind: "if",1889 condition: {1890 expressionKind: "booleanLit",1891 isTrue: false,1892 },1893 trueBody: [1894 {1895 statementKind: "return",1896 returnedValue: {1897 expressionKind: "numberLit",1898 value: 1,1899 },1900 },1901 ],1902 falseBody: [1903 {1904 statementKind: "return",1905 returnedValue: {1906 expressionKind: "numberLit",1907 value: 2,1908 },1909 },1910 ],1911 },1912 ];1913 // Act1914 const evalResult = evaluateTestProgram(wrapBlock(ast));1915 // Assert1916 if (!isRight(evalResult)) {1917 throw new Error("Evaluation failed, should have succeeded");1918 }1919 if (evalResult.right.valueKind !== "number") {1920 throw new Error("Evaluated to non-numeric value");1921 }1922 expect(evalResult.right.value).toBe(2);1923 });1924 it("Evaluates { let x; x = 0; if (true) { x = x + 1; } else { } return x; } to 1 (evaluating if statements with side effects in true block", () => {1925 // Arrange1926 const ast: Block = [1927 {1928 statementKind: "varDecl",1929 variableName: identifierIso.wrap("x"),1930 },1931 {1932 statementKind: "assignment",1933 variableName: identifierIso.wrap("x"),1934 variableValue: {1935 expressionKind: "numberLit",1936 value: 0,1937 },1938 },1939 {1940 statementKind: "if",1941 condition: {1942 expressionKind: "booleanLit",1943 isTrue: true,1944 },1945 trueBody: [1946 {1947 statementKind: "assignment",1948 variableName: identifierIso.wrap("x"),1949 variableValue: {1950 expressionKind: "binOp",1951 binOp: "add",1952 leftOperand: {1953 expressionKind: "variableRef",1954 variableName: identifierIso.wrap("x"),1955 },1956 rightOperand: {1957 expressionKind: "numberLit",1958 value: 1,1959 },1960 },1961 },1962 ],1963 falseBody: [],1964 },1965 {1966 statementKind: "return",1967 returnedValue: {1968 expressionKind: "variableRef",1969 variableName: identifierIso.wrap("x"),1970 },1971 },1972 ];1973 // Act1974 const evalResult = evaluateTestProgram(wrapBlock(ast));1975 // Assert1976 if (!isRight(evalResult)) {1977 throw new Error("Evaluation failed, should have succeeded");1978 }1979 if (evalResult.right.valueKind !== "number") {1980 throw new Error("Evaluated to non-numeric value");1981 }1982 expect(evalResult.right.value).toBe(1);1983 });1984 it("Evaluates { let x; x = 0; if (false) { x = x + 1; } else { x = x + 2; } return x; } to 2 (evaluating if statements with side effects in else block", () => {1985 // Arrange1986 const ast: Block = [1987 {1988 statementKind: "varDecl",1989 variableName: identifierIso.wrap("x"),1990 },1991 {1992 statementKind: "assignment",1993 variableName: identifierIso.wrap("x"),1994 variableValue: {1995 expressionKind: "numberLit",1996 value: 0,1997 },1998 },1999 {2000 statementKind: "if",2001 condition: {2002 expressionKind: "booleanLit",2003 isTrue: false,2004 },2005 trueBody: [2006 {2007 statementKind: "assignment",2008 variableName: identifierIso.wrap("x"),2009 variableValue: {2010 expressionKind: "binOp",2011 binOp: "add",2012 leftOperand: {2013 expressionKind: "variableRef",2014 variableName: identifierIso.wrap("x"),2015 },2016 rightOperand: {2017 expressionKind: "numberLit",2018 value: 1,2019 },2020 },2021 },2022 ],2023 falseBody: [2024 {2025 statementKind: "assignment",2026 variableName: identifierIso.wrap("x"),2027 variableValue: {2028 expressionKind: "binOp",2029 binOp: "add",2030 leftOperand: {2031 expressionKind: "variableRef",2032 variableName: identifierIso.wrap("x"),2033 },2034 rightOperand: {2035 expressionKind: "numberLit",2036 value: 2,2037 },2038 },2039 },2040 ],2041 },2042 {2043 statementKind: "return",2044 returnedValue: {2045 expressionKind: "variableRef",2046 variableName: identifierIso.wrap("x"),2047 },2048 },2049 ];2050 // Act2051 const evalResult = evaluateTestProgram(wrapBlock(ast));2052 // Assert2053 if (!isRight(evalResult)) {2054 throw new Error("Evaluation failed, should have succeeded");2055 }2056 if (evalResult.right.valueKind !== "number") {2057 throw new Error("Evaluated to non-numeric value");2058 }2059 expect(evalResult.right.value).toBe(2);2060 });2061 });2062 describe("Programs with while statements", () => {2063 it("Evaluates { while (true) { return 1; } } to 1 (evaluating while statement with return)", () => {2064 // Arrange2065 const ast: Block = [2066 {2067 statementKind: "while",2068 condition: {2069 expressionKind: "booleanLit",2070 isTrue: true,2071 },2072 body: [2073 {2074 statementKind: "return",2075 returnedValue: {2076 expressionKind: "numberLit",2077 value: 1,2078 },2079 },2080 ],2081 },2082 ];2083 // Act2084 const evalResult = evaluateTestProgram(wrapBlock(ast));2085 // Assert2086 if (!isRight(evalResult)) {2087 throw new Error("Evaluation failed, should have succeeded");2088 }2089 if (evalResult.right.valueKind !== "number") {2090 throw new Error("Evaluated to non-numeric value");2091 }2092 expect(evalResult.right.value).toBe(1);2093 });2094 it("Evaluates { let x; x = 0; let y; y = 0; while (x < 2) { y = y + 5; x = x + 1; } return y; } to 10 (evaluating side-effecting while statements)", () => {2095 // Arrange2096 const ast: Block = [2097 {2098 statementKind: "varDecl",2099 variableName: identifierIso.wrap("x"),2100 },2101 {2102 statementKind: "assignment",2103 variableName: identifierIso.wrap("x"),2104 variableValue: {2105 expressionKind: "numberLit",2106 value: 0,2107 },2108 },2109 {2110 statementKind: "varDecl",2111 variableName: identifierIso.wrap("y"),2112 },2113 {2114 statementKind: "assignment",2115 variableName: identifierIso.wrap("y"),2116 variableValue: {2117 expressionKind: "numberLit",2118 value: 0,2119 },2120 },2121 {2122 statementKind: "while",2123 condition: {2124 expressionKind: "binOp",2125 binOp: "lessThan",2126 leftOperand: {2127 expressionKind: "variableRef",2128 variableName: identifierIso.wrap("x"),2129 },2130 rightOperand: {2131 expressionKind: "numberLit",2132 value: 2,2133 },2134 },2135 body: [2136 {2137 statementKind: "assignment",2138 variableName: identifierIso.wrap("y"),2139 variableValue: {2140 expressionKind: "binOp",2141 binOp: "add",2142 leftOperand: {2143 expressionKind: "variableRef",2144 variableName: identifierIso.wrap("y"),2145 },2146 rightOperand: {2147 expressionKind: "numberLit",2148 value: 5,2149 },2150 },2151 },2152 {2153 statementKind: "assignment",2154 variableName: identifierIso.wrap("x"),2155 variableValue: {2156 expressionKind: "binOp",2157 binOp: "add",2158 leftOperand: {2159 expressionKind: "variableRef",2160 variableName: identifierIso.wrap("x"),2161 },2162 rightOperand: {2163 expressionKind: "numberLit",2164 value: 1,2165 },2166 },2167 },2168 ],2169 },2170 {2171 statementKind: "return",2172 returnedValue: {2173 expressionKind: "variableRef",2174 variableName: identifierIso.wrap("y"),2175 },2176 },2177 ];2178 // Act2179 const evalResult = evaluateTestProgram(wrapBlock(ast));2180 // Assert2181 if (!isRight(evalResult)) {2182 console.error(evalResult.left.runtimeErrorKind);2183 throw new Error("Evaluation failed, should have succeeded");2184 }2185 if (evalResult.right.valueKind !== "number") {2186 throw new Error("Evaluated to non-numeric value");2187 }2188 expect(evalResult.right.value).toBe(10);2189 });2190 });2191 describe("Recursive functions", () => {2192 it("Evaluates { function factorial(n) if (n == 0) { return 1; } else { return n * factorial(n - 1); } return factorial(3); } to 6 (checking recursive function evaluation)", () => {2193 // Arrange2194 const ast: Block = [2195 {2196 statementKind: "funcDecl",2197 functionName: identifierIso.wrap("factorial"),2198 argNames: [identifierIso.wrap("n")],2199 body: [2200 {2201 statementKind: "if",2202 condition: {2203 expressionKind: "binOp",2204 binOp: "equals",2205 leftOperand: {2206 expressionKind: "variableRef",2207 variableName: identifierIso.wrap("n"),2208 },2209 rightOperand: {2210 expressionKind: "numberLit",2211 value: 0,2212 },2213 },2214 trueBody: [2215 {2216 statementKind: "return",2217 returnedValue: {2218 expressionKind: "numberLit",2219 value: 1,2220 },2221 },2222 ],2223 falseBody: [2224 {2225 statementKind: "return",2226 returnedValue: {2227 expressionKind: "binOp",2228 binOp: "multiply",2229 leftOperand: {2230 expressionKind: "variableRef",2231 variableName: identifierIso.wrap("n"),2232 },2233 rightOperand: {2234 expressionKind: "funcCall",2235 callee: {2236 expressionKind: "variableRef",2237 variableName: identifierIso.wrap("factorial"),2238 },2239 args: [2240 {2241 expressionKind: "binOp",2242 binOp: "subtract",2243 leftOperand: {2244 expressionKind: "variableRef",2245 variableName: identifierIso.wrap("n"),2246 },2247 rightOperand: {2248 expressionKind: "numberLit",2249 value: 1,2250 },2251 },2252 ],2253 },2254 },2255 },2256 ],2257 },2258 ],2259 },2260 {2261 statementKind: "return",2262 returnedValue: {2263 expressionKind: "funcCall",2264 callee: {2265 expressionKind: "variableRef",2266 variableName: identifierIso.wrap("factorial"),2267 },2268 args: [2269 {2270 expressionKind: "numberLit",2271 value: 3,2272 },2273 ],2274 },2275 },2276 ];2277 // Act2278 const evalResult = evaluateTestProgram(wrapBlock(ast));2279 // Assert2280 if (!isRight(evalResult)) {2281 throw new Error("Evaluation failed, should have succeeded");2282 }2283 if (evalResult.right.valueKind !== "number") {2284 throw new Error("Evaluated to non-numeric value");2285 }2286 expect(evalResult.right.value).toBe(6);2287 });2288 });2289 describe("Object usage", () => {2290 it("Evaluates { let x = { field: 1 }; return x.field; } to 1 (basic getter usage)", () => {2291 // Arrange2292 const ast: Block = [2293 {2294 statementKind: "varDecl",2295 variableName: identifierIso.wrap("x"),2296 },2297 {2298 statementKind: "assignment",2299 variableName: identifierIso.wrap("x"),2300 variableValue: {2301 expressionKind: "objectLit",2302 fields: [2303 {2304 fieldName: identifierIso.wrap("field"),2305 fieldValue: {2306 expressionKind: "numberLit",2307 value: 1,2308 },2309 },2310 ],2311 },2312 },2313 {2314 statementKind: "return",2315 returnedValue: {2316 expressionKind: "get",2317 object: {2318 expressionKind: "variableRef",2319 variableName: identifierIso.wrap("x"),2320 },2321 field: identifierIso.wrap("field"),2322 },2323 },2324 ];2325 // Act2326 const evalResult = evaluateTestProgram(wrapBlock(ast));2327 // Assert2328 if (!isRight(evalResult)) {2329 throw new Error("Evaluation failed, should have succeeded");2330 }2331 if (evalResult.right.valueKind !== "number") {2332 throw new Error("Evaluated to non-numeric value");2333 }2334 expect(evalResult.right.value).toBe(1);2335 });2336 it("Evaluates { let x = { field: 1 }; x.field = 2; return x.field; } to 2 (basic setter usage)", () => {2337 // Arrange2338 const ast: Block = [2339 {2340 statementKind: "varDecl",2341 variableName: identifierIso.wrap("x"),2342 },2343 {2344 statementKind: "assignment",2345 variableName: identifierIso.wrap("x"),2346 variableValue: {2347 expressionKind: "objectLit",2348 fields: [2349 {2350 fieldName: identifierIso.wrap("field"),2351 fieldValue: {2352 expressionKind: "numberLit",2353 value: 1,2354 },2355 },2356 ],2357 },2358 },2359 {2360 statementKind: "set",2361 object: {2362 expressionKind: "variableRef",2363 variableName: identifierIso.wrap("x"),2364 },2365 field: identifierIso.wrap("field"),2366 value: {2367 expressionKind: "numberLit",2368 value: 2,2369 },2370 },2371 {2372 statementKind: "return",2373 returnedValue: {2374 expressionKind: "get",2375 object: {2376 expressionKind: "variableRef",2377 variableName: identifierIso.wrap("x"),2378 },2379 field: identifierIso.wrap("field"),2380 },2381 },2382 ];2383 // Act2384 const evalResult = evaluateTestProgram(wrapBlock(ast));2385 // Assert2386 if (!isRight(evalResult)) {2387 throw new Error("Evaluation failed, should have succeeded");2388 }2389 if (evalResult.right.valueKind !== "number") {2390 throw new Error("Evaluated to non-numeric value");2391 }2392 expect(evalResult.right.value).toBe(2);2393 });2394 it("Evaluates { let nested = { outer: { inner: 1 } }; return nested.outer.inner; } to 1 (chained getters)", () => {2395 // Arrange2396 const ast: Block = [2397 {2398 statementKind: "varDecl",2399 variableName: identifierIso.wrap("nested"),2400 },2401 {2402 statementKind: "assignment",2403 variableName: identifierIso.wrap("nested"),2404 variableValue: {2405 expressionKind: "objectLit",2406 fields: [2407 {2408 fieldName: identifierIso.wrap("outer"),2409 fieldValue: {2410 expressionKind: "objectLit",2411 fields: [2412 {2413 fieldName: identifierIso.wrap("inner"),2414 fieldValue: {2415 expressionKind: "numberLit",2416 value: 1,2417 },2418 },2419 ],2420 },2421 },2422 ],2423 },2424 },2425 {2426 statementKind: "return",2427 returnedValue: {2428 expressionKind: "get",2429 object: {2430 expressionKind: "get",2431 object: {2432 expressionKind: "variableRef",2433 variableName: identifierIso.wrap("nested"),2434 },2435 field: identifierIso.wrap("outer"),2436 },2437 field: identifierIso.wrap("inner"),2438 },2439 },2440 ];2441 // Act2442 const evalResult = evaluateTestProgram(wrapBlock(ast));2443 // Assert2444 if (!isRight(evalResult)) {2445 throw new Error("Evaluation failed, should have succeeded");2446 }2447 if (evalResult.right.valueKind !== "number") {2448 throw new Error("Evaluated to non-numeric value");2449 }2450 expect(evalResult.right.value).toBe(1);2451 });2452 it("Evaluates { let nested = { outer: { inner: 1 } }; nested.outer.inner = 2; return nested.outer.inner; } to 2 (nested setter)", () => {2453 // Arrange2454 const ast: Block = [2455 {2456 statementKind: "varDecl",2457 variableName: identifierIso.wrap("nested"),2458 },2459 {2460 statementKind: "assignment",2461 variableName: identifierIso.wrap("nested"),2462 variableValue: {2463 expressionKind: "objectLit",2464 fields: [2465 {2466 fieldName: identifierIso.wrap("outer"),2467 fieldValue: {2468 expressionKind: "objectLit",2469 fields: [2470 {2471 fieldName: identifierIso.wrap("inner"),2472 fieldValue: {2473 expressionKind: "numberLit",2474 value: 1,2475 },2476 },2477 ],2478 },2479 },2480 ],2481 },2482 },2483 {2484 statementKind: "set",2485 object: {2486 expressionKind: "get",2487 object: {2488 expressionKind: "variableRef",2489 variableName: identifierIso.wrap("nested"),2490 },2491 field: identifierIso.wrap("outer"),2492 },2493 field: identifierIso.wrap("inner"),2494 value: {2495 expressionKind: "numberLit",2496 value: 2,2497 },2498 },2499 {2500 statementKind: "return",2501 returnedValue: {2502 expressionKind: "get",2503 object: {2504 expressionKind: "get",2505 object: {2506 expressionKind: "variableRef",2507 variableName: identifierIso.wrap("nested"),2508 },2509 field: identifierIso.wrap("outer"),2510 },2511 field: identifierIso.wrap("inner"),2512 },2513 },2514 ];2515 // Act2516 const evalResult = evaluateTestProgram(wrapBlock(ast));2517 // Assert2518 if (!isRight(evalResult)) {2519 throw new Error("Evaluation failed, should have succeeded");2520 }2521 if (evalResult.right.valueKind !== "number") {2522 throw new Error("Evaluated to non-numeric value");2523 }2524 expect(evalResult.right.value).toBe(2);2525 });2526 it("Evaluates { let x = { a: 1 }; return x.b; } to null (nonexistent property on object)", () => {2527 // Arrange2528 const ast: Block = [2529 {2530 statementKind: "varDecl",2531 variableName: identifierIso.wrap("x"),2532 },2533 {2534 statementKind: "assignment",2535 variableName: identifierIso.wrap("x"),2536 variableValue: {2537 expressionKind: "objectLit",2538 fields: [2539 {2540 fieldName: identifierIso.wrap("a"),2541 fieldValue: {2542 expressionKind: "numberLit",2543 value: 1,2544 },2545 },2546 ],2547 },2548 },2549 {2550 statementKind: "return",2551 returnedValue: {2552 expressionKind: "get",2553 object: {2554 expressionKind: "variableRef",2555 variableName: identifierIso.wrap("x"),2556 },2557 field: identifierIso.wrap("b"),2558 },2559 },2560 ];2561 // Act2562 const evalResult = evaluateTestProgram(wrapBlock(ast));2563 // Assert2564 if (!isRight(evalResult)) {2565 throw new Error("Evaluation failed, should have succeeded");2566 }2567 expect(evalResult.right.valueKind).toBe("null");2568 });2569 it("Evaluates { let x = { a: { b: 1 } }; x.a = null; return x.a; } to null (assignment of null via setter)", () => {2570 // Arrange2571 const ast: Block = [2572 {2573 statementKind: "varDecl",2574 variableName: identifierIso.wrap("x"),2575 },2576 {2577 statementKind: "assignment",2578 variableName: identifierIso.wrap("x"),2579 variableValue: {2580 expressionKind: "objectLit",2581 fields: [2582 {2583 fieldName: identifierIso.wrap("a"),2584 fieldValue: {2585 expressionKind: "objectLit",2586 fields: [2587 {2588 fieldName: identifierIso.wrap("b"),2589 fieldValue: {2590 expressionKind: "numberLit",2591 value: 1,2592 },2593 },2594 ],2595 },2596 },2597 ],2598 },2599 },2600 {2601 statementKind: "set",2602 object: {2603 expressionKind: "variableRef",2604 variableName: identifierIso.wrap("x"),2605 },2606 field: identifierIso.wrap("a"),2607 value: {2608 expressionKind: "nullLit",2609 },2610 },2611 {2612 statementKind: "return",2613 returnedValue: {2614 expressionKind: "get",2615 object: {2616 expressionKind: "variableRef",2617 variableName: identifierIso.wrap("x"),2618 },2619 field: identifierIso.wrap("a"),2620 },2621 },2622 ];2623 // Act2624 const evalResult = evaluateTestProgram(wrapBlock(ast));2625 // Assert2626 if (!isRight(evalResult)) {2627 throw new Error("Evaluation failed, should have succeeded");2628 }2629 expect(evalResult.right.valueKind).toBe("null");2630 });2631 it("Evaluates { return {} == {}; } to true (empty objects are equal to each other)", () => {2632 // Arrange2633 const ast: Block = [2634 {2635 statementKind: "return",2636 returnedValue: {2637 expressionKind: "binOp",2638 binOp: "equals",2639 leftOperand: {2640 expressionKind: "objectLit",2641 fields: [],2642 },2643 rightOperand: {2644 expressionKind: "objectLit",2645 fields: [],2646 },2647 },2648 },2649 ];2650 // Act2651 const evalResult = evaluateTestProgram(wrapBlock(ast));2652 // Assert2653 if (!isRight(evalResult)) {2654 throw new Error("Evaluation failed, should have succeeded");2655 }2656 if (evalResult.right.valueKind !== "boolean") {2657 throw new Error("Evaluated to non-boolean value");2658 }2659 expect(evalResult.right.isTrue).toBe(true);2660 });2661 it("Evaluates { return { a: 1 } == {}; } to false (nonempty objects aren't equal to empty objects", () => {2662 // Arrange2663 const ast: Block = [2664 {2665 statementKind: "return",2666 returnedValue: {2667 expressionKind: "binOp",2668 binOp: "equals",2669 leftOperand: {2670 expressionKind: "objectLit",2671 fields: [2672 {2673 fieldName: identifierIso.wrap("a"),2674 fieldValue: {2675 expressionKind: "numberLit",2676 value: 1,2677 },2678 },2679 ],2680 },2681 rightOperand: {2682 expressionKind: "objectLit",2683 fields: [],2684 },2685 },2686 },2687 ];2688 // Act2689 const evalResult = evaluateTestProgram(wrapBlock(ast));2690 // Assert2691 if (!isRight(evalResult)) {2692 throw new Error("Evaluation failed, should have succeeded");2693 }2694 if (evalResult.right.valueKind !== "boolean") {2695 throw new Error("Evaluated to non-boolean value");2696 }2697 expect(evalResult.right.isTrue).toBe(false);2698 });2699 it("Evaluates { return { a: 1} == { a: 1}; } to true (objects with same fields and same values are equal)", () => {2700 // Arrange2701 const ast: Block = [2702 {2703 statementKind: "return",2704 returnedValue: {2705 expressionKind: "binOp",2706 binOp: "equals",2707 leftOperand: {2708 expressionKind: "objectLit",2709 fields: [2710 {2711 fieldName: identifierIso.wrap("a"),2712 fieldValue: {2713 expressionKind: "numberLit",2714 value: 1,2715 },2716 },2717 ],2718 },2719 rightOperand: {2720 expressionKind: "objectLit",2721 fields: [2722 {2723 fieldName: identifierIso.wrap("a"),2724 fieldValue: {2725 expressionKind: "numberLit",2726 value: 1,2727 },2728 },2729 ],2730 },2731 },2732 },2733 ];2734 // Act2735 const evalResult = evaluateTestProgram(wrapBlock(ast));2736 // Assert2737 if (!isRight(evalResult)) {2738 throw new Error("Evaluation failed, should have succeeded");2739 }2740 if (evalResult.right.valueKind !== "boolean") {2741 throw new Error("Evaluated to non-boolean value");2742 }2743 expect(evalResult.right.isTrue).toBe(true);2744 });2745 it("Evaluates { return { a: 1 } == { a: 2 }; } to false (objects with same fields but different values are nonequal", () => {2746 // Arrange2747 const ast: Block = [2748 {2749 statementKind: "return",2750 returnedValue: {2751 expressionKind: "binOp",2752 binOp: "equals",2753 leftOperand: {2754 expressionKind: "objectLit",2755 fields: [2756 {2757 fieldName: identifierIso.wrap("a"),2758 fieldValue: {2759 expressionKind: "numberLit",2760 value: 1,2761 },2762 },2763 ],2764 },2765 rightOperand: {2766 expressionKind: "objectLit",2767 fields: [2768 {2769 fieldName: identifierIso.wrap("a"),2770 fieldValue: {2771 expressionKind: "numberLit",2772 value: 2,2773 },2774 },2775 ],2776 },2777 },2778 },2779 ];2780 // Act2781 const evalResult = evaluateTestProgram(wrapBlock(ast));2782 // Assert2783 if (!isRight(evalResult)) {2784 throw new Error("Evaluation failed, should have succeeded");2785 }2786 if (evalResult.right.valueKind !== "boolean") {2787 throw new Error("Evaluated to non-boolean value");2788 }2789 expect(evalResult.right.isTrue).toBe(false);2790 });2791 it("Evaluates { return { a: 1, b: 2 } == { a: 1 }; } to false (extra fields cause objects to be nonequal)", () => {2792 // Arrange2793 const ast: Block = [2794 {2795 statementKind: "return",2796 returnedValue: {2797 expressionKind: "binOp",2798 binOp: "equals",2799 leftOperand: {2800 expressionKind: "objectLit",2801 fields: [2802 {2803 fieldName: identifierIso.wrap("a"),2804 fieldValue: {2805 expressionKind: "numberLit",2806 value: 1,2807 },2808 },2809 {2810 fieldName: identifierIso.wrap("b"),2811 fieldValue: {2812 expressionKind: "numberLit",2813 value: 2,2814 },2815 },2816 ],2817 },2818 rightOperand: {2819 expressionKind: "objectLit",2820 fields: [2821 {2822 fieldName: identifierIso.wrap("a"),2823 fieldValue: {2824 expressionKind: "numberLit",2825 value: 1,2826 },2827 },2828 ],2829 },2830 },2831 },2832 ];2833 // Act2834 const evalResult = evaluateTestProgram(wrapBlock(ast));2835 // Assert2836 if (!isRight(evalResult)) {2837 throw new Error("Evaluation failed, should have succeeded");2838 }2839 if (evalResult.right.valueKind !== "boolean") {2840 throw new Error("Evaluated to non-boolean value");2841 }2842 expect(evalResult.right.isTrue).toBe(false);2843 });2844 it("Evaluates { return { a: 1, b: 2 } == { a: 1, b: 2 }; } to true (objects with multiple, identical fields are equal)", () => {2845 // Arrange2846 const ast: Block = [2847 {2848 statementKind: "return",2849 returnedValue: {2850 expressionKind: "binOp",2851 binOp: "equals",2852 leftOperand: {2853 expressionKind: "objectLit",2854 fields: [2855 {2856 fieldName: identifierIso.wrap("a"),2857 fieldValue: {2858 expressionKind: "numberLit",2859 value: 1,2860 },2861 },2862 {2863 fieldName: identifierIso.wrap("b"),2864 fieldValue: {2865 expressionKind: "numberLit",2866 value: 2,2867 },2868 },2869 ],2870 },2871 rightOperand: {2872 expressionKind: "objectLit",2873 fields: [2874 {2875 fieldName: identifierIso.wrap("a"),2876 fieldValue: {2877 expressionKind: "numberLit",2878 value: 1,2879 },2880 },2881 {2882 fieldName: identifierIso.wrap("b"),2883 fieldValue: {2884 expressionKind: "numberLit",2885 value: 2,2886 },2887 },2888 ],2889 },2890 },2891 },2892 ];2893 // Act2894 const evalResult = evaluateTestProgram(wrapBlock(ast));2895 // Assert2896 if (!isRight(evalResult)) {2897 throw new Error("Evaluation failed, should have succeeded");2898 }2899 if (evalResult.right.valueKind !== "boolean") {2900 throw new Error("Evaluated to non-boolean value");2901 }2902 expect(evalResult.right.isTrue).toBe(true);2903 });2904 it("Evaluates { return { a: 1, b: 2} == { a: 1, c: 2 }; } to false (objects with same number of fields but different names are nonequal)", () => {2905 // Arrange2906 const ast: Block = [2907 {2908 statementKind: "return",2909 returnedValue: {2910 expressionKind: "binOp",2911 binOp: "equals",2912 leftOperand: {2913 expressionKind: "objectLit",2914 fields: [2915 {2916 fieldName: identifierIso.wrap("a"),2917 fieldValue: {2918 expressionKind: "numberLit",2919 value: 1,2920 },2921 },2922 {2923 fieldName: identifierIso.wrap("b"),2924 fieldValue: {2925 expressionKind: "numberLit",2926 value: 2,2927 },2928 },2929 ],2930 },2931 rightOperand: {2932 expressionKind: "objectLit",2933 fields: [2934 {2935 fieldName: identifierIso.wrap("a"),2936 fieldValue: {2937 expressionKind: "numberLit",2938 value: 1,2939 },2940 },2941 {2942 fieldName: identifierIso.wrap("c"),2943 fieldValue: {2944 expressionKind: "numberLit",2945 value: 2,2946 },2947 },2948 ],2949 },2950 },2951 },2952 ];2953 // Act2954 const evalResult = evaluateTestProgram(wrapBlock(ast));2955 // Assert2956 if (!isRight(evalResult)) {2957 throw new Error("Evaluation failed, should have succeeded");2958 }2959 if (evalResult.right.valueKind !== "boolean") {2960 throw new Error("Evaluated to non-boolean value");2961 }2962 expect(evalResult.right.isTrue).toBe(false);2963 });2964 it("Evaluates { return {} /= {}; } to false (empty objects are equal to each other)", () => {2965 // Arrange2966 const ast: Block = [2967 {2968 statementKind: "return",2969 returnedValue: {2970 expressionKind: "binOp",2971 binOp: "notEqual",2972 leftOperand: {2973 expressionKind: "objectLit",2974 fields: [],2975 },2976 rightOperand: {2977 expressionKind: "objectLit",2978 fields: [],2979 },2980 },2981 },2982 ];2983 // Act2984 const evalResult = evaluateTestProgram(wrapBlock(ast));2985 // Assert2986 if (!isRight(evalResult)) {2987 throw new Error("Evaluation failed, should have succeeded");2988 }2989 if (evalResult.right.valueKind !== "boolean") {2990 throw new Error("Evaluated to non-boolean value");2991 }2992 expect(evalResult.right.isTrue).toBe(false);2993 });2994 it("Evaluates { return { a: 1 } /= {}; } to true (nonempty objects aren't equal to empty objects", () => {2995 // Arrange2996 const ast: Block = [2997 {2998 statementKind: "return",2999 returnedValue: {3000 expressionKind: "binOp",3001 binOp: "notEqual",3002 leftOperand: {3003 expressionKind: "objectLit",3004 fields: [3005 {3006 fieldName: identifierIso.wrap("a"),3007 fieldValue: {3008 expressionKind: "numberLit",3009 value: 1,3010 },3011 },3012 ],3013 },3014 rightOperand: {3015 expressionKind: "objectLit",3016 fields: [],3017 },3018 },3019 },3020 ];3021 // Act3022 const evalResult = evaluateTestProgram(wrapBlock(ast));3023 // Assert3024 if (!isRight(evalResult)) {3025 throw new Error("Evaluation failed, should have succeeded");3026 }3027 if (evalResult.right.valueKind !== "boolean") {3028 throw new Error("Evaluated to non-boolean value");3029 }3030 expect(evalResult.right.isTrue).toBe(true);3031 });3032 it("Evaluates { return { a: 1} /= { a: 1}; } to false (objects with same fields and same values are equal)", () => {3033 // Arrange3034 const ast: Block = [3035 {3036 statementKind: "return",3037 returnedValue: {3038 expressionKind: "binOp",3039 binOp: "notEqual",3040 leftOperand: {3041 expressionKind: "objectLit",3042 fields: [3043 {3044 fieldName: identifierIso.wrap("a"),3045 fieldValue: {3046 expressionKind: "numberLit",3047 value: 1,3048 },3049 },3050 ],3051 },3052 rightOperand: {3053 expressionKind: "objectLit",3054 fields: [3055 {3056 fieldName: identifierIso.wrap("a"),3057 fieldValue: {3058 expressionKind: "numberLit",3059 value: 1,3060 },3061 },3062 ],3063 },3064 },3065 },3066 ];3067 // Act3068 const evalResult = evaluateTestProgram(wrapBlock(ast));3069 // Assert3070 if (!isRight(evalResult)) {3071 throw new Error("Evaluation failed, should have succeeded");3072 }3073 if (evalResult.right.valueKind !== "boolean") {3074 throw new Error("Evaluated to non-boolean value");3075 }3076 expect(evalResult.right.isTrue).toBe(false);3077 });3078 it("Evaluates { return { a: 1 } /= { a: 2 }; } to true (objects with same fields but different values are nonequal", () => {3079 // Arrange3080 const ast: Block = [3081 {3082 statementKind: "return",3083 returnedValue: {3084 expressionKind: "binOp",3085 binOp: "notEqual",3086 leftOperand: {3087 expressionKind: "objectLit",3088 fields: [3089 {3090 fieldName: identifierIso.wrap("a"),3091 fieldValue: {3092 expressionKind: "numberLit",3093 value: 1,3094 },3095 },3096 ],3097 },3098 rightOperand: {3099 expressionKind: "objectLit",3100 fields: [3101 {3102 fieldName: identifierIso.wrap("a"),3103 fieldValue: {3104 expressionKind: "numberLit",3105 value: 2,3106 },3107 },3108 ],3109 },3110 },3111 },3112 ];3113 // Act3114 const evalResult = evaluateTestProgram(wrapBlock(ast));3115 // Assert3116 if (!isRight(evalResult)) {3117 throw new Error("Evaluation failed, should have succeeded");3118 }3119 if (evalResult.right.valueKind !== "boolean") {3120 throw new Error("Evaluated to non-boolean value");3121 }3122 expect(evalResult.right.isTrue).toBe(true);3123 });3124 it("Evaluates { return { a: 1, b: 2 } /= { a: 1 }; } to true (extra fields cause objects to be nonequal)", () => {3125 // Arrange3126 const ast: Block = [3127 {3128 statementKind: "return",3129 returnedValue: {3130 expressionKind: "binOp",3131 binOp: "notEqual",3132 leftOperand: {3133 expressionKind: "objectLit",3134 fields: [3135 {3136 fieldName: identifierIso.wrap("a"),3137 fieldValue: {3138 expressionKind: "numberLit",3139 value: 1,3140 },3141 },3142 {3143 fieldName: identifierIso.wrap("b"),3144 fieldValue: {3145 expressionKind: "numberLit",3146 value: 2,3147 },3148 },3149 ],3150 },3151 rightOperand: {3152 expressionKind: "objectLit",3153 fields: [3154 {3155 fieldName: identifierIso.wrap("a"),3156 fieldValue: {3157 expressionKind: "numberLit",3158 value: 1,3159 },3160 },3161 ],3162 },3163 },3164 },3165 ];3166 // Act3167 const evalResult = evaluateTestProgram(wrapBlock(ast));3168 // Assert3169 if (!isRight(evalResult)) {3170 throw new Error("Evaluation failed, should have succeeded");3171 }3172 if (evalResult.right.valueKind !== "boolean") {3173 throw new Error("Evaluated to non-boolean value");3174 }3175 expect(evalResult.right.isTrue).toBe(true);3176 });3177 it("Evaluates { return { a: 1, b: 2 } /= { a: 1, b: 2 }; } to false (objects with multiple, identical fields are equal)", () => {3178 // Arrange3179 const ast: Block = [3180 {3181 statementKind: "return",3182 returnedValue: {3183 expressionKind: "binOp",3184 binOp: "notEqual",3185 leftOperand: {3186 expressionKind: "objectLit",3187 fields: [3188 {3189 fieldName: identifierIso.wrap("a"),3190 fieldValue: {3191 expressionKind: "numberLit",3192 value: 1,3193 },3194 },3195 {3196 fieldName: identifierIso.wrap("b"),3197 fieldValue: {3198 expressionKind: "numberLit",3199 value: 2,3200 },3201 },3202 ],3203 },3204 rightOperand: {3205 expressionKind: "objectLit",3206 fields: [3207 {3208 fieldName: identifierIso.wrap("a"),3209 fieldValue: {3210 expressionKind: "numberLit",3211 value: 1,3212 },3213 },3214 {3215 fieldName: identifierIso.wrap("b"),3216 fieldValue: {3217 expressionKind: "numberLit",3218 value: 2,3219 },3220 },3221 ],3222 },3223 },3224 },3225 ];3226 // Act3227 const evalResult = evaluateTestProgram(wrapBlock(ast));3228 // Assert3229 if (!isRight(evalResult)) {3230 throw new Error("Evaluation failed, should have succeeded");3231 }3232 if (evalResult.right.valueKind !== "boolean") {3233 throw new Error("Evaluated to non-boolean value");3234 }3235 expect(evalResult.right.isTrue).toBe(false);3236 });3237 });3238 describe("Multi-module programs", () => {3239 it("Evaluates module Source { let someNum = 1; } export someNum; module Main { import someNum from Source; return someNum; } to 1 (simple module use)", () => {3240 // Arrange3241 const sourceModule: Module = {3242 name: identifierIso.wrap("Source"),3243 body: [3244 {3245 statementKind: "varDecl",3246 variableName: identifierIso.wrap("someNum"),3247 },3248 {3249 statementKind: "assignment",3250 variableName: identifierIso.wrap("someNum"),3251 variableValue: {3252 expressionKind: "numberLit",3253 value: 1,3254 },3255 },3256 ],3257 exports: [identifierIso.wrap("someNum")],3258 };3259 const mainModule: Module = {3260 name: testModuleName,3261 body: [3262 {3263 statementKind: "import",3264 imports: [identifierIso.wrap("someNum")],3265 moduleName: identifierIso.wrap("Source"),3266 },3267 {3268 statementKind: "return",3269 returnedValue: {3270 expressionKind: "variableRef",3271 variableName: identifierIso.wrap("someNum"),3272 },3273 },3274 ],3275 exports: [],3276 };3277 // Act3278 const evalResult = evaluateTestProgram([sourceModule, mainModule]);3279 // Assert3280 if (!isRight(evalResult)) {3281 throw new Error("Evaluation failed, should have succeeded");3282 }3283 if (evalResult.right.valueKind !== "number") {3284 throw new Error("Evaluated to non-numeric value");3285 }3286 expect(evalResult.right.value).toBe(1);3287 });3288 it("Evaluates module Source { let num1 = 1; let num2 = 2; } export num1, num2; module Main { import num1, num2 from Source; return num1 + num2; } to 3 (multiple imports from same module)", () => {3289 // Arrange3290 const sourceModule: Module = {3291 name: identifierIso.wrap("Source"),3292 body: [3293 {3294 statementKind: "varDecl",3295 variableName: identifierIso.wrap("num1"),3296 },3297 {3298 statementKind: "assignment",3299 variableName: identifierIso.wrap("num1"),3300 variableValue: {3301 expressionKind: "numberLit",3302 value: 1,3303 },3304 },3305 {3306 statementKind: "varDecl",3307 variableName: identifierIso.wrap("num2"),3308 },3309 {3310 statementKind: "assignment",3311 variableName: identifierIso.wrap("num2"),3312 variableValue: {3313 expressionKind: "numberLit",3314 value: 2,3315 },3316 },3317 ],3318 exports: [identifierIso.wrap("num1"), identifierIso.wrap("num2")],3319 };3320 const mainModule: Module = {3321 name: testModuleName,3322 body: [3323 {3324 statementKind: "import",3325 imports: [identifierIso.wrap("num1"), identifierIso.wrap("num2")],3326 moduleName: identifierIso.wrap("Source"),3327 },3328 {3329 statementKind: "return",3330 returnedValue: {3331 expressionKind: "binOp",3332 binOp: "add",3333 leftOperand: {3334 expressionKind: "variableRef",3335 variableName: identifierIso.wrap("num1"),3336 },3337 rightOperand: {3338 expressionKind: "variableRef",3339 variableName: identifierIso.wrap("num2"),3340 },3341 },3342 },3343 ],3344 exports: [],3345 };3346 // Act3347 const evalResult = evaluateTestProgram([sourceModule, mainModule]);3348 // Assert3349 if (!isRight(evalResult)) {3350 throw new Error("Evaluation failed, should have succeeded");3351 }3352 if (evalResult.right.valueKind !== "number") {3353 throw new Error("Evaluated to non-numeric value");3354 }3355 expect(evalResult.right.value).toBe(3);3356 });3357 it("Only logs *once* from module Source { import print from Native; print(0); let num1 = 1; let num2 = 2; } export num1, num2; module Main { import num1, num2 from Source; return num1 + num2; } to 3 (imported modules are only evaluated once, even if imported multiple times)", () => {3358 // Arrange3359 const sourceModule: Module = {3360 name: identifierIso.wrap("Source"),3361 body: [3362 {3363 statementKind: "import",3364 imports: [identifierIso.wrap("print")],3365 moduleName: NATIVE_MODULE_NAME,3366 },3367 {3368 statementKind: "expression",3369 expression: {3370 expressionKind: "funcCall",3371 callee: {3372 expressionKind: "variableRef",3373 variableName: identifierIso.wrap("print"),3374 },3375 args: [3376 {3377 expressionKind: "numberLit",3378 value: 0,3379 },3380 ],3381 },3382 },3383 {3384 statementKind: "varDecl",3385 variableName: identifierIso.wrap("num1"),3386 },3387 {3388 statementKind: "assignment",3389 variableName: identifierIso.wrap("num1"),3390 variableValue: {3391 expressionKind: "numberLit",3392 value: 1,3393 },3394 },3395 {3396 statementKind: "varDecl",3397 variableName: identifierIso.wrap("num2"),3398 },3399 {3400 statementKind: "assignment",3401 variableName: identifierIso.wrap("num2"),3402 variableValue: {3403 expressionKind: "numberLit",3404 value: 2,3405 },3406 },3407 ],3408 exports: [identifierIso.wrap("num1"), identifierIso.wrap("num2")],3409 };3410 const mainModule: Module = {3411 name: testModuleName,3412 body: [3413 {3414 statementKind: "import",3415 imports: [identifierIso.wrap("num1"), identifierIso.wrap("num2")],3416 moduleName: identifierIso.wrap("Source"),3417 },3418 {3419 statementKind: "return",3420 returnedValue: {3421 expressionKind: "binOp",3422 binOp: "add",3423 leftOperand: {3424 expressionKind: "variableRef",3425 variableName: identifierIso.wrap("num1"),3426 },3427 rightOperand: {3428 expressionKind: "variableRef",3429 variableName: identifierIso.wrap("num2"),3430 },3431 },3432 },3433 ],3434 exports: [],3435 };3436 const spyImplementations: NativeFunctionImplementations = {3437 ...nativeFunctionsTestImplementations,3438 print: jest.fn(),3439 };3440 // Act3441 const evalResult = evaluateProgram(spyImplementations)([sourceModule, mainModule]);3442 // Assert3443 if (!isRight(evalResult)) {3444 throw new Error("Evaluation failed, should have succeeded");3445 }3446 if (evalResult.right.valueKind !== "number") {3447 throw new Error("Evaluated to non-numeric value");3448 }3449 expect(evalResult.right.value).toBe(3);3450 expect(spyImplementations.print).toHaveBeenCalledTimes(1);3451 });3452 });3453 describe("Other complex programs", () => {3454 it("Evaluates { let x; x = 1; function f() { let x; x = 2; return x; } return x + f(); } to 3 (checking that local variables shadow variables in outer scopes)", () => {3455 // Arrange3456 const ast: Block = [3457 {3458 statementKind: "varDecl",3459 variableName: identifierIso.wrap("x"),3460 },3461 {3462 statementKind: "assignment",3463 variableName: identifierIso.wrap("x"),3464 variableValue: {3465 expressionKind: "numberLit",3466 value: 1,3467 },3468 },3469 {3470 statementKind: "funcDecl",3471 functionName: identifierIso.wrap("f"),3472 argNames: [],3473 body: [3474 {3475 statementKind: "varDecl",3476 variableName: identifierIso.wrap("x"),3477 },3478 {3479 statementKind: "assignment",3480 variableName: identifierIso.wrap("x"),3481 variableValue: {3482 expressionKind: "numberLit",3483 value: 2,3484 },3485 },3486 {3487 statementKind: "return",3488 returnedValue: {3489 expressionKind: "variableRef",3490 variableName: identifierIso.wrap("x"),3491 },3492 },3493 ],3494 },3495 {3496 statementKind: "return",3497 returnedValue: {3498 expressionKind: "binOp",3499 binOp: "add",3500 leftOperand: {3501 expressionKind: "variableRef",3502 variableName: identifierIso.wrap("x"),3503 },3504 rightOperand: {3505 expressionKind: "funcCall",3506 args: [],3507 callee: {3508 expressionKind: "variableRef",3509 variableName: identifierIso.wrap("f"),3510 },3511 },3512 },3513 },3514 ];3515 // Act3516 const evalResult = evaluateTestProgram(wrapBlock(ast));3517 // Assert3518 if (!isRight(evalResult)) {3519 throw new Error("Evaluation failed, should have succeeded");3520 }3521 if (evalResult.right.valueKind !== "number") {3522 throw new Error("Evaluated to non-numeric value");3523 }3524 expect(evalResult.right.value).toBe(3);3525 });3526 it("Evaluates { let x; x = 1; function returnX() { return x; } x = 2; return returnX(); } to 2 (not 1) (checking that closures capture reference to mutable variables)", () => {3527 // Arrange3528 const ast: Block = [3529 {3530 statementKind: "varDecl",3531 variableName: identifierIso.wrap("x"),3532 },3533 {3534 statementKind: "assignment",3535 variableName: identifierIso.wrap("x"),3536 variableValue: {3537 expressionKind: "numberLit",3538 value: 1,3539 },3540 },3541 {3542 statementKind: "funcDecl",3543 functionName: identifierIso.wrap("returnX"),3544 argNames: [],3545 body: [3546 {3547 statementKind: "return",3548 returnedValue: {3549 expressionKind: "variableRef",3550 variableName: identifierIso.wrap("x"),3551 },3552 },3553 ],3554 },3555 {3556 statementKind: "assignment",3557 variableName: identifierIso.wrap("x"),3558 variableValue: {3559 expressionKind: "numberLit",3560 value: 2,3561 },3562 },3563 {3564 statementKind: "return",3565 returnedValue: {3566 expressionKind: "funcCall",3567 args: [],3568 callee: {3569 expressionKind: "variableRef",3570 variableName: identifierIso.wrap("returnX"),3571 },3572 },3573 },3574 ];3575 // Act3576 const evalResult = evaluateTestProgram(wrapBlock(ast));3577 // Assert3578 if (!isRight(evalResult)) {3579 throw new Error("Evaluation failed, should have succeeded");3580 }3581 if (evalResult.right.valueKind !== "number") {3582 throw new Error("Evaluated to non-numeric value");3583 }3584 expect(evalResult.right.value).toBe(2);3585 });3586 });3587 describe("Corner cases", () => {3588 it("Evaluates { function f() { return x; } return 1; } to 1, despite x not being in scope in f's definition", () => {3589 // Arrange3590 const ast: Block = [3591 {3592 statementKind: "funcDecl",3593 functionName: identifierIso.wrap("f"),3594 argNames: [],3595 body: [3596 {3597 statementKind: "return",3598 returnedValue: {3599 expressionKind: "variableRef",3600 variableName: identifierIso.wrap("x"),3601 },3602 },3603 ],3604 },3605 {3606 statementKind: "return",3607 returnedValue: {3608 expressionKind: "numberLit",3609 value: 1,3610 },3611 },3612 ];3613 // Act3614 const evalResult = evaluateTestProgram(wrapBlock(ast));3615 // Assert3616 if (!isRight(evalResult)) {3617 throw new Error("Evaluation failed, should have succeeded");3618 }3619 if (evalResult.right.valueKind !== "number") {3620 throw new Error("Evaluated to non-numeric value");3621 }3622 expect(evalResult.right.value).toBe(1);3623 });3624 it("Evaluates { let x; x = 1; if (true) { x = 2; } else {} return x; } to 2 (changes to non-shadowed variables propagate to outer scopes)", () => {3625 // Arrange3626 const ast: Block = [3627 {3628 statementKind: "varDecl",3629 variableName: identifierIso.wrap("x"),3630 },3631 {3632 statementKind: "assignment",3633 variableName: identifierIso.wrap("x"),3634 variableValue: {3635 expressionKind: "numberLit",3636 value: 1,3637 },3638 },3639 {3640 statementKind: "if",3641 condition: {3642 expressionKind: "booleanLit",3643 isTrue: true,3644 },3645 trueBody: [3646 {3647 statementKind: "assignment",3648 variableName: identifierIso.wrap("x"),3649 variableValue: {3650 expressionKind: "numberLit",3651 value: 2,3652 },3653 },3654 ],3655 falseBody: [],3656 },3657 {3658 statementKind: "return",3659 returnedValue: {3660 expressionKind: "variableRef",3661 variableName: identifierIso.wrap("x"),3662 },3663 },3664 ];3665 // Act3666 const evalResult = evaluateTestProgram(wrapBlock(ast));3667 // Assert3668 if (!isRight(evalResult)) {3669 throw new Error("Evaluation failed, should have succeeded");3670 }3671 if (evalResult.right.valueKind !== "number") {3672 throw new Error("Evaluated to non-numeric value");3673 }3674 expect(evalResult.right.value).toBe(2);3675 });3676 it("Evaluates { let x; if (true) { x = 1; } else { } return x; } to 1 (variables can be declared in an outer scope and assigned in an inner scope) ", () => {3677 // Arrange3678 const ast: Block = [3679 {3680 statementKind: "varDecl",3681 variableName: identifierIso.wrap("x"),3682 },3683 {3684 statementKind: "if",3685 condition: {3686 expressionKind: "booleanLit",3687 isTrue: true,3688 },3689 trueBody: [3690 {3691 statementKind: "assignment",3692 variableName: identifierIso.wrap("x"),3693 variableValue: {3694 expressionKind: "numberLit",3695 value: 1,3696 },3697 },3698 ],3699 falseBody: [],3700 },3701 {3702 statementKind: "return",3703 returnedValue: {3704 expressionKind: "variableRef",3705 variableName: identifierIso.wrap("x"),3706 },3707 },3708 ];3709 // Act3710 const evalResult = evaluateTestProgram(wrapBlock(ast));3711 // Assert3712 if (!isRight(evalResult)) {3713 throw new Error("Evaluation failed, should have succeeded");3714 }3715 if (evalResult.right.valueKind !== "number") {3716 throw new Error("Evaluated to non-numeric value");3717 }3718 expect(evalResult.right.value).toBe(1);3719 });3720 // similar to the error in https://craftinginterpreters.com/resolving-and-binding.html3721 // program is the same as examples/closure_shadowing_interaction.wheel3722 it("Does *not* let shadowing variables modify existing closures", () => {3723 // Arrange3724 const ast: Block = [3725 {3726 statementKind: "varDecl",3727 variableName: identifierIso.wrap("accumulator"),3728 },3729 {3730 statementKind: "assignment",3731 variableName: identifierIso.wrap("accumulator"),3732 variableValue: {3733 expressionKind: "numberLit",3734 value: 0,3735 },3736 },3737 {3738 statementKind: "varDecl",3739 variableName: identifierIso.wrap("a"),3740 },3741 {3742 statementKind: "assignment",3743 variableName: identifierIso.wrap("a"),3744 variableValue: {3745 expressionKind: "numberLit",3746 value: 0,3747 },3748 },3749 {3750 statementKind: "funcDecl",3751 functionName: identifierIso.wrap("addByA"),3752 argNames: [],3753 body: [3754 {3755 statementKind: "assignment",3756 variableName: identifierIso.wrap("accumulator"),3757 variableValue: {3758 expressionKind: "binOp",3759 binOp: "add",3760 leftOperand: {3761 expressionKind: "variableRef",3762 variableName: identifierIso.wrap("accumulator"),3763 },3764 rightOperand: {3765 expressionKind: "variableRef",3766 variableName: identifierIso.wrap("a"),3767 },3768 },3769 },3770 {3771 statementKind: "return",3772 returnedValue: {3773 expressionKind: "numberLit",3774 value: 0,3775 },3776 },3777 ],3778 },3779 {3780 statementKind: "varDecl",3781 variableName: identifierIso.wrap("throwaway1"),3782 },3783 {3784 statementKind: "assignment",3785 variableName: identifierIso.wrap("throwaway1"),3786 variableValue: {3787 expressionKind: "funcCall",3788 args: [],3789 callee: {3790 expressionKind: "variableRef",3791 variableName: identifierIso.wrap("addByA"),3792 },3793 },3794 },3795 {3796 statementKind: "funcDecl",3797 functionName: identifierIso.wrap("inner"),3798 argNames: [],3799 body: [3800 {3801 statementKind: "varDecl",3802 variableName: identifierIso.wrap("a"),3803 },3804 {3805 statementKind: "assignment",3806 variableName: identifierIso.wrap("a"),3807 variableValue: {3808 expressionKind: "numberLit",3809 value: 99,3810 },3811 },3812 {3813 statementKind: "varDecl",3814 variableName: identifierIso.wrap("throwaway2"),3815 },3816 {3817 statementKind: "assignment",3818 variableName: identifierIso.wrap("throwaway2"),3819 variableValue: {3820 expressionKind: "funcCall",3821 args: [],3822 callee: {3823 expressionKind: "variableRef",3824 variableName: identifierIso.wrap("addByA"),3825 },3826 },3827 },3828 {3829 statementKind: "return",3830 returnedValue: {3831 expressionKind: "numberLit",3832 value: 0,3833 },3834 },3835 ],3836 },3837 {3838 statementKind: "varDecl",3839 variableName: identifierIso.wrap("throwaway3"),3840 },3841 {3842 statementKind: "assignment",3843 variableName: identifierIso.wrap("throwaway3"),3844 variableValue: {3845 expressionKind: "funcCall",3846 args: [],3847 callee: {3848 expressionKind: "variableRef",3849 variableName: identifierIso.wrap("inner"),3850 },3851 },3852 },3853 {3854 statementKind: "return",3855 returnedValue: {3856 expressionKind: "variableRef",3857 variableName: identifierIso.wrap("accumulator"),3858 },3859 },3860 ];3861 // Act3862 const evalResult = evaluateTestProgram(wrapBlock(ast));3863 // Assert3864 if (!isRight(evalResult)) {3865 throw new Error("Evaluation failed, should have succeeded");3866 }3867 if (evalResult.right.valueKind !== "number") {3868 throw new Error("Evaluated to non-numeric value");3869 }3870 // returning 99 indicates that a = 99 was used when inner() called3871 expect(evalResult.right.value).toBe(0);3872 });3873 });3874 });3875 describe("Failed evaluations", () => {3876 describe("NotInScope errors", () => {3877 it("Recognizes a NotInScope error for { return x; }", () => {3878 // Arrange3879 const ast: Block = [3880 {3881 statementKind: "return",3882 returnedValue: {3883 expressionKind: "variableRef",3884 variableName: identifierIso.wrap("x"),3885 },3886 },3887 ];3888 // Act3889 const evalResult = evaluateTestProgram(wrapBlock(ast));3890 // Assert3891 if (!isLeft(evalResult)) {3892 throw new Error("Evaluation succeeded, should have failed");3893 }3894 if (evalResult.left.runtimeErrorKind !== "notInScope") {3895 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of NotInScope error`);3896 }3897 expect(evalResult.left.outOfScopeIdentifier).toBe("x");3898 });3899 it("Recognizes a NotInScope error for { x = 1; } (undeclared variable)", () => {3900 // Arrange3901 const ast: Block = [3902 {3903 statementKind: "assignment",3904 variableName: identifierIso.wrap("x"),3905 variableValue: {3906 expressionKind: "numberLit",3907 value: 1,3908 },3909 },3910 ];3911 // Act3912 const evalResult = evaluateTestProgram(wrapBlock(ast));3913 // Assert3914 if (!isLeft(evalResult)) {3915 throw new Error("Evaluation succeeded, should have failed");3916 }3917 if (evalResult.left.runtimeErrorKind !== "notInScope") {3918 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of NotInScope error`);3919 }3920 expect(evalResult.left.outOfScopeIdentifier).toBe("x");3921 });3922 it("Recognizes a NotInScope error for { if (true) { let x; x = 1; } else {} return x; } (variables declared in an if statement's true block's scope don't exist in outer scopes)", () => {3923 // Arrange3924 const ast: Block = [3925 {3926 statementKind: "if",3927 condition: {3928 expressionKind: "booleanLit",3929 isTrue: true,3930 },3931 trueBody: [3932 {3933 statementKind: "varDecl",3934 variableName: identifierIso.wrap("x"),3935 },3936 {3937 statementKind: "assignment",3938 variableName: identifierIso.wrap("x"),3939 variableValue: {3940 expressionKind: "numberLit",3941 value: 1,3942 },3943 },3944 ],3945 falseBody: [],3946 },3947 {3948 statementKind: "return",3949 returnedValue: {3950 expressionKind: "variableRef",3951 variableName: identifierIso.wrap("x"),3952 },3953 },3954 ];3955 // Act3956 const evalResult = evaluateTestProgram(wrapBlock(ast));3957 // Assert3958 if (!isLeft(evalResult)) {3959 throw new Error("Evaluation succeeded, should have failed");3960 }3961 if (evalResult.left.runtimeErrorKind !== "notInScope") {3962 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of NotInScope error`);3963 }3964 expect(evalResult.left.outOfScopeIdentifier).toBe("x");3965 });3966 it("Recognizes a NotInScope error for { if (false) {} else { let x; x = 1; } return x; } (variables declared in an if statement's false block's scope don't exist in outer scopes)", () => {3967 // Arrange3968 const ast: Block = [3969 {3970 statementKind: "if",3971 condition: {3972 expressionKind: "booleanLit",3973 isTrue: false,3974 },3975 trueBody: [],3976 falseBody: [3977 {3978 statementKind: "varDecl",3979 variableName: identifierIso.wrap("x"),3980 },3981 {3982 statementKind: "assignment",3983 variableName: identifierIso.wrap("x"),3984 variableValue: {3985 expressionKind: "numberLit",3986 value: 1,3987 },3988 },3989 ],3990 },3991 {3992 statementKind: "return",3993 returnedValue: {3994 expressionKind: "variableRef",3995 variableName: identifierIso.wrap("x"),3996 },3997 },3998 ];3999 // Act4000 const evalResult = evaluateTestProgram(wrapBlock(ast));4001 // Assert4002 if (!isLeft(evalResult)) {4003 throw new Error("Evaluation succeeded, should have failed");4004 }4005 if (evalResult.left.runtimeErrorKind !== "notInScope") {4006 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of NotInScope error`);4007 }4008 expect(evalResult.left.outOfScopeIdentifier).toBe("x");4009 });4010 it("Recognizes a NotInScope error for { let x; x = 0; while (x < 1) { let y; x = x + 1; } return y; } (variables declared in a while statement's block's scope don't exist in outer scopes)", () => {4011 // Arrange4012 const ast: Block = [4013 {4014 statementKind: "varDecl",4015 variableName: identifierIso.wrap("x"),4016 },4017 {4018 statementKind: "assignment",4019 variableName: identifierIso.wrap("x"),4020 variableValue: {4021 expressionKind: "numberLit",4022 value: 0,4023 },4024 },4025 {4026 statementKind: "while",4027 condition: {4028 expressionKind: "binOp",4029 binOp: "lessThan",4030 leftOperand: {4031 expressionKind: "variableRef",4032 variableName: identifierIso.wrap("x"),4033 },4034 rightOperand: {4035 expressionKind: "numberLit",4036 value: 1,4037 },4038 },4039 body: [4040 {4041 statementKind: "varDecl",4042 variableName: identifierIso.wrap("y"),4043 },4044 {4045 statementKind: "assignment",4046 variableName: identifierIso.wrap("x"),4047 variableValue: {4048 expressionKind: "binOp",4049 binOp: "add",4050 leftOperand: {4051 expressionKind: "variableRef",4052 variableName: identifierIso.wrap("x"),4053 },4054 rightOperand: {4055 expressionKind: "numberLit",4056 value: 1,4057 },4058 },4059 },4060 ],4061 },4062 {4063 statementKind: "return",4064 returnedValue: {4065 expressionKind: "variableRef",4066 variableName: identifierIso.wrap("y"),4067 },4068 },4069 ];4070 // Act4071 const evalResult = evaluateTestProgram(wrapBlock(ast));4072 // Assert4073 if (!isLeft(evalResult)) {4074 throw new Error("Evaluation succeeded, should have failed");4075 }4076 if (evalResult.left.runtimeErrorKind !== "notInScope") {4077 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of NotInScope error`);4078 }4079 expect(evalResult.left.outOfScopeIdentifier).toBe("y");4080 });4081 it("Recognizes a NotInScope error for { function f() { let x; return 1; } return f() + x; } (variables declared local to a function don't exist in outer scopes)", () => {4082 // Arrange4083 const ast: Block = [4084 {4085 statementKind: "funcDecl",4086 functionName: identifierIso.wrap("f"),4087 argNames: [],4088 body: [4089 {4090 statementKind: "varDecl",4091 variableName: identifierIso.wrap("x"),4092 },4093 {4094 statementKind: "return",4095 returnedValue: {4096 expressionKind: "numberLit",4097 value: 1,4098 },4099 },4100 ],4101 },4102 {4103 statementKind: "return",4104 returnedValue: {4105 expressionKind: "binOp",4106 binOp: "add",4107 leftOperand: {4108 expressionKind: "funcCall",4109 callee: {4110 expressionKind: "variableRef",4111 variableName: identifierIso.wrap("f"),4112 },4113 args: [],4114 },4115 rightOperand: {4116 expressionKind: "variableRef",4117 variableName: identifierIso.wrap("x"),4118 },4119 },4120 },4121 ];4122 // Act4123 const evalResult = evaluateTestProgram(wrapBlock(ast));4124 // Assert4125 if (!isLeft(evalResult)) {4126 throw new Error("Evaluation succeeded, should have failed");4127 }4128 if (evalResult.left.runtimeErrorKind !== "notInScope") {4129 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of NotInScope error`);4130 }4131 expect(evalResult.left.outOfScopeIdentifier).toBe("x");4132 });4133 it("Recognizes a NotInScope error for { function f() { return x; } return f(); }", () => {4134 // Arrange4135 const ast: Block = [4136 {4137 statementKind: "funcDecl",4138 functionName: identifierIso.wrap("f"),4139 argNames: [],4140 body: [4141 {4142 statementKind: "return",4143 returnedValue: {4144 expressionKind: "variableRef",4145 variableName: identifierIso.wrap("x"),4146 },4147 },4148 ],4149 },4150 {4151 statementKind: "return",4152 returnedValue: {4153 expressionKind: "funcCall",4154 args: [],4155 callee: {4156 expressionKind: "variableRef",4157 variableName: identifierIso.wrap("f"),4158 },4159 },4160 },4161 ];4162 // Act4163 const evalResult = evaluateTestProgram(wrapBlock(ast));4164 // Assert4165 if (!isLeft(evalResult)) {4166 throw new Error("Evaluation succeeded, should have failed");4167 }4168 if (evalResult.left.runtimeErrorKind !== "notInScope") {4169 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of NotInScope error`);4170 }4171 expect(evalResult.left.outOfScopeIdentifier).toBe("x");4172 });4173 it("Recognizes a NotInScope error for module Source { } export nonexistent; module Main { import nonexistent from Source; }", () => {4174 // Arrange4175 const sourceModule: Module = {4176 name: identifierIso.wrap("Source"),4177 body: [],4178 exports: [identifierIso.wrap("nonexistent")],4179 };4180 const mainModule: Module = {4181 name: testModuleName,4182 body: [4183 {4184 statementKind: "import",4185 imports: [identifierIso.wrap("nonexistent")],4186 moduleName: identifierIso.wrap("Source"),4187 },4188 ],4189 exports: [],4190 };4191 // Act4192 const evalResult = evaluateTestProgram([sourceModule, mainModule]);4193 // Assert4194 if (!isLeft(evalResult)) {4195 throw new Error("Evaluation succeeded, should have failed");4196 }4197 if (evalResult.left.runtimeErrorKind !== "notInScope") {4198 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of NotInScope error`);4199 }4200 expect(evalResult.left.outOfScopeIdentifier).toBe("nonexistent");4201 });4202 });4203 describe("NotFunction errors", () => {4204 it("Recognizes a NotFunction error for { return 1(); }", () => {4205 // Arrange4206 const ast: Block = [4207 {4208 statementKind: "return",4209 returnedValue: {4210 expressionKind: "funcCall",4211 args: [],4212 callee: {4213 expressionKind: "numberLit",4214 value: 1,4215 },4216 },4217 },4218 ];4219 // Act4220 const evalResult = evaluateTestProgram(wrapBlock(ast));4221 // Assert4222 if (!isLeft(evalResult)) {4223 throw new Error("Evaluation succeeded, should have failed");4224 }4225 if (evalResult.left.runtimeErrorKind !== "notFunction") {4226 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of NotFunction error`);4227 }4228 expect(evalResult.left.nonFunctionType).toBe("number");4229 });4230 });4231 describe("TypeMismatch errors", () => {4232 test.each([["add" as const], ["subtract" as const], ["multiply" as const], ["divide" as const]])(4233 "Recognizes a TypeMismatch error for non-numbers on LHS of %s operations",4234 (binOp) => {4235 // Arrange4236 const ast: Block = [4237 {4238 statementKind: "funcDecl",4239 functionName: identifierIso.wrap("f"),4240 argNames: [],4241 body: [],4242 },4243 {4244 statementKind: "return",4245 returnedValue: {4246 expressionKind: "binOp",4247 binOp,4248 leftOperand: {4249 expressionKind: "variableRef",4250 variableName: identifierIso.wrap("f"),4251 },4252 rightOperand: {4253 expressionKind: "numberLit",4254 value: 1,4255 },4256 },4257 },4258 ];4259 // Act4260 const evalResult = evaluateTestProgram(wrapBlock(ast));4261 // Assert4262 if (!isLeft(evalResult)) {4263 throw new Error("Evaluation succeeded, should have failed");4264 }4265 if (evalResult.left.runtimeErrorKind !== "typeMismatch") {4266 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of TypeMismatch error`);4267 }4268 expect(evalResult.left.expectedTypes).toEqual(["number"]);4269 expect(evalResult.left.actualType).toBe("closure");4270 },4271 );4272 test.each([["add" as const], ["subtract" as const], ["multiply" as const], ["divide" as const]])(4273 "Recognizes a TypeMismatch error for non-numbers on RHS of %s operations",4274 (binOp) => {4275 // Arrange4276 const ast: Block = [4277 {4278 statementKind: "funcDecl",4279 functionName: identifierIso.wrap("f"),4280 argNames: [],4281 body: [],4282 },4283 {4284 statementKind: "return",4285 returnedValue: {4286 expressionKind: "binOp",4287 binOp,4288 leftOperand: {4289 expressionKind: "numberLit",4290 value: 1,4291 },4292 rightOperand: {4293 expressionKind: "variableRef",4294 variableName: identifierIso.wrap("f"),4295 },4296 },4297 },4298 ];4299 // Act4300 const evalResult = evaluateTestProgram(wrapBlock(ast));4301 // Assert4302 if (!isLeft(evalResult)) {4303 throw new Error("Evaluation succeeded, should have failed");4304 }4305 if (evalResult.left.runtimeErrorKind !== "typeMismatch") {4306 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of TypeMismatch error`);4307 }4308 expect(evalResult.left.expectedTypes).toEqual(["number"]);4309 expect(evalResult.left.actualType).toBe("closure");4310 },4311 );4312 it("Recognizes a TypeMismatch error for { if(1) {} else {} } (non-boolean in if statement's condition", () => {4313 // Arrange4314 const ast: Block = [4315 {4316 statementKind: "if",4317 condition: {4318 expressionKind: "numberLit",4319 value: 1,4320 },4321 trueBody: [],4322 falseBody: [],4323 },4324 ];4325 // Act4326 const evalResult = evaluateTestProgram(wrapBlock(ast));4327 // Assert4328 if (!isLeft(evalResult)) {4329 throw new Error("Evaluation succeeded, should have failed");4330 }4331 if (evalResult.left.runtimeErrorKind !== "typeMismatch") {4332 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of TypeMismatch error`);4333 }4334 expect(evalResult.left.expectedTypes).toEqual(["boolean"]);4335 });4336 it("Recognizes a TypeMismatch error for { while(2) {} } (non-boolean in while statement's condition", () => {4337 // Arrange4338 const ast: Block = [4339 {4340 statementKind: "while",4341 condition: {4342 expressionKind: "numberLit",4343 value: 2,4344 },4345 body: [],4346 },4347 ];4348 // Act4349 const evalResult = evaluateTestProgram(wrapBlock(ast));4350 // Assert4351 if (!isLeft(evalResult)) {4352 throw new Error("Evaluation succeeded, should have failed");4353 }4354 if (evalResult.left.runtimeErrorKind !== "typeMismatch") {4355 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of TypeMismatch error`);4356 }4357 expect(evalResult.left.expectedTypes).toEqual(["boolean"]);4358 });4359 test.each([4360 ["lessThan" as const],4361 ["greaterThan" as const],4362 ["lessThanEquals" as const],4363 ["greaterThanEquals" as const],4364 ])("Recognizes a TypeMismatch error for non-numbers in %s relations", (binOp) => {4365 // Arrange4366 const ast: Block = [4367 {4368 statementKind: "return",4369 returnedValue: {4370 expressionKind: "binOp",4371 binOp,4372 leftOperand: {4373 expressionKind: "numberLit",4374 value: 1,4375 },4376 rightOperand: {4377 expressionKind: "booleanLit",4378 isTrue: true,4379 },4380 },4381 },4382 ];4383 // Act4384 const evalResult = evaluateTestProgram(wrapBlock(ast));4385 // Assert4386 if (!isLeft(evalResult)) {4387 throw new Error("Evaluation succeeded, should have failed");4388 }4389 if (evalResult.left.runtimeErrorKind !== "typeMismatch") {4390 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of TypeMismatch error`);4391 }4392 expect(evalResult.left.expectedTypes).toEqual(["number"]);4393 });4394 it("Recognizes a TypeMismatch error for { return true & 1; } (non-boolean in logical and)", () => {4395 // Arrange4396 const ast: Block = [4397 {4398 statementKind: "return",4399 returnedValue: {4400 expressionKind: "binOp",4401 binOp: "and",4402 leftOperand: {4403 expressionKind: "booleanLit",4404 isTrue: true,4405 },4406 rightOperand: {4407 expressionKind: "numberLit",4408 value: 1,4409 },4410 },4411 },4412 ];4413 // Act4414 const evalResult = evaluateTestProgram(wrapBlock(ast));4415 // Assert4416 if (!isLeft(evalResult)) {4417 throw new Error("Evaluation succeeded, should have failed");4418 }4419 if (evalResult.left.runtimeErrorKind !== "typeMismatch") {4420 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of TypeMismatch error`);4421 }4422 expect(evalResult.left.expectedTypes).toEqual(["boolean"]);4423 });4424 it("Recognizes a TypeMismatch error for { return false | 2; } (non-boolean in logical or)", () => {4425 // Arrange4426 const ast: Block = [4427 {4428 statementKind: "return",4429 returnedValue: {4430 expressionKind: "binOp",4431 binOp: "or",4432 leftOperand: {4433 expressionKind: "booleanLit",4434 isTrue: false,4435 },4436 rightOperand: {4437 expressionKind: "numberLit",4438 value: 2,4439 },4440 },4441 },4442 ];4443 // Act4444 const evalResult = evaluateTestProgram(wrapBlock(ast));4445 // Assert4446 if (!isLeft(evalResult)) {4447 throw new Error("Evaluation succeeded, should have failed");4448 }4449 if (evalResult.left.runtimeErrorKind !== "typeMismatch") {4450 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of TypeMismatch error`);4451 }4452 expect(evalResult.left.expectedTypes).toEqual(["boolean"]);4453 });4454 it("Recognizes a TypeMismatch error for { return !3; } (non-boolean in logical not)", () => {4455 // Arrange4456 const ast: Block = [4457 {4458 statementKind: "return",4459 returnedValue: {4460 expressionKind: "unaryOp",4461 unaryOp: "not",4462 operand: {4463 expressionKind: "numberLit",4464 value: 3,4465 },4466 },4467 },4468 ];4469 // Act4470 const evalResult = evaluateTestProgram(wrapBlock(ast));4471 // Assert4472 if (!isLeft(evalResult)) {4473 throw new Error("Evaluation succeeded, should have failed");4474 }4475 if (evalResult.left.runtimeErrorKind !== "typeMismatch") {4476 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of TypeMismatch error`);4477 }4478 expect(evalResult.left.expectedTypes).toEqual(["boolean"]);4479 });4480 it("Recognizes a TypeMismatch error for { return -true; } (non-number in unary negation)", () => {4481 // Arrange4482 const ast: Block = [4483 {4484 statementKind: "return",4485 returnedValue: {4486 expressionKind: "unaryOp",4487 unaryOp: "negative",4488 operand: {4489 expressionKind: "booleanLit",4490 isTrue: true,4491 },4492 },4493 },4494 ];4495 // Act4496 const evalResult = evaluateTestProgram(wrapBlock(ast));4497 // Assert4498 if (!isLeft(evalResult)) {4499 throw new Error("Evaluation succeeded, should have failed");4500 }4501 if (evalResult.left.runtimeErrorKind !== "typeMismatch") {4502 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of TypeMismatch error`);4503 }4504 expect(evalResult.left.expectedTypes).toEqual(["number"]);4505 });4506 it("Recognizes a TypeMismatch error for { return 1 == true; } (mismatched types in equals expression)", () => {4507 // Arrange4508 const ast: Block = [4509 {4510 statementKind: "return",4511 returnedValue: {4512 expressionKind: "binOp",4513 binOp: "equals",4514 leftOperand: {4515 expressionKind: "numberLit",4516 value: 1,4517 },4518 rightOperand: {4519 expressionKind: "booleanLit",4520 isTrue: true,4521 },4522 },4523 },4524 ];4525 // Act4526 const evalResult = evaluateTestProgram(wrapBlock(ast));4527 // Assert4528 if (!isLeft(evalResult)) {4529 throw new Error("Evaluation succeeded, should have failed");4530 }4531 if (evalResult.left.runtimeErrorKind !== "typeMismatch") {4532 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of TypeMismatch error`);4533 }4534 expect(evalResult.left.expectedTypes).toEqual(["number"]);4535 });4536 it("Recognizes a TypeMismatch error for { return true == 1; } (mismatched types in equals expression)", () => {4537 // Arrange4538 const ast: Block = [4539 {4540 statementKind: "return",4541 returnedValue: {4542 expressionKind: "binOp",4543 binOp: "equals",4544 leftOperand: {4545 expressionKind: "booleanLit",4546 isTrue: true,4547 },4548 rightOperand: {4549 expressionKind: "numberLit",4550 value: 1,4551 },4552 },4553 },4554 ];4555 // Act4556 const evalResult = evaluateTestProgram(wrapBlock(ast));4557 // Assert4558 if (!isLeft(evalResult)) {4559 throw new Error("Evaluation succeeded, should have failed");4560 }4561 if (evalResult.left.runtimeErrorKind !== "typeMismatch") {4562 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of TypeMismatch error`);4563 }4564 expect(evalResult.left.expectedTypes).toEqual(["boolean"]);4565 });4566 it("Recognizes a TypeMismatch error for { return 1 /= true; } (mismatched types in not-equal expression)", () => {4567 // Arrange4568 const ast: Block = [4569 {4570 statementKind: "return",4571 returnedValue: {4572 expressionKind: "binOp",4573 binOp: "notEqual",4574 leftOperand: {4575 expressionKind: "numberLit",4576 value: 1,4577 },4578 rightOperand: {4579 expressionKind: "booleanLit",4580 isTrue: true,4581 },4582 },4583 },4584 ];4585 // Act4586 const evalResult = evaluateTestProgram(wrapBlock(ast));4587 // Assert4588 if (!isLeft(evalResult)) {4589 throw new Error("Evaluation succeeded, should have failed");4590 }4591 if (evalResult.left.runtimeErrorKind !== "typeMismatch") {4592 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of TypeMismatch error`);4593 }4594 expect(evalResult.left.expectedTypes).toEqual(["number"]);4595 });4596 it("Recognizes a TypeMismatch error for { return true /= 1; } (mismatched types in not-equal expression)", () => {4597 // Arrange4598 const ast: Block = [4599 {4600 statementKind: "return",4601 returnedValue: {4602 expressionKind: "binOp",4603 binOp: "notEqual",4604 leftOperand: {4605 expressionKind: "booleanLit",4606 isTrue: true,4607 },4608 rightOperand: {4609 expressionKind: "numberLit",4610 value: 1,4611 },4612 },4613 },4614 ];4615 // Act4616 const evalResult = evaluateTestProgram(wrapBlock(ast));4617 // Assert4618 if (!isLeft(evalResult)) {4619 throw new Error("Evaluation succeeded, should have failed");4620 }4621 if (evalResult.left.runtimeErrorKind !== "typeMismatch") {4622 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of TypeMismatch error`);4623 }4624 expect(evalResult.left.expectedTypes).toEqual(["boolean"]);4625 });4626 it("Recognizes a TypeMismatch error for { function f() {} return f == 1; } (closure on LHS of equals expression)", () => {4627 // Arrange4628 const ast: Block = [4629 {4630 statementKind: "funcDecl",4631 functionName: identifierIso.wrap("f"),4632 argNames: [],4633 body: [],4634 },4635 {4636 statementKind: "return",4637 returnedValue: {4638 expressionKind: "binOp",4639 binOp: "equals",4640 leftOperand: {4641 expressionKind: "variableRef",4642 variableName: identifierIso.wrap("f"),4643 },4644 rightOperand: {4645 expressionKind: "numberLit",4646 value: 1,4647 },4648 },4649 },4650 ];4651 // Act4652 const evalResult = evaluateTestProgram(wrapBlock(ast));4653 // Assert4654 if (!isLeft(evalResult)) {4655 throw new Error("Evaluation succeeded, should have failed");4656 }4657 if (evalResult.left.runtimeErrorKind !== "typeMismatch") {4658 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of TypeMismatch error`);4659 }4660 expect(evalResult.left.expectedTypes).toContain("boolean");4661 expect(evalResult.left.expectedTypes).toContain("number");4662 expect(evalResult.left.actualType).toBe("closure");4663 });4664 it("Recognizes a TypeMismatch error for { function f() {} return 1 == f; } (closure on RHS of equals expression)", () => {4665 // Arrange4666 const ast: Block = [4667 {4668 statementKind: "funcDecl",4669 functionName: identifierIso.wrap("f"),4670 argNames: [],4671 body: [],4672 },4673 {4674 statementKind: "return",4675 returnedValue: {4676 expressionKind: "binOp",4677 binOp: "equals",4678 leftOperand: {4679 expressionKind: "numberLit",4680 value: 1,4681 },4682 rightOperand: {4683 expressionKind: "variableRef",4684 variableName: identifierIso.wrap("f"),4685 },4686 },4687 },4688 ];4689 // Act4690 const evalResult = evaluateTestProgram(wrapBlock(ast));4691 // Assert4692 if (!isLeft(evalResult)) {4693 throw new Error("Evaluation succeeded, should have failed");4694 }4695 if (evalResult.left.runtimeErrorKind !== "typeMismatch") {4696 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of TypeMismatch error`);4697 }4698 expect(evalResult.left.expectedTypes).toContain("boolean");4699 expect(evalResult.left.expectedTypes).toContain("number");4700 expect(evalResult.left.actualType).toBe("closure");4701 });4702 it("Recognizes a TypeMismatch error for { function f() {} return f /= 1; } (closure on LHS of not-equal expression)", () => {4703 // Arrange4704 const ast: Block = [4705 {4706 statementKind: "funcDecl",4707 functionName: identifierIso.wrap("f"),4708 argNames: [],4709 body: [],4710 },4711 {4712 statementKind: "return",4713 returnedValue: {4714 expressionKind: "binOp",4715 binOp: "notEqual",4716 leftOperand: {4717 expressionKind: "variableRef",4718 variableName: identifierIso.wrap("f"),4719 },4720 rightOperand: {4721 expressionKind: "numberLit",4722 value: 1,4723 },4724 },4725 },4726 ];4727 // Act4728 const evalResult = evaluateTestProgram(wrapBlock(ast));4729 // Assert4730 if (!isLeft(evalResult)) {4731 throw new Error("Evaluation succeeded, should have failed");4732 }4733 if (evalResult.left.runtimeErrorKind !== "typeMismatch") {4734 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of TypeMismatch error`);4735 }4736 expect(evalResult.left.expectedTypes).toContain("boolean");4737 expect(evalResult.left.expectedTypes).toContain("number");4738 expect(evalResult.left.actualType).toBe("closure");4739 });4740 it("Recognizes a TypeMismatch error for { function f() {} return 1 /= f; } (closure on RHS of not-equal expression)", () => {4741 // Arrange4742 const ast: Block = [4743 {4744 statementKind: "funcDecl",4745 functionName: identifierIso.wrap("f"),4746 argNames: [],4747 body: [],4748 },4749 {4750 statementKind: "return",4751 returnedValue: {4752 expressionKind: "binOp",4753 binOp: "notEqual",4754 leftOperand: {4755 expressionKind: "numberLit",4756 value: 1,4757 },4758 rightOperand: {4759 expressionKind: "variableRef",4760 variableName: identifierIso.wrap("f"),4761 },4762 },4763 },4764 ];4765 // Act4766 const evalResult = evaluateTestProgram(wrapBlock(ast));4767 // Assert4768 if (!isLeft(evalResult)) {4769 throw new Error("Evaluation succeeded, should have failed");4770 }4771 if (evalResult.left.runtimeErrorKind !== "typeMismatch") {4772 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of TypeMismatch error`);4773 }4774 expect(evalResult.left.expectedTypes).toContain("boolean");4775 expect(evalResult.left.expectedTypes).toContain("number");4776 expect(evalResult.left.actualType).toBe("closure");4777 });4778 it("Recognizes a TypeMismatch error for { import clock from Native; return clock == 1; } (native function on LHS of equals expression)", () => {4779 // Arrange4780 const ast: Block = [4781 {4782 statementKind: "import",4783 moduleName: NATIVE_MODULE_NAME,4784 imports: [identifierIso.wrap("clock")],4785 },4786 {4787 statementKind: "return",4788 returnedValue: {4789 expressionKind: "binOp",4790 binOp: "equals",4791 leftOperand: {4792 expressionKind: "variableRef",4793 variableName: identifierIso.wrap("clock"),4794 },4795 rightOperand: {4796 expressionKind: "numberLit",4797 value: 1,4798 },4799 },4800 },4801 ];4802 // Act4803 const evalResult = evaluateTestProgram(wrapBlock(ast));4804 // Assert4805 if (!isLeft(evalResult)) {4806 throw new Error("Evaluation succeeded, should have failed");4807 }4808 if (evalResult.left.runtimeErrorKind !== "typeMismatch") {4809 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of TypeMismatch error`);4810 }4811 expect(evalResult.left.expectedTypes).toContain("boolean");4812 expect(evalResult.left.expectedTypes).toContain("number");4813 expect(evalResult.left.actualType).toBe("nativeFunc");4814 });4815 it("Recognizes a TypeMismatch error for { import clock from Native; return 1 == clock; } (native function on RHS of equals expression)", () => {4816 // Arrange4817 const ast: Block = [4818 {4819 statementKind: "import",4820 moduleName: NATIVE_MODULE_NAME,4821 imports: [identifierIso.wrap("clock")],4822 },4823 {4824 statementKind: "return",4825 returnedValue: {4826 expressionKind: "binOp",4827 binOp: "equals",4828 leftOperand: {4829 expressionKind: "numberLit",4830 value: 1,4831 },4832 rightOperand: {4833 expressionKind: "variableRef",4834 variableName: identifierIso.wrap("clock"),4835 },4836 },4837 },4838 ];4839 // Act4840 const evalResult = evaluateTestProgram(wrapBlock(ast));4841 // Assert4842 if (!isLeft(evalResult)) {4843 throw new Error("Evaluation succeeded, should have failed");4844 }4845 if (evalResult.left.runtimeErrorKind !== "typeMismatch") {4846 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of TypeMismatch error`);4847 }4848 expect(evalResult.left.expectedTypes).toContain("boolean");4849 expect(evalResult.left.expectedTypes).toContain("number");4850 expect(evalResult.left.actualType).toBe("nativeFunc");4851 });4852 it("Recognizes a TypeMismatch error for { import clock from Native; return clock /= 1; } (native function on LHS of not-equal expression)", () => {4853 // Arrange4854 const ast: Block = [4855 {4856 statementKind: "import",4857 moduleName: NATIVE_MODULE_NAME,4858 imports: [identifierIso.wrap("clock")],4859 },4860 {4861 statementKind: "return",4862 returnedValue: {4863 expressionKind: "binOp",4864 binOp: "notEqual",4865 leftOperand: {4866 expressionKind: "variableRef",4867 variableName: identifierIso.wrap("clock"),4868 },4869 rightOperand: {4870 expressionKind: "numberLit",4871 value: 1,4872 },4873 },4874 },4875 ];4876 // Act4877 const evalResult = evaluateTestProgram(wrapBlock(ast));4878 // Assert4879 if (!isLeft(evalResult)) {4880 throw new Error("Evaluation succeeded, should have failed");4881 }4882 if (evalResult.left.runtimeErrorKind !== "typeMismatch") {4883 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of TypeMismatch error`);4884 }4885 expect(evalResult.left.expectedTypes).toContain("boolean");4886 expect(evalResult.left.expectedTypes).toContain("number");4887 expect(evalResult.left.actualType).toBe("nativeFunc");4888 });4889 it("Recognizes a TypeMismatch error for { import clock from Native; return 1 /= clock; } (native function on RHS of not-equal expression)", () => {4890 // Arrange4891 const ast: Block = [4892 {4893 statementKind: "import",4894 moduleName: NATIVE_MODULE_NAME,4895 imports: [identifierIso.wrap("clock")],4896 },4897 {4898 statementKind: "return",4899 returnedValue: {4900 expressionKind: "binOp",4901 binOp: "notEqual",4902 leftOperand: {4903 expressionKind: "numberLit",4904 value: 1,4905 },4906 rightOperand: {4907 expressionKind: "variableRef",4908 variableName: identifierIso.wrap("clock"),4909 },4910 },4911 },4912 ];4913 // Act4914 const evalResult = evaluateTestProgram(wrapBlock(ast));4915 // Assert4916 if (!isLeft(evalResult)) {4917 throw new Error("Evaluation succeeded, should have failed");4918 }4919 if (evalResult.left.runtimeErrorKind !== "typeMismatch") {4920 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of TypeMismatch error`);4921 }4922 expect(evalResult.left.expectedTypes).toContain("boolean");4923 expect(evalResult.left.expectedTypes).toContain("number");4924 expect(evalResult.left.actualType).toBe("nativeFunc");4925 });4926 });4927 describe("ArityMismatch errors", () => {4928 it("Recognizes an arity mismatch (too few arguments) for { function f(x) { return x; } return f(); }", () => {4929 // Arrange4930 const ast: Block = [4931 {4932 statementKind: "funcDecl",4933 functionName: identifierIso.wrap("f"),4934 argNames: [identifierIso.wrap("x")],4935 body: [4936 {4937 statementKind: "return",4938 returnedValue: {4939 expressionKind: "variableRef",4940 variableName: identifierIso.wrap("x"),4941 },4942 },4943 ],4944 },4945 {4946 statementKind: "return",4947 returnedValue: {4948 expressionKind: "funcCall",4949 args: [],4950 callee: {4951 expressionKind: "variableRef",4952 variableName: identifierIso.wrap("f"),4953 },4954 },4955 },4956 ];4957 // Act4958 const evalResult = evaluateTestProgram(wrapBlock(ast));4959 // Assert4960 if (!isLeft(evalResult)) {4961 throw new Error("Evaluation succeeded, should have failed");4962 }4963 if (evalResult.left.runtimeErrorKind !== "arityMismatch") {4964 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of ArityMismatch error`);4965 }4966 expect(evalResult.left.expectedNumArgs).toBe(1);4967 expect(evalResult.left.actualNumArgs).toBe(0);4968 });4969 it("Recognizes an arity mismatch (too many arguments) for { function f() { return 1; } return f(2); }", () => {4970 // Arrange4971 const ast: Block = [4972 {4973 statementKind: "funcDecl",4974 functionName: identifierIso.wrap("f"),4975 argNames: [],4976 body: [4977 {4978 statementKind: "return",4979 returnedValue: {4980 expressionKind: "numberLit",4981 value: 1,4982 },4983 },4984 ],4985 },4986 {4987 statementKind: "return",4988 returnedValue: {4989 expressionKind: "funcCall",4990 args: [4991 {4992 expressionKind: "numberLit",4993 value: 2,4994 },4995 ],4996 callee: {4997 expressionKind: "variableRef",4998 variableName: identifierIso.wrap("f"),4999 },5000 },5001 },5002 ];5003 // Act5004 const evalResult = evaluateTestProgram(wrapBlock(ast));5005 // Assert5006 if (!isLeft(evalResult)) {5007 throw new Error("Evaluation succeeded, should have failed");5008 }5009 if (evalResult.left.runtimeErrorKind !== "arityMismatch") {5010 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of ArityMismatch error`);5011 }5012 expect(evalResult.left.expectedNumArgs).toBe(0);5013 expect(evalResult.left.actualNumArgs).toBe(1);5014 });5015 it("Recognizes an arity mismatch (too many arguments to native function) for { import clock from Native; return clock(1); }", () => {5016 // Arrange5017 const ast: Block = [5018 {5019 statementKind: "import",5020 moduleName: NATIVE_MODULE_NAME,5021 imports: [identifierIso.wrap("clock")],5022 },5023 {5024 statementKind: "return",5025 returnedValue: {5026 expressionKind: "funcCall",5027 args: [5028 {5029 expressionKind: "numberLit",5030 value: 1,5031 },5032 ],5033 callee: {5034 expressionKind: "variableRef",5035 variableName: identifierIso.wrap("clock"),5036 },5037 },5038 },5039 ];5040 // Act5041 const evalResult = evaluateTestProgram(wrapBlock(ast));5042 // Assert5043 if (!isLeft(evalResult)) {5044 throw new Error("Evaluation succeeded, should have failed");5045 }5046 if (evalResult.left.runtimeErrorKind !== "arityMismatch") {5047 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of ArityMismatch error`);5048 }5049 expect(evalResult.left.expectedNumArgs).toBe(0);5050 expect(evalResult.left.actualNumArgs).toBe(1);5051 });5052 });5053 describe("UnassignedVariable errors", () => {5054 it("Recognizes an UnassignedVariable error for { let x; return x; } (variable used before assigning it a value)", () => {5055 // Arrange5056 const ast: Block = [5057 {5058 statementKind: "varDecl",5059 variableName: identifierIso.wrap("x"),5060 },5061 {5062 statementKind: "return",5063 returnedValue: {5064 expressionKind: "variableRef",5065 variableName: identifierIso.wrap("x"),5066 },5067 },5068 ];5069 // Act5070 const evalResult = evaluateTestProgram(wrapBlock(ast));5071 // Assert5072 if (!isLeft(evalResult)) {5073 throw new Error("Evaluation succeeded, should have failed");5074 }5075 if (evalResult.left.runtimeErrorKind !== "unassignedVariable") {5076 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of UnassignedVariable error`);5077 }5078 expect(evalResult.left.unassignedIdentifier).toBe("x");5079 });5080 it("Recognizes an UnassignedVariable error for module Source { let x; } export x; module Main { import x from Source; } (exported variable not assigned a value)", () => {5081 // Arrange5082 const sourceModule: Module = {5083 name: identifierIso.wrap("Source"),5084 body: [5085 {5086 statementKind: "varDecl",5087 variableName: identifierIso.wrap("x"),5088 },5089 ],5090 exports: [identifierIso.wrap("x")],5091 };5092 const mainModule: Module = {5093 name: testModuleName,5094 body: [5095 {5096 statementKind: "import",5097 imports: [identifierIso.wrap("x")],5098 moduleName: identifierIso.wrap("Source"),5099 },5100 ],5101 exports: [],5102 };5103 // Act5104 const evalResult = evaluateTestProgram([sourceModule, mainModule]);5105 // Assert5106 if (!isLeft(evalResult)) {5107 throw new Error("Evaluation succeeded, should have failed");5108 }5109 if (evalResult.left.runtimeErrorKind !== "unassignedVariable") {5110 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of UnassignedVariable error`);5111 }5112 expect(evalResult.left.unassignedIdentifier).toBe("x");5113 });5114 });5115 describe("NotObject errors", () => {5116 it("Recognizes a NotObject error for { return 1.field; } (attempting to call getter on non-object)", () => {5117 // Arrange5118 const ast: Block = [5119 {5120 statementKind: "return",5121 returnedValue: {5122 expressionKind: "get",5123 object: {5124 expressionKind: "numberLit",5125 value: 1,5126 },5127 field: identifierIso.wrap("field"),5128 },5129 },5130 ];5131 // Act5132 const evalResult = evaluateTestProgram(wrapBlock(ast));5133 // Assert5134 if (!isLeft(evalResult)) {5135 throw new Error("Evaluation succeeded, should have failed");5136 }5137 if (evalResult.left.runtimeErrorKind !== "notObject") {5138 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of NotObject error`);5139 }5140 expect(evalResult.left.nonObjectType).toBe("number");5141 });5142 it("Recognizes a NotObject error for { 1.field = 2; } (attempting to call setter on non-object)", () => {5143 // Arrange5144 const ast: Block = [5145 {5146 statementKind: "set",5147 object: {5148 expressionKind: "numberLit",5149 value: 1,5150 },5151 field: identifierIso.wrap("field"),5152 value: {5153 expressionKind: "numberLit",5154 value: 2,5155 },5156 },5157 ];5158 // Act5159 const evalResult = evaluateTestProgram(wrapBlock(ast));5160 // Assert5161 if (!isLeft(evalResult)) {5162 throw new Error("Evaluation succeeded, should have failed");5163 }5164 if (evalResult.left.runtimeErrorKind !== "notObject") {5165 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of NotObject error`);5166 }5167 expect(evalResult.left.nonObjectType).toBe("number");5168 });5169 });5170 describe("NoSuchModule errors", () => {5171 it("Recognizes a NoSuchModule error for { import someNum from Nonexistent; } (importing from nonexistent module)", () => {5172 // Arrange5173 const ast: Block = [5174 {5175 statementKind: "import",5176 imports: [identifierIso.wrap("someNum")],5177 moduleName: identifierIso.wrap("Nonexistent"),5178 },5179 ];5180 // Act5181 const evalResult = evaluateTestProgram(wrapBlock(ast));5182 // Assert5183 if (!isLeft(evalResult)) {5184 throw new Error("Evaluation succeeded, should have failed");5185 }5186 if (evalResult.left.runtimeErrorKind !== "noSuchModule") {5187 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of NoSuchModule error`);5188 }5189 expect(evalResult.left.moduleName).toBe("Nonexistent");5190 });5191 });5192 describe("NoSuchExport errors", () => {5193 it("Recognizes a NoSuchExport error for { import notAFunc from Native; } (importing nonexistent native function from Native)", () => {5194 // Arrange5195 const ast: Block = [5196 {5197 statementKind: "import",5198 imports: [identifierIso.wrap("notAFunc")],5199 moduleName: NATIVE_MODULE_NAME,5200 },5201 ];5202 // Act5203 const evalResult = evaluateTestProgram(wrapBlock(ast));5204 // Assert5205 if (!isLeft(evalResult)) {5206 throw new Error("Evaluation succeeded, should have failed");5207 }5208 if (evalResult.left.runtimeErrorKind !== "noSuchExport") {5209 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of NoSuchExport error`);5210 }5211 expect(evalResult.left.exportName).toBe("notAFunc");5212 });5213 it("Recognizes a NoSuchExport error for module Source {} module Main { import notAFunc from Source; } (importing nonexistent function from existing module)", () => {5214 // Arrange5215 const sourceModule: Module = {5216 name: identifierIso.wrap("Source"),5217 body: [],5218 exports: [],5219 };5220 const mainModule: Module = {5221 name: testModuleName,5222 body: [5223 {5224 statementKind: "import",5225 imports: [identifierIso.wrap("notAFunc")],5226 moduleName: identifierIso.wrap("Source"),5227 },5228 ],5229 exports: [],5230 };5231 // Act5232 const evalResult = evaluateTestProgram([sourceModule, mainModule]);5233 // Assert5234 if (!isLeft(evalResult)) {5235 throw new Error("Evaluation succeeded, should have failed");5236 }5237 if (evalResult.left.runtimeErrorKind !== "noSuchExport") {5238 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of NoSuchExport error`);5239 }5240 expect(evalResult.left.exportName).toBe("notAFunc");5241 });5242 });5243 describe("Other modularization errors", () => {5244 it("Returns a NotInScope error for module Source { let someVar = nonexistent; } export someVar; module Main { import someVar from Source; } (error evaluating imported module)", () => {5245 // Arrange5246 const sourceModule: Module = {5247 name: identifierIso.wrap("Source"),5248 body: [5249 {5250 statementKind: "varDecl",5251 variableName: identifierIso.wrap("someVar"),5252 },5253 {5254 statementKind: "assignment",5255 variableName: identifierIso.wrap("someVar"),5256 variableValue: {5257 expressionKind: "variableRef",5258 variableName: identifierIso.wrap("nonexistent"),5259 },5260 },5261 ],5262 exports: [identifierIso.wrap("someVar")],5263 };5264 const mainModule: Module = {5265 name: testModuleName,5266 body: [5267 {5268 statementKind: "import",5269 imports: [identifierIso.wrap("someVar")],5270 moduleName: identifierIso.wrap("Source"),5271 },5272 ],5273 exports: [],5274 };5275 // Act5276 const evalResult = evaluateTestProgram([sourceModule, mainModule]);5277 // Assert5278 if (!isLeft(evalResult)) {5279 throw new Error("Evaluation succeeded, should have failed");5280 }5281 if (evalResult.left.runtimeErrorKind !== "notInScope") {5282 throw new Error(`Detected ${evalResult.left.runtimeErrorKind} error instead of NotInScope error`);5283 }5284 expect(evalResult.left.outOfScopeIdentifier).toBe("nonexistent");5285 });5286 it("Returns a NoMain error for module Source {} (no Main module)", () => {5287 // Arrange5288 const sourceModule: Module = {5289 name: identifierIso.wrap("Source"),5290 body: [],5291 exports: [],5292 };5293 // Act5294 const evalResult = evaluateTestProgram([sourceModule]);5295 // Assert5296 if (!isLeft(evalResult)) {5297 throw new Error("Evaluation succeeded, should have failed");5298 }5299 expect(evalResult.left.runtimeErrorKind).toBe("noMain");5300 });5301 it("Returns a MultipleMains error for module Main {} module Main {}", () => {5302 // Arrange5303 const mainModule1: Module = {5304 name: testModuleName,5305 body: [],5306 exports: [],5307 };5308 const mainModule2: Module = {5309 name: testModuleName,5310 body: [],5311 exports: [],5312 };5313 // Act5314 const evalResult = evaluateTestProgram([mainModule1, mainModule2]);5315 // Assert5316 if (!isLeft(evalResult)) {5317 throw new Error("Evaluation succeeded, should have failed");5318 }5319 expect(evalResult.left.runtimeErrorKind).toBe("multipleMains");5320 });5321 });5322 });...

Full Screen

Full Screen

10.6.1-01.js

Source:10.6.1-01.js Github

copy

Full Screen

1/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */2/* ***** BEGIN LICENSE BLOCK *****3 * Version: MPL 1.1/GPL 2.0/LGPL 2.14 *5 * The contents of this file are subject to the Mozilla Public License Version6 * 1.1 (the "License"); you may not use this file except in compliance with7 * the License. You may obtain a copy of the License at8 * http://www.mozilla.org/MPL/9 *10 * Software distributed under the License is distributed on an "AS IS" basis,11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License12 * for the specific language governing rights and limitations under the13 * License.14 *15 * The Original Code is JavaScript Engine testing utilities.16 *17 * The Initial Developer of the Original Code is18 * Mozilla Foundation.19 * Portions created by the Initial Developer are Copyright (C) 200520 * the Initial Developer. All Rights Reserved.21 *22 * Contributor(s): Bryant Chen23 *24 * Alternatively, the contents of this file may be used under the terms of25 * either the GNU General Public License Version 2 or later (the "GPL"), or26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),27 * in which case the provisions of the GPL or the LGPL are applicable instead28 * of those above. If you wish to allow use of your version of this file only29 * under the terms of either the GPL or the LGPL, and not to allow others to30 * use your version of this file under the terms of the MPL, indicate your31 * decision by deleting the provisions above and replace them with the notice32 * and other provisions required by the GPL or the LGPL. If you do not delete33 * the provisions above, a recipient may use your version of this file under34 * the terms of any one of the MPL, the GPL or the LGPL.35 *36 * ***** END LICENSE BLOCK ***** */37var gTestfile = '10.6.1-01.js';38//-----------------------------------------------------------------------------39var BUGNUMBER = 290774;40var summary = 'activation object never delegates to Object.prototype';41var actual = '';42var expect = '';43printBugNumber(BUGNUMBER);44printStatus (summary);45var toStringResult;46var evalResult;47var watchResult;48var parseIntResult;49var eval = 'fooEval';50var watch = undefined;51var parseInt = 'fooParseInt';52function toString()53{54 return 'fooString';55}56function normal()57{58 toStringResult = toString;59 evalResult = eval;60 watchResult = watch;61 parseIntResult = parseInt;62}63function outerinnervar()64{65 toStringResult = toString;66 evalResult = eval;67 watchResult = watch;68 parseIntResult = parseInt;69 function inner()70 {71 // addition of any statement72 // which accesses a variable73 // from the outer scope causes the bug74 printStatus(toString);75 }76}77expect = true;78printStatus('normal');79printStatus('======');80normal();81printStatus('toStringResult ' + toStringResult);82printStatus('toString ' + toString);83actual = ((toStringResult + '') == (toString + ''));84reportCompare(expect, actual, inSection(1));85printStatus('evalResult ' + evalResult);86printStatus('eval ' + eval);87actual = ((evalResult + '') == (eval + ''));88reportCompare(expect, actual, inSection(2));89printStatus('watchResult ' + watchResult);90printStatus('watch ' + watch);91actual = ((watchResult + '') == (watch + ''));92reportCompare(expect, actual, inSection(3));93printStatus('parseIntResult ' + parseIntResult);94printStatus('parseInt ' + parseInt);95actual = ((parseIntResult + '') == (parseInt + ''));96reportCompare(expect, actual, inSection(4));97printStatus('outerinner');98printStatus('==========');99outerinnervar();100printStatus('toStringResult ' + toStringResult);101printStatus('toString ' + toString);102actual = ((toStringResult + '') == (toString + ''));103reportCompare(expect, actual, inSection(5));104printStatus('evalResult ' + evalResult);105printStatus('eval ' + eval);106actual = ((evalResult + '') == (eval + ''));107reportCompare(expect, actual, inSection(6));108printStatus('watchResult ' + watchResult);109printStatus('watch ' + watch);110actual = ((watchResult + '') == (watch + ''));111reportCompare(expect, actual, inSection(7));112printStatus('parseIntResult ' + parseIntResult);113printStatus('parseInt ' + parseInt);114actual = ((parseIntResult + '') == (parseInt + ''));...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1var apickli = require('apickli');2var {defineSupportCode} = require('cucumber');3defineSupportCode(function({Given, When, Then}) {4 var apickli = new apickli.Apickli('http', 'httpbin.org');5 this.Given(/^I set header (.*) to (.*)$/, function(header, value, callback) {6 apickli.addRequestHeader(header, value);7 callback();8 });9 this.When(/^I GET (.*)$/, function(path, callback) {10 apickli.get(path, callback);11 });12 this.Then(/^I should get (.*)$/, function(statusCode, callback) {13 apickli.assertResponseCode(statusCode);14 callback();15 });16 this.Then(/^I should get valid JSON$/, function(callback) {17 apickli.assertResponseBodyContainsJson();18 callback();19 });20 this.Then(/^I should get (.*) in JSON response$/, function(expectedValue, callback) {21 apickli.assertResponseBodyContains(expectedValue);22 callback();23 });24 this.Then(/^I should see the following headers$/, function(table, callback) {25 var headers = table.hashes();26 headers.forEach(function(header) {27 apickli.assertResponseContainsHeader(header.name, header.value);28 });29 callback();30 });31 this.Then(/^I should see the following values in JSON response$/, function(table, callback) {32 var values = table.hashes();33 values.forEach(function(value) {34 apickli.assertResponseBodyContainsJsonPath(value.name, value.value);35 });36 callback();37 });38 this.Then(/^I should see the following values in JSON response using evalResult$/, function(table, callback) {39 var values = table.hashes();40 values.forEach(function(value) {41 apickli.evalResult(value.name, value.value);42 });43 callback();44 });45});46var apickli = require('apickli');47var {defineSupportCode} = require('cucumber');48defineSupportCode(function({Given, When, Then}) {49 var apickli = new apickli.Apickli('http', 'httpbin.org');50 this.Given(/^I set header (.*) to (.*)$/, function(header, value, callback) {51 apickli.addRequestHeader(header, value);52 callback();53 });

Full Screen

Using AI Code Generation

copy

Full Screen

1var apickli = require('apickli');2var apickliObject = new apickli.Apickli('http', 'httpbin.org');3apickliObject.addRequestHeader('Content-Type', 'application/json');4apickliObject.addRequestHeader('Accept', 'application/json');5apickliObject.setRequestBody('{ "name": "John Doe" }');6apickliObject.post('/post', function(error, response) {7 var result = apickliObject.evalResult('{ "name": "John Doe" }');8 console.log(result);9});

Full Screen

Using AI Code Generation

copy

Full Screen

1var apickli = require('apickli');2var { defineSupportCode } = require('cucumber');3defineSupportCode(function({ Given, When, Then }) {4 this.apickli = new apickli.Apickli('https', 'httpbin.org');5 this.apickli.addRequestHeader('Content-Type', 'application/json');6 Given('I set request header Content-Type to application/json', function(callback) {7 this.apickli.addRequestHeader('Content-Type', 'application/json');8 callback();9 });10 When('I GET /get', function(callback) {11 this.apickli.get('/get', callback);12 });13 Then('I should get the response', function(callback) {14 this.apickli.assertResponseCode(200);15 callback();16 });17});18{19 "scripts": {20 },21 "dependencies": {22 }23}24var apickli = require('apickli');25var { defineSupportCode } = require('cucumber');26defineSupportCode(function({ Given, When, Then }) {27 this.apickli = new apickli.Apickli('https', 'httpbin.org');28 this.apickli.addRequestHeader('Content-Type', 'application/json');29 Given('I set request header Content-Type to application/json', function(callback) {30 this.apickli.addRequestHeader('Content-Type', 'application/json');31 callback();32 });33 When('I GET /get', function(callback) {34 this.apickli.get('/get', callback);35 });36 Then('I should get the response', function(callback) {37 this.apickli.assertResponseCode(200);38 callback();39 });40});

Full Screen

Using AI Code Generation

copy

Full Screen

1'use strict';2var apickli = require('apickli');3var {defineSupportCode} = require('cucumber');4defineSupportCode(function({Given, When, Then}) {5 var apickli = new apickli.Apickli('https', 'httpbin.org');6 apickli.addRequestHeader('Content-Type', 'application/json');7 Given('a request to httpbin', function(callback) {8 callback(null, 'pending');9 });10 When('the request is sent', function(callback) {11 callback(null, 'pending');12 });13 Then('the response is valid', function(callback) {14 callback(null, 'pending');15 });16});17'use strict';18var apickli = require('apickli');19var {defineSupportCode} = require('cucumber');20defineSupportCode(function({Given, When, Then}) {21 Given('a request to httpbin', function(callback) {22 callback(null, 'pending');23 });24 When('the request is sent', function(callback) {25 callback(null, 'pending');26 });27 Then('the response is valid', function(callback) {28 callback(null, 'pending');29 });30});31'use strict';32var apickli = require('apickli');33var {defineSupportCode} = require('cucumber');34defineSupportCode(function({Given, When, Then}) {35 Given('a request to httpbin', function(callback) {36 callback(null, 'pending');37 });38 When('the request is sent', function(callback) {39 callback(null, 'pending');40 });41 Then('the response is valid', function(callback) {42 callback(null, 'pending');43 });44});

Full Screen

Using AI Code Generation

copy

Full Screen

1var apickli = require('apickli');2var {defineSupportCode} = require('cucumber');3var myApickli = new apickli.Apickli('https', 'httpbin.org');4defineSupportCode(function({Given, When, Then}) {5 Given('I set header {stringInDoubleQuotes} to {stringInDoubleQuotes}', function (header, value, callback) {6 this.apickli.addRequestHeader(header, value);7 callback();8 });9 When('I GET {stringInDoubleQuotes}', function (resource, callback) {10 this.apickli.get(resource, callback);11 });12 Then('I expect the response code to be {int}', function (httpStatusCode, callback) {13 this.apickli.assertResponseCode(httpStatusCode);14 callback();15 });16});17var apickli = require('apickli');18var {defineSupportCode} = require('cucumber');19var myApickli = new apickli.Apickli('https', 'httpbin.org');20module.exports = myApickli;21defineSupportCode(function({Given, When, Then}) {22 Given('I set header {stringInDoubleQuotes} to {stringInDoubleQuotes}', function (header, value, callback) {23 this.apickli.addRequestHeader(header, value);24 callback();25 });26 When('I GET {stringInDoubleQuotes}', function (resource, callback) {27 this.apickli.get(resource, callback);28 });29 Then('I expect the response code to be {int}', function (httpStatusCode, callback) {30 this.apickli.assertResponseCode(httpStatusCode);31 callback();32 });33});

Full Screen

Using AI Code Generation

copy

Full Screen

1var apickli = require('apickli');2var {defineSupportCode} = require('cucumber');3var myApickli = new apickli.Apickli('http', 'localhost:8000');4defineSupportCode(function({Given, When, Then}) {5Given('I set the header {string} to {string}', function (header, value, callback) {6 this.apickli.addRequestHeader(header, value);7 callback();8});9When('I GET {string}', function (path, callback) {10 this.apickli.get(path, callback);11});12Then('I should get a response with status code {int}', function (statusCode, callback) {13 this.apickli.assertResponseCode(statusCode);14 callback();15});16Then('I should get a response with body', function (body, callback) {17 this.apickli.assertResponseBodyContains(body);18 callback();19});20Then('I should get a response with body {string}', function (body, callback) {21 this.apickli.assertResponseBodyContains(body);22 callback();23});24Then('I should get a response with body containing {string}', function (body, callback) {25 this.apickli.assertResponseBodyContains(body);26 callback();27});28Then('I should get a response with body not containing {string}', function (body, callback) {29 this.apickli.assertResponseBodyNotContains(body);30 callback();31});32Then('I should get a response with header {string} containing {string}', function (header, value, callback) {33 this.apickli.assertResponseHeaderContains(header, value);34 callback();35});36Then('I should get a response with header {string} not containing {string}', function (header, value, callback) {37 this.apickli.assertResponseHeaderNotContains(header, value);38 callback();39});40Then('I should get a response with header {string} equal to {string}', function (header, value, callback) {41 this.apickli.assertResponseHeader(header, value);42 callback();43});44Then('I should get a response with header {string} not equal to {string}', function (header, value, callback) {45 this.apickli.assertResponseHeaderNot(header, value);46 callback();47});48Then('I should get a response with header {string} containing {string} in {int} ms', function (header, value, timeout, callback) {

Full Screen

Using AI Code Generation

copy

Full Screen

1var Apickli = require('apickli');2var apickli = new Apickli.Apickli('http', 'httpbin.org');3apickli.scenarioVariables['foo'] = 'bar';4apickli.evalResult('foo', 'bar');5apickli.scenarioVariables['foo'] = 'bar';6apickli.evalResult('foo', 'bar', 'Should be bar');7apickli.scenarioVariables['foo'] = 'bar';8apickli.evalResult('foo', 'bar', 'Should be bar', true);9apickli.scenarioVariables['foo'] = 'bar';10apickli.evalResult('foo', 'bar', 'Should be bar', false);11apickli.scenarioVariables['foo'] = 'bar';12apickli.evalResult('foo', 'bar', 'Should be bar', false, true);13apickli.scenarioVariables['foo'] = 'bar';14apickli.evalResult('foo', 'bar', 'Should be bar', false, false);15apickli.scenarioVariables['foo'] = 'bar';16apickli.evalResult('foo', 'bar', 'Should be bar', false, false, true);17apickli.scenarioVariables['foo'] = 'bar';18apickli.evalResult('foo', 'bar', 'Should be bar', false, false, false);19apickli.scenarioVariables['foo'] = 'bar';20apickli.evalResult('foo', 'bar', 'Should be bar', false, false, false, true);21apickli.scenarioVariables['foo'] = 'bar';

Full Screen

Using AI Code Generation

copy

Full Screen

1var assert = require('assert');2var Apickli = require('apickli');3var apickli = new Apickli('http', 'jsonplaceholder.typicode.com');4module.exports = function() {5 this.Given(/^I have a request$/, function (callback) {6 callback(null, 'pending');7 });8 this.When(/^I send a GET request to "([^"]*)"$/, function (path, callback) {9 apickli.get(path, function(err, response) {10 if (err) {11 callback(err);12 } else {13 callback();14 }15 });16 });17 this.Then(/^I should see the response code as "([^"]*)"$/, function (code, callback) {18 assert.equal(apickli.getResponseObject().statusCode, code);19 callback();20 });21};221 scenario (1 passed)233 steps (3 passed)

Full Screen

Using AI Code Generation

copy

Full Screen

1var apickli = require('apickli');2var assert = require('assert');3var myStepDefinitionsWrapper = function () {4 this.Given('I set the header $headerName to $headerValue', function (headerName, headerValue, callback) {5 this.apickli.addRequestHeader(headerName, headerValue);6 callback();7 });8 this.When('I GET $resource', function (resource, callback) {9 this.apickli.get(resource, callback);10 });11 this.Then('the response code should be $statusCode', function (statusCode, callback) {12 this.apickli.assertResponseCode(statusCode);13 callback();14 });15 this.Then('the response should contain $expectedText', function (expectedText, callback) {16 this.apickli.assertContains(expectedText);17 callback();18 });19};20module.exports = myStepDefinitionsWrapper;

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run apickli automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful