This commit is contained in:
Martin Karkowski 2022-08-03 13:18:50 +02:00
commit 70e17ec2f1
12 changed files with 250 additions and 82 deletions

View File

@ -124,7 +124,61 @@ export function rqueryAttr<T>(
/**
* Helper to query data from an object.
* @example data = [{a:1},{a:2}]; rqueryAttr(data, "+/a") -> [{path: "0/a", data: 0},{path: "1/a", data: 1}]
*
* props is defined as followed:
* ```typescript
* props: {
* key: string;
* query: string;
* }[]
* ```
*
* @example Example 1:
*
* ```javascript
*
* const data = { "deep": { "nested": "test" } };
* const result = convert_data(data, [
* {
* "key": "result",
* "query": "deep/nested",
* },
* ]);
*
* // ==> result = [{"result": "test"}]
* ```
*
* @example Example 2:
*
* ```javascript
* data = {
* "array": [
* {
* "data1": 0,
* "data2": "a",
* },
* {
* "data1": 1,
* "data2": "a",
* },
* ],
* "not": { "nested": "hello" }
* }
*
* let result = convert_data(data, [
* {
* "key": "a",
* "query": "array/+/data1",
* },
* {
* "key": "b",
* "query": "array/+/data2",
* },
* ])
*
* // ==> result = [{"a": 0, "b": "a"}, {"a": 1, "b": "a"}]
* ```
*
* @param data The data
* @param query The query to use.
* @returns Returns an array
@ -186,9 +240,9 @@ export function convertData<T>(
* @author M.Karkowski
* @export
* @param {*} _data The Object, where the data should be stored
* @param {string} _path The Path of the Attribute. All are seprated by a the splitchar. (Defaults to'.' => For Instance 'a.b.0.a.c')
* @param {string} _path The Path of the Attribute. All are seprated by a the splitchar. (Defaults to'.' => For Instance 'a/b/0/a/c')
* @param {*} _value The Value which should be Stored in the Attribute.
* @param {string} [_SPLITCHAR=SPLITCHAR] The Splitchar to use. Defaults to "."
* @param {string} [_SPLITCHAR=SPLITCHAR] The Splitchar to use. Defaults to "/"
*/
export function rsetattr(
_data: any,

View File

@ -16,6 +16,12 @@ export function convertPath(path: string): string {
}
/**
* Returns the least common segmet of all pathes, included in the pathes array.
*
* The Following options are available.
*
* "considerSingleLevel":boolean -> allows "singlelevel"-wildcards in the segments
* "considerMultiLevel":boolean -> allows "multilevel"-wildcards in the segments
*
* @param pathes The Segments to compare
* @param opts Additional Options.

View File

@ -152,7 +152,7 @@ export function generateResult(
* @export
* @param {string} pathPattern The pattern to test
* @param {string} contentPath The path to use as basis
* @return {*} {TPathCompareResult}
* @return {TPathCompareResult}
*/
export function comparePatternAndPath(
pathPattern: string,

View File

@ -40,9 +40,9 @@ export function replaceAll(
/**
* Function to Pad a String.
* @param num
* @param size
* @param maxLength
* @param num The Number to pad
* @param size the amount of zeros to add
* @param maxLength The max length of the number.
*/
export function padString(
num: number,

View File

@ -7,4 +7,4 @@ cd "$( dirname -- "$0"; )"
cd ..
rm -rf temp
nope-py-prepare-code --input dist-py --output temp --type js
nope-py-prepare-code --input dist-py --output temp --type js --convert_snake_case

View File

@ -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

View File

@ -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]

View File

@ -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

View File

@ -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(
ret = self._op(
[
_ast.Call(
func=_ast.Name(id='type'),
args=[
items[0]
items[0],
],
keywords=[]
),
items[1]
],
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(
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

View File

@ -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.

View File

@ -16,11 +16,14 @@ replacers = {
"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:

View File

@ -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)