whitespace-ruby 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/Gemfile +3 -0
  4. data/LICENSE.md +21 -0
  5. data/README.md +52 -0
  6. data/Rakefile +10 -0
  7. data/bin/whitespace +67 -0
  8. data/examples/count.ws +76 -0
  9. data/examples/fact.ws +135 -0
  10. data/examples/hello.ws +110 -0
  11. data/examples/name.ws +135 -0
  12. data/lib/whitespace.rb +14 -0
  13. data/lib/whitespace/data_structures/console.rb +56 -0
  14. data/lib/whitespace/data_structures/counter.rb +24 -0
  15. data/lib/whitespace/data_structures/memory.rb +19 -0
  16. data/lib/whitespace/data_structures/stack.rb +25 -0
  17. data/lib/whitespace/instructions/arithmetic/add.rb +9 -0
  18. data/lib/whitespace/instructions/arithmetic/binop.rb +18 -0
  19. data/lib/whitespace/instructions/arithmetic/div.rb +9 -0
  20. data/lib/whitespace/instructions/arithmetic/mod.rb +9 -0
  21. data/lib/whitespace/instructions/arithmetic/mul.rb +9 -0
  22. data/lib/whitespace/instructions/arithmetic/sub.rb +9 -0
  23. data/lib/whitespace/instructions/flow_control/call.rb +19 -0
  24. data/lib/whitespace/instructions/flow_control/end.rb +7 -0
  25. data/lib/whitespace/instructions/flow_control/label.rb +16 -0
  26. data/lib/whitespace/instructions/flow_control/njmp.rb +20 -0
  27. data/lib/whitespace/instructions/flow_control/return.rb +7 -0
  28. data/lib/whitespace/instructions/flow_control/ujmp.rb +18 -0
  29. data/lib/whitespace/instructions/flow_control/zjmp.rb +20 -0
  30. data/lib/whitespace/instructions/heap_access/retrieve.rb +9 -0
  31. data/lib/whitespace/instructions/heap_access/store.rb +10 -0
  32. data/lib/whitespace/instructions/instruction.rb +13 -0
  33. data/lib/whitespace/instructions/io/putc.rb +15 -0
  34. data/lib/whitespace/instructions/io/putn.rb +15 -0
  35. data/lib/whitespace/instructions/io/readc.rb +16 -0
  36. data/lib/whitespace/instructions/io/readn.rb +16 -0
  37. data/lib/whitespace/instructions/stack_manipulation/discard.rb +7 -0
  38. data/lib/whitespace/instructions/stack_manipulation/dup.rb +7 -0
  39. data/lib/whitespace/instructions/stack_manipulation/push.rb +17 -0
  40. data/lib/whitespace/instructions/stack_manipulation/swap.rb +11 -0
  41. data/lib/whitespace/isa.rb +9 -0
  42. data/lib/whitespace/parser.rb +243 -0
  43. data/lib/whitespace/util.rb +37 -0
  44. data/lib/whitespace/version.rb +3 -0
  45. data/lib/whitespace/vm.rb +44 -0
  46. data/test/test_helper.rb +4 -0
  47. data/test/whitespace/data_structures/console_test.rb +113 -0
  48. data/test/whitespace/data_structures/counter_test.rb +37 -0
  49. data/test/whitespace/data_structures/memory_test.rb +25 -0
  50. data/test/whitespace/data_structures/stack_test.rb +63 -0
  51. data/test/whitespace/instructions/arithmetic/add_test.rb +43 -0
  52. data/test/whitespace/instructions/arithmetic/div_test.rb +52 -0
  53. data/test/whitespace/instructions/arithmetic/mod_test.rb +52 -0
  54. data/test/whitespace/instructions/arithmetic/mul_test.rb +43 -0
  55. data/test/whitespace/instructions/arithmetic/sub_test.rb +43 -0
  56. data/test/whitespace/instructions/flow_control/call_test.rb +50 -0
  57. data/test/whitespace/instructions/flow_control/end_test.rb +15 -0
  58. data/test/whitespace/instructions/flow_control/label_test.rb +24 -0
  59. data/test/whitespace/instructions/flow_control/njmp_test.rb +94 -0
  60. data/test/whitespace/instructions/flow_control/return_test.rb +33 -0
  61. data/test/whitespace/instructions/flow_control/ujmp_test.rb +44 -0
  62. data/test/whitespace/instructions/flow_control/zjmp_test.rb +94 -0
  63. data/test/whitespace/instructions/heap_access/retrieve_test.rb +49 -0
  64. data/test/whitespace/instructions/heap_access/store_test.rb +44 -0
  65. data/test/whitespace/instructions/instruction_test.rb +11 -0
  66. data/test/whitespace/instructions/io/putc_test.rb +42 -0
  67. data/test/whitespace/instructions/io/putn_test.rb +42 -0
  68. data/test/whitespace/instructions/io/readc_test.rb +71 -0
  69. data/test/whitespace/instructions/io/readn_test.rb +79 -0
  70. data/test/whitespace/instructions/stack_manipulation/discard_test.rb +30 -0
  71. data/test/whitespace/instructions/stack_manipulation/dup_test.rb +32 -0
  72. data/test/whitespace/instructions/stack_manipulation/push_test.rb +30 -0
  73. data/test/whitespace/instructions/stack_manipulation/swap_test.rb +43 -0
  74. data/test/whitespace/parser_test.rb +362 -0
  75. data/test/whitespace/util_test.rb +87 -0
  76. data/test/whitespace/vm_test.rb +80 -0
  77. data/whitespace-ruby.gemspec +30 -0
  78. metadata +178 -0
@@ -0,0 +1,3 @@
1
+ module Whitespace
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,44 @@
1
+ require_relative "data_structures/console"
2
+ require_relative "data_structures/counter"
3
+ require_relative "data_structures/memory"
4
+ require_relative "data_structures/stack"
5
+
6
+ module Whitespace
7
+ class VM
8
+ attr_reader :instructions, :vstack, :cstack, :memory, :pc
9
+
10
+ def initialize
11
+ @instructions = []
12
+ reset
13
+ end
14
+
15
+ def load(instructions)
16
+ @instructions = Array(instructions)
17
+ end
18
+
19
+ def run
20
+ reset
21
+
22
+ loop do
23
+ instruction = instructions.fetch pc
24
+ pc.increment
25
+ execute instruction
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def reset
32
+ @vstack = Stack.new # a value stack
33
+ @cstack = Stack.new # a call stack
34
+ @memory = Memory.new # heap memory
35
+ @pc = Counter.new # program counter
36
+ end
37
+
38
+ def execute(instruction)
39
+ instruction.execute
40
+ rescue Halt
41
+ raise StopIteration
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,4 @@
1
+ require "pry-byebug"
2
+ require "whitespace"
3
+
4
+ require "minitest/autorun"
@@ -0,0 +1,113 @@
1
+ require "test_helper"
2
+
3
+ module Whitespace
4
+ describe Console do
5
+ before do
6
+ @stdin = Object.new
7
+ end
8
+
9
+ describe "#printc" do
10
+ describe "when given an ASCII character" do
11
+ it "prints it" do
12
+ expect { Console.new.printc 65 }.must_output("A")
13
+ end
14
+ end
15
+
16
+ describe "when not given an ASCII character" do
17
+ it "raises ArgumentError" do
18
+ e = expect { Console.new.printc 0 }.must_raise ArgumentError
19
+ expect(e.message).must_match /must be an ASCII character: 0/
20
+ end
21
+ end
22
+ end
23
+
24
+ describe "#printn" do
25
+ describe "when given an integer" do
26
+ it "prints it" do
27
+ expect { Console.new.printn 65 }.must_output("65")
28
+ end
29
+ end
30
+
31
+ describe "when not given an integer" do
32
+ it "raises ArgumentError" do
33
+ e = expect { Console.new.printn :x }.must_raise ArgumentError
34
+ expect(e.message).must_match /must be an integer/
35
+ end
36
+ end
37
+ end
38
+
39
+ describe "#getc" do
40
+ describe "when given an ASCII character" do
41
+ it "gets it" do
42
+ def @stdin.getc
43
+ "A"
44
+ end
45
+
46
+ expect(Console.new(stdin: @stdin).getc).must_equal "A"
47
+ end
48
+ end
49
+
50
+ describe "when not given an ASCII character" do
51
+ describe "when there is a character" do
52
+ it "raises ArgumentError" do
53
+ def @stdin.getc
54
+ "\a"
55
+ end
56
+
57
+ e = expect { Console.new(stdin: @stdin).getc }.must_raise ArgumentError
58
+ expect(e.message).must_match /must be an ASCII character/
59
+ end
60
+ end
61
+
62
+ describe "when EOF" do
63
+ it "raises ArgumentError" do
64
+ def @stdin.getc
65
+ end
66
+
67
+ e = expect { Console.new(stdin: @stdin).getc }.must_raise ArgumentError
68
+ expect(e.message).must_match /must be an ASCII character: EOF/
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ describe "#getn" do
75
+ describe "when given an integer" do
76
+ it "gets it" do
77
+ def @stdin.getc
78
+ @chars = ["1", "2", "3"]
79
+ @i ||= 0
80
+
81
+ @i += 1
82
+ @chars[@i - 1]
83
+ end
84
+
85
+ expect(Console.new(stdin: @stdin).getn).must_equal 123
86
+ end
87
+ end
88
+
89
+ describe "when not given an integer" do
90
+ describe "when there is at least one character" do
91
+ it "raises ArgumentError" do
92
+ def @stdin.getc
93
+ "\n"
94
+ end
95
+
96
+ e = expect { Console.new(stdin: @stdin).getn }.must_raise ArgumentError
97
+ expect(e.message).must_match /must be an integer: /
98
+ end
99
+ end
100
+
101
+ describe "when EOF" do
102
+ it "raises ArgumentError" do
103
+ def @stdin.getc
104
+ end
105
+
106
+ e = expect { Console.new(stdin: @stdin).getn }.must_raise ArgumentError
107
+ expect(e.message).must_match /must be an integer: EOF/
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,37 @@
1
+ require "test_helper"
2
+
3
+ module Whitespace
4
+ describe Counter do
5
+ before do
6
+ @counter = Counter.new
7
+ end
8
+
9
+ it "is initially 0" do
10
+ expect(@counter.to_int).must_equal 0
11
+ end
12
+
13
+ describe "#increment" do
14
+ it "increases its value by 1" do
15
+ @counter.increment
16
+
17
+ expect(@counter.to_int).must_equal 1
18
+ end
19
+ end
20
+
21
+ describe "#change_to" do
22
+ describe "when the value is non-negative" do
23
+ it "changes the counter's value to the given value" do
24
+ @counter.change_to 5
25
+
26
+ expect(@counter.to_int).must_equal 5
27
+ end
28
+ end
29
+
30
+ describe "when the value is negative" do
31
+ it "raises ArgumentError" do
32
+ expect { @counter.change_to -1 }.must_raise ArgumentError
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,25 @@
1
+ require "test_helper"
2
+
3
+ module Whitespace
4
+ describe Memory do
5
+ before do
6
+ @memory = Memory.new
7
+ end
8
+
9
+ describe "how values are accessed" do
10
+ describe "when the given address exists" do
11
+ it "returns the value" do
12
+ @memory[:address] = :value
13
+
14
+ expect(@memory[:address]).must_equal :value
15
+ end
16
+ end
17
+
18
+ describe "when the given address does not exist" do
19
+ it "raises Whitespace::AddressError" do
20
+ expect { @memory[:address] }.must_raise AddressError
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,63 @@
1
+ require "test_helper"
2
+
3
+ module Whitespace
4
+ describe Stack do
5
+ before do
6
+ @stack = Stack.new
7
+ end
8
+
9
+ describe "#push" do
10
+ it "places an element on top the stack" do
11
+ @stack.push :a
12
+ @stack.push :b
13
+
14
+ expect(@stack.top).must_equal :b
15
+ end
16
+ end
17
+
18
+ describe "#pop" do
19
+ describe "when the stack is empty" do
20
+ it "raises Whitespace::EmptyError" do
21
+ expect { @stack.pop }.must_raise EmptyError
22
+ end
23
+ end
24
+
25
+ describe "when the stack is not empty" do
26
+ it "removes and returns the top element" do
27
+ @stack.push :a
28
+ @stack.push :b
29
+
30
+ expect(@stack.pop).must_equal :b
31
+ expect(@stack.top).must_equal :a
32
+ end
33
+ end
34
+ end
35
+
36
+ describe "#top" do
37
+ describe "when the stack is empty" do
38
+ it "raises Whitespace::EmptyError" do
39
+ expect { @stack.top }.must_raise EmptyError
40
+ end
41
+ end
42
+
43
+ describe "when the stack is not empty" do
44
+ it "returns the top element" do
45
+ @stack.push :a
46
+ expect(@stack.top).must_equal :a
47
+ end
48
+ end
49
+ end
50
+
51
+ describe "#size" do
52
+ it "returns the number of elements on the stack" do
53
+ expect(@stack.size).must_equal 0
54
+
55
+ @stack.push :a
56
+ @stack.push :b
57
+ @stack.push :c
58
+
59
+ expect(@stack.size).must_equal 3
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,43 @@
1
+ require "test_helper"
2
+
3
+ module Whitespace::ISA
4
+ describe Add do
5
+ before do
6
+ @vm = Whitespace::VM.new
7
+ end
8
+
9
+ describe "#execute" do
10
+ describe "when the value stack is empty" do
11
+ it "raises Whitespace::EmptyError" do
12
+ expect(@vm.vstack.size).must_equal 0
13
+
14
+ expect { Add.new(@vm).execute }.must_raise Whitespace::EmptyError
15
+ end
16
+ end
17
+
18
+ describe "when the value stack has one element" do
19
+ it "raises Whitespace::EmptyError" do
20
+ @vm.vstack.push 1
21
+ expect(@vm.vstack.size).must_equal 1
22
+
23
+ expect { Add.new(@vm).execute }.must_raise Whitespace::EmptyError
24
+ end
25
+ end
26
+
27
+ describe "when the value stack has at least 2 elements" do
28
+ it "replaces the top 2 elements with their sum" do
29
+ @vm.vstack.push 1
30
+ @vm.vstack.push 2
31
+ @vm.vstack.push 3
32
+ expect(@vm.vstack.size).must_equal 3
33
+
34
+ Add.new(@vm).execute
35
+
36
+ expect(@vm.vstack.pop).must_equal 5
37
+ expect(@vm.vstack.pop).must_equal 1
38
+ expect(@vm.vstack.size).must_equal 0
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,52 @@
1
+ require "test_helper"
2
+
3
+ module Whitespace::ISA
4
+ describe Div do
5
+ before do
6
+ @vm = Whitespace::VM.new
7
+ end
8
+
9
+ describe "#execute" do
10
+ describe "when the value stack is empty" do
11
+ it "raises Whitespace::EmptyError" do
12
+ expect(@vm.vstack.size).must_equal 0
13
+
14
+ expect { Div.new(@vm).execute }.must_raise Whitespace::EmptyError
15
+ end
16
+ end
17
+
18
+ describe "when the value stack has one element" do
19
+ it "raises Whitespace::EmptyError" do
20
+ @vm.vstack.push 1
21
+ expect(@vm.vstack.size).must_equal 1
22
+
23
+ expect { Div.new(@vm).execute }.must_raise Whitespace::EmptyError
24
+ end
25
+ end
26
+
27
+ describe "when the value stack has at least 2 elements" do
28
+ it "replaces the top 2 elements with their quotient" do
29
+ @vm.vstack.push 1
30
+ @vm.vstack.push 6
31
+ @vm.vstack.push 2
32
+ expect(@vm.vstack.size).must_equal 3
33
+
34
+ Div.new(@vm).execute
35
+
36
+ expect(@vm.vstack.pop).must_equal 3
37
+ expect(@vm.vstack.pop).must_equal 1
38
+ expect(@vm.vstack.size).must_equal 0
39
+ end
40
+ end
41
+
42
+ describe "division by 0" do
43
+ it "raises ZeroDivisionError" do
44
+ @vm.vstack.push 1
45
+ @vm.vstack.push 0
46
+
47
+ expect { Div.new(@vm).execute }.must_raise ZeroDivisionError
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,52 @@
1
+ require "test_helper"
2
+
3
+ module Whitespace::ISA
4
+ describe Mod do
5
+ before do
6
+ @vm = Whitespace::VM.new
7
+ end
8
+
9
+ describe "#execute" do
10
+ describe "when the value stack is empty" do
11
+ it "raises Whitespace::EmptyError" do
12
+ expect(@vm.vstack.size).must_equal 0
13
+
14
+ expect { Mod.new(@vm).execute }.must_raise Whitespace::EmptyError
15
+ end
16
+ end
17
+
18
+ describe "when the value stack has one element" do
19
+ it "raises Whitespace::EmptyError" do
20
+ @vm.vstack.push 1
21
+ expect(@vm.vstack.size).must_equal 1
22
+
23
+ expect { Mod.new(@vm).execute }.must_raise Whitespace::EmptyError
24
+ end
25
+ end
26
+
27
+ describe "when the value stack has at least 2 elements" do
28
+ it "replaces the top 2 elements with their remainder" do
29
+ @vm.vstack.push 1
30
+ @vm.vstack.push 5
31
+ @vm.vstack.push 3
32
+ expect(@vm.vstack.size).must_equal 3
33
+
34
+ Mod.new(@vm).execute
35
+
36
+ expect(@vm.vstack.pop).must_equal 2
37
+ expect(@vm.vstack.pop).must_equal 1
38
+ expect(@vm.vstack.size).must_equal 0
39
+ end
40
+ end
41
+
42
+ describe "modulo 0" do
43
+ it "raises ZeroDivisionError" do
44
+ @vm.vstack.push 1
45
+ @vm.vstack.push 0
46
+
47
+ expect { Mod.new(@vm).execute }.must_raise ZeroDivisionError
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end