hackasm 0.1.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 (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +7 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +6 -0
  7. data/Gemfile.lock +139 -0
  8. data/README.md +40 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/exe/hackasm +18 -0
  13. data/hackasm.gemspec +64 -0
  14. data/lib/hackasm.rb +6 -0
  15. data/lib/hackasm/assembler/constants/dests.rb +1 -0
  16. data/lib/hackasm/assembler/constants/jumps.rb +1 -0
  17. data/lib/hackasm/assembler/hack_parser.rb +50 -0
  18. data/lib/hackasm/assembler/instructions/address_instruction.rb +18 -0
  19. data/lib/hackasm/assembler/instructions/command_instruction.rb +76 -0
  20. data/lib/hackasm/assembler/symbol_table.rb +53 -0
  21. data/lib/hackasm/assembler/translator.rb +51 -0
  22. data/lib/hackasm/cli.rb +45 -0
  23. data/lib/hackasm/command.rb +121 -0
  24. data/lib/hackasm/commands/.gitkeep +1 -0
  25. data/lib/hackasm/commands/asm2binary.rb +26 -0
  26. data/lib/hackasm/commands/vm2asm.rb +36 -0
  27. data/lib/hackasm/templates/.gitkeep +1 -0
  28. data/lib/hackasm/templates/assemble/.gitkeep +1 -0
  29. data/lib/hackasm/templates/vm2asm/.gitkeep +1 -0
  30. data/lib/hackasm/version.rb +3 -0
  31. data/lib/hackasm/vm/instructions/arithmetic_instruction.rb +30 -0
  32. data/lib/hackasm/vm/instructions/arithmetic_operations/binary_operation.rb +47 -0
  33. data/lib/hackasm/vm/instructions/arithmetic_operations/comparison.rb +50 -0
  34. data/lib/hackasm/vm/instructions/arithmetic_operations/unary_operation.rb +34 -0
  35. data/lib/hackasm/vm/instructions/memory_access_instruction.rb +36 -0
  36. data/lib/hackasm/vm/instructions/memory_access_operations/constant_operation.rb +30 -0
  37. data/lib/hackasm/vm/instructions/memory_access_operations/fixed_segment_operation.rb +62 -0
  38. data/lib/hackasm/vm/instructions/memory_access_operations/static_operation.rb +43 -0
  39. data/lib/hackasm/vm/instructions/memory_access_operations/virtual_segment_operation.rb +65 -0
  40. data/lib/hackasm/vm/translator.rb +46 -0
  41. data/lib/hackasm/vm/vm_code_parser.rb +39 -0
  42. metadata +467 -0
@@ -0,0 +1 @@
1
+ #
@@ -0,0 +1,3 @@
1
+ module Hackasm
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,30 @@
1
+ require_relative './arithmetic_operations/comparison'
2
+ require_relative './arithmetic_operations/binary_operation'
3
+ require_relative './arithmetic_operations/unary_operation'
4
+
5
+ module Vm
6
+ module Instructions
7
+ class ArithmeticInstruction
8
+ attr_reader :operation
9
+
10
+ def initialize(instruction)
11
+ @operation = instruction[:operation].to_s
12
+ end
13
+
14
+ def to_asm
15
+ comment + case operation
16
+ when *ArithmeticOperations::Comparison.operations
17
+ ArithmeticOperations::Comparison.new(operation).to_asm
18
+ when *ArithmeticOperations::UnaryOperation.operations
19
+ ArithmeticOperations::UnaryOperation.new(operation).to_asm
20
+ when *ArithmeticOperations::BinaryOperation.operations
21
+ ArithmeticOperations::BinaryOperation.new(operation).to_asm
22
+ end.split("\n").map(&:strip).join("\n")
23
+ end
24
+
25
+ def comment
26
+ "// #{operation}\n"
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,47 @@
1
+ module Vm
2
+ module Instructions
3
+ module ArithmeticOperations
4
+ class BinaryOperation
5
+ OPERATION_TO_INSTRUCTION = {
6
+ "add" => "M=M+D",
7
+ "sub" => "M=M-D",
8
+ "and" => "M=M&D",
9
+ "or" => "M=M|D",
10
+ }.freeze
11
+
12
+ attr_reader :operation
13
+
14
+ def initialize(operation)
15
+ @operation = operation
16
+ end
17
+
18
+ def to_asm
19
+ %Q{
20
+ @SP
21
+ M=M-1
22
+ @SP
23
+ A=M
24
+ D=M
25
+ @SP
26
+ M=M-1
27
+ @SP
28
+ A=M
29
+ #{instruction}
30
+ @SP
31
+ M=M+1
32
+ }.strip
33
+ end
34
+
35
+ def self.operations
36
+ OPERATION_TO_INSTRUCTION.keys
37
+ end
38
+
39
+ private
40
+
41
+ def instruction
42
+ OPERATION_TO_INSTRUCTION[operation]
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,50 @@
1
+ require 'securerandom'
2
+
3
+ module Vm
4
+ module Instructions
5
+ module ArithmeticOperations
6
+ class Comparison
7
+ attr_reader :operation
8
+
9
+ def initialize(operation)
10
+ @operation = operation
11
+ end
12
+
13
+ def to_asm
14
+ if_label = 'c_if_' + SecureRandom.hex(10)
15
+ else_label = 'c_else_' + SecureRandom.hex(10)
16
+ %Q{
17
+ @SP
18
+ M=M-1
19
+ @SP
20
+ A=M
21
+ D=M
22
+ @SP
23
+ M=M-1
24
+ @SP
25
+ A=M
26
+ D=M-D
27
+ @#{if_label}
28
+ D;J#{operation.upcase}
29
+ D=0
30
+ @#{else_label}
31
+ 0;JEQ
32
+ (#{if_label})
33
+ D=-1
34
+ (#{else_label})
35
+ @SP
36
+ A=M
37
+ M=D
38
+ @SP
39
+ M=M+1
40
+ }.strip
41
+ end
42
+
43
+ def self.operations
44
+ %w[gt lt eq]
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+
@@ -0,0 +1,34 @@
1
+ module Vm
2
+ module Instructions
3
+ module ArithmeticOperations
4
+ class UnaryOperation
5
+ OPERATION_TO_INSTRUCTION = {
6
+ "neg" => "M=-M",
7
+ "not" => "M=!M"
8
+ }.freeze
9
+
10
+ attr_reader :operation
11
+
12
+ def initialize(operation)
13
+ @operation = operation
14
+ end
15
+
16
+ def to_asm
17
+ %Q{
18
+ @SP
19
+ M=M-1
20
+ @SP
21
+ A=M
22
+ #{OPERATION_TO_INSTRUCTION[operation]}
23
+ @SP
24
+ M=M+1
25
+ }.strip
26
+ end
27
+
28
+ def self.operations
29
+ OPERATION_TO_INSTRUCTION.keys
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,36 @@
1
+ require_relative './memory_access_operations/static_operation'
2
+ require_relative './memory_access_operations/constant_operation'
3
+ require_relative './memory_access_operations/fixed_segment_operation'
4
+ require_relative './memory_access_operations/virtual_segment_operation.rb'
5
+
6
+ module Vm
7
+ module Instructions
8
+ class MemoryAccessInstruction
9
+ attr_reader :segment, :operation, :index, :object_name
10
+
11
+ def initialize(instruction, object_name)
12
+ @segment = instruction[:segment].to_s
13
+ @operation = instruction[:operation].to_s
14
+ @index = instruction[:index].to_s
15
+ @object_name = object_name
16
+ end
17
+
18
+ def to_asm
19
+ comment + case segment
20
+ when *MemoryAccessOperations::ConstantOperation.segments
21
+ MemoryAccessOperations::ConstantOperation.new(index).to_asm
22
+ when *MemoryAccessOperations::StaticOperation.segments
23
+ MemoryAccessOperations::StaticOperation.new(index, object_name).to_asm
24
+ when *MemoryAccessOperations::VirtualSegmentOperation.segments
25
+ MemoryAccessOperations::VirtualSegmentOperation.new(operation, segment, index).to_asm
26
+ when *MemoryAccessOperations::FixedSegmentOperation.segments
27
+ MemoryAccessOperations::FixedSegmentOperation.new(operation, segment, index).to_asm
28
+ end.split("\n").map(&:strip).join("\n")
29
+ end
30
+
31
+ def comment
32
+ "// #{operation} #{segment} #{index}\n"
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,30 @@
1
+ module Vm
2
+ module Instructions
3
+ module MemoryAccessOperations
4
+ class ConstantOperation
5
+
6
+ attr_reader :index
7
+
8
+ def initialize(index)
9
+ @index = index
10
+ end
11
+
12
+ def to_asm
13
+ %Q{
14
+ @#{index}
15
+ D=A
16
+ @SP
17
+ A=M
18
+ M=D
19
+ @SP
20
+ M=M+1
21
+ }.strip
22
+ end
23
+
24
+ def self.segments
25
+ %w[constant]
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,62 @@
1
+ module Vm
2
+ module Instructions
3
+ module MemoryAccessOperations
4
+ class FixedSegmentOperation
5
+ FIXED_SEGMENT_TO_START_INDEX = {
6
+ 'temp' => '5',
7
+ 'pointer' => '3',
8
+ }.freeze
9
+
10
+ attr_reader :operation, :segment, :index
11
+
12
+ def initialize(operation, segment, index)
13
+ @operation = operation
14
+ @segment = segment
15
+ @index = index
16
+ end
17
+
18
+ def to_asm
19
+ if operation == "push"
20
+ %Q{
21
+ @#{index}
22
+ D=A
23
+ @#{FIXED_SEGMENT_TO_START_INDEX[segment]}
24
+ A=A+D
25
+ D=M
26
+ @SP
27
+ A=M
28
+ M=D
29
+ @SP
30
+ M=M+1
31
+ }.strip
32
+ else
33
+ %Q{
34
+ @SP
35
+ M=M-1
36
+ @SP
37
+ A=M
38
+ D=M
39
+ @R14
40
+ M=D
41
+ @#{index}
42
+ D=A
43
+ @#{FIXED_SEGMENT_TO_START_INDEX[segment]}
44
+ D=A+D
45
+ @R15
46
+ M=D
47
+ @R14
48
+ D=M
49
+ @R15
50
+ A=M
51
+ M=D
52
+ }.strip
53
+ end
54
+ end
55
+
56
+ def self.segments
57
+ FIXED_SEGMENT_TO_START_INDEX.keys
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,43 @@
1
+ module Vm
2
+ module Instructions
3
+ module MemoryAccessOperations
4
+ class StaticOperation
5
+
6
+ attr_reader :object_name, :index
7
+
8
+ def initialize(object_name, index)
9
+ @index = index
10
+ @object_name = object_name
11
+ end
12
+
13
+ def to_asm
14
+ if operation == "push"
15
+ %Q{
16
+ @#{object_name}.#{index}
17
+ D=M
18
+ @SP
19
+ A=M
20
+ M=D
21
+ @SP
22
+ M=M+1
23
+ }.strip
24
+ else
25
+ %Q{
26
+ @SP
27
+ M=M-1
28
+ @SP
29
+ A=M
30
+ D=M
31
+ @#{object_name}.#{index}
32
+ M=D
33
+ }.strip
34
+ end
35
+ end
36
+
37
+ def self.segments
38
+ %w[static]
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,65 @@
1
+ module Vm
2
+ module Instructions
3
+ module MemoryAccessOperations
4
+ class VirtualSegmentOperation
5
+ VIRTUAL_SEGMENT_TO_POINTER_NAME = {
6
+ 'argument' => 'ARG',
7
+ 'local' => 'LCL',
8
+ 'this' => 'THIS',
9
+ 'that' => 'THAT',
10
+ }.freeze
11
+
12
+
13
+ attr_reader :operation, :segment, :index
14
+
15
+ def initialize(operation, segment, index)
16
+ @operation = operation
17
+ @segment = segment
18
+ @index = index
19
+ end
20
+
21
+ def to_asm
22
+ if operation == "push"
23
+ %Q{
24
+ @#{index}
25
+ D=A
26
+ @#{VIRTUAL_SEGMENT_TO_POINTER_NAME[segment]}
27
+ A=M+D
28
+ D=M
29
+ @SP
30
+ A=M
31
+ M=D
32
+ @SP
33
+ M=M+1
34
+ }.strip
35
+ else
36
+ %Q{
37
+ @SP
38
+ M=M-1
39
+ @SP
40
+ A=M
41
+ D=M
42
+ @R14
43
+ M=D
44
+ @#{index}
45
+ D=A
46
+ @#{VIRTUAL_SEGMENT_TO_POINTER_NAME[segment]}
47
+ D=M+D
48
+ @R15
49
+ M=D
50
+ @R14
51
+ D=M
52
+ @R15
53
+ A=M
54
+ M=D
55
+ }.strip
56
+ end
57
+ end
58
+
59
+ def self.segments
60
+ VIRTUAL_SEGMENT_TO_POINTER_NAME.keys
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,46 @@
1
+ require_relative "./vm_code_parser"
2
+ require_relative "./instructions/arithmetic_instruction"
3
+ require_relative "./instructions/memory_access_instruction"
4
+
5
+ module Vm
6
+ class Translator
7
+ def initialize(text, object_name)
8
+ @text = text
9
+ @object_name = object_name
10
+ end
11
+
12
+ def translate
13
+ expressions = parser.parse(@text)
14
+ expressions.map do |expression|
15
+ process_instruction(expression)
16
+ end.compact.join("\n") << "\n"
17
+ rescue Parslet::ParseFailed => failure
18
+ failure.parse_failure_cause.ascii_tree
19
+ end
20
+
21
+ private
22
+
23
+ def process_instruction(instruction)
24
+ instruction_name = instruction.keys.first
25
+ instruction_body = instruction.values.first
26
+
27
+ case instruction_name
28
+ when :arithmetic_instruction
29
+ Instructions::ArithmeticInstruction.new(instruction_body).to_asm
30
+ when :memory_access_instruction
31
+ Instructions::MemoryAccessInstruction.new(instruction_body, @object_name).to_asm
32
+ when :jump_label
33
+ nil
34
+ else raise "Unknown instruction!"
35
+ end
36
+ end
37
+
38
+ def parser
39
+ @parser ||= VmCodeParser.new
40
+ end
41
+
42
+ def expressions
43
+ @expressions ||= parser.parse(@text)
44
+ end
45
+ end
46
+ end