added structure & interpreter & lexer & first ast stucture
This commit is contained in:
commit
ed97118df0
29
doc/definition.txt
Normal file
29
doc/definition.txt
Normal 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
13
doc/error_codes.txt
Normal 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
16
doc/target/main.bl
Normal 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
33
doc/target/test.bl
Normal 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
12
doc/target/test2.bl
Normal 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
31
doc/test.bl
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
src/Interpreter/Interpreter.py
Normal file
28
src/Interpreter/Interpreter.py
Normal 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
139
src/Interpreter/Lexer.py
Normal 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
12
src/Interpreter/Parser.py
Normal 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
54
src/Interpreter/Repo.py
Normal 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
19
src/Interpreter/Utils.py
Normal 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'))
|
12
src/Interpreter/Validator.py
Normal file
12
src/Interpreter/Validator.py
Normal 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
|
0
src/Interpreter/__init__.py
Normal file
0
src/Interpreter/__init__.py
Normal file
38
src/Main.py
Normal file
38
src/Main.py
Normal 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
6
src/Models/Class.py
Normal 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
5
src/Models/Error.py
Normal 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
6
src/Models/Func.py
Normal 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
5
src/Models/Lib.py
Normal 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
5
src/Models/Token.py
Normal 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
6
src/Models/Var.py
Normal 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
0
src/Models/__init__.py
Normal file
12
src/ServiceInitializer.py
Normal file
12
src/ServiceInitializer.py
Normal 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)
|
Loading…
Reference in New Issue
Block a user