From 6f8cc52a65c8fc5f382a9fc2f8e80289a698a653 Mon Sep 17 00:00:00 2001 From: scientist19 <40214489+scientist19@users.noreply.github.com> Date: Thu, 3 Dec 2020 22:00:47 +0200 Subject: [PATCH 01/17] created project infrastructure for lab2, started impl of lexical_analysis package --- lab2/.gitignore | 139 ++++++++++++++++++++++++++++++ lab2/lexical_analysis/__init__.py | 0 lab2/lexical_analysis/lexema.py | 38 ++++++++ lab2/lexical_analysis/lexer.py | 31 +++++++ lab2/main.py | 3 + 5 files changed, 211 insertions(+) create mode 100644 lab2/.gitignore create mode 100644 lab2/lexical_analysis/__init__.py create mode 100644 lab2/lexical_analysis/lexema.py create mode 100644 lab2/lexical_analysis/lexer.py create mode 100644 lab2/main.py diff --git a/lab2/.gitignore b/lab2/.gitignore new file mode 100644 index 0000000..14054eb --- /dev/null +++ b/lab2/.gitignore @@ -0,0 +1,139 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +.idea/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ \ No newline at end of file diff --git a/lab2/lexical_analysis/__init__.py b/lab2/lexical_analysis/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lab2/lexical_analysis/lexema.py b/lab2/lexical_analysis/lexema.py new file mode 100644 index 0000000..416bd9b --- /dev/null +++ b/lab2/lexical_analysis/lexema.py @@ -0,0 +1,38 @@ +from enum import Enum + +class LexemType(Enum): + NUMBER = 0, + IDENTIFIER = 1, + STRING = 2, + LEFT_BRACKET = 3, + RIGHT_BRACKET = 4, + LEFT_PAREN = 5, + RIGHT_PAREN = 6, + LEFT_BRACE = 7, + RIGHT_BRACE = 8, + LESS_THAN = 9, + GREATER_THAN = 10, + EQUAL = 11, + OPERATOR = 12, + SLASH = 13, + HASH = 14, + DOT = 15, + COMMA = 16, + COLON = 17, + SEMICOLON = 18, + SINGLE_QUOTE = 19, + DOUBLE_QUOTE = 20, + WHITESPACE = 21, + UNEXPECTED = 22 + + +class Lexema: + def __init__(self, lexem_type, str): + self.__lexem_type = lexem_type + self.__str = str + + def has_type(self, lexem_type): + return self.__lexem_type == lexem_type + + def get_str(self): + return self.__str \ No newline at end of file diff --git a/lab2/lexical_analysis/lexer.py b/lab2/lexical_analysis/lexer.py new file mode 100644 index 0000000..e47c20f --- /dev/null +++ b/lab2/lexical_analysis/lexer.py @@ -0,0 +1,31 @@ +from lexical_analysis.lexema import Lexema +from lexical_analysis.lexema import LexemType +import re + +class Lexer: + @classmethod + def lex(cls, text): + str = text + lexems = [] + + while not str: + number, str = cls.__lex_number(str) + if number: + lexems.append(number) + continue + lexems.append(LexemType.UNEXPECTED, str[0]) + str = str[1:] + + return lexems + + @classmethod + def __lex_number(cls, str): + numbers = re.findall("[-+]?[.]?[\d]+(?:,\d\d\d)*[\.]?\d*(?:[eE][-+]?\d+)?", str) + first_number = numbers[0] + number_len = len(first_number) + + p_number = str[0, number_len] + if p_number == first_number: + return Lexema(LexemType.NUMBER, first_number), str[number_len:] + else: + return None, str diff --git a/lab2/main.py b/lab2/main.py new file mode 100644 index 0000000..b5c02cc --- /dev/null +++ b/lab2/main.py @@ -0,0 +1,3 @@ + +if __name__ == '__main__': + print('Hello world!') \ No newline at end of file From 6760356b6ecc9195246f11cc4aa44cf7be59c119 Mon Sep 17 00:00:00 2001 From: scientist19 <40214489+scientist19@users.noreply.github.com> Date: Thu, 3 Dec 2020 23:46:52 +0200 Subject: [PATCH 02/17] added lexing of strings, symbols, identifiers --- lab2/lexical_analysis/lexema.py | 49 +++++++++++++++-- lab2/lexical_analysis/lexer.py | 95 +++++++++++++++++++++++++++++++-- lab2/main.py | 27 +++++++++- 3 files changed, 161 insertions(+), 10 deletions(-) diff --git a/lab2/lexical_analysis/lexema.py b/lab2/lexical_analysis/lexema.py index 416bd9b..9caf956 100644 --- a/lab2/lexical_analysis/lexema.py +++ b/lab2/lexical_analysis/lexema.py @@ -1,4 +1,25 @@ from enum import Enum +KEYWORDS = ( + "abstract", "arguments", "await", "boolean", + "break", "byte", "case", "catch", + "char", "class", "const", + "continue", "debugger", "default", "delete", + "do", "double", "else", "enum", + "eval", "export", "extends", "false", + "final", "finally", "float", "for", + "function", "goto", "if", "implements", + "import", "int", "in", "instanceof", + "interface", "let", "long", "native", + "new", "null", "private", "package", + "protected", "public", + "return", "short", + "static", "super", + "synchronized", "switch", "this", "throw", + "true", "try", "typeof", "throws", + "transient", "var", "with", "yield", + "void", "volatile", + "while" +) class LexemType(Enum): NUMBER = 0, @@ -20,19 +41,37 @@ class LexemType(Enum): COMMA = 16, COLON = 17, SEMICOLON = 18, - SINGLE_QUOTE = 19, - DOUBLE_QUOTE = 20, + COMMENT = 19, + MULTI_COMMENT = 20, WHITESPACE = 21, - UNEXPECTED = 22 + UNEXPECTED = 22, + KEYWORD = 23, + FUNCTION_IDENTIFIER = 24, + CLASS_IDENTIFIER = 25, + VARIABLE_IDENTIFIER = 26, + AT = 27, + PRIVATE_FUNCTION_IDENTIFIER = 28, + CONSTANT_IDENTIFIER = 29, + ENUM_IDENTIFIER = 30 class Lexema: - def __init__(self, lexem_type, str): + def __init__(self, lexem_type, str, line=-1): self.__lexem_type = lexem_type self.__str = str + self.__line = line def has_type(self, lexem_type): return self.__lexem_type == lexem_type def get_str(self): - return self.__str \ No newline at end of file + return self.__str + + def get_line(self): + return self.__line + + def get_type(self): + return self.__lexem_type + + def set_type(self, new_type): + self.__lexem_type = new_type diff --git a/lab2/lexical_analysis/lexer.py b/lab2/lexical_analysis/lexer.py index e47c20f..718c18e 100644 --- a/lab2/lexical_analysis/lexer.py +++ b/lab2/lexical_analysis/lexer.py @@ -8,23 +8,112 @@ def lex(cls, text): str = text lexems = [] - while not str: + while str: + string, str = cls.__lex_str(str) + if string: + lexems.append(string) + continue + identifier, str = cls.__lex_identifier(str) + if identifier: + lexems.append(identifier) + continue number, str = cls.__lex_number(str) if number: lexems.append(number) continue - lexems.append(LexemType.UNEXPECTED, str[0]) + symbol, str = cls.__lex_symbol(str) + if symbol: + lexems.append(symbol) + continue + lexems.append(Lexema(LexemType.UNEXPECTED, str[0])) str = str[1:] return lexems + @classmethod + def __lex_symbol(cls, str): + if str[0] == '[': + return Lexema(LexemType.LEFT_BRACKET, str[0:1]), str[1:] + elif str[0] == ']': + return Lexema(LexemType.RIGHT_BRACKET, str[0:1]), str[1:] + elif str[0] == '(': + return Lexema(LexemType.LEFT_PAREN, str[0:1]), str[1:] + elif str[0] == ')': + return Lexema(LexemType.RIGHT_PAREN, str[0:1]), str[1:] + elif str[0] == '{': + return Lexema(LexemType.LEFT_BRACE, str[0:1]), str[1:] + elif str[0] == '}': + return Lexema(LexemType.RIGHT_BRACE, str[0:1]), str[1:] + elif str[0] == '<': + return Lexema(LexemType.LESS_THAN, str[0:1]), str[1:] + elif str[0] == '>': + return Lexema(LexemType.GREATER_THAN, str[0:1]), str[1:] + elif str[0] == '=': + return Lexema(LexemType.EQUAL, str[0:1]), str[1:] + elif str[0] == '*': + return Lexema(LexemType.OPERATOR, str[0:1]), str[1:] + elif str[0] == '+': + return Lexema(LexemType.OPERATOR, str[0:1]), str[1:] + elif str[0] == '%': + return Lexema(LexemType.OPERATOR, str[0:1]), str[1:] + elif str[0] == '-': + return Lexema(LexemType.OPERATOR, str[0:1]), str[1:] + elif str[0] == '/': + return Lexema(LexemType.OPERATOR, str[0:1]), str[1:] + elif str[0] == '&': + return Lexema(LexemType.OPERATOR, str[0:1]), str[1:] + elif str[0] == '|': + return Lexema(LexemType.OPERATOR, str[0:1]), str[1:] + elif str[0] == '#': + return Lexema(LexemType.HASH, str[0:1]), str[1:] + elif str[0] == '.': + return Lexema(LexemType.DOT, str[0:1]), str[1:] + elif str[0] == ',': + return Lexema(LexemType.COMMA, str[0:1]), str[1:] + elif str[0] == ':': + return Lexema(LexemType.COLON, str[0:1]), str[1:] + elif str[0] == ';': + return Lexema(LexemType.SEMICOLON, str[0:1]), str[1:] + elif str[0] == '@': + return Lexema(LexemType.AT, str[0:1]), str[1:] + else: + return None, str + + @classmethod + def __lex_identifier(cls, str): + i = 0 + if str[i] != '_' and not str[i].isalpha(): + return None, str + + while str[i].isalpha() or str[i].isnumeric() or str[i] == '_': + i += 1 + + return Lexema(LexemType.IDENTIFIER, str[0:i]), str[i:] + + @classmethod + def __lex_str(cls, str): + if str[0] != '\'' and str[0] != '\"': + return None, str + + quote = str[0] + i = 1 + + while str[i] != quote: + i += 1 + i += 1 + + return Lexema(LexemType.STRING, str[0:i]), str[i:] + @classmethod def __lex_number(cls, str): numbers = re.findall("[-+]?[.]?[\d]+(?:,\d\d\d)*[\.]?\d*(?:[eE][-+]?\d+)?", str) + if len(numbers) == 0: + return None, str + first_number = numbers[0] number_len = len(first_number) - p_number = str[0, number_len] + p_number = str[0:number_len] if p_number == first_number: return Lexema(LexemType.NUMBER, first_number), str[number_len:] else: diff --git a/lab2/main.py b/lab2/main.py index b5c02cc..4a5ba7e 100644 --- a/lab2/main.py +++ b/lab2/main.py @@ -1,3 +1,26 @@ +from lexical_analysis.lexer import Lexer -if __name__ == '__main__': - print('Hello world!') \ No newline at end of file + +def main(): + text = 'var x;\n' + \ + '// doing x = \"6E10\";\n\n' + \ + '/* useless comment\n' + \ + ' useless comment\n' + \ + ' useless comment*/\n' + \ + 'x = \"6E10\";\n' + \ + 'document.getElementById("demo").innerHTML = x;\n' \ + '@private\n' \ + '// comment\n' \ + '/* another comment\n' \ + 'bla bla */' \ + 'private_function (var1, var2, var3)' \ + '//@private\n' \ + 'not_private_function (var1, var2)' + + lexems = Lexer.lex(text) + + for lexem in lexems: + print(lexem.get_type()) + print('\'' + lexem.get_str() + '\'') + +main() \ No newline at end of file From d3d02584192520f639e207c62aa50fee054e7f81 Mon Sep 17 00:00:00 2001 From: scientist19 <40214489+scientist19@users.noreply.github.com> Date: Fri, 4 Dec 2020 00:24:44 +0200 Subject: [PATCH 03/17] added lexing of whitespaces and comments --- lab2/lexical_analysis/lexer.py | 48 ++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/lab2/lexical_analysis/lexer.py b/lab2/lexical_analysis/lexer.py index 718c18e..d71d3c2 100644 --- a/lab2/lexical_analysis/lexer.py +++ b/lab2/lexical_analysis/lexer.py @@ -9,6 +9,14 @@ def lex(cls, text): lexems = [] while str: + multi_comment, str = cls.__lex_multi_comment(str) + if multi_comment: + lexems.append(multi_comment) + continue + comment, str = cls.__lex_comment(str) + if comment: + lexems.append(comment) + continue string, str = cls.__lex_str(str) if string: lexems.append(string) @@ -21,6 +29,10 @@ def lex(cls, text): if number: lexems.append(number) continue + whitespace, str = cls.__lex_whitespace(str) + if whitespace: + lexems.append(whitespace) + continue symbol, str = cls.__lex_symbol(str) if symbol: lexems.append(symbol) @@ -79,6 +91,42 @@ def __lex_symbol(cls, str): else: return None, str + @classmethod + def __lex_whitespace(cls, str): + i = 0 + while str[i] in ' \n\r\t\v\0\f': + i += 1 + + if i == 0: + return None, str + else: + return Lexema(LexemType.WHITESPACE, str[0:i]), str[i:] + + @classmethod + def __lex_multi_comment(cls, str): + if not (str[0] == '/' and str[1] == '*'): + return None, str + i = 2 + + while i < len(str) - 1 and not (str[i] == '*' and str[i + 1] == '/'): + i += 1 + if i == len(str) - 1: + return None, str + i += 2 + + return Lexema(LexemType.MULTI_COMMENT, str[0:i]), str[i:] + + @classmethod + def __lex_comment(cls, str): + if not (str[0] == '/' and str[1] == '/'): + return None, str + i = 2 + + while str[i] != '\n': + i += 1 + + return Lexema(LexemType.COMMENT, str[0:i]), str[i:] + @classmethod def __lex_identifier(cls, str): i = 0 From 84feed77af6050202602e5cdfaf3496cf63bdb55 Mon Sep 17 00:00:00 2001 From: scientist19 <40214489+scientist19@users.noreply.github.com> Date: Fri, 4 Dec 2020 23:16:03 +0200 Subject: [PATCH 04/17] started impl of sementical_analysis package, added parsing of js functions --- lab2/main.py | 2 + lab2/semantical_analysis/__init__.py | 0 lab2/semantical_analysis/parser.py | 58 ++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 lab2/semantical_analysis/__init__.py create mode 100644 lab2/semantical_analysis/parser.py diff --git a/lab2/main.py b/lab2/main.py index 4a5ba7e..9621667 100644 --- a/lab2/main.py +++ b/lab2/main.py @@ -1,4 +1,5 @@ from lexical_analysis.lexer import Lexer +from semantical_analysis.parser import Parser def main(): @@ -18,6 +19,7 @@ def main(): 'not_private_function (var1, var2)' lexems = Lexer.lex(text) + Parser.parse(lexems) for lexem in lexems: print(lexem.get_type()) diff --git a/lab2/semantical_analysis/__init__.py b/lab2/semantical_analysis/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lab2/semantical_analysis/parser.py b/lab2/semantical_analysis/parser.py new file mode 100644 index 0000000..1919a09 --- /dev/null +++ b/lab2/semantical_analysis/parser.py @@ -0,0 +1,58 @@ +from lexical_analysis.lexema import LexemType + + +class Parser: + @classmethod + def parse(cls, lexems): + position = 0 + next_position = 0 + while position < len(lexems): + next_position = cls.__parse_function(lexems, position) + if next_position != position: + position = next_position + continue + position += 1 + + @classmethod + def __parse_function(cls, lexems, position): + if lexems[position].get_type() == LexemType.IDENTIFIER: + next_position = cls.__get_next_lexema_position(lexems, position) + + if next_position and lexems[next_position].get_type() == LexemType.LEFT_PAREN: + lexems[position].set_type(LexemType.FUNCTION_IDENTIFIER) + prev_position = cls.__get_prev_lexema_position(lexems, position) + + if prev_position and lexems[prev_position].get_str() == 'private' and \ + lexems[prev_position - 1].get_str() == '@': + lexems[position].set_type(LexemType.PRIVATE_FUNCTION_IDENTIFIER) + + i = next_position + 1 + while i < len(lexems) and lexems[i].get_type() != LexemType.RIGHT_PAREN: + if lexems[i].get_type() == LexemType.IDENTIFIER: + lexems[i].set_type(LexemType.VARIABLE_IDENTIFIER) + i += 1 + return i + + return position + + @classmethod + def __get_next_lexema_position(cls, lexems, current_position): + position = current_position + 1 + while position < len(lexems): + if lexems[position].get_type() != LexemType.COMMENT and \ + lexems[position].get_type() != LexemType.MULTI_COMMENT and \ + lexems[position].get_type() != LexemType.WHITESPACE: + return position + position += 1 + return None + + @classmethod + def __get_prev_lexema_position(cls, lexems, current_position): + position = current_position - 1 + while position > 0: + if lexems[position].get_type() != LexemType.COMMENT and \ + lexems[position].get_type() != LexemType.MULTI_COMMENT and \ + lexems[position].get_type() != LexemType.WHITESPACE: + return position + position -= 1 + return None \ No newline at end of file From 065d44b370f5fedd41843da4c29b06f16288ec34 Mon Sep 17 00:00:00 2001 From: scientist19 <40214489+scientist19@users.noreply.github.com> Date: Fri, 4 Dec 2020 23:58:15 +0200 Subject: [PATCH 05/17] added parsing of js variables and classes --- lab2/lexical_analysis/lexer.py | 6 ++-- lab2/main.py | 10 ++++-- lab2/semantical_analysis/parser.py | 51 ++++++++++++++++++++++++++++-- 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/lab2/lexical_analysis/lexer.py b/lab2/lexical_analysis/lexer.py index d71d3c2..d875e96 100644 --- a/lab2/lexical_analysis/lexer.py +++ b/lab2/lexical_analysis/lexer.py @@ -94,7 +94,7 @@ def __lex_symbol(cls, str): @classmethod def __lex_whitespace(cls, str): i = 0 - while str[i] in ' \n\r\t\v\0\f': + while i < len(str) and str[i] in ' \n\r\t\v\0\f': i += 1 if i == 0: @@ -122,7 +122,7 @@ def __lex_comment(cls, str): return None, str i = 2 - while str[i] != '\n': + while i < len(str) and str[i] != '\n': i += 1 return Lexema(LexemType.COMMENT, str[0:i]), str[i:] @@ -133,7 +133,7 @@ def __lex_identifier(cls, str): if str[i] != '_' and not str[i].isalpha(): return None, str - while str[i].isalpha() or str[i].isnumeric() or str[i] == '_': + while i < len(str) and (str[i].isalpha() or str[i].isnumeric() or str[i] == '_'): i += 1 return Lexema(LexemType.IDENTIFIER, str[0:i]), str[i:] diff --git a/lab2/main.py b/lab2/main.py index 9621667..d99afd3 100644 --- a/lab2/main.py +++ b/lab2/main.py @@ -13,10 +13,16 @@ def main(): '@private\n' \ '// comment\n' \ '/* another comment\n' \ - 'bla bla */' \ + 'bla bla */ const hello = \"hello\"\n' \ 'private_function (var1, var2, var3)' \ '//@private\n' \ - 'not_private_function (var1, var2)' + 'not_private_function (var1, var2)\n' \ + 'class /* comment */Rectangle {}\n' \ + 'colors = {\n' \ + ' RED: 1,\n' \ + ' GREEN: 2,\n' \ + ' blue: 3\n' \ + '}\n' lexems = Lexer.lex(text) Parser.parse(lexems) diff --git a/lab2/semantical_analysis/parser.py b/lab2/semantical_analysis/parser.py index 1919a09..e05941a 100644 --- a/lab2/semantical_analysis/parser.py +++ b/lab2/semantical_analysis/parser.py @@ -5,9 +5,16 @@ class Parser: @classmethod def parse(cls, lexems): position = 0 - next_position = 0 while position < len(lexems): next_position = cls.__parse_function(lexems, position) + if next_position != position: + position = next_position + continue + next_position = cls.__parse_variable(lexems, position) + if next_position != position: + position = next_position + continue + next_position = cls.__parse_class(lexems, position) if next_position != position: position = next_position continue @@ -22,7 +29,7 @@ def __parse_function(cls, lexems, position): lexems[position].set_type(LexemType.FUNCTION_IDENTIFIER) prev_position = cls.__get_prev_lexema_position(lexems, position) - if prev_position and lexems[prev_position].get_str() == 'private' and \ + if prev_position != None and lexems[prev_position].get_str() == 'private' and \ lexems[prev_position - 1].get_str() == '@': lexems[position].set_type(LexemType.PRIVATE_FUNCTION_IDENTIFIER) @@ -35,6 +42,44 @@ def __parse_function(cls, lexems, position): return position + @classmethod + def __parse_variable(cls, lexems, position): + if lexems[position].get_type() == LexemType.IDENTIFIER: + prev_position = cls.__get_prev_lexema_position(lexems, position) + + if prev_position != None: + prev_lexema_str = lexems[prev_position].get_str() + if prev_lexema_str == 'var' or \ + prev_lexema_str == 'let': + lexems[position].set_type(LexemType.VARIABLE_IDENTIFIER) + position += 1 + return position + elif prev_lexema_str == 'const': + lexems[position].set_type(LexemType.CONSTANT_IDENTIFIER) + position += 1 + return position + + next_position = cls.__get_next_lexema_position(lexems, position) + if next_position: + next_lexema = lexems[next_position] + if next_lexema.get_type() == LexemType.COLON: + lexems[position].set_type(LexemType.CONSTANT_IDENTIFIER) + position += 1 + + return position + + @classmethod + def __parse_class(cls, lexems, position): + if lexems[position].get_type() == LexemType.IDENTIFIER: + prev_position = cls.__get_prev_lexema_position(lexems, position) + + if prev_position != None: + prev_lexema_str = lexems[prev_position].get_str() + if prev_lexema_str == 'class': + lexems[position].set_type(LexemType.CLASS_IDENTIFIER) + position += 1 + return position + @classmethod def __get_next_lexema_position(cls, lexems, current_position): position = current_position + 1 @@ -49,7 +94,7 @@ def __get_next_lexema_position(cls, lexems, current_position): @classmethod def __get_prev_lexema_position(cls, lexems, current_position): position = current_position - 1 - while position > 0: + while position >= 0: if lexems[position].get_type() != LexemType.COMMENT and \ lexems[position].get_type() != LexemType.MULTI_COMMENT and \ lexems[position].get_type() != LexemType.WHITESPACE: From 88a1bd5c2ad3fbeadaa4ba9dc3a5aadfa859a8d6 Mon Sep 17 00:00:00 2001 From: scientist19 <40214489+scientist19@users.noreply.github.com> Date: Sun, 6 Dec 2020 19:35:22 +0200 Subject: [PATCH 06/17] fixed bug with function call as argument for another function --- lab2/main.py | 6 ++++++ lab2/semantical_analysis/parser.py | 20 +++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/lab2/main.py b/lab2/main.py index d99afd3..faf12d7 100644 --- a/lab2/main.py +++ b/lab2/main.py @@ -24,6 +24,12 @@ def main(): ' blue: 3\n' \ '}\n' + test_file = 'test.js' + + f = open(test_file, 'r') + text = f.read() + f.close() + lexems = Lexer.lex(text) Parser.parse(lexems) diff --git a/lab2/semantical_analysis/parser.py b/lab2/semantical_analysis/parser.py index e05941a..c306b33 100644 --- a/lab2/semantical_analysis/parser.py +++ b/lab2/semantical_analysis/parser.py @@ -26,6 +26,20 @@ def __parse_function(cls, lexems, position): next_position = cls.__get_next_lexema_position(lexems, position) if next_position and lexems[next_position].get_type() == LexemType.LEFT_PAREN: + i = next_position + 1 + opened_paren = 1 + while i < len(lexems) and opened_paren != 0: + if lexems[i].get_type() == LexemType.LEFT_PAREN: + opened_paren += 1 + elif lexems[i].get_type() == LexemType.RIGHT_PAREN: + opened_paren -= 1 + i += 1 + if i == len(lexems): + return position + next_after_right_paren = cls.__get_next_lexema_position(lexems, i) + if lexems[next_after_right_paren].get_type() != LexemType.LEFT_BRACE: + return position + lexems[position].set_type(LexemType.FUNCTION_IDENTIFIER) prev_position = cls.__get_prev_lexema_position(lexems, position) @@ -34,7 +48,11 @@ def __parse_function(cls, lexems, position): lexems[position].set_type(LexemType.PRIVATE_FUNCTION_IDENTIFIER) i = next_position + 1 - while i < len(lexems) and lexems[i].get_type() != LexemType.RIGHT_PAREN: + while i < len(lexems) and opened_paren != 0: + if lexems[i].get_type() == LexemType.LEFT_PAREN: + opened_paren += 1 + elif lexems[i].get_type() == LexemType.RIGHT_PAREN: + opened_paren -= 1 if lexems[i].get_type() == LexemType.IDENTIFIER: lexems[i].set_type(LexemType.VARIABLE_IDENTIFIER) i += 1 From d02b4faacc068d951632ad5576c4f2c46acba155 Mon Sep 17 00:00:00 2001 From: scientist19 <40214489+scientist19@users.noreply.github.com> Date: Sun, 6 Dec 2020 22:02:15 +0200 Subject: [PATCH 07/17] added naming_fixer --- lab2/lexical_analysis/lexema.py | 3 ++ lab2/lexical_analysis/lexer.py | 4 ++ lab2/main.py | 30 ++++++++++++++- lab2/semantical_analysis/naming_fixer.py | 45 ++++++++++++++++++++++ lab2/semantical_analysis/parser.py | 49 ++++++++++++++++++++---- 5 files changed, 123 insertions(+), 8 deletions(-) create mode 100644 lab2/semantical_analysis/naming_fixer.py diff --git a/lab2/lexical_analysis/lexema.py b/lab2/lexical_analysis/lexema.py index 9caf956..245ee47 100644 --- a/lab2/lexical_analysis/lexema.py +++ b/lab2/lexical_analysis/lexema.py @@ -67,6 +67,9 @@ def has_type(self, lexem_type): def get_str(self): return self.__str + def set_str(self, new_str): + self.__str = new_str + def get_line(self): return self.__line diff --git a/lab2/lexical_analysis/lexer.py b/lab2/lexical_analysis/lexer.py index d875e96..8e5dd2b 100644 --- a/lab2/lexical_analysis/lexer.py +++ b/lab2/lexical_analysis/lexer.py @@ -88,6 +88,10 @@ def __lex_symbol(cls, str): return Lexema(LexemType.SEMICOLON, str[0:1]), str[1:] elif str[0] == '@': return Lexema(LexemType.AT, str[0:1]), str[1:] + elif str[0] == '?': + return Lexema(LexemType.SEMICOLON, str[0:1]), str[1:] + elif str[0] == '!': + return Lexema(LexemType.AT, str[0:1]), str[1:] else: return None, str diff --git a/lab2/main.py b/lab2/main.py index faf12d7..7e20d33 100644 --- a/lab2/main.py +++ b/lab2/main.py @@ -1,4 +1,7 @@ +import re + from lexical_analysis.lexer import Lexer +from semantical_analysis.naming_fixer import NamingFixer from semantical_analysis.parser import Parser @@ -24,7 +27,7 @@ def main(): ' blue: 3\n' \ '}\n' - test_file = 'test.js' + test_file = 'D:\\Meta\\Lab2\\JsFiles\\test.js' f = open(test_file, 'r') text = f.read() @@ -37,4 +40,29 @@ def main(): print(lexem.get_type()) print('\'' + lexem.get_str() + '\'') + fixes = NamingFixer.analyze(lexems) + + for fix in fixes: + print(f'{fix[0]} -> {fix[1]}') + ''' + test1 = 'HelloWorld' + test2 = 'HELLO_WORLD123' + test3 = 'hello_world' + test4 = 'helloWorld' + test5 = 'IOManip' + + print(test1) + print(camel_to_snake(test1)) + print(test2) + print(camel_to_snake(test2)) + print(test3) + print(camel_to_snake(test3)) + print(test4) + print(camel_to_snake(test4)) + print(test5) + print(camel_to_snake(test5)) + print() + print(test2.isupper()) + ''' + main() \ No newline at end of file diff --git a/lab2/semantical_analysis/naming_fixer.py b/lab2/semantical_analysis/naming_fixer.py new file mode 100644 index 0000000..f9f4373 --- /dev/null +++ b/lab2/semantical_analysis/naming_fixer.py @@ -0,0 +1,45 @@ +import re + +from lexical_analysis.lexema import LexemType + + +class NamingFixer: + + @classmethod + def verify(cls): + print() + + @classmethod + def fix(cls): + print() + + @classmethod + def analyze(cls, lexems): + fixes = [] + for lexema in lexems: + if lexema.get_type() == LexemType.FUNCTION_IDENTIFIER and not cls.__is_needed_case(lexema.get_str()): + fixes.append((lexema.get_str(), cls.__to_needed_case(lexema.get_str()))) + elif lexema.get_type() == LexemType.PRIVATE_FUNCTION_IDENTIFIER and not cls.__is_needed_case(lexema.get_str()): + fixes.append((lexema.get_str(), cls.__to_needed_case(lexema.get_str()))) + elif lexema.get_type() == LexemType.VARIABLE_IDENTIFIER and not cls.__is_needed_case(lexema.get_str()): + fixes.append((lexema.get_str(), cls.__to_needed_case(lexema.get_str()))) + elif lexema.get_type() == LexemType.CLASS_IDENTIFIER and not cls.__is_needed_case(lexema.get_str()): + fixes.append((lexema.get_str(), cls.__to_needed_case(lexema.get_str()))) + elif lexema.get_type() == LexemType.CONSTANT_IDENTIFIER and not lexema.get_str().isupper(): + fixes.append((lexema.get_str(), cls.__to_snake(lexema.get_str()))) + elif lexema.get_type() == LexemType.ENUM_IDENTIFIER and not cls.__is_needed_case(lexema.get_str()): + fixes.append((lexema.get_str(), cls.__to_needed_case(lexema.get_str()))) + return fixes + + @classmethod + def __to_needed_case(cls, name): + return name + + @classmethod + def __is_needed_case(cls, name): + return True + + @classmethod + def __to_snake(cls, name): + name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) + return re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).upper() diff --git a/lab2/semantical_analysis/parser.py b/lab2/semantical_analysis/parser.py index c306b33..af3f39f 100644 --- a/lab2/semantical_analysis/parser.py +++ b/lab2/semantical_analysis/parser.py @@ -15,11 +15,53 @@ def parse(cls, lexems): position = next_position continue next_position = cls.__parse_class(lexems, position) + if next_position != position: + position = next_position + continue + next_position = cls.__parse_enum(lexems, position) if next_position != position: position = next_position continue position += 1 + @classmethod + def __parse_enum(cls, lexems, position): + if lexems[position].get_type() == LexemType.IDENTIFIER: + prev_position = cls.__get_prev_lexema_position(lexems, position) + + if prev_position != None and lexems[prev_position].get_str() == 'enum': + lexems[position].set_type(LexemType.ENUM_IDENTIFIER) + + next_position = cls.__get_next_lexema_position(lexems, position) + + if next_position and lexems[next_position].get_type() == LexemType.EQUAL: + next_position = cls.__get_next_lexema_position(lexems, next_position) + + if next_position < len(lexems) and lexems[next_position].get_type() == LexemType.LEFT_BRACE: + i = next_position + 1 + opened_brace = 1 + while i < len(lexems) and opened_brace != 0: + if lexems[i].get_type() == LexemType.LEFT_BRACE: + opened_brace += 1 + elif lexems[i].get_type() == LexemType.RIGHT_BRACE: + opened_brace -= 1 + i += 1 + if i == len(lexems): + return position + + i = next_position + 1 + opened_brace = 1 + while i < len(lexems) and opened_brace != 0: + if lexems[i].get_type() == LexemType.LEFT_BRACE: + opened_brace += 1 + elif lexems[i].get_type() == LexemType.RIGHT_BRACE: + opened_brace -= 1 + + if lexems[i].get_type() == LexemType.IDENTIFIER: + lexems[i].set_type(LexemType.CONSTANT_IDENTIFIER) + i += 1 + return position + @classmethod def __parse_function(cls, lexems, position): if lexems[position].get_type() == LexemType.IDENTIFIER: @@ -77,13 +119,6 @@ def __parse_variable(cls, lexems, position): position += 1 return position - next_position = cls.__get_next_lexema_position(lexems, position) - if next_position: - next_lexema = lexems[next_position] - if next_lexema.get_type() == LexemType.COLON: - lexems[position].set_type(LexemType.CONSTANT_IDENTIFIER) - position += 1 - return position @classmethod From 87c742e19b9919c485f067debf4cb7e320b7cabd Mon Sep 17 00:00:00 2001 From: scientist19 <40214489+scientist19@users.noreply.github.com> Date: Sun, 6 Dec 2020 22:50:59 +0200 Subject: [PATCH 08/17] added methods for converting names for pascal and camel cases --- lab2/main.py | 7 +++-- lab2/semantical_analysis/naming_fixer.py | 38 ++++++++++++++++-------- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/lab2/main.py b/lab2/main.py index 7e20d33..a6df963 100644 --- a/lab2/main.py +++ b/lab2/main.py @@ -36,14 +36,15 @@ def main(): lexems = Lexer.lex(text) Parser.parse(lexems) - for lexem in lexems: - print(lexem.get_type()) - print('\'' + lexem.get_str() + '\'') + #for lexem in lexems: + # print(lexem.get_type()) + # print('\'' + lexem.get_str() + '\'') fixes = NamingFixer.analyze(lexems) for fix in fixes: print(f'{fix[0]} -> {fix[1]}') + ''' test1 = 'HelloWorld' test2 = 'HELLO_WORLD123' diff --git a/lab2/semantical_analysis/naming_fixer.py b/lab2/semantical_analysis/naming_fixer.py index f9f4373..0daebde 100644 --- a/lab2/semantical_analysis/naming_fixer.py +++ b/lab2/semantical_analysis/naming_fixer.py @@ -1,4 +1,5 @@ import re +import string from lexical_analysis.lexema import LexemType @@ -17,27 +18,40 @@ def fix(cls): def analyze(cls, lexems): fixes = [] for lexema in lexems: - if lexema.get_type() == LexemType.FUNCTION_IDENTIFIER and not cls.__is_needed_case(lexema.get_str()): - fixes.append((lexema.get_str(), cls.__to_needed_case(lexema.get_str()))) + if lexema.get_type() == LexemType.FUNCTION_IDENTIFIER and not cls.__is_camel_case(lexema.get_str()): + fixes.append((lexema.get_str(), cls.__to_camel_case(lexema.get_str()))) elif lexema.get_type() == LexemType.PRIVATE_FUNCTION_IDENTIFIER and not cls.__is_needed_case(lexema.get_str()): fixes.append((lexema.get_str(), cls.__to_needed_case(lexema.get_str()))) - elif lexema.get_type() == LexemType.VARIABLE_IDENTIFIER and not cls.__is_needed_case(lexema.get_str()): - fixes.append((lexema.get_str(), cls.__to_needed_case(lexema.get_str()))) - elif lexema.get_type() == LexemType.CLASS_IDENTIFIER and not cls.__is_needed_case(lexema.get_str()): - fixes.append((lexema.get_str(), cls.__to_needed_case(lexema.get_str()))) + elif lexema.get_type() == LexemType.VARIABLE_IDENTIFIER and not cls.__is_camel_case(lexema.get_str()): + fixes.append((lexema.get_str(), cls.__to_camel_case(lexema.get_str()))) + elif lexema.get_type() == LexemType.CLASS_IDENTIFIER and not cls.__is_pascal_case(lexema.get_str()): + fixes.append((lexema.get_str(), cls.__to_pascal_case(lexema.get_str()))) elif lexema.get_type() == LexemType.CONSTANT_IDENTIFIER and not lexema.get_str().isupper(): fixes.append((lexema.get_str(), cls.__to_snake(lexema.get_str()))) - elif lexema.get_type() == LexemType.ENUM_IDENTIFIER and not cls.__is_needed_case(lexema.get_str()): - fixes.append((lexema.get_str(), cls.__to_needed_case(lexema.get_str()))) + elif lexema.get_type() == LexemType.ENUM_IDENTIFIER and not cls.__is_pascal_case(lexema.get_str()): + fixes.append((lexema.get_str(), cls.__to_pascal_case(lexema.get_str()))) return fixes @classmethod - def __to_needed_case(cls, name): - return name + def __to_pascal_case(cls, name): + return string.capwords(name.replace("_", " ")).replace(" ", "") + + @classmethod + def __to_camel_case(cls, name): + res = string.capwords(name.replace("_", " ")).replace(" ", "") + return res[0].lower() + res[1:] + + @classmethod + def __is_camel_case(cls, name): + if name[0].isupper(): + return False + return "_" not in name @classmethod - def __is_needed_case(cls, name): - return True + def __is_pascal_case(cls, name): + if name[0].islower(): + return False + return "_" not in name @classmethod def __to_snake(cls, name): From d9362a1c3aecc012a6d672a6682479ce5f5dd1fc Mon Sep 17 00:00:00 2001 From: scientist19 <40214489+scientist19@users.noreply.github.com> Date: Sun, 6 Dec 2020 23:19:09 +0200 Subject: [PATCH 09/17] added conversion to trailing camel case --- lab2/semantical_analysis/naming_fixer.py | 20 ++++++++++++++++++-- lab2/semantical_analysis/parser.py | 2 ++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lab2/semantical_analysis/naming_fixer.py b/lab2/semantical_analysis/naming_fixer.py index 0daebde..5051312 100644 --- a/lab2/semantical_analysis/naming_fixer.py +++ b/lab2/semantical_analysis/naming_fixer.py @@ -20,8 +20,8 @@ def analyze(cls, lexems): for lexema in lexems: if lexema.get_type() == LexemType.FUNCTION_IDENTIFIER and not cls.__is_camel_case(lexema.get_str()): fixes.append((lexema.get_str(), cls.__to_camel_case(lexema.get_str()))) - elif lexema.get_type() == LexemType.PRIVATE_FUNCTION_IDENTIFIER and not cls.__is_needed_case(lexema.get_str()): - fixes.append((lexema.get_str(), cls.__to_needed_case(lexema.get_str()))) + elif lexema.get_type() == LexemType.PRIVATE_FUNCTION_IDENTIFIER and not cls.__is_trailing_camel_case(lexema.get_str()): + fixes.append((lexema.get_str(), cls.__to_trailing_camel_case(lexema.get_str()))) elif lexema.get_type() == LexemType.VARIABLE_IDENTIFIER and not cls.__is_camel_case(lexema.get_str()): fixes.append((lexema.get_str(), cls.__to_camel_case(lexema.get_str()))) elif lexema.get_type() == LexemType.CLASS_IDENTIFIER and not cls.__is_pascal_case(lexema.get_str()): @@ -47,6 +47,22 @@ def __is_camel_case(cls, name): return False return "_" not in name + @classmethod + def __to_trailing_camel_case(cls, name): + res = string.capwords(name.replace("_", " ")).replace(" ", "") + res = res[0].lower() + res[1:] + if res[len(res) - 1] != '_': + res = res + "_" + return res + + @classmethod + def __is_trailing_camel_case(cls, name): + if name[0].isupper(): + return False + if name[len(name)-1] != '_': + return False + return "_" not in name[0:len(name)-2] + @classmethod def __is_pascal_case(cls, name): if name[0].islower(): diff --git a/lab2/semantical_analysis/parser.py b/lab2/semantical_analysis/parser.py index af3f39f..d5fc25d 100644 --- a/lab2/semantical_analysis/parser.py +++ b/lab2/semantical_analysis/parser.py @@ -78,6 +78,7 @@ def __parse_function(cls, lexems, position): i += 1 if i == len(lexems): return position + next_after_right_paren = cls.__get_next_lexema_position(lexems, i) if lexems[next_after_right_paren].get_type() != LexemType.LEFT_BRACE: return position @@ -90,6 +91,7 @@ def __parse_function(cls, lexems, position): lexems[position].set_type(LexemType.PRIVATE_FUNCTION_IDENTIFIER) i = next_position + 1 + opened_paren = 1 while i < len(lexems) and opened_paren != 0: if lexems[i].get_type() == LexemType.LEFT_PAREN: opened_paren += 1 From 5cd42c68644649d4d2cc1963be8793efd67f79d9 Mon Sep 17 00:00:00 2001 From: scientist19 <40214489+scientist19@users.noreply.github.com> Date: Mon, 7 Dec 2020 21:55:57 +0200 Subject: [PATCH 10/17] added methods fix and verify for single files, added logger --- lab2/main.py | 18 +++++++++-- lab2/semantical_analysis/naming_fixer.py | 39 +++++++++++++++++++++--- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/lab2/main.py b/lab2/main.py index a6df963..02e0dfb 100644 --- a/lab2/main.py +++ b/lab2/main.py @@ -1,3 +1,4 @@ +import logging import re from lexical_analysis.lexer import Lexer @@ -28,6 +29,7 @@ def main(): '}\n' test_file = 'D:\\Meta\\Lab2\\JsFiles\\test.js' + test_file = 'D:\\Meta\\Lab2\\JsFiles\\test.log' f = open(test_file, 'r') text = f.read() @@ -40,10 +42,20 @@ def main(): # print(lexem.get_type()) # print('\'' + lexem.get_str() + '\'') - fixes = NamingFixer.analyze(lexems) + #fixes = NamingFixer.analyze(lexems) - for fix in fixes: - print(f'{fix[0]} -> {fix[1]}') + logger = logging.getLogger() + fhandler = logging.FileHandler(filename=test_log, mode='w') + formatter = logging.Formatter('%(levelname)s: %(message)s') + fhandler.setFormatter(formatter) + logger.addHandler(fhandler) + logger.setLevel(logging.DEBUG) + + #NamingFixer.verify_file(test_file, logger) + NamingFixer.fix_file(test_file, logger) + + #for fix in fixes: + # print(f'{fix[0]} -> {fix[1]}') ''' test1 = 'HelloWorld' diff --git a/lab2/semantical_analysis/naming_fixer.py b/lab2/semantical_analysis/naming_fixer.py index 5051312..a973cf1 100644 --- a/lab2/semantical_analysis/naming_fixer.py +++ b/lab2/semantical_analysis/naming_fixer.py @@ -1,18 +1,49 @@ +import logging import re import string from lexical_analysis.lexema import LexemType +from lexical_analysis.lexer import Lexer +from semantical_analysis.parser import Parser class NamingFixer: @classmethod - def verify(cls): - print() + def verify_file(cls, filepath, logger): + f = open(filepath, 'r') + text = f.read() + f.close() + lexems = Lexer.lex(text) + Parser.parse(lexems) + + fixes = cls.analyze(lexems) + + for fix in fixes: + for lexema in lexems: + if lexema.get_str() == fix[0]: + logging.warning(f'{filepath}: {fix[0]} naming error -> should be {fix[1]}') @classmethod - def fix(cls): - print() + def fix_file(cls, filepath, logger): + f = open(filepath, 'r') + text = f.read() + f.close() + lexems = Lexer.lex(text) + Parser.parse(lexems) + + fixes = cls.analyze(lexems) + + for fix in fixes: + for lexema in lexems: + if lexema.get_str() == fix[0]: + lexema.set_str(fix[1]) + logging.error(f'{filepath}: {fix[0]} naming error -> fixed to {fix[1]}') + + f = open(filepath, 'w') + for lexema in lexems: + f.write(lexema.get_str()) + f.close() @classmethod def analyze(cls, lexems): From 2fe545f4e80113cc8236bbffbac11a5cbb5edea7 Mon Sep 17 00:00:00 2001 From: scientist19 <40214489+scientist19@users.noreply.github.com> Date: Tue, 8 Dec 2020 20:34:05 +0200 Subject: [PATCH 11/17] added fix and verify for projects and directories --- lab2/examples/subdir/test.js | 499 +++++++++++++++++++++++ lab2/examples/test.js | 499 +++++++++++++++++++++++ lab2/main.py | 15 +- lab2/semantical_analysis/naming_fixer.py | 61 ++- 4 files changed, 1069 insertions(+), 5 deletions(-) create mode 100644 lab2/examples/subdir/test.js create mode 100644 lab2/examples/test.js diff --git a/lab2/examples/subdir/test.js b/lab2/examples/subdir/test.js new file mode 100644 index 0000000..30f81e6 --- /dev/null +++ b/lab2/examples/subdir/test.js @@ -0,0 +1,499 @@ +"use strict" + +@private +privateFunction_ (var1, var2, var3) { +} + +var makeTree = require("../rbtree.js") +var tape = require("tape") +var util = require("util") +var iota = require("iota-array") + +var colors = [ "r", "b", "bb" ] + +enum Statuses = { + READY: 1, + DOING: 2, + DONE: 3 +} + +enum Duplicate { + READY_D: 1, + DOING_D: 2, + DONE_D: 3 +} + +class TestClass { +} + + +function printTree(tree) { + if(!tree) { + return [] + } + return [ colors[tree.color], tree.key, printTree(tree.left), printTree(tree.right) ] +} + +function print(t) { + console.log(util.inspect(printTree(t.root), {depth:12})) +} + +//Ensures the red black axioms are satisfied by tree +function checkTree(tree, t) { + if(!tree.root) { + return + } + t.equals(tree.root.color, 1, "root is black") + function checkNode(node) { + if(!node) { + return [1, 0] + } + if(node.color === 0) { + t.assert(!node.left || node.left.color === 1, "children of red node must be black") + t.assert(!node.right || node.right.color === 1, "children of red node must be black") + } else { + t.equals(node.color, 1, "node color must be red or black") + } + if(node.left) { + t.assert(tree._compare(node.left.key, node.key) <= 0, "left tree order invariant") + } + if(node.right) { + t.assert(tree._compare(node.right.key, node.key) >= 0, "right tree order invariant") + } + var cl = checkNode(node.left) + var cr = checkNode(node.right) + t.equals(cl[0], cr[0], "number of black nodes along all paths to root must be constant") + t.equals(cl[1] + cr[1] + 1, node._count, "item count consistency") + return [cl[0] + node.color, cl[1] + cr[1] + 1] + } + var r = checkNode(tree.root) + t.equals(r[1], tree.length, "tree length") +} + +tape("insert()", function(t) { + var t1 = makeTree() + + var u = t1 + var arr = [] + for(var i=20; i>=0; --i) { + var x = i + var next = u.insert(x, true) + checkTree(u, t) + checkTree(next, t) + t.equals(u.length, arr.length) + arr.push(x) + u = next + } + for(var i=-20; i<0; ++i) { + var x = i + var next = u.insert(x, true) + checkTree(u, t) + checkTree(next, t) + arr.sort(function(a,b) { return a-b }) + var ptr = 0 + u.forEach(function(k,v) { + t.equals(k, arr[ptr++]) + }) + t.equals(ptr, arr.length) + arr.push(x) + u = next + } + + var start = u.begin + for(var i=-20, j=0; j<=40; ++i, ++j) { + t.equals(u.at(j).key, i, "checking at()") + t.equals(start.key, i, "checking iter") + t.equals(start.index, j, "checking index") + t.assert(start.valid, "checking valid") + if(j < 40) { + t.assert(start.hasNext, "hasNext()") + } else { + t.assert(!start.hasNext, "eof hasNext()") + } + start.next() + } + t.assert(!start.valid, "invalid eof iterator") + t.assert(!start.hasNext, "hasNext() at eof fail") + t.equals(start.index, 41, "eof index") + + t.end() +}) + +tape("foreach", function(t) { + var u = iota(31).reduce(function(u, k, v) { + return u.insert(k, v) + }, makeTree()) + + //Check basic foreach + var visitKeys = [] + var visitVals = [] + u.forEach(function(k,v) { + visitKeys.push(k) + visitVals.push(v) + }) + t.same(visitKeys, u.keys) + t.same(visitVals, u.values) + + //Check foreach with termination + visitKeys = [] + visitVals = [] + t.equals(u.forEach(function(k,v) { + if(k === 5) { + return 1000 + } + visitKeys.push(k) + visitVals.push(v) + }), 1000) + t.same(visitKeys, u.keys.slice(0, 5)) + t.same(visitVals, u.values.slice(0, 5)) + + //Check half interval foreach + visitKeys = [] + visitVals = [] + u.forEach(function(k,v) { + visitKeys.push(k) + visitVals.push(v) + }, 3) + t.same(visitKeys, u.keys.slice(3)) + t.same(visitVals, u.values.slice(3)) + + //Check half interval foreach with termination + visitKeys = [] + visitVals = [] + t.equals(u.forEach(function(k,v) { + if(k === 12) { + return 1000 + } + visitKeys.push(k) + visitVals.push(v) + }, 3), 1000) + t.same(visitKeys, u.keys.slice(3, 12)) + t.same(visitVals, u.values.slice(3, 12)) + + + //Check interval foreach + visitKeys = [] + visitVals = [] + u.forEach(function(k,v) { + visitKeys.push(k) + visitVals.push(v) + }, 3, 15) + t.same(visitKeys, u.keys.slice(3, 15)) + t.same(visitVals, u.values.slice(3, 15)) + + //Check interval foreach with termination + visitKeys = [] + visitVals = [] + t.equals(u.forEach(function(k,v) { + if(k === 12) { + return 1000 + } + visitKeys.push(k) + visitVals.push(v) + }, 3, 15), 1000) + t.same(visitKeys, u.keys.slice(3, 12)) + t.same(visitVals, u.values.slice(3, 12)) + + t.end() +}) + +function compareIterators(a, b, t) { + t.equals(a.tree, b.tree, "iter trees") + t.equals(a.valid, b.valid, "iter validity") + if(!b.valid) { + return + } + t.equals(a.node, b.node, "iter node") + t.equals(a.key, b.key, "iter key") + t.equals(a.value, b.value, "iter value") + t.equals(a.index, b.index, "iter index") +} + +tape("iterators", function(t) { + var u = iota(20).reduce(function(u, k, v) { + return u.insert(k, v) + }, makeTree()) + + //Try walking forward + var iter = u.begin + var c = iter.clone() + t.ok(iter.hasNext, "must have next at beginneing") + t.ok(!iter.hasPrev, "must not have predecessor") + for(var i=0; i<20; ++i) { + var v = u.at(i) + compareIterators(iter, v, t) + t.equals(iter.index, i) + iter.next() + } + t.ok(!iter.valid, "must be eof iterator") + + //Check if the clone worked + compareIterators(c, u.begin, t) + + //Try walking backward + var iter = u.end + t.ok(!iter.hasNext, "must not have next") + t.ok(iter.hasPrev, "must have predecessor") + for(var i=19; i>=0; --i) { + var v = u.at(i) + compareIterators(iter, v, t) + t.equals(iter.index, i) + iter.prev() + } + t.ok(!iter.valid, "must be eof iterator") + + t.end() +}) + + +tape("remove()", function(t) { + + var sz = [1, 2, 10, 20, 23, 31, 32, 33] + for(var n=0; n b[0]) { return 1 } + return 0 + }) + + var keys = zipped.map(function(v) { return v[0] }) + var values = zipped.map(function(v) { return v[1] }) + + t.same(u.keys, keys) + t.same(u.values, values) + + t.end() +}) + +tape("searching", function(t) { + + var arr = [0, 1, 1, 1, 1, 2, 3, 4, 5, 6, 6 ] + var u = arr.reduce(function(u, k, v) { + return u.insert(k, v) + }, makeTree()) + + + for(var i=0; i 0, "find repeat") + t.ok(u.find(1).index < 5, "find repeat") + + for(var i=0; i= 0, "right tree order invariant") + } + var cl = checkNode(node.left) + var cr = checkNode(node.right) + t.equals(cl[0], cr[0], "number of black nodes along all paths to root must be constant") + t.equals(cl[1] + cr[1] + 1, node._count, "item count consistency") + return [cl[0] + node.color, cl[1] + cr[1] + 1] + } + var r = checkNode(tree.root) + t.equals(r[1], tree.length, "tree length") +} + +tape("insert()", function(t) { + var t1 = makeTree() + + var u = t1 + var arr = [] + for(var i=20; i>=0; --i) { + var x = i + var next = u.insert(x, true) + checkTree(u, t) + checkTree(next, t) + t.equals(u.length, arr.length) + arr.push(x) + u = next + } + for(var i=-20; i<0; ++i) { + var x = i + var next = u.insert(x, true) + checkTree(u, t) + checkTree(next, t) + arr.sort(function(a,b) { return a-b }) + var ptr = 0 + u.forEach(function(k,v) { + t.equals(k, arr[ptr++]) + }) + t.equals(ptr, arr.length) + arr.push(x) + u = next + } + + var start = u.begin + for(var i=-20, j=0; j<=40; ++i, ++j) { + t.equals(u.at(j).key, i, "checking at()") + t.equals(start.key, i, "checking iter") + t.equals(start.index, j, "checking index") + t.assert(start.valid, "checking valid") + if(j < 40) { + t.assert(start.hasNext, "hasNext()") + } else { + t.assert(!start.hasNext, "eof hasNext()") + } + start.next() + } + t.assert(!start.valid, "invalid eof iterator") + t.assert(!start.hasNext, "hasNext() at eof fail") + t.equals(start.index, 41, "eof index") + + t.end() +}) + +tape("foreach", function(t) { + var u = iota(31).reduce(function(u, k, v) { + return u.insert(k, v) + }, makeTree()) + + //Check basic foreach + var visitKeys = [] + var visitVals = [] + u.forEach(function(k,v) { + visitKeys.push(k) + visitVals.push(v) + }) + t.same(visitKeys, u.keys) + t.same(visitVals, u.values) + + //Check foreach with termination + visitKeys = [] + visitVals = [] + t.equals(u.forEach(function(k,v) { + if(k === 5) { + return 1000 + } + visitKeys.push(k) + visitVals.push(v) + }), 1000) + t.same(visitKeys, u.keys.slice(0, 5)) + t.same(visitVals, u.values.slice(0, 5)) + + //Check half interval foreach + visitKeys = [] + visitVals = [] + u.forEach(function(k,v) { + visitKeys.push(k) + visitVals.push(v) + }, 3) + t.same(visitKeys, u.keys.slice(3)) + t.same(visitVals, u.values.slice(3)) + + //Check half interval foreach with termination + visitKeys = [] + visitVals = [] + t.equals(u.forEach(function(k,v) { + if(k === 12) { + return 1000 + } + visitKeys.push(k) + visitVals.push(v) + }, 3), 1000) + t.same(visitKeys, u.keys.slice(3, 12)) + t.same(visitVals, u.values.slice(3, 12)) + + + //Check interval foreach + visitKeys = [] + visitVals = [] + u.forEach(function(k,v) { + visitKeys.push(k) + visitVals.push(v) + }, 3, 15) + t.same(visitKeys, u.keys.slice(3, 15)) + t.same(visitVals, u.values.slice(3, 15)) + + //Check interval foreach with termination + visitKeys = [] + visitVals = [] + t.equals(u.forEach(function(k,v) { + if(k === 12) { + return 1000 + } + visitKeys.push(k) + visitVals.push(v) + }, 3, 15), 1000) + t.same(visitKeys, u.keys.slice(3, 12)) + t.same(visitVals, u.values.slice(3, 12)) + + t.end() +}) + +function compareIterators(a, b, t) { + t.equals(a.tree, b.tree, "iter trees") + t.equals(a.valid, b.valid, "iter validity") + if(!b.valid) { + return + } + t.equals(a.node, b.node, "iter node") + t.equals(a.key, b.key, "iter key") + t.equals(a.value, b.value, "iter value") + t.equals(a.index, b.index, "iter index") +} + +tape("iterators", function(t) { + var u = iota(20).reduce(function(u, k, v) { + return u.insert(k, v) + }, makeTree()) + + //Try walking forward + var iter = u.begin + var c = iter.clone() + t.ok(iter.hasNext, "must have next at beginneing") + t.ok(!iter.hasPrev, "must not have predecessor") + for(var i=0; i<20; ++i) { + var v = u.at(i) + compareIterators(iter, v, t) + t.equals(iter.index, i) + iter.next() + } + t.ok(!iter.valid, "must be eof iterator") + + //Check if the clone worked + compareIterators(c, u.begin, t) + + //Try walking backward + var iter = u.end + t.ok(!iter.hasNext, "must not have next") + t.ok(iter.hasPrev, "must have predecessor") + for(var i=19; i>=0; --i) { + var v = u.at(i) + compareIterators(iter, v, t) + t.equals(iter.index, i) + iter.prev() + } + t.ok(!iter.valid, "must be eof iterator") + + t.end() +}) + + +tape("remove()", function(t) { + + var sz = [1, 2, 10, 20, 23, 31, 32, 33] + for(var n=0; n b[0]) { return 1 } + return 0 + }) + + var keys = zipped.map(function(v) { return v[0] }) + var values = zipped.map(function(v) { return v[1] }) + + t.same(u.keys, keys) + t.same(u.values, values) + + t.end() +}) + +tape("searching", function(t) { + + var arr = [0, 1, 1, 1, 1, 2, 3, 4, 5, 6, 6 ] + var u = arr.reduce(function(u, k, v) { + return u.insert(k, v) + }, makeTree()) + + + for(var i=0; i 0, "find repeat") + t.ok(u.find(1).index < 5, "find repeat") + + for(var i=0; i {fix[1]}') @@ -78,4 +82,9 @@ def main(): print(test2.isupper()) ''' + #if self.ccf_type == CCFType.VERIFY: + # logging.basicConfig(filename=self.verify_log_file_name) + # else: + # logging.basicConfig(filename=self.fix_log_file_name) + main() \ No newline at end of file diff --git a/lab2/semantical_analysis/naming_fixer.py b/lab2/semantical_analysis/naming_fixer.py index a973cf1..5b43236 100644 --- a/lab2/semantical_analysis/naming_fixer.py +++ b/lab2/semantical_analysis/naming_fixer.py @@ -1,4 +1,5 @@ import logging +import os import re import string @@ -10,7 +11,35 @@ class NamingFixer: @classmethod - def verify_file(cls, filepath, logger): + def verify_project(cls, projpath): + files_to_verify = [] + for root, dirs, files in os.walk(projpath): + for f in files: + if f.endswith(".js"): + files_to_verify.append(os.path.join(root, f)) + for f in files_to_verify: + cls.verify_file(f) + + @classmethod + def verify_directory(cls, dirpath): + files = [] + for f in os.listdir(dirpath): + if f.endswith(".js"): + files.append(os.path.join(dirpath, f)) + for f in files: + cls.verify_file(f) + + @classmethod + def verify_file(cls, filepath): + fhandler = logging.FileHandler(filename=filepath + '_verification.log', mode='w') + formatter = logging.Formatter('%(levelname)s: %(message)s') + fhandler.setFormatter(formatter) + + log = logging.getLogger() # root logger + for hdlr in log.handlers[:]: # remove all old handlers + log.removeHandler(hdlr) + log.addHandler(fhandler) + f = open(filepath, 'r') text = f.read() f.close() @@ -25,7 +54,35 @@ def verify_file(cls, filepath, logger): logging.warning(f'{filepath}: {fix[0]} naming error -> should be {fix[1]}') @classmethod - def fix_file(cls, filepath, logger): + def fix_project(cls, projpath): + files_to_fix = [] + for root, dirs, files in os.walk(projpath): + for f in files: + if f.endswith(".js"): + files_to_fix.append(os.path.join(root, f)) + for f in files_to_fix: + cls.fix_file(f) + + @classmethod + def fix_directory(cls, dirpath): + files_to_fix = [] + for f in os.listdir(dirpath): + if f.endswith(".js"): + files_to_fix.append(os.path.join(dirpath, f)) + for f in files_to_fix: + cls.fix_file(f) + + @classmethod + def fix_file(cls, filepath): + fhandler = logging.FileHandler(filename=filepath + '_fixing.log', mode='w') + formatter = logging.Formatter('%(levelname)s: %(message)s') + fhandler.setFormatter(formatter) + + log = logging.getLogger() # root logger + for hdlr in log.handlers[:]: # remove all old handlers + log.removeHandler(hdlr) + log.addHandler(fhandler) + f = open(filepath, 'r') text = f.read() f.close() From b8da8b6f6814d977f75902ea626077e64ff3c41e Mon Sep 17 00:00:00 2001 From: scientist19 <40214489+scientist19@users.noreply.github.com> Date: Tue, 8 Dec 2020 21:10:45 +0200 Subject: [PATCH 12/17] main.py clean up + command line arguments handling --- lab2/main.py | 108 +++++++++++++++------------------------------------ 1 file changed, 32 insertions(+), 76 deletions(-) diff --git a/lab2/main.py b/lab2/main.py index 8abeeca..b8798b9 100644 --- a/lab2/main.py +++ b/lab2/main.py @@ -1,90 +1,46 @@ +import argparse import logging -import re -from lexical_analysis.lexer import Lexer from semantical_analysis.naming_fixer import NamingFixer -from semantical_analysis.parser import Parser - def main(): - text = 'var x;\n' + \ - '// doing x = \"6E10\";\n\n' + \ - '/* useless comment\n' + \ - ' useless comment\n' + \ - ' useless comment*/\n' + \ - 'x = \"6E10\";\n' + \ - 'document.getElementById("demo").innerHTML = x;\n' \ - '@private\n' \ - '// comment\n' \ - '/* another comment\n' \ - 'bla bla */ const hello = \"hello\"\n' \ - 'private_function (var1, var2, var3)' \ - '//@private\n' \ - 'not_private_function (var1, var2)\n' \ - 'class /* comment */Rectangle {}\n' \ - 'colors = {\n' \ - ' RED: 1,\n' \ - ' GREEN: 2,\n' \ - ' blue: 3\n' \ - '}\n' - - test_file = 'examples\\test.js' - test_log = 'examples\\all.log' - - f = open(test_file, 'r') - text = f.read() - f.close() - - lexems = Lexer.lex(text) - Parser.parse(lexems) - - #for lexem in lexems: - # print(lexem.get_type()) - # print('\'' + lexem.get_str() + '\'') - - #fixes = NamingFixer.analyze(lexems) logger = logging.getLogger() - fhandler = logging.FileHandler(filename=test_log, mode='w') + fhandler = logging.FileHandler(filename='all.log', mode='w') formatter = logging.Formatter('%(levelname)s: %(message)s') fhandler.setFormatter(formatter) logger.addHandler(fhandler) logger.setLevel(logging.DEBUG) - #NamingFixer.verify_file(test_file, logger) - #NamingFixer.fix_file(test_file, logger) - test_dir = 'examples' - - #NamingFixer.verify_directory(test_dir) - NamingFixer.verify_project(test_dir) - - #for fix in fixes: - # print(f'{fix[0]} -> {fix[1]}') - - ''' - test1 = 'HelloWorld' - test2 = 'HELLO_WORLD123' - test3 = 'hello_world' - test4 = 'helloWorld' - test5 = 'IOManip' - - print(test1) - print(camel_to_snake(test1)) - print(test2) - print(camel_to_snake(test2)) - print(test3) - print(camel_to_snake(test3)) - print(test4) - print(camel_to_snake(test4)) - print(test5) - print(camel_to_snake(test5)) - print() - print(test2.isupper()) - ''' + parser = argparse.ArgumentParser('JavaScriptCCF') + parser.add_argument('-v', '--verify', action='store_true', help='verify naming conventions') + parser.add_argument('-fx', '--fix', action='store_true', help='fix naming conventions') + parser.add_argument('-f', '--file', nargs=1, required=False, help='javascript source code file') + parser.add_argument('-p', '--project', nargs=1, required=False, help='javascript project directory javascript source code files') + parser.add_argument('-d', '--directory', nargs=1, required=False, help='directory javascript source code files') + + my_namespace = parser.parse_args() + if not (my_namespace.verify or my_namespace.fix) or \ + not (my_namespace.directory is not None or + my_namespace.file is not None or + my_namespace.project is not None): + print("Incorrect input. Help:") + parser.print_help() + return + + if my_namespace.fix: + if my_namespace.project is not None: + NamingFixer.fix_project(my_namespace.project[0]) + elif my_namespace.directory is not None: + NamingFixer.fix_directory(my_namespace.directory[0]) + else: + NamingFixer.fix_file(my_namespace.file[0]) + else: + if my_namespace.project is not None: + NamingFixer.verify_project(my_namespace.project[0]) + elif my_namespace.directory is not None: + NamingFixer.verify_directory(my_namespace.directory[0]) + else: + NamingFixer.verify_file(my_namespace.file[0]) - #if self.ccf_type == CCFType.VERIFY: - # logging.basicConfig(filename=self.verify_log_file_name) - # else: - # logging.basicConfig(filename=self.fix_log_file_name) - main() \ No newline at end of file From da443139eb1fb1aa719a431b07c4a9351b66008a Mon Sep 17 00:00:00 2001 From: scientist19 <40214489+scientist19@users.noreply.github.com> Date: Tue, 8 Dec 2020 21:55:22 +0200 Subject: [PATCH 13/17] added file names fixing and verification --- lab2/semantical_analysis/naming_fixer.py | 37 ++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/lab2/semantical_analysis/naming_fixer.py b/lab2/semantical_analysis/naming_fixer.py index 5b43236..dad80ed 100644 --- a/lab2/semantical_analysis/naming_fixer.py +++ b/lab2/semantical_analysis/naming_fixer.py @@ -1,5 +1,7 @@ import logging +import ntpath import os +import platform import re import string @@ -83,6 +85,16 @@ def fix_file(cls, filepath): log.removeHandler(hdlr) log.addHandler(fhandler) + filename = ntpath.basename(filepath) + dir_path = os.path.dirname(os.path.realpath(filepath)) + clear_filename = filename[0:(len(filename)-3)] + if not cls.__is_correct_filename(clear_filename): + new_name = cls.__to_correct_filename(clear_filename) + slash = "\\" if platform.system() in ['Windows', 'windows', 'Win', 'win'] else "/" + filepath = dir_path + slash + new_name + ".js" + os.rename(dir_path + slash + filename, filepath) + logging.error(f'{filepath}: {filename} naming error -> fixed to {new_name+".js"}') + f = open(filepath, 'r') text = f.read() f.close() @@ -161,3 +173,28 @@ def __is_pascal_case(cls, name): def __to_snake(cls, name): name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) return re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).upper() + + @classmethod + def __is_correct_filename(cls, name): + contains_dashes = "-" in name + contains_underscores = "_" in name + contains_uppercase = False + for ch in name: + if ch.isupper(): + contains_uppercase = True + break + if contains_uppercase: + return False + if contains_underscores and contains_dashes: + return False + return True + + @classmethod + def __to_correct_filename(cls, name): + contains_dashes = "-" in name + contains_underscores = "_" in name + if contains_underscores and contains_dashes: + name = cls.__to_snake(name) + name = name.lower() + name = name.replace("_", "-") + return name From edffac037a3ad2091b537c3d7c02363418c3c3ad Mon Sep 17 00:00:00 2001 From: scientist19 <40214489+scientist19@users.noreply.github.com> Date: Tue, 8 Dec 2020 23:50:53 +0200 Subject: [PATCH 14/17] added README.md --- lab2/README.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 lab2/README.md diff --git a/lab2/README.md b/lab2/README.md new file mode 100644 index 0000000..c71bc5e --- /dev/null +++ b/lab2/README.md @@ -0,0 +1,34 @@ +# Lab2 JS code conventions analyzer and fixer + + +To use package install it from pipy: + +https://pypi.org/project/jsconventionfixer/1.0.1/ + + +Run 'pip install jsconventionfixer==1.0.1' + + +After successful installation project can be used command: +`jsconventionfixer (-v|-fx) -(p|d|f) ` + +#### Command line options +```text +Options: + -v, --verify Verify files without editing. + + -fx, --fix Rewrite files + + -d, --directory TEXT Absolute or relative path to directory with + files to be fixed. + + -p, --project TEXT Absolute or relative path to project with + files to be fixed. + + -f, --file TEXT Absolute or relative path to file to be fixed. + + -h, --help Show this message and exit.` + ``` + *Note: any other input is equivalent to* `--help` + + \ No newline at end of file From 36addf76e6c7cc21333b0124b3fce2793f0bb216 Mon Sep 17 00:00:00 2001 From: scientist19 <40214489+scientist19@users.noreply.github.com> Date: Wed, 9 Dec 2020 03:07:39 +0200 Subject: [PATCH 15/17] fixed some crashes --- lab2/lexical_analysis/lexer.py | 27 +++++++++++++++++++-------- lab2/semantical_analysis/parser.py | 3 +++ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/lab2/lexical_analysis/lexer.py b/lab2/lexical_analysis/lexer.py index 8e5dd2b..0ea4d0c 100644 --- a/lab2/lexical_analysis/lexer.py +++ b/lab2/lexical_analysis/lexer.py @@ -150,23 +150,34 @@ def __lex_str(cls, str): quote = str[0] i = 1 - while str[i] != quote: + def is_closing_quote(str, i): + return str[i] == quote and str[i-1] != '\\' + + while i < len(str) and not is_closing_quote(str, i): i += 1 - i += 1 + if i == len(str): + return None, str + + i += 1 return Lexema(LexemType.STRING, str[0:i]), str[i:] @classmethod def __lex_number(cls, str): - numbers = re.findall("[-+]?[.]?[\d]+(?:,\d\d\d)*[\.]?\d*(?:[eE][-+]?\d+)?", str) - if len(numbers) == 0: + t = re.search("[-+]?[.]?[\d]+(?:,\d\d\d)*[\.]?\d*(?:[eE][-+]?\d+)?", str) + + if not t: + return None, str + + number = t.group() + + if not number: return None, str - first_number = numbers[0] - number_len = len(first_number) + number_len = len(number) p_number = str[0:number_len] - if p_number == first_number: - return Lexema(LexemType.NUMBER, first_number), str[number_len:] + if p_number == number: + return Lexema(LexemType.NUMBER, number), str[number_len:] else: return None, str diff --git a/lab2/semantical_analysis/parser.py b/lab2/semantical_analysis/parser.py index d5fc25d..028b1ed 100644 --- a/lab2/semantical_analysis/parser.py +++ b/lab2/semantical_analysis/parser.py @@ -80,6 +80,9 @@ def __parse_function(cls, lexems, position): return position next_after_right_paren = cls.__get_next_lexema_position(lexems, i) + if not next_after_right_paren: + return position + if lexems[next_after_right_paren].get_type() != LexemType.LEFT_BRACE: return position From 742857c8691002c33876c3e630eaa658185ed5ef Mon Sep 17 00:00:00 2001 From: scientist19 <40214489+scientist19@users.noreply.github.com> Date: Wed, 9 Dec 2020 03:08:30 +0200 Subject: [PATCH 16/17] fixed file names verification, added progress bar --- lab2/semantical_analysis/naming_fixer.py | 48 ++++++++++++++++++++---- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/lab2/semantical_analysis/naming_fixer.py b/lab2/semantical_analysis/naming_fixer.py index dad80ed..d1289c5 100644 --- a/lab2/semantical_analysis/naming_fixer.py +++ b/lab2/semantical_analysis/naming_fixer.py @@ -9,6 +9,7 @@ from lexical_analysis.lexer import Lexer from semantical_analysis.parser import Parser +from console_progressbar import ProgressBar class NamingFixer: @@ -19,8 +20,15 @@ def verify_project(cls, projpath): for f in files: if f.endswith(".js"): files_to_verify.append(os.path.join(root, f)) + if len(files) == 0: + return + pb = ProgressBar(total=len(files_to_verify), prefix='Files processed', suffix='', decimals=1, length=50) + progress = 0 + pb.print_progress_bar(0) for f in files_to_verify: cls.verify_file(f) + progress += 1 + pb.print_progress_bar(progress) @classmethod def verify_directory(cls, dirpath): @@ -28,8 +36,15 @@ def verify_directory(cls, dirpath): for f in os.listdir(dirpath): if f.endswith(".js"): files.append(os.path.join(dirpath, f)) + if len(files) == 0: + return + pb = ProgressBar(total=len(files), prefix='Files processed', suffix='', decimals=1, length=50) + progress = 0 + pb.print_progress_bar(0) for f in files: cls.verify_file(f) + progress += 1 + pb.print_progress_bar(progress) @classmethod def verify_file(cls, filepath): @@ -42,6 +57,12 @@ def verify_file(cls, filepath): log.removeHandler(hdlr) log.addHandler(fhandler) + filename = ntpath.basename(filepath) + clear_filename = filename[0:(len(filename)-3)] + if not cls.__is_correct_filename(clear_filename): + new_name = cls.__to_correct_filename(clear_filename) + logging.warning(f'{filepath}: {filename} naming error -> should be {new_name+".js"}') + f = open(filepath, 'r') text = f.read() f.close() @@ -62,8 +83,15 @@ def fix_project(cls, projpath): for f in files: if f.endswith(".js"): files_to_fix.append(os.path.join(root, f)) + if len(files_to_fix) == 0: + return + pb = ProgressBar(total=len(files_to_fix), prefix='Files processed', suffix='', decimals=1, length=50) + progress = 0 + pb.print_progress_bar(0) for f in files_to_fix: cls.fix_file(f) + progress += 1 + pb.print_progress_bar(progress) @classmethod def fix_directory(cls, dirpath): @@ -71,8 +99,15 @@ def fix_directory(cls, dirpath): for f in os.listdir(dirpath): if f.endswith(".js"): files_to_fix.append(os.path.join(dirpath, f)) + if len(files_to_fix) == 0: + return + pb = ProgressBar(total=len(files_to_fix), prefix='Files processed', suffix='', decimals=1, length=50) + progress = 0 + pb.print_progress_bar(0) for f in files_to_fix: cls.fix_file(f) + progress += 1 + pb.print_progress_bar(progress) @classmethod def fix_file(cls, filepath): @@ -139,6 +174,8 @@ def __to_pascal_case(cls, name): @classmethod def __to_camel_case(cls, name): res = string.capwords(name.replace("_", " ")).replace(" ", "") + if not res: + return name return res[0].lower() + res[1:] @classmethod @@ -191,10 +228,7 @@ def __is_correct_filename(cls, name): @classmethod def __to_correct_filename(cls, name): - contains_dashes = "-" in name - contains_underscores = "_" in name - if contains_underscores and contains_dashes: - name = cls.__to_snake(name) - name = name.lower() - name = name.replace("_", "-") - return name + upper_snake_case = cls.__to_snake(name) + lower_snake_case = upper_snake_case.lower() + lower_dash = lower_snake_case.replace("_", "-") + return lower_dash From cadaac53a90feda197488208eb92750e396a1e1f Mon Sep 17 00:00:00 2001 From: scientist19 <40214489+scientist19@users.noreply.github.com> Date: Wed, 9 Dec 2020 03:52:56 +0200 Subject: [PATCH 17/17] updated README with new version number --- lab2/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lab2/README.md b/lab2/README.md index c71bc5e..591066c 100644 --- a/lab2/README.md +++ b/lab2/README.md @@ -3,10 +3,10 @@ To use package install it from pipy: -https://pypi.org/project/jsconventionfixer/1.0.1/ +https://pypi.org/project/jsconventionfixer/1.1.4/ -Run 'pip install jsconventionfixer==1.0.1' +Run 'pip install jsconventionfixer==1.1.4' After successful installation project can be used command: