added parser logic

This commit is contained in:
edraft 2020-05-24 17:56:15 +02:00
parent d66ea29d8f
commit 38353246b2
14 changed files with 260 additions and 29 deletions

9
doc/ast_rules.txt Normal file
View File

@ -0,0 +1,9 @@
len 3:
lib main {
min len 3, max len 4:
<public> class test {
min len 5
<public> func test() {

View File

@ -1,13 +0,0 @@
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
2.6 Access error: no export
2.7 Expression error

View File

@ -9,6 +9,8 @@ lib Main {
func Main() { func Main() {
testBool: bool; testBool: bool;
testEmpty: emptyType = empty; testEmpty: emptyType = empty;
testNum: number = 3.0;
testBool_2: bool = 3 > 1;
output('Hello World'); output('Hello World');
output(66); output(66);
output(3 + 3); output(3 + 3);
@ -22,7 +24,9 @@ lib Main {
test1234(range(0, 10)); test1234(range(0, 10));
} }
public func test1234(param: list) { # public func test1234(param: list) {
public func test1234()
{
/*for i in range(0, length(param)) { /*for i in range(0, length(param)) {
output(i); output(i);
}*/ }*/
@ -30,3 +34,11 @@ lib Main {
} }
} }
} }
lib test
{
public class Test
{
}
}

View File

@ -57,7 +57,7 @@ class Lexer:
i += 2 i += 2
# end of number # end of number
elif not c.isdigit() and is_number: elif not c.isdigit() and c != '.' and is_number:
self.__add_tok(word, 'number') self.__add_tok(word, 'number')
word = '' word = ''
is_number = False is_number = False
@ -102,7 +102,7 @@ class Lexer:
is_number = True is_number = True
# continue number # continue number
elif c.isdigit() and is_number: elif (c.isdigit() or c == '.') and is_number:
word += c word += c
# begin expression char # begin expression char
@ -115,7 +115,7 @@ class Lexer:
word += c word += c
# bool expression char # bool expression char
elif c in self.__repo.expr_chars: elif c in self.__repo.bool_expr_chars:
self.__add_tok(word, 'word') self.__add_tok(word, 'word')
self.__add_tok(c, 'bool_expr_char') self.__add_tok(c, 'bool_expr_char')
word = '' word = ''
@ -134,5 +134,5 @@ class Lexer:
if line[i - 1] == '*' and c == '/': if line[i - 1] == '*' and c == '/':
self.__ml_comment = False self.__ml_comment = False
self.__repo.output_tokens(self.__toks) # self.__repo.output_tokens(self.__toks)
return self.__toks return self.__toks

View File

@ -1,6 +1,10 @@
from Interpreter.Repo import Repo from Interpreter.Repo import Repo
from Interpreter.Utils import Utils from Interpreter.Utils import Utils
from Models.Interpreter.Error import Error
from Models.Interpreter.Token import Token from Models.Interpreter.Token import Token
from Models.Language.AST.Class import Class
from Models.Language.AST.Func import Func
from Models.Language.AST.Lib import Lib
class Parser: class Parser:
@ -9,6 +13,178 @@ class Parser:
self.__repo = repo self.__repo = repo
self.__utils = utils self.__utils = utils
# runtime representation
self.__lib = None
self.__class = None
self.__func = None
# helpers
self.__tokens = [] # reset each line
self.__i = 0 # for loop index
self.__token_storage = []
self.__is_start_lib = False
self.__is_start_class = False
self.__is_start_func = False
self.__is_public = False
def parse(self, toks: list) -> None: def parse(self, toks: list) -> None:
self.__tokens = toks
# self.__repo.output_tokens(toks) # self.__repo.output_tokens(toks)
# output
if len(toks) > 0:
tokens = []
for t in toks:
tokens.append({t.value: t.type})
# print(tokens)
self.__check()
# 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.access)
print('___')
# print(self.__repo.ast, '\n')
""" parser helpers """
def __get_next_token(self) -> Token:
if len(self.__tokens) > self.__i + 1:
return self.__tokens[self.__i + 1]
else:
return Token('EOL', 'EOL')
def __get_last_token(self) -> Token:
if len(self.__tokens) >= self.__i - 1:
return self.__tokens[self.__i - 1]
else:
return Token('EOL', 'EOL')
def __get_token_by_i_dif(self, delta: int) -> Token:
return self.__tokens[self.__i + delta]
def __is_scope_started(self) -> bool:
return self.__is_start_lib or self.__is_start_class or self.__is_start_func
def __is_only_func_started(self) -> bool:
return not self.__is_start_lib and not self.__is_start_class and self.__is_start_func
""" token checks """
def __check(self):
# check tokens
for self.__i in range(0, len(self.__tokens)):
tok = self.__tokens[self.__i]
if not self.__is_scope_started() and tok.type == 'keyword':
self.__check_keyword(tok)
elif not self.__is_scope_started() and tok.type == 'type' or tok.type in self.__repo.types:
pass pass
elif not self.__is_scope_started() and tok.type in self.__repo.var_types:
pass
elif tok.type == 'format_char':
self.__check_format_char(tok)
elif not self.__is_scope_started() and tok.type == 'expr_char':
pass
elif not self.__is_scope_started() and tok.type == 'bool_expr_char':
self.__check_name(tok)
elif tok.type == 'name':
self.__check_name(tok)
else:
self.__utils.error(Error(2.9, f'{tok.type}: {tok.value}'))
def __check_keyword(self, tok: Token) -> None:
if tok.value == 'lib':
self.__is_start_lib = True
elif tok.value == 'class':
self.__is_start_class = True
elif tok.value == 'func':
self.__is_start_func = True
elif tok.value == 'public':
next = self.__get_next_token()
if next.type == 'keyword' and (next.value == 'class' or next.value == 'func'):
self.__is_public = True
else:
if next.type == 'EOL':
self.__utils.error(Error(2.8))
else:
self.__utils.error(Error(2.9, next.value))
def __check_format_char(self, tok: Token) -> None:
if tok.value == '{':
if len(self.__token_storage) > 0 and self.__token_storage[0].type == 'name':
if self.__is_start_lib and not self.__is_start_class and not self.__is_start_func:
self.__lib = Lib(self.__token_storage[0].value)
self.__token_storage = []
self.__is_start_lib = False
elif not self.__is_start_lib and self.__is_start_class and not self.__is_start_func:
access = ''
if self.__is_public:
access = 'public'
self.__is_public = False
self.__class = Class(self.__token_storage[0].value, access=access)
self.__token_storage = []
self.__is_start_class = False
elif not self.__is_start_lib and not self.__is_start_class and self.__is_start_func:
access = ''
if self.__is_public:
access = 'public'
self.__is_public = False
self.__func = Func(self.__token_storage[0].value, access=access)
self.__token_storage = []
self.__is_start_func = False
else:
self.__utils.error(Error(2.9, f'{tok.type}: {tok.value}'))
elif not self.__is_scope_started() and tok.value == '}':
if self.__lib is not None and self.__class is None and self.__func is None:
self.__repo.ast.append(self.__lib)
self.__lib = None
elif self.__lib is not None and self.__class is not None and self.__func is None:
self.__lib.ast.append(self.__class)
self.__class = None
elif self.__lib is not None and self.__class is not None and self.__func is not None:
self.__class.ast.append(self.__func)
self.__func = None
else:
self.__utils.error(Error(2.9, f'{tok.type}: {tok.value}'))
elif self.__is_only_func_started() and (tok.value == '(' or tok.value == ')'):
pass
elif not self.__is_scope_started() and tok.value in self.__repo.format_chars:
pass
else:
self.__utils.error(Error(2.9, f'{tok.type}: {tok.value}'))
def __check_name(self, tok: Token) -> None:
if self.__is_start_lib or self.__is_start_class or self.__is_start_func:
if len(self.__token_storage) == 0:
self.__token_storage.append(tok)
else:
self.__utils.error(Error(2.9, f'{tok.type}: {tok.value}'))

View File

@ -44,13 +44,14 @@ class Repo:
'list', 'list',
'dict' 'dict'
] ]
self.format_chars = ['{', '}', '(', ')', ';', ':', ',']
self.expr_chars = ['+', '-', '*', '/', '=', '^'] self.expr_chars = ['+', '-', '*', '/', '=', '^']
self.bool_expr_chars = ['<', '>', '!', '!=', '==', '>=', '<='] self.bool_expr_chars = ['<', '>', '!', '!=', '==', '>=', '<=']
self.bool_values = ['true', 'false'] self.bool_values = ['true', 'false']
self.format_chars = ['{', '}', '(', ')', ';', ':', ',']
# runtime # runtime
self.error = None self.error = None
self.ast = []
def output_tokens(self, toks: list) -> None: def output_tokens(self, toks: list) -> None:
if self.debug and len(toks) > 0: if self.debug and len(toks) > 0:

View File

@ -1,6 +1,7 @@
from termcolor import colored from termcolor import colored
from Interpreter.Repo import Repo from Interpreter.Repo import Repo
from Models.Interpreter.Error import Error
class Utils: class Utils:
@ -14,6 +15,6 @@ class Utils:
def output(self, text: str) -> None: def output(self, text: str) -> None:
print(f'-> {text}') print(f'-> {text}')
def error(self) -> None: def error(self, error: Error) -> None:
if self.__repo is not None: self.__repo.error = error
print(colored(f'{self.__repo.error.code}: {self.__repo.error.msg}', 'red')) print(colored(f'{self.__repo.error.code}: {self.__repo.error.msg}', 'red'))

View File

@ -1,7 +1,7 @@
import os import os
import sys import sys
from Models.Language.Error import Error from Models.Interpreter.Error import Error
from ServiceInitializer import ServiceInitializer from ServiceInitializer import ServiceInitializer

View File

@ -0,0 +1,33 @@
class Error:
def __init__(self, code: float, 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',
2.6: 'Access error: no export',
2.7: 'Expression error',
2.8: 'Unexpected end of line',
2.9: 'Unexpected {}', # other types
# 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',
}
self.msg = self.__msgs[code].format(msg)

View File

@ -0,0 +1,6 @@
class Class:
def __init__(self, name: str, access: str = '') -> None:
self.name = name
self.ast = []
self.access = access

View File

@ -0,0 +1,6 @@
class Func:
def __init__(self, name: str, access: str = '') -> None:
self.name = name
self.ast = []
self.access = access

View File

@ -0,0 +1,5 @@
class Lib:
def __init__(self, name: str) -> None:
self.name = name
self.ast = []

View File

View File

@ -1,5 +0,0 @@
class Error:
def __init__(self, code: float, msg: str):
self.code = code
self.msg = msg