diff options
Diffstat (limited to 'spec/practical_spec.cr')
| -rw-r--r-- | spec/practical_spec.cr | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/spec/practical_spec.cr b/spec/practical_spec.cr index 9dc8ed5..f992a15 100644 --- a/spec/practical_spec.cr +++ b/spec/practical_spec.cr @@ -37,3 +37,110 @@ describe "Text surrounded by whitespace" do end end +enum BFOpType + BFAdd + BFShift + BFByteIn + BFByteOut + BFLoopStart + BFLoopEnd +end + +alias BFOp = { + type: BFOpType, + amount: Int32, +} + +describe "brainfuck parser" do + # From https://esolangs.org/wiki/Brainfuck#Examples + hello_world_str = "1 +++++ +++ Set Cell #0 to 8 + 2 [ + 3 >++++ Add 4 to Cell #1; this will always set Cell #1 to 4 + 4 [ as the cell will be cleared by the loop + 5 >++ Add 4*2 to Cell #2 + 6 >+++ Add 4*3 to Cell #3 + 7 >+++ Add 4*3 to Cell #4 + 8 >+ Add 4 to Cell #5 + 9 <<<<- Decrement the loop counter in Cell #1 +10 ] Loop till Cell #1 is zero +11 >+ Add 1 to Cell #2 +12 >+ Add 1 to Cell #3 +13 >- Subtract 1 from Cell #4 +14 >>+ Add 1 to Cell #6 +15 [<] Move back to the first zero cell you find; this will +16 be Cell #1 which was cleared by the previous loop +17 <- Decrement the loop Counter in Cell #0 +18 ] Loop till Cell #0 is zero +19 +20 The result of this is: +21 Cell No : 0 1 2 3 4 5 6 +22 Contents: 0 0 72 104 88 32 8 +23 Pointer : ^ +24 +25 >>. Cell #2 has value 72 which is 'H' +26 >---. Subtract 3 from Cell #3 to get 101 which is 'e' +27 +++++ ++..+++. Likewise for 'llo' from Cell #3 +28 >>. Cell #5 is 32 for the space +29 <-. Subtract 1 from Cell #4 for 87 to give a 'W' +30 <. Cell #3 was set to 'o' from the end of 'Hello' +31 +++.----- -.----- ---. Cell #3 for 'rl' and 'd' +32 >>+. Add 1 to Cell #5 gives us an exclamation point +33 >++. And finally a newline from Cell #6" + + bf_chars = Set{'+', '-', '.', ',', '<', '>', '[', ']'} + other = Parser(Char, Char).satisfy { |c| !bf_chars.includes?(c) } + bf_char = Parser(Char, Char).satisfy { |c| bf_chars.includes?(c) } + + char_body = bf_char.some.sep_by(other.some).map(&.flatten) + just_bf_chars = other.many >> char_body << other.many + + loop_start = Parser(Char, Char).token('[').map do |_| + {type: BFOpType::BFLoopStart, amount: 0} + end + + loop_end = Parser(Char, Char).token(']').map do |_| + {type: BFOpType::BFLoopEnd, amount: 0} + end + + read_block = Parser(Char, Char).token(',').some.map do |cs| + {type: BFOpType::BFByteIn, amount: cs.size} + end + + write_block = Parser(Char, Char).token('.').some.map do |cs| + {type: BFOpType::BFByteOut, amount: cs.size} + end + + shift_block = (Parser.token('<') | Parser.token('>')).some.map do |cs| + left_count = cs.count { |c| c == '<' } + right_count = cs.size - left_count + {type: BFOpType::BFShift, amount: right_count - left_count} + end + + add_block = (Parser.token('+') | Parser.token('-')).some.map do |cs| + minus_count = cs.count { |c| c == '-' } + plus_count = cs.size - minus_count + {type: BFOpType::BFAdd, amount: plus_count - minus_count} + end + + bf_token = Parser.first_of([ + loop_start, + loop_end, + read_block, + write_block, + shift_block, + add_block, + ]) + + tokenizer = Parser(Char, Array(BFOp)).new("BF tokenizer") do |tokens| + result = just_bf_chars.parse(tokens) + puts result + chars = Tokens.new(result.value) + bf_token.some.parse(chars) + end + + tokens = Tokens.from_string(hello_world_str) + result = tokenizer.parse(tokens) + puts result + # TODO: find a good way to verify this result +end + |
