From dfc4b44b2507ea950971f808c64df0acff888b04 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Thu, 17 Sep 2020 19:33:52 +0200 Subject: [PATCH] Improved parsing --- doc/ast_rules.txt | 10 +- doc/target/test2.bl | 4 +- src/First.bl | 25 ++ src/Interpreter/Interpreter.py | 29 +- src/Interpreter/Lexer.py | 87 +++-- src/Interpreter/Parser.py | 328 ++++++++++++++++-- src/Interpreter/Repo.py | 93 ++--- src/Interpreter/Utils.py | 4 +- src/Main.py | 26 +- .../AbstractSyntaxTree/AbstractSyntaxTree.py | 9 + src/Models/AbstractSyntaxTree/Class.py | 13 + src/Models/AbstractSyntaxTree/Function.py | 14 + src/Models/AbstractSyntaxTree/Instruction.py | 9 + .../AbstractSyntaxTree/InstructionTypes.py | 10 + src/Models/AbstractSyntaxTree/Library.py | 11 + .../RuntimeAbstractSyntaxTree.py | 9 + src/Models/AbstractSyntaxTree/Variable.py | 12 + .../{Nodes => AbstractSyntaxTree}/__init__.py | 0 src/Models/Interpreter/Booleans.py | 4 + src/Models/Interpreter/Error.py | 53 ++- .../Interpreter/ExpressionCharacters.py | 19 + src/Models/Interpreter/FormatCharacters.py | 27 ++ src/Models/Interpreter/Keywords.py | 30 ++ src/Models/Interpreter/Token.py | 7 +- src/Models/Interpreter/TokenTypes.py | 14 + src/Models/Interpreter/Types.py | 24 ++ .../Interpreter/UnresolvedTokenTypes.py | 11 + src/Models/Nodes/Class.py | 6 - src/Models/Nodes/Func.py | 7 - src/Models/Nodes/Lib.py | 5 - src/Models/Nodes/Var.py | 6 - 31 files changed, 717 insertions(+), 189 deletions(-) create mode 100644 src/First.bl create mode 100644 src/Models/AbstractSyntaxTree/AbstractSyntaxTree.py create mode 100644 src/Models/AbstractSyntaxTree/Class.py create mode 100644 src/Models/AbstractSyntaxTree/Function.py create mode 100644 src/Models/AbstractSyntaxTree/Instruction.py create mode 100644 src/Models/AbstractSyntaxTree/InstructionTypes.py create mode 100644 src/Models/AbstractSyntaxTree/Library.py create mode 100644 src/Models/AbstractSyntaxTree/RuntimeAbstractSyntaxTree.py create mode 100644 src/Models/AbstractSyntaxTree/Variable.py rename src/Models/{Nodes => AbstractSyntaxTree}/__init__.py (100%) create mode 100644 src/Models/Interpreter/Booleans.py create mode 100644 src/Models/Interpreter/ExpressionCharacters.py create mode 100644 src/Models/Interpreter/FormatCharacters.py create mode 100644 src/Models/Interpreter/Keywords.py create mode 100644 src/Models/Interpreter/TokenTypes.py create mode 100644 src/Models/Interpreter/Types.py create mode 100644 src/Models/Interpreter/UnresolvedTokenTypes.py delete mode 100644 src/Models/Nodes/Class.py delete mode 100644 src/Models/Nodes/Func.py delete mode 100644 src/Models/Nodes/Lib.py delete mode 100644 src/Models/Nodes/Var.py diff --git a/doc/ast_rules.txt b/doc/ast_rules.txt index c1a571a..be7dced 100644 --- a/doc/ast_rules.txt +++ b/doc/ast_rules.txt @@ -1,9 +1,7 @@ -len 3: +lib main { - lib main { + class test { -min len 3, max len 4: - class test { + func test(): void { -min len 5 - func test() { \ No newline at end of file + var: bool <= test <> >; \ No newline at end of file diff --git a/doc/target/test2.bl b/doc/target/test2.bl index a2adee5..4f6a612 100644 --- a/doc/target/test2.bl +++ b/doc/target/test2.bl @@ -1,12 +1,12 @@ lib Tests { public class test2 { - var string_a = string1(); + var string_a = strings(); public func continue(): void { input(string_a.string1 + ': '); } } class strings { - var public string1 = "hello world"; + public var string1 = "hello world"; } } \ No newline at end of file diff --git a/src/First.bl b/src/First.bl new file mode 100644 index 0000000..f2c756b --- /dev/null +++ b/src/First.bl @@ -0,0 +1,25 @@ +// hi1 +# hi2 +/* + hi3 +*/ + +public lib Main { + class Program { + var test: bool = false; + var test2: Program2 = empty; + var test3: Program2 = Test(); + + func Main(): void { + #func Main { + var hallo: any; + #output('Hello'); + #output(this.isTrue(false)); + } + + func isTrue(value: bool): bool { + #func isTrue { + #return value; + } + } +} \ No newline at end of file diff --git a/src/Interpreter/Interpreter.py b/src/Interpreter/Interpreter.py index 3c4ff4f..dc184a9 100644 --- a/src/Interpreter/Interpreter.py +++ b/src/Interpreter/Interpreter.py @@ -14,22 +14,19 @@ class Interpreter: self.__parser = Parser(repo, utils) self.__validator = Validator(repo, utils) - def interpret(self, line_int: int, line_str: str) -> bool: - toks = [] - ast = [] + def interpret(self, line_str: str) -> None: + """ + Interprets code line + :param line_str: + :return: + """ + tokens = [] - self.__utils.line_number = line_int - if self.__repo.error is None: - toks = self.__lexer.tokenize(line_str) + if self.__repo.is_error is None: + tokens = self.__lexer.tokenize(line_str) - self.__repo.output_tokens(toks) + if self.__repo.is_error is None: + self.__parser.parse(tokens) - if self.__repo.error is None: - ast = self.__parser.parse(toks) - - # self.__repo.output_ast(ast) - - if self.__repo.error is None: - self.__validator.validate(ast) - - return self.__repo.error is None + # if self.__repo.is_error is None: + # self.__validator.validate(self.__repo.AST) diff --git a/src/Interpreter/Lexer.py b/src/Interpreter/Lexer.py index 51c28ef..43772d0 100644 --- a/src/Interpreter/Lexer.py +++ b/src/Interpreter/Lexer.py @@ -1,6 +1,10 @@ +from typing import List + from Interpreter.Repo import Repo from Interpreter.Utils import Utils from Models.Interpreter.Token import Token +from Models.Interpreter.TokenTypes import TokenTypes +from Models.Interpreter.UnresolvedTokenTypes import UnresolvedTokenTypes class Lexer: @@ -9,31 +13,58 @@ class Lexer: self.__repo = repo self.__utils = utils - self.__ml_comment = False - self.__toks = [] + self.__is_ml_comment = False + + def __add_tok(self, tokens: List[Token], value: str, input_token_type: UnresolvedTokenTypes) -> None: + """ + Creates token object + :param value: + :param token_type: + :return: + """ + token_type: TokenTypes = TokenTypes.Empty - def __add_tok(self, value: str, type: str) -> None: if value != '': - if type == 'word': + if input_token_type == UnresolvedTokenTypes.Word: if value in self.__repo.keywords: - type = 'keyword' + token_type = TokenTypes.Keyword elif value in self.__repo.types: - type = 'type' + token_type = TokenTypes.Type elif value in self.__repo.bool_values: - type = 'bool' + token_type = TokenTypes.Bool - elif value == 'empty': - type = 'emptyType' + elif value == UnresolvedTokenTypes.Empty: + token_type = TokenTypes.Empty else: - type = 'name' + token_type = TokenTypes.Name - self.__toks.append(Token(type, value)) + elif input_token_type == UnresolvedTokenTypes.Number: + token_type = TokenTypes.Number - def tokenize(self, line: str) -> list: - self.__toks = [] + elif input_token_type == UnresolvedTokenTypes.String: + token_type = TokenTypes.String + + elif input_token_type == UnresolvedTokenTypes.Expression_Character: + token_type = TokenTypes.Expression_Character + + elif input_token_type == UnresolvedTokenTypes.Bool_Expression_Character: + token_type = TokenTypes.Bool_Expression_Character + + elif input_token_type == UnresolvedTokenTypes.Format_Character: + token_type = TokenTypes.Format_Character + + tokens.append(Token(token_type, value)) + + def tokenize(self, line: str) -> List[Token]: + """ + Creates token list from code line + :param line: + :return: + """ + tokens: List[Token] = [] word = '' ol_comment = False is_string1 = False # 'hello' @@ -44,27 +75,27 @@ class Lexer: for i in range(0, len(line)): c = line[i] # ignore comments and spaces - if not ol_comment and not self.__ml_comment: + if not ol_comment and not self.__is_ml_comment: # comment filtering if c == '#' and not is_string1 and not is_string2: ol_comment = True - elif line[i-1] == '/' and c == '/': + elif line[i - 1] == '/' and c == '/': ol_comment = True - elif line[i-1] == '/' and c == '*': - self.__ml_comment = True + elif line[i - 1] == '/' and c == '*': + self.__is_ml_comment = True i += 2 # end of number elif not c.isdigit() and c != '.' and is_number: - self.__add_tok(word, 'number') + self.__add_tok(tokens, word, UnresolvedTokenTypes.Number) word = '' is_number = False # end of expression char elif c not in self.__repo.expr_chars and is_expr_char: - self.__add_tok(word, 'expr_char') + self.__add_tok(tokens, word, UnresolvedTokenTypes.Expression_Character) word = '' is_expr_char = False @@ -76,7 +107,7 @@ class Lexer: # end of is_string1 elif c == '\'' and is_string1: is_string1 = False - self.__add_tok(word, 'string') + self.__add_tok(tokens, word, UnresolvedTokenTypes.String) word = '' # begin of is_string2 @@ -87,13 +118,13 @@ class Lexer: # end of is_string2 elif c == '\"' and is_string2: is_string2 = False - self.__add_tok(word, 'string') + self.__add_tok(tokens, word, UnresolvedTokenTypes.String) word = '' # format char elif c in self.__repo.format_chars: - self.__add_tok(word, 'word') - self.__add_tok(c, 'format_char') + self.__add_tok(tokens, word, UnresolvedTokenTypes.Word) + self.__add_tok(tokens, c, UnresolvedTokenTypes.Format_Character) word = '' # begin of number @@ -116,13 +147,13 @@ class Lexer: # bool expression char elif c in self.__repo.bool_expr_chars: - self.__add_tok(word, 'word') - self.__add_tok(c, 'bool_expr_char') + self.__add_tok(tokens, word, UnresolvedTokenTypes.Word) + self.__add_tok(tokens, c, UnresolvedTokenTypes.Bool_Expression_Character) word = '' # end of word elif c == ' ' and not is_string1 and not is_string2 or c == '\n': - self.__add_tok(word, 'word') + self.__add_tok(tokens, word, UnresolvedTokenTypes.Word) word = '' else: @@ -132,6 +163,6 @@ class Lexer: ol_comment = False if line[i - 1] == '*' and c == '/': - self.__ml_comment = False + self.__is_ml_comment = False - return self.__toks + return tokens diff --git a/src/Interpreter/Parser.py b/src/Interpreter/Parser.py index 301b15c..b6f9fe8 100644 --- a/src/Interpreter/Parser.py +++ b/src/Interpreter/Parser.py @@ -1,10 +1,20 @@ +from typing import List, Optional + +from termcolor import colored + from Interpreter.Repo import Repo from Interpreter.Utils import Utils -from Models.Interpreter.Error import Error +from Models.AbstractSyntaxTree.Class import Class +from Models.AbstractSyntaxTree.Function import Function +from Models.AbstractSyntaxTree.Library import Library +from Models.AbstractSyntaxTree.Variable import Variable +from Models.Interpreter.Error import Error, ErrorCode +from Models.Interpreter.ExpressionCharacters import ExpressionCharacters +from Models.Interpreter.FormatCharacters import FormatCharacters +from Models.Interpreter.Keywords import Keywords from Models.Interpreter.Token import Token -from Models.Nodes.Class import Class -from Models.Nodes.Func import Func -from Models.Nodes.Lib import Lib +from Models.Interpreter.TokenTypes import TokenTypes +from Models.Interpreter.Types import Types class Parser: @@ -13,37 +23,295 @@ class Parser: self.__repo = repo self.__utils = utils - self.__ast = [] + # library status + self.__is_creating_library = False + self.__is_saving_library = False + self.__library: Optional[Library] = None - def parse(self, toks: list) -> []: - self.__tokens = toks - self.__ast = [] + # class status + self.__is_creating_class = False + self.__is_saving_class = False + self.__class: Optional[Class] = None + + # function status + self.__is_creating_function = False + self.__is_creating_function_parameters = False + self.__is_saving_function = False + self.__function: Optional[Function] = None + + # variable status + self.__is_creating_variable = False + self.__is_saving_variable = False + self.__variable: Optional[Variable] = None + + # type status + self.__is_creating_type = False + self.__type: Optional[Types] = None + + # common + self.__next_is_public = True + + def parse(self, tokens: List[Token]) -> None: + """ + Creates abstract syntax tree from token list + :param tokens: + :return: + """ # output - if len(toks) > 0: - tokens = [] - for t in toks: - tokens.append({t.value: t.type}) + if len(tokens) == 0: + return None - # print(tokens) + output_tokens = [] + for t in tokens: + output_tokens.append({t.value: t.type}) + # print(output_tokens) - self.__check() + for i in range(0, len(tokens)): + token = tokens[i] + print(colored(f'>> {self.__utils.line_number}: {token.type} {token.value}', 'blue')) - # output - if len(self.__repo.ast) > 1: - print('___') - for a_lib in self.__repo.ast: - print(a_lib.name) - for a_class in a_lib.ast: - print(a_class.name, a_class.access) - for a_funcs in a_class.ast: - print(a_funcs.name, a_funcs.return_type, a_funcs.access) + """ error status checks """ + # check if var name is valid + if i-1 >= 0 and self.__is_creating_variable and tokens[i-1].value == Keywords.Variable: + if token.type != TokenTypes.Name: + self.__utils.error(Error(ErrorCode.Unexpected, f'{token.type.name}: {token.value}')) - print('___') - # print(self.__repo.ast, '\n') + """ normal status checks """ + # token is member of variable value + if self.__is_saving_variable and token.value != FormatCharacters.chars.value[FormatCharacters.Semicolon.value] and self.__variable is not None: + print(6, 'save variable value') + self.__variable.value.append(token) - return self.__ast + # token type keyword + if token.type == TokenTypes.Keyword: + self.__check_token_keyword(tokens, i, token) - def __check(self) -> None: - for i in range(0, len(self.__tokens)): - tok = self.__tokens[i] - # print(tok.value, tok.type) + # token type format character + elif token.type == TokenTypes.Format_Character: + self.__check_token_format_character(token) + + # token type expression character + elif token.type == TokenTypes.Expression_Character: + self.__check_token_expression_character(token) + + # token type name + elif token.type == TokenTypes.Name: + self.__check_token_name(token) + + # token type type + elif token.type == TokenTypes.Type: + self.__check_token_type(token) + + """ validation function """ + def __check_token_keyword(self, tokens: List[Token], i: int, token: Token) -> None: + if token.value == Keywords.Library: + # create library + self.__is_creating_library = True + self.__library = Library() + self.__set_is_public(self.__library) + print(1, 'create lib') + + elif token.value == Keywords.Class: + # create class + self.__is_creating_class = True + self.__class = Class() + self.__set_is_public(self.__class) + print(2, 'create class') + + elif token.value == Keywords.Function: + # create function + self.__is_creating_function = True + self.__function = Function() + self.__set_is_public(self.__function) + print(3, 'create function') + + elif token.value == Keywords.Variable: + self.__is_creating_variable = True + self.__variable = Variable() + print(6, 'create variable') + + elif token.value == Keywords.Public: + if self.__is_saving_function: + self.__utils.error(Error(ErrorCode.Unexpected, f'{token.type.name}: {token.value}')) + return + + self.__next_is_public = True + print(0, 'Next is public') + + elif self.__next_is_public: + if i - 1 > 0: + error_msg = f'{token.type.name}: {tokens[i - 1].value}' + else: + error_msg = 'keyword: public' + + self.__utils.error(Error(ErrorCode.Unexpected, error_msg)) + + def __check_token_format_character(self, token: Token) -> None: + if token.value == FormatCharacters.chars.value[FormatCharacters.Left_Brace.value]: + + if self.__is_creating_library: + # set status to save vars and funcs to library + self.__is_creating_library = False + self.__is_saving_library = True + print(1, 'status save lib') + + elif self.__is_creating_class: + # set status to save vars and funcs to class + self.__is_creating_class = False + self.__is_saving_class = True + print(2, 'status save class') + + elif self.__is_creating_function: + # set status to save vars and commands to function + self.__is_creating_function = False + self.__is_saving_function = True + print(3, 'status save function') + + # check for type end + if self.__is_creating_type: + self.__check_for_type_end() + + elif token.value == FormatCharacters.chars.value[FormatCharacters.Right_Brace.value]: + if self.__is_saving_function: + # save function to class + self.__is_saving_function = False + + if self.__class is not None: + self.__class.functions.append(self.__function) + print(3, f'saved function {self.__function.name}') + else: + self.__utils.error(Error(ErrorCode.Unexpected, f'function: {self.__function.name}')) + + self.__function = None + + elif self.__is_saving_class: + # save class to library + self.__is_saving_class = False + + if self.__library is not None: + self.__library.classes.append(self.__class) + print(2, f'saved class {self.__class.name}') + else: + self.__utils.error(Error(ErrorCode.Unexpected, f'class: {self.__class.name}')) + + self.__class = None + + elif self.__is_saving_library: + # save library to ast + self.__is_saving_library = False + + self.__repo.AST.libs.append(self.__library) + print(1, f'saved lib {self.__library.name}') + self.__library = None + + elif token.value == FormatCharacters.chars.value[FormatCharacters.Left_Parenthesis.value]: + if self.__is_creating_function: + self.__is_creating_function_parameters = True + print(4, 'create function parameters') + + elif token.value == FormatCharacters.chars.value[FormatCharacters.Right_Parenthesis.value]: + if self.__is_creating_function: + self.__is_creating_function_parameters = False + self.__type = None + print(4, 'save function parameters') + + # check for type end + if self.__is_creating_type: + self.__check_for_type_end() + + elif token.value == FormatCharacters.chars.value[FormatCharacters.Colon.value]: + self.__check_create_type(token) + + elif token.value == FormatCharacters.chars.value[FormatCharacters.Semicolon.value]: + if self.__is_creating_type: + self.__check_for_type_end() + + if self.__is_saving_variable: + self.__is_saving_variable = False + self.__add_variable() + self.__variable = None + print(6, 'status saved variable') + + elif self.__is_creating_variable: + self.__is_creating_variable = False + self.__is_saving_variable = True + self.__add_variable() + self.__variable = None + print(6, 'status saved variable') + + def __check_token_expression_character(self, token: Token) -> None: + if token.value == ExpressionCharacters.chars.value[ExpressionCharacters.Equal.value]: + # check for type end + if self.__is_creating_type: + self.__is_creating_variable = False + self.__is_saving_variable = True + self.__check_for_type_end() + + def __check_token_name(self, token: Token) -> None: + if self.__is_creating_type and self.__type is not None: + self.__type = token.value + print(5, 'save type') + + elif self.__is_creating_variable and self.__variable is not None: + self.__variable.name = token.value + print(6, 'save variable name') + + elif self.__is_creating_library: + # save library name + self.__library.name = token.value + print(1, 'lib name') + + elif self.__is_creating_class: + # save class name + self.__class.name = token.value + print(2, 'class name') + + elif self.__is_creating_function and not self.__is_creating_function_parameters: + # save function name + self.__function.name = token.value + print(3, 'function name') + + def __check_token_type(self, token: Token) -> None: + if self.__is_creating_function and self.__function is not None and not self.__is_creating_function_parameters: + self.__function.return_type = Types(Types.strings.value.index(token.value)) + print(3, 'save function type') + + elif self.__is_creating_type: + self.__type = Types(Types.strings.value.index(token.value)) + print(5, 'save type') + + """ util functions """ + def __set_is_public(self, ast_element): + if ast_element is not None and self.__next_is_public: + ast_element.is_public = True + self.__next_is_public = False + + def __check_for_type_end(self): + # end status creating type + self.__is_creating_type = False + print(5, 'status saved type') + + def __check_create_type(self, token: Token): + if self.__is_creating_function or self.__is_creating_function_parameters or self.__is_creating_variable: + self.__is_creating_type = True + print(5, 'create type') + else: + self.__utils.error(Error(ErrorCode.Unexpected, f'{token.type.name}: {token.value}')) + + def __add_variable(self): + if len(self.__variable.value) == 0: + self.__variable.value.append(Token(TokenTypes.Empty, Types.strings.value[Types.Empty.value])) + + if self.__type is not None: + self.__variable.type = self.__type + else: + self.__utils.error(Error(ErrorCode.Expected, f'type')) + + if self.__is_saving_function and self.__function is not None: + self.__function.variables.append(self.__variable) + + elif self.__is_saving_class and self.__class is not None: + self.__class.variables.append(self.__variable) + + else: + self.__utils.error(Error(ErrorCode.Unexpected, f'variable: {self.__variable.name}')) diff --git a/src/Interpreter/Repo.py b/src/Interpreter/Repo.py index 4fabc7b..694bbb3 100644 --- a/src/Interpreter/Repo.py +++ b/src/Interpreter/Repo.py @@ -1,3 +1,10 @@ +from Models.AbstractSyntaxTree.RuntimeAbstractSyntaxTree import RuntimeAbstractSyntaxTree +from Models.Interpreter.Booleans import Booleans +from Models.Interpreter.FormatCharacters import FormatCharacters +from Models.Interpreter.Keywords import Keywords +from Models.Interpreter.Types import Types + + class Repo: def __init__(self) -> None: @@ -5,56 +12,58 @@ class Repo: # interpreter self.keywords = [ - # define keys - 'lib', - 'class', - 'func', - 'var', - 'use', - 'from', + # define keywords + Keywords.Library, + Keywords.Class, + Keywords.Function, + Keywords.Variable, + Keywords.Use, + Keywords.From, # builtin functions - 'output', - 'input', - 'length', - 'range', - 'exit', + Keywords.Output, + Keywords.Input, + Keywords.Length, + Keywords.Range, + Keywords.Exit, # normal keywords - 'if', - 'elseif', - 'else', - 'pass', - 'in', + Keywords.If, + Keywords.ElseIf, + Keywords.Else, + Keywords.Pass, + Keywords.If, + Keywords.Return, # loops - 'while', - 'for', + Keywords.While, + Keywords.For, # access - 'public' + Keywords.Public ] self.types = [ - 'any', - 'number', - 'string', - 'bool', - 'list', - 'dict', - 'emptyType', - 'void' + Types.strings.value[Types.Any.value], + Types.strings.value[Types.Number.value], + Types.strings.value[Types.String.value], + Types.strings.value[Types.Bool.value], + Types.strings.value[Types.List.value], + Types.strings.value[Types.Dict.value], + Types.strings.value[Types.Empty.value], + Types.strings.value[Types.Void.value] + ] + self.format_chars = [ + FormatCharacters.chars.value[FormatCharacters.Left_Brace.value], + FormatCharacters.chars.value[FormatCharacters.Right_Brace.value], + FormatCharacters.chars.value[FormatCharacters.Left_Parenthesis.value], + FormatCharacters.chars.value[FormatCharacters.Right_Parenthesis.value], + FormatCharacters.chars.value[FormatCharacters.Left_Bracket.value], + FormatCharacters.chars.value[FormatCharacters.Right_Bracket.value], + FormatCharacters.chars.value[FormatCharacters.Semicolon.value], + FormatCharacters.chars.value[FormatCharacters.Colon.value], + FormatCharacters.chars.value[FormatCharacters.Comma.value], + FormatCharacters.chars.value[FormatCharacters.Point.value] ] - self.format_chars = ['{', '}', '(', ')', ';', ':', ',', '.'] self.expr_chars = ['+', '-', '*', '/', '=', '^'] self.bool_expr_chars = ['<', '>', '!', '!=', '==', '>=', '<=', '&&', '||'] - self.bool_values = ['true', 'false'] + self.bool_values = [Booleans.Right, Booleans.Wrong] # runtime - self.error = None - self.ast = [] - - def output_tokens(self, toks: list) -> None: - if self.debug and len(toks) > 0: - outp_toks = [] - for tok in toks: - outp_toks.append({tok.value: tok.type}) - # print({tok.value: tok.type}) - - print(outp_toks) - # print('\n') + self.is_error = None + self.AST: RuntimeAbstractSyntaxTree = RuntimeAbstractSyntaxTree() diff --git a/src/Interpreter/Utils.py b/src/Interpreter/Utils.py index 4e4dce0..57d64cc 100644 --- a/src/Interpreter/Utils.py +++ b/src/Interpreter/Utils.py @@ -17,6 +17,6 @@ class Utils: print(f'-> {text}') def error(self, error: Error) -> None: - self.__repo.error = error - print(colored(f'Error in line {self.line_number}\n{self.__repo.error.code}: {self.__repo.error.msg}', 'red')) + self.__repo.is_error = error + print(colored(f'Error in line {self.line_number}\n{self.__repo.is_error.msg}', 'red')) # exit() diff --git a/src/Main.py b/src/Main.py index a43d8f9..b0be30a 100644 --- a/src/Main.py +++ b/src/Main.py @@ -1,7 +1,8 @@ import os import sys +from pprint import pprint -from Models.Interpreter.Error import Error +from Models.Interpreter.Error import Error, ErrorCode from ServiceInitializer import ServiceInitializer @@ -14,20 +15,31 @@ class Main: self.__interpreter = self.__services.interpreter def console(self) -> None: - print('sh-edraft.de basic language interpreter:') + """ + Getting code from console input + :return: + """ i = 0 - cont = True - while cont: - cont = self.__interpreter.interpret(i+1, input('> ')) + while self.__repo.is_error is not None: + self.__utils.line_number = i + 1 + self.__interpreter.interpret(self.__utils.line_number, input('> ')) i += 1 def files(self, file: str) -> None: + """ + Getting input from file + :param file: + :return: + """ if os.path.isfile(file): f = open(file, 'r', encoding='utf-8').readlines() for i in range(0, len(f)): - self.__interpreter.interpret(i+1, f[i]) + self.__utils.line_number = i + 1 + self.__interpreter.interpret(f[i]) + + print(self.__repo.AST) else: - self.__utils.error(Error(1.1)) + self.__utils.is_error(Error(ErrorCode.FileNotFound)) if __name__ == '__main__': diff --git a/src/Models/AbstractSyntaxTree/AbstractSyntaxTree.py b/src/Models/AbstractSyntaxTree/AbstractSyntaxTree.py new file mode 100644 index 0000000..c7f17ea --- /dev/null +++ b/src/Models/AbstractSyntaxTree/AbstractSyntaxTree.py @@ -0,0 +1,9 @@ +from typing import List + +from Models.Interpreter.Token import Token + + +class AbstractSyntaxTree: + + def __init__(self): + self.tokens: List[Token] = [] diff --git a/src/Models/AbstractSyntaxTree/Class.py b/src/Models/AbstractSyntaxTree/Class.py new file mode 100644 index 0000000..10deb39 --- /dev/null +++ b/src/Models/AbstractSyntaxTree/Class.py @@ -0,0 +1,13 @@ +from typing import List + +from Models.AbstractSyntaxTree.Function import Function +from Models.AbstractSyntaxTree.Variable import Variable + + +class Class: + + def __init__(self): + self.name: str = '' + self.variables: List[Variable] = [] + self.functions: List[Function] = [] + self.is_public = False diff --git a/src/Models/AbstractSyntaxTree/Function.py b/src/Models/AbstractSyntaxTree/Function.py new file mode 100644 index 0000000..7b375c5 --- /dev/null +++ b/src/Models/AbstractSyntaxTree/Function.py @@ -0,0 +1,14 @@ +from typing import List + +from Models.AbstractSyntaxTree.Variable import Variable +from Models.Interpreter.Types import Types + + +class Function: + + def __init__(self) -> None: + self.name: str = '' + self.variables: List[Variable] = [] + self.instructions: [] = [] + self.return_type: Types = Types.Any + self.is_public = False diff --git a/src/Models/AbstractSyntaxTree/Instruction.py b/src/Models/AbstractSyntaxTree/Instruction.py new file mode 100644 index 0000000..9c5676e --- /dev/null +++ b/src/Models/AbstractSyntaxTree/Instruction.py @@ -0,0 +1,9 @@ +from Models.AbstractSyntaxTree.AbstractSyntaxTree import AbstractSyntaxTree +from Models.AbstractSyntaxTree.InstructionTypes import InstructionTypes + + +class Instruction: + + def __init__(self): + self.type: InstructionTypes = InstructionTypes.Unknown + self.ast: AbstractSyntaxTree = AbstractSyntaxTree() diff --git a/src/Models/AbstractSyntaxTree/InstructionTypes.py b/src/Models/AbstractSyntaxTree/InstructionTypes.py new file mode 100644 index 0000000..766f305 --- /dev/null +++ b/src/Models/AbstractSyntaxTree/InstructionTypes.py @@ -0,0 +1,10 @@ +from enum import Enum + + +class InstructionTypes(Enum): + + Unknown = 0 + Expression = 1 + Bool_Expression = 2 + Assignment = 3 + Call = 4 diff --git a/src/Models/AbstractSyntaxTree/Library.py b/src/Models/AbstractSyntaxTree/Library.py new file mode 100644 index 0000000..7ba8f96 --- /dev/null +++ b/src/Models/AbstractSyntaxTree/Library.py @@ -0,0 +1,11 @@ +from typing import List + +from Models.AbstractSyntaxTree.Class import Class + + +class Library: + + def __init__(self): + self.name: str = '' + self.classes: List[Class] = [] + self.is_public = False diff --git a/src/Models/AbstractSyntaxTree/RuntimeAbstractSyntaxTree.py b/src/Models/AbstractSyntaxTree/RuntimeAbstractSyntaxTree.py new file mode 100644 index 0000000..dce3d46 --- /dev/null +++ b/src/Models/AbstractSyntaxTree/RuntimeAbstractSyntaxTree.py @@ -0,0 +1,9 @@ +from typing import List + +from Models.AbstractSyntaxTree.Library import Library + + +class RuntimeAbstractSyntaxTree: + + def __init__(self): + self.libs: List[Library] = [] diff --git a/src/Models/AbstractSyntaxTree/Variable.py b/src/Models/AbstractSyntaxTree/Variable.py new file mode 100644 index 0000000..24b53af --- /dev/null +++ b/src/Models/AbstractSyntaxTree/Variable.py @@ -0,0 +1,12 @@ +from typing import List + +from Models.Interpreter.Token import Token + + +class Variable: + + def __init__(self): + self.name: str = '' + self.type: str = '' + self.value: List[Token] = [] + self.is_public = False diff --git a/src/Models/Nodes/__init__.py b/src/Models/AbstractSyntaxTree/__init__.py similarity index 100% rename from src/Models/Nodes/__init__.py rename to src/Models/AbstractSyntaxTree/__init__.py diff --git a/src/Models/Interpreter/Booleans.py b/src/Models/Interpreter/Booleans.py new file mode 100644 index 0000000..ad6c313 --- /dev/null +++ b/src/Models/Interpreter/Booleans.py @@ -0,0 +1,4 @@ +class Booleans: + + Right = 'true' + Wrong = 'false' diff --git a/src/Models/Interpreter/Error.py b/src/Models/Interpreter/Error.py index 68ad6ae..dd4eb24 100644 --- a/src/Models/Interpreter/Error.py +++ b/src/Models/Interpreter/Error.py @@ -1,34 +1,27 @@ +from enum import Enum + + +class ErrorCode(Enum): + StartFailed = 'Start failed' + FileNotFound = 'File not found' + + 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' + + class Error: - def __init__(self, code: float, msg: str = ''): + def __init__(self, code: ErrorCode, msg: str = ''): self.code = code - self.__msgs = { - # Interpreter: - 1.0: 'Start failed', - 1.1: 'File not found', - - # Runtime: - 2.0: 'Unknown keyword', - 2.1: 'Unknown type', - 2.2: 'Unknown variable', - 2.3: 'Unknown function', - 2.4: 'Unknown class', - 2.5: 'Unknown library', - - # Parser: - 3.0: 'Lib in lib', - 3.1: 'Lib in class', - 3.2: 'Lib in func', - 3.3: 'Class in class', - 3.4: 'Class in func', - 3.5: 'Func in lib', - 3.6: 'Func in func', - 3.7: 'Expected: type', - 3.8: 'Access error: no export', - 3.9: 'Expression error', - 3.10: 'Unexpected end of line', - 3.11: 'Unexpected {}', # other types - } - - self.msg = self.__msgs[code].format(msg) + self.msg = code.value.format(msg) diff --git a/src/Models/Interpreter/ExpressionCharacters.py b/src/Models/Interpreter/ExpressionCharacters.py new file mode 100644 index 0000000..b700ca3 --- /dev/null +++ b/src/Models/Interpreter/ExpressionCharacters.py @@ -0,0 +1,19 @@ +from enum import Enum + + +class ExpressionCharacters(Enum): + Plus = 0 + Minus = 1 + Asterisk = 2 + Slash = 3 + Equal = 4 + Caret = 5 + + chars = [ + '+', + '-', + '*', + '/', + '=', + '^' + ] diff --git a/src/Models/Interpreter/FormatCharacters.py b/src/Models/Interpreter/FormatCharacters.py new file mode 100644 index 0000000..eb931f8 --- /dev/null +++ b/src/Models/Interpreter/FormatCharacters.py @@ -0,0 +1,27 @@ +from enum import Enum + + +class FormatCharacters(Enum): + Left_Brace = 0 + Right_Brace = 1 + Left_Parenthesis = 2 + Right_Parenthesis = 3 + Left_Bracket = 4 + Right_Bracket = 5 + Semicolon = 6 + Colon = 7 + Comma = 8 + Point = 9 + + chars = [ + '{', + '}', + '(', + ')', + '[', + ']', + ';', + ':', + ',', + '.' + ] diff --git a/src/Models/Interpreter/Keywords.py b/src/Models/Interpreter/Keywords.py new file mode 100644 index 0000000..2394166 --- /dev/null +++ b/src/Models/Interpreter/Keywords.py @@ -0,0 +1,30 @@ +class Keywords: + # define keywords + Library: str = 'lib' + Class: str = 'class' + Function: str = 'func' + Variable: str = 'var' + Use: str = 'use' + From: str = 'from' + + # builtin functions + Output: str = 'output' + Input: str = 'input' + Length: str = 'length' + Range: str = 'range' + Exit: str = 'exit' + + # normal keywords + If: str = 'if' + ElseIf: str = 'elseif' + Else: str = 'else' + Pass: str = 'pass' + In: str = 'in' + Return: str = 'return' + + # loops + While: str = 'while' + For: str = 'for' + + # access + Public: str = 'public' diff --git a/src/Models/Interpreter/Token.py b/src/Models/Interpreter/Token.py index 855acf5..b91c4c1 100644 --- a/src/Models/Interpreter/Token.py +++ b/src/Models/Interpreter/Token.py @@ -1,5 +1,8 @@ +from Models.Interpreter import TokenTypes + + class Token: - def __init__(self, type: str, value: str) -> None: - self.type = type + def __init__(self, token_type: TokenTypes, value: str) -> None: + self.type: TokenTypes = token_type self.value = value diff --git a/src/Models/Interpreter/TokenTypes.py b/src/Models/Interpreter/TokenTypes.py new file mode 100644 index 0000000..182d4eb --- /dev/null +++ b/src/Models/Interpreter/TokenTypes.py @@ -0,0 +1,14 @@ +from enum import Enum + + +class TokenTypes(Enum): + Empty = 0 + Keyword = 1 + Type = 2 + Name = 3 + Bool = 4 + String = 5 + Number = 6 + Expression_Character = 7 + Bool_Expression_Character = 8 + Format_Character = 9 diff --git a/src/Models/Interpreter/Types.py b/src/Models/Interpreter/Types.py new file mode 100644 index 0000000..247de9f --- /dev/null +++ b/src/Models/Interpreter/Types.py @@ -0,0 +1,24 @@ +from enum import Enum + + +class Types(Enum): + + Any = 0 + Number = 1 + String = 2 + Bool = 3 + List = 4 + Dict = 5 + Empty = 6 + Void = 7 + + strings = [ + 'any', + 'number', + 'string', + 'bool', + 'list', + 'dict', + 'empty', + 'void' + ] diff --git a/src/Models/Interpreter/UnresolvedTokenTypes.py b/src/Models/Interpreter/UnresolvedTokenTypes.py new file mode 100644 index 0000000..6bc9d3a --- /dev/null +++ b/src/Models/Interpreter/UnresolvedTokenTypes.py @@ -0,0 +1,11 @@ +from enum import Enum + + +class UnresolvedTokenTypes(Enum): + Empty = 0 + Word = 1 + Number = 2 + String = 3 + Expression_Character = 4 + Bool_Expression_Character = 5 + Format_Character = 6 diff --git a/src/Models/Nodes/Class.py b/src/Models/Nodes/Class.py deleted file mode 100644 index 4ce7f94..0000000 --- a/src/Models/Nodes/Class.py +++ /dev/null @@ -1,6 +0,0 @@ -class Class: - - def __init__(self, name: str, access: str = '') -> None: - self.name = name - self.ast = [] # filled by parser - self.access = access diff --git a/src/Models/Nodes/Func.py b/src/Models/Nodes/Func.py deleted file mode 100644 index e2b0f28..0000000 --- a/src/Models/Nodes/Func.py +++ /dev/null @@ -1,7 +0,0 @@ -class Func: - - def __init__(self, name: str, return_type: str, access: str = '') -> None: - self.name = name - self.return_type = return_type - self.ast = [] # filled by parser - self.access = access diff --git a/src/Models/Nodes/Lib.py b/src/Models/Nodes/Lib.py deleted file mode 100644 index faf6d97..0000000 --- a/src/Models/Nodes/Lib.py +++ /dev/null @@ -1,5 +0,0 @@ -class Lib: - - def __init__(self, name: str) -> None: - self.name = name - self.ast = [] # filled by parser diff --git a/src/Models/Nodes/Var.py b/src/Models/Nodes/Var.py deleted file mode 100644 index b80c942..0000000 --- a/src/Models/Nodes/Var.py +++ /dev/null @@ -1,6 +0,0 @@ -class Var: - - def __init__(self, name: str, type: str, access: str = ''): - self.name = name - self.type = type - self.access = access