diff --git a/exercises/practice/forth/.meta/test_template.tera b/exercises/practice/forth/.meta/test_template.tera new file mode 100644 index 000000000..1cc59aef5 --- /dev/null +++ b/exercises/practice/forth/.meta/test_template.tera @@ -0,0 +1,44 @@ +{% for test_group in cases %} +mod {{ test_group.description | snake_case }} { + use forth::*; + +{% for test in test_group.cases %} +#[test] +#[ignore] +fn {{ test.description | snake_case }}() { + let mut f = Forth::new(); + {% if test.property == "evaluateBoth" -%} + {% for instr in test.input.instructionsFirst -%} + assert!(f.eval("{{ instr }}").is_ok()); + {% endfor -%} + assert_eq!(f.stack(), {{ test.expected[0] | json_encode() }}); + let mut f = Forth::new(); + {% for instr in test.input.instructionsSecond -%} + assert!(f.eval("{{ instr }}").is_ok()); + {% endfor -%} + assert_eq!(f.stack(), {{ test.expected[1] | json_encode() }}); + } + {% continue %} + {% endif -%} + + {% if test.expected is object -%} + {% if test.expected.error == "empty stack" or test.expected.error == "only one value on the stack" -%} + assert_eq!(f.eval("{{ test.input.instructions[0] }}"), Err(Error::StackUnderflow)); + {% elif test.expected.error == "divide by zero" -%} + assert_eq!(f.eval("{{ test.input.instructions[0] }}"), Err(Error::DivisionByZero)); + {% elif test.expected.error == "illegal operation" -%} + assert_eq!(f.eval("{{ test.input.instructions[0] }}"), Err(Error::InvalidWord)); + {% elif test.expected.error == "undefined operation" -%} + assert_eq!(f.eval("{{ test.input.instructions[0] }}"), Err(Error::UnknownWord)); + {% endif -%} + {% else -%} + {% for instr in test.input.instructions -%} + assert!(f.eval("{{ instr }}").is_ok()); + {% endfor -%} + assert_eq!(f.stack(), {{ test.expected | json_encode() }}); + {% endif -%} +} +{% endfor %} + +} +{% endfor %} diff --git a/exercises/practice/forth/.meta/tests.toml b/exercises/practice/forth/.meta/tests.toml index ed1f74b2c..c9c1d6377 100644 --- a/exercises/practice/forth/.meta/tests.toml +++ b/exercises/practice/forth/.meta/tests.toml @@ -1,39 +1,157 @@ -# This is an auto-generated file. Regular comments will be removed when this -# file is regenerated. Regenerating will not touch any manually added keys, -# so comments can be added in a "comment" key. +# 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 = "numbers just get pushed onto the stack" +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 = "can add two numbers" +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 = "can subtract two numbers" +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 = "can multiply two numbers" +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 = "can divide two numbers" +description = "division -> can divide two numbers" [54f6711c-4b14-4bb0-98ad-d974a22c4620] -description = "performs integer division" +description = "division -> performs integer division" [a5df3219-29b4-4d2f-b427-81f82f42a3f1] -description = "errors if dividing by zero" +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 = "addition and subtraction" +description = "combined arithmetic -> addition and subtraction" [40b197da-fa4b-4aca-a50b-f000d19422c1] -description = "multiplication and division" +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 = "execute in the right order" +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 = "can use different words with the same name" +description = "user-defined words -> can use different words with the same name" [53f82ef0-2750-4ccb-ac04-5d8c1aefabb1] -description = "can define word that uses word with the same name" +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/tests/forth.rs b/exercises/practice/forth/tests/forth.rs index a2526e98e..c6ed17ed5 100644 --- a/exercises/practice/forth/tests/forth.rs +++ b/exercises/practice/forth/tests/forth.rs @@ -1,373 +1,437 @@ -use forth::{Error, Forth, Value}; - -#[test] -fn no_input_no_stack() { - assert_eq!(Vec::::new(), Forth::new().stack()); -} - -#[test] -#[ignore] -fn numbers_just_get_pushed_onto_the_stack() { - let mut f = Forth::new(); - assert!(f.eval("1 2 3 4 5").is_ok()); - assert_eq!(vec![1, 2, 3, 4, 5], f.stack()); -} - -#[test] -#[ignore] -fn can_add_two_numbers() { - let mut f = Forth::new(); - assert!(f.eval("1 2 +").is_ok()); - assert_eq!(vec![3], f.stack()); -} - -#[test] -#[ignore] -fn addition_error() { - let mut f = Forth::new(); - assert_eq!(Err(Error::StackUnderflow), f.eval("1 +")); - assert_eq!(Err(Error::StackUnderflow), f.eval("+")); -} - -#[test] -#[ignore] -fn can_subtract_two_numbers() { - let mut f = Forth::new(); - assert!(f.eval("3 4 -").is_ok()); - assert_eq!(vec![-1], f.stack()); -} - -#[test] -#[ignore] -fn subtraction_error() { - let mut f = Forth::new(); - assert_eq!(Err(Error::StackUnderflow), f.eval("1 -")); - assert_eq!(Err(Error::StackUnderflow), f.eval("-")); -} - -#[test] -#[ignore] -fn can_multiply_two_numbers() { - let mut f = Forth::new(); - assert!(f.eval("2 4 *").is_ok()); - assert_eq!(vec![8], f.stack()); -} - -#[test] -#[ignore] -fn multiplication_error() { - let mut f = Forth::new(); - assert_eq!(Err(Error::StackUnderflow), f.eval("1 *")); - assert_eq!(Err(Error::StackUnderflow), f.eval("*")); -} - -#[test] -#[ignore] -fn can_divide_two_numbers() { - let mut f = Forth::new(); - assert!(f.eval("12 3 /").is_ok()); - assert_eq!(vec![4], f.stack()); -} - -#[test] -#[ignore] -fn performs_integer_division() { - let mut f = Forth::new(); - assert!(f.eval("8 3 /").is_ok()); - assert_eq!(vec![2], f.stack()); -} - -#[test] -#[ignore] -fn division_error() { - let mut f = Forth::new(); - assert_eq!(Err(Error::StackUnderflow), f.eval("1 /")); - assert_eq!(Err(Error::StackUnderflow), f.eval("/")); -} - -#[test] -#[ignore] -fn errors_if_dividing_by_zero() { - let mut f = Forth::new(); - assert_eq!(Err(Error::DivisionByZero), f.eval("4 0 /")); -} - -#[test] -#[ignore] -fn addition_and_subtraction() { - let mut f = Forth::new(); - assert!(f.eval("1 2 + 4 -").is_ok()); - assert_eq!(vec![-1], f.stack()); -} - -#[test] -#[ignore] -fn multiplication_and_division() { - let mut f = Forth::new(); - assert!(f.eval("2 4 * 3 /").is_ok()); - assert_eq!(vec![2], f.stack()); -} - -#[test] -#[ignore] -fn dup() { - let mut f = Forth::new(); - assert!(f.eval("1 dup").is_ok()); - assert_eq!(vec![1, 1], f.stack()); -} - -#[test] -#[ignore] -fn dup_top_value_only() { - let mut f = Forth::new(); - assert!(f.eval("1 2 dup").is_ok()); - assert_eq!(vec![1, 2, 2], f.stack()); -} - -#[test] -#[ignore] -fn dup_case_insensitive() { - let mut f = Forth::new(); - assert!(f.eval("1 DUP Dup dup").is_ok()); - assert_eq!(vec![1, 1, 1, 1], f.stack()); -} - -#[test] -#[ignore] -fn dup_error() { - let mut f = Forth::new(); - assert_eq!(Err(Error::StackUnderflow), f.eval("dup")); -} - -#[test] -#[ignore] -fn drop() { - let mut f = Forth::new(); - assert!(f.eval("1 drop").is_ok()); - assert_eq!(Vec::::new(), f.stack()); -} - -#[test] -#[ignore] -fn drop_with_two() { - let mut f = Forth::new(); - assert!(f.eval("1 2 drop").is_ok()); - assert_eq!(vec![1], f.stack()); -} - -#[test] -#[ignore] -fn drop_case_insensitive() { - let mut f = Forth::new(); - assert!(f.eval("1 2 3 4 DROP Drop drop").is_ok()); - assert_eq!(vec![1], f.stack()); -} - -#[test] -#[ignore] -fn drop_error() { - let mut f = Forth::new(); - assert_eq!(Err(Error::StackUnderflow), f.eval("drop")); -} - -#[test] -#[ignore] -fn swap() { - let mut f = Forth::new(); - assert!(f.eval("1 2 swap").is_ok()); - assert_eq!(vec![2, 1], f.stack()); -} - -#[test] -#[ignore] -fn swap_with_three() { - let mut f = Forth::new(); - assert!(f.eval("1 2 3 swap").is_ok()); - assert_eq!(vec![1, 3, 2], f.stack()); -} - -#[test] -#[ignore] -fn swap_case_insensitive() { - let mut f = Forth::new(); - assert!(f.eval("1 2 SWAP 3 Swap 4 swap").is_ok()); - assert_eq!(vec![2, 3, 4, 1], f.stack()); -} - -#[test] -#[ignore] -fn swap_error() { - let mut f = Forth::new(); - assert_eq!(Err(Error::StackUnderflow), f.eval("1 swap")); - assert_eq!(Err(Error::StackUnderflow), f.eval("swap")); -} - -#[test] -#[ignore] -fn over() { - let mut f = Forth::new(); - assert!(f.eval("1 2 over").is_ok()); - assert_eq!(vec![1, 2, 1], f.stack()); -} - -#[test] -#[ignore] -fn over_with_three() { - let mut f = Forth::new(); - assert!(f.eval("1 2 3 over").is_ok()); - assert_eq!(vec![1, 2, 3, 2], f.stack()); -} - -#[test] -#[ignore] -fn over_case_insensitive() { - let mut f = Forth::new(); - assert!(f.eval("1 2 OVER Over over").is_ok()); - assert_eq!(vec![1, 2, 1, 2, 1], f.stack()); -} - -#[test] -#[ignore] -fn over_error() { - let mut f = Forth::new(); - assert_eq!(Err(Error::StackUnderflow), f.eval("1 over")); - assert_eq!(Err(Error::StackUnderflow), f.eval("over")); -} - -// User-defined words - -#[test] -#[ignore] -fn can_consist_of_built_in_words() { - let mut f = Forth::new(); - assert!(f.eval(": dup-twice dup dup ;").is_ok()); - assert!(f.eval("1 dup-twice").is_ok()); - assert_eq!(vec![1, 1, 1], f.stack()); -} - -#[test] -#[ignore] -fn execute_in_the_right_order() { - let mut f = Forth::new(); - assert!(f.eval(": countup 1 2 3 ;").is_ok()); - assert!(f.eval("countup").is_ok()); - assert_eq!(vec![1, 2, 3], f.stack()); -} - -#[test] -#[ignore] -fn redefining_an_existing_word() { - let mut f = Forth::new(); - assert!(f.eval(": foo dup ;").is_ok()); - assert!(f.eval(": foo dup dup ;").is_ok()); - assert!(f.eval("1 foo").is_ok()); - assert_eq!(vec![1, 1, 1], f.stack()); -} - -#[test] -#[ignore] -fn redefining_an_existing_built_in_word() { - let mut f = Forth::new(); - assert!(f.eval(": swap dup ;").is_ok()); - assert!(f.eval("1 swap").is_ok()); - assert_eq!(vec![1, 1], f.stack()); -} - -#[test] -#[ignore] -fn user_defined_words_are_case_insensitive() { - let mut f = Forth::new(); - assert!(f.eval(": foo dup ;").is_ok()); - assert!(f.eval("1 FOO Foo foo").is_ok()); - assert_eq!(vec![1, 1, 1, 1], f.stack()); -} - -#[test] -#[ignore] -fn definitions_are_case_insensitive() { - let mut f = Forth::new(); - assert!(f.eval(": SWAP DUP Dup dup ;").is_ok()); - assert!(f.eval("1 swap").is_ok()); - assert_eq!(vec![1, 1, 1, 1], f.stack()); -} - -#[test] -#[ignore] -fn redefining_a_built_in_operator() { - let mut f = Forth::new(); - assert!(f.eval(": + * ;").is_ok()); - assert!(f.eval("3 4 +").is_ok()); - assert_eq!(vec![12], f.stack()); -} - -#[test] -#[ignore] -fn can_use_different_words_with_the_same_name() { - let mut f = Forth::new(); - assert!(f.eval(": foo 5 ;").is_ok()); - assert!(f.eval(": bar foo ;").is_ok()); - assert!(f.eval(": foo 6 ;").is_ok()); - assert!(f.eval("bar foo").is_ok()); - assert_eq!(vec![5, 6], f.stack()); -} - -#[test] -#[ignore] -fn can_define_word_that_uses_word_with_the_same_name() { - let mut f = Forth::new(); - assert!(f.eval(": foo 10 ;").is_ok()); - assert!(f.eval(": foo foo 1 + ;").is_ok()); - assert!(f.eval("foo").is_ok()); - assert_eq!(vec![11], f.stack()); -} - -#[test] -#[ignore] -fn defining_a_number() { - let mut f = Forth::new(); - assert_eq!(Err(Error::InvalidWord), f.eval(": 1 2 ;")); -} - -#[test] -#[ignore] -fn malformed_word_definition() { - let mut f = Forth::new(); - assert_eq!(Err(Error::InvalidWord), f.eval(":")); - assert_eq!(Err(Error::InvalidWord), f.eval(": foo")); - assert_eq!(Err(Error::InvalidWord), f.eval(": foo 1")); -} - -#[test] -#[ignore] -fn calling_non_existing_word() { - let mut f = Forth::new(); - assert_eq!(Err(Error::UnknownWord), f.eval("1 foo")); -} - -#[test] -#[ignore] -fn multiple_definitions() { - let mut f = Forth::new(); - assert!(f.eval(": one 1 ; : two 2 ; one two +").is_ok()); - assert_eq!(vec![3], f.stack()); -} - -#[test] -#[ignore] -fn definitions_after_ops() { - let mut f = Forth::new(); - assert!(f.eval("1 2 + : addone 1 + ; addone").is_ok()); - assert_eq!(vec![4], f.stack()); -} - -#[test] -#[ignore] -fn redefine_an_existing_word_with_another_existing_word() { - let mut f = Forth::new(); - assert!(f.eval(": foo 5 ;").is_ok()); - assert!(f.eval(": bar foo ;").is_ok()); - assert!(f.eval(": foo 6 ;").is_ok()); - assert!(f.eval(": bar foo ;").is_ok()); - assert!(f.eval("bar foo").is_ok()); - assert_eq!(vec![6, 6], f.stack()); +mod parsing_and_numbers { + use forth::*; + + #[test] + fn numbers_just_get_pushed_onto_the_stack() { + let mut f = Forth::new(); + assert!(f.eval("1 2 3 4 5").is_ok()); + assert_eq!(f.stack(), [1, 2, 3, 4, 5]); + } + + #[test] + #[ignore] + fn pushes_negative_numbers_onto_the_stack() { + let mut f = Forth::new(); + assert!(f.eval("-1 -2 -3 -4 -5").is_ok()); + assert_eq!(f.stack(), [-1, -2, -3, -4, -5]); + } +} + +mod addition { + use forth::*; + + #[test] + #[ignore] + fn can_add_two_numbers() { + let mut f = Forth::new(); + assert!(f.eval("1 2 +").is_ok()); + assert_eq!(f.stack(), [3]); + } + + #[test] + #[ignore] + fn errors_if_there_is_nothing_on_the_stack() { + let mut f = Forth::new(); + assert_eq!(f.eval("+"), Err(Error::StackUnderflow)); + } + + #[test] + #[ignore] + fn errors_if_there_is_only_one_value_on_the_stack() { + let mut f = Forth::new(); + assert_eq!(f.eval("1 +"), Err(Error::StackUnderflow)); + } +} + +mod subtraction { + use forth::*; + + #[test] + #[ignore] + fn can_subtract_two_numbers() { + let mut f = Forth::new(); + assert!(f.eval("3 4 -").is_ok()); + assert_eq!(f.stack(), [-1]); + } + + #[test] + #[ignore] + fn errors_if_there_is_nothing_on_the_stack() { + let mut f = Forth::new(); + assert_eq!(f.eval("-"), Err(Error::StackUnderflow)); + } + + #[test] + #[ignore] + fn errors_if_there_is_only_one_value_on_the_stack() { + let mut f = Forth::new(); + assert_eq!(f.eval("1 -"), Err(Error::StackUnderflow)); + } +} + +mod multiplication { + use forth::*; + + #[test] + #[ignore] + fn can_multiply_two_numbers() { + let mut f = Forth::new(); + assert!(f.eval("2 4 *").is_ok()); + assert_eq!(f.stack(), [8]); + } + + #[test] + #[ignore] + fn errors_if_there_is_nothing_on_the_stack() { + let mut f = Forth::new(); + assert_eq!(f.eval("*"), Err(Error::StackUnderflow)); + } + + #[test] + #[ignore] + fn errors_if_there_is_only_one_value_on_the_stack() { + let mut f = Forth::new(); + assert_eq!(f.eval("1 *"), Err(Error::StackUnderflow)); + } +} + +mod division { + use forth::*; + + #[test] + #[ignore] + fn can_divide_two_numbers() { + let mut f = Forth::new(); + assert!(f.eval("12 3 /").is_ok()); + assert_eq!(f.stack(), [4]); + } + + #[test] + #[ignore] + fn performs_integer_division() { + let mut f = Forth::new(); + assert!(f.eval("8 3 /").is_ok()); + assert_eq!(f.stack(), [2]); + } + + #[test] + #[ignore] + fn errors_if_dividing_by_zero() { + let mut f = Forth::new(); + assert_eq!(f.eval("4 0 /"), Err(Error::DivisionByZero)); + } + + #[test] + #[ignore] + fn errors_if_there_is_nothing_on_the_stack() { + let mut f = Forth::new(); + assert_eq!(f.eval("/"), Err(Error::StackUnderflow)); + } + + #[test] + #[ignore] + fn errors_if_there_is_only_one_value_on_the_stack() { + let mut f = Forth::new(); + assert_eq!(f.eval("1 /"), Err(Error::StackUnderflow)); + } +} + +mod combined_arithmetic { + use forth::*; + + #[test] + #[ignore] + fn addition_and_subtraction() { + let mut f = Forth::new(); + assert!(f.eval("1 2 + 4 -").is_ok()); + assert_eq!(f.stack(), [-1]); + } + + #[test] + #[ignore] + fn multiplication_and_division() { + let mut f = Forth::new(); + assert!(f.eval("2 4 * 3 /").is_ok()); + assert_eq!(f.stack(), [2]); + } +} + +mod dup { + use forth::*; + + #[test] + #[ignore] + fn copies_a_value_on_the_stack() { + let mut f = Forth::new(); + assert!(f.eval("1 dup").is_ok()); + assert_eq!(f.stack(), [1, 1]); + } + + #[test] + #[ignore] + fn copies_the_top_value_on_the_stack() { + let mut f = Forth::new(); + assert!(f.eval("1 2 dup").is_ok()); + assert_eq!(f.stack(), [1, 2, 2]); + } + + #[test] + #[ignore] + fn errors_if_there_is_nothing_on_the_stack() { + let mut f = Forth::new(); + assert_eq!(f.eval("dup"), Err(Error::StackUnderflow)); + } +} + +mod drop { + use forth::*; + + #[test] + #[ignore] + fn removes_the_top_value_on_the_stack_if_it_is_the_only_one() { + let mut f = Forth::new(); + assert!(f.eval("1 drop").is_ok()); + assert_eq!(f.stack(), []); + } + + #[test] + #[ignore] + fn removes_the_top_value_on_the_stack_if_it_is_not_the_only_one() { + let mut f = Forth::new(); + assert!(f.eval("1 2 drop").is_ok()); + assert_eq!(f.stack(), [1]); + } + + #[test] + #[ignore] + fn errors_if_there_is_nothing_on_the_stack() { + let mut f = Forth::new(); + assert_eq!(f.eval("drop"), Err(Error::StackUnderflow)); + } +} + +mod swap { + use forth::*; + + #[test] + #[ignore] + fn swaps_the_top_two_values_on_the_stack_if_they_are_the_only_ones() { + let mut f = Forth::new(); + assert!(f.eval("1 2 swap").is_ok()); + assert_eq!(f.stack(), [2, 1]); + } + + #[test] + #[ignore] + fn swaps_the_top_two_values_on_the_stack_if_they_are_not_the_only_ones() { + let mut f = Forth::new(); + assert!(f.eval("1 2 3 swap").is_ok()); + assert_eq!(f.stack(), [1, 3, 2]); + } + + #[test] + #[ignore] + fn errors_if_there_is_nothing_on_the_stack() { + let mut f = Forth::new(); + assert_eq!(f.eval("swap"), Err(Error::StackUnderflow)); + } + + #[test] + #[ignore] + fn errors_if_there_is_only_one_value_on_the_stack() { + let mut f = Forth::new(); + assert_eq!(f.eval("1 swap"), Err(Error::StackUnderflow)); + } +} + +mod over { + use forth::*; + + #[test] + #[ignore] + fn copies_the_second_element_if_there_are_only_two() { + let mut f = Forth::new(); + assert!(f.eval("1 2 over").is_ok()); + assert_eq!(f.stack(), [1, 2, 1]); + } + + #[test] + #[ignore] + fn copies_the_second_element_if_there_are_more_than_two() { + let mut f = Forth::new(); + assert!(f.eval("1 2 3 over").is_ok()); + assert_eq!(f.stack(), [1, 2, 3, 2]); + } + + #[test] + #[ignore] + fn errors_if_there_is_nothing_on_the_stack() { + let mut f = Forth::new(); + assert_eq!(f.eval("over"), Err(Error::StackUnderflow)); + } + + #[test] + #[ignore] + fn errors_if_there_is_only_one_value_on_the_stack() { + let mut f = Forth::new(); + assert_eq!(f.eval("1 over"), Err(Error::StackUnderflow)); + } +} + +mod user_defined_words { + use forth::*; + + #[test] + #[ignore] + fn can_consist_of_built_in_words() { + let mut f = Forth::new(); + assert!(f.eval(": dup-twice dup dup ;").is_ok()); + assert!(f.eval("1 dup-twice").is_ok()); + assert_eq!(f.stack(), [1, 1, 1]); + } + + #[test] + #[ignore] + fn execute_in_the_right_order() { + let mut f = Forth::new(); + assert!(f.eval(": countup 1 2 3 ;").is_ok()); + assert!(f.eval("countup").is_ok()); + assert_eq!(f.stack(), [1, 2, 3]); + } + + #[test] + #[ignore] + fn can_override_other_user_defined_words() { + let mut f = Forth::new(); + assert!(f.eval(": foo dup ;").is_ok()); + assert!(f.eval(": foo dup dup ;").is_ok()); + assert!(f.eval("1 foo").is_ok()); + assert_eq!(f.stack(), [1, 1, 1]); + } + + #[test] + #[ignore] + fn can_override_built_in_words() { + let mut f = Forth::new(); + assert!(f.eval(": swap dup ;").is_ok()); + assert!(f.eval("1 swap").is_ok()); + assert_eq!(f.stack(), [1, 1]); + } + + #[test] + #[ignore] + fn can_override_built_in_operators() { + let mut f = Forth::new(); + assert!(f.eval(": + * ;").is_ok()); + assert!(f.eval("3 4 +").is_ok()); + assert_eq!(f.stack(), [12]); + } + + #[test] + #[ignore] + fn can_use_different_words_with_the_same_name() { + let mut f = Forth::new(); + assert!(f.eval(": foo 5 ;").is_ok()); + assert!(f.eval(": bar foo ;").is_ok()); + assert!(f.eval(": foo 6 ;").is_ok()); + assert!(f.eval("bar foo").is_ok()); + assert_eq!(f.stack(), [5, 6]); + } + + #[test] + #[ignore] + fn can_define_word_that_uses_word_with_the_same_name() { + let mut f = Forth::new(); + assert!(f.eval(": foo 10 ;").is_ok()); + assert!(f.eval(": foo foo 1 + ;").is_ok()); + assert!(f.eval("foo").is_ok()); + assert_eq!(f.stack(), [11]); + } + + #[test] + #[ignore] + fn cannot_redefine_non_negative_numbers() { + let mut f = Forth::new(); + assert_eq!(f.eval(": 1 2 ;"), Err(Error::InvalidWord)); + } + + #[test] + #[ignore] + fn cannot_redefine_negative_numbers() { + let mut f = Forth::new(); + assert_eq!(f.eval(": -1 2 ;"), Err(Error::InvalidWord)); + } + + #[test] + #[ignore] + fn errors_if_executing_a_non_existent_word() { + let mut f = Forth::new(); + assert_eq!(f.eval("foo"), Err(Error::UnknownWord)); + } + + #[test] + #[ignore] + fn only_defines_locally() { + let mut f = Forth::new(); + assert!(f.eval(": + - ;").is_ok()); + assert!(f.eval("1 1 +").is_ok()); + assert_eq!(f.stack(), [0]); + let mut f = Forth::new(); + assert!(f.eval("1 1 +").is_ok()); + assert_eq!(f.stack(), [2]); + } +} + +mod case_insensitivity { + use forth::*; + + #[test] + #[ignore] + fn dup_is_case_insensitive() { + let mut f = Forth::new(); + assert!(f.eval("1 DUP Dup dup").is_ok()); + assert_eq!(f.stack(), [1, 1, 1, 1]); + } + + #[test] + #[ignore] + fn drop_is_case_insensitive() { + let mut f = Forth::new(); + assert!(f.eval("1 2 3 4 DROP Drop drop").is_ok()); + assert_eq!(f.stack(), [1]); + } + + #[test] + #[ignore] + fn swap_is_case_insensitive() { + let mut f = Forth::new(); + assert!(f.eval("1 2 SWAP 3 Swap 4 swap").is_ok()); + assert_eq!(f.stack(), [2, 3, 4, 1]); + } + + #[test] + #[ignore] + fn over_is_case_insensitive() { + let mut f = Forth::new(); + assert!(f.eval("1 2 OVER Over over").is_ok()); + assert_eq!(f.stack(), [1, 2, 1, 2, 1]); + } + + #[test] + #[ignore] + fn user_defined_words_are_case_insensitive() { + let mut f = Forth::new(); + assert!(f.eval(": foo dup ;").is_ok()); + assert!(f.eval("1 FOO Foo foo").is_ok()); + assert_eq!(f.stack(), [1, 1, 1, 1]); + } + + #[test] + #[ignore] + fn definitions_are_case_insensitive() { + let mut f = Forth::new(); + assert!(f.eval(": SWAP DUP Dup dup ;").is_ok()); + assert!(f.eval("1 swap").is_ok()); + assert_eq!(f.stack(), [1, 1, 1, 1]); + } }