kompiler 0.3.0.pre.2 → 0.3.0.pre.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/lib/kompiler/arch_manager.rb +2 -2
 - data/lib/kompiler/architectures/armv8a/instructions.rb +12 -0
 - data/lib/kompiler/compiler_functions.rb +110 -106
 - data/lib/kompiler/config.rb +29 -0
 - data/lib/kompiler/directives.rb +354 -4
 - data/lib/kompiler/math_ast.rb +665 -0
 - data/lib/kompiler/parsers.rb +124 -51
 - data/lib/kompiler.rb +3 -1
 - metadata +4 -2
 
    
        data/lib/kompiler/directives.rb
    CHANGED
    
    | 
         @@ -14,9 +14,12 @@ end 
     | 
|
| 
       14 
14 
     | 
    
         
             
            		keyword: "zeros",
         
     | 
| 
       15 
15 
     | 
    
         
             
            		func: lambda do |operands, state|
         
     | 
| 
       16 
16 
     | 
    
         
             
            			raise "Incorrect use of the \"zeros\" directive." if operands.size > 1 || (operands[0] && operands[0][:type] != "immediate")
         
     | 
| 
      
 17 
     | 
    
         
            +
            			
         
     | 
| 
       17 
18 
     | 
    
         
             
            			n_zeros = operands[0][:value]
         
     | 
| 
       18 
19 
     | 
    
         
             
            			state[:current_address] += n_zeros
         
     | 
| 
       19 
20 
     | 
    
         
             
            			state[:parsed_lines] << {type: "insert", bits: (n_zeros*8).times.map{0} }
         
     | 
| 
      
 21 
     | 
    
         
            +
            			state[:line_i] += 1
         
     | 
| 
      
 22 
     | 
    
         
            +
            			
         
     | 
| 
       20 
23 
     | 
    
         
             
            			state
         
     | 
| 
       21 
24 
     | 
    
         
             
            		end
         
     | 
| 
       22 
25 
     | 
    
         
             
            	},
         
     | 
| 
         @@ -30,6 +33,7 @@ end 
     | 
|
| 
       30 
33 
     | 
    
         | 
| 
       31 
34 
     | 
    
         
             
            			state[:parsed_lines] << {type: "insert", bits: insert_bits, address: state[:current_address]}
         
     | 
| 
       32 
35 
     | 
    
         
             
            			state[:current_address] += insert_bytes.size
         
     | 
| 
      
 36 
     | 
    
         
            +
            			state[:line_i] += 1
         
     | 
| 
       33 
37 
     | 
    
         | 
| 
       34 
38 
     | 
    
         
             
            			state
         
     | 
| 
       35 
39 
     | 
    
         
             
            		end
         
     | 
| 
         @@ -39,6 +43,8 @@ end 
     | 
|
| 
       39 
43 
     | 
    
         
             
            		func: lambda do |operands, state|
         
     | 
| 
       40 
44 
     | 
    
         
             
            			raise "Incorrect use of the \"align\" directive." if operands.size > 1 || (operands[0] && operands[0][:type] != "immediate")
         
     | 
| 
       41 
45 
     | 
    
         | 
| 
      
 46 
     | 
    
         
            +
            			state[:line_i] += 1
         
     | 
| 
      
 47 
     | 
    
         
            +
            			
         
     | 
| 
       42 
48 
     | 
    
         
             
            			alignment = operands[0][:value]
         
     | 
| 
       43 
49 
     | 
    
         
             
            			to_add = alignment - (state[:current_address] % alignment)			
         
     | 
| 
       44 
50 
     | 
    
         | 
| 
         @@ -72,6 +78,7 @@ end 
     | 
|
| 
       72 
78 
     | 
    
         | 
| 
       73 
79 
     | 
    
         
             
            			# Add the label definition
         
     | 
| 
       74 
80 
     | 
    
         
             
            			state[:parsed_lines] << {type: "label", label_name: label_name, label_address: label_value, address: state[:current_address]}
         
     | 
| 
      
 81 
     | 
    
         
            +
            			state[:line_i] += 1
         
     | 
| 
       75 
82 
     | 
    
         | 
| 
       76 
83 
     | 
    
         
             
            			state
         
     | 
| 
       77 
84 
     | 
    
         
             
            		end
         
     | 
| 
         @@ -89,6 +96,7 @@ end 
     | 
|
| 
       89 
96 
     | 
    
         
             
            			# Insert 64 bits of the value into the program
         
     | 
| 
       90 
97 
     | 
    
         
             
            			state[:current_address] += 8
         
     | 
| 
       91 
98 
     | 
    
         
             
            			state[:parsed_lines] << {type: "insert", bits: value_bits}
         
     | 
| 
      
 99 
     | 
    
         
            +
            			state[:line_i] += 1
         
     | 
| 
       92 
100 
     | 
    
         | 
| 
       93 
101 
     | 
    
         
             
            			state
         
     | 
| 
       94 
102 
     | 
    
         
             
            		end
         
     | 
| 
         @@ -106,6 +114,7 @@ end 
     | 
|
| 
       106 
114 
     | 
    
         
             
            			# Insert 64 bits of the value into the program
         
     | 
| 
       107 
115 
     | 
    
         
             
            			state[:current_address] += 4
         
     | 
| 
       108 
116 
     | 
    
         
             
            			state[:parsed_lines] << {type: "insert", bits: value_bits}
         
     | 
| 
      
 117 
     | 
    
         
            +
            			state[:line_i] += 1
         
     | 
| 
       109 
118 
     | 
    
         | 
| 
       110 
119 
     | 
    
         
             
            			state
         
     | 
| 
       111 
120 
     | 
    
         
             
            		end
         
     | 
| 
         @@ -113,8 +122,8 @@ end 
     | 
|
| 
       113 
122 
     | 
    
         
             
            	{
         
     | 
| 
       114 
123 
     | 
    
         
             
            		keyword: "bytes",
         
     | 
| 
       115 
124 
     | 
    
         
             
            		func: lambda do |operands, state|
         
     | 
| 
      
 125 
     | 
    
         
            +
            			raise "Incorrect use of the \"bytes\" directive." if (operands.size != 2) || (operands[0][:type] != "immediate" || operands[1][:type] != "immediate")			
         
     | 
| 
       116 
126 
     | 
    
         | 
| 
       117 
     | 
    
         
            -
            			raise "Incorrect use of the \"bytes\" directive." if (operands.size != 2) || (operands[0][:type] != "immediate" && operands[1][:type] != "immediate")			
         
     | 
| 
       118 
127 
     | 
    
         | 
| 
       119 
128 
     | 
    
         
             
            			n_bytes = operands[0][:value]
         
     | 
| 
       120 
129 
     | 
    
         
             
            			value = operands[1][:value]
         
     | 
| 
         @@ -124,6 +133,7 @@ end 
     | 
|
| 
       124 
133 
     | 
    
         
             
            			# Insert the input amount of bytes of the value into the program
         
     | 
| 
       125 
134 
     | 
    
         
             
            			state[:current_address] += n_bytes
         
     | 
| 
       126 
135 
     | 
    
         
             
            			state[:parsed_lines] << {type: "insert", bits: value_bits}
         
     | 
| 
      
 136 
     | 
    
         
            +
            			state[:line_i] += 1
         
     | 
| 
       127 
137 
     | 
    
         | 
| 
       128 
138 
     | 
    
         
             
            			state
         
     | 
| 
       129 
139 
     | 
    
         
             
            		end
         
     | 
| 
         @@ -132,11 +142,12 @@ end 
     | 
|
| 
       132 
142 
     | 
    
         
             
            		keyword: "set_pc",
         
     | 
| 
       133 
143 
     | 
    
         
             
            		func: lambda do |operands, state|
         
     | 
| 
       134 
144 
     | 
    
         | 
| 
       135 
     | 
    
         
            -
            			raise "Incorrect use of the \"set_pc\" directive." if (operands.size != 1) || (operands[0][:type] != "immediate") 
     | 
| 
      
 145 
     | 
    
         
            +
            			raise "Incorrect use of the \"set_pc\" directive." if (operands.size != 1) || (operands[0][:type] != "immediate")						
         
     | 
| 
       136 
146 
     | 
    
         | 
| 
       137 
147 
     | 
    
         
             
            			new_pc = operands[0][:value]
         
     | 
| 
       138 
     | 
    
         
            -
             
     | 
| 
      
 148 
     | 
    
         
            +
            			
         
     | 
| 
       139 
149 
     | 
    
         
             
            			state[:current_address] = new_pc
         
     | 
| 
      
 150 
     | 
    
         
            +
            			state[:line_i] += 1
         
     | 
| 
       140 
151 
     | 
    
         | 
| 
       141 
152 
     | 
    
         
             
            			state
         
     | 
| 
       142 
153 
     | 
    
         
             
            		end
         
     | 
| 
         @@ -155,7 +166,346 @@ end 
     | 
|
| 
       155 
166 
     | 
    
         | 
| 
       156 
167 
     | 
    
         
             
            			state[:current_address] += file_content.bytes.size
         
     | 
| 
       157 
168 
     | 
    
         
             
            			state[:parsed_lines] << {type: "insert", byte_string: file_content}
         
     | 
| 
      
 169 
     | 
    
         
            +
            			
         
     | 
| 
      
 170 
     | 
    
         
            +
            			state[:line_i] += 1
         
     | 
| 
      
 171 
     | 
    
         
            +
            			
         
     | 
| 
      
 172 
     | 
    
         
            +
            			state
         
     | 
| 
      
 173 
     | 
    
         
            +
            		end
         
     | 
| 
      
 174 
     | 
    
         
            +
            	},
         
     | 
| 
      
 175 
     | 
    
         
            +
            	{
         
     | 
| 
      
 176 
     | 
    
         
            +
            		keyword: ["load_end", "include_end"],
         
     | 
| 
      
 177 
     | 
    
         
            +
            		func: lambda do |operands, state|
         
     | 
| 
      
 178 
     | 
    
         
            +
             
     | 
| 
      
 179 
     | 
    
         
            +
            			raise "Incorrect use of the \"load_end\" directive" if (operands.size != 1) || (operands[0][:type] != "string")
         
     | 
| 
       158 
180 
     | 
    
         | 
| 
      
 181 
     | 
    
         
            +
            			file_selector = operands[0][:string]
         
     | 
| 
      
 182 
     | 
    
         
            +
             
     | 
| 
      
 183 
     | 
    
         
            +
            			files_to_load = Dir[file_selector]
         
     | 
| 
      
 184 
     | 
    
         
            +
             
     | 
| 
      
 185 
     | 
    
         
            +
            			# Create the loaded_files state entry if it doesn't exist
         
     | 
| 
      
 186 
     | 
    
         
            +
            			state[:extra_state][:loaded_files] = Array.new if !state[:extra_state].keys.include?(:loaded_files)
         
     | 
| 
      
 187 
     | 
    
         
            +
             
     | 
| 
      
 188 
     | 
    
         
            +
            			# Select only files that haven't been previously loaded
         
     | 
| 
      
 189 
     | 
    
         
            +
            			files_to_load.select!{|file_name| !state[:extra_state][:loaded_files].include?(file_name)}
         
     | 
| 
      
 190 
     | 
    
         
            +
             
     | 
| 
      
 191 
     | 
    
         
            +
            			# Add the files that will be loaded to the state entry
         
     | 
| 
      
 192 
     | 
    
         
            +
            			state[:extra_state][:loaded_files] += files_to_load
         
     | 
| 
      
 193 
     | 
    
         
            +
             
     | 
| 
      
 194 
     | 
    
         
            +
            			files_to_load.each do |load_filename|
         
     | 
| 
      
 195 
     | 
    
         
            +
            				file_content = File.read load_filename
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
      
 197 
     | 
    
         
            +
            				file_lines = Kompiler::Parsers.get_code_lines(file_content)
         
     | 
| 
      
 198 
     | 
    
         
            +
             
     | 
| 
      
 199 
     | 
    
         
            +
            				state[:lines] += file_lines
         
     | 
| 
      
 200 
     | 
    
         
            +
            			end
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
            			state[:line_i] += 1
         
     | 
| 
      
 203 
     | 
    
         
            +
             
     | 
| 
      
 204 
     | 
    
         
            +
            			state
         
     | 
| 
      
 205 
     | 
    
         
            +
            		end
         
     | 
| 
      
 206 
     | 
    
         
            +
            	},
         
     | 
| 
      
 207 
     | 
    
         
            +
            	{
         
     | 
| 
      
 208 
     | 
    
         
            +
            		keyword: ["load", "include"],
         
     | 
| 
      
 209 
     | 
    
         
            +
            		func: lambda do |operands, state|
         
     | 
| 
      
 210 
     | 
    
         
            +
             
     | 
| 
      
 211 
     | 
    
         
            +
            			raise "Incorrect use of the \"load\" directive" if (operands.size != 1) || (operands[0][:type] != "string")
         
     | 
| 
      
 212 
     | 
    
         
            +
             
     | 
| 
      
 213 
     | 
    
         
            +
            			file_selector = operands[0][:string]
         
     | 
| 
      
 214 
     | 
    
         
            +
             
     | 
| 
      
 215 
     | 
    
         
            +
            			files_to_load = Dir[file_selector]
         
     | 
| 
      
 216 
     | 
    
         
            +
             
     | 
| 
      
 217 
     | 
    
         
            +
            			# Create the loaded_files state entry if it doesn't exist
         
     | 
| 
      
 218 
     | 
    
         
            +
            			state[:extra_state][:loaded_files] = Array.new if !state[:extra_state].keys.include?(:loaded_files)
         
     | 
| 
      
 219 
     | 
    
         
            +
             
     | 
| 
      
 220 
     | 
    
         
            +
            			# Select only files that haven't been previously loaded
         
     | 
| 
      
 221 
     | 
    
         
            +
            			files_to_load.select!{|file_name| !state[:extra_state][:loaded_files].include?(file_name)}
         
     | 
| 
      
 222 
     | 
    
         
            +
             
     | 
| 
      
 223 
     | 
    
         
            +
            			# Add the files that will be loaded to the state entry
         
     | 
| 
      
 224 
     | 
    
         
            +
            			state[:extra_state][:loaded_files] += files_to_load
         
     | 
| 
      
 225 
     | 
    
         
            +
             
     | 
| 
      
 226 
     | 
    
         
            +
            			total_load_lines = []
         
     | 
| 
      
 227 
     | 
    
         
            +
             
     | 
| 
      
 228 
     | 
    
         
            +
            			files_to_load.each do |load_filename|
         
     | 
| 
      
 229 
     | 
    
         
            +
            				file_content = File.read load_filename
         
     | 
| 
      
 230 
     | 
    
         
            +
             
     | 
| 
      
 231 
     | 
    
         
            +
            				file_lines = Kompiler::Parsers.get_code_lines(file_content)
         
     | 
| 
      
 232 
     | 
    
         
            +
             
     | 
| 
      
 233 
     | 
    
         
            +
            				total_load_lines += file_lines
         
     | 
| 
      
 234 
     | 
    
         
            +
            			end
         
     | 
| 
      
 235 
     | 
    
         
            +
             
     | 
| 
      
 236 
     | 
    
         
            +
            			# Move to the next line
         
     | 
| 
      
 237 
     | 
    
         
            +
            			state[:line_i] += 1
         
     | 
| 
      
 238 
     | 
    
         
            +
             
     | 
| 
      
 239 
     | 
    
         
            +
            			# Insert the lines at the correct place
         
     | 
| 
      
 240 
     | 
    
         
            +
            			state[:lines] = state[:lines][0...state[:line_i]] + total_load_lines + state[:lines][state[:line_i]..]
         
     | 
| 
      
 241 
     | 
    
         
            +
             
     | 
| 
      
 242 
     | 
    
         
            +
            			state
         
     | 
| 
      
 243 
     | 
    
         
            +
            		end
         
     | 
| 
      
 244 
     | 
    
         
            +
            	},
         
     | 
| 
      
 245 
     | 
    
         
            +
            	{
         
     | 
| 
      
 246 
     | 
    
         
            +
            		keyword: "value",
         
     | 
| 
      
 247 
     | 
    
         
            +
            		collect_operands: false,
         
     | 
| 
      
 248 
     | 
    
         
            +
            		func: lambda do |_, state|			
         
     | 
| 
      
 249 
     | 
    
         
            +
            			_, operands = Kompiler::Parsers.extract_instruction_parts(state[:lines][state[:line_i]])
         
     | 
| 
      
 250 
     | 
    
         
            +
            			
         
     | 
| 
      
 251 
     | 
    
         
            +
            			raise "Incorrect use of the .value directive - expected 2 operands: Program build not possible" if operands.size != 2
         
     | 
| 
      
 252 
     | 
    
         
            +
            			
         
     | 
| 
      
 253 
     | 
    
         
            +
            			value_name = operands[0]
         
     | 
| 
      
 254 
     | 
    
         
            +
            			
         
     | 
| 
      
 255 
     | 
    
         
            +
            			# Check that the name is made out of allowed characters
         
     | 
| 
      
 256 
     | 
    
         
            +
            			if value_name.each_char.map{|char| Kompiler::Config.keyword_chars.include?(char)}.include?(false)
         
     | 
| 
      
 257 
     | 
    
         
            +
            				raise "Incorrect use of the .value directive - the value name must contain only keyword characters: Program build not possible"
         
     | 
| 
      
 258 
     | 
    
         
            +
            			end
         
     | 
| 
      
 259 
     | 
    
         
            +
            			
         
     | 
| 
      
 260 
     | 
    
         
            +
            			value_def = operands[1]
         
     | 
| 
      
 261 
     | 
    
         
            +
            			
         
     | 
| 
      
 262 
     | 
    
         
            +
            			scan_lines = state[:lines][(state[:line_i] + 1)..]
         
     | 
| 
      
 263 
     | 
    
         
            +
            			
         
     | 
| 
      
 264 
     | 
    
         
            +
            			scan_lines.each_with_index do |line, line_i|
         
     | 
| 
      
 265 
     | 
    
         
            +
            				
         
     | 
| 
      
 266 
     | 
    
         
            +
            				start_i = 0
         
     | 
| 
      
 267 
     | 
    
         
            +
            				
         
     | 
| 
      
 268 
     | 
    
         
            +
            				# Loop through each character starting position
         
     | 
| 
      
 269 
     | 
    
         
            +
            				while start_i < line.size					
         
     | 
| 
      
 270 
     | 
    
         
            +
            					
         
     | 
| 
      
 271 
     | 
    
         
            +
            					# Skip whitespace characters
         
     | 
| 
      
 272 
     | 
    
         
            +
            					if Kompiler::Config.whitespace_chars.include?(line[start_i])
         
     | 
| 
      
 273 
     | 
    
         
            +
            						start_i += 1
         
     | 
| 
      
 274 
     | 
    
         
            +
            						next
         
     | 
| 
      
 275 
     | 
    
         
            +
            					end
         
     | 
| 
      
 276 
     | 
    
         
            +
            					
         
     | 
| 
      
 277 
     | 
    
         
            +
            					# Skip string definitions
         
     | 
| 
      
 278 
     | 
    
         
            +
            					if ['"', "'"].include? line[start_i]
         
     | 
| 
      
 279 
     | 
    
         
            +
            						str, parsed_length = Kompiler::Parsers.parse_str line[start_i..]
         
     | 
| 
      
 280 
     | 
    
         
            +
            						start_i += parsed_length
         
     | 
| 
      
 281 
     | 
    
         
            +
            						next
         
     | 
| 
      
 282 
     | 
    
         
            +
            					end
         
     | 
| 
      
 283 
     | 
    
         
            +
            					
         
     | 
| 
      
 284 
     | 
    
         
            +
            					cut_line = line[start_i..]
         
     | 
| 
      
 285 
     | 
    
         
            +
            					
         
     | 
| 
      
 286 
     | 
    
         
            +
            					value_word_found = false
         
     | 
| 
      
 287 
     | 
    
         
            +
            					
         
     | 
| 
      
 288 
     | 
    
         
            +
            					# Check if the value name works
         
     | 
| 
      
 289 
     | 
    
         
            +
            					
         
     | 
| 
      
 290 
     | 
    
         
            +
            					if cut_line.start_with?(value_name) # Check that the piece of text starts with the value name
         
     | 
| 
      
 291 
     | 
    
         
            +
            						if !Kompiler::Config.keyword_chars.include?(cut_line[value_name.size]) # Check that the cut text is a full word. This will not fire when value_name='arg', but the cut text is 'arg1'
         
     | 
| 
      
 292 
     | 
    
         
            +
            							value_word_found = true # Indicate that a replacement was found
         
     | 
| 
      
 293 
     | 
    
         
            +
            							
         
     | 
| 
      
 294 
     | 
    
         
            +
            							scan_lines[line_i] = scan_lines[line_i][...start_i] + value_def + (scan_lines[line_i][(start_i+value_name.size)..] || "")
         
     | 
| 
      
 295 
     | 
    
         
            +
            							line = scan_lines[line_i]
         
     | 
| 
      
 296 
     | 
    
         
            +
            						end
         
     | 
| 
      
 297 
     | 
    
         
            +
            					end
         
     | 
| 
      
 298 
     | 
    
         
            +
            					
         
     | 
| 
      
 299 
     | 
    
         
            +
            					# Check if the value name wasn't detected
         
     | 
| 
      
 300 
     | 
    
         
            +
            					# If not, skip the text until the next whitespace character
         
     | 
| 
      
 301 
     | 
    
         
            +
            					if !value_word_found
         
     | 
| 
      
 302 
     | 
    
         
            +
            						while start_i < line.size && Kompiler::Config.keyword_chars.include?(line[start_i])
         
     | 
| 
      
 303 
     | 
    
         
            +
            							start_i += 1
         
     | 
| 
      
 304 
     | 
    
         
            +
            						end
         
     | 
| 
      
 305 
     | 
    
         
            +
            						start_i += 1 # Move one more character
         
     | 
| 
      
 306 
     | 
    
         
            +
            					end
         
     | 
| 
      
 307 
     | 
    
         
            +
            					
         
     | 
| 
      
 308 
     | 
    
         
            +
            				end
         
     | 
| 
      
 309 
     | 
    
         
            +
            				
         
     | 
| 
      
 310 
     | 
    
         
            +
            			end
         
     | 
| 
      
 311 
     | 
    
         
            +
            			
         
     | 
| 
      
 312 
     | 
    
         
            +
            			
         
     | 
| 
      
 313 
     | 
    
         
            +
            			state[:extra_state][:values] = Array.new if !state[:extra_state].keys.include?(:values)
         
     | 
| 
      
 314 
     | 
    
         
            +
            			
         
     | 
| 
      
 315 
     | 
    
         
            +
            			state[:extra_state][:values] << {name: value_name, def_value: value_def}
         
     | 
| 
      
 316 
     | 
    
         
            +
            			
         
     | 
| 
      
 317 
     | 
    
         
            +
            			
         
     | 
| 
      
 318 
     | 
    
         
            +
            			state[:lines] = state[:lines][..state[:line_i]] + scan_lines
         
     | 
| 
      
 319 
     | 
    
         
            +
            			
         
     | 
| 
      
 320 
     | 
    
         
            +
            			state[:line_i] += 1
         
     | 
| 
      
 321 
     | 
    
         
            +
            			
         
     | 
| 
      
 322 
     | 
    
         
            +
            			state
         
     | 
| 
      
 323 
     | 
    
         
            +
            		end
         
     | 
| 
      
 324 
     | 
    
         
            +
            	},
         
     | 
| 
      
 325 
     | 
    
         
            +
            	{
         
     | 
| 
      
 326 
     | 
    
         
            +
            		keyword: "macro",
         
     | 
| 
      
 327 
     | 
    
         
            +
            		collect_operands: false,
         
     | 
| 
      
 328 
     | 
    
         
            +
            		func: lambda do |_, state|
         
     | 
| 
      
 329 
     | 
    
         
            +
            			line_i = state[:line_i]
         
     | 
| 
      
 330 
     | 
    
         
            +
            			
         
     | 
| 
      
 331 
     | 
    
         
            +
            			def_line = state[:lines][line_i]
         
     | 
| 
      
 332 
     | 
    
         
            +
            			
         
     | 
| 
      
 333 
     | 
    
         
            +
            			# First: collect the part after ".macro"
         
     | 
| 
      
 334 
     | 
    
         
            +
            			
         
     | 
| 
      
 335 
     | 
    
         
            +
            			char_i = 0
         
     | 
| 
      
 336 
     | 
    
         
            +
            			# Skip the whitespace before .macro
         
     | 
| 
      
 337 
     | 
    
         
            +
            			while char_i < def_line.size && Kompiler::Config.whitespace_chars.include?(def_line[char_i])
         
     | 
| 
      
 338 
     | 
    
         
            +
            				char_i += 1
         
     | 
| 
      
 339 
     | 
    
         
            +
            			end
         
     | 
| 
      
 340 
     | 
    
         
            +
            			# Skip the .macro
         
     | 
| 
      
 341 
     | 
    
         
            +
            			while char_i < def_line.size && Kompiler::Config.keyword_chars.include?(def_line[char_i])
         
     | 
| 
      
 342 
     | 
    
         
            +
            				char_i += 1
         
     | 
| 
      
 343 
     | 
    
         
            +
            			end
         
     | 
| 
      
 344 
     | 
    
         
            +
            			# Skip the whitespace after .macro
         
     | 
| 
      
 345 
     | 
    
         
            +
            			while char_i < def_line.size && Kompiler::Config.whitespace_chars.include?(def_line[char_i])
         
     | 
| 
      
 346 
     | 
    
         
            +
            				char_i += 1
         
     | 
| 
      
 347 
     | 
    
         
            +
            			end
         
     | 
| 
      
 348 
     | 
    
         
            +
            			
         
     | 
| 
      
 349 
     | 
    
         
            +
            			# If the end of the line was reached, throw an error
         
     | 
| 
      
 350 
     | 
    
         
            +
            			raise "Incorrect .macro definition" if char_i == def_line.size
         
     | 
| 
      
 351 
     | 
    
         
            +
            			
         
     | 
| 
      
 352 
     | 
    
         
            +
            			# Now char_i contains the first index of the text after .macro
         
     | 
| 
      
 353 
     | 
    
         
            +
            			
         
     | 
| 
      
 354 
     | 
    
         
            +
            			macro_def = def_line[char_i..]
         
     | 
| 
      
 355 
     | 
    
         
            +
            			
         
     | 
| 
      
 356 
     | 
    
         
            +
            			# Second: extract the macro's name
         
     | 
| 
      
 357 
     | 
    
         
            +
            			
         
     | 
| 
      
 358 
     | 
    
         
            +
            			macro_name = ""			
         
     | 
| 
      
 359 
     | 
    
         
            +
            			
         
     | 
| 
      
 360 
     | 
    
         
            +
            			while char_i < def_line.size && Kompiler::Config.keyword_chars.include?(def_line[char_i])
         
     | 
| 
      
 361 
     | 
    
         
            +
            				macro_name << def_line[char_i]
         
     | 
| 
      
 362 
     | 
    
         
            +
            				char_i += 1
         
     | 
| 
      
 363 
     | 
    
         
            +
            			end
         
     | 
| 
      
 364 
     | 
    
         
            +
            			
         
     | 
| 
      
 365 
     | 
    
         
            +
            			# Third: extract the operand names (code taken from parse_instruction_line in parsers.rb)
         
     | 
| 
      
 366 
     | 
    
         
            +
            			
         
     | 
| 
      
 367 
     | 
    
         
            +
            			arg_names = Kompiler::Parsers.extract_instruction_operands(def_line[char_i..])
         
     | 
| 
      
 368 
     | 
    
         
            +
            			
         
     | 
| 
      
 369 
     | 
    
         
            +
            			# Make sure that the arg names are unique
         
     | 
| 
      
 370 
     | 
    
         
            +
            			raise "Macro definition error - arguments cannot have the same name: Program build not possible" if arg_names.size != arg_names.uniq.size
         
     | 
| 
      
 371 
     | 
    
         
            +
            			
         
     | 
| 
      
 372 
     | 
    
         
            +
            			# Extract the macro inside definition
         
     | 
| 
      
 373 
     | 
    
         
            +
            			
         
     | 
| 
      
 374 
     | 
    
         
            +
            			line_i = state[:line_i] + 1
         
     | 
| 
      
 375 
     | 
    
         
            +
            			def_lines = []
         
     | 
| 
      
 376 
     | 
    
         
            +
            			
         
     | 
| 
      
 377 
     | 
    
         
            +
            			whitespace_regexp = /[#{Kompiler::Config.whitespace_chars.join("|")}]*/
         
     | 
| 
      
 378 
     | 
    
         
            +
            			
         
     | 
| 
      
 379 
     | 
    
         
            +
            			endmacro_regexp = /\A#{whitespace_regexp}\.?endmacro#{whitespace_regexp}\z/
         
     | 
| 
      
 380 
     | 
    
         
            +
            			
         
     | 
| 
      
 381 
     | 
    
         
            +
            			while line_i < state[:lines].size
         
     | 
| 
      
 382 
     | 
    
         
            +
            				break if state[:lines][line_i].match? endmacro_regexp # Check if it's an end macro instruction
         
     | 
| 
      
 383 
     | 
    
         
            +
            				def_lines << state[:lines][line_i]
         
     | 
| 
      
 384 
     | 
    
         
            +
            				line_i += 1
         
     | 
| 
      
 385 
     | 
    
         
            +
            			end
         
     | 
| 
      
 386 
     | 
    
         
            +
            			
         
     | 
| 
      
 387 
     | 
    
         
            +
            			
         
     | 
| 
      
 388 
     | 
    
         
            +
            			# Find insert indexes for each argument
         
     | 
| 
      
 389 
     | 
    
         
            +
            			arg_insert_locations = arg_names.map{|arg_name| [arg_name, []]}.to_h
         
     | 
| 
      
 390 
     | 
    
         
            +
            			
         
     | 
| 
      
 391 
     | 
    
         
            +
            			def_lines.each_with_index do |line, line_i|
         
     | 
| 
      
 392 
     | 
    
         
            +
            				
         
     | 
| 
      
 393 
     | 
    
         
            +
            				start_i = 0
         
     | 
| 
      
 394 
     | 
    
         
            +
            				
         
     | 
| 
      
 395 
     | 
    
         
            +
            				# Loop through each character starting position
         
     | 
| 
      
 396 
     | 
    
         
            +
            				while start_i < line.size					
         
     | 
| 
      
 397 
     | 
    
         
            +
            					
         
     | 
| 
      
 398 
     | 
    
         
            +
            					# Skip whitespace characters
         
     | 
| 
      
 399 
     | 
    
         
            +
            					if Kompiler::Config.whitespace_chars.include?(line[start_i])
         
     | 
| 
      
 400 
     | 
    
         
            +
            						start_i += 1
         
     | 
| 
      
 401 
     | 
    
         
            +
            						next
         
     | 
| 
      
 402 
     | 
    
         
            +
            					end
         
     | 
| 
      
 403 
     | 
    
         
            +
            					
         
     | 
| 
      
 404 
     | 
    
         
            +
            					# Skip string definitions
         
     | 
| 
      
 405 
     | 
    
         
            +
            					if ['"', "'"].include? line[start_i]
         
     | 
| 
      
 406 
     | 
    
         
            +
            						str, parsed_length = Kompiler::Parsers.parse_str line[start_i..]
         
     | 
| 
      
 407 
     | 
    
         
            +
            						start_i += parsed_length
         
     | 
| 
      
 408 
     | 
    
         
            +
            						next
         
     | 
| 
      
 409 
     | 
    
         
            +
            					end
         
     | 
| 
      
 410 
     | 
    
         
            +
            					
         
     | 
| 
      
 411 
     | 
    
         
            +
            					cut_line = line[start_i..]
         
     | 
| 
      
 412 
     | 
    
         
            +
            					
         
     | 
| 
      
 413 
     | 
    
         
            +
            					arg_found = false
         
     | 
| 
      
 414 
     | 
    
         
            +
            					
         
     | 
| 
      
 415 
     | 
    
         
            +
            					# Check if one of the argument names works
         
     | 
| 
      
 416 
     | 
    
         
            +
            					arg_names.each do |arg_name|
         
     | 
| 
      
 417 
     | 
    
         
            +
            						next if !cut_line.start_with?(arg_name) # Skip the argument if the line doesn't begin with it
         
     | 
| 
      
 418 
     | 
    
         
            +
            						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'
         
     | 
| 
      
 419 
     | 
    
         
            +
            						# Here if the argument name should be replaced with the contents
         
     | 
| 
      
 420 
     | 
    
         
            +
            						arg_found = true # Indicate that a replacement was found						
         
     | 
| 
      
 421 
     | 
    
         
            +
            						
         
     | 
| 
      
 422 
     | 
    
         
            +
            						arg_insert_locations[arg_name] << [line_i, start_i] # Add the insert location to the list
         
     | 
| 
      
 423 
     | 
    
         
            +
            						
         
     | 
| 
      
 424 
     | 
    
         
            +
            						# start_i += arg_name.size
         
     | 
| 
      
 425 
     | 
    
         
            +
            						def_lines[line_i] = def_lines[line_i][...start_i] + (def_lines[line_i][(start_i+arg_name.size)..] || "")
         
     | 
| 
      
 426 
     | 
    
         
            +
            						line = def_lines[line_i]
         
     | 
| 
      
 427 
     | 
    
         
            +
            						
         
     | 
| 
      
 428 
     | 
    
         
            +
            						break # Skip the arguments loop
         
     | 
| 
      
 429 
     | 
    
         
            +
            					end
         
     | 
| 
      
 430 
     | 
    
         
            +
            					
         
     | 
| 
      
 431 
     | 
    
         
            +
            					# Check if an argument was found
         
     | 
| 
      
 432 
     | 
    
         
            +
            					# If not, skip the text until the next whitespace character
         
     | 
| 
      
 433 
     | 
    
         
            +
            					if !arg_found
         
     | 
| 
      
 434 
     | 
    
         
            +
            						while start_i < line.size && Kompiler::Config.keyword_chars.include?(line[start_i])
         
     | 
| 
      
 435 
     | 
    
         
            +
            							start_i += 1
         
     | 
| 
      
 436 
     | 
    
         
            +
            						end
         
     | 
| 
      
 437 
     | 
    
         
            +
            						start_i += 1 # Move one more character
         
     | 
| 
      
 438 
     | 
    
         
            +
            					end
         
     | 
| 
      
 439 
     | 
    
         
            +
            					
         
     | 
| 
      
 440 
     | 
    
         
            +
            				end
         
     | 
| 
      
 441 
     | 
    
         
            +
            				
         
     | 
| 
      
 442 
     | 
    
         
            +
            			end
         
     | 
| 
      
 443 
     | 
    
         
            +
            			
         
     | 
| 
      
 444 
     | 
    
         
            +
            			state[:extra_state][:macros] = Array.new if !state[:extra_state].keys.include?(:macros)
         
     | 
| 
      
 445 
     | 
    
         
            +
            			
         
     | 
| 
      
 446 
     | 
    
         
            +
            			state[:extra_state][:macros] << {name: macro_name, args: arg_names, def_lines: def_lines, arg_insert_locations: arg_insert_locations}
         
     | 
| 
      
 447 
     | 
    
         
            +
            			
         
     | 
| 
      
 448 
     | 
    
         
            +
            			
         
     | 
| 
      
 449 
     | 
    
         
            +
            			# Scan the lines after the macro for the macro call and replace it with the macro definition
         
     | 
| 
      
 450 
     | 
    
         
            +
            			
         
     | 
| 
      
 451 
     | 
    
         
            +
            			scan_lines = state[:lines][(state[:line_i] + def_lines.size + 1 + 1)..]
         
     | 
| 
      
 452 
     | 
    
         
            +
            			
         
     | 
| 
      
 453 
     | 
    
         
            +
            			line_i = 0
         
     | 
| 
      
 454 
     | 
    
         
            +
            			
         
     | 
| 
      
 455 
     | 
    
         
            +
            			# Re-group argument insert locations by line -> [index, arg index]			
         
     | 
| 
      
 456 
     | 
    
         
            +
            			
         
     | 
| 
      
 457 
     | 
    
         
            +
            			arg_insert_locations_regrouped = def_lines.size.times.map{[]}
         
     | 
| 
      
 458 
     | 
    
         
            +
            			
         
     | 
| 
      
 459 
     | 
    
         
            +
            			arg_insert_locations.each do |arg_name, insert_locations|
         
     | 
| 
      
 460 
     | 
    
         
            +
            				insert_locations.each do |line_i, char_i|
         
     | 
| 
      
 461 
     | 
    
         
            +
            					arg_insert_locations_regrouped[line_i] << [char_i, arg_names.index(arg_name)]
         
     | 
| 
      
 462 
     | 
    
         
            +
            				end
         
     | 
| 
      
 463 
     | 
    
         
            +
            			end
         
     | 
| 
      
 464 
     | 
    
         
            +
            			
         
     | 
| 
      
 465 
     | 
    
         
            +
            			
         
     | 
| 
      
 466 
     | 
    
         
            +
            			while line_i < scan_lines.size
         
     | 
| 
      
 467 
     | 
    
         
            +
            				keyword, operands = Kompiler::Parsers.extract_instruction_parts(scan_lines[line_i])
         
     | 
| 
      
 468 
     | 
    
         
            +
            				
         
     | 
| 
      
 469 
     | 
    
         
            +
            				# If parsing failed, move on to the next line
         
     | 
| 
      
 470 
     | 
    
         
            +
            				if keyword == false
         
     | 
| 
      
 471 
     | 
    
         
            +
            					line_i += 1
         
     | 
| 
      
 472 
     | 
    
         
            +
            					next
         
     | 
| 
      
 473 
     | 
    
         
            +
            				end
         
     | 
| 
      
 474 
     | 
    
         
            +
            				
         
     | 
| 
      
 475 
     | 
    
         
            +
            				# If the keyword isn't the macro's name, skip the line
         
     | 
| 
      
 476 
     | 
    
         
            +
            				if keyword != macro_name
         
     | 
| 
      
 477 
     | 
    
         
            +
            					line_i += 1
         
     | 
| 
      
 478 
     | 
    
         
            +
            					next
         
     | 
| 
      
 479 
     | 
    
         
            +
            				end
         
     | 
| 
      
 480 
     | 
    
         
            +
            				
         
     | 
| 
      
 481 
     | 
    
         
            +
            				# Here when the keyword matches the macro name
         
     | 
| 
      
 482 
     | 
    
         
            +
            				
         
     | 
| 
      
 483 
     | 
    
         
            +
            				# Check that the number of operands is correct
         
     | 
| 
      
 484 
     | 
    
         
            +
            				if operands.size != arg_names.size
         
     | 
| 
      
 485 
     | 
    
         
            +
            					raise "Incorrect use of the \"#{macro_name}\" macro - #{arg_names.size} operands expected, but #{operands.size} were given: Program build not possible."
         
     | 
| 
      
 486 
     | 
    
         
            +
            				end
         
     | 
| 
      
 487 
     | 
    
         
            +
            				
         
     | 
| 
      
 488 
     | 
    
         
            +
            				# Build the replacement lines for the macro call
         
     | 
| 
      
 489 
     | 
    
         
            +
            				build_lines = def_lines.map{|line| line.dup} # Copying strings inside array, because array.dup doesn't work for elements
         
     | 
| 
      
 490 
     | 
    
         
            +
            				
         
     | 
| 
      
 491 
     | 
    
         
            +
            				arg_insert_locations_regrouped.each_with_index do |locations, line_i|
         
     | 
| 
      
 492 
     | 
    
         
            +
            					# Sort the locations by the insert character from largest to smallest, so that the inserts are made from end to start
         
     | 
| 
      
 493 
     | 
    
         
            +
            					locations.sort_by{|el| el[0]}.reverse.each do |char_i, arg_index|
         
     | 
| 
      
 494 
     | 
    
         
            +
            						build_lines[line_i].insert char_i, operands[arg_index]
         
     | 
| 
      
 495 
     | 
    
         
            +
            					end
         
     | 
| 
      
 496 
     | 
    
         
            +
            				end
         
     | 
| 
      
 497 
     | 
    
         
            +
            				
         
     | 
| 
      
 498 
     | 
    
         
            +
            				# Replace the macro call with the built lines
         
     | 
| 
      
 499 
     | 
    
         
            +
            				scan_lines = scan_lines[...line_i] + build_lines + scan_lines[(line_i + 1)..]
         
     | 
| 
      
 500 
     | 
    
         
            +
            				
         
     | 
| 
      
 501 
     | 
    
         
            +
            				# Skip the inserted macro
         
     | 
| 
      
 502 
     | 
    
         
            +
            				line_i += build_lines.size
         
     | 
| 
      
 503 
     | 
    
         
            +
            			end			
         
     | 
| 
      
 504 
     | 
    
         
            +
            			
         
     | 
| 
      
 505 
     | 
    
         
            +
            			state[:lines] = state[:lines][...state[:line_i]] + scan_lines
         
     | 
| 
      
 506 
     | 
    
         
            +
            			
         
     | 
| 
      
 507 
     | 
    
         
            +
            			state[:line_i] += 1
         
     | 
| 
      
 508 
     | 
    
         
            +
            			
         
     | 
| 
       159 
509 
     | 
    
         
             
            			state
         
     | 
| 
       160 
510 
     | 
    
         
             
            		end
         
     | 
| 
       161 
511 
     | 
    
         
             
            	}
         
     | 
| 
         @@ -164,4 +514,4 @@ end 
     | 
|
| 
       164 
514 
     | 
    
         | 
| 
       165 
515 
     | 
    
         
             
            end # Kompiler::Directives
         
     | 
| 
       166 
516 
     | 
    
         | 
| 
       167 
     | 
    
         
            -
            end # Kompiler
         
     | 
| 
      
 517 
     | 
    
         
            +
            end # Kompiler
         
     |