Improved parser to handle names and classes

This commit is contained in:
Sven Heidemann 2021-10-27 21:03:46 +02:00
parent ff025bdc7f
commit b11ec9003e
5 changed files with 98 additions and 19 deletions

View File

@ -11,6 +11,8 @@ from lexer.model.token import Token
from parser.abc.ast import AST from parser.abc.ast import AST
from parser.abc.parser_abc import ParserABC from parser.abc.parser_abc import ParserABC
from runtime.abc.runtime_service_abc import RuntimeServiceABC 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): class Application(ApplicationABC):
@ -55,17 +57,18 @@ class Application(ApplicationABC):
def _read_file(self, file: str): def _read_file(self, file: str):
if not os.path.isfile(file): if not os.path.isfile(file):
raise FileNotFoundError self._runtime.error(Error(ErrorCodesEnum.FileNotFound))
# self.__utils.runtime_error(Error(ErrorCodes.FileNotFound))
if not file.endswith('.ccl'): if not file.endswith('.ccl'):
raise Exception('Wrong file type') self._runtime.error(Error(ErrorCodesEnum.WrongFileType))
# self.__utils.runtime_error(Error(ErrorCodes.WrongFileType))
self._runtime.file = file
f = open(file, 'r', encoding='utf-8').readlines() f = open(file, 'r', encoding='utf-8').readlines()
for i in range(0, len(f)): for i in range(0, len(f)):
self._runtime.line_count = i + 1 self._runtime.line_count = i + 1
self._interpret(f[i]) self._interpret(f[i])
self._runtime.file = ''
def configure(self): pass def configure(self): pass

View File

@ -9,4 +9,6 @@ class ASTTypesEnum(Enum):
LibraryDeclaration = 'library_declaration' LibraryDeclaration = 'library_declaration'
ClassDeclaration = 'class_declaration' ClassDeclaration = 'class_declaration'
FuncDeclaration = 'func_declaration'
VariableDeclaration = 'variable_declaration'

View File

@ -1,3 +1,4 @@
from typing import Optional, Tuple
from parser.abc.ast import AST from parser.abc.ast import AST
from parser.abc.parser_abc import ParserABC from parser.abc.parser_abc import ParserABC
from parser.model.ast_types_enum import ASTTypesEnum from parser.model.ast_types_enum import ASTTypesEnum
@ -26,8 +27,43 @@ class ParserService(ParserABC):
Keywords.This.value, Keywords.This.value,
] ]
def _parse_name(self, tokens: List[Token]) -> Tuple[AST, Token]:
""" Parses names
Args:
tokens (List[Token]): Tokens from lexer
AST:
Program
<name>
Program.Test
<name> <.> <name>
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: def _parse_library_or_class(self, tokens: List[Token], cls=False) -> AST:
""" Handles library declaration """ Parses library or class declarations
Args: Args:
tokens (List[Token]): Tokens from lexer tokens (List[Token]): Tokens from lexer
@ -39,35 +75,47 @@ class ParserService(ParserABC):
<access> <lib, class> <name> <end> <access> <lib, class> <name> <end>
public lib Main {} public lib Main {}
<access> <lib, class> <name> <end> <end> <access> <lib, class> <name> <end> <end>
public lib Main.Test {
<access> <lib, class> <name> <.> <name> <end> <end>
Returns: Returns:
AST: Library or class AST AST: Library or class AST
""" """
end = None end = None
ast = List(AST) ast = List(AST)
for i in range(0, tokens.count()): i = 0
while i < tokens.count():
token: Token = tokens[i] token: Token = tokens[i]
# if line contains } # if line contains }
if token.type == TokenTypes.Format_Character and token.value == FormatCharacters.Right_Brace.value: if token.type == TokenTypes.Format_Character and token.value == FormatCharacters.Right_Brace.value:
end = self._runtime.line_count end = self._runtime.line_count
elif i == tokens.count()-1 and token.type == TokenTypes.Format_Character and token.value == FormatCharacters.Left_Brace.value: elif i == tokens.count()-1 and token.type == TokenTypes.Format_Character and token.value == FormatCharacters.Left_Brace.value:
break break
elif i == tokens.count()-1: elif i == tokens.count()-1:
self._runtime.error( self._runtime.error(Error(ErrorCodesEnum.Expected, FormatCharacters.Left_Brace.value))
Error(ErrorCodesEnum.Expected, FormatCharacters.Left_Brace.value))
if i == 0 and token.type == TokenTypes.Keyword and token.value in self._access_keywords: elif i == 0 and token.type == TokenTypes.Keyword and token.value in self._access_keywords:
ast.append(AST(ASTTypesEnum.Access, token.value, ast.append(AST(ASTTypesEnum.Access, token.value, self._runtime.line_count, self._runtime.line_count))
self._runtime.line_count, self._runtime.line_count))
if i <= 1 and token.type == TokenTypes.Keyword and token.value == Keywords.Library.value: elif i <= 1 and token.type == TokenTypes.Keyword and token.value == Keywords.Library.value and not cls:
ast.append(AST(ASTTypesEnum.Keyword, token.value, ast.append(AST(ASTTypesEnum.Keyword, token.value, self._runtime.line_count, self._runtime.line_count))
self._runtime.line_count, self._runtime.line_count))
if i >= 1 and token.type == TokenTypes.Name: elif i <= 1 and token.type == TokenTypes.Keyword and token.value == Keywords.Class.value and cls:
ast.append(AST(ASTTypesEnum.Name, token.value, ast.append(AST(ASTTypesEnum.Keyword, token.value, self._runtime.line_count, self._runtime.line_count))
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) return AST(ASTTypesEnum.LibraryDeclaration if not cls else ASTTypesEnum.ClassDeclaration, ast, self._runtime.line_count, end)
@ -92,6 +140,12 @@ class ParserService(ParserABC):
Returns: Returns:
AST: Library or class AST 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]: def create_ast(self, tokens: List[Token]) -> List[AST]:
self._ast = List(AST) self._ast = List(AST)
@ -103,6 +157,6 @@ class ParserService(ParserABC):
self._ast.append(self._parse_library_or_class(tokens, True)) 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: 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 return self._ast

View File

@ -19,6 +19,13 @@ class RuntimeServiceABC(ABC):
@abstractmethod @abstractmethod
def line_count(self, line_count: int): pass 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 @abstractmethod
def input(self, prefix: str) -> str: pass def input(self, prefix: str) -> str: pass

View File

@ -7,6 +7,7 @@ class RuntimeService(RuntimeServiceABC):
def __init__(self): def __init__(self):
self._line_count = 0 self._line_count = 0
self._file = ''
@property @property
def line_count(self) -> int: def line_count(self) -> int:
@ -16,6 +17,13 @@ class RuntimeService(RuntimeServiceABC):
def line_count(self, line_count: int): def line_count(self, line_count: int):
self._line_count = line_count 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: def input(self, prefix: str) -> str:
return Console.read_line(prefix) return Console.read_line(prefix)
@ -24,12 +32,17 @@ class RuntimeService(RuntimeServiceABC):
def error(self, error: Error) -> None: def error(self, error: Error) -> None:
Console.set_foreground_color(ForegroundColorEnum.red) Console.set_foreground_color(ForegroundColorEnum.red)
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.write_line(f'Error in line {self._line_count}\n{error.message}')
Console.color_reset() Console.color_reset()
Console.write_line()
exit() exit()
def runtime_error(self, error: Error) -> None: def runtime_error(self, error: Error) -> None:
Console.set_foreground_color(ForegroundColorEnum.red) Console.set_foreground_color(ForegroundColorEnum.red)
Console.write_line(f'{error.message}', 'red') Console.write_line(f'{error.message}', 'red')
Console.color_reset() Console.color_reset()
Console.write_line()
exit() exit()