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/parsers.rb
    CHANGED
    
    | @@ -3,7 +3,7 @@ | |
| 3 3 |  | 
| 4 4 | 
             
            module Kompiler
         | 
| 5 5 |  | 
| 6 | 
            -
             | 
| 6 | 
            +
            module Parsers
         | 
| 7 7 |  | 
| 8 8 | 
             
            def self.parse_str(code)
         | 
| 9 9 |  | 
| @@ -171,6 +171,29 @@ def self.check_char_operand(str) | |
| 171 171 | 
             
            end
         | 
| 172 172 |  | 
| 173 173 |  | 
| 174 | 
            +
            def self.check_expression_operand(str)
         | 
| 175 | 
            +
            	begin
         | 
| 176 | 
            +
            		
         | 
| 177 | 
            +
            		ast = Kompiler::Parsers::SymAST.parse str
         | 
| 178 | 
            +
            		
         | 
| 179 | 
            +
            		run_block = lambda do |state|
         | 
| 180 | 
            +
            			state[:labels]["here"] = state[:current_address]
         | 
| 181 | 
            +
            			
         | 
| 182 | 
            +
            			ast_result = Kompiler::Parsers::SymAST.run_ast state[:block_args][:ast], state[:labels], []
         | 
| 183 | 
            +
            			
         | 
| 184 | 
            +
            			return {type: "immediate", value: ast_result, def_type: "sym_ast", definition: state[:block_args][:definition]}
         | 
| 185 | 
            +
            		end
         | 
| 186 | 
            +
            		
         | 
| 187 | 
            +
            		return [true, {type: "run_block", block: run_block, block_args: {ast: ast, definition: str}, block_output_type: "immediate"}]
         | 
| 188 | 
            +
            		
         | 
| 189 | 
            +
            	rescue RuntimeError => e
         | 
| 190 | 
            +
            		p e
         | 
| 191 | 
            +
            		# If an error was caused, return false
         | 
| 192 | 
            +
            		return [false, nil]
         | 
| 193 | 
            +
            	end
         | 
| 194 | 
            +
             | 
| 195 | 
            +
            end
         | 
| 196 | 
            +
             | 
| 174 197 |  | 
| 175 198 | 
             
            def self.check_immediate_operand(operand_str)
         | 
| 176 199 |  | 
| @@ -195,7 +218,7 @@ def self.check_label_operand(str) | |
| 195 218 | 
             
            	return false if ("0".."9").to_a.include?(str[0])
         | 
| 196 219 |  | 
| 197 220 | 
             
            	# Check if it's only made up of allowed characters
         | 
| 198 | 
            -
            	allowed_chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a + ["_"] | 
| 221 | 
            +
            	allowed_chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a + ["_", "."]
         | 
| 199 222 |  | 
| 200 223 | 
             
            	is_each_char_allowed = str.each_char.map{|c| allowed_chars.include?(c)}
         | 
| 201 224 |  | 
| @@ -231,45 +254,22 @@ def self.parse_operand_str(operand_str) | |
| 231 254 | 
             
            	return {type: "label", value: operand_str, definition: operand_str} if is_label
         | 
| 232 255 |  | 
| 233 256 |  | 
| 257 | 
            +
            	is_expr, expr_operand = check_expression_operand(operand_str)
         | 
| 258 | 
            +
            	return expr_operand if is_expr
         | 
| 259 | 
            +
            	
         | 
| 234 260 | 
             
            	# If no checks succeeded, return false
         | 
| 235 261 | 
             
            	return false
         | 
| 236 262 | 
             
            end
         | 
| 237 263 |  | 
| 238 | 
            -
             | 
| 239 | 
            -
             | 
| 240 | 
            -
             | 
| 241 | 
            -
            def self.parse_instruction_line(line)
         | 
| 242 | 
            -
            	keyword = ""
         | 
| 264 | 
            +
            # Extract operand strings from the structure "op1, op2, op3, ..."
         | 
| 265 | 
            +
            # Returns an array of the operand strings
         | 
| 266 | 
            +
            def self.extract_instruction_operands(line)
         | 
| 243 267 | 
             
            	i = 0
         | 
| 244 | 
            -
            	
         | 
| 245 | 
            -
            	# Loop until a non-whitespace character
         | 
| 246 | 
            -
            	while i < line.size
         | 
| 247 | 
            -
            		break if ![" ", "\t"].include?(line[i])
         | 
| 248 | 
            -
            		i += 1
         | 
| 249 | 
            -
            	end
         | 
| 250 | 
            -
             | 
| 251 | 
            -
            	# Loop to get the keyword
         | 
| 252 | 
            -
            	loop do
         | 
| 253 | 
            -
            		# Exit out of the loop if the character is a whitespace
         | 
| 254 | 
            -
            		break if [" ", "\t"].include?(line[i]) || i >= line.size
         | 
| 255 | 
            -
            		# Add the character if not a whitespace
         | 
| 256 | 
            -
            		keyword << line[i]
         | 
| 257 | 
            -
            		# Proceed to the next character
         | 
| 258 | 
            -
            		i += 1	
         | 
| 259 | 
            -
            	end
         | 
| 260 | 
            -
             | 
| 261 268 | 
             
            	operand_strings = []
         | 
| 262 269 |  | 
| 263 | 
            -
            	# Loop for operands
         | 
| 264 270 | 
             
            	loop do
         | 
| 265 271 | 
             
            		break if i >= line.size
         | 
| 266 | 
            -
             | 
| 267 | 
            -
            		# # Whitespace - skip
         | 
| 268 | 
            -
            		# if [" ", "\t"].include? line[i]
         | 
| 269 | 
            -
            		# 	i += 1
         | 
| 270 | 
            -
            		# 	next
         | 
| 271 | 
            -
            		# end
         | 
| 272 | 
            -
             | 
| 272 | 
            +
            		
         | 
| 273 273 | 
             
            		operand_content = ""
         | 
| 274 274 |  | 
| 275 275 | 
             
            		# Collect the operand's content until a comma or end of line
         | 
| @@ -283,13 +283,13 @@ def self.parse_instruction_line(line) | |
| 283 283 | 
             
            			end
         | 
| 284 284 |  | 
| 285 285 | 
             
            			# Skip whitespace
         | 
| 286 | 
            -
            			if  | 
| 286 | 
            +
            			if Kompiler::Config.whitespace_chars.include? line[i]
         | 
| 287 287 | 
             
            				i += 1
         | 
| 288 288 | 
             
            				next
         | 
| 289 289 | 
             
            			end
         | 
| 290 290 |  | 
| 291 291 | 
             
            			# If a string definition, parse to the end of the string
         | 
| 292 | 
            -
            			if  | 
| 292 | 
            +
            			if Kompiler::Config.string_delimiters.include?(line[i])
         | 
| 293 293 | 
             
            				str_content, parsed_size = parse_str(line[i..])
         | 
| 294 294 | 
             
            				operand_content += line[i] + str_content + line[i]
         | 
| 295 295 | 
             
            				i += parsed_size
         | 
| @@ -305,7 +305,45 @@ def self.parse_instruction_line(line) | |
| 305 305 |  | 
| 306 306 | 
             
            		# After operand content was collected, add it to the list of operands if the content isn't empty
         | 
| 307 307 | 
             
            		operand_strings << operand_content if operand_content.size != 0
         | 
| 308 | 
            -
            	end | 
| 308 | 
            +
            	end
         | 
| 309 | 
            +
             | 
| 310 | 
            +
            	operand_strings
         | 
| 311 | 
            +
            end
         | 
| 312 | 
            +
             | 
| 313 | 
            +
             | 
| 314 | 
            +
            def self.extract_instruction_parts(line)
         | 
| 315 | 
            +
             | 
| 316 | 
            +
            	keyword = ""
         | 
| 317 | 
            +
            	i = 0
         | 
| 318 | 
            +
            	
         | 
| 319 | 
            +
            	# Loop until a non-whitespace character
         | 
| 320 | 
            +
            	while i < line.size
         | 
| 321 | 
            +
            		break if !Kompiler::Config.whitespace_chars.include?(line[i])
         | 
| 322 | 
            +
            		i += 1
         | 
| 323 | 
            +
            	end
         | 
| 324 | 
            +
            	
         | 
| 325 | 
            +
            	# Loop to get the keyword
         | 
| 326 | 
            +
            	loop do
         | 
| 327 | 
            +
            		# Exit out of the loop if the character is a whitespace
         | 
| 328 | 
            +
            		break if Kompiler::Config.whitespace_chars.include?(line[i]) || i >= line.size
         | 
| 329 | 
            +
            		# Add the character if not a whitespace
         | 
| 330 | 
            +
            		keyword << line[i]
         | 
| 331 | 
            +
            		# Proceed to the next character
         | 
| 332 | 
            +
            		i += 1	
         | 
| 333 | 
            +
            	end
         | 
| 334 | 
            +
            	
         | 
| 335 | 
            +
            	operand_strings = extract_instruction_operands(line[i..])
         | 
| 336 | 
            +
            	
         | 
| 337 | 
            +
            	# Loop for operands
         | 
| 338 | 
            +
            	
         | 
| 339 | 
            +
            	return keyword, operand_strings
         | 
| 340 | 
            +
            end
         | 
| 341 | 
            +
             | 
| 342 | 
            +
             | 
| 343 | 
            +
             | 
| 344 | 
            +
            def self.parse_instruction_line(line)
         | 
| 345 | 
            +
            	
         | 
| 346 | 
            +
            	keyword, operand_strings = extract_instruction_parts(line)
         | 
| 309 347 |  | 
| 310 348 | 
             
            	# Parse operand strings into operand types and values
         | 
| 311 349 |  | 
| @@ -324,8 +362,12 @@ end | |
| 324 362 |  | 
| 325 363 | 
             
            def self.check_operand_match(operand_description, operand)
         | 
| 326 364 |  | 
| 327 | 
            -
            	#  | 
| 328 | 
            -
             | 
| 365 | 
            +
            	if operand[:type] == "run_block" # A special check for a run block
         | 
| 366 | 
            +
            		return false if operand[:block_output_type] != operand_description[:type]
         | 
| 367 | 
            +
            	else
         | 
| 368 | 
            +
            		# If operand type doesn't not match, return false
         | 
| 369 | 
            +
            		return false if operand[:type] != operand_description[:type]
         | 
| 370 | 
            +
            	end
         | 
| 329 371 |  | 
| 330 372 | 
             
            	# Get the restrictions
         | 
| 331 373 | 
             
            	operand_restrictions = operand_description[:restrictions]
         | 
| @@ -337,6 +379,8 @@ def self.check_operand_match(operand_description, operand) | |
| 337 379 | 
             
            		operand_encoding = operand[:value]
         | 
| 338 380 | 
             
            	when "immediate"
         | 
| 339 381 | 
             
            		operand_encoding = operand[:value]
         | 
| 382 | 
            +
            	when "run_block"
         | 
| 383 | 
            +
            		operand_encoding = Hash.new
         | 
| 340 384 | 
             
            	when "string"
         | 
| 341 385 | 
             
            		operand_encoding = Hash.new
         | 
| 342 386 | 
             
            	end
         | 
| @@ -366,10 +410,9 @@ end | |
| 366 410 |  | 
| 367 411 | 
             
            # Returns array of [status, operands]
         | 
| 368 412 | 
             
            # If status = false, operands = nil; otherwise, status = true, operands = instruction operands
         | 
| 369 | 
            -
            def self. | 
| 370 | 
            -
             | 
| 371 | 
            -
            	keyword, operands = parse_instruction_line(line)
         | 
| 413 | 
            +
            def self.match_parsed_line_to_instruction(parsed_line, instruction)
         | 
| 372 414 |  | 
| 415 | 
            +
            	keyword, operands = parsed_line
         | 
| 373 416 |  | 
| 374 417 | 
             
            	# Check if the keyword matches
         | 
| 375 418 | 
             
            	if instruction[:keyword] != keyword
         | 
| @@ -396,9 +439,12 @@ def self.check_instruction(line) | |
| 396 439 | 
             
            	instruction = nil
         | 
| 397 440 | 
             
            	operands = nil
         | 
| 398 441 |  | 
| 442 | 
            +
            	parsed_line = Kompiler::Parsers.parse_instruction_line(line)
         | 
| 443 | 
            +
            	
         | 
| 399 444 | 
             
            	Kompiler::Architecture.instructions.each do |curr_instruction|
         | 
| 400 445 | 
             
            		# If the instruction matches - break
         | 
| 401 | 
            -
            		 | 
| 446 | 
            +
            		
         | 
| 447 | 
            +
            		status, curr_operands = match_parsed_line_to_instruction(parsed_line, curr_instruction)
         | 
| 402 448 | 
             
            		if status == true
         | 
| 403 449 | 
             
            			instruction = curr_instruction
         | 
| 404 450 | 
             
            			operands = curr_operands
         | 
| @@ -416,12 +462,20 @@ end | |
| 416 462 |  | 
| 417 463 |  | 
| 418 464 | 
             
            def self.check_directive(line)
         | 
| 419 | 
            -
            	 | 
| 420 | 
            -
             | 
| 421 | 
            -
            	 | 
| 422 | 
            -
             | 
| 423 | 
            -
            	 | 
| 424 | 
            -
             | 
| 465 | 
            +
            	# Skip whitespace
         | 
| 466 | 
            +
            	char_i = 0
         | 
| 467 | 
            +
            	while char_i < line.size && Kompiler::Config.whitespace_chars.include?(line[char_i])
         | 
| 468 | 
            +
            		char_i += 1
         | 
| 469 | 
            +
            	end
         | 
| 470 | 
            +
            	
         | 
| 471 | 
            +
            	# Collect the keyword
         | 
| 472 | 
            +
            	keyword = ""
         | 
| 473 | 
            +
            	
         | 
| 474 | 
            +
            	while char_i < line.size && Kompiler::Config.keyword_chars.include?(line[char_i])
         | 
| 475 | 
            +
            		keyword << line[char_i]
         | 
| 476 | 
            +
            		char_i += 1
         | 
| 477 | 
            +
            	end
         | 
| 478 | 
            +
            	
         | 
| 425 479 | 
             
            	if keyword[0] == "."
         | 
| 426 480 | 
             
            		keyword = keyword[1..]
         | 
| 427 481 | 
             
            	end
         | 
| @@ -429,16 +483,35 @@ def self.check_directive(line) | |
| 429 483 | 
             
            	directive = nil
         | 
| 430 484 |  | 
| 431 485 | 
             
            	Kompiler::Directives.directives.each do |curr_directive|
         | 
| 432 | 
            -
            		if curr_directive[:keyword]  | 
| 433 | 
            -
            			 | 
| 434 | 
            -
             | 
| 486 | 
            +
            		if curr_directive[:keyword].is_a? String
         | 
| 487 | 
            +
            			if curr_directive[:keyword] == keyword
         | 
| 488 | 
            +
            				directive = curr_directive
         | 
| 489 | 
            +
            				break
         | 
| 490 | 
            +
            			end
         | 
| 491 | 
            +
            		elsif curr_directive[:keyword].is_a? Array
         | 
| 492 | 
            +
            			if curr_directive[:keyword].include? keyword
         | 
| 493 | 
            +
            				directive = curr_directive
         | 
| 494 | 
            +
            				break
         | 
| 495 | 
            +
            			end
         | 
| 496 | 
            +
            		else
         | 
| 497 | 
            +
            			raise "Directive name error"
         | 
| 435 498 | 
             
            		end
         | 
| 436 499 | 
             
            	end
         | 
| 437 500 |  | 
| 438 501 | 
             
            	if directive == nil
         | 
| 439 502 | 
             
            		return [false, nil]
         | 
| 503 | 
            +
            	end
         | 
| 504 | 
            +
            	
         | 
| 505 | 
            +
            	# Check if the directive requires pre-collected operands (with the :collect_operands key that is true by default)
         | 
| 506 | 
            +
            	if !directive.keys.include?([:collect_operands]) || directive[:collect_operands] == true
         | 
| 507 | 
            +
            		parse_status, operands = parse_instruction_line(line)
         | 
| 508 | 
            +
            		
         | 
| 509 | 
            +
            		return [false, nil] if parse_status == false # Return negative if operands can't be parsed
         | 
| 510 | 
            +
            		
         | 
| 511 | 
            +
            		return [true, {directive: directive, operands: operands}] # Otherwise, return the directive
         | 
| 440 512 | 
             
            	else
         | 
| 441 | 
            -
            		 | 
| 513 | 
            +
            		# If operand collection isn't required, return the directive
         | 
| 514 | 
            +
            		return [true, {directive: directive, operands: []}]
         | 
| 442 515 | 
             
            	end
         | 
| 443 516 | 
             
            end
         | 
| 444 517 |  | 
    
        data/lib/kompiler.rb
    CHANGED
    
    | @@ -12,9 +12,11 @@ | |
| 12 12 | 
             
            # See the License for the specific language governing permissions and
         | 
| 13 13 | 
             
            # limitations under the License.
         | 
| 14 14 |  | 
| 15 | 
            +
            require 'kompiler/config.rb'
         | 
| 15 16 | 
             
            require 'kompiler/mc_builder.rb'
         | 
| 16 17 | 
             
            require 'kompiler/parsers.rb'
         | 
| 17 18 | 
             
            require 'kompiler/compiler_functions.rb'
         | 
| 18 19 | 
             
            require 'kompiler/architecture.rb'
         | 
| 19 20 | 
             
            require 'kompiler/directives.rb'
         | 
| 20 | 
            -
            require 'kompiler/arch_manager.rb'
         | 
| 21 | 
            +
            require 'kompiler/arch_manager.rb'
         | 
| 22 | 
            +
            require 'kompiler/math_ast.rb'
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: kompiler
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.3.0.pre. | 
| 4 | 
            +
              version: 0.3.0.pre.4
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Kyryl Shyshko
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date:  | 
| 11 | 
            +
            date: 2025-02-28 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies: []
         | 
| 13 13 | 
             
            description: 'Kompiler is a low-level, modular and extendable compiler for any architecture.
         | 
| 14 14 | 
             
              By default Kompiler supports ARMv8-a, but other architecture extensions can be downloaded
         | 
| @@ -33,7 +33,9 @@ files: | |
| 33 33 | 
             
            - lib/kompiler/architectures/armv8a/sys_instructions.rb
         | 
| 34 34 | 
             
            - lib/kompiler/architectures/armv8a/sys_registers.rb
         | 
| 35 35 | 
             
            - lib/kompiler/compiler_functions.rb
         | 
| 36 | 
            +
            - lib/kompiler/config.rb
         | 
| 36 37 | 
             
            - lib/kompiler/directives.rb
         | 
| 38 | 
            +
            - lib/kompiler/math_ast.rb
         | 
| 37 39 | 
             
            - lib/kompiler/mc_builder.rb
         | 
| 38 40 | 
             
            - lib/kompiler/parsers.rb
         | 
| 39 41 | 
             
            homepage: https://github.com/kyryloshy/kompiler
         |