from typing import Tuple, Dict, List, Any
from text_tools import (
find_first_of_characters,
rfind_first_of_characters,
smart_find_first_of_characters,
find_scope_borders,
find_first_not_restricted_character,
)
from parse_arguments import parse_arguments, parse_type
def parse_method_or_constructor(data: str, start: int = 0) -> Tuple[int, Dict]:
"""
Returns end-pos, dict-parsed method or constructor
Note: 'function' in names of variables is alias for 'method or constructor'
"""
res: Dict[str, Any] = {}
next_semicolon = smart_find_first_of_characters(";", data, start)
start_of_body = smart_find_first_of_characters("{", data, start)
if next_semicolon <= start_of_body:
end_of_function_declaration = next_semicolon
end_of_function = next_semicolon
elif start_of_body != len(data):
start_of_body, end_of_body = find_scope_borders(data, start_of_body)
end_of_function_declaration = start_of_body
end_of_function = end_of_body
else:
raise RuntimeError("Error! End of function declaration not found\n")
if data[start: find_first_of_characters("(", data, start)].find("operator") != -1:
return end_of_function + 1, {}
end_of_args = parse_declaration_without_postfix(data, start, res)
colon_pos = find_first_of_characters(":", data, end_of_args, end_of_function_declaration)
if colon_pos == len(data):
function_declaration_postfix = data[end_of_args + 1 : end_of_function_declaration].strip(" \n")
res["raw_declaration"] = data[start:end_of_function_declaration].strip(" \n")
else:
function_declaration_postfix = data[end_of_args + 1 : colon_pos].strip(" \n")
res["raw_declaration"] = data[start:colon_pos].strip(" \n")
end_of_initializers, initializers = parse_initializers(data, colon_pos + 1)
updated_args, other_initializers = extract_init_args(res["args"], initializers)
if updated_args != []:
res["args"] = updated_args
if other_initializers != []:
res["other_initializers"] = other_initializers
if data[end_of_initializers] == '{':
start_of_body, end_of_body = find_scope_borders(data, end_of_initializers, "{")
end_of_function = end_of_body
if len(function_declaration_postfix):
res["postfix"] = function_declaration_postfix
if end_of_function < len(data) and data[end_of_function] != ';':
first_body_token_pos = find_first_not_restricted_character(' \n\t', data, start_of_body + 1)
if data[first_body_token_pos: first_body_token_pos + len('return')] == 'return':
res["additional_attributes"] = "get"
return end_of_function + 1, res
setters_begin_names = ["Set", "Add", "Update"]
setters_banned_names = ["SetOriginalNode", "SetParent", "SetRange", "SetStart", "SetEnd", "SetAnnotations"]
is_setter = any(res["name"].startswith(s) for s in setters_begin_names)
is_banned_setter = any(res["name"].find(s) != -1 for s in setters_banned_names)
if is_setter and not is_banned_setter:
res["additional_attributes"] = "set"
return end_of_function + 1, res
def parse_declaration_without_postfix(data: str, start: int, res: Dict[str, Any]) -> int:
start_of_args = smart_find_first_of_characters("(", data, start)
if start_of_args >= len("operator") and data[start_of_args - len("operator") : start_of_args] == "operator":
start_of_args = find_first_of_characters("(", data, start_of_args + 1)
if (
start_of_args > find_first_of_characters(";", data, start)
and data[start : data.find("\n", start)].find("operator==") == -1
):
raise RuntimeError("Not method or constructor!")
end_of_args, res["args"] = parse_arguments(data, start_of_args)
start_of_function_name = rfind_first_of_characters(" *&\n", data, start_of_args - 1) + 1
if start_of_function_name > len(data):
start_of_function_name = 0
res["name"] = data[start_of_function_name:start_of_args]
if res["name"].isupper():
raise RuntimeError("New macros found: '" + res["name"] + "'. Please add it to list.")
res["return_type"] = parse_type(data[start:start_of_function_name].strip(" \n"))
if not res["return_type"]:
del res["return_type"]
return end_of_args
def parse_initializer(init: str) -> dict:
"""
Note ' left (left init) ' ---> 'class field': 'left', 'init value': 'left init'
"""
init = init.strip(" \n")
res = {}
parenthese_open = find_first_of_characters("{(", init, 0)
if parenthese_open != -1:
parenthese_open, parenthese_close = find_scope_borders(init, parenthese_open, "")
else:
raise RuntimeError("Error! Can't find '(' or '{' in parse_initializer: '" + init + "'")
res["class_field"] = init[:parenthese_open].strip(" \n")
res["init_value"] = init[parenthese_open + 1 : parenthese_close]
return res
def parse_initializers(data: str, start: int = 0) -> Tuple[int, List]:
res = []
current_pos = find_first_not_restricted_character(" \n", data, start)
parethese_open = find_first_of_characters("{(", data, current_pos)
while data[current_pos] != "{" and data[current_pos] != ';' and parethese_open != len(data):
parethese_open, parenthese_close = find_scope_borders(data, parethese_open, "")
res.append(parse_initializer(data[current_pos : parenthese_close + 1]))
current_pos = find_first_not_restricted_character(", \n", data, parenthese_close + 1)
parethese_open = find_first_of_characters("{(", data, current_pos)
return current_pos, res
def extract_init_args(args: list, initializers: list) -> Tuple[List, List]:
"""
If some argument is init value for class field, it adds property for this argument
and remove it from initializer list:
Constructor(char *a, ...) : class_field_(a), ... {}
Args list:
Before: {'name': 'a', 'type': 'char *'}
After: {'name': 'a', 'type': 'char *', 'initializer_for': 'class_field_'}
Initializers list:
Before: {'class_field': 'class_field_', 'init_value': 'a'}
After: {}
"""
i = 0
j = 0
while i < len(initializers):
while j < len(args) and i < len(initializers):
if args[j]["name"] == initializers[i]["init_value"]:
args[j]["initializer_for"] = initializers[i]["class_field"]
initializers.pop(i)
j = 0
continue
j += 1
i += 1
j = 0
return args, initializers