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.
@@ -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
@@ -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
  }
@@ -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
- # token_i += token_i_change
544
+ token_i += token_i_change
545
545
 
546
546
  break
547
547
  end
@@ -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