kompiler 0.3.0.pre.3 → 0.3.0.pre.4

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
  SHA256:
3
- metadata.gz: d37e70a65615ea1b7aece9de1a2ae544a30d9f3f9f1f44d233fac8ecde023b63
4
- data.tar.gz: 533a6ac4a19a03c8ca483c63bf454d4cda802943462ea23bcbc1a59688e64d8d
3
+ metadata.gz: b00e5940820b7f2798ede46f7014a87ab43d7ade0ca171c223eae550668673c0
4
+ data.tar.gz: dcd9d7bd8d170a37206d51a6b427c48217d115f048620404b045a8f6005eb6e0
5
5
  SHA512:
6
- metadata.gz: ad275ed1d7bbb7dad29409b3ca2f4e061c08f4534bf7c2104d2c20c235a1030e496ae6381322042fe1d1bdd699acc5e970ef82b521031853d4222792b6bf388f
7
- data.tar.gz: 9132f9c232bc01ff2ebfac62bfa09527e79f644ce0d15a360de91ece0a854187da581b6e67c2bec78e6745845fb2257c759786d71e02d44f17fbf4919e7e106c
6
+ metadata.gz: 023ae20fa5229d793e71ebea06bc46dea243170129e12eaf5c099c16dabdabcecb6302f0893ef17cbf0128c30a8d7d9f0b77841ecfa6ddb871d18e0c517373d4
7
+ data.tar.gz: ae856004b38f75b46b4b62c7455c5f94cbeb2f21dd34e54443ff776f4fbb23432eb42f389bd706f3d649c8b2d69bee3a3e2d2cf7095d24178c32e50f32b47149
@@ -20,7 +20,7 @@ module Kompiler
20
20
  end
21
21
 
22
22
  def self.load_all_entries
23
- Dir[File.join(__dir__, 'arch_entries', '*.rb')].each { |file| require file }
23
+ Gem.find_files("kompiler/arch_entries/*").each { |file| require file }
24
24
  end
25
25
  end
26
26
 
@@ -5,222 +5,35 @@ module Kompiler
5
5
 
6
6
  module CompilerFunctions
7
7
 
8
- def self.parse_includes(lines, loaded_files=[])
9
-
10
- final_lines = lines.dup
8
+ def self.parse_code(lines)
11
9
 
12
- line_i = 0
10
+ parsed_lines = []
13
11
 
14
- loop do
15
- break if line_i >= final_lines.size
16
-
17
- line = final_lines[line_i]
18
-
19
- keyword, operands = Kompiler::Parsers.parse_instruction_line(line)
20
-
21
- if keyword == false
22
- line_i += 1
23
- next
24
- end
25
-
26
- if keyword.start_with? "."
27
- keyword = keyword[1..]
28
- end
29
-
30
- if !["load", "include", "load_end", "include_end"].include?(keyword)
31
- line_i += 1
32
- next
33
- end
34
-
35
- raise "Incorrect use of the \"#{keyword}\" directive: requires a filename in a string." if operands.size != 1 || (operands.size == 1 && operands[0][:type] != "string")
36
-
37
- load_file_name_selector = operands[0][:string]
38
-
39
-
40
- # Remove the include code line from the lines array
41
- final_lines.delete_at line_i
42
-
43
- # If ends with _end, that means that the file contents should be appended at the end of the current lines
44
- if keyword.end_with? "_end"
45
- next_i_insert = final_lines.size
46
- else
47
- # If doesn't end with _end, files should be loaded in-place of the current line
48
- next_i_insert = line_i
49
- end
50
-
51
-
52
- Dir[load_file_name_selector].each do |load_file_name|
53
- # Get the absolute path filename
54
- full_file_name = File.expand_path(load_file_name)
55
-
56
- raise "#{keyword} \"#{load_file_name}\": File not found." if !File.exist?(full_file_name)
57
-
58
- # Check if the file was already loaded (stop recursive loading)
59
- if loaded_files.include?(full_file_name)
60
- next
61
- end
62
-
63
- # Read the file to load
64
- include_code = File.read(full_file_name)
65
-
66
- # Separate the lines inside it
67
- include_code_lines = Kompiler::Parsers.get_code_lines(include_code)
68
-
69
- # Add the lines from the load file to the lines array at the line_i index, effectively replacing the load command with the content of the load file
70
- final_lines.insert next_i_insert, *include_code_lines
71
-
72
- next_i_insert += include_code_lines.size
73
-
74
- # Add the filename (absolute path) to the list of included files
75
- loaded_files << full_file_name
76
- end
77
-
78
- # Don't increment line_i, since the new loop will now start include-parsing the newly loaded file in the same program
79
- end
80
-
81
- final_lines
12
+ instr_adr = 0
82
13
 
83
- end
84
-
85
-
86
- def self.parse_macros(lines)
87
-
88
- macros = []
89
-
90
14
  line_i = 0
91
-
92
- loop do
93
- break if line_i >= lines.size
94
-
95
- line = lines[line_i]
96
-
97
- keyword, operands = Kompiler::Parsers.parse_instruction_line line
98
-
99
- keyword = keyword[1..] if keyword.start_with? "."
100
-
101
- if keyword == 'macro'
102
- raise "Macro definition error: Expected two operands to be provided, while #{operands.size} were given" if operands.size != 2
103
- raise "Macro definition error: The macro name must either be a word or a string" if !["label", "string"].include?(operands[0][:type])
104
-
105
- case operands[0][:type]
106
- when "label"
107
- macro_name = operands[0][:value]
108
- when "string"
109
- macro_name = operands[0][:value]
110
- end
111
-
112
- macro_definition = operands[1][:definition]
113
-
114
- macros << {name: macro_name, definition: macro_definition}
115
-
116
- lines.delete_at line_i
117
- else
118
- line_i += 1
119
- end
120
- end
121
-
122
- # Apply the macros to all lines, and then the lines that were changed, and than again, and so on (iterative loop until none of the lines need to be checked)
123
-
124
- lines_to_check = (0...lines.size).to_a
125
-
126
- while lines_to_check.size != 0 # Repeat until no more lines to check
127
-
128
- # Define restricted separators characters (characters that CAN'T separate a macro from everything else)
129
- restricted_separator_chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a + ["_", "."]
130
-
131
- # Create an array for the lines to check once again
132
- new_lines_to_check = []
133
-
134
- lines_to_check.each do |line_i|
135
- line = lines[line_i]
136
-
137
- char_i = 0
138
-
139
- # Create a variable to indicate if a macro was used on the current line
140
- line_macro_used = false
141
-
142
- loop do
143
- break if char_i >= line.size
144
-
145
- if ['"', "'"].include? line[char_i]
146
- # If a string, skip it
147
- str, parsed_length = Kompiler::Parsers.parse_str line[char_i..]
148
- char_i += parsed_length
149
- next
150
- end
151
-
152
- cut_line = line[char_i..]
153
-
154
- # Create a variable to indicate if a macro was used in the current position
155
- macro_used = false
156
-
157
- macros.each do |macro|
158
- macro_name = macro[:name]
159
- macro_def = macro[:definition]
160
- next if !cut_line.start_with?(macro_name) # Skip if doesn't begin with the macro's name
161
- next if (cut_line.size > macro_name.size) && restricted_separator_chars.include?(cut_line[macro_name.size]) # Skip if there is a character after the macro name, and the character is not permitted
162
- next if (char_i > 0) && restricted_separator_chars.include?(line[char_i - 1]) # Skip if there is a character before the checking sequence, and the character is not permitted
163
-
164
- # Here if the macro is 'accepted'
165
- line = line[...char_i] + macro_def + line[(char_i + macro_name.size)..]
166
- # Indicate that a macro was used
167
- macro_used = true
168
- end
169
-
170
- if macro_used
171
- # If a macro was used, indicate that on line level
172
- line_macro_used = true
173
- else
174
- # If a macro wasn't used, proceed to the next character
175
- char_i += 1
176
- end
177
- end # line characters loop
178
-
179
- if line_macro_used
180
- # If a macro was used on the current line, update the lines array, and add the line to check for macros again
181
- lines[line_i] = line
182
- new_lines_to_check << line_i
183
- end
184
- end # lines_to_check.each loop
185
-
186
- lines_to_check = new_lines_to_check # Update lines to check with the new ones
187
-
188
- end # while lines_to_check.size != 0 loop
189
15
 
190
- lines
191
- end
192
-
193
-
194
- def self.parse_code(lines)
16
+ extra_state = Hash.new
195
17
 
196
- parsed_lines = []
197
-
198
- instr_adr = 0
18
+ add_later_directives = []
199
19
 
200
- lines.each_with_index do |line, line_i|
20
+ while line_i < lines.size
21
+ line = lines[line_i]
201
22
 
202
23
  # Check if line is not just whitespace
203
- is_char_whitespace = line.each_char.map{|c| [" ", "\t"].include? c}
24
+ is_char_whitespace = line.each_char.map{|c| Kompiler::Config.whitespace_chars.include? c}
204
25
  if !is_char_whitespace.include?(false) # If only whitespace
26
+ line_i += 1
205
27
  next # Skip
206
28
  end
207
29
 
208
- #
209
- # Label definitions are now a directive, so this isn't needed
210
- #
211
-
212
- # is_label, label_name = check_label(line)
213
- # if is_label
214
- # # labels[label_name] = instr_adr
215
- # parsed_lines << {type: "label", label_name: label_name, label_address: instr_adr, address: instr_adr}
216
- # next
217
- # end
218
-
219
30
  is_instruction, exec_instruction = Kompiler::Parsers.check_instruction(line)
220
31
  if is_instruction
221
32
  parsed_lines << {type: "instruction", instruction: exec_instruction[:instruction], operands: exec_instruction[:operands], address: instr_adr}
222
33
  instr_adr += exec_instruction[:instruction][:bitsize] / 8
223
-
34
+
35
+ line_i += 1
36
+
224
37
  next # Go to the next line
225
38
  end
226
39
 
@@ -230,12 +43,59 @@ def self.parse_code(lines)
230
43
  directive = directive_hash[:directive]
231
44
  operands = directive_hash[:operands]
232
45
 
233
- state = {current_address: instr_adr, parsed_lines: parsed_lines}
46
+ state = {current_address: instr_adr, parsed_lines: parsed_lines, lines: lines, line_i: line_i, extra_state: extra_state}
47
+
48
+ add_later_directive = false
49
+
50
+ if directive.keys.include?(:add_later_directive) && directive[:add_later_directive] == true
51
+ add_later_directive = true
52
+ end
53
+
54
+ new_operands = operands.map do |op|
55
+ if op[:type] == "run_block"
56
+ begin
57
+ block_state = state.dup
58
+ block_state[:block_args] = op[:block_args]
59
+ block_state[:labels] = self.get_labels(parsed_lines)
60
+ op = op[:block].call(block_state)
61
+ rescue
62
+ add_later_directive = true
63
+ op = {type: "immediate", value: 0, def_type: "kompiler_test_value", definition: "0"}
64
+ end
65
+ end
66
+ op
67
+ end
68
+
69
+
70
+ if add_later_directive
71
+
72
+ try_state = {current_address: instr_adr, parsed_lines: parsed_lines.dup, lines: lines.dup, line_i: line_i, extra_state: extra_state.dup}
73
+
74
+ try_state = directive[:func].call(new_operands, try_state)
75
+
76
+
77
+ instr_adr = try_state[:current_address]
78
+ line_i = try_state[:line_i]
79
+
80
+ raise "Directive error 1.1" if !(lines == try_state[:lines])
81
+ raise "Directive error 1.2" if !(extra_state == try_state[:extra_state])
82
+ raise "Directive error 1.3" if !(parsed_lines == try_state[:parsed_lines][...parsed_lines.size]) # Check that the previous parsed lines were not changed by the directive
83
+
84
+ state.delete :parsed_lines
85
+ state.delete :lines
86
+
87
+ add_later_directives << {directive_hash: directive_hash, insert_i: parsed_lines.size, run_state: state, return_line_i: line_i, return_current_address: instr_adr}
88
+
89
+ next
90
+ end
234
91
 
235
- state = directive[:func].call(operands, state)
92
+ state = directive[:func].call(new_operands, state)
236
93
 
237
94
  instr_adr = state[:current_address]
238
95
  parsed_lines = state[:parsed_lines]
96
+ lines = state[:lines]
97
+ line_i = state[:line_i]
98
+ extra_state = state[:extra_state]
239
99
 
240
100
  next # Skip to the next lime
241
101
  end
@@ -246,6 +106,37 @@ def self.parse_code(lines)
246
106
 
247
107
  raise "\"#{line}\" - Unknown syntax: Program build not possible"
248
108
 
109
+ end
110
+
111
+ add_later_directives.sort_by{|hash| hash[:insert_i]}.reverse.each do |add_later_directive|
112
+ directive_hash = add_later_directive[:directive_hash]
113
+ insert_i = add_later_directive[:insert_i]
114
+ state = add_later_directive[:run_state]
115
+
116
+ state[:parsed_lines] = parsed_lines[...insert_i]
117
+ state[:lines] = lines
118
+
119
+ directive = directive_hash[:directive]
120
+ operands = directive_hash[:operands]
121
+
122
+ new_operands = operands.map do |op|
123
+ if op[:type] == "run_block"
124
+ block_state = state.dup
125
+ block_state[:block_args] = op[:block_args]
126
+ block_state[:labels] = self.get_labels(parsed_lines)
127
+ op = op[:block].call(block_state)
128
+ end
129
+ op
130
+ end
131
+
132
+ state = directive[:func].call(new_operands, state)
133
+
134
+
135
+ raise "Directive error 2.1" if add_later_directive[:return_current_address] != state[:current_address]
136
+ raise "Directive error 2.2" if add_later_directive[:return_line_i] != state[:line_i]
137
+
138
+
139
+ parsed_lines = state[:parsed_lines] + parsed_lines[insert_i..]
249
140
  end
250
141
 
251
142
  parsed_lines
@@ -280,9 +171,21 @@ def self.construct_program_mc(parsed_lines, labels)
280
171
  parsed_lines.each do |line|
281
172
  case line[:type]
282
173
  when "instruction"
283
- program_state[:operands] = line[:operands]
284
174
  program_state[:current_address] = line[:address]
285
175
 
176
+ operands = line[:operands]
177
+ operands.map! do |op|
178
+ if op[:type] == "run_block"
179
+ state = program_state.dup
180
+ state[:block_args] = op[:block_args]
181
+ op = op[:block].call(state)
182
+ end
183
+ op
184
+ end
185
+
186
+ program_state[:operands] = operands
187
+
188
+
286
189
  mc_constructor = line[:instruction][:mc_constructor]
287
190
 
288
191
  instr_bits = Kompiler::MachineCode_AST.build_mc(mc_constructor, program_state)
@@ -351,17 +254,9 @@ def self.compile(code, included_files=[])
351
254
 
352
255
  lines = Kompiler::Parsers.get_code_lines(code)
353
256
 
354
- included_lines = parse_includes(lines, included_files.map{|fname| File.expand_path(fname)})
355
-
356
- macroed_lines = parse_macros(included_lines)
357
-
358
- parsed_lines = parse_code(macroed_lines)
359
-
360
- labels = get_labels(parsed_lines)
257
+ parsed_lines = parse_code(lines)
361
258
 
362
- # machine_code_bit_lines = construct_program_mc(parsed_lines, labels)
363
- #
364
- # machine_code_bytes = bit_lines_to_bytes(machine_code_bit_lines)
259
+ labels = get_labels(parsed_lines)
365
260
 
366
261
  machine_code_bytes = construct_program_mc(parsed_lines, labels)
367
262
  end
@@ -0,0 +1,29 @@
1
+ module Kompiler
2
+
3
+ module Config
4
+
5
+ @keyword_chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a + ["_", "."]
6
+ @label_chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a + ["_", "."]
7
+ @whitespace_chars = [" ", "\t"]
8
+ @string_delimiters = ['"', "'"]
9
+
10
+ # Returns the permittable keyword characters
11
+ def self.keyword_chars
12
+ @keyword_chars
13
+ end
14
+
15
+ # Returns the permittable label characters
16
+ def self.label_chars
17
+ @label_chars
18
+ end
19
+
20
+ def self.whitespace_chars
21
+ @whitespace_chars
22
+ end
23
+
24
+ def self.string_delimiters
25
+ @string_delimiters
26
+ end
27
+ end
28
+
29
+ end