hackasm 0.1.0

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