From b11ec9003eec7fc8b5c12fa481ec61a18cebb476 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Wed, 27 Oct 2021 21:03:46 +0200 Subject: [PATCH] Improved parser to handle names and classes --- src/cc_lang_interpreter/application.py | 11 ++-- src/parser/model/ast_types_enum.py | 2 + src/parser/service/parser_service.py | 82 +++++++++++++++++++++----- src/runtime/abc/runtime_service_abc.py | 7 +++ src/runtime/service/runtime_service.py | 15 ++++- 5 files changed, 98 insertions(+), 19 deletions(-) diff --git a/src/cc_lang_interpreter/application.py b/src/cc_lang_interpreter/application.py index dd436a0..5315d0b 100644 --- a/src/cc_lang_interpreter/application.py +++ b/src/cc_lang_interpreter/application.py @@ -11,6 +11,8 @@ from lexer.model.token import Token from parser.abc.ast import AST from parser.abc.parser_abc import ParserABC from runtime.abc.runtime_service_abc import RuntimeServiceABC +from runtime.model.error import Error +from runtime.model.error_codes_enum import ErrorCodesEnum class Application(ApplicationABC): @@ -55,17 +57,18 @@ class Application(ApplicationABC): def _read_file(self, file: str): if not os.path.isfile(file): - raise FileNotFoundError - # self.__utils.runtime_error(Error(ErrorCodes.FileNotFound)) + self._runtime.error(Error(ErrorCodesEnum.FileNotFound)) if not file.endswith('.ccl'): - raise Exception('Wrong file type') - # self.__utils.runtime_error(Error(ErrorCodes.WrongFileType)) + self._runtime.error(Error(ErrorCodesEnum.WrongFileType)) + + self._runtime.file = file f = open(file, 'r', encoding='utf-8').readlines() for i in range(0, len(f)): self._runtime.line_count = i + 1 self._interpret(f[i]) + self._runtime.file = '' def configure(self): pass diff --git a/src/parser/model/ast_types_enum.py b/src/parser/model/ast_types_enum.py index ea56099..9c3aaad 100644 --- a/src/parser/model/ast_types_enum.py +++ b/src/parser/model/ast_types_enum.py @@ -9,4 +9,6 @@ class ASTTypesEnum(Enum): LibraryDeclaration = 'library_declaration' ClassDeclaration = 'class_declaration' + FuncDeclaration = 'func_declaration' + VariableDeclaration = 'variable_declaration' diff --git a/src/parser/service/parser_service.py b/src/parser/service/parser_service.py index 449b495..8c594fb 100644 --- a/src/parser/service/parser_service.py +++ b/src/parser/service/parser_service.py @@ -1,3 +1,4 @@ +from typing import Optional, Tuple from parser.abc.ast import AST from parser.abc.parser_abc import ParserABC from parser.model.ast_types_enum import ASTTypesEnum @@ -26,8 +27,43 @@ class ParserService(ParserABC): Keywords.This.value, ] + def _parse_name(self, tokens: List[Token]) -> Tuple[AST, Token]: + """ Parses names + + Args: + tokens (List[Token]): Tokens from lexer + + AST: + Program + + Program.Test + <.> + + Returns: + AST: Name AST + """ + name = '' + last_token: Optional[Token] = None + for i in range(0, tokens.count()): + token = tokens[i] + + if i == tokens.count() and token.type == TokenTypes.Format_Character and token.value == FormatCharacters.Point: + self._runtime.error(Error(ErrorCodesEnum.Unexpected), FormatCharacters.Point.value) + + if token.type == TokenTypes.Name or token.type == TokenTypes.Format_Character and token.value == FormatCharacters.Point.value: + name += token.value + else: + break + + + last_token = token + + return (AST(ASTTypesEnum.Name, name, self._runtime.line_count, self._runtime.line_count), last_token) + + + def _parse_library_or_class(self, tokens: List[Token], cls=False) -> AST: - """ Handles library declaration + """ Parses library or class declarations Args: tokens (List[Token]): Tokens from lexer @@ -39,35 +75,47 @@ class ParserService(ParserABC): public lib Main {} + public lib Main.Test { + <.> Returns: AST: Library or class AST """ end = None ast = List(AST) - for i in range(0, tokens.count()): + i = 0 + while i < tokens.count(): token: Token = tokens[i] # if line contains } if token.type == TokenTypes.Format_Character and token.value == FormatCharacters.Right_Brace.value: end = self._runtime.line_count + elif i == tokens.count()-1 and token.type == TokenTypes.Format_Character and token.value == FormatCharacters.Left_Brace.value: break + elif i == tokens.count()-1: - self._runtime.error( - Error(ErrorCodesEnum.Expected, FormatCharacters.Left_Brace.value)) + self._runtime.error(Error(ErrorCodesEnum.Expected, FormatCharacters.Left_Brace.value)) - if i == 0 and token.type == TokenTypes.Keyword and token.value in self._access_keywords: - ast.append(AST(ASTTypesEnum.Access, token.value, - self._runtime.line_count, self._runtime.line_count)) + elif i == 0 and token.type == TokenTypes.Keyword and token.value in self._access_keywords: + ast.append(AST(ASTTypesEnum.Access, token.value, self._runtime.line_count, self._runtime.line_count)) - if i <= 1 and token.type == TokenTypes.Keyword and token.value == Keywords.Library.value: - ast.append(AST(ASTTypesEnum.Keyword, token.value, - self._runtime.line_count, self._runtime.line_count)) + elif i <= 1 and token.type == TokenTypes.Keyword and token.value == Keywords.Library.value and not cls: + ast.append(AST(ASTTypesEnum.Keyword, token.value, self._runtime.line_count, self._runtime.line_count)) - if i >= 1 and token.type == TokenTypes.Name: - ast.append(AST(ASTTypesEnum.Name, token.value, - self._runtime.line_count, self._runtime.line_count)) + elif i <= 1 and token.type == TokenTypes.Keyword and token.value == Keywords.Class.value and cls: + ast.append(AST(ASTTypesEnum.Keyword, token.value, self._runtime.line_count, self._runtime.line_count)) + + elif i >= 1 and token.type == TokenTypes.Name: + name, last_token = self._parse_name(tokens.skip(i)) + i = tokens.index(last_token) + ast.append(name) + ast.append(AST(ASTTypesEnum.Name, token.value, self._runtime.line_count, self._runtime.line_count)) + + else: + self._runtime.error(Error(ErrorCodesEnum.Unexpected, token.value)) + + i += 1 return AST(ASTTypesEnum.LibraryDeclaration if not cls else ASTTypesEnum.ClassDeclaration, ast, self._runtime.line_count, end) @@ -92,6 +140,12 @@ class ParserService(ParserABC): Returns: AST: Library or class AST """ + end = None + ast = List(AST) + for i in range(0, tokens.count()): + token: Token = tokens[i] + + return AST(ASTTypesEnum.VariableDeclaration, ast) def create_ast(self, tokens: List[Token]) -> List[AST]: self._ast = List(AST) @@ -103,6 +157,6 @@ class ParserService(ParserABC): self._ast.append(self._parse_library_or_class(tokens, True)) elif tokens.where(lambda t: t.type == TokenTypes.Keyword and t.value == Keywords.Variable.value).count() > 0: - self._ast.append(self._parse_variable(tokens, True)) + self._ast.append(self._parse_variable(tokens)) return self._ast diff --git a/src/runtime/abc/runtime_service_abc.py b/src/runtime/abc/runtime_service_abc.py index f0d9130..90d6a64 100644 --- a/src/runtime/abc/runtime_service_abc.py +++ b/src/runtime/abc/runtime_service_abc.py @@ -19,6 +19,13 @@ class RuntimeServiceABC(ABC): @abstractmethod def line_count(self, line_count: int): pass + @property + def file(self) -> str: + return self._file + @file.setter + def file(self, value: str): + self._file = value + @abstractmethod def input(self, prefix: str) -> str: pass diff --git a/src/runtime/service/runtime_service.py b/src/runtime/service/runtime_service.py index 152d657..c4f5952 100644 --- a/src/runtime/service/runtime_service.py +++ b/src/runtime/service/runtime_service.py @@ -7,6 +7,7 @@ class RuntimeService(RuntimeServiceABC): def __init__(self): self._line_count = 0 + self._file = '' @property def line_count(self) -> int: @@ -16,6 +17,13 @@ class RuntimeService(RuntimeServiceABC): def line_count(self, line_count: int): self._line_count = line_count + @property + def file(self) -> str: + return self._file + @file.setter + def file(self, value: str): + self._file = value + def input(self, prefix: str) -> str: return Console.read_line(prefix) @@ -24,12 +32,17 @@ class RuntimeService(RuntimeServiceABC): def error(self, error: Error) -> None: Console.set_foreground_color(ForegroundColorEnum.red) - Console.write_line(f'Error in line {self._line_count}\n{error.message}') + if self._file is not None: + Console.write_line(f'Error in {self._file} line {self._line_count}\n{error.message}') + else: + Console.write_line(f'Error in line {self._line_count}\n{error.message}') Console.color_reset() + Console.write_line() exit() def runtime_error(self, error: Error) -> None: Console.set_foreground_color(ForegroundColorEnum.red) Console.write_line(f'{error.message}', 'red') Console.color_reset() + Console.write_line() exit()