hack_assembler 0.1.0 → 0.2.0

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