kompiler 0.3.2 → 0.3.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/bin/kompile +373 -73
- data/lib/kompiler/alias_manager.rb +160 -0
- data/lib/kompiler/architectures/armv8a/instructions.rb +42 -0
- data/lib/kompiler/config/aliases +0 -0
- data/lib/kompiler/directives.rb +361 -5
- data/lib/kompiler/math_ast.rb +2 -2
- data/lib/kompiler/parsers.rb +1 -1
- data/lib/kompiler/wrappers/elf_wrapper.rb +2 -2
- data/lib/kompiler/wrappers/macho_wrapper.rb +889 -56
- data/lib/kompiler/wrappers/packed_bytes.rb +18 -4
- data/lib/kompiler.rb +1 -0
- metadata +5 -3
@@ -0,0 +1,160 @@
|
|
1
|
+
|
2
|
+
module Kompiler
|
3
|
+
|
4
|
+
module AliasManager
|
5
|
+
|
6
|
+
@aliases = []
|
7
|
+
|
8
|
+
def self.aliases
|
9
|
+
@aliases
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.default_alias_file_path()
|
13
|
+
ENV["KOMPILER_ALIASES_FILE_PATH"] || Gem.find_files("kompiler/config/aliases")[0]
|
14
|
+
end
|
15
|
+
|
16
|
+
# Load aliases from the config file in Kompiler
|
17
|
+
def self.load_aliases()
|
18
|
+
file_path = AliasManager.default_alias_file_path()
|
19
|
+
AliasManager.import_aliases_file(file_path)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Save aliases to the config file in Kompiler
|
23
|
+
def self.save_aliases()
|
24
|
+
file_path = AliasManager.default_alias_file_path()
|
25
|
+
AliasManager.export_aliases_file(file_path)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.reset_aliases()
|
29
|
+
@aliases = []
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
|
33
|
+
# Apply aliases to the instruction set
|
34
|
+
def self.apply_aliases()
|
35
|
+
|
36
|
+
@aliases.each do |alias_entry|
|
37
|
+
|
38
|
+
instr_index = alias_entry[:index] - 1
|
39
|
+
instr_keyword = alias_entry[:keyword]
|
40
|
+
|
41
|
+
instruction = Kompiler::Architecture.instructions[instr_index]
|
42
|
+
|
43
|
+
# Something is not right in the alias config, so just skip
|
44
|
+
next if instruction[:keyword] != instr_keyword
|
45
|
+
|
46
|
+
instruction[:aliases] = alias_entry[:aliases]
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
return true
|
51
|
+
end
|
52
|
+
|
53
|
+
# Import aliases from a string
|
54
|
+
def self.import_aliases(aliases_content)
|
55
|
+
lines = Kompiler::Parsers.get_code_lines(aliases_content)
|
56
|
+
|
57
|
+
line_words = []
|
58
|
+
|
59
|
+
@aliases = []
|
60
|
+
|
61
|
+
lines.each_with_index do |line, line_i|
|
62
|
+
|
63
|
+
words = []
|
64
|
+
|
65
|
+
curr_i = 0
|
66
|
+
max_i = line.bytesize
|
67
|
+
|
68
|
+
while curr_i < max_i
|
69
|
+
|
70
|
+
while curr_i < max_i && Kompiler::Config.whitespace_chars.include?(line[curr_i])
|
71
|
+
curr_i += 1
|
72
|
+
end
|
73
|
+
|
74
|
+
word = ""
|
75
|
+
|
76
|
+
while curr_i < max_i && !Kompiler::Config.whitespace_chars.include?(line[curr_i])
|
77
|
+
word << line[curr_i]
|
78
|
+
curr_i += 1
|
79
|
+
end
|
80
|
+
|
81
|
+
words << word
|
82
|
+
end
|
83
|
+
|
84
|
+
instr_index = words[0].to_i
|
85
|
+
instr_keyword = words[1]
|
86
|
+
instr_aliases = words[2..]
|
87
|
+
|
88
|
+
@aliases << {index: instr_index, keyword: instr_keyword, aliases: instr_aliases}
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
@aliases.size
|
93
|
+
end
|
94
|
+
|
95
|
+
# Import aliases from a file
|
96
|
+
def self.import_aliases_file(filename)
|
97
|
+
content = File.binread(filename)
|
98
|
+
AliasManager.import_aliases(content)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Export aliases to a string
|
102
|
+
def self.export_aliases()
|
103
|
+
separator = Kompiler::Config.whitespace_chars[0]
|
104
|
+
|
105
|
+
output = ""
|
106
|
+
|
107
|
+
@aliases.each do |instr_alias|
|
108
|
+
line = ([instr_alias[:index], instr_alias[:keyword]] + instr_alias[:aliases]).join(separator)
|
109
|
+
|
110
|
+
output << line
|
111
|
+
output << "\n"
|
112
|
+
end
|
113
|
+
|
114
|
+
output
|
115
|
+
end
|
116
|
+
|
117
|
+
# Export aliases to a file
|
118
|
+
def self.export_aliases_file(filename)
|
119
|
+
content = AliasManager.export_aliases()
|
120
|
+
File.binwrite filename, content
|
121
|
+
end
|
122
|
+
|
123
|
+
# Add alias
|
124
|
+
def self.add_alias(index, keyword, *aliases)
|
125
|
+
alias_entry, entry_index = @aliases.each_with_index.filter{|entry, idx| entry[:index] == index && entry[:keyword] == keyword }[0]
|
126
|
+
|
127
|
+
if alias_entry == nil
|
128
|
+
alias_entry = {index: index, keyword: keyword, aliases: []}
|
129
|
+
entry_index = @aliases.size
|
130
|
+
end
|
131
|
+
|
132
|
+
alias_entry[:aliases] += aliases
|
133
|
+
|
134
|
+
@aliases[entry_index] = alias_entry
|
135
|
+
|
136
|
+
return true
|
137
|
+
end
|
138
|
+
|
139
|
+
# Remove alias
|
140
|
+
def self.remove_alias(index, keyword, *aliases)
|
141
|
+
alias_entry, entry_index = @aliases.each_with_index.filter{|entry, idx| entry[:index] == index && entry[:keyword] == keyword }[0]
|
142
|
+
|
143
|
+
if alias_entry == nil
|
144
|
+
return false
|
145
|
+
end
|
146
|
+
|
147
|
+
alias_entry[:aliases] -= aliases
|
148
|
+
|
149
|
+
if alias_entry[:aliases].size == 0
|
150
|
+
@aliases.delete_at entry_index
|
151
|
+
else
|
152
|
+
@aliases[entry_index] = alias_entry
|
153
|
+
end
|
154
|
+
|
155
|
+
return true
|
156
|
+
end
|
157
|
+
|
158
|
+
end # Kompiler::AliasManager
|
159
|
+
|
160
|
+
end # Kompiler
|
@@ -22,6 +22,20 @@ end
|
|
22
22
|
],
|
23
23
|
bitsize: 32
|
24
24
|
},
|
25
|
+
{ keyword: "mov",
|
26
|
+
name: "Move (immediate, with shift)",
|
27
|
+
description: "Moves a shifted immediate value to the destination register.",
|
28
|
+
operands: [{type: "register", restrictions: {reg_type: "gpr"}, name: "Destination register"}, {type: "immediate", restrictions: {}, name: "Immediate value"}, {type: "immediate", name: "Shift amount (multiple of 16)"}],
|
29
|
+
mc_constructor: [
|
30
|
+
["if_eq_else", ["modulo", ["get_operand", 2], 16], 0, [], ["raise_error", "movk Error: Shift amount must be divisible by 16."]],
|
31
|
+
["get_bits", ["encode_gp_register", ["get_operand", 0]], 0, 5],
|
32
|
+
["get_bits", ["get_operand", 1], 0, 16],
|
33
|
+
["get_bits", ["divide", ["get_operand", 2], 16], 0, 2],
|
34
|
+
["bits", 1,0,1,0,0,1, 0,1],
|
35
|
+
["case", ["get_key", ["get_operand", 0], :reg_size], 64, ["bits", 1], 32, ["bits", 0], 0],
|
36
|
+
],
|
37
|
+
bitsize: 32
|
38
|
+
},
|
25
39
|
{ keyword: "mov",
|
26
40
|
name: "Move (register)",
|
27
41
|
description: "Copies the value in the source register to the destination register",
|
@@ -52,6 +66,34 @@ end
|
|
52
66
|
],
|
53
67
|
bitsize: 32
|
54
68
|
},
|
69
|
+
{
|
70
|
+
keyword: "movk",
|
71
|
+
name: "Move with keep (immediate)",
|
72
|
+
description: "Moves a 16-bit immediate value to the destination register, keeping other bits unchanged.",
|
73
|
+
operands: [{type: "register", restrictions: {reg_type: "gpr"}, name: "Destination register"}, {type: "immediate", restrictions: {}, name: "Immediate value"}],
|
74
|
+
mc_constructor: [
|
75
|
+
["get_bits", ["encode_gp_register", ["get_operand", 0]], 0, 5],
|
76
|
+
["get_bits", ["get_operand", 1], 0, 16],
|
77
|
+
["bits", 0,0, 1,0,1,0,0,1, 1,1],
|
78
|
+
["case", ["get_key", ["get_operand", 0], :reg_size], 64, ["bits", 1], 32, ["bits", 0], 0],
|
79
|
+
],
|
80
|
+
bitsize: 32
|
81
|
+
},
|
82
|
+
{
|
83
|
+
keyword: "movk",
|
84
|
+
name: "Move with keep (immediate, with shift)",
|
85
|
+
description: "Moves a shifted 16-bit immediate value to the destination register, keeping other bits unchanged.",
|
86
|
+
operands: [{type: "register", restrictions: {reg_type: "gpr"}, name: "Destination register"}, {type: "immediate", restrictions: {}, name: "Immediate value"}, {type: "immediate", name: "Shift amount (multiple of 16)"}],
|
87
|
+
mc_constructor: [
|
88
|
+
["if_eq_else", ["modulo", ["get_operand", 2], 16], 0, [], ["raise_error", "movk Error: Shift amount must be divisible by 16."]],
|
89
|
+
["get_bits", ["encode_gp_register", ["get_operand", 0]], 0, 5],
|
90
|
+
["get_bits", ["get_operand", 1], 0, 16],
|
91
|
+
["get_bits", ["divide", ["get_operand", 2], 16], 0, 2],
|
92
|
+
["bits", 1,0,1,0,0,1, 1,1],
|
93
|
+
["case", ["get_key", ["get_operand", 0], :reg_size], 64, ["bits", 1], 32, ["bits", 0], 0],
|
94
|
+
],
|
95
|
+
bitsize: 32
|
96
|
+
},
|
55
97
|
{ keyword: "mvn", # MVN writes the bitwise opposite of the source register to a destination register
|
56
98
|
name: "Move inverse",
|
57
99
|
description: "Writes the bitwise opposite of the source register value to the destination register.",
|
File without changes
|
data/lib/kompiler/directives.rb
CHANGED
@@ -13,6 +13,8 @@
|
|
13
13
|
# If false, operands will be a raw string containing the string after the keyword.
|
14
14
|
#
|
15
15
|
|
16
|
+
require 'securerandom'
|
17
|
+
|
16
18
|
|
17
19
|
module Kompiler
|
18
20
|
|
@@ -250,7 +252,7 @@ end
|
|
250
252
|
state[:line_i] += 1
|
251
253
|
|
252
254
|
# Insert the lines at the correct place
|
253
|
-
state[:lines] = state[:lines][0...state[:line_i]] + total_load_lines + state[:lines][state[:line_i]..]
|
255
|
+
state[:lines] = state[:lines][0...state[:line_i]] + total_load_lines + state[:lines][state[:line_i]..]
|
254
256
|
|
255
257
|
state
|
256
258
|
end
|
@@ -415,7 +417,7 @@ end
|
|
415
417
|
end
|
416
418
|
|
417
419
|
# Skip string definitions
|
418
|
-
if
|
420
|
+
if Kompiler::Config.string_delimiters.include? line[start_i]
|
419
421
|
str, parsed_length = Kompiler::Parsers.parse_str line[start_i..]
|
420
422
|
start_i += parsed_length
|
421
423
|
next
|
@@ -515,10 +517,200 @@ end
|
|
515
517
|
line_i += build_lines.size
|
516
518
|
end
|
517
519
|
|
518
|
-
state[:lines] = state[:lines][...state[:line_i]] + scan_lines
|
520
|
+
state[:lines] = state[:lines][...(state[:line_i] + def_lines.size + 2)] + scan_lines
|
519
521
|
|
520
|
-
state[:line_i] +=
|
522
|
+
state[:line_i] += def_lines.size + 2
|
523
|
+
|
524
|
+
state
|
525
|
+
end
|
526
|
+
},
|
527
|
+
{
|
528
|
+
keyword: "isomacro",
|
529
|
+
collect_operands: false,
|
530
|
+
func: lambda do |_, state|
|
531
|
+
line_i = state[:line_i]
|
532
|
+
|
533
|
+
def_line = state[:lines][line_i]
|
534
|
+
|
535
|
+
# First: collect the part after ".macro"
|
536
|
+
|
537
|
+
char_i = 0
|
538
|
+
# Skip the whitespace before .macro
|
539
|
+
while char_i < def_line.size && Kompiler::Config.whitespace_chars.include?(def_line[char_i])
|
540
|
+
char_i += 1
|
541
|
+
end
|
542
|
+
# Skip the .macro
|
543
|
+
while char_i < def_line.size && Kompiler::Config.keyword_chars.include?(def_line[char_i])
|
544
|
+
char_i += 1
|
545
|
+
end
|
546
|
+
# Skip the whitespace after .macro
|
547
|
+
while char_i < def_line.size && Kompiler::Config.whitespace_chars.include?(def_line[char_i])
|
548
|
+
char_i += 1
|
549
|
+
end
|
550
|
+
|
551
|
+
# If the end of the line was reached, throw an error
|
552
|
+
raise "Incorrect .isomacro definition" if char_i == def_line.size
|
553
|
+
|
554
|
+
# Now char_i contains the first index of the text after .macro
|
555
|
+
|
556
|
+
macro_def = def_line[char_i..]
|
557
|
+
|
558
|
+
# Second: extract the macro's name
|
559
|
+
|
560
|
+
macro_name = ""
|
561
|
+
|
562
|
+
while char_i < def_line.size && Kompiler::Config.keyword_chars.include?(def_line[char_i])
|
563
|
+
macro_name << def_line[char_i]
|
564
|
+
char_i += 1
|
565
|
+
end
|
566
|
+
|
567
|
+
# Third: extract the operand names (code taken from parse_instruction_line in parsers.rb)
|
568
|
+
|
569
|
+
arg_names = Kompiler::Parsers.extract_instruction_operands(def_line[char_i..])
|
570
|
+
|
571
|
+
# Make sure that the arg names are unique
|
572
|
+
raise "Macro definition error - arguments cannot have the same name: Program build not possible" if arg_names.size != arg_names.uniq.size
|
573
|
+
|
574
|
+
# Extract the macro inside definition
|
575
|
+
|
576
|
+
line_i = state[:line_i] + 1
|
577
|
+
def_lines = []
|
578
|
+
|
579
|
+
whitespace_regexp = /[#{Kompiler::Config.whitespace_chars.join("|")}]*/
|
580
|
+
|
581
|
+
endmacro_regexp = /\A#{whitespace_regexp}\.?endisomacro#{whitespace_regexp}\z/
|
582
|
+
|
583
|
+
while line_i < state[:lines].size
|
584
|
+
break if state[:lines][line_i].match? endmacro_regexp # Check if it's an end macro instruction
|
585
|
+
def_lines << state[:lines][line_i]
|
586
|
+
line_i += 1
|
587
|
+
end
|
588
|
+
|
589
|
+
|
590
|
+
# Find insert indexes for each argument
|
591
|
+
arg_insert_locations = arg_names.map{|arg_name| [arg_name, []]}.to_h
|
521
592
|
|
593
|
+
def_lines.each_with_index do |line, line_i|
|
594
|
+
|
595
|
+
start_i = 0
|
596
|
+
|
597
|
+
# Loop through each character starting position
|
598
|
+
while start_i < line.size
|
599
|
+
|
600
|
+
# Skip whitespace characters
|
601
|
+
if Kompiler::Config.whitespace_chars.include?(line[start_i])
|
602
|
+
start_i += 1
|
603
|
+
next
|
604
|
+
end
|
605
|
+
|
606
|
+
# Skip string definitions
|
607
|
+
if Kompiler::Config.string_delimiters.include? line[start_i]
|
608
|
+
str, parsed_length = Kompiler::Parsers.parse_str line[start_i..]
|
609
|
+
start_i += parsed_length
|
610
|
+
next
|
611
|
+
end
|
612
|
+
|
613
|
+
cut_line = line[start_i..]
|
614
|
+
|
615
|
+
arg_found = false
|
616
|
+
|
617
|
+
# Check if one of the argument names works
|
618
|
+
arg_names.each do |arg_name|
|
619
|
+
next if !cut_line.start_with?(arg_name) # Skip the argument if the line doesn't begin with it
|
620
|
+
next if Kompiler::Config.keyword_chars.include?(cut_line[arg_name.size]) # Skip if the argument is a partial word. So, for the argument 'arg', this will skip in the case of 'arg1'
|
621
|
+
# Here if the argument name should be replaced with the contents
|
622
|
+
arg_found = true # Indicate that a replacement was found
|
623
|
+
|
624
|
+
arg_insert_locations[arg_name] << [line_i, start_i] # Add the insert location to the list
|
625
|
+
|
626
|
+
# start_i += arg_name.size
|
627
|
+
def_lines[line_i] = def_lines[line_i][...start_i] + (def_lines[line_i][(start_i+arg_name.size)..] || "")
|
628
|
+
line = def_lines[line_i]
|
629
|
+
|
630
|
+
break # Skip the arguments loop
|
631
|
+
end
|
632
|
+
|
633
|
+
# Check if an argument was found
|
634
|
+
# If not, skip the text until the next whitespace character
|
635
|
+
if !arg_found
|
636
|
+
while start_i < line.size && Kompiler::Config.keyword_chars.include?(line[start_i])
|
637
|
+
start_i += 1
|
638
|
+
end
|
639
|
+
start_i += 1 # Move one more character
|
640
|
+
end
|
641
|
+
|
642
|
+
end
|
643
|
+
|
644
|
+
end
|
645
|
+
|
646
|
+
state[:extra_state][:isomacros] = Array.new if !state[:extra_state].keys.include?(:macros)
|
647
|
+
|
648
|
+
state[:extra_state][:isomacros] << {name: macro_name, args: arg_names, def_lines: def_lines, arg_insert_locations: arg_insert_locations}
|
649
|
+
|
650
|
+
|
651
|
+
# Scan the lines after the macro for the macro call and replace it with the macro definition
|
652
|
+
|
653
|
+
scan_lines = state[:lines][(state[:line_i] + def_lines.size + 1 + 1)..]
|
654
|
+
|
655
|
+
line_i = 0
|
656
|
+
|
657
|
+
# Re-group argument insert locations by line -> [index, arg index]
|
658
|
+
|
659
|
+
arg_insert_locations_regrouped = def_lines.size.times.map{[]}
|
660
|
+
|
661
|
+
arg_insert_locations.each do |arg_name, insert_locations|
|
662
|
+
insert_locations.each do |line_i, char_i|
|
663
|
+
arg_insert_locations_regrouped[line_i] << [char_i, arg_names.index(arg_name)]
|
664
|
+
end
|
665
|
+
end
|
666
|
+
|
667
|
+
|
668
|
+
while line_i < scan_lines.size
|
669
|
+
keyword, operands = Kompiler::Parsers.extract_instruction_parts(scan_lines[line_i])
|
670
|
+
|
671
|
+
# If parsing failed, move on to the next line
|
672
|
+
if keyword == false
|
673
|
+
line_i += 1
|
674
|
+
next
|
675
|
+
end
|
676
|
+
|
677
|
+
# If the keyword isn't the macro's name, skip the line
|
678
|
+
if keyword != macro_name
|
679
|
+
line_i += 1
|
680
|
+
next
|
681
|
+
end
|
682
|
+
|
683
|
+
# Here when the keyword matches the macro name
|
684
|
+
|
685
|
+
# Check that the number of operands is correct
|
686
|
+
if operands.size != arg_names.size
|
687
|
+
raise "Incorrect use of the \"#{macro_name}\" macro - #{arg_names.size} operands expected, but #{operands.size} were given: Program build not possible."
|
688
|
+
end
|
689
|
+
|
690
|
+
# Build the replacement lines for the macro call
|
691
|
+
build_lines = def_lines.map{|line| line.dup} # Copying strings inside array, because array.dup doesn't work for elements
|
692
|
+
|
693
|
+
arg_insert_locations_regrouped.each_with_index do |locations, line_i|
|
694
|
+
# Sort the locations by the insert character from largest to smallest, so that the inserts are made from end to start
|
695
|
+
locations.sort_by{|el| el[0]}.reverse.each do |char_i, arg_index|
|
696
|
+
build_lines[line_i].insert char_i, operands[arg_index]
|
697
|
+
end
|
698
|
+
end
|
699
|
+
|
700
|
+
build_lines.insert 0, ".namespace macro.#{macro_name}.#{SecureRandom.uuid.gsub('-', '')}"
|
701
|
+
build_lines << ".endnamespace"
|
702
|
+
|
703
|
+
# Replace the macro call with the built lines
|
704
|
+
scan_lines = scan_lines[...line_i] + build_lines + scan_lines[(line_i + 1)..]
|
705
|
+
|
706
|
+
# Skip the inserted macro
|
707
|
+
line_i += build_lines.size
|
708
|
+
end
|
709
|
+
|
710
|
+
state[:lines] = state[:lines][...(state[:line_i] + def_lines.size + 2)] + scan_lines
|
711
|
+
|
712
|
+
state[:line_i] += def_lines.size + 2
|
713
|
+
|
522
714
|
state
|
523
715
|
end
|
524
716
|
},
|
@@ -530,7 +722,7 @@ end
|
|
530
722
|
info_name = operands[0][:definition]
|
531
723
|
info_value = operands[1][:definition]
|
532
724
|
rescue
|
533
|
-
|
725
|
+
raise "Incorrect use of the \".info\" directive."
|
534
726
|
end
|
535
727
|
|
536
728
|
|
@@ -548,6 +740,170 @@ end
|
|
548
740
|
|
549
741
|
state[:extra_state][:info_entries][info_name] << {context_info: context_info, input_str: info_value, input_full: operands[1]}
|
550
742
|
|
743
|
+
state
|
744
|
+
end
|
745
|
+
},
|
746
|
+
{
|
747
|
+
keyword: "namespace",
|
748
|
+
func: lambda do |operands, state|
|
749
|
+
|
750
|
+
raise "Incorrect use of the \".namespace\" directive" if operands.size != 1
|
751
|
+
|
752
|
+
namespace_name = operands[0][:definition]
|
753
|
+
|
754
|
+
|
755
|
+
namespace_lines = []
|
756
|
+
|
757
|
+
line_i = state[:line_i] + 1
|
758
|
+
|
759
|
+
whitespace_regexp = /[#{Kompiler::Config.whitespace_chars.join("|")}]*/
|
760
|
+
end_namespace_regexp = /\A#{whitespace_regexp}\.?endnamespace#{whitespace_regexp}\z/
|
761
|
+
|
762
|
+
start_namespace_regexp = /\A#{whitespace_regexp}\.?namespace/
|
763
|
+
|
764
|
+
namespace_level = 1
|
765
|
+
|
766
|
+
while line_i < state[:lines].size
|
767
|
+
if state[:lines][line_i].match?(end_namespace_regexp)
|
768
|
+
namespace_level -= 1
|
769
|
+
end
|
770
|
+
break if namespace_level == 0
|
771
|
+
|
772
|
+
# Indicates whether a line should be looked at and changed based on whether it's on the first level
|
773
|
+
line_change = namespace_level == 1
|
774
|
+
|
775
|
+
namespace_lines << [line_change, state[:lines][line_i]]
|
776
|
+
|
777
|
+
if state[:lines][line_i].match?(start_namespace_regexp)
|
778
|
+
namespace_level += 1
|
779
|
+
end
|
780
|
+
|
781
|
+
line_i += 1
|
782
|
+
end
|
783
|
+
|
784
|
+
|
785
|
+
raise "\".endnamespace\" was not found for \".namespace\"" if line_i == state[:lines].size
|
786
|
+
|
787
|
+
|
788
|
+
# Collect the definitions that happen inside of the namespace
|
789
|
+
defs = []
|
790
|
+
|
791
|
+
namespace_lines.each do |line_info|
|
792
|
+
|
793
|
+
line_change, line = line_info
|
794
|
+
|
795
|
+
next if !line_change
|
796
|
+
|
797
|
+
keyword, operands = Kompiler::Parsers.extract_instruction_parts(line)
|
798
|
+
|
799
|
+
is_instruction = false
|
800
|
+
Kompiler::Architecture.instructions.each do |instr|
|
801
|
+
is_instruction = instr[:keyword] == keyword
|
802
|
+
break if is_instruction
|
803
|
+
end
|
804
|
+
|
805
|
+
next if is_instruction
|
806
|
+
|
807
|
+
keyword = "." + keyword if keyword[0] != "."
|
808
|
+
|
809
|
+
|
810
|
+
case keyword
|
811
|
+
when ".value"
|
812
|
+
value_name = operands[0]
|
813
|
+
defs << {type: "value", name: value_name}
|
814
|
+
when ".macro"
|
815
|
+
# Remove the ".macro"
|
816
|
+
macro_def = line.split(keyword)[1..].join(keyword)
|
817
|
+
macro_name, _ = Kompiler::Parsers.extract_instruction_parts(macro_def)
|
818
|
+
defs << {type: "macro", name: macro_name}
|
819
|
+
when ".label"
|
820
|
+
label_name = operands[0]
|
821
|
+
defs << {type: "label", name: label_name}
|
822
|
+
when ".namespace"
|
823
|
+
inside_namespace_name = operands[0]
|
824
|
+
defs << {type: "namespace", name: inside_namespace_name}
|
825
|
+
end
|
826
|
+
|
827
|
+
end
|
828
|
+
|
829
|
+
|
830
|
+
# Create replacements for each definition inside of the namespace
|
831
|
+
|
832
|
+
replacements = defs.map do |definition|
|
833
|
+
[definition[:name], namespace_name + "." + definition[:name]]
|
834
|
+
end.to_h
|
835
|
+
defs.each do |definition|
|
836
|
+
next if definition[:type] != "namespace"
|
837
|
+
replacements[definition[:name] + "."] = namespace_name + "." + definition[:name] + "."
|
838
|
+
end
|
839
|
+
|
840
|
+
|
841
|
+
# Replace all words in the namespace that are equal to replacement[:from] as replacement[:to]
|
842
|
+
# Logic same as .macro's
|
843
|
+
|
844
|
+
namespace_lines.each_with_index do |line_info, line_i|
|
845
|
+
|
846
|
+
line_change, line = line_info
|
847
|
+
|
848
|
+
next if !line_change
|
849
|
+
|
850
|
+
start_i = 0
|
851
|
+
|
852
|
+
# Loop through each character starting position
|
853
|
+
while start_i < line.size
|
854
|
+
|
855
|
+
# Skip whitespace characters
|
856
|
+
if Kompiler::Config.whitespace_chars.include?(line[start_i])
|
857
|
+
start_i += 1
|
858
|
+
next
|
859
|
+
end
|
860
|
+
|
861
|
+
# Skip string definitions
|
862
|
+
if Kompiler::Config.string_delimiters.include? line[start_i]
|
863
|
+
str, parsed_length = Kompiler::Parsers.parse_str line[start_i..]
|
864
|
+
start_i += parsed_length
|
865
|
+
next
|
866
|
+
end
|
867
|
+
|
868
|
+
cut_line = line[start_i..]
|
869
|
+
|
870
|
+
skip_to_next_word = true
|
871
|
+
|
872
|
+
# Check if one of the argument names works
|
873
|
+
replacements.each do |from, to|
|
874
|
+
next if !cut_line.start_with?(from) # Skip the argument if the line doesn't begin with it
|
875
|
+
next if Kompiler::Config.keyword_chars.include?(cut_line[from.size]) && from[-1] != "." # Skip if the argument is a partial word. So, for the argument 'arg', this will skip in the case of 'arg1'
|
876
|
+
# Here if the argument name should be replaced with the contents
|
877
|
+
skip_to_next_word = from[-1] == "." # Skip to the next word only if the replacement is a namespace call
|
878
|
+
|
879
|
+
new_line = line[...start_i] + to + cut_line[from.size..]
|
880
|
+
|
881
|
+
|
882
|
+
start_i += to.size
|
883
|
+
|
884
|
+
namespace_lines[line_i] = [true, new_line]
|
885
|
+
line = namespace_lines[line_i]
|
886
|
+
|
887
|
+
break # Skip the arguments loop
|
888
|
+
end
|
889
|
+
|
890
|
+
# Check if an argument was found
|
891
|
+
# If not, skip the text until the next whitespace character
|
892
|
+
if skip_to_next_word
|
893
|
+
while start_i < line.size && Kompiler::Config.keyword_chars.include?(line[start_i])
|
894
|
+
start_i += 1
|
895
|
+
end
|
896
|
+
start_i += 1 # Move one more character
|
897
|
+
end
|
898
|
+
|
899
|
+
end
|
900
|
+
|
901
|
+
end
|
902
|
+
|
903
|
+
namespace_lines.map!{_1[1]}
|
904
|
+
|
905
|
+
state[:lines] = state[:lines][...state[:line_i]] + namespace_lines + state[:lines][(state[:line_i] + namespace_lines.size + 2)..]
|
906
|
+
|
551
907
|
state
|
552
908
|
end
|
553
909
|
}
|
data/lib/kompiler/math_ast.rb
CHANGED
@@ -537,11 +537,11 @@ module SymAST
|
|
537
537
|
|
538
538
|
operation_found = true
|
539
539
|
|
540
|
-
ast_node = {type: "operation", op_type: operation[:name], elements: }
|
540
|
+
ast_node = {type: "operation", op_type: operation[:name], elements: elements}
|
541
541
|
|
542
542
|
tokens = tokens[...(token_i - 1)] + [ast_node] + tokens[(token_i + 1 + 1)..]
|
543
543
|
|
544
|
-
|
544
|
+
token_i += token_i_change
|
545
545
|
|
546
546
|
break
|
547
547
|
end
|
data/lib/kompiler/parsers.rb
CHANGED
@@ -436,7 +436,7 @@ def self.match_parsed_line_to_instruction(parsed_line, instruction)
|
|
436
436
|
keyword, operands = parsed_line
|
437
437
|
|
438
438
|
# Check if the keyword matches
|
439
|
-
if instruction[:keyword] != keyword
|
439
|
+
if instruction[:keyword] != keyword && !(instruction[:aliases] != nil && instruction[:aliases].include?(keyword))
|
440
440
|
return [false, nil]
|
441
441
|
end
|
442
442
|
|
@@ -77,10 +77,10 @@ module Kompiler
|
|
77
77
|
|
78
78
|
|
79
79
|
# Converts a label hash of name-address pairs into an array of symbols
|
80
|
-
def self.labels_to_symbols labels
|
80
|
+
def self.labels_to_symbols labels, vaddr: 0
|
81
81
|
out = []
|
82
82
|
labels.each do |name, value|
|
83
|
-
out << {name: name, value: value, type: 0, binding: 0}
|
83
|
+
out << {name: name, value: value + vaddr, type: 0, binding: 0}
|
84
84
|
end
|
85
85
|
out
|
86
86
|
end
|