diff --git a/config.json b/config.json index f454fbd..c3f8e73 100644 --- a/config.json +++ b/config.json @@ -210,6 +210,14 @@ "prerequisites": [], "difficulty": 1 }, + { + "slug": "forth", + "name": "Forth", + "uuid": "48dd93c3-6fa4-4e9c-a973-6fdbd3a640a5", + "practices": [], + "prerequisites": [], + "difficulty": 10 + }, { "slug": "pangram", "name": "Pangram", diff --git a/exercises/practice/forth/.docs/instructions.md b/exercises/practice/forth/.docs/instructions.md new file mode 100644 index 0000000..91ad26e --- /dev/null +++ b/exercises/practice/forth/.docs/instructions.md @@ -0,0 +1,23 @@ +# Instructions + +Implement an evaluator for a very simple subset of Forth. + +[Forth][forth] +is a stack-based programming language. +Implement a very basic evaluator for a small subset of Forth. + +Your evaluator has to support the following words: + +- `+`, `-`, `*`, `/` (integer arithmetic) +- `DUP`, `DROP`, `SWAP`, `OVER` (stack manipulation) + +Your evaluator also has to support defining new words using the customary syntax: `: word-name definition ;`. + +To keep things simple the only data type you need to support is signed integers of at least 16 bits size. + +You should use the following rules for the syntax: a number is a sequence of one or more (ASCII) digits, a word is a sequence of one or more letters, digits, symbols or punctuation that is not a number. +(Forth probably uses slightly different rules, but this is close enough.) + +Words are case-insensitive. + +[forth]: https://en.wikipedia.org/wiki/Forth_%28programming_language%29 diff --git a/exercises/practice/forth/.meta/config.json b/exercises/practice/forth/.meta/config.json new file mode 100644 index 0000000..b498ff8 --- /dev/null +++ b/exercises/practice/forth/.meta/config.json @@ -0,0 +1,16 @@ +{ + "authors": ["lucaferranti"], + "contributors": ["kytrinyx"], + "files": { + "solution": [ + "src/forth.chpl" + ], + "test": [ + "test/tests.chpl" + ], + "example": [ + ".meta/reference.chpl" + ] + }, + "blurb": "Implement an evaluator for a very simple subset of Forth." +} diff --git a/exercises/practice/forth/.meta/reference.chpl b/exercises/practice/forth/.meta/reference.chpl new file mode 100644 index 0000000..5178640 --- /dev/null +++ b/exercises/practice/forth/.meta/reference.chpl @@ -0,0 +1,85 @@ +module Forth { + + use List; + use Map; + + const BINARY_OPS = ["+", "-", "*", "/", "swap", "over"]; + const UNARY_OPS = ["drop", "dup"]; + + proc evaluate(statements: [] string) throws { + var stack: list(int); + var env: map(string, string); + for statement in statements { + var instructions = statement.toLower().split(); + if instructions[0] == ":" && instructions[instructions.size-1] == ";" { + parse_variable_assignment(env, instructions); + } else { + for instruction in instructions { + evaluate_instruction(stack, env, instruction); + } + } + } + return stack; + } + + proc parse_variable_assignment(ref env: map(string, string), instructions: [] string) throws { + const varname = instructions[1], + varvalues = instructions[2..instructions.size-2]; + if isNumber(varname) then throw new IllegalArgumentError("illegal operation"); + var substitued_instr = " ".join([varvalue in varvalues] env.get(varvalue, varvalue)); + if env.contains(varname) then env.replace(varname, substitued_instr); + else env.add(varname, substitued_instr); + } + + proc evaluate_instruction(ref stack: list(int), env: map(string, string), instr: string): void throws { + if env.contains(instr) { + var new_instrs = env.get(instr, "").split(); + for new_instr in new_instrs { + evaluate_instruction(stack, env, new_instr); + } + } else execute_instruction(stack, instr); + } + + proc execute_instruction(ref stack: list(int), instr: string) throws { + if isNumber(instr) then stack.pushBack(instr: int); + else if BINARY_OPS.find(instr) > -1 then execute_binary_operation(stack, instr); + else if UNARY_OPS.find(instr) > -1 then execute_unary_operation(stack, instr); + else throw new IllegalArgumentError("undefined operation"); + } + + proc execute_binary_operation(ref stack: list(int), instr: string) throws { + if stack.isEmpty() then throw new IllegalArgumentError("empty stack"); + if stack.size == 1 then throw new IllegalArgumentError("only one value on the stack"); + + const op1 = stack.popBack(), + op2 = stack.popBack(); + + select instr { + when "+" do stack.pushBack(op1 + op2); + when "-" do stack.pushBack(op2 - op1); + when "*" do stack.pushBack(op1 * op2); + when "/" { + if op1 == 0 then throw new IllegalArgumentError("divide by zero"); + else stack.pushBack(op2 / op1); + } + when "swap" do stack.pushBack([op1, op2]); + when "over" do stack.pushBack([op2, op1, op2]); + otherwise throw new IllegalArgumentError("How did you even get here?"); + } + } + + proc execute_unary_operation(ref stack: list(int), instr: string) throws { + if stack.isEmpty() then throw new IllegalArgumentError("empty stack"); + + select instr { + when "drop" do stack.popBack(); + otherwise do stack.pushBack(stack[stack.size-1]); // dup + } + } + + proc isNumber(s: string) { + if s.startsWith("+") || s.startsWith("-") then return s[1..].isDigit(); + else return s.isDigit(); + } + +} diff --git a/exercises/practice/forth/.meta/tests.toml b/exercises/practice/forth/.meta/tests.toml new file mode 100644 index 0000000..c9c1d63 --- /dev/null +++ b/exercises/practice/forth/.meta/tests.toml @@ -0,0 +1,157 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[9962203f-f00a-4a85-b404-8a8ecbcec09d] +description = "parsing and numbers -> numbers just get pushed onto the stack" + +[fd7a8da2-6818-4203-a866-fed0714e7aa0] +description = "parsing and numbers -> pushes negative numbers onto the stack" + +[9e69588e-a3d8-41a3-a371-ea02206c1e6e] +description = "addition -> can add two numbers" + +[52336dd3-30da-4e5c-8523-bdf9a3427657] +description = "addition -> errors if there is nothing on the stack" + +[06efb9a4-817a-435e-b509-06166993c1b8] +description = "addition -> errors if there is only one value on the stack" + +[09687c99-7bbc-44af-8526-e402f997ccbf] +description = "subtraction -> can subtract two numbers" + +[5d63eee2-1f7d-4538-b475-e27682ab8032] +description = "subtraction -> errors if there is nothing on the stack" + +[b3cee1b2-9159-418a-b00d-a1bb3765c23b] +description = "subtraction -> errors if there is only one value on the stack" + +[5df0ceb5-922e-401f-974d-8287427dbf21] +description = "multiplication -> can multiply two numbers" + +[9e004339-15ac-4063-8ec1-5720f4e75046] +description = "multiplication -> errors if there is nothing on the stack" + +[8ba4b432-9f94-41e0-8fae-3b3712bd51b3] +description = "multiplication -> errors if there is only one value on the stack" + +[e74c2204-b057-4cff-9aa9-31c7c97a93f5] +description = "division -> can divide two numbers" + +[54f6711c-4b14-4bb0-98ad-d974a22c4620] +description = "division -> performs integer division" + +[a5df3219-29b4-4d2f-b427-81f82f42a3f1] +description = "division -> errors if dividing by zero" + +[1d5bb6b3-6749-4e02-8a79-b5d4d334cb8a] +description = "division -> errors if there is nothing on the stack" + +[d5547f43-c2ff-4d5c-9cb0-2a4f6684c20d] +description = "division -> errors if there is only one value on the stack" + +[ee28d729-6692-4a30-b9be-0d830c52a68c] +description = "combined arithmetic -> addition and subtraction" + +[40b197da-fa4b-4aca-a50b-f000d19422c1] +description = "combined arithmetic -> multiplication and division" + +[c5758235-6eef-4bf6-ab62-c878e50b9957] +description = "dup -> copies a value on the stack" + +[f6889006-5a40-41e7-beb3-43b09e5a22f4] +description = "dup -> copies the top value on the stack" + +[40b7569c-8401-4bd4-a30d-9adf70d11bc4] +description = "dup -> errors if there is nothing on the stack" + +[1971da68-1df2-4569-927a-72bf5bb7263c] +description = "drop -> removes the top value on the stack if it is the only one" + +[8929d9f2-4a78-4e0f-90ad-be1a0f313fd9] +description = "drop -> removes the top value on the stack if it is not the only one" + +[6dd31873-6dd7-4cb8-9e90-7daa33ba045c] +description = "drop -> errors if there is nothing on the stack" + +[3ee68e62-f98a-4cce-9e6c-8aae6c65a4e3] +description = "swap -> swaps the top two values on the stack if they are the only ones" + +[8ce869d5-a503-44e4-ab55-1da36816ff1c] +description = "swap -> swaps the top two values on the stack if they are not the only ones" + +[74ba5b2a-b028-4759-9176-c5c0e7b2b154] +description = "swap -> errors if there is nothing on the stack" + +[dd52e154-5d0d-4a5c-9e5d-73eb36052bc8] +description = "swap -> errors if there is only one value on the stack" + +[a2654074-ba68-4f93-b014-6b12693a8b50] +description = "over -> copies the second element if there are only two" + +[c5b51097-741a-4da7-8736-5c93fa856339] +description = "over -> copies the second element if there are more than two" + +[6e1703a6-5963-4a03-abba-02e77e3181fd] +description = "over -> errors if there is nothing on the stack" + +[ee574dc4-ef71-46f6-8c6a-b4af3a10c45f] +description = "over -> errors if there is only one value on the stack" + +[ed45cbbf-4dbf-4901-825b-54b20dbee53b] +description = "user-defined words -> can consist of built-in words" + +[2726ea44-73e4-436b-bc2b-5ff0c6aa014b] +description = "user-defined words -> execute in the right order" + +[9e53c2d0-b8ef-4ad8-b2c9-a559b421eb33] +description = "user-defined words -> can override other user-defined words" + +[669db3f3-5bd6-4be0-83d1-618cd6e4984b] +description = "user-defined words -> can override built-in words" + +[588de2f0-c56e-4c68-be0b-0bb1e603c500] +description = "user-defined words -> can override built-in operators" + +[ac12aaaf-26c6-4a10-8b3c-1c958fa2914c] +description = "user-defined words -> can use different words with the same name" + +[53f82ef0-2750-4ccb-ac04-5d8c1aefabb1] +description = "user-defined words -> can define word that uses word with the same name" + +[35958cee-a976-4a0f-9378-f678518fa322] +description = "user-defined words -> cannot redefine non-negative numbers" + +[df5b2815-3843-4f55-b16c-c3ed507292a7] +description = "user-defined words -> cannot redefine negative numbers" + +[5180f261-89dd-491e-b230-62737e09806f] +description = "user-defined words -> errors if executing a non-existent word" + +[3c8bfef3-edbb-49c1-9993-21d4030043cb] +description = "user-defined words -> only defines locally" + +[7b83bb2e-b0e8-461f-ad3b-96ee2e111ed6] +description = "case-insensitivity -> DUP is case-insensitive" + +[339ed30b-f5b4-47ff-ab1c-67591a9cd336] +description = "case-insensitivity -> DROP is case-insensitive" + +[ee1af31e-1355-4b1b-bb95-f9d0b2961b87] +description = "case-insensitivity -> SWAP is case-insensitive" + +[acdc3a49-14c8-4cc2-945d-11edee6408fa] +description = "case-insensitivity -> OVER is case-insensitive" + +[5934454f-a24f-4efc-9fdd-5794e5f0c23c] +description = "case-insensitivity -> user-defined words are case-insensitive" + +[037d4299-195f-4be7-a46d-f07ca6280a06] +description = "case-insensitivity -> definitions are case-insensitive" diff --git a/exercises/practice/forth/Mason.toml b/exercises/practice/forth/Mason.toml new file mode 100644 index 0000000..ece87d6 --- /dev/null +++ b/exercises/practice/forth/Mason.toml @@ -0,0 +1,5 @@ +[brick] +name="forth" +version="0.1.0" +chplVersion="1.33.0" +type="application" diff --git a/exercises/practice/forth/src/forth.chpl b/exercises/practice/forth/src/forth.chpl new file mode 100644 index 0000000..24d13dd --- /dev/null +++ b/exercises/practice/forth/src/forth.chpl @@ -0,0 +1,3 @@ +module Forth { + // write your solution here +} diff --git a/exercises/practice/forth/test/tests.chpl b/exercises/practice/forth/test/tests.chpl new file mode 100644 index 0000000..fa177ad --- /dev/null +++ b/exercises/practice/forth/test/tests.chpl @@ -0,0 +1,330 @@ +use UnitTest; +use UnitTest.TestError; +use List; +use Forth; + +proc testParsingAndNumbersNumbersJustGetPushedOntoTheStack(test : borrowed Test) throws { + test.assertEqual(evaluate(["1 2 3 4 5"]), new list([1, 2, 3, 4, 5])); +} + +proc testParsingAndNumbersPushesNegativeNumbersOntoTheStack(test : borrowed Test) throws { + test.assertEqual(evaluate(["-1 -2 -3 -4 -5"]), new list([-1, -2, -3, -4, -5])); +} + +proc testAdditionCanAddTwoNumbers(test : borrowed Test) throws { + test.assertEqual(evaluate(["1 2 +"]), new list([3])); +} + +proc testAdditionErrorsIfThereIsNothingOnTheStack(test : borrowed Test) throws { + try { + evaluate(["+"]); + throw new AssertionError("Function was expected to throw an error"); + } catch e : IllegalArgumentError { + test.assertEqual(e.message(), "empty stack"); + } catch e { + throw e; + } +} + +proc testAdditionErrorsIfThereIsOnlyOneValueOnTheStack(test : borrowed Test) throws { + try { + evaluate(["1 +"]); + throw new AssertionError("Function was expected to throw an error"); + } catch e : IllegalArgumentError { + test.assertEqual(e.message(), "only one value on the stack"); + } catch e { + throw e; + } +} + +proc testSubtractionCanSubtractTwoNumbers(test : borrowed Test) throws { + test.assertEqual(evaluate(["3 4 -"]), new list([-1])); +} + +proc testSubtractionErrorsIfThereIsNothingOnTheStack(test : borrowed Test) throws { + try { + evaluate(["-"]); + throw new AssertionError("Function was expected to throw an error"); + } catch e : IllegalArgumentError { + test.assertEqual(e.message(), "empty stack"); + } catch e { + throw e; + } +} + +proc testSubtractionErrorsIfThereIsOnlyOneValueOnTheStack(test : borrowed Test) throws { + try { + evaluate(["1 -"]); + throw new AssertionError("Function was expected to throw an error"); + } catch e : IllegalArgumentError { + test.assertEqual(e.message(), "only one value on the stack"); + } catch e { + throw e; + } +} + +proc testMultiplicationCanMultiplyTwoNumbers(test : borrowed Test) throws { + test.assertEqual(evaluate(["2 4 *"]), new list([8])); +} + +proc testMultiplicationErrorsIfThereIsNothingOnTheStack(test : borrowed Test) throws { + try { + evaluate(["*"]); + throw new AssertionError("Function was expected to throw an error"); + } catch e : IllegalArgumentError { + test.assertEqual(e.message(), "empty stack"); + } catch e { + throw e; + } +} + +proc testMultiplicationErrorsIfThereIsOnlyOneValueOnTheStack(test : borrowed Test) throws { + try { + evaluate(["1 *"]); + throw new AssertionError("Function was expected to throw an error"); + } catch e : IllegalArgumentError { + test.assertEqual(e.message(), "only one value on the stack"); + } catch e { + throw e; + } +} + +proc testDivisionCanDivideTwoNumbers(test : borrowed Test) throws { + test.assertEqual(evaluate(["12 3 /"]), new list([4])); +} + +proc testDivisionPerformsIntegerDivision(test : borrowed Test) throws { + test.assertEqual(evaluate(["8 3 /"]), new list([2])); +} + +proc testDivisionErrorsIfDividingByZero(test : borrowed Test) throws { + try { + evaluate(["4 0 /"]); + throw new AssertionError("Function was expected to throw an error"); + } catch e : IllegalArgumentError { + test.assertEqual(e.message(), "divide by zero"); + } catch e { + throw e; + } +} + +proc testDivisionErrorsIfThereIsNothingOnTheStack(test : borrowed Test) throws { + try { + evaluate(["/"]); + throw new AssertionError("Function was expected to throw an error"); + } catch e : IllegalArgumentError { + test.assertEqual(e.message(), "empty stack"); + } catch e { + throw e; + } +} + +proc testDivisionErrorsIfThereIsOnlyOneValueOnTheStack(test : borrowed Test) throws { + try { + evaluate(["1 /"]); + throw new AssertionError("Function was expected to throw an error"); + } catch e : IllegalArgumentError { + test.assertEqual(e.message(), "only one value on the stack"); + } catch e { + throw e; + } +} + +proc testCombinedArithmeticAdditionAndSubtraction(test : borrowed Test) throws { + test.assertEqual(evaluate(["1 2 + 4 -"]), new list([-1])); +} + +proc testCombinedArithmeticMultiplicationAndDivision(test : borrowed Test) throws { + test.assertEqual(evaluate(["2 4 * 3 /"]), new list([2])); +} + +proc testDupCopiesAValueOnTheStack(test : borrowed Test) throws { + test.assertEqual(evaluate(["1 dup"]), new list([1, 1])); +} + +proc testDupCopiesTheTopValueOnTheStack(test : borrowed Test) throws { + test.assertEqual(evaluate(["1 2 dup"]), new list([1, 2, 2])); +} + +proc testDupErrorsIfThereIsNothingOnTheStack(test : borrowed Test) throws { + try { + evaluate(["dup"]); + throw new AssertionError("Function was expected to throw an error"); + } catch e : IllegalArgumentError { + test.assertEqual(e.message(), "empty stack"); + } catch e { + throw e; + } +} + +proc testDropRemovesTheTopValueOnTheStackIfItIsTheOnlyOne(test : borrowed Test) throws { + test.assertTrue(evaluate(["1 drop"]).isEmpty()); +} + +proc testDropRemovesTheTopValueOnTheStackIfItIsNotTheOnlyOne(test : borrowed Test) throws { + test.assertEqual(evaluate(["1 2 drop"]), new list([1])); +} + +proc testDropErrorsIfThereIsNothingOnTheStack(test : borrowed Test) throws { + try { + evaluate(["drop"]); + throw new AssertionError("Function was expected to throw an error"); + } catch e : IllegalArgumentError { + test.assertEqual(e.message(), "empty stack"); + } catch e { + throw e; + } +} + +proc testSwapSwapsTheTopTwoValuesOnTheStackIfTheyAreTheOnlyOnes(test : borrowed Test) throws { + test.assertEqual(evaluate(["1 2 swap"]), new list([2, 1])); +} + +proc testSwapSwapsTheTopTwoValuesOnTheStackIfTheyAreNotTheOnlyOnes(test : borrowed Test) throws { + test.assertEqual(evaluate(["1 2 3 swap"]), new list([1, 3, 2])); +} + +proc testSwapErrorsIfThereIsNothingOnTheStack(test : borrowed Test) throws { + try { + evaluate(["swap"]); + throw new AssertionError("Function was expected to throw an error"); + } catch e : IllegalArgumentError { + test.assertEqual(e.message(), "empty stack"); + } catch e { + throw e; + } +} + +proc testSwapErrorsIfThereIsOnlyOneValueOnTheStack(test : borrowed Test) throws { + try { + evaluate(["1 swap"]); + throw new AssertionError("Function was expected to throw an error"); + } catch e : IllegalArgumentError { + test.assertEqual(e.message(), "only one value on the stack"); + } catch e { + throw e; + } +} + +proc testOverCopiesTheSecondElementIfThereAreOnlyTwo(test : borrowed Test) throws { + test.assertEqual(evaluate(["1 2 over"]), new list([1, 2, 1])); +} + +proc testOverCopiesTheSecondElementIfThereAreMoreThanTwo(test : borrowed Test) throws { + test.assertEqual(evaluate(["1 2 3 over"]), new list([1, 2, 3, 2])); +} + +proc testOverErrorsIfThereIsNothingOnTheStack(test : borrowed Test) throws { + try { + evaluate(["over"]); + throw new AssertionError("Function was expected to throw an error"); + } catch e : IllegalArgumentError { + test.assertEqual(e.message(), "empty stack"); + } catch e { + throw e; + } +} + +proc testOverErrorsIfThereIsOnlyOneValueOnTheStack(test : borrowed Test) throws { + try { + evaluate(["1 over"]); + throw new AssertionError("Function was expected to throw an error"); + } catch e : IllegalArgumentError { + test.assertEqual(e.message(), "only one value on the stack"); + } catch e { + throw e; + } +} + +proc testUserDefinedWordsCanConsistOfBuiltInWords(test : borrowed Test) throws { + test.assertEqual(evaluate([": dup-twice dup dup ;", "1 dup-twice"]), new list([1, 1, 1])); +} + +proc testUserDefinedWordsExecuteInTheRightOrder(test : borrowed Test) throws { + test.assertEqual(evaluate([": countup 1 2 3 ;", "countup"]), new list([1, 2, 3])); +} + +proc testUserDefinedWordsCanOverrideOtherUserDefinedWords(test : borrowed Test) throws { + test.assertEqual(evaluate([": foo dup ;", ": foo dup dup ;", "1 foo"]), new list([1, 1, 1])); +} + +proc testUserDefinedWordsCanOverrideBuiltInWords(test : borrowed Test) throws { + test.assertEqual(evaluate([": swap dup ;", "1 swap"]), new list([1, 1])); +} + +proc testUserDefinedWordsCanOverrideBuiltInOperators(test : borrowed Test) throws { + test.assertEqual(evaluate([": + * ;", "3 4 +"]), new list([12])); +} + +proc testUserDefinedWordsCanUseDifferentWordsWithTheSameName(test : borrowed Test) throws { + test.assertEqual(evaluate([": foo 5 ;", ": bar foo ;", ": foo 6 ;", "bar foo"]), new list([5, 6])); +} + +proc testUserDefinedWordsCanDefineWordThatUsesWordWithTheSameName(test : borrowed Test) throws { + test.assertEqual(evaluate([": foo 10 ;", ": foo foo 1 + ;", "foo"]), new list([11])); +} + +proc testUserDefinedWordsCannotRedefineNonNegativeNumbers(test : borrowed Test) throws { + try { + evaluate([": 1 2 ;"]); + throw new AssertionError("Function was expected to throw an error"); + } catch e : IllegalArgumentError { + test.assertEqual(e.message(), "illegal operation"); + } catch e { + throw e; + } +} + +proc testUserDefinedWordsCannotRedefineNegativeNumbers(test : borrowed Test) throws { + try { + evaluate([": -1 2 ;"]); + throw new AssertionError("Function was expected to throw an error"); + } catch e : IllegalArgumentError { + test.assertEqual(e.message(), "illegal operation"); + } catch e { + throw e; + } +} + +proc testUserDefinedWordsErrorsIfExecutingANonExistentWord(test : borrowed Test) throws { + try { + evaluate(["foo"]); + throw new AssertionError("Function was expected to throw an error"); + } catch e : IllegalArgumentError { + test.assertEqual(e.message(), "undefined operation"); + } catch e { + throw e; + } +} + +proc testUserDefinedWordsOnlyDefinesLocally(test : borrowed Test) throws { + var result1 = evaluate([": + - ;", "1 1 +"]); + var result2 = evaluate(["1 1 +"]); + test.assertEqual([result1, result2], [new list([0]), new list([2])]); +} + +proc testCaseInsensitivityDupIsCaseInsensitive(test : borrowed Test) throws { + test.assertEqual(evaluate(["1 DUP Dup dup"]), new list([1, 1, 1, 1])); +} + +proc testCaseInsensitivityDropIsCaseInsensitive(test : borrowed Test) throws { + test.assertEqual(evaluate(["1 2 3 4 DROP Drop drop"]), new list([1])); +} + +proc testCaseInsensitivitySwapIsCaseInsensitive(test : borrowed Test) throws { + test.assertEqual(evaluate(["1 2 SWAP 3 Swap 4 swap"]), new list([2, 3, 4, 1])); +} + +proc testCaseInsensitivityOverIsCaseInsensitive(test : borrowed Test) throws { + test.assertEqual(evaluate(["1 2 OVER Over over"]), new list([1, 2, 1, 2, 1])); +} + +proc testCaseInsensitivityUserDefinedWordsAreCaseInsensitive(test : borrowed Test) throws { + test.assertEqual(evaluate([": foo dup ;", "1 FOO Foo foo"]), new list([1, 1, 1, 1])); +} + +proc testCaseInsensitivityDefinitionsAreCaseInsensitive(test : borrowed Test) throws { + test.assertEqual(evaluate([": SWAP DUP Dup dup ;", "1 swap"]), new list([1, 1, 1, 1])); +} + +UnitTest.main();