kompiler 0.0.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 +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
|