hack_assembler 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2c382e0c2ca7ae32e05aec74da6e0ab061d6fdbf
4
- data.tar.gz: 022d493d48819e560fd0ad763395d5a677718dbf
3
+ metadata.gz: 16849ad49ea5724294483935e3bd416de48576fa
4
+ data.tar.gz: eb36db703661f2acc60e2c8e7781581e6998b4fb
5
5
  SHA512:
6
- metadata.gz: 125e257a54f2695db744394ac9f9e8f474beb43690a48583177a080a1f77305adb223507d2bbe0cbd8c1dd0fca8e7562c8c25faebfbb9659e796f3c0c11b213b
7
- data.tar.gz: b94dfaf5371f68bfb91c74556e8de03cb556db95ee2ef4341a82b9381fc8414106ca83c754e09f8957950297d9546288a2f5ce8dcd99b694e2de258843108680
6
+ metadata.gz: 92a36fcda84358674b2c0699bf548639604312bf7a874da237d3fb7203cb4c5c71dbd04e1870915c226861cb61349bfcc0744b9f485eb0b73ef71ddcab4a9e61
7
+ data.tar.gz: 93822f4774998d3d4ba30ed8b0aa7df089ebedd4573f7a988845d96f2ede6492615e695aac88d6f3b3f30bd490f2e937781f9ebfc5d7fe737e82a288859f191f
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Build Status](https://travis-ci.org/Fuffi/hack2asm.svg?branch=master)](https://travis-ci.org/Fuffi/hack_assembler)
1
+ [![Build Status](https://travis-ci.org/Fuffi/hack-assembler.svg?branch=master)](https://travis-ci.org/Fuffi/hack-assembler)
2
2
 
3
3
  # Hack Assembler
4
4
 
@@ -21,8 +21,16 @@ Or install it yourself as:
21
21
  ## Usage
22
22
 
23
23
  After installing the Gem you can run this command from your terminal:
24
+
24
25
  hack_assembler <input-file.asm> <output-file.hack>
25
26
 
27
+ ## TO DOs
28
+
29
+ * Refactor code and tests
30
+ * Add better error reporting
31
+ * Make sure any valid symbol name is recognized
32
+
33
+
26
34
  ## Contributing
27
35
 
28
36
  1. Fork it
File without changes
@@ -6,13 +6,18 @@ require_relative 'hack_assembler/destination_translator'
6
6
  require_relative 'hack_assembler/jump_translator'
7
7
  require_relative 'hack_assembler/c_instruction'
8
8
  require_relative 'hack_assembler/assembler'
9
+ require_relative 'hack_assembler/symbol_table'
9
10
 
10
11
  module HackAssembler
11
12
  def self.translate_file(input_file, output_file)
12
13
  source_file = File.open(input_file)
13
14
  source_code = source_file.read
14
15
 
15
- machine_code = Assembler.translate(source_code)
16
+ symbol_table = SymbolTable.new
17
+
18
+ label_less_code = Assembler.scan_and_remove_labels(source_code, symbol_table)
19
+ processed_source = Assembler.process_symbols(label_less_code, symbol_table)
20
+ machine_code = Assembler.translate(processed_source)
16
21
 
17
22
  File.open(output_file, 'w') { |file| file.write(machine_code); file.close }
18
23
 
@@ -1,13 +1,56 @@
1
1
  module HackAssembler
2
2
  module Assembler
3
+ def self.scan_and_remove_labels(source_code, symbol_table)
4
+ machine_code_line_number = 0
5
+ label_less_code = ''
6
+ source_code.each_line do |line|
7
+ if is_empty_line?(line) || is_comment_line?(line)
8
+ label_less_code << line
9
+ next
10
+ end
11
+
12
+ clean_line = line.strip
13
+
14
+ match = /\((.*)\)/.match(clean_line)
15
+ if match
16
+ label = match[1]
17
+
18
+ symbol_table.add_label_address(label, machine_code_line_number)
19
+ label_less_code << "\n"
20
+ else
21
+ machine_code_line_number += 1
22
+ label_less_code << line
23
+ end
24
+ end
25
+
26
+ label_less_code
27
+ end
28
+
29
+ def self.process_symbols(source_code, symbol_table)
30
+ processed_code = ''
31
+ source_code.each_line do |line|
32
+ clean_line = line.strip
33
+
34
+ match = /@([^0-9].*)/.match(clean_line)
35
+ if match
36
+ symbol = match[1]
37
+
38
+ address = symbol_table.get_address(symbol)
39
+ line = "@#{address}\n"
40
+ end
41
+
42
+ processed_code << line
43
+ end
44
+
45
+ processed_code
46
+ end
47
+
3
48
  def self.translate(source_code)
4
49
  machine_code = ''
5
50
 
6
- source_code_extract = source_code.gsub(/^[\s]*$\n/, '')
51
+ source_code.each_line do |line|
52
+ next if is_empty_line?(line) || is_comment_line?(line)
7
53
 
8
- source_code_extract.each_line do |line|
9
- next if line.start_with? '//'
10
-
11
54
  clean_line = line.strip
12
55
 
13
56
  instruction = clean_line.start_with?('@') ? AInstruction : CInstruction
@@ -17,5 +60,18 @@ module HackAssembler
17
60
 
18
61
  machine_code
19
62
  end
63
+
64
+ private
65
+ def self.is_empty_line?(line)
66
+ line =~ /^[\s]*$\n/
67
+ end
68
+
69
+ def self.is_comment_line?(line)
70
+ line.start_with? '//'
71
+ end
72
+
73
+ def self.is_label_line?(line)
74
+ line =~ /\(.*\)/
75
+ end
20
76
  end
21
77
  end
@@ -0,0 +1,48 @@
1
+ module HackAssembler
2
+ class SymbolTable
3
+ def initialize
4
+ @num_variable_symbols = 0
5
+
6
+ @symbol_table = {
7
+ 'R0' => '0',
8
+ 'R1' => '1',
9
+ 'R2' => '2',
10
+ 'R3' => '3',
11
+ 'R4' => '4',
12
+ 'R5' => '5',
13
+ 'R6' => '6',
14
+ 'R7' => '7',
15
+ 'R8' => '8',
16
+ 'R9' => '9',
17
+ 'R10' => '10',
18
+ 'R11' => '11',
19
+ 'R12' => '12',
20
+ 'R13' => '13',
21
+ 'R14' => '14',
22
+ 'R15' => '15',
23
+ 'SCREEN' => '16384',
24
+ 'KBD' => '24576',
25
+ 'SP' => '0',
26
+ 'LCL' => '1',
27
+ 'ARG' => '2',
28
+ 'THIS' => '3',
29
+ 'THAT' => '4'
30
+ }
31
+ end
32
+
33
+ def get_address(symbol)
34
+ address = @symbol_table[symbol]
35
+ if address.nil?
36
+ address = (16 + @num_variable_symbols).to_s
37
+ @symbol_table[symbol] = address
38
+ @num_variable_symbols += 1
39
+ end
40
+
41
+ address
42
+ end
43
+
44
+ def add_label_address(symbol, address)
45
+ @symbol_table[symbol] = address.to_s
46
+ end
47
+ end
48
+ end
@@ -1,3 +1,3 @@
1
1
  module HackAssembler
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -1,27 +1,72 @@
1
- require 'test_helper'
2
-
3
- class AssemblerTest < Minitest::Test
4
- def test_translates_a_and_c_instructions
5
- source_code = "@4\n" << "0;JMP\n"
6
- bytecode = Assembler.translate(source_code)
7
- assert_equal "0000000000000100\n" << "1110101010000111\n", bytecode
8
- end
9
-
10
- def test_ignores_comments
11
- source_code = "// A comment\n" << "@4\n" << "// Another comment\n"
12
- bytecode = Assembler.translate(source_code)
13
- assert_equal "0000000000000100\n", bytecode
14
- end
15
-
16
- def test_ignores_empty_lines
17
- source_code = "\n@4\n\n"
18
- bytecode = Assembler.translate(source_code)
19
- assert_equal "0000000000000100\n", bytecode
20
- end
21
-
22
- def test_ignores_indentation_and_trailing_spaces
23
- source_code = " @4 \n"
24
- bytecode = Assembler.translate(source_code)
25
- assert_equal "0000000000000100\n", bytecode
26
- end
27
- end
1
+ require 'test_helper'
2
+
3
+ class AssemblerTest < Minitest::Test
4
+ def test_translates_a_and_c_instructions
5
+ source_code = "@4\n" << "0;JMP\n"
6
+ bytecode = Assembler.translate(source_code)
7
+ assert_equal "0000000000000100\n" << "1110101010000111\n", bytecode
8
+ end
9
+
10
+ def test_ignores_comments
11
+ source_code = "// A comment\n" << "@4\n" << "// Another comment\n"
12
+ bytecode = Assembler.translate(source_code)
13
+ assert_equal "0000000000000100\n", bytecode
14
+ end
15
+
16
+ def test_ignores_empty_lines
17
+ source_code = "\n@4\n\n"
18
+ bytecode = Assembler.translate(source_code)
19
+ assert_equal "0000000000000100\n", bytecode
20
+ end
21
+
22
+ def test_ignores_indentation_and_trailing_spaces
23
+ source_code = " @4 \n"
24
+ bytecode = Assembler.translate(source_code)
25
+ assert_equal "0000000000000100\n", bytecode
26
+ end
27
+
28
+ def test_that_scan_and_remove_labels_adds_labels_to_symbol_table
29
+ symbol_table = SymbolTable.new
30
+
31
+ source_code = "(LOOP)\n" << "@LOOP\n" << " \n" << "(END_LOOP)\n" << "@END_LOOP\n"
32
+ Assembler.scan_and_remove_labels(source_code, symbol_table)
33
+
34
+ loop_address = symbol_table.get_address('LOOP')
35
+ end_address = symbol_table.get_address('END_LOOP')
36
+
37
+ assert_equal '0', loop_address
38
+ assert_equal '1', end_address
39
+ end
40
+
41
+ def test_that_it_processes_built_in_symbols
42
+ symbol_table = SymbolTable.new
43
+
44
+ source_code = "@R0\n" << "@R15\n"
45
+ processed_code = Assembler.process_symbols(source_code, symbol_table)
46
+
47
+ assert_equal "@0\n" << "@15\n", processed_code
48
+ end
49
+
50
+ def test_that_it_does_not_change_symbols
51
+ symbol_table = SymbolTable.new
52
+
53
+ source_code = "@256\n" << "@0\n"
54
+ processed_code = Assembler.process_symbols(source_code, symbol_table)
55
+
56
+ assert_equal "@256\n" << "@0\n", processed_code
57
+ end
58
+
59
+ def test_that_it_translates_code_with_symbols_and_labels
60
+ symbol_table = SymbolTable.new
61
+
62
+ source_code = "(START_LOOP)\n" << "@START_LOOP\n" << "(END_LOOP)\n" << "@END_LOOP"
63
+
64
+ label_less_code = Assembler.scan_and_remove_labels(source_code, symbol_table)
65
+
66
+ processed_code = Assembler.process_symbols(label_less_code, symbol_table)
67
+
68
+ bytecode = Assembler.translate(processed_code)
69
+
70
+ assert_equal "0000000000000000\n" << "0000000000000001\n", bytecode
71
+ end
72
+ end
@@ -0,0 +1,146 @@
1
+ require 'test_helper'
2
+
3
+ class SymbolTableTest < Minitest::Test
4
+ def setup
5
+ @symbol_table = SymbolTable.new
6
+ end
7
+
8
+ def test_that_adds_one_variable_symbol
9
+ address = @symbol_table.get_address('index')
10
+ assert_equal '16', address
11
+ end
12
+
13
+ def test_that_adds_two_variable_symbols
14
+ @symbol_table.get_address('index')
15
+ address = @symbol_table.get_address('count')
16
+ assert_equal '17', address
17
+ end
18
+
19
+ def test_that_addresses_dont_change
20
+ @symbol_table.get_address('index')
21
+ @symbol_table.get_address('count')
22
+ address = @symbol_table.get_address('index')
23
+ assert_equal '16', address
24
+ end
25
+
26
+ def test_add_one_label_address
27
+ @symbol_table.add_label_address('END', 32)
28
+ address = @symbol_table.get_address('END')
29
+ assert '32', address
30
+ end
31
+
32
+ def test_reserved_symbol_r0
33
+ address = @symbol_table.get_address('R0')
34
+ assert_equal '0', address
35
+ end
36
+
37
+ def test_reserved_symbol_r1
38
+ address = @symbol_table.get_address('R1')
39
+ assert_equal '1', address
40
+ end
41
+
42
+ def test_reserved_symbol_r2
43
+ address = @symbol_table.get_address('R2')
44
+ assert_equal '2', address
45
+ end
46
+
47
+ def test_reserved_symbol_r3
48
+ address = @symbol_table.get_address('R3')
49
+ assert_equal '3', address
50
+ end
51
+
52
+ def test_reserved_symbol_r4
53
+ address = @symbol_table.get_address('R4')
54
+ assert_equal '4', address
55
+ end
56
+
57
+ def test_reserved_symbol_r5
58
+ address = @symbol_table.get_address('R5')
59
+ assert_equal '5', address
60
+ end
61
+
62
+ def test_reserved_symbol_r6
63
+ address = @symbol_table.get_address('R6')
64
+ assert_equal '6', address
65
+ end
66
+
67
+ def test_reserved_symbol_r7
68
+ address = @symbol_table.get_address('R7')
69
+ assert_equal '7', address
70
+ end
71
+
72
+ def test_reserved_symbol_r8
73
+ address = @symbol_table.get_address('R8')
74
+ assert_equal '8', address
75
+ end
76
+
77
+ def test_reserved_symbol_r9
78
+ address = @symbol_table.get_address('R9')
79
+ assert_equal '9', address
80
+ end
81
+
82
+ def test_reserved_symbol_r10
83
+ address = @symbol_table.get_address('R10')
84
+ assert_equal '10', address
85
+ end
86
+
87
+ def test_reserved_symbol_r11
88
+ address = @symbol_table.get_address('R11')
89
+ assert_equal '11', address
90
+ end
91
+
92
+ def test_reserved_symbol_r12
93
+ address = @symbol_table.get_address('R12')
94
+ assert_equal '12', address
95
+ end
96
+
97
+ def test_reserved_symbol_r13
98
+ address = @symbol_table.get_address('R13')
99
+ assert_equal '13', address
100
+ end
101
+
102
+ def test_reserved_symbol_r14
103
+ address = @symbol_table.get_address('R14')
104
+ assert_equal '14', address
105
+ end
106
+
107
+ def test_reserved_symbol_r15
108
+ address = @symbol_table.get_address('R15')
109
+ assert_equal '15', address
110
+ end
111
+
112
+ def test_reserved_symbol_screen
113
+ address = @symbol_table.get_address('SCREEN')
114
+ assert_equal '16384', address
115
+ end
116
+
117
+ def test_reserved_symbol_kbd
118
+ address = @symbol_table.get_address('KBD')
119
+ assert_equal '24576', address
120
+ end
121
+
122
+ def test_reserved_symbol_sp
123
+ address = @symbol_table.get_address('SP')
124
+ assert_equal '0', address
125
+ end
126
+
127
+ def test_reserved_symbol_lcl
128
+ address = @symbol_table.get_address('LCL')
129
+ assert_equal '1', address
130
+ end
131
+
132
+ def test_reserved_symbol_arg
133
+ address = @symbol_table.get_address('ARG')
134
+ assert_equal '2', address
135
+ end
136
+
137
+ def test_reserved_symbol_this
138
+ address = @symbol_table.get_address('THIS')
139
+ assert_equal '3', address
140
+ end
141
+
142
+ def test_reserved_symbol_that
143
+ address = @symbol_table.get_address('THAT')
144
+ assert_equal '4', address
145
+ end
146
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hack_assembler
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Federico Rebora
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-08-24 00:00:00.000000000 Z
11
+ date: 2015-08-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -64,12 +64,10 @@ files:
64
64
  - ".travis.yml"
65
65
  - Gemfile
66
66
  - LICENSE.txt
67
- - Pong.asm
68
- - PongL.asm
69
67
  - README.md
70
68
  - Rakefile
71
69
  - bin/hack_assembler
72
- - hack2asm.gemspec
70
+ - hack_assembler.gemspec
73
71
  - lib/hack_assembler.rb
74
72
  - lib/hack_assembler/a_instruction.rb
75
73
  - lib/hack_assembler/assembler.rb
@@ -78,8 +76,8 @@ files:
78
76
  - lib/hack_assembler/destination_translator.rb
79
77
  - lib/hack_assembler/jump_translator.rb
80
78
  - lib/hack_assembler/parser_error.rb
79
+ - lib/hack_assembler/symbol_table.rb
81
80
  - lib/hack_assembler/version.rb
82
- - out.hack
83
81
  - test/a_instruction_test.rb
84
82
  - test/assembler_test.rb
85
83
  - test/c_instruction_test.rb
@@ -87,6 +85,7 @@ files:
87
85
  - test/destination_translator_test.rb
88
86
  - test/hack2asm_test.rb
89
87
  - test/jump_translator_test.rb
88
+ - test/symbol_table_test.rb
90
89
  - test/test_helper.rb
91
90
  homepage: ''
92
91
  licenses:
@@ -120,4 +119,5 @@ test_files:
120
119
  - test/destination_translator_test.rb
121
120
  - test/hack2asm_test.rb
122
121
  - test/jump_translator_test.rb
122
+ - test/symbol_table_test.rb
123
123
  - test/test_helper.rb