Added logic to handle lib & class declaration

This commit is contained in:
Sven Heidemann 2021-10-27 18:30:22 +02:00
parent 793ca62ddd
commit 2b02341336
23 changed files with 292 additions and 33 deletions

View File

@ -1,12 +1,12 @@
{ {
"WorkspaceSettings": { "WorkspaceSettings": {
"DefaultProject": "cc-lang", "DefaultProject": "cc-lang-interpreter",
"Projects": { "Projects": {
"cc-lang-interpreter": "src/cc_lang_interpreter/cc-lang-interpreter.json",
"cc-lang": "src/cc_lang/cc-lang.json", "cc-lang": "src/cc_lang/cc-lang.json",
"parser": "src/parser/parser.json", "parser": "src/parser/parser.json",
"lexer": "src/lexer/lexer.json", "lexer": "src/lexer/lexer.json",
"runtime": "src/runtime/runtime.json", "runtime": "src/runtime/runtime.json"
"cc-lang-interpreter": "src/cc_lang_interpreter/cc-lang-interpreter.json"
} }
} }
} }

View File

@ -16,7 +16,8 @@
"LicenseName": "", "LicenseName": "",
"LicenseDescription": "", "LicenseDescription": "",
"Dependencies": [ "Dependencies": [
"sh_cpl==2021.4.0.post2" "sh_cpl-core==2021.10.0.post1",
"sh_cpl-query==2021.10.0.post1"
], ],
"PythonVersion": ">=3.9.2", "PythonVersion": ">=3.9.2",
"PythonPath": { "PythonPath": {

View File

@ -4,8 +4,11 @@ from cpl_core.application import ApplicationABC
from cpl_core.configuration import ConfigurationABC from cpl_core.configuration import ConfigurationABC
from cpl_core.console import Console from cpl_core.console import Console
from cpl_core.dependency_injection import ServiceProviderABC from cpl_core.dependency_injection import ServiceProviderABC
from cpl_query.extension.list import List
from lexer.abc.lexer_abc import LexerABC from lexer.abc.lexer_abc import LexerABC
from lexer.model.token import Token
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
@ -22,15 +25,17 @@ class Application(ApplicationABC):
self._path = config.get_configuration('p') self._path = config.get_configuration('p')
def _interpret(self, line: str): def _interpret(self, line: str):
tokens = self._lexer.tokenize(line) tokens: List[Token] = self._lexer.tokenize(line)
ast = self._parser.create_ast(tokens) ast: List[AST] = self._parser.create_ast(tokens)
line.replace("\n", "").replace("\t", "") line.replace("\n", "").replace("\t", "")
# Console.write_line(f'<{self._runtime.line_count}> LINE: {line}') Console.write_line(f'<{self._runtime.line_count}> LINE: {line}')
# header, values = ['Type', 'Value'], [] # header, values = ['Type', 'Value'], []
# tokens.for_each(lambda t: values.append([t.type, t.value])) # tokens.for_each(lambda t: values.append([t.type, t.value]))
# Console.table(header, values) # Console.table(header, values)
Console.write(ast, '\n')
def _console(self): def _console(self):
i = 0 i = 0
while True: while True:
@ -62,8 +67,7 @@ class Application(ApplicationABC):
self._runtime.line_count = i + 1 self._runtime.line_count = i + 1
self._interpret(f[i]) self._interpret(f[i])
def configure(self): def configure(self): pass
pass
def main(self): def main(self):
if self._path is None: if self._path is None:

View File

@ -17,7 +17,7 @@
"LicenseDescription": "", "LicenseDescription": "",
"Dependencies": [ "Dependencies": [
"sh_cpl-core==2021.10.0.post1", "sh_cpl-core==2021.10.0.post1",
"sh_cpl-query==2021.10.0" "sh_cpl-query==2021.10.0.post1"
], ],
"PythonVersion": ">=3.9.2", "PythonVersion": ">=3.9.2",
"PythonPath": { "PythonPath": {

View File

@ -23,8 +23,8 @@ class Startup(StartupABC):
return config return config
def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironment) -> ServiceProviderABC: def configure_services(self, services: ServiceCollectionABC, env: ApplicationEnvironment) -> ServiceProviderABC:
services.add_singleton(RuntimeServiceABC, RuntimeService)
services.add_singleton(LexerABC, LexerService) services.add_singleton(LexerABC, LexerService)
services.add_singleton(ParserABC, ParserService) services.add_singleton(ParserABC, ParserService)
services.add_singleton(RuntimeServiceABC, RuntimeService)
return services.build_service_provider() return services.build_service_provider()

View File

@ -16,7 +16,8 @@
"LicenseName": "", "LicenseName": "",
"LicenseDescription": "", "LicenseDescription": "",
"Dependencies": [ "Dependencies": [
"sh_cpl==2021.4.0.post2" "sh_cpl-core==2021.10.0.post1",
"sh_cpl-query==2021.10.0.post1"
], ],
"PythonVersion": ">=3.9.2", "PythonVersion": ">=3.9.2",
"PythonPath": { "PythonPath": {

View File

@ -19,5 +19,8 @@ class Token:
def value(self, value: str): def value(self, value: str):
self._value = value self._value = value
def __repr__(self) -> str:
return f'Token <Type: {self._type}> <Value: {self._value}>'
def __str__(self) -> str: def __str__(self) -> str:
return f'Token: Type: {self._type}, Value: {self._value}' return f'Token: Type: {self._type}, Value: {self._value}'

34
src/parser/abc/ast.py Normal file
View File

@ -0,0 +1,34 @@
from typing import Optional, Union
from cpl_query.extension.list import List
from parser.model.ast_types_enum import ASTTypesEnum
class AST():
def __init__(self, type: ASTTypesEnum, value: Union[str, List['AST']], start: Optional[int] = None, end: Optional[int] = None):
self._type = type
self._value = value
self._start = start
self._end = end
@property
def type(self) -> ASTTypesEnum:
return self._type
@property
def value(self) -> Union[str, List['AST']]:
return self._value
@property
def start(self) -> Optional[int]:
return self._start
@property
def end(self) -> Optional[int]:
return self._end
def __repr__(self) -> str:
return f'AST <Type: {self._type}> <Value: {self._value}> <{self._start},{self._end}>'
def __str__(self) -> str:
return f'AST <Type: {self._type}> <Value: {self._value}> <{self._start},{self._end}>'

View File

@ -1,7 +0,0 @@
from abc import ABC, abstractmethod
class AST(ABC):
@abstractmethod
def __init__(self): pass

View File

@ -2,7 +2,7 @@ from abc import ABC, abstractmethod
from cpl_query.extension.list import List from cpl_query.extension.list import List
from parser.abc.ast_abc import AST from parser.abc.ast import AST
from lexer.model.token import Token from lexer.model.token import Token
class ParserABC(ABC): class ParserABC(ABC):

View File

@ -0,0 +1 @@
# imports

View File

@ -0,0 +1,12 @@
from enum import Enum
class ASTTypesEnum(Enum):
Access = 'access'
Keyword = 'keyword'
Name = 'name'
LibraryDeclaration = 'library_declaration'
ClassDeclaration = 'class_declaration'

View File

@ -0,0 +1,18 @@
from parser.abc.ast import AST
from cpl_query.extension.list import List
class ClassAST(AST):
def __init__(self):
AST.__init__(self)
self._body = List(AST)
@property
def body(self) -> List['AST']:
return self._body
@body.setter
def body(self, value: List['AST']):
self._body = value

View File

@ -0,0 +1,18 @@
from parser.abc.ast import AST
from cpl_query.extension.list import List
class LibraryAST(AST):
def __init__(self):
AST.__init__(self)
self._body = List(AST)
@property
def body(self) -> List['AST']:
return self._body
@body.setter
def body(self, value: List['AST']):
self._body = value

View File

@ -0,0 +1,8 @@
from enum import Enum
class ParserStateEnum(Enum):
Default = 0
Library = 1
Class = 2

View File

@ -16,7 +16,8 @@
"LicenseName": "", "LicenseName": "",
"LicenseDescription": "", "LicenseDescription": "",
"Dependencies": [ "Dependencies": [
"sh_cpl==2021.4.0.post2" "sh_cpl-core==2021.10.0.post1",
"sh_cpl-query==2021.10.0.post1"
], ],
"PythonVersion": ">=3.9.2", "PythonVersion": ">=3.9.2",
"PythonPath": { "PythonPath": {

View File

@ -1,19 +1,108 @@
from parser.abc.ast import AST
from parser.abc.parser_abc import ParserABC
from parser.model.ast_types_enum import ASTTypesEnum
from cc_lang.model.language_definition_classes import FormatCharacters, Keywords
from cpl_core.console.console import Console from cpl_core.console.console import Console
from cpl_query.extension.list import List from cpl_query.extension.list import List
from lexer.model.token import Token from lexer.model.token import Token
from parser.abc.ast_abc import AST from lexer.model.token_types import TokenTypes
from parser.abc.parser_abc import ParserABC from pynput.keyboard import Key
from parser.model.parser_state_enum import ParserStateEnum
from runtime.abc.runtime_service_abc import RuntimeServiceABC
from runtime.model.error import Error
from runtime.model.error_codes_enum import ErrorCodesEnum
class ParserService(ParserABC): class ParserService(ParserABC):
def __init__(self): def __init__(self, runtime: RuntimeServiceABC):
pass self._runtime = runtime
self._access_keywords = [
Keywords.Public.value,
Keywords.Private.value,
Keywords.Static.value,
Keywords.This.value,
]
def _parse_library_or_class(self, tokens: List[Token], cls=False) -> AST:
""" Handles library declaration
Args:
tokens (List[Token]): Tokens from lexer
AST:
lib Main {
<lib, class> <name> <end>
public lib Main {
<access> <lib, class> <name> <end>
public lib Main {}
<access> <lib, class> <name> <end> <end>
Returns:
AST: Library or class AST
"""
end = None
ast = List(AST)
for i in range(0, 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))
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))
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))
if i >= 1 and token.type == TokenTypes.Name:
ast.append(AST(ASTTypesEnum.Name, token.value,
self._runtime.line_count, self._runtime.line_count))
return AST(ASTTypesEnum.LibraryDeclaration if not cls else ASTTypesEnum.ClassDeclaration, ast, self._runtime.line_count, end)
def _parse_variable(self, tokens: List[AST]) -> AST:
""" Parses variable declarations
Args:
tokens (List[Token]): Tokens from lexer
AST:
var test: number;
<var> <name> <type> <end>
var test: number = 0;
<var> <name> <type> <value | int, str, bool> <end>
var test: number = test;
<var> <name> <type> <name> <end>
var test: number = TestClass();
<var> <name> <type> <object-assign> <end>
private var test: number = 0;
<access> <var> <name> <type> <end>
Returns:
AST: Library or class AST
"""
def create_ast(self, tokens: List[Token]) -> List[AST]: def create_ast(self, tokens: List[Token]) -> List[AST]:
for i in range(0, tokens.count()): self._ast = List(AST)
prev_token = tokens[i-1] if i-1 > 0 else None
token = tokens[i]
next_token = tokens[i+1] if i+1 < tokens.count() else None
Console.write_line(token) if tokens.where(lambda t: t.type == TokenTypes.Keyword and t.value == Keywords.Library.value).count() > 0:
self._ast.append(self._parse_library_or_class(tokens))
elif tokens.where(lambda t: t.type == TokenTypes.Keyword and t.value == Keywords.Class.value).count() > 0:
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))
return self._ast

View File

@ -1,5 +1,10 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from cpl_core.console.console import Console
from cpl_core.console.foreground_color_enum import ForegroundColorEnum
from runtime.model.error import Error
class RuntimeServiceABC(ABC): class RuntimeServiceABC(ABC):
@ -13,3 +18,15 @@ class RuntimeServiceABC(ABC):
@line_count.setter @line_count.setter
@abstractmethod @abstractmethod
def line_count(self, line_count: int): pass def line_count(self, line_count: int): pass
@abstractmethod
def input(self, prefix: str) -> str: pass
@abstractmethod
def output(self, text: str): pass
@abstractmethod
def error(self, error: Error): pass
@abstractmethod
def runtime_error(self, error: Error): pass

View File

@ -0,0 +1 @@
# imports

View File

@ -0,0 +1,16 @@
from runtime.model.error_codes_enum import ErrorCodesEnum
class Error:
def __init__(self, code: ErrorCodesEnum, msg: str):
self._code = code
self._msg = code.value.format(msg)
@property
def code(self) -> ErrorCodesEnum:
return self._code
@property
def message(self) -> str:
return self._msg

View File

@ -0,0 +1,21 @@
from enum import Enum
class ErrorCodesEnum(Enum):
StartFailed = 'Start failed'
FileNotFound = 'File not found'
WrongFileType = 'Wrong file type'
Unknown = 'Unknown {}'
Inaccessible = '{} inaccessible'
Unexpected = 'Unexpected {}'
Expected = 'Expected {}'
LibInLib = 'Lib in lib'
LibInClass = 'Lib in class'
LibInFunc = 'Lib in func'
ClassInClass = 'Class in class'
ClassInFunc = 'Class in func'
FuncInLib = 'Func in lib'
FuncInFunc = 'Func in func'

View File

@ -16,7 +16,8 @@
"LicenseName": "", "LicenseName": "",
"LicenseDescription": "", "LicenseDescription": "",
"Dependencies": [ "Dependencies": [
"sh_cpl==2021.4.0.post2" "sh_cpl-core==2021.10.0.post1",
"sh_cpl-query==2021.10.0.post1"
], ],
"PythonVersion": ">=3.9.2", "PythonVersion": ">=3.9.2",
"PythonPath": { "PythonPath": {

View File

@ -1,4 +1,6 @@
from cpl_core.console import Console, ForegroundColorEnum
from runtime.abc.runtime_service_abc import RuntimeServiceABC from runtime.abc.runtime_service_abc import RuntimeServiceABC
from runtime.model.error import Error
class RuntimeService(RuntimeServiceABC): class RuntimeService(RuntimeServiceABC):
@ -13,3 +15,21 @@ class RuntimeService(RuntimeServiceABC):
@line_count.setter @line_count.setter
def line_count(self, line_count: int): def line_count(self, line_count: int):
self._line_count = line_count self._line_count = line_count
def input(self, prefix: str) -> str:
return Console.read_line(prefix)
def output(self, text: str) -> None:
Console.write_line(f'> {text}')
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}')
Console.color_reset()
exit()
def runtime_error(self, error: Error) -> None:
Console.set_foreground_color(ForegroundColorEnum.red)
Console.write_line(f'{error.message}', 'red')
Console.color_reset()
exit()