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