kompiler 0.3.0.pre.3 → 0.3.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 +4 -4
- data/bin/kompile +230 -33
- data/lib/kompiler/arch_manager.rb +11 -2
- data/lib/kompiler/architecture.rb +5 -1
- data/lib/kompiler/architectures/armv8a/instructions.rb +76 -27
- data/lib/kompiler/architectures/armv8a/load.rb +3 -1
- data/lib/kompiler/architectures/armv8a/simd_fp_instructions.rb +1308 -0
- data/lib/kompiler/architectures/armv8a/simd_fp_registers.rb +23 -0
- data/lib/kompiler/architectures/armv8a/sys_registers.rb +3 -0
- data/lib/kompiler/compiler_functions.rb +138 -219
- data/lib/kompiler/config.rb +40 -0
- data/lib/kompiler/directives.rb +367 -4
- data/lib/kompiler/math_ast.rb +693 -0
- data/lib/kompiler/mc_builder.rb +48 -0
- data/lib/kompiler/parsers.rb +137 -45
- data/lib/kompiler/wrappers/elf_wrapper.rb +499 -0
- data/lib/kompiler/wrappers/packed_bytes.rb +68 -0
- data/lib/kompiler/wrappers.rb +1 -0
- data/lib/kompiler.rb +4 -1
- metadata +10 -3
@@ -0,0 +1,693 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# Implements logic to parse math-like expressions into an ASTs.
|
4
|
+
#
|
5
|
+
# Main functions:
|
6
|
+
# str_to_ast - converts a raw string into an AST.
|
7
|
+
# run_ast - runs the AST created by str_to_ast.
|
8
|
+
#
|
9
|
+
# Config options are available in Kompiler::Parsers::SymAST::Config :
|
10
|
+
# word_begin_chars - a list of characters that a word can begin with
|
11
|
+
# word_chars - a list of characters that a word can contain
|
12
|
+
# number_begin_chars - a list of characters that a number can begin with
|
13
|
+
# number_chars - a list of characters that a number can contain
|
14
|
+
# whitespace_chars - a list of whitespace / separator characters
|
15
|
+
# parse_functions - a boolean specifying whether functions with syntax func(x + 2) should be parsed or throw an error
|
16
|
+
# sign_types - a list of available signs, their names and character sequences that qualify as the sign.
|
17
|
+
# Entries appearing earlier in the list are prioritized.
|
18
|
+
# one_element_ast_operations - a list of one element operations, their names, sign types, and checking direction (1 for left to right, -1 for right to left).
|
19
|
+
# For example, the negation "-x" is a one element operation, with the sign type "sub", and check_direction -1 (checks from right to left) because it is on the left of the 'x'
|
20
|
+
# Entries appearing earlier in the list are prioritized.
|
21
|
+
# two_element_ast_operations - a list of two element operation 'groups'. Groups are implemented to list operations on the same priority level with the same check direction.
|
22
|
+
# Each group has a check_direction (1 for left to right, -1 for opposite), and a list of operations in this group, their names and sign types, similar to one element operations.
|
23
|
+
# Entries appearing earlier in the list are prioritized.
|
24
|
+
# An example group could be multiplication and division. The check_direction will be 1, and there will be two operations (mul and div). This group will be below the power (a ** b) group.
|
25
|
+
# functions - a list of available functions in expressions in Kompiler programs
|
26
|
+
#
|
27
|
+
|
28
|
+
|
29
|
+
module Kompiler
|
30
|
+
|
31
|
+
module Parsers
|
32
|
+
|
33
|
+
|
34
|
+
module SymAST
|
35
|
+
|
36
|
+
module Config
|
37
|
+
|
38
|
+
# Word begin characters are characters from which a word can begin (right now, everything except numbers)
|
39
|
+
@word_begin_chars = ("a".."z").to_a + ("A".."Z").to_a + ["_"]
|
40
|
+
# Word characters are characters that the word can contain (excluding the first character)
|
41
|
+
@word_chars = @word_begin_chars + ("0".."9").to_a
|
42
|
+
|
43
|
+
# Number begin characters. Same as word_begin_chars but for numbers
|
44
|
+
@number_begin_chars = ("0".."9").to_a
|
45
|
+
# Number characters. Same as word_chars but for numbers
|
46
|
+
@number_chars = ("0".."9").to_a + ["."]
|
47
|
+
|
48
|
+
# Whitespace characters
|
49
|
+
@whitespace_chars = [" ", "\t"]
|
50
|
+
|
51
|
+
class <<self
|
52
|
+
attr_accessor :word_begin_chars, :word_chars, :number_begin_chars, :number_chars, :whitespace_chars
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
# Include function operations parsing (e.g., func(x + 2) )
|
58
|
+
@parse_functions = true
|
59
|
+
|
60
|
+
class <<self
|
61
|
+
attr_accessor :parse_functions
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
@sign_types = [
|
66
|
+
{name: "open_bracket", chars: ["("]},
|
67
|
+
{name: "close_bracket", chars: [")"]},
|
68
|
+
{name: "power", chars: ["**"]},
|
69
|
+
{name: "div", chars: ["/"]},
|
70
|
+
{name: "mul", chars: ["*"]},
|
71
|
+
{name: "add", chars: ["+"]},
|
72
|
+
{name: "sub", chars: ["-"]},
|
73
|
+
{name: "shift_left", chars: ["<<"]},
|
74
|
+
{name: "shift_right", chars: [">>"]},
|
75
|
+
{name: "or", chars: ["|"]},
|
76
|
+
{name: "and", chars: ["&"]},
|
77
|
+
{name: "modulo_sign", chars: ["%"]},
|
78
|
+
{name: "equal_sign", chars: ["=="]},
|
79
|
+
{name: "not_equal_sign", chars: ["!="]},
|
80
|
+
{name: "less_or_eq_sign", chars: ["<="]},
|
81
|
+
{name: "greater_or_eq_sign", chars: [">="]},
|
82
|
+
{name: "less_than_sign", chars: ["<"]},
|
83
|
+
{name: "greater_than_sign", chars: [">"]},
|
84
|
+
|
85
|
+
{name: "exclamation_mark", chars: ["!"]},
|
86
|
+
]
|
87
|
+
|
88
|
+
# One element operations (e.g., negate operation as "-x" or factorial as "x!"). Elements earlier have higher priority
|
89
|
+
# check_direction means whether parsing should start from the left (-1) or from the right (1)
|
90
|
+
@one_element_ast_operations = [
|
91
|
+
{name: "negate", sign_type: "sub", check_direction: -1},
|
92
|
+
{name: "factorial", sign_type: "exclamation_mark", check_direction: 1},
|
93
|
+
]
|
94
|
+
|
95
|
+
# # Two element operations (e.g., division as "a / b"). Elements earlier have higher priority
|
96
|
+
# # check_direction means whether parsing should start from the left (-1) or from the right (1)
|
97
|
+
# @two_element_ast_operations = [
|
98
|
+
# {name: "power", sign_type: "power", check_direction: -1},
|
99
|
+
# {name: "div", sign_type: "div", check_direction: 1},
|
100
|
+
# {name: "mul", sign_type: "mul", check_direction: 1},
|
101
|
+
# {name: "add", sign_type: "add", check_direction: 1},
|
102
|
+
# {name: "sub", sign_type: "sub", check_direction: 1},
|
103
|
+
# {name: "bitshift_left", sign_type: "shift_left", check_direction: 1},
|
104
|
+
# {name: "bitshift_right", sign_type: "shift_right", check_direction: 1},
|
105
|
+
# {name: "bit_or", sign_type: "or", check_direction: 1},
|
106
|
+
# {name: "bit_and", sign_type: "and", check_direction: 1},
|
107
|
+
# ]
|
108
|
+
|
109
|
+
# Two element operations (e.g., division as "a / b"). Elements earlier have higher priority
|
110
|
+
# check_direction means whether parsing should start from the left (-1) or from the right (1)
|
111
|
+
@two_element_ast_operations = [
|
112
|
+
{
|
113
|
+
group_check_direction: -1,
|
114
|
+
group_operations: [
|
115
|
+
{name: "power", sign_type: "power"},
|
116
|
+
]
|
117
|
+
},
|
118
|
+
{
|
119
|
+
group_check_direction: 1,
|
120
|
+
group_operations: [
|
121
|
+
{name: "modulo", sign_type: "modulo_sign"},
|
122
|
+
]
|
123
|
+
},
|
124
|
+
{
|
125
|
+
group_check_direction: 1,
|
126
|
+
group_operations: [
|
127
|
+
{name: "div", sign_type: "div"},
|
128
|
+
{name: "mul", sign_type: "mul"},
|
129
|
+
]
|
130
|
+
},
|
131
|
+
{
|
132
|
+
group_check_direction: 1,
|
133
|
+
group_operations: [
|
134
|
+
{name: "bitshift_left", sign_type: "shift_left"},
|
135
|
+
{name: "bitshift_right", sign_type: "shift_right"},
|
136
|
+
{name: "bit_or", sign_type: "or"},
|
137
|
+
{name: "bit_and", sign_type: "and"},
|
138
|
+
]
|
139
|
+
},
|
140
|
+
{
|
141
|
+
group_check_direction: 1,
|
142
|
+
group_operations: [
|
143
|
+
{name: "add", sign_type: "add"},
|
144
|
+
{name: "sub", sign_type: "sub"},
|
145
|
+
]
|
146
|
+
},
|
147
|
+
{
|
148
|
+
group_check_direction: 1,
|
149
|
+
group_operations: [
|
150
|
+
{name: "equal", sign_type: "equal_sign"},
|
151
|
+
{name: "not_equal", sign_type: "not_equal_sign"},
|
152
|
+
{name: "less_than", sign_type: "less_than_sign"},
|
153
|
+
{name: "greater_than", sign_type: "less_than_sign"},
|
154
|
+
{name: "less_or_eq", sign_type: "less_or_eq_sign"},
|
155
|
+
{name: "greater_or_eq", sign_type: "greater_or_eq_sign"},
|
156
|
+
]
|
157
|
+
},
|
158
|
+
]
|
159
|
+
|
160
|
+
|
161
|
+
@functions = {
|
162
|
+
"len" => lambda do |arg|
|
163
|
+
if !arg.is_a?(String) then raise "Math AST len() error 1" end
|
164
|
+
return arg.size
|
165
|
+
end,
|
166
|
+
"floor" => lambda do |arg|
|
167
|
+
if !arg.is_a?(Numeric) then raise "Math AST floor() error 1" end
|
168
|
+
return arg.floor
|
169
|
+
end
|
170
|
+
}
|
171
|
+
|
172
|
+
class <<self
|
173
|
+
attr_accessor :sign_types, :one_element_ast_operations, :two_element_ast_operations, :functions
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
|
178
|
+
def self.str_to_tokens str
|
179
|
+
|
180
|
+
tokens = []
|
181
|
+
|
182
|
+
char_i = 0
|
183
|
+
|
184
|
+
# Types of available signs (the first one is prioritized)
|
185
|
+
sign_types = Config.sign_types
|
186
|
+
|
187
|
+
|
188
|
+
full_word = ""
|
189
|
+
|
190
|
+
while char_i < str.size
|
191
|
+
|
192
|
+
|
193
|
+
# Check if the character is a whitespace
|
194
|
+
if Config.whitespace_chars.include?(str[char_i])
|
195
|
+
char_i += 1 # Move to the next character
|
196
|
+
next # Skip
|
197
|
+
end
|
198
|
+
|
199
|
+
cut_str = str[char_i..]
|
200
|
+
|
201
|
+
# Check if the current position is a math sign
|
202
|
+
|
203
|
+
sign_found = false
|
204
|
+
str_found = false
|
205
|
+
|
206
|
+
sign_types.each do |sign|
|
207
|
+
sign[:chars].each do |seq|
|
208
|
+
if cut_str.start_with? seq
|
209
|
+
# Here when the sign matched
|
210
|
+
tokens << {type: "sign", sign_type: sign[:name], match_seq: seq}
|
211
|
+
char_i += seq.size
|
212
|
+
sign_found = true
|
213
|
+
end
|
214
|
+
break if sign_found
|
215
|
+
end
|
216
|
+
break if sign_found
|
217
|
+
end
|
218
|
+
|
219
|
+
|
220
|
+
if !sign_found && Kompiler::Config.string_delimiters.include?(str[char_i])
|
221
|
+
str_content, len_parsed = Kompiler::Parsers.parse_str(cut_str)
|
222
|
+
|
223
|
+
|
224
|
+
case str[char_i]
|
225
|
+
when '"'
|
226
|
+
tokens << {type: "string", str_content: str_content}
|
227
|
+
when "'"
|
228
|
+
if str_content.size != 1 then raise "Math AST parse error - a character definition cannot be longer than 1" end
|
229
|
+
full_str = str[char_i...(char_i + len_parsed)]
|
230
|
+
tokens << {type: "number", number_content: full_str}
|
231
|
+
end
|
232
|
+
|
233
|
+
str_found = true
|
234
|
+
char_i += len_parsed
|
235
|
+
end
|
236
|
+
|
237
|
+
|
238
|
+
if sign_found || str_found
|
239
|
+
next if full_word.size == 0
|
240
|
+
|
241
|
+
is_imm, imm_value = Kompiler::Parsers.check_immediate_operand(full_word)
|
242
|
+
if is_imm
|
243
|
+
tokens.insert -2, {type: "number", number_content: full_word, number_value: imm_value[:value]}
|
244
|
+
full_word = ""
|
245
|
+
next
|
246
|
+
end
|
247
|
+
|
248
|
+
if Config.word_begin_chars.include?(full_word[0]) && !(full_word[1..].each_char.map{|c| Config.word_chars.include?(c)}.include?(false))
|
249
|
+
tokens.insert -2, {type: "word", word_content: full_word}
|
250
|
+
full_word = ""
|
251
|
+
next
|
252
|
+
end
|
253
|
+
|
254
|
+
raise "Math AST Error 1"
|
255
|
+
else
|
256
|
+
full_word << str[char_i]
|
257
|
+
char_i += 1
|
258
|
+
end
|
259
|
+
|
260
|
+
next
|
261
|
+
|
262
|
+
|
263
|
+
|
264
|
+
next_word = ""
|
265
|
+
word_char_i = char_i.dup
|
266
|
+
|
267
|
+
while word_char_i < str.size && !Config.whitespace_chars.include?(str[word_char_i])
|
268
|
+
next_word += str[word_char_i]
|
269
|
+
word_char_i += 1
|
270
|
+
end
|
271
|
+
|
272
|
+
is_imm, imm_value = Kompiler::Parsers.check_immediate_operand(next_word)
|
273
|
+
|
274
|
+
if is_imm
|
275
|
+
tokens << {type: "number", number_content: next_word, number_value: imm_value[:value]}
|
276
|
+
char_i = word_char_i
|
277
|
+
next
|
278
|
+
end
|
279
|
+
|
280
|
+
# if Config.number_begin_chars.include?(str[char_i])
|
281
|
+
# full_number = str[char_i]
|
282
|
+
# char_i += 1
|
283
|
+
#
|
284
|
+
# while char_i < str.size && Config.number_chars.include?(str[char_i])
|
285
|
+
# full_number << str[char_i]
|
286
|
+
# char_i += 1
|
287
|
+
# end
|
288
|
+
#
|
289
|
+
# tokens << {type: "number", number_content: full_number}
|
290
|
+
#
|
291
|
+
# next
|
292
|
+
# end
|
293
|
+
|
294
|
+
|
295
|
+
if Config.word_begin_chars.include?(str[char_i])
|
296
|
+
full_word = str[char_i]
|
297
|
+
char_i += 1
|
298
|
+
|
299
|
+
while char_i < str.size && Config.word_chars.include?(str[char_i])
|
300
|
+
full_word << str[char_i]
|
301
|
+
char_i += 1
|
302
|
+
end
|
303
|
+
|
304
|
+
tokens << {type: "word", word_content: full_word}
|
305
|
+
|
306
|
+
next
|
307
|
+
end
|
308
|
+
|
309
|
+
|
310
|
+
# Here when non of the checks worked
|
311
|
+
raise "\"#{str}\" - unrecognized syntax at position #{char_i}"
|
312
|
+
end
|
313
|
+
|
314
|
+
a = 0
|
315
|
+
while a != 1
|
316
|
+
a = 1
|
317
|
+
if full_word.size > 0
|
318
|
+
is_imm, imm_value = Kompiler::Parsers.check_immediate_operand(full_word)
|
319
|
+
if is_imm
|
320
|
+
tokens.insert -1, {type: "number", number_content: full_word, number_value: imm_value[:value]}
|
321
|
+
full_word = ""
|
322
|
+
next
|
323
|
+
end
|
324
|
+
|
325
|
+
if Config.word_begin_chars.include?(full_word[0]) && !(full_word[1..].each_char.map{|c| Config.word_chars.include?(c)}.include?(false))
|
326
|
+
tokens.insert -1, {type: "word", word_content: full_word}
|
327
|
+
full_word = ""
|
328
|
+
next
|
329
|
+
end
|
330
|
+
|
331
|
+
raise "Math AST Error 2"
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
|
336
|
+
tokens
|
337
|
+
end
|
338
|
+
|
339
|
+
|
340
|
+
# A recursive function that makes blocks (bracket enclosed) into single tokens
|
341
|
+
def self.parse_blocks_from_tokens tokens
|
342
|
+
|
343
|
+
final_tokens = []
|
344
|
+
|
345
|
+
token_i = 0
|
346
|
+
|
347
|
+
while token_i < tokens.size
|
348
|
+
|
349
|
+
token = tokens[token_i]
|
350
|
+
|
351
|
+
if !(token[:type] == "sign" && ["open_bracket", "close_bracket"].include?(token[:sign_type]))
|
352
|
+
final_tokens << token
|
353
|
+
token_i += 1
|
354
|
+
next
|
355
|
+
end
|
356
|
+
|
357
|
+
if token[:sign_type] == "close_bracket"
|
358
|
+
raise "Parsing error - unexpected close bracket at token #{token_i}"
|
359
|
+
end
|
360
|
+
|
361
|
+
# Set up a bracket count that counts the bracket level (zero means 'absolute' / ground level)
|
362
|
+
bracket_count = 1
|
363
|
+
block_end_i = token_i + 1
|
364
|
+
|
365
|
+
while block_end_i < tokens.size && bracket_count != 0
|
366
|
+
if tokens[block_end_i][:type] != "sign"
|
367
|
+
block_end_i += 1
|
368
|
+
next
|
369
|
+
end
|
370
|
+
|
371
|
+
case tokens[block_end_i][:sign_type]
|
372
|
+
when "open_bracket"
|
373
|
+
bracket_count += 1
|
374
|
+
when "close_bracket"
|
375
|
+
bracket_count -= 1
|
376
|
+
end
|
377
|
+
|
378
|
+
block_end_i += 1
|
379
|
+
end
|
380
|
+
|
381
|
+
raise "Parsing error - Bracket amount does not match" if bracket_count != 0
|
382
|
+
|
383
|
+
block_tokens = tokens[(token_i + 1)...(block_end_i - 1)]
|
384
|
+
|
385
|
+
parsed_block_tokens = parse_blocks_from_tokens(block_tokens)
|
386
|
+
parsed_block_tokens = parse_functions_from_tokens(parsed_block_tokens)
|
387
|
+
|
388
|
+
final_tokens << {type: "block", content: parsed_block_tokens}
|
389
|
+
|
390
|
+
token_i = block_end_i
|
391
|
+
end
|
392
|
+
|
393
|
+
final_tokens
|
394
|
+
|
395
|
+
end
|
396
|
+
|
397
|
+
|
398
|
+
def self.parse_functions_from_tokens tokens
|
399
|
+
|
400
|
+
final_tokens = []
|
401
|
+
|
402
|
+
token_i = 0
|
403
|
+
|
404
|
+
while token_i < (tokens.size - 1)
|
405
|
+
token = tokens[token_i]
|
406
|
+
|
407
|
+
if !(token[:type] == "word" && tokens[token_i + 1][:type] == "block")
|
408
|
+
token_i += 1
|
409
|
+
final_tokens << token
|
410
|
+
next
|
411
|
+
end
|
412
|
+
|
413
|
+
final_tokens << {type: "func", func_name: token[:word_content], func_arg_block: tokens[token_i + 1]}
|
414
|
+
token_i += 2
|
415
|
+
end
|
416
|
+
|
417
|
+
final_tokens << tokens.last
|
418
|
+
|
419
|
+
final_tokens
|
420
|
+
end
|
421
|
+
|
422
|
+
|
423
|
+
def self.tokens_to_ast tokens
|
424
|
+
|
425
|
+
|
426
|
+
# Swap words and numbers for operations of type word and number
|
427
|
+
token_i = 0
|
428
|
+
|
429
|
+
while token_i < tokens.size
|
430
|
+
token = tokens[token_i]
|
431
|
+
|
432
|
+
if !["word", "number", "block", "string", "func"].include?(token[:type])
|
433
|
+
token_i += 1
|
434
|
+
next
|
435
|
+
end
|
436
|
+
|
437
|
+
case token[:type]
|
438
|
+
when "word"
|
439
|
+
tokens[token_i] = {type: "operation", op_type: "word", elements: [token[:word_content]]}
|
440
|
+
when "number"
|
441
|
+
tokens[token_i] = {type: "operation", op_type: "number", elements: [token[:number_content]]}
|
442
|
+
when "string"
|
443
|
+
tokens[token_i] = {type: "operation", op_type: "string", elements: [token[:str_content]]}
|
444
|
+
when "block"
|
445
|
+
tokens[token_i] = tokens_to_ast(token[:content])
|
446
|
+
when "func"
|
447
|
+
tokens[token_i] = {type: "operation", op_type: "func", elements: [token[:func_name], tokens_to_ast(token[:func_arg_block][:content])]}
|
448
|
+
end
|
449
|
+
|
450
|
+
token_i += 1
|
451
|
+
end
|
452
|
+
|
453
|
+
# Check for negation operations of type "-x"
|
454
|
+
|
455
|
+
one_element_ast_ops = Config.one_element_ast_operations
|
456
|
+
|
457
|
+
one_element_ast_ops.each do |operation|
|
458
|
+
|
459
|
+
if operation[:check_direction] == -1
|
460
|
+
token_i = tokens.size - 1
|
461
|
+
token_i_change = -1
|
462
|
+
check_condition = -> {token_i >= 0}
|
463
|
+
check_boundary = 0
|
464
|
+
elsif operation[:check_direction] == 1
|
465
|
+
token_i = 0
|
466
|
+
token_i_change = 1
|
467
|
+
check_condition = -> {token_i < tokens.size}
|
468
|
+
check_boundary = tokens.size - 1
|
469
|
+
end
|
470
|
+
|
471
|
+
while check_condition.call
|
472
|
+
token = tokens[token_i]
|
473
|
+
|
474
|
+
if token[:type] != "sign"
|
475
|
+
token_i += token_i_change
|
476
|
+
next
|
477
|
+
end
|
478
|
+
|
479
|
+
if token[:sign_type] != operation[:sign_type]
|
480
|
+
token_i += token_i_change
|
481
|
+
next
|
482
|
+
end
|
483
|
+
|
484
|
+
# Check if this is the first token (and a minus sign), which means "[-]x"
|
485
|
+
# Or check if this token is preceded by another sign, e.g. "+[-]x"
|
486
|
+
if token_i == check_boundary || ["sign"].include?(tokens[token_i + token_i_change][:type])
|
487
|
+
ast_node = {type: "operation", op_type: operation[:name], elements: [tokens[token_i - token_i_change]]}
|
488
|
+
if token_i_change == -1
|
489
|
+
tokens = tokens[...token_i] + [ast_node] + tokens[(token_i + 1 + 1)..]
|
490
|
+
elsif token_i_change == 1
|
491
|
+
tokens = tokens[...(token_i - 1)] + [ast_node] + tokens[(token_i + 1)..]
|
492
|
+
end
|
493
|
+
check_boundary -= token_i_change
|
494
|
+
next
|
495
|
+
end
|
496
|
+
|
497
|
+
token_i += token_i_change
|
498
|
+
end
|
499
|
+
|
500
|
+
end
|
501
|
+
|
502
|
+
|
503
|
+
# Math AST operations sorted in priority order
|
504
|
+
two_element_ast_ops = Config.two_element_ast_operations
|
505
|
+
|
506
|
+
two_element_ast_ops.each do |operations_group|
|
507
|
+
|
508
|
+
if operations_group[:group_check_direction] == -1
|
509
|
+
token_i = tokens.size - 1
|
510
|
+
token_i_change = -1
|
511
|
+
check_condition = -> {token_i >= 0}
|
512
|
+
elsif operations_group[:group_check_direction] == 1
|
513
|
+
token_i = 0
|
514
|
+
token_i_change = 1
|
515
|
+
check_condition = -> {token_i < tokens.size}
|
516
|
+
end
|
517
|
+
|
518
|
+
while check_condition.call
|
519
|
+
token = tokens[token_i]
|
520
|
+
|
521
|
+
if token[:type] != "sign"
|
522
|
+
token_i += token_i_change
|
523
|
+
next
|
524
|
+
end
|
525
|
+
|
526
|
+
operation_found = false
|
527
|
+
|
528
|
+
operations_group[:group_operations].each do |operation|
|
529
|
+
if token[:sign_type] != operation[:sign_type]
|
530
|
+
next
|
531
|
+
end
|
532
|
+
|
533
|
+
elements = [tokens[token_i - 1], tokens[token_i + 1]]
|
534
|
+
|
535
|
+
# Check if there are some non-operation elements, which shouldn't happen
|
536
|
+
raise "Parsing error - something went wrong, compute elements were not operations" if elements.filter{|e| e[:type] != "operation"}.size > 0
|
537
|
+
|
538
|
+
operation_found = true
|
539
|
+
|
540
|
+
ast_node = {type: "operation", op_type: operation[:name], elements: }
|
541
|
+
|
542
|
+
tokens = tokens[...(token_i - 1)] + [ast_node] + tokens[(token_i + 1 + 1)..]
|
543
|
+
|
544
|
+
# token_i += token_i_change
|
545
|
+
|
546
|
+
break
|
547
|
+
end
|
548
|
+
|
549
|
+
if !operation_found
|
550
|
+
token_i += token_i_change
|
551
|
+
next
|
552
|
+
end
|
553
|
+
|
554
|
+
# if token[:sign_type] != operation[:sign_type]
|
555
|
+
# token_i += token_i_change
|
556
|
+
# next
|
557
|
+
# end
|
558
|
+
#
|
559
|
+
# elements = [tokens[token_i - 1], tokens[token_i + 1]]
|
560
|
+
#
|
561
|
+
# # Check if there are some non-operation elements, which shouldn't happen
|
562
|
+
# raise "Parsing error - something went wrong, compute elements were not operations" if elements.filter{|e| e[:type] != "operation"}.size > 0
|
563
|
+
#
|
564
|
+
# ast_node = {type: "operation", op_type: operation[:name], elements: }
|
565
|
+
#
|
566
|
+
# tokens = tokens[...(token_i - 1)] + [ast_node] + tokens[(token_i + 1 + 1)..]
|
567
|
+
#
|
568
|
+
# token_i += token_i_change
|
569
|
+
end
|
570
|
+
|
571
|
+
end
|
572
|
+
|
573
|
+
|
574
|
+
raise "Parsing error - something went wrong, tokens should've collapsed into a single AST, but didn't :(" if tokens.size != 1
|
575
|
+
|
576
|
+
tokens[0]
|
577
|
+
end
|
578
|
+
|
579
|
+
def self.token_ast_to_ast(token_ast)
|
580
|
+
final_ast = Hash.new
|
581
|
+
|
582
|
+
final_ast[:type] = token_ast[:op_type]
|
583
|
+
|
584
|
+
elements = token_ast[:elements]
|
585
|
+
|
586
|
+
elements.map! do |el|
|
587
|
+
if el.is_a?(Hash) && el.keys.include?(:type) && el[:type] == "operation"
|
588
|
+
el = token_ast_to_ast(el)
|
589
|
+
end
|
590
|
+
el
|
591
|
+
end
|
592
|
+
|
593
|
+
final_ast[:elements] = elements
|
594
|
+
|
595
|
+
final_ast
|
596
|
+
end
|
597
|
+
|
598
|
+
|
599
|
+
def self.str_to_ast str
|
600
|
+
tokens = str_to_tokens(str)
|
601
|
+
|
602
|
+
tokens = parse_blocks_from_tokens(tokens)
|
603
|
+
|
604
|
+
tokens = parse_functions_from_tokens(tokens) if Config.parse_functions
|
605
|
+
|
606
|
+
token_ast = tokens_to_ast(tokens)
|
607
|
+
|
608
|
+
ast = token_ast_to_ast(token_ast)
|
609
|
+
|
610
|
+
ast
|
611
|
+
end
|
612
|
+
|
613
|
+
# Strange looking thing to create an alias for str_to_ast
|
614
|
+
class <<self
|
615
|
+
alias_method :parse, :str_to_ast
|
616
|
+
end
|
617
|
+
|
618
|
+
|
619
|
+
def self.run_ast ast, words=Hash.new, functions=Hash.new
|
620
|
+
|
621
|
+
# p ast
|
622
|
+
|
623
|
+
case ast[:type]
|
624
|
+
when "word"
|
625
|
+
return words[ast[:elements][0]]
|
626
|
+
when "number"
|
627
|
+
# if ast[:elements][0].include?(".")
|
628
|
+
# return ast[:elements][0].to_f
|
629
|
+
# else
|
630
|
+
# return ast[:elements][0].to_i
|
631
|
+
# end
|
632
|
+
is_num, imm_value = Kompiler::Parsers.check_immediate_operand(ast[:elements][0])
|
633
|
+
raise "AST recognition error - \"#{ast[:elements][0]}\" is not a number" if !is_num
|
634
|
+
|
635
|
+
return imm_value[:value]
|
636
|
+
when "string"
|
637
|
+
return ast[:elements][0]
|
638
|
+
when "func"
|
639
|
+
func_name = ast[:elements][0]
|
640
|
+
return Config.functions[func_name].call(run_ast(ast[:elements][1]))
|
641
|
+
when "add"
|
642
|
+
return run_ast(ast[:elements][0], words, functions) + run_ast(ast[:elements][1], words, functions)
|
643
|
+
when "sub"
|
644
|
+
return run_ast(ast[:elements][0], words, functions) - run_ast(ast[:elements][1], words, functions)
|
645
|
+
when "mul"
|
646
|
+
return run_ast(ast[:elements][0], words, functions) * run_ast(ast[:elements][1], words, functions)
|
647
|
+
when "div"
|
648
|
+
return run_ast(ast[:elements][0], words, functions) / run_ast(ast[:elements][1], words, functions)
|
649
|
+
when "power"
|
650
|
+
return run_ast(ast[:elements][0], words, functions) ** run_ast(ast[:elements][1], words, functions)
|
651
|
+
when "negate"
|
652
|
+
return -run_ast(ast[:elements][0], words, functions)
|
653
|
+
when "bitshift_left"
|
654
|
+
return run_ast(ast[:elements][0], words, functions) << run_ast(ast[:elements][1], words, functions)
|
655
|
+
when "bitshift_right"
|
656
|
+
return run_ast(ast[:elements][0], words, functions) >> run_ast(ast[:elements][1], words, functions)
|
657
|
+
when "bit_or"
|
658
|
+
return run_ast(ast[:elements][0], words, functions) | run_ast(ast[:elements][1], words, functions)
|
659
|
+
when "bit_and"
|
660
|
+
return run_ast(ast[:elements][0], words, functions) & run_ast(ast[:elements][1], words, functions)
|
661
|
+
when "factorial"
|
662
|
+
res = 1
|
663
|
+
lim = run_ast(ast[:elements][0], words, functions)
|
664
|
+
(1..lim).each do |n|
|
665
|
+
res *= n
|
666
|
+
end
|
667
|
+
return res
|
668
|
+
when "modulo"
|
669
|
+
return run_ast(ast[:elements][0], words, functions) % run_ast(ast[:elements][1], words, functions)
|
670
|
+
|
671
|
+
when "equal"
|
672
|
+
return (run_ast(ast[:elements][0], words, functions) == run_ast(ast[:elements][1], words, functions)) ? 1 : 0
|
673
|
+
when "not_equal"
|
674
|
+
return (run_ast(ast[:elements][0], words, functions) != run_ast(ast[:elements][1], words, functions)) ? 1 : 0
|
675
|
+
when "less_than"
|
676
|
+
return (run_ast(ast[:elements][0], words, functions) < run_ast(ast[:elements][1], words, functions)) ? 1 : 0
|
677
|
+
when "greater_than"
|
678
|
+
return (run_ast(ast[:elements][0], words, functions) > run_ast(ast[:elements][1], words, functions)) ? 1 : 0
|
679
|
+
when "less_or_eq"
|
680
|
+
return (run_ast(ast[:elements][0], words, functions) <= run_ast(ast[:elements][1], words, functions)) ? 1 : 0
|
681
|
+
when "greater_or_eq"
|
682
|
+
return (run_ast(ast[:elements][0], words, functions) >= run_ast(ast[:elements][1], words, functions)) ? 1 : 0
|
683
|
+
end
|
684
|
+
|
685
|
+
|
686
|
+
end
|
687
|
+
|
688
|
+
|
689
|
+
end # Kompiler::Parsers::SymAST
|
690
|
+
|
691
|
+
end # Kompiler::Parsers
|
692
|
+
|
693
|
+
end # Kompiler
|