kompiler 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +201 -0
- data/bin/kompile +51 -0
- data/lib/kompiler/arch/armv8a/instructions.rb +490 -0
- data/lib/kompiler/arch/armv8a/load.rb +8 -0
- data/lib/kompiler/arch/armv8a/registers.rb +67 -0
- data/lib/kompiler/architecture.rb +30 -0
- data/lib/kompiler/compiler_functions.rb +262 -0
- data/lib/kompiler/directives.rb +167 -0
- data/lib/kompiler/mc_builder.rb +88 -0
- data/lib/kompiler/parsers.rb +508 -0
- data/lib/kompiler.rb +19 -0
- metadata +60 -0
@@ -0,0 +1,262 @@
|
|
1
|
+
# Copyright 2024 Kyrylo Shyshko
|
2
|
+
# Licensed under the Apache License, Version 2.0. See LICENSE file for details.
|
3
|
+
|
4
|
+
module Kompiler
|
5
|
+
|
6
|
+
class CompilerFunctions
|
7
|
+
|
8
|
+
def self.parse_includes(lines, loaded_files=[])
|
9
|
+
|
10
|
+
final_lines = lines.dup
|
11
|
+
|
12
|
+
line_i = 0
|
13
|
+
|
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
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
|
87
|
+
def self.parse_code(lines)
|
88
|
+
|
89
|
+
parsed_lines = []
|
90
|
+
|
91
|
+
instr_adr = 0
|
92
|
+
|
93
|
+
lines.each_with_index do |line, line_i|
|
94
|
+
|
95
|
+
# Check if line is not just whitespace
|
96
|
+
is_char_whitespace = line.each_char.map{|c| [" ", "\t"].include? c}
|
97
|
+
if !is_char_whitespace.include?(false) # If only whitespace
|
98
|
+
next # Skip
|
99
|
+
end
|
100
|
+
|
101
|
+
#
|
102
|
+
# Label definitions are now a directive, so this isn't needed
|
103
|
+
#
|
104
|
+
|
105
|
+
# is_label, label_name = check_label(line)
|
106
|
+
# if is_label
|
107
|
+
# # labels[label_name] = instr_adr
|
108
|
+
# parsed_lines << {type: "label", label_name: label_name, label_address: instr_adr, address: instr_adr}
|
109
|
+
# next
|
110
|
+
# end
|
111
|
+
|
112
|
+
is_instruction, exec_instruction = Kompiler::Parsers.check_instruction(line)
|
113
|
+
if is_instruction
|
114
|
+
parsed_lines << {type: "instruction", instruction: exec_instruction[:instruction], operands: exec_instruction[:operands], address: instr_adr}
|
115
|
+
instr_adr += exec_instruction[:instruction][:bitsize] / 8
|
116
|
+
|
117
|
+
next # Go to the next line
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
is_directive, directive_hash = Kompiler::Parsers.check_directive(line)
|
122
|
+
if is_directive
|
123
|
+
directive = directive_hash[:directive]
|
124
|
+
operands = directive_hash[:operands]
|
125
|
+
|
126
|
+
state = {current_address: instr_adr, parsed_lines: parsed_lines}
|
127
|
+
|
128
|
+
state = directive[:func].call(operands, state)
|
129
|
+
|
130
|
+
instr_adr = state[:current_address]
|
131
|
+
parsed_lines = state[:parsed_lines]
|
132
|
+
|
133
|
+
next # Skip to the next lime
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
# Line wasn't classified
|
138
|
+
# Throw an error
|
139
|
+
|
140
|
+
raise "\"#{line}\" - Unknown syntax: Program build not possible"
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
parsed_lines
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
def self.get_labels(parsed_lines)
|
149
|
+
|
150
|
+
label_definitions = parsed_lines.filter{|line| line[:type] == "label"}
|
151
|
+
|
152
|
+
labels_hash = Hash.new
|
153
|
+
|
154
|
+
label_definitions.each do |label_def|
|
155
|
+
if labels_hash.keys.include?(label_def[:label_name])
|
156
|
+
puts "Warning: Label #{label_def[:label_name]} was aleady defined. Label is now re-defined"
|
157
|
+
end
|
158
|
+
|
159
|
+
labels_hash[label_def[:label_name]] = label_def[:label_address]
|
160
|
+
end
|
161
|
+
|
162
|
+
labels_hash
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
def self.construct_program_mc(parsed_lines, labels)
|
167
|
+
|
168
|
+
lines_bytes = ""
|
169
|
+
|
170
|
+
|
171
|
+
program_state = {labels: labels, current_address: 0}
|
172
|
+
|
173
|
+
parsed_lines.each do |line|
|
174
|
+
case line[:type]
|
175
|
+
when "instruction"
|
176
|
+
program_state[:operands] = line[:operands]
|
177
|
+
program_state[:current_address] = line[:address]
|
178
|
+
|
179
|
+
mc_constructor = line[:instruction][:mc_constructor]
|
180
|
+
|
181
|
+
instr_bits = Kompiler::MachineCode_AST.build_mc(mc_constructor, program_state)
|
182
|
+
|
183
|
+
instr_bytes = bits_to_bytes(instr_bits)
|
184
|
+
|
185
|
+
lines_bytes += instr_bytes.map(&:chr).join
|
186
|
+
when "insert"
|
187
|
+
if line[:bits]
|
188
|
+
lines_bytes += bits_to_bytes(line[:bits]).map(&:chr).join
|
189
|
+
elsif line[:bytes]
|
190
|
+
lines_bytes += line[:bytes].map(&:chr).join
|
191
|
+
elsif line[:byte_string]
|
192
|
+
lines_bytes += line[:byte_string]
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
lines_bytes
|
198
|
+
end
|
199
|
+
|
200
|
+
|
201
|
+
def self.bits_to_bytes(bits)
|
202
|
+
|
203
|
+
bit_byte_groups = (0...(bits.size / 8)).map{|byte_i| bits[(byte_i * 8)...(byte_i * 8 + 8)] }
|
204
|
+
|
205
|
+
bytes = []
|
206
|
+
|
207
|
+
|
208
|
+
bit_byte_groups.each do |byte_bits|
|
209
|
+
byte_val = 0
|
210
|
+
byte_bits.each_with_index do |bit, bit_i|
|
211
|
+
byte_val += bit * 2 ** bit_i
|
212
|
+
end
|
213
|
+
bytes << byte_val
|
214
|
+
end
|
215
|
+
|
216
|
+
bytes
|
217
|
+
end
|
218
|
+
|
219
|
+
|
220
|
+
def self.bit_lines_to_bytes(bit_lines)
|
221
|
+
|
222
|
+
bits_flat = bit_lines.flatten
|
223
|
+
|
224
|
+
bit_byte_groups = (0...(bits_flat.size / 8)).map{|byte_i| bits_flat[(byte_i * 8)...(byte_i * 8 + 8)] }
|
225
|
+
|
226
|
+
bytes = []
|
227
|
+
|
228
|
+
bit_byte_groups.each do |byte_bits|
|
229
|
+
byte_val = 0
|
230
|
+
|
231
|
+
byte_bits.each_with_index do |bit, bit_index|
|
232
|
+
byte_val += bit * 2 ** bit_index
|
233
|
+
end
|
234
|
+
|
235
|
+
bytes << byte_val
|
236
|
+
end
|
237
|
+
|
238
|
+
bytes
|
239
|
+
end
|
240
|
+
|
241
|
+
|
242
|
+
def self.compile(code, included_files=[])
|
243
|
+
|
244
|
+
lines = Kompiler::Parsers.get_code_lines(code)
|
245
|
+
|
246
|
+
final_lines = parse_includes(lines, included_files.map{|fname| File.expand_path(fname)})
|
247
|
+
|
248
|
+
parsed_lines = parse_code(final_lines)
|
249
|
+
|
250
|
+
labels = get_labels(parsed_lines)
|
251
|
+
|
252
|
+
# machine_code_bit_lines = construct_program_mc(parsed_lines, labels)
|
253
|
+
#
|
254
|
+
# machine_code_bytes = bit_lines_to_bytes(machine_code_bit_lines)
|
255
|
+
|
256
|
+
machine_code_bytes = construct_program_mc(parsed_lines, labels)
|
257
|
+
end
|
258
|
+
|
259
|
+
|
260
|
+
end # Kompiler::CompilerFunctions
|
261
|
+
|
262
|
+
end # Kompiler
|
@@ -0,0 +1,167 @@
|
|
1
|
+
# Copyright 2024 Kyrylo Shyshko
|
2
|
+
# Licensed under the Apache License, Version 2.0. See LICENSE file for details.
|
3
|
+
|
4
|
+
module Kompiler
|
5
|
+
|
6
|
+
class Directives
|
7
|
+
|
8
|
+
def self.directives
|
9
|
+
@@DIRECTIVES
|
10
|
+
end
|
11
|
+
|
12
|
+
@@DIRECTIVES = [
|
13
|
+
{
|
14
|
+
keyword: "zeros",
|
15
|
+
func: lambda do |operands, state|
|
16
|
+
raise "Incorrect use of the \"zeros\" directive." if operands.size > 1 || (operands[0] && operands[0][:type] != "immediate")
|
17
|
+
n_zeros = operands[0][:value]
|
18
|
+
state[:current_address] += n_zeros
|
19
|
+
state[:parsed_lines] << {type: "insert", bits: (n_zeros*8).times.map{0} }
|
20
|
+
state
|
21
|
+
end
|
22
|
+
},
|
23
|
+
{
|
24
|
+
keyword: "ascii",
|
25
|
+
func: lambda do |operands, state|
|
26
|
+
raise "Incorrect use of the \"ascii\" directive." if operands.size > 1 || (operands[0] && operands[0][:type] != "string")
|
27
|
+
|
28
|
+
insert_bytes = operands[0][:string].encode("ascii").bytes
|
29
|
+
insert_bits = insert_bytes.map{|byte| 8.times.map{|bit_i| byte[bit_i]}}.flatten
|
30
|
+
|
31
|
+
state[:parsed_lines] << {type: "insert", bits: insert_bits, address: state[:current_address]}
|
32
|
+
state[:current_address] += insert_bytes.size
|
33
|
+
|
34
|
+
state
|
35
|
+
end
|
36
|
+
},
|
37
|
+
{
|
38
|
+
keyword: "align",
|
39
|
+
func: lambda do |operands, state|
|
40
|
+
raise "Incorrect use of the \"align\" directive." if operands.size > 1 || (operands[0] && operands[0][:type] != "immediate")
|
41
|
+
|
42
|
+
alignment = operands[0][:value]
|
43
|
+
to_add = alignment - (state[:current_address] % alignment)
|
44
|
+
|
45
|
+
# If aligned, do nothing
|
46
|
+
return state if to_add == alignment
|
47
|
+
|
48
|
+
# Else add stuff
|
49
|
+
|
50
|
+
state[:current_address] += to_add
|
51
|
+
state[:parsed_lines] << {type: "insert", bits: (to_add * 8).times.map{0} }
|
52
|
+
|
53
|
+
state
|
54
|
+
end
|
55
|
+
},
|
56
|
+
{
|
57
|
+
keyword: "label",
|
58
|
+
func: lambda do |operands, state|
|
59
|
+
|
60
|
+
raise "Incorrect use of the \"label\" directive." if (operands.size < 1 || operands.size > 2) || (operands[0] && operands[0][:type] != "label")
|
61
|
+
|
62
|
+
raise "Incorrect use of the \"label\" directive: second argument must be an immediate value" if operands[1] && operands[1][:type] != "immediate"
|
63
|
+
|
64
|
+
label_name = operands[0][:value]
|
65
|
+
|
66
|
+
# If a second argument is provided, use it as the label value; otherwise use the current instruction address (PC)
|
67
|
+
if operands[1] && operands[1][:type] == "immediate"
|
68
|
+
label_value = operands[1][:value]
|
69
|
+
else
|
70
|
+
label_value = state[:current_address]
|
71
|
+
end
|
72
|
+
|
73
|
+
# Add the label definition
|
74
|
+
state[:parsed_lines] << {type: "label", label_name: label_name, label_address: label_value, address: state[:current_address]}
|
75
|
+
|
76
|
+
state
|
77
|
+
end
|
78
|
+
},
|
79
|
+
{
|
80
|
+
keyword: "8byte",
|
81
|
+
func: lambda do |operands, state|
|
82
|
+
|
83
|
+
raise "Incorrect use of the \"8byte\" directive." if (operands.size != 1) || (operands[0] && operands[0][:type] != "immediate")
|
84
|
+
|
85
|
+
value = operands[0][:value]
|
86
|
+
|
87
|
+
value_bits = (0...(8 * 8)).map{|bit_i| value[bit_i]}
|
88
|
+
|
89
|
+
# Insert 64 bits of the value into the program
|
90
|
+
state[:current_address] += 8
|
91
|
+
state[:parsed_lines] << {type: "insert", bits: value_bits}
|
92
|
+
|
93
|
+
state
|
94
|
+
end
|
95
|
+
},
|
96
|
+
{
|
97
|
+
keyword: "4byte",
|
98
|
+
func: lambda do |operands, state|
|
99
|
+
|
100
|
+
raise "Incorrect use of the \"4byte\" directive." if (operands.size != 1) || (operands[0] && operands[0][:type] != "immediate")
|
101
|
+
|
102
|
+
value = operands[0][:value]
|
103
|
+
|
104
|
+
value_bits = (0...(4 * 8)).map{|bit_i| value[bit_i]}
|
105
|
+
|
106
|
+
# Insert 64 bits of the value into the program
|
107
|
+
state[:current_address] += 4
|
108
|
+
state[:parsed_lines] << {type: "insert", bits: value_bits}
|
109
|
+
|
110
|
+
state
|
111
|
+
end
|
112
|
+
},
|
113
|
+
{
|
114
|
+
keyword: "bytes",
|
115
|
+
func: lambda do |operands, state|
|
116
|
+
|
117
|
+
raise "Incorrect use of the \"bytes\" directive." if (operands.size != 2) || (operands[0][:type] != "immediate" && operands[1][:type] != "immediate")
|
118
|
+
|
119
|
+
n_bytes = operands[0][:value]
|
120
|
+
value = operands[1][:value]
|
121
|
+
|
122
|
+
value_bits = (0...(n_bytes * 8)).map{|bit_i| value[bit_i]}
|
123
|
+
|
124
|
+
# Insert the input amount of bytes of the value into the program
|
125
|
+
state[:current_address] += n_bytes
|
126
|
+
state[:parsed_lines] << {type: "insert", bits: value_bits}
|
127
|
+
|
128
|
+
state
|
129
|
+
end
|
130
|
+
},
|
131
|
+
{
|
132
|
+
keyword: "set_pc",
|
133
|
+
func: lambda do |operands, state|
|
134
|
+
|
135
|
+
raise "Incorrect use of the \"set_pc\" directive." if (operands.size != 1) || (operands[0][:type] != "immediate")
|
136
|
+
|
137
|
+
new_pc = operands[0][:value]
|
138
|
+
|
139
|
+
state[:current_address] = new_pc
|
140
|
+
|
141
|
+
state
|
142
|
+
end
|
143
|
+
},
|
144
|
+
{
|
145
|
+
keyword: "insert_file",
|
146
|
+
func: lambda do |operands, state|
|
147
|
+
raise "Incorrect use of the \"insert_file\" directive" if (operands.size != 1) || (operands[0][:type] != "string")
|
148
|
+
|
149
|
+
filename = operands[0][:string]
|
150
|
+
|
151
|
+
file_content = ""
|
152
|
+
File.open(filename, "rb") do |f|
|
153
|
+
file_content = f.read()
|
154
|
+
end
|
155
|
+
|
156
|
+
state[:current_address] += file_content.bytes.size
|
157
|
+
state[:parsed_lines] << {type: "insert", byte_string: file_content}
|
158
|
+
|
159
|
+
state
|
160
|
+
end
|
161
|
+
}
|
162
|
+
]
|
163
|
+
|
164
|
+
|
165
|
+
end # Kompiler::Directives
|
166
|
+
|
167
|
+
end # Kompiler
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# Copyright 2024 Kyrylo Shyshko
|
2
|
+
# Licensed under the Apache License, Version 2.0. See LICENSE file for details.
|
3
|
+
|
4
|
+
module Kompiler
|
5
|
+
|
6
|
+
class MachineCode_AST
|
7
|
+
|
8
|
+
MC_AST_NODES = [
|
9
|
+
{name: "get_operand", n_args: 1, func: lambda {|args, state| state[:operands][args[0]][:value]} },
|
10
|
+
{name: "get_bits", n_args: 3, func: lambda {|args, state| (args[1]...(args[1] + args[2])).map{|bit_i| args[0][bit_i]} } },
|
11
|
+
{name: "get_bits_signed", n_args: 3, func: lambda do |args, state|
|
12
|
+
if args[1] == 0
|
13
|
+
# If sign should be included
|
14
|
+
(args[0] >= 0 ? [0] : [1]) + (0...(args[2] - 1)).map{|bit_i| args[0].abs[bit_i]}
|
15
|
+
else
|
16
|
+
# If sign shouldn't be included, since the bit range omits it
|
17
|
+
((args[1] - 1)...(args[1] + args[2] - 1)).map{|bit_i| args[0].abs[bit_i]}
|
18
|
+
end
|
19
|
+
end},
|
20
|
+
{name: "reverse", n_args: 1, func: lambda {|args, state| args[0].reverse } },
|
21
|
+
{name: "encode_gp_register", n_args: 1, func: lambda {|args, state| args[0][:reg_value] } },
|
22
|
+
{name: "add", n_args: 2, func: lambda {|args, state| args[0] + args[1] } },
|
23
|
+
{name: "subtract", n_args: 2, func: lambda {|args, state| args[0] - args[1] } },
|
24
|
+
{name: "multiply", n_args: 2, func: lambda {|args, state| args[0] * args[1] } },
|
25
|
+
{name: "divide", n_args: 2, func: lambda {|args, state| args[0] / args[1] } },
|
26
|
+
{name: "modulo", n_args: 2, func: lambda {|args, state| args[0] % args[1] } },
|
27
|
+
{name: "get_current_address", n_args: 0, func: lambda {|args, state| state[:current_address] } },
|
28
|
+
{name: "get_label_address", n_args: 1, func: lambda {|args, state| state[:labels].include?(args[0]) ? state[:labels][args[0]] : raise("Label \"#{args[0]}\" not found: Program build not possible") } },
|
29
|
+
{name: "bits", n_args: "any", func: lambda {|args, state| args } },
|
30
|
+
{name: "if_eq_else", n_args: 4, eval_args: false, func: lambda {|args, state| (eval_mc_node_arg(args[0], state) == eval_mc_node_arg(args[1], state)) ? eval_mc_node_arg(args[2], state) : eval_mc_node_arg(args[3], state) }},
|
31
|
+
{name: "raise_error", n_args: 1, func: lambda {|args, state| raise args[0] } },
|
32
|
+
]
|
33
|
+
|
34
|
+
def self.is_ast_node(val)
|
35
|
+
val.is_a?(Array) && (val.size >= 1) && val[0].is_a?(String)
|
36
|
+
end
|
37
|
+
|
38
|
+
# If an argument is a node, evaluates it. Otherwise just returns the argument
|
39
|
+
def self.eval_mc_node_arg(arg, state)
|
40
|
+
is_ast_node(arg) ? run_mc_ast(arg, state) : arg
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.run_mc_ast(node, state)
|
44
|
+
|
45
|
+
node_name = node[0]
|
46
|
+
node_args = node[1..]
|
47
|
+
|
48
|
+
node_logic = MC_AST_NODES.filter{|any_node| any_node[:name] == node_name}[0]
|
49
|
+
|
50
|
+
if !node_logic
|
51
|
+
raise "MC Node \"#{node_name}\" wasn't found. Cannot build the program"
|
52
|
+
end
|
53
|
+
|
54
|
+
if !node_logic.keys.include?(:eval_args) || node_logic[:eval_args] != false
|
55
|
+
node_args.map!{|arg| eval_mc_node_arg(arg, state) }
|
56
|
+
end
|
57
|
+
|
58
|
+
raise "Undefined node \"#{node_name}\"" if !node_logic
|
59
|
+
|
60
|
+
# Check if the amount of arguments is correct for the node
|
61
|
+
raise "Incorrect node use for \"#{node_name}\": Expected #{node_logic[:n_args]} operands, but received #{node_args.size}" if (node_logic[:n_args] != "any") && (node_logic[:n_args] != node_args.size)
|
62
|
+
|
63
|
+
node_logic[:func].call(node_args, state)
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def self.build_mc(mc_constructor, state)
|
68
|
+
final = []
|
69
|
+
mc_constructor.each do |ast_node|
|
70
|
+
|
71
|
+
ast_result = run_mc_ast(ast_node, state)
|
72
|
+
|
73
|
+
# Check if ast_result is only zeros and ones
|
74
|
+
is_element_zero_or_one = ast_result.map{|el| [0, 1].include?(el)}
|
75
|
+
if is_element_zero_or_one.include?(false)
|
76
|
+
raise "MC AST Build resulted in a non-bit value (#{ast_result}): Cannot build the program"
|
77
|
+
end
|
78
|
+
|
79
|
+
final += ast_result
|
80
|
+
end
|
81
|
+
|
82
|
+
return final
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
end # Kompiler::MC_AST
|
87
|
+
|
88
|
+
end # Kompiler
|