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 +4 -4
- data/lib/kompiler/arch_manager.rb +1 -1
- data/lib/kompiler/compiler_functions.rb +107 -212
- data/lib/kompiler/config.rb +29 -0
- data/lib/kompiler/directives.rb +354 -4
- data/lib/kompiler/math_ast.rb +665 -0
- data/lib/kompiler/parsers.rb +116 -45
- data/lib/kompiler.rb +3 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b00e5940820b7f2798ede46f7014a87ab43d7ade0ca171c223eae550668673c0
|
4
|
+
data.tar.gz: dcd9d7bd8d170a37206d51a6b427c48217d115f048620404b045a8f6005eb6e0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 023ae20fa5229d793e71ebea06bc46dea243170129e12eaf5c099c16dabdabcecb6302f0893ef17cbf0128c30a8d7d9f0b77841ecfa6ddb871d18e0c517373d4
|
7
|
+
data.tar.gz: ae856004b38f75b46b4b62c7455c5f94cbeb2f21dd34e54443ff776f4fbb23432eb42f389bd706f3d649c8b2d69bee3a3e2d2cf7095d24178c32e50f32b47149
|
@@ -5,222 +5,35 @@ module Kompiler
|
|
5
5
|
|
6
6
|
module CompilerFunctions
|
7
7
|
|
8
|
-
def self.
|
9
|
-
|
10
|
-
final_lines = lines.dup
|
8
|
+
def self.parse_code(lines)
|
11
9
|
|
12
|
-
|
10
|
+
parsed_lines = []
|
13
11
|
|
14
|
-
|
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
|
-
|
191
|
-
end
|
192
|
-
|
193
|
-
|
194
|
-
def self.parse_code(lines)
|
16
|
+
extra_state = Hash.new
|
195
17
|
|
196
|
-
|
197
|
-
|
198
|
-
instr_adr = 0
|
18
|
+
add_later_directives = []
|
199
19
|
|
200
|
-
|
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|
|
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(
|
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
|
-
|
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
|
-
|
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
|