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.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +139 -0
- data/README.md +40 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/hackasm +18 -0
- data/hackasm.gemspec +64 -0
- data/lib/hackasm.rb +6 -0
- data/lib/hackasm/assembler/constants/dests.rb +1 -0
- data/lib/hackasm/assembler/constants/jumps.rb +1 -0
- data/lib/hackasm/assembler/hack_parser.rb +50 -0
- data/lib/hackasm/assembler/instructions/address_instruction.rb +18 -0
- data/lib/hackasm/assembler/instructions/command_instruction.rb +76 -0
- data/lib/hackasm/assembler/symbol_table.rb +53 -0
- data/lib/hackasm/assembler/translator.rb +51 -0
- data/lib/hackasm/cli.rb +45 -0
- data/lib/hackasm/command.rb +121 -0
- data/lib/hackasm/commands/.gitkeep +1 -0
- data/lib/hackasm/commands/asm2binary.rb +26 -0
- data/lib/hackasm/commands/vm2asm.rb +36 -0
- data/lib/hackasm/templates/.gitkeep +1 -0
- data/lib/hackasm/templates/assemble/.gitkeep +1 -0
- data/lib/hackasm/templates/vm2asm/.gitkeep +1 -0
- data/lib/hackasm/version.rb +3 -0
- data/lib/hackasm/vm/instructions/arithmetic_instruction.rb +30 -0
- data/lib/hackasm/vm/instructions/arithmetic_operations/binary_operation.rb +47 -0
- data/lib/hackasm/vm/instructions/arithmetic_operations/comparison.rb +50 -0
- data/lib/hackasm/vm/instructions/arithmetic_operations/unary_operation.rb +34 -0
- data/lib/hackasm/vm/instructions/memory_access_instruction.rb +36 -0
- data/lib/hackasm/vm/instructions/memory_access_operations/constant_operation.rb +30 -0
- data/lib/hackasm/vm/instructions/memory_access_operations/fixed_segment_operation.rb +62 -0
- data/lib/hackasm/vm/instructions/memory_access_operations/static_operation.rb +43 -0
- data/lib/hackasm/vm/instructions/memory_access_operations/virtual_segment_operation.rb +65 -0
- data/lib/hackasm/vm/translator.rb +46 -0
- data/lib/hackasm/vm/vm_code_parser.rb +39 -0
- metadata +467 -0
@@ -0,0 +1 @@
|
|
1
|
+
#
|
@@ -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
|