sceptic-biomem/ScEpTIC/llvmir_parser/sections_divider.py
2026-07-10 10:38:57 +02:00

209 lines
6.5 KiB
Python

import logging
from ScEpTIC import tools
from ScEpTIC.exceptions import ParsingException
from ScEpTIC.llvmir_parser.sections_parser import attributes_groups
from ScEpTIC.llvmir_parser.sections_parser import custom_types
from ScEpTIC.llvmir_parser.sections_parser import function
from ScEpTIC.llvmir_parser.sections_parser import global_vars
from ScEpTIC.llvmir_parser.sections_parser import headers
from ScEpTIC.llvmir_parser.sections_parser import metadata
def divide_into_sections(text, force_extraction = False):
"""
Divides code into sections
Document structure (in order):
- Headers (source file name, target datalayout, target triple)
- Custom Types (Struct and Union) Definitions
- Global Variables Definitions
- Function Definitions
- Function's Attributes Definitions
- Debug Informations
"""
logging.debug('Calling divide_into_sections()')
# verification functions to be called, in order.
simple_elements_functions = [
headers.is_header,
custom_types.is_custom_type,
global_vars.is_global_var_def,
function.is_function_declaration,
attributes_groups.is_attributes_group,
metadata.is_metadata
]
# name of the section extracted by the function
section_names = [
'headers',
'custom_types',
'global_vars',
'function_declarations',
'attributes_groups',
'metadata'
]
sections = dict.fromkeys(section_names, [])
for i, f_reference in enumerate(simple_elements_functions):
# extract elements
section, text = simple_elements_extractor(text, f_reference)
sections[section_names[i]] = section
# special extraction
sections['function_definitions'], text = function_def_elements_extractor(text)
if len(text) > 0:
logging.critical("Some code has not been persed!\n{}\n".format(tools.fancy_list_to_str(text)))
return sections
def simple_elements_extractor(text, verification_function):
"""
Extract the section composed by simple elements of the same type.
An element is simple if its contained inside a single line of code.
The type of the elements is given by the verification_function.
It returns als the lines that have not been parsed.
"""
logging.debug('Calling simple_elements_extractor()')
lines = get_elements_lines(text, verification_function)
elements = [text[line] for line in lines]
text = remove_lines(text, lines)
# fancy logging
if logging.getLogger().isEnabledFor(logging.INFO):
elements_str = tools.fancy_list_to_str(elements)
logging.info('Elements extracted with {}.{}()\n{}\n'.format(verification_function.__module__, verification_function.__name__, elements_str))
return elements, text
def function_def_elements_extractor(text):
"""
Extract the function_definition section (both definition and body).
Acts like simple_elements_extractor, but only extracts function defs.
"""
logging.debug('Calling function_def_elements_extractor()')
lines = get_elements_lines(text, function.is_function_definition)
elements = []
for line in lines:
body, relative_body_end = extract_function_body(text[line:])
element = {'def': text[line], 'body': body}
elements.append(element)
# definition line already parsed. body lines start from next one.
line = line + 1
# cast range to list to fix python3 compatibility issues
rng = list(range(line, line+relative_body_end))
# append body lines to parsed lines
lines = lines + rng
text = remove_lines(text, lines)
# fancy logging
if logging.getLogger().isEnabledFor(logging.INFO):
elements_log_str = ""
max_id = len(elements)
spacing = len(str(max_id))
# leading 0
id_format = '{:0'+str(spacing)+'}'
# add space for ': ['
body_spacing = ' '*(spacing+2)
# line number: line content
# body line number: body content
for i in range(0, max_id):
# format line number
line_num = id_format.format(i)
# format line with max length default
elements_log_str += tools.instert_new_line_every("{}: {}\n".format(line_num, elements[i]['def']), new_line_padding=' '*(spacing+2))
# format function body
elements_log_str += tools.fancy_list_to_str(elements[i]['body'], body_spacing)
elements_log_str += "\n"
logging.info('Elements extracted with {}.{}()\n{}'.format(function.is_function_definition.__module__, function.is_function_definition.__name__, elements_log_str))
return elements, text
def extract_function_body(text):
"""
Extracts and returns the function body.
Returns also the number of the latest parsed line.
"""
logging.debug('Calling extract_function_body()')
if text[0][-1] != '{':
raise ParsingException('Unable to find function body initial delimiter "{".\n'+"{}".format(text))
try:
body_start = 1
# find the line composed by only the body final delimiter
# NB: is a relative value w.r.t. the function definition line
body_end = text.index(['}'])
except ValueError:
raise ParsingException('Unable to find function body final delimiter "}".\n'+"{}".format(text))
return text[body_start:body_end], body_end
def get_elements_lines(text, verification_function):
"""
Returns the line number of the elements on which verification_function returns true.
"""
logging.debug('Calling get_elements_lines()')
lines = []
for i, val in enumerate(text):
if verification_function(val):
lines.append(i)
return lines
def remove_lines(text, lines):
"""
Removes lines that have already been parsed.
"""
logging.debug('Calling remove_lines()')
if lines is None:
return text
# remove duplicates and revert back to list
lines = list(set(lines))
lines.sort()
# compensation value: if line 1 gets removed, line 2 becomes new line 1.
# on remove add 1 to compensation
compensation = 0
for i in lines:
del text[i-compensation]
compensation += 1
return text