kompiler 0.3.2 → 0.3.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 12bbbb642809ac234ac1cfdcd59b3d43362c0ba284a6286656fecc2cf5024e99
4
- data.tar.gz: 86e653003489923b7a63bd24b0d6019b07fde08c10c8dc426357dca265b3445e
3
+ metadata.gz: f5938bafe84031bb6dfb99ab6452d66fb1da892a42520e5e05c274e2b89f0b73
4
+ data.tar.gz: 8d80c58a6d2b689fbd3bfcd69f780cb2aa258c0f02d5b905c0b16b7d1b2c8a12
5
5
  SHA512:
6
- metadata.gz: e729dfc9ee22702200ea082beef419bdbcbaa6bc2ba5a4cadbc2899f6442c6f65c163ec90675ce8d8e2fe41cd80e97cee445fb7386547e78f5569016bbba69ed
7
- data.tar.gz: d88ba4f53b56086cae6e63f8308058f3f28735f8db671d122c3eecbd3270e5443a1945407b7e8e73b15859758897992f2183481f667282fea29ee57f47f2b494
6
+ metadata.gz: e79f4af903656d29d7d2a4b9b5b1c1e75f7bc10461d4a1e203d68b98c70420d920e13e651e263469a679e361349e7b4106b519a3712efa5185d5124cbe71c1ed
7
+ data.tar.gz: b8065b466b2b5bca9234b7ba8fd00a5560914cc819cfa9c973884f5c489eed1d797d1eb1d633a47570629133c616caedff3ce6743d69502c7069715d1c0ae1ec
data/bin/kompile CHANGED
@@ -15,7 +15,7 @@
15
15
  # limitations under the License.
16
16
 
17
17
  require 'kompiler'
18
-
18
+ require 'fileutils'
19
19
 
20
20
  arg_string = ARGV.join(" ")
21
21
 
@@ -108,20 +108,27 @@ Available wrapping formats:
108
108
  elf.obj
109
109
  elf.exec
110
110
  mach-o.obj
111
- mach-o.exec [not implemented yet]
112
-
111
+ mach-o.exec
112
+
113
113
  Additional options for wrapping are:
114
114
  --elf-machine=<type> Specifies ELF header's e_machine to be the type provided (default is 0)
115
115
  --elf-class=<class> Specifies the ELF file's class to either 32 or 64 (default is 64)
116
116
  --mach-o-machine=<cputype.subtype> Specifies Mach-O header's cputype and subtype
117
117
  to be the type provided
118
118
  --mach-o-archtype=<type> Specifies the file architecture type to either 32 or 64 (default is 64)
119
+ --exec-type=<type> Used with --wrap=mach-o.exec. Specifies whether the executable is
120
+ statically (static) or dynamically (dylink) linked (default is dylink)
121
+ --mach-o-threadstate=<type> Used with --wrap=mach-o.exec and --exec-type=static. Specifies which
122
+ thread state type to use (arm64, arm32, x86-64 or x86-32)
123
+ --codesign=<bool> Used with --wrap=mach-o.*. Specifies whether to add a basic
124
+ code signature to the Mach-O file (default is false)
119
125
 
120
126
  Available options:
121
127
  --help, -h Prints this information
122
128
  --list-architectures Lists available architectures
123
129
  --list-instructions [arch] Lists available instructions for the specified architecture
124
130
  --list-registers [arch] Lists available registers for the specified architecture
131
+ --list-lexis Lists available lexis entries
125
132
  """
126
133
  exit # Exit
127
134
  end
@@ -141,14 +148,18 @@ end
141
148
  if arg_opts == ["list-instructions"]
142
149
  arch_name = arg_words[0] || "armv8a"
143
150
  Kompiler::ArchManager.load_all_entries()
144
- arch = Kompiler::ArchManager.get_arch(arch_name)
151
+ Kompiler::ArchManager.load_arch arch_name
145
152
 
146
- require arch[:include_path]
153
+ Kompiler::Architecture.instructions.each do |instruction|
154
+ print instruction[:keyword]
155
+
156
+ print " "
157
+
158
+ puts instruction[:operands].map{"<" + (_1[:name] || _1[:type] || "") + ">"}.join(" ")
159
+
160
+ puts instruction[:description]
147
161
 
148
- Kompiler::Architecture.instructions.each do |instr|
149
- puts "#{instr[:keyword]} (#{instr[:operands].size} operands): #{instr[:name]}"
150
- puts instr[:description]
151
- puts
162
+ print "\n"
152
163
  end
153
164
 
154
165
  exit
@@ -235,6 +246,7 @@ else
235
246
  arch_name = "armv8a"
236
247
  end
237
248
 
249
+
238
250
  # Load all the architecture entries
239
251
 
240
252
  Kompiler::ArchManager.load_all_entries()
@@ -272,44 +284,139 @@ labels = detailed_out[:labels]
272
284
  labels.delete "here"
273
285
 
274
286
 
287
+ def get_number str
288
+ bool, val = Kompiler::Parsers.check_immediate_operand(str)
289
+ return (bool == false) ? nil : val
290
+ end
291
+
292
+
293
+ def get_arg_key arg_keys, key, default_value=0
294
+ filtered = arg_keys.filter{_1[0] == key}[0]
295
+ value = (filtered != nil) ? filtered[1] : default_value
296
+ return value
297
+ end
298
+
299
+
300
+ class String
301
+ def to_num
302
+ status, val = Kompiler::Parsers.check_immediate_operand(self)
303
+ return (status == false) ? nil : val[:value]
304
+ end
305
+ end
306
+
307
+
308
+ add_exec_permission = false
309
+
310
+
275
311
  case wrap_opt
276
312
  when "none"
277
313
  out = code
278
314
  when "elf.obj"
279
- elf_machine = arg_keys.filter{_1[0] == "elf-machine"}[0]
280
- elf_machine = (elf_machine != nil) ? elf_machine[1].to_i : 0
281
-
282
- elf_class = arg_keys.filter{_1[0] == "elf-class"}[0]
283
- elf_class ||= ["elf-class", "64"]
284
- elf_class = elf_class[1].to_i
315
+ elf_machine = get_arg_key(arg_keys, "elf-machine", "0").to_num
316
+ elf_class = get_arg_key(arg_keys, "elf-class", "64").to_num
317
+
318
+ if ![32, 64].include?(elf_class)
319
+ puts "kompile: Invalid ELF class specified."
320
+ puts "Type \"kompile --help\" for more information."
321
+ exit
322
+ end
285
323
 
286
324
  symbols = Kompiler::Wrappers::ELF.labels_to_symbols(labels)
287
325
  out = Kompiler::Wrappers::ELF.wrap_obj(code, symbols, machine: elf_machine, elf_class: elf_class)
288
326
  when "elf.exec"
289
- elf_machine = arg_keys.filter{_1[0] == "elf-machine"}[0]
290
- elf_machine = (elf_machine != nil) ? elf_machine[1].to_i : 0
327
+ vaddr = 0x80000
291
328
 
292
- elf_class = arg_keys.filter{_1[0] == "elf-class"}[0]
293
- elf_class ||= ["elf-class", "64"]
294
- elf_class = elf_class[1].to_i
329
+ elf_machine = get_arg_key(arg_keys, "elf-machine", "0").to_num
330
+ elf_class = get_arg_key(arg_keys, "elf-class", "64").to_num
295
331
 
296
- symbols = Kompiler::Wrappers::ELF.labels_to_symbols(labels)
297
- out = Kompiler::Wrappers::ELF.wrap_exec(code, symbols, machine: elf_machine, elf_class: elf_class)
332
+ if ![32, 64].include?(elf_class)
333
+ puts "kompile: Invalid ELF class specified."
334
+ puts "Type \"kompile --help\" for more information."
335
+ exit
336
+ end
337
+
338
+
339
+ symbols = Kompiler::Wrappers::ELF.labels_to_symbols(labels, vaddr: vaddr)
340
+ out = Kompiler::Wrappers::ELF.wrap_exec(code, symbols, machine: elf_machine, elf_class: elf_class, vaddr: vaddr)
341
+
342
+ add_exec_permission = true
343
+
298
344
  when "mach-o.obj"
299
- macho_cpu = arg_keys.filter{_1[0] == "mach-o-machine"}[0]
300
- macho_cpu = (macho_cpu != nil) ? macho_cpu[1] : "0.0"
301
- cputype, cpusubtype = macho_cpu.split(".").map(&:to_i)
345
+ macho_cpu = get_arg_key(arg_keys, "mach-o-machine", "0.0")
346
+ cputype, cpusubtype = macho_cpu.split(".").map(&:to_num)
347
+
348
+ if [cputype, cpusubtype].include? nil
349
+ puts "kompile: Invalid Mach-O machine specified."
350
+ puts "Type \"kompile --help\" for more information."
351
+ exit
352
+ end
302
353
 
303
- arch_type = arg_keys.filter{_1[0] == "mach-o-archtype"}[0]
304
- arch_type = (arch_type != nil) ? arch_type[1].to_i : 64
354
+ arch_type = get_arg_key(arg_keys, "mach-o-archtype", "64").to_num
355
+
356
+ codesign = get_arg_key(arg_keys, "codesign", "false")
357
+ if !["true", "false"].include?(codesign)
358
+ puts "kompile: Invalid --codesign value."
359
+ puts "Type \"kompile --help\" for more information."
360
+ exit
361
+ end
362
+
363
+ codesign = (codesign == "true")
305
364
 
306
365
  symbols = Kompiler::Wrappers::MachO.labels_to_symbols(labels)
307
366
  out = Kompiler::Wrappers::MachO.wrap_obj(code, symbols, cputype: cputype, cpusubtype: cpusubtype, arch_type: arch_type)
308
367
  when "mach-o.exec"
309
- puts "Mach-O executable not yet implemented."
310
- exit
368
+ macho_cpu = get_arg_key(arg_keys, "mach-o-machine", "0.0")
369
+ cputype, cpusubtype = macho_cpu.split(".").map(&:to_num)
370
+
371
+ if [cputype, cpusubtype].include? nil
372
+ puts "kompile: Invalid Mach-O machine specified."
373
+ puts "Type \"kompile --help\" for more information."
374
+ exit
375
+ end
376
+
377
+ arch_type = get_arg_key(arg_keys, "mach-o-archtype", "64").to_num
378
+
379
+ exec_type = get_arg_key(arg_keys, "exec-type", "dylink")
380
+
381
+
382
+ codesign = get_arg_key(arg_keys, "codesign", "false")
383
+ if !["true", "false"].include?(codesign)
384
+ puts "kompile: Invalid --codesign value."
385
+ puts "Type \"kompile --help\" for more information."
386
+ exit
387
+ end
388
+
389
+ codesign = (codesign == "true")
390
+
391
+
392
+ case exec_type
393
+ when "dylink"
394
+ symbols = Kompiler::Wrappers::MachO.labels_to_symbols(labels)
395
+ out = Kompiler::Wrappers::MachO.wrap_exec_dylink(code, symbols, cputype: cputype, cpusubtype: cpusubtype, arch_type: arch_type, codesign: codesign)
396
+ when "static"
397
+ symbols = Kompiler::Wrappers::MachO.labels_to_symbols(labels)
398
+
399
+ thread_state_arch = get_arg_key(arg_keys, "mach-o-threadstate", "arm64")
400
+
401
+ entry_address = 0x1000000
402
+
403
+ thread_state = Kompiler::Wrappers::MachO.build_thread_state arch: thread_state_arch, entry_address: 0x1000000, stack_pointer: 0
404
+
405
+ out = Kompiler::Wrappers::MachO.wrap_exec_static(code, symbols, cputype: cputype, cpusubtype: cpusubtype, arch_type: arch_type, thread_state: thread_state, codesign: codesign, virtual_entry_address: entry_address)
406
+
407
+ else
408
+ puts "kompile: Invalid Mach-O executable type specified."
409
+ puts "Type \"kompile --help\" for more information."
410
+ exit
411
+ end
412
+
413
+ add_exec_permission = true
311
414
  end
312
415
 
313
416
 
314
417
  File.binwrite out_filename, out
315
418
 
419
+ if add_exec_permission
420
+ FileUtils.chmod "+x", out_filename
421
+ end
422
+
@@ -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 ['"', "'"].include? line[start_i]
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] += 1
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
- puts "Incorrect use of the \".info\" directive."
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
  }
@@ -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