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