added structure & interpreter & lexer & first ast stucture

This commit is contained in:
edraft 2020-05-22 22:08:37 +02:00
commit ed97118df0
22 changed files with 481 additions and 0 deletions

29
doc/definition.txt Normal file
View File

@ -0,0 +1,29 @@
keywords:
builtin-functions:
output
input
range
length
pass
if
elseif
else
global vars:
error:
code
msg
sys:
os_name
data types:
empty
any
string
number
bool
list
dict

13
doc/error_codes.txt Normal file
View File

@ -0,0 +1,13 @@
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

16
doc/target/main.bl Normal file
View File

@ -0,0 +1,16 @@
use test1 from Tests;
use test2 as test3 from Tests;
lib Main {
class Program {
func Main(args: list): void {
test_a = test1();
test_a.dec_vars();
test_a.is_error();
if (!error) {
test_b = test3();
test3.continue();
}
}
}
}

33
doc/target/test.bl Normal file
View File

@ -0,0 +1,33 @@
lib Tests
{
/*
declaration of some tests
*/
export class test1
{
export test_string: string = 'Hello';
export test_string_2: string = "Hello World";
export test_num: num = 1;
export test_num_2: num = 1.0;
export test_num_3: num = this.test_num + this.test_num_2;
export func dec_vars(): void
{
test_bool: bool = true;
test_bool_2: bool = false;
test_bool_3: bool = test_bool != test_bool_2; # true
}
export is_error(): bool
{
if (error != empty)
{
output(error.code + ' ' + error.message);
}
else
{
output('continue');
}
}
}
}

12
doc/target/test2.bl Normal file
View File

@ -0,0 +1,12 @@
lib Tests {
export class test2 {
string_a = string1();
export func continue() {
input(string_a.string1 + ': ');
}
}
class strings {
public string1 = "hello world";
}
}

31
doc/test.bl Normal file
View File

@ -0,0 +1,31 @@
// hi1
# hi2
/*
hi3
*/
lib Main {
class Program {
func Main() {
testBool: bool;
testEmpty: emptyType = empty;
output('Hello World');
output(66);
output(3 + 3);
test: string = input('# ');
output(test);
output(false);
if (testBool != empty) {
output(testEmpty);
}
test1234(range(0, 10));
}
public func test1234(param: list) {
for i in range(0, length(param)) {
output(i);
}
}
}
}

View File

@ -0,0 +1,28 @@
from Interpreter.Validator import Validator
from Interpreter.Lexer import Lexer
from Interpreter.Parser import Parser
from Interpreter.Repo import Repo
from Interpreter.Utils import Utils
class Interpreter:
def __init__(self, repo: Repo, utils: Utils) -> None:
self.__repo = repo
self.__utils = utils
self.__lexer = Lexer(repo, utils)
self.__parser = Parser(repo, utils)
self.__validator = Validator(repo, utils)
def interpret(self, line: str) -> bool:
toks = []
if self.__repo.error is None:
toks = self.__lexer.tokenize(line)
if self.__repo.error is None:
self.__parser.parse(toks)
if self.__repo.error is None:
self.__validator.validate()
return self.__repo.error is None

139
src/Interpreter/Lexer.py Normal file
View File

@ -0,0 +1,139 @@
from Interpreter.Repo import Repo
from Interpreter.Utils import Utils
from Models.Token import Token
class Lexer:
def __init__(self, repo: Repo, utils: Utils) -> None:
self.__repo = repo
self.__utils = utils
self.__ml_comment = False
self.__toks = []
def __add_tok(self, value: str, type: str) -> None:
if value != '':
if type == 'word':
if value in self.__repo.keywords:
type = 'keyword'
elif value in self.__repo.types:
type = 'type'
elif value in self.__repo.bool_values:
type = 'bool'
elif value == 'empty':
type = 'emptyType'
else:
type = 'name'
self.__toks.append(Token(type, value))
def tokenize(self, line: str) -> list:
self.__toks = []
word = ''
ol_comment = False
is_string1 = False # 'hello'
is_string2 = False # "hello"
is_number = False
is_expr_char = False
for i in range(0, len(line)):
c = line[i]
# ignore comments and spaces
if not ol_comment and not self.__ml_comment:
# end of number
if not c.isdigit() and is_number:
self.__add_tok(word, 'number')
word = ''
is_number = False
# end of expression char
if c not in self.__repo.expr_chars and is_expr_char:
self.__add_tok(word, 'expr_char')
word = ''
is_expr_char = False
# comment filtering
if c == '#' and not is_string1 and not is_string2:
ol_comment = True
elif c == '/' and line[+1] == '/':
ol_comment = True
elif c == '/' and line[+1] == '*':
self.__ml_comment = True
i += 2
# begin of is_string1
elif c == '\'' and not is_string1:
is_string1 = True
word = ''
# end of is_string1
elif c == '\'' and is_string1:
is_string1 = False
self.__add_tok(word, 'string')
word = ''
# begin of is_string2
elif c == '\"' and not is_string2:
is_string2 = True
word = ''
# end of is_string2
elif c == '\"' and is_string2:
is_string2 = False
self.__add_tok(word, 'string')
word = ''
# format char
elif c in self.__repo.format_chars:
self.__add_tok(word, 'word')
self.__add_tok(c, 'format_char')
word = ''
# begin of number
elif c.isdigit() and not is_number and word == '':
word += c
is_number = True
# continue number
elif c.isdigit() and is_number:
word += c
# begin expression char
elif c in self.__repo.expr_chars and not is_expr_char:
word += c
is_expr_char = True
# continue expression char
elif c in self.__repo.expr_chars and is_expr_char:
word += c
# bool expression char
elif c in self.__repo.expr_chars:
self.__add_tok(word, 'word')
self.__add_tok(c, 'bool_expr_char')
word = ''
# end of word
elif c == ' ' and not is_string1 and not is_string2 or c == '\n':
self.__add_tok(word, 'word')
word = ''
else:
word += c
if c == '\n' and ol_comment:
ol_comment = False
if c == '*' and line[i + 1] == '/':
self.__ml_comment = False
# self.__repo.output_tokens(self.__toks)
return self.__toks

12
src/Interpreter/Parser.py Normal file
View File

@ -0,0 +1,12 @@
from Interpreter.Repo import Repo
from Interpreter.Utils import Utils
class Parser:
def __init__(self, repo: Repo, utils: Utils) -> None:
self.__repo = repo
self.__utils = utils
def parse(self, toks: list) -> None:
self.__repo.output_tokens(toks)

54
src/Interpreter/Repo.py Normal file
View File

@ -0,0 +1,54 @@
class Repo:
def __init__(self) -> None:
self.debug = True
# interpreter
self.keywords = [
# define keys
'lib',
'class',
'func',
# builtin functions
'output',
'input',
'length',
'range',
# normal keywords
'if',
'elseif',
'else',
'pass',
'in',
# loops
'while',
'for',
# access
'public'
]
self.types = [
'number',
'string',
'bool',
'list',
'dict',
'emptyType',
'void'
]
self.expr_chars = ['+', '-', '*', '/', '=']
self.bool_expr_chars = ['<', '>', '!', '!=', '==', '>=', '<=']
self.bool_values = ['true', 'false']
self.format_chars = ['{', '}', '(', ')', ';', ':', ',']
# runtime
self.error = None
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')

19
src/Interpreter/Utils.py Normal file
View File

@ -0,0 +1,19 @@
from termcolor import colored
from Interpreter.Repo import Repo
class Utils:
def __init__(self, repo: Repo) -> None:
self.__repo = repo
def input(self, prefix: str) -> str:
return input(prefix)
def output(self, text: str) -> None:
print(f'-> {text}')
def error(self) -> None:
if self.__repo is not None:
print(colored(f'{self.__repo.error.code}: {self.__repo.error.msg}', 'red'))

View File

@ -0,0 +1,12 @@
from Interpreter.Repo import Repo
from Interpreter.Utils import Utils
class Validator:
def __init__(self, repo: Repo, utils: Utils) -> None:
self.__repo = repo
self.__utils = utils
def validate(self) -> None:
pass

View File

38
src/Main.py Normal file
View File

@ -0,0 +1,38 @@
import os
import sys
from Models.Error import Error
from ServiceInitializer import ServiceInitializer
class Main:
def __init__(self) -> None:
self.__services = ServiceInitializer()
self.__utils = self.__services.utils
self.__repo = self.__services.repo
self.__interpreter = self.__services.interpreter
def console(self) -> None:
print('sh-edraft.de basic language interpreter:')
cont = True
while cont:
cont = self.__interpreter.interpret(input('> '))
def files(self, file: str) -> None:
if os.path.isfile(file):
f = open(file, 'r', encoding='utf-8').readlines()
for line in f:
self.__interpreter.interpret(line)
else:
self.__repo.error = Error(1.1, 'File not found')
self.__utils.error()
if __name__ == '__main__':
main = Main()
print(sys.argv)
if len(sys.argv) == 2:
main.files(sys.argv[1])
else:
main.console()

6
src/Models/Class.py Normal file
View File

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

5
src/Models/Error.py Normal file
View File

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

6
src/Models/Func.py Normal file
View File

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

5
src/Models/Lib.py Normal file
View File

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

5
src/Models/Token.py Normal file
View File

@ -0,0 +1,5 @@
class Token:
def __init__(self, type: str, value: str) -> None:
self.type = type
self.value = value

6
src/Models/Var.py Normal file
View File

@ -0,0 +1,6 @@
class Var:
def __init__(self, name: str, value: str, type: str) -> None:
self.name = name
self.value = value
self.type = type

0
src/Models/__init__.py Normal file
View File

12
src/ServiceInitializer.py Normal file
View File

@ -0,0 +1,12 @@
from Interpreter.Validator import Validator
from Interpreter.Interpreter import Interpreter
from Interpreter.Utils import Utils
from Interpreter.Repo import Repo
class ServiceInitializer:
def __init__(self) -> None:
self.repo = Repo()
self.utils = Utils(self.repo)
self.interpreter = Interpreter(self.repo, self.utils)