"""Depository of parsing methods.
This module holds various methods used for parsing of the header and normal C files and their
subsequent interpretation into corresponding python objects.
"""
import json5
import mib_generator.data.warn as warn
import mib_generator.parsing.load as load
[docs]
def clean(stri, tokens):
"""Replace tokens with spaces.
Takes a string and a list of tokens and replaces all occurences of the tokens in the string
with an appropriate number of space so the absolute positions of unaffected parts of the
string stay the same.
Args:
stri (str): A string to be "cleaned".
tokens (set): A set of tokens to be replaced.
Returns:
str: The resulting "cleaned" string.
"""
c = stri
for i in tokens:
c = c.replace(i, " " * len(i))
return c
[docs]
def erase_text(stri):
"""Erase all text inside quotation marks in the given string.
Looks for first order quotation marks (that is, not inside another quotation marks) and
replaces their content with matching amount of spaces so that the total length of the
string remains the same.
Args:
stri (str): The string in which the quotation marks are to be replaced.
Returns:
str: The string with the quotation marks contents replaced.
"""
mod = 0 # 0 for no quotation, 1 for ' and 2 for "
new = stri
for i in range(len(stri)):
if stri[i] == '"' and mod != 2:
if mod == 0:
mod = 1
else:
mod = 0
elif stri[i] == "'" and mod != 1:
if mod == 0:
mod = 2
else:
mod = 0
elif mod != 0:
new = new[:i] + " " + new[i + 1 :]
return new
[docs]
def line_index(stri):
"""Give list of indexes of line starts of the given string.
Based on the newline character, looks for indexes of new lines and adds them to a list.
Args:
stri (str): The string to be analysed.
Returns:
list: List of indexes at which new lines start.
"""
starts = []
for i in range(len(stri)):
if stri[i] == "\n":
starts.append(i + 1)
return starts
[docs]
def preproc_parse(stri):
"""Parse C-preprocessor conditional syntax into logic blocks.
Looks for logical C-preprocessor directives (specifically ``#ifdef``, ``#ifndef``, etc.), and marks
the start/end indices of the corresponding "logic blocks" (i.e. sections beween ``#ifdef`` and ``#else``).
Then returns these block indexes in a list. Also performs some checks along the way.
Args:
stri (str): The string to be analysed/parsed.
Returns:
list: List of list of indexes denoting start/end positions of the logic blocks.
"""
blocks = []
log = []
status = 0
status_log = [0]
for i in range(len(stri)):
if stri[i : i + 6] == "#ifdef":
log.append([i])
status_log.append(status)
status = 1
elif stri[i : i + 7] == "#ifndef":
log.append([i])
status_log.append(status)
status = 1
elif stri[i : i + 5] == "#else":
log[-1].append(i)
if status != 1:
warn.raises("WPM1")
status = 2
elif stri[i : i + 6] == "#endif":
if status not in {1, 2}:
warn.raises("WPM1")
status = status_log.pop(-1)
log[-1].append(i)
blocks.append(log.pop(-1))
if status != 0:
warn.raises("WPM1")
return blocks
[docs]
def preproc_eval(variab):
"""Evaluate whether a pre-processing condition holds.
Looks into config file whether the passed pre-processing condition holds. If the condition name
does not occur in the config file, asks the user what boolean value it should assign to it.
Args:
variab (str): Name of the pre-processing variable/condition to be evaluated.
Returns:
bool: Truth-value of the condition.
"""
config = load.conf["def"]
if variab in config.keys():
return bool(config[variab])
else:
return bool(input("Is " + variab + "? (skip for False) "))
[docs]
def preproc_filter(stri_o, stri_c):
"""Filter out what parts of the C-code should be further considered based on preprocessor directives.
First, this function looks for blocks pre-processor logic using the :obj:`preproc_parse` function. It then evaluates
the condition associated with this logic with :obj:`preproc_eval` and depending on the result, decides what
parts of the string should be deleted (replaced by equal number of spaces). Finally it performs this replacement.
The preprocessor logic is determined based on the passed string with the code (``stri_o``), but the deliting
is applied also to the string with comment (``stri_c``).
Args:
stri_o (str): The text with C-code to be filtered on the basis of pre-processor logic.
stri_c (str): The text with comments to be filtered on the basis of pre-processor logic.
Returns:
tuple: A tuple containing:
* *str* - The string with C-code with the pre-processor logic applied.
* *str* - The string with comments with the pre-processor logic applied.
"""
blocks = preproc_parse(stri_o)
filt_o = stri_o
filt_c = stri_c
delete = []
for i in blocks:
condition = stri_o[i[0] : i[1]].split()[1]
evalu = preproc_eval(condition)
if stri_o[i[0] : i[1]].split()[0] == "#ifndef":
evalu = not evalu
if not evalu:
delete.append([i[0], i[1]])
if len(i) == 2:
delete.append([i[1], i[1] + 6])
else:
delete.append([i[1], i[1] + 5])
delete.append([i[2], i[2] + 6])
else:
if len(i) == 3:
delete.append([i[1], i[2]])
delete.append([i[2], i[2] + 6])
else:
delete.append([i[1], i[1] + 6])
if stri_o[i[0] : i[1]].startswith("#ifdef"):
delete.append([i[0], i[0] + 6])
else:
delete.append([i[0], i[0] + 7])
for i in delete:
filt_o = filt_o[: i[0]] + " " * (i[1] - i[0]) + filt_o[i[1] :]
filt_c = filt_c[: i[0]] + " " * (i[1] - i[0]) + filt_c[i[1] :]
return filt_o, filt_c
[docs]
def com_parse(comm):
"""Parse the comments in the string into blocks corresponding to singular comments.
Looks at each passed comment and evaluates whether it should be interpreted or not. If yes, it then calls
the class :obj:`comment` from it and adds this object to the list of interpreted comments.
Args:
comm (list): List of strings each representing the content of a single comment.
Returns:
list: List of found interpreted comments, each represented by an object of the :obj:`comment` class.
"""
depth = 0
start = 0
blocks = []
for i in range(len(comm)):
if comm[i : i + 3] == "/*{" and depth == 0:
start = i + 2
depth = 1
elif comm[i : i + 3] == "}*/" and depth == 1:
blocks.append(comment(start, i, comm[start : i + 1]))
depth = 0
elif comm[i : i + 2] == "//" and depth == 0:
depth = 2
elif comm[i] == "\n" and depth == 2:
depth = 0
return blocks