whitespace-ruby 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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