diff --git a/py-helpers/02-prepare-js.sh b/py-helpers/02-prepare-js.sh index 6f433d3..682c1f8 100755 --- a/py-helpers/02-prepare-js.sh +++ b/py-helpers/02-prepare-js.sh @@ -7,4 +7,4 @@ cd "$( dirname -- "$0"; )" cd .. rm -rf temp -nope-py-prepare-code --input dist-py --output temp --type js \ No newline at end of file +nope-py-prepare-code --input dist-py --output temp --type js --convert_snake_case \ No newline at end of file diff --git a/py-helpers/prepare_code/__init__.py b/py-helpers/prepare_code/__init__.py index 6fbe8b8..a0ee526 100644 --- a/py-helpers/prepare_code/__init__.py +++ b/py-helpers/prepare_code/__init__.py @@ -6,6 +6,7 @@ __version__ = '0.1.0' from .logger import get_logger from .post_processor import post_process +from .helpers import define_dotted_dict, to_snake_case from .js import get_parser as get_parser_js, transform as transform_js from .ts import get_parser as get_parser_ts, transform as transform_ts from .main import main \ No newline at end of file diff --git a/py-helpers/prepare_code/helpers.py b/py-helpers/prepare_code/helpers.py new file mode 100644 index 0000000..4fbfd51 --- /dev/null +++ b/py-helpers/prepare_code/helpers.py @@ -0,0 +1,36 @@ +import ast, _ast + +CODE = '''class dotted_dict(dict): + """dot.notation access to dictionary attributes""" + __getattr__ = dict.get + __setattr__ = dict.__setitem__ + __delattr__ = dict.__delitem__''' + +def to_snake_case(str:str)->str: + """ Helper to convert the string to the snake-case + + Args: + str (str): The string to convert + + Returns: + str: The converted string + """ + if str == str.upper(): + return str + + ret = ''.join(['_'+i.lower() if i.isupper() + else i for i in str]).lstrip('_') + + if ret.startswith("_"): + ret[1:] + + return ret + +def define_dotted_dict(type= "name"): + """ Returns the Deinfition of the dotdict class + """ + + if (type == "name"): + return _ast.Name(id= "dotted_dict") + + return ast.parse(CODE).body[0] \ No newline at end of file diff --git a/py-helpers/prepare_code/js/grammar.js.lark b/py-helpers/prepare_code/js/grammar.js.lark index c496f85..a36c283 100644 --- a/py-helpers/prepare_code/js/grammar.js.lark +++ b/py-helpers/prepare_code/js/grammar.js.lark @@ -141,11 +141,11 @@ decrement: accessor "--" invert: "!" ret_expr -instanceof: id "instanceof" id +instanceof.100: id "instanceof" id -typeof: "typeof" accessor +typeof.100: "typeof" accessor -delete_stmt: "delete" accessor +delete_stmt.100: "delete" accessor await_stmt: "await" ret_expr @@ -355,21 +355,21 @@ break_statement: "break" // Define a If - Statement // We have to consider "if" "else if" and "else" -if_statement: "if" "(" ret_expr ")" body_or_expr_with_terminator [else_if_statements] [else_statement] +if_statement.10: "if" "(" ret_expr ")" body_or_expr_with_terminator [else_if_statements] [else_statement] else_if_statements: else_if_statement+ -else_if_statement: "else" "if" "(" ret_expr ")" body_or_expr_with_terminator -else_statement: "else" body_or_expr_with_terminator +else_if_statement.9: "else" "if" "(" ret_expr ")" body_or_expr_with_terminator +else_statement.9: "else" body_or_expr_with_terminator -inline_if: ret_expr "?" ret_expr ":" ret_expr +inline_if.10: ret_expr "?" ret_expr ":" ret_expr // -------------------------------------------------------------------------- // switch-case // -------------------------------------------------------------------------- -switch: "switch" "(" ret_expr ")" switch_body -switch_body: "{" (switch_case* [switch_default])* "}" -switch_case: "case" ret_expr ":" switch_case_body -switch_default: "default" ":" switch_case_body +switch.10: "switch" "(" ret_expr ")" switch_body +switch_body.9: "{" (switch_case* [switch_default])* "}" +switch_case.8: "case" ret_expr ":" switch_case_body +switch_default.7: "default" ":" switch_case_body switch_case_body: ("{" switch_case_statements "}") | switch_case_statements diff --git a/py-helpers/prepare_code/js/transformer.py b/py-helpers/prepare_code/js/transformer.py index d94e735..777d6c8 100644 --- a/py-helpers/prepare_code/js/transformer.py +++ b/py-helpers/prepare_code/js/transformer.py @@ -5,16 +5,9 @@ import logging # from prepare_code import get_logger from ..logger import get_logger +from ..helpers import to_snake_case, define_dotted_dict from lark import Transformer - -def to_snake_case(str): - if str == str.upper(): - return str - return ''.join(['_'+i.lower() if i.isupper() - else i for i in str]).lstrip('_') - - class CodeTransformeJs(Transformer): def __init__(self, visit_tokens: bool = True, level=logging.INFO, to_snake_case=False, switch_case_to_if_else=True, **kwargs) -> None: @@ -87,6 +80,7 @@ class CodeTransformeJs(Transformer): "switch", "try_catch", "class_statement", + # "constructor", # "method", # "async_method", "start" @@ -150,6 +144,8 @@ class CodeTransformeJs(Transformer): try: self._logger.debug( f"calling '{__name}' resulted in result={ast.dump(items[0])}") + self._logger.debug( + f"calling '{__name}' created node-id={items[0]}") except: self._logger.debug( f"calling '{__name}' resulted in result={items[0]}") @@ -197,7 +193,7 @@ class CodeTransformeJs(Transformer): self._log(__name, items) # Call the original Function - result = attr(items) + result = attr(items, **kwargs) try: self._logger.debug( @@ -207,9 +203,11 @@ class CodeTransformeJs(Transformer): f"calling '{__name}' resulted in result={result}") try: + # Test if the Elment does not contain a body. if __name not in _contains_body and type(result) not in (list, tuple, dict): # Now register some callbacks: self._add_to_tree(result, items) + except: pass @@ -267,11 +265,13 @@ class CodeTransformeJs(Transformer): for item in items: if type(item) is list: to_check += item - else: + elif item is not None: to_check.append(item) - else: + elif items is not None: to_check.append(items) + self._logger.debug(f"'_add_to_tree' -> '{ret}' checks {to_check}") + for item in to_check: try: self._add_to_tree_single(ret, item) @@ -281,11 +281,6 @@ class CodeTransformeJs(Transformer): def _add_to_tree_single(self, parent, item): id = item - try: - id = item.name - except: - pass - if id in self._ids_to_anonymous_func: if parent not in self._anonymous_func_tree: self._anonymous_func_tree[parent] = set() @@ -293,6 +288,19 @@ class CodeTransformeJs(Transformer): self._anonymous_func_tree[parent].add( self._ids_to_anonymous_func[id] ) + + self._logger.debug(f"\t\t'{parent}' added defintions (function_defintion) for {self._anonymous_func_tree[parent]}") + + elif id in self._anonymous_func_to_ids: + if parent not in self._anonymous_func_tree: + self._anonymous_func_tree[parent] = set() + + self._anonymous_func_tree[parent].add( + id + ) + + self._logger.debug(f"\t\t'{parent}' added defintions (a function) for {self._anonymous_func_tree[parent]}") + elif item in self._anonymous_func_tree: if parent not in self._anonymous_func_tree: self._anonymous_func_tree[parent] = set() @@ -302,11 +310,19 @@ class CodeTransformeJs(Transformer): self._anonymous_func_tree[parent] = s.union( self._anonymous_func_tree[item]) + self._logger.debug(f"\t\t'{parent}' added defintions (a containing node) for {self._anonymous_func_tree[parent]}") + + else: + self._logger.debug(f"\t\tfailed to find function for '{id}'") + self._logger.debug(f"\t\t_anonymous_func_to_ids: '{self._anonymous_func_to_ids}'") + self._logger.debug(f"\t\t_ids_to_anonymous_func: '{self._ids_to_anonymous_func}'") + def _adapt_body(self, body): defintions_to_add = set() for item in body: if item in self._anonymous_func_tree: + self._logger.debug(f"Found callback items for {item}") defintions_to_add = defintions_to_add.union( self._anonymous_func_tree[item]) self._anonymous_func_tree.pop(item) @@ -327,13 +343,13 @@ class CodeTransformeJs(Transformer): for _idx, _item in enumerate(item): try: self._logger.debug( - f"\t\titem[{idx}]->{_idx}: {ast.dump(_item)}") + f"\t\titem[{idx}]->{_idx}: {ast.dump(_item)} - '{_item}'") except: self._logger.debug( f"\t\titem[{idx}]->{_idx}: {_item}") else: self._logger.debug( - f"\t\titem[{idx}]: {ast.dump(item)}") + f"\t\titem[{idx}]: {ast.dump(item)} - '{_item}'") except: self._logger.debug( f"\t\titem[{idx}]: {item}") @@ -351,6 +367,7 @@ class CodeTransformeJs(Transformer): def start(self, items): self._log("start", items) body = self.body(items) + body.insert(0, define_dotted_dict("ast")) return _ast.Module(body=body) def return_statement(self, items): @@ -472,13 +489,21 @@ class CodeTransformeJs(Transformer): ) def instanceof(self, items): - return _ast.Call( - func=_ast.Name(id='type'), - args=[ - items[0] + + ret = self._op( + [ + _ast.Call( + func=_ast.Name(id='type'), + args=[ + items[0], + ], + keywords=[] + ), + items[1] ], - keywords=[] + self.bool_op_is([]) ) + return ret def delete_stmt(self, items): return _ast.Delete( @@ -584,6 +609,9 @@ class CodeTransformeJs(Transformer): def bool_op_in(self, items): return _ast.In() + def bool_op_is(self, items): + return _ast.Is() + def list(self, items): if items[0] is not None: @@ -639,11 +667,31 @@ class CodeTransformeJs(Transformer): keys += item["keys"] values += item["values"] - ret = _ast.Dict( - keys=keys, - values=self._adapt_items(values), - ctx=ast.Load() - ) + args = self.func_args([]) + args.args= [ + _ast.Dict( + keys=keys, + values=self._adapt_items(values), + ctx=ast.Load() + ) + ] + + ret = self.new_class([ + define_dotted_dict(), + [ + _ast.Dict( + keys=keys, + values=self._adapt_items(values), + ctx=ast.Load() + ) + ] + ]) + + # ret = _ast.Dict( + # keys=keys, + # values=self._adapt_items(values), + # ctx=ast.Load() + # ) return ret @@ -940,7 +988,7 @@ class CodeTransformeJs(Transformer): def reassign(self, items): return self.declare_var([None, None, items[0], items[1]]) - def _function(self, items, type=None): + def _function(self, items, func_type=None): """ Uses Rule = [export] "function" [id] "(" [func_args] ")" func_body Args: @@ -951,7 +999,7 @@ class CodeTransformeJs(Transformer): _type_: _description_ """ - _constructor = _ast.AsyncFunctionDef if type == "async" else _ast.FunctionDef + _constructor = _ast.AsyncFunctionDef if func_type == "async" else _ast.FunctionDef (export, name, func_args, body) = items @@ -977,9 +1025,9 @@ class CodeTransformeJs(Transformer): ret = _constructor(** kwargs) # Register the Function. - self._anonymous_func_to_ids[ret] = name - self._ids_to_anonymous_func[name.id] = ret - # self._anonymous_func_tree[ret] = name + def_name = _ast.Name(id = "__definition_of__"+name.id) + self._anonymous_func_to_ids[ret] = def_name + self._ids_to_anonymous_func[def_name] = ret return ret @@ -991,11 +1039,11 @@ class CodeTransformeJs(Transformer): def async_function(self, items): self._log("async_function", items) - return self._function(items, "async") + return self._function(items, func_type="async") - def _arrow_function(self, items, type=None): + def _arrow_function(self, items, func_type=None): self._log("_arrow_function", items) - return self._function([None, None, *items], type) + return self._function([None, None, *items], func_type=func_type) def arrow_function(self, items): self._log("arrow_function", items) @@ -1010,7 +1058,7 @@ class CodeTransformeJs(Transformer): def async_arrow_function(self, items): self._log("async_arrow_function", items) - return self._arrow_function(items, "async") + return self._arrow_function(items, func_type="async") def func_args(self, items): ret = { @@ -1032,7 +1080,12 @@ class CodeTransformeJs(Transformer): else: ret["vararg"] = ret["vararg"][0] - return _ast.arguments(**ret) + + args = _ast.arguments(**ret) + + self._add_to_tree(args, ret["args"] + ret["defaults"]) + + return args def default_func_arg(self, items): @@ -1112,6 +1165,8 @@ class CodeTransformeJs(Transformer): keywords=[] ) + # self._add_to_tree(ret, args) + return ret def rest_call_arg(self, items): @@ -1216,7 +1271,7 @@ class CodeTransformeJs(Transformer): test = items[0] body = items[1] elifs = items[2] - else_body = items[3] if items[3] is not None else [] + else_body = self._adapt_body(items[3]) if items[3] is not None else [] inclused_elif = elifs is not None @@ -1405,7 +1460,7 @@ class CodeTransformeJs(Transformer): args=[], defaults=[]) args.args.insert(0, _ast.arg(arg='self', annotation=None)) - return _ast.FunctionDef(name='__init__', args=args, body=body, decorator_list=[]) + return _ast.FunctionDef(name='__init__', args=args, body= self._adapt_body(body), decorator_list=[]) def getter(self, items): # Rule = id "(" [func_args] ")" func_body @@ -1452,7 +1507,7 @@ class CodeTransformeJs(Transformer): def async_method(self, items): # Rule = "async" id "(" [func_args] ")" func_body - ret = self._function([None, *items], "async") + ret = self._function([None, *items], func_type="async") method_args = [_ast.arg(arg='self', annotation=None)] method_args.extend(ret.args.args) @@ -1461,12 +1516,16 @@ class CodeTransformeJs(Transformer): return ret -def transform(tree, debug): +def transform(tree, debug, to_snake_case): transformer = CodeTransformeJs( - True, logging.DEBUG if debug else logging.INFO) + level = logging.DEBUG if debug else logging.INFO, + to_snake_case = to_snake_case + ) program = transformer.transform(tree) + code = astor.to_source(program) + if debug: print(ast.dump(program, indent=2)) @@ -1478,6 +1537,6 @@ def transform(tree, debug): print("-"*l) print("\n"*2) - code = astor.to_source(program) + print(code) return code diff --git a/py-helpers/prepare_code/main.py b/py-helpers/prepare_code/main.py index cc9d0b0..f55a0a2 100644 --- a/py-helpers/prepare_code/main.py +++ b/py-helpers/prepare_code/main.py @@ -3,7 +3,7 @@ import os import re import multiprocessing as mp -from . import get_logger, ts, js, post_process +from . import get_logger, ts, js, post_process, to_snake_case MAX_CPU = mp.cpu_count() @@ -29,18 +29,19 @@ def worker(opt): return parse(func[type].get_parser(), *opt) -def parse(parser, type, logger, input_path, output_path, name, path_to_file, dir_path, debug): +def parse(parser, type, logger, input_path, output_path, name, path_to_file, dir_path, debug, convert_snake_case): """ Function to generate the python-code Args: - parser (_type_): The Parser to use (js/ts) - logger (_type_): The Logger - input_path (_type_): path of the folder to readin - output_path (_type_): main path of the output - name (_type_): name of the file - path_to_file (_type_): path to the file - dir_path (_type_): directory of the file - debug (_type_): Flag to enable debugging + parser: The Parser to use (js/ts) + logger (logging.logger): The Logger + input_path (str): path of the folder to readin + output_path (str): main path of the output + name (str): name of the file + path_to_file (str): path to the file + dir_path (str): directory of the file + debug (boolean): Flag to enable debugging + convert_snake_case (boolean): Flag to enable converting methods and names to ids. Returns: ( @@ -59,8 +60,13 @@ def parse(parser, type, logger, input_path, output_path, name, path_to_file, dir try: python_name = name.replace(".ts", ".py").replace(".js", ".py") + rel_path = dir_path[len(input_path) + 1:] + if convert_snake_case: + python_name = to_snake_case(python_name) + rel_path = to_snake_case(rel_path) + logger.debug(f"determined the following rel-path = {rel_path}") pytho_path_to_file = os.path.join(output_path,rel_path,python_name) @@ -68,7 +74,8 @@ def parse(parser, type, logger, input_path, output_path, name, path_to_file, dir try: code = func[type].transform( parser.parse(open(path_to_file, encoding="utf-8").read()), - debug + debug, + convert_snake_case ) logger.debug(f"converted {path_to_file}") @@ -136,6 +143,8 @@ def main(): help='Shows debug related output') parser.add_argument('--cores', type=int, default=MAX_CPU, dest='cores', help='The Amount of cores, which must be use') + parser.add_argument('--convert_snake_case', dest='convert_snake_case', action='store_true', + help='Converts the names to snake-case') # Create a Logger: logger = get_logger("nope-py-prepare") @@ -215,10 +224,10 @@ def main(): file_name, # Name of the File path_to_file, # Path to the File dir_path, # Path of the Directory. - args.debug + args.debug, + args.convert_snake_case ) for file_name, path_to_file, dir_path in typescript_files - ] - ) + ]) # Close the Pool pool.close() # Wait to finish all. diff --git a/py-helpers/prepare_code/post_processor.py b/py-helpers/prepare_code/post_processor.py index 6dab253..9ac5770 100644 --- a/py-helpers/prepare_code/post_processor.py +++ b/py-helpers/prepare_code/post_processor.py @@ -11,16 +11,19 @@ replacers = { "const _this = this;": "", "_this": "self", "this": "self", - "Set": "set", - "Map": "dict", + " Set": " set", + " Map": " dict", "toLowerCase": "lower", "toUpperCase": "upper", ".push(": ".append(", + ".indexOf(": ".index(", "Array.from": "list", "null": "None", '"null"': "None", - '"undefined"': "None" - + '"undefined"': "None", + 'undefined': "None", + 'self = self': "", + "__definition_of__": "" } def post_process(code: str) -> str: diff --git a/py-helpers/prepare_code/ts/transformer.py b/py-helpers/prepare_code/ts/transformer.py index 69a9baf..7a6bd0c 100644 --- a/py-helpers/prepare_code/ts/transformer.py +++ b/py-helpers/prepare_code/ts/transformer.py @@ -901,7 +901,7 @@ class DebuggedCodeTransformeTs(Transformer): raise KeyError(f"'{__name}' has not been found!") -def transform(tree, debug): +def transform(tree, debug, to_snake_case): transformer = CodeTransformeTs( True, logging.DEBUG if debug else logging.INFO) program = transformer.transform(tree)