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,14 @@
1
+ module Whitespace
2
+ class Error < StandardError; end
3
+ class EmptyError < Error; end
4
+ class AddressError < Error; end
5
+ class LabelError < Error; end
6
+ class Halt < Error; end
7
+ class ParseError < Error; end
8
+ end
9
+
10
+ require_relative "whitespace/version"
11
+ require_relative "whitespace/util"
12
+ require_relative "whitespace/vm"
13
+ require_relative "whitespace/isa"
14
+ require_relative "whitespace/parser"
@@ -0,0 +1,56 @@
1
+ module Whitespace
2
+ class Console
3
+ attr_reader :stdin, :stdout
4
+
5
+ def initialize(stdin: $stdin, stdout: $stdout)
6
+ @stdin = stdin
7
+ @stdout = stdout
8
+ end
9
+
10
+ def printc(n)
11
+ unless Util.is_ascii?(n)
12
+ raise ArgumentError, "must be an ASCII character: #{n}"
13
+ end
14
+ stdout.print n.chr
15
+ end
16
+
17
+ def printn(n)
18
+ unless Util.is_integer?(n)
19
+ raise ArgumentError, "must be an integer: #{n}"
20
+ end
21
+ stdout.print n
22
+ end
23
+
24
+ def getc
25
+ if c = stdin.getc
26
+ unless Util.is_ascii?(c.ord)
27
+ raise ArgumentError, "must be an ASCII character: #{c}"
28
+ end
29
+ c
30
+ else
31
+ raise ArgumentError, "must be an ASCII character: EOF"
32
+ end
33
+ end
34
+
35
+ LINE_SEPARATOR = "\n"
36
+
37
+ def getn
38
+ input = ""
39
+ loop do
40
+ c = stdin.getc
41
+ break if c.nil?
42
+ input << c
43
+ break if c == LINE_SEPARATOR
44
+ end
45
+
46
+ raise ArgumentError, "must be an integer: EOF" if input.empty?
47
+
48
+ input = input.chomp(LINE_SEPARATOR)
49
+ begin
50
+ Integer input
51
+ rescue
52
+ raise ArgumentError, "must be an integer: #{input}"
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,24 @@
1
+ module Whitespace
2
+ class Counter
3
+ def initialize
4
+ @value = 0
5
+ end
6
+
7
+ def increment
8
+ @value += 1
9
+ end
10
+
11
+ def change_to(new_value)
12
+ new_value = new_value.to_i
13
+ if new_value >= 0
14
+ @value = new_value
15
+ else
16
+ raise ArgumentError, "must be non-negative: #{new_value}"
17
+ end
18
+ end
19
+
20
+ def to_int
21
+ @value
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ module Whitespace
2
+ class Memory
3
+ def initialize
4
+ @store = {}
5
+ end
6
+
7
+ def [](address)
8
+ if @store.key?(address)
9
+ @store[address]
10
+ else
11
+ raise AddressError, "no such address exists: #{address}"
12
+ end
13
+ end
14
+
15
+ def []=(address, value)
16
+ @store[address] = value
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,25 @@
1
+ module Whitespace
2
+ class Stack
3
+ def initialize
4
+ @elements = []
5
+ end
6
+
7
+ def push(x)
8
+ @elements.push x
9
+ end
10
+
11
+ def pop
12
+ return @elements.pop unless @elements.empty?
13
+ raise EmptyError
14
+ end
15
+
16
+ def top
17
+ return @elements.last unless @elements.empty?
18
+ raise EmptyError
19
+ end
20
+
21
+ def size
22
+ @elements.size
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,9 @@
1
+ require_relative "binop"
2
+
3
+ module Whitespace::ISA
4
+ class Add < Binop
5
+ def initialize(vm)
6
+ super(vm, :add)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,18 @@
1
+ module Whitespace::ISA
2
+ class Binop < Instruction
3
+ def initialize(vm, op)
4
+ unless Whitespace::Util.is_binop?(op)
5
+ raise ArgumentError, "must be a binary operator: #{op}"
6
+ end
7
+ super(vm)
8
+ @op = op
9
+ end
10
+
11
+ def execute
12
+ right = vm.vstack.pop
13
+ left = vm.vstack.pop
14
+
15
+ vm.vstack.push Whitespace::Util::BINOPS[@op].call(left, right)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,9 @@
1
+ require_relative "binop"
2
+
3
+ module Whitespace::ISA
4
+ class Div < Binop
5
+ def initialize(vm)
6
+ super(vm, :div)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require_relative "binop"
2
+
3
+ module Whitespace::ISA
4
+ class Mod < Binop
5
+ def initialize(vm)
6
+ super(vm, :mod)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require_relative "binop"
2
+
3
+ module Whitespace::ISA
4
+ class Mul < Binop
5
+ def initialize(vm)
6
+ super(vm, :mul)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require_relative "binop"
2
+
3
+ module Whitespace::ISA
4
+ class Sub < Binop
5
+ def initialize(vm)
6
+ super(vm, :sub)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ module Whitespace::ISA
2
+ class Call < Instruction
3
+ attr_reader :name
4
+
5
+ def initialize(vm, name)
6
+ unless Whitespace::Util.is_label?(name)
7
+ raise ArgumentError, "must be a label: #{name}"
8
+ end
9
+ super(vm)
10
+ @name = name
11
+ end
12
+
13
+ def execute
14
+ index = Whitespace::Util.find_label(vm.instructions, name)
15
+ vm.cstack.push vm.pc.to_int
16
+ vm.pc.change_to index + 1
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,7 @@
1
+ module Whitespace::ISA
2
+ class End < Instruction
3
+ def execute
4
+ raise Whitespace::Halt
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,16 @@
1
+ module Whitespace::ISA
2
+ class Label < Instruction
3
+ attr_reader :name
4
+
5
+ def initialize(vm, name)
6
+ unless Whitespace::Util.is_label?(name)
7
+ raise ArgumentError, "must be a label: #{name}"
8
+ end
9
+ super(vm)
10
+ @name = name
11
+ end
12
+
13
+ def execute
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ module Whitespace::ISA
2
+ class Njmp < Instruction
3
+ attr_reader :name
4
+
5
+ def initialize(vm, name)
6
+ unless Whitespace::Util.is_label?(name)
7
+ raise ArgumentError, "must be a label: #{name}"
8
+ end
9
+ super(vm)
10
+ @name = name
11
+ end
12
+
13
+ def execute
14
+ if vm.vstack.pop < 0
15
+ index = Whitespace::Util.find_label(vm.instructions, name)
16
+ vm.pc.change_to index + 1
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,7 @@
1
+ module Whitespace::ISA
2
+ class Return < Instruction
3
+ def execute
4
+ vm.pc.change_to vm.cstack.pop
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,18 @@
1
+ module Whitespace::ISA
2
+ class Ujmp < Instruction
3
+ attr_reader :name
4
+
5
+ def initialize(vm, name)
6
+ unless Whitespace::Util.is_label?(name)
7
+ raise ArgumentError, "must be a label: #{name}"
8
+ end
9
+ super(vm)
10
+ @name = name
11
+ end
12
+
13
+ def execute
14
+ index = Whitespace::Util.find_label(vm.instructions, name)
15
+ vm.pc.change_to index + 1
16
+ end
17
+ end
18
+ end