Reworked Interpreter WIP

This commit is contained in:
weckyy702
2020-12-30 19:24:14 +01:00
parent d0746c0465
commit 248ee14abd
4 changed files with 286 additions and 189 deletions

2
.vscode/launch.json vendored
View File

@@ -8,7 +8,7 @@
"name": "Python: Aktuelle Datei",
"type": "python",
"request": "launch",
"program": "run.py",
"program": "debug.py",
"console": "integratedTerminal"
}
]

View File

@@ -6,7 +6,7 @@ import os.path
import secrets
from draw.Iinstruction import Iinstruction
from interpreter import interpret_source as itp
from interpreter.interpret_source import JavaInterpreter
from draw.code_to_image_wrapper import NSD_writer
import draw.code_to_image as cti
@@ -70,4 +70,5 @@ class NassiShneidermanDiagram:
raise
def load_from_file(self, filepath:str):
self.function_scopes = itp.load_scoped_instructions(filepath)
itp = JavaInterpreter(filepath)
self.function_scopes = itp.load_instruction_scopes()

View File

@@ -1,8 +1,6 @@
import logging
from os import remove
import re
from re import split
from typing import List, Match, Tuple
from typing import Dict, List, Match, Tuple
from errors.custom import InterpreterException, JavaSyntaxError, ScopeNotFoundException
from draw.Iinstruction import *
@@ -28,215 +26,281 @@ class Function_scope(Iterable):
def __iter__(self):
return self.contents.__iter__()
COMMENT_PATTERN = re.compile(r"""^(//|--|#).*""")
REMOVE_KEYWORDS = [' ', ';', "public", "private", "final", "protected"]
VARIABLE_TAGS = ["byte", "short", "int", "long", "float", "double", "boolean", "char", "String"]
FUNCTION_IDENTIFIERS = ["void"]
FUNCTION_IDENTIFIERS.extend(VARIABLE_TAGS)
class JavaInterpreter:
WHILE_TAG = "solange " #german for 'while'. Change this depending on your language
def __init__(self, filepath: str) -> None:
with open(filepath) as file:
if not file.readable():
raise InterpreterException(f"Unable to read input file {filepath}")
self.src = file.read()
self.lines = [] # to be filled in later
self._init_regex()
self._recompile_regex()
REPLACE = dict((re.escape(k), '') for k in REMOVE_KEYWORDS)
remove_pattern = re.compile("|".join(REPLACE.keys()), flags=re.MULTILINE)
def reset_tags(self, additional_tags: Dict[str, List[str]]=None):
"""Reset the Interpreters internal tags to include the new tags given in {additional_tags}
the key for comment tags is "comments"
the key for tags that should be ignored is "ignore"
the key for additional type tags is "types"
"""
variable_regex = "^("
for kw in VARIABLE_TAGS:
variable_regex += fr"""{kw}|"""
variable_pattern = re.compile(variable_regex[0:-1]+")(.*)=?(.*)", flags=re.MULTILINE)
if additional_tags:
if "comments" in additional_tags.keys():
self.comment_tags.extend(additional_tags["comments"])
if "ignore" in additional_tags.keys():
self.remove_tags.extend(additional_tags["ignore"])
if "types" in additional_tags.keys():
self.type_tags.extend(additional_tags["types"])
self.function_tags.extend(additional_tags["types"])
else:
self._init_regex()
self._recompile_regex()
function_regex = "^("
for kw in FUNCTION_IDENTIFIERS:
function_regex += fr"""{kw}|"""
function_regex = function_regex[0:-1]+ ").*([(].*[)].*)"
def _init_regex(self):
"""Initialize all tag lists to their default state"""
function_pattern = re.compile(function_regex)
self.comment_tags = ["//", "#", "--"] #dont ask why a java interpreter supports lua and python style comments
self.remove_tags = ['\n', "public", "private", "protected", "final"]
self.type_tags = ["byte", "short", "int", "long", "float", "double", "boolean", "char", "String"]
self.function_tags = ["void"]
self.function_tags.extend(self.type_tags)
def replace_all_tags(org: str):
return remove_pattern.sub(lambda m: REPLACE[re.escape(m.group(0))], org)
def _recompile_regex(self):
"""recompile all regex's using the new tag lists"""
comment_regex = r"""^("""
for tag in self.comment_tags:
comment_regex += fr"""{re.escape(tag)}|"""
self.comment_pattern = re.compile(comment_regex[:-1]+").*", flags=re.MULTILINE)
def load_src(filepath: str) -> List[str]:
lines: List[str] = []
brace_open_count, brace_closed_count = 0,0
try:
with open(filepath, encoding="utf-8") as file:
for _line in file:
line = replace_all_tags(_line.strip())
if line and not COMMENT_PATTERN.match(line):
lines.append(line)
if '{' in line:
brace_open_count += 1
if '}' in line:
brace_closed_count += 1
except:
raise FileNotFoundError(f"File {filepath} was not found!")
remove_regex = r"""^("""
for tag in self.remove_tags:
remove_regex += fr"""{re.escape(tag)}|"""
self.remove_pattern = re.compile(remove_regex[:-1]+')', flags=re.MULTILINE)
if brace_open_count != brace_closed_count:
raise JavaSyntaxError("Number of opened braces does not match number of closed ones. Program is ill-formed!")
variable_regex = r"""^("""
for tag in self.type_tags:
variable_regex += fr"""{re.escape(tag)}|"""
self.variable_pattern = re.compile(variable_regex[:-1]+")(.*=|.*;)(.*)", flags=re.MULTILINE)
function_regex = r"""^(?P<return_type>"""
for tag in self.function_tags:
function_regex += fr"""{re.escape(tag)}|"""
self.function_pattern = re.compile(function_regex[:-1]+")(?P<name>.*)[(](?P<args>.*)[)][^;]", flags=re.MULTILINE)
def _check_src(self, idx: int, tag: str) -> bool:
if idx >= len(self.lines):
return False
return tag in self.lines[idx]
def _check_line_start(self, idx: int, tag: str) -> bool:
if idx >= len(self.lines):
return False
return self.lines[idx].startswith(tag)
def _get_scope_start_offset(self, i: int) -> int:
if self._check_src(i, "{"):
return 1
elif self._check_src(i+1, "{"):
return 2
raise ScopeNotFoundException("Unable to find scope start. Is the program ill-formed?")
def _handle_while(self, line: str, idx: int):
bracket_idx = line.rindex(')') # throws if while contruct is illformed
instruction_txt = line[6:bracket_idx]
brace_offset = self._get_scope_start_offset(idx)
child_instructions, idx = self._get_instructions_in_scope(idx+brace_offset)
return while_instruction_front(("while" + instruction_txt), child_instructions), idx
return lines
def _handle_else(self, idx: int):
brace_offset = self._get_scope_start_offset(idx)
return self._get_instructions_in_scope(idx+brace_offset)
def check_src(src: List[str], line_index: int, tag: str) -> bool:
if line_index >= len(src):
return False
return tag in src[line_index]
def _get_else_scope(self, idx:int):
instructions = None
if self._check_line_start(idx, "}else"):
logging.debug("found else construct in line: %i", idx+1)
instructions, idx = self._handle_else(idx)
elif self._check_line_start(idx+1, "else"):
logging.debug("found else construct in line: %i", idx+2)
instructions, idx = self._handle_else(idx+1)
return instructions
def check_line_start(src: List[str], line_index: int, tag: str) -> bool:
if line_index >= len(src):
return False
return src[line_index].startswith(tag)
def _handle_if(self, line: str, idx: int):
bracket_idx = line.rindex(')') # throws if the contruct is illformed
instruction_txt = line[3:bracket_idx]
def get_next_occurence_of(src: List[str], start_idx:int, tag:str) -> int:
"""Returns the index of the next occurence of tag in src from start_idx"""
i = start_idx
while i < len(src):
if check_src(src, i, tag):
break
i += 1
return i
brace_offset = self._get_scope_start_offset(idx)
true_instructions, idx = self._get_instructions_in_scope(idx+brace_offset)
def get_scope_start_offset(src: List[str], i: int) -> int:
if check_src(src, i, "{"):
return 1
elif check_src(src, i+1, "{"):
return 2
raise ScopeNotFoundException("Unable to find scope start. Is the program ill-formed?")
false_instructions = self._get_else_scope(idx)
return if_instruction(instruction_txt, true_instructions, false_instructions), idx
def _handle_do_while(self, line: str, idx: int):
brace_offset = self._get_scope_start_offset(idx)
child_instructions, idx = self._get_instructions_in_scope(idx+brace_offset)
def handle_while(line: str, src: List[str], i: int) -> Tuple[Iinstruction, int]:
bracket_idx = line.rindex(')') # throws if while contruct is illformed
instruction_txt = None
if self._check_line_start(idx, "}while("):
end_line = self.lines[idx]
bracket_index = end_line.rindex(')') #throws if contruct id ill-formed
instruction_txt = end_line[7:bracket_index]
elif self._check_line_start(idx+1, "while("):
idx += 1
end_line = self.lines[idx]
bracket_index = end_line.rindex(')')
instruction_txt = end_line[6:bracket_index]
else:
raise JavaSyntaxError("Ill-formed do-while construct!")
instruction_txt = line[6:bracket_idx]
brace_offset = get_scope_start_offset(src, i)
child_instructions, i = get_instructions_in_scope(src, i+brace_offset)
return while_instruction_front((WHILE_TAG + instruction_txt), child_instructions), i
return while_instruction_back(instruction_txt, child_instructions), idx
def handle_else(src: List[str], i: int) -> Tuple[List[Iinstruction], int]:
brace_offset = get_scope_start_offset(src, i)
return get_instructions_in_scope(src, i+brace_offset)
def _handle_variable(self, line: str, idx: int):
groups = self.variable_pattern.match(line).groups()
var_type = groups[0]
var_name = groups[1][:-1]
var_value = groups[2]
if var_value == '':
return generic_instruction(f"declare variable '{var_name}' of type {var_type}"), idx
return generic_instruction(f"declare variable '{var_name}' of type {var_type} with value {var_value}"), idx
def handle_if(line: str, src: List[str], i: int) -> Tuple[Iinstruction, int]:
bracket_idx = line.rindex(')') # throws if the contruct is illformed
instruction_txt = line[3:bracket_idx]
def _handle_instruction(self, line: str, idx:int) -> Tuple[Iinstruction, int]:
if line.startswith("while("):
logging.debug("Found while construct in line: %i", idx+1)
return self._handle_while(line, idx)
elif line.startswith("if("):
logging.debug("Found if construct in line: %i", idx+1)
return self._handle_if(line, idx)
brace_offset = get_scope_start_offset(src, i)
true_instructions, i = get_instructions_in_scope(src, i+brace_offset)
elif line.startswith("do"):
logging.debug("Found do-while construct in line: %i", idx+1)
return self._handle_do_while(line, idx)
false_instructions = None
elif self.variable_pattern.match(line):
logging.debug("Found variable in line %i", idx+1)
return self._handle_variable(line, idx)
#check for else statements
if check_line_start(src, i, "}else"):
logging.debug("found else construct in line: %i", i+1)
false_instructions, i = handle_else(src, i)
elif check_line_start(src, i+1, "else"):
logging.debug("found else construct in line: %i", i+2)
false_instructions, i = handle_else(src, i+1)
return if_instruction(instruction_txt, true_instructions, false_instructions), i
else:
logging.debug("Found generic instruction in line %i", idx+1)
return generic_instruction(line), idx
def handle_do_while(line: str, src: List[str], i: int) -> Tuple[Iinstruction, int]:
brace_offset = get_scope_start_offset(src, i)
child_instructions, i = get_instructions_in_scope(src, i+brace_offset)
instruction_txt = None
if check_line_start(src, i, "}while("):
end_line = src[i]
bracket_index = end_line.rindex(')') #throws if contruct id ill-formed
instruction_txt = end_line[7:bracket_index]
elif check_line_start(src, i+1, "while("):
i += 1
end_line = src[i]
bracket_index = end_line.rindex(')')
instruction_txt = end_line[6:bracket_index]
else:
raise JavaSyntaxError("Ill-formed do-while construct!")
return while_instruction_back(instruction_txt, child_instructions), i
def handle_variable(line:str, src: List[str], i: int) -> Tuple[Iinstruction, int]:
groups = variable_pattern.match(line).groups()
var_type = groups[0]
var_name = groups[1][:-1]
var_value = groups[2]
if var_value == "":
return generic_instruction(f"declare variable '{var_name}' of type {var_type}"), i
return generic_instruction(f"declare variable '{var_name}' of type {var_type} with value {var_value}"), i
def handle_instruction(line: str, src: List[str], i: int) -> Tuple[Iinstruction, int]:
if line.startswith("while("):
logging.debug("Found while construct in line: %i", i+1)
return handle_while(line, src, i)
elif line.startswith("if("):
logging.debug("Found if construct in line: %i", i+1)
return handle_if(line, src, i)
elif line.startswith("do"):
logging.debug("Found do-while construct in line: %i", i+1)
return handle_do_while(line, src, i)
elif variable_pattern.match(line):
logging.debug("Found variable in line %i", i+1)
print("found variable in line ", i+1)
return handle_variable(line, src, i)
else:
logging.debug("found generic instruction in line %i", i+1)
return generic_instruction(line), i
def get_instructions_in_scope(src: List[str], start_idx: int = 0) -> Tuple[List[Iinstruction], int]:
outer_scope: List[Iinstruction] = []
i = start_idx
while i < len(src):
line = src[i]
try:
if check_src(src, i, '}'): #We exited this scope, return it
def _get_instructions_in_scope(self, idx: int=0) -> Tuple[List[Iinstruction], int]:
scope: List[Iinstruction] = []
i = idx
while i < len(self.lines):
line = self.lines[i]
if self._check_src(i, '}'):
break
instruction, i = handle_instruction(line, src, i)
outer_scope.append(instruction)
except Exception as e:
logging.error("Encountered error in line %i: %s", i+1, str(e))
raise
except:
logging.fatal("Encountered unexpected error in line: %i", i+1)
raise
i += 1
instruction, i = self._handle_instruction(line, i)
scope.append(instruction)
i += 1
return scope, i
return outer_scope, i
def _remove_keywords(self):
self.src = self.src.replace(' ', '')
self.src = self.comment_pattern.sub('', self.src)
self.src = self.remove_pattern.sub('', self.src)
self.lines = self.src.splitlines(True)
def get_function_info(match: Match[str], line: str) -> Tuple[str, str, str]:
groups = match.groups()
ftype = groups[0]
fargs = groups[1]
fname = line.removeprefix(ftype).removesuffix(fargs) #remove return type and argument list to get the function name
return ftype, fname, fargs
def _get_function_info(self, match: Match[str]) -> Tuple[str, str, List[str]]:
groups = match.groupdict()
ftype = groups["return_type"]
fargs = groups["args"]
fname = groups["name"]
return ftype, fname, fargs.split(',')
def get_function_scopes(src: List[str]) -> List[Function_scope]:
functions = []
def _get_function_instructions(self, function_header: str) -> List[Iinstruction]:
idx = self.lines.index(function_header)
brace_offset = self._get_scope_start_offset(idx)
return self._get_instructions_in_scope(idx+brace_offset)[0]
i = 0
while i < len(src):
line = src[i]
if match:=function_pattern.match(line):
function_return_type, function_name, function_args = get_function_info(match, line)
def _get_function_scopes(self) -> List[Function_scope]:
scopes = []
for match in self.function_pattern.finditer(self.src):
span = match.span()
header = self.src[span[0]:span[1]]
brace_offset = get_scope_start_offset(src, i)
child_instructions, i = get_instructions_in_scope(src, i+brace_offset)
functions.append(Function_scope(child_instructions, function_name, function_return_type, function_args.split(',')))
i+=1
return functions
rtype, name, args = self._get_function_info(match)
def remove_keywords(src: str) -> str:
without_comments = ""
for line in src.splitlines(True):
if line != "\n" and not COMMENT_PATTERN.match(line):
without_comments += line
return remove_pattern.sub('', without_comments)
child_instructions = self._get_function_instructions(header)
def load_scoped_instructions(filepath: str) -> List[Function_scope]:
src = load_src(filepath)
return get_function_scopes(src)
scopes.append(Function_scope(child_instructions, name, rtype, args))
return scopes
def load_instruction_scopes(self):
self._remove_keywords()
return self._get_function_scopes()
# COMMENT_PATTERN = re.compile(r"""^(//|#|--)""")
# REMOVE_KEYWORDS = [' ', "public", "private", "final", "protected"]
# VARIABLE_TAGS = ["byte", "short", "int", "long", "float", "double", "boolean", "char", "String"]
# FUNCTION_IDENTIFIERS = ["void"]
# FUNCTION_IDENTIFIERS.extend(VARIABLE_TAGS)
# WHILE_TAG = "solange " #german for 'while'. Change this depending on your language
# REPLACE = dict((re.escape(k), '') for k in REMOVE_KEYWORDS)
# remove_pattern = re.compile("|".join(REPLACE.keys()), flags=re.MULTILINE)
# variable_regex = "^("
# for kw in VARIABLE_TAGS:
# variable_regex += fr"""{kw}|"""
# variable_pattern = re.compile(variable_regex[0:-1]+")(.*=|.*;)(.*)", flags=re.MULTILINE)
# function_regex = "^(?P<return_type>"
# for kw in FUNCTION_IDENTIFIERS:
# function_regex += fr"""{kw}|"""
# function_regex = function_regex[0:-1]+ ")(?P<name>.*)[(](?P<args>.*)[)][^;]"
# function_pattern = re.compile(function_regex, flags=re.MULTILINE)
# def check_src(src: List[str], line_index: int, tag: str) -> bool:
# if line_index >= len(src):
# return False
# return tag in src[line_index]
# def check_line_start(src: List[str], line_index: int, tag: str) -> bool:
# if line_index >= len(src):
# return False
# return src[line_index].startswith(tag)
# def get_next_occurence_of(src: List[str], start_idx:int, tag:str) -> int:
# """Returns the index of the next occurence of tag in src from start_idx"""
# i = start_idx
# while i < len(src):
# if check_src(src, i, tag):
# break
# i += 1
# return i
# def get_scope_start_offset(src: List[str], i: int) -> int:
# if check_src(src, i, "{"):
# return 1
# elif check_src(src, i+1, "{"):
# return 2
# raise ScopeNotFoundException("Unable to find scope start. Is the program ill-formed?")
# def get_function_scopes(src: str) -> List[Function_scope]:
# scopes = []
# lines = src.splitlines(True)
# for match in function_pattern.finditer(src):
# span = match.span()
# matched_str = src[span[0]:span[1]]
# rtype, name, args = get_function_info(match, matched_str)
# child_instructions = get_function_instructions(lines, matched_str)
# scopes.append(Function_scope(child_instructions, name, rtype, args))
# return scopes

View File

@@ -1,8 +1,11 @@
//except for this line, this is what interpret_source.load_src returns
importgreenfoot.*;//(World,Actor,GreenfootImage,GreenfootandMouseInfo)
classRoverextendsActor
{
Displayanzeige;
/**
*thisfunctionistobeimplementedbytheuser
*dependingontheneededactions
*/
voidact()
{
S66Nr3(7);
@@ -81,6 +84,11 @@ fahreZeileDreheRunter(true);
}
fahreZeileDreheRunter(false);
}
/**
*DerRoverbewegtsicheinFeldinFahrtrichtungweiter.
*SolltesichinFahrtrichtungeinObjektderKlasseHuegelbefindenoderersichanderGrenzederWeltbefinden,
*dannerscheinteineentsprechendeMeldungaufdemDisplay.
*/
voidfahre()
{
intposX=getX();
@@ -103,6 +111,10 @@ if(posX==getX()&&posY==getY()&&!huegelVorhanden("vorne"))
nachricht("Ichkannmichnichtbewegen");
}
}
/**
*DerRoverdrehtsichum90GradindieRichtung,diemitrichtung(ᅵlinksᅵoderᅵrechtsᅵ)ᅵbergebenwurde.
*SollteeinandererText(String)als"rechts"oder"links"ᅵbergebenwerden,dannerscheinteineentsprechendeMeldungaufdemDisplay.
*/
voiddrehe(Stringrichtung)
{
if(richtung.equals("rechts")){
@@ -115,6 +127,10 @@ setRotation(getRotation()+180);
nachricht("KeinenKorrekteRichtunggegeben!");
}
}
/**
*DerRovergibtdurcheinenWahrheitswert(trueoderfalse)zurᅵck,obsichaufseinerPositioneinObjektderKlasseGesteinbefindet.
*EineentsprechendeMeldungerscheintauchaufdemDisplay.
*/
booleangesteinVorhanden()
{
if(getOneIntersectingObject(Gestein.class)!=null)
@@ -124,6 +140,11 @@ returntrue;
}
returnfalse;
}
/**
*DerRoverᅵberprᅵft,obsichinrichtung("rechts","links",oder"vorne")einObjektderKlasseHuegelbefindet.
*DasErgebniswirdaufdemDisplayangezeigt.
*SollteeinandererText(String)als"rechts","links"oder"vorne"ᅵbergebenwerden,dannerscheinteineentsprechendeMeldungaufdemDisplay.
*/
booleanhuegelVorhanden(Stringrichtung)
{
introt=getRotation();
@@ -161,6 +182,10 @@ nachricht("Befehlnichtkorrekt!");
}
returnfalse;
}
/**
*DerRoverermitteltdenWassergehaltdesGesteinsaufseinerPositionundgibtdiesenaufdemDisplayaus.
*SolltekeinObjektderKlasseGesteinvorhandensein,dannerscheinteineentsprechendeMeldungaufdemDisplay.
*/
voidanalysiereGestein()
{
if(gesteinVorhanden())
@@ -174,10 +199,17 @@ else
nachricht("HieristkeinGestein");
}
}
/**
*DerRovererzeugteinObjektderKlasseᅵMarkierungᅵaufseinerPosition.
*/
voidsetzeMarke()
{
getWorld().addObject(newMarke(),getX(),getY());
}
/**
**DerRovergibtdurcheinenWahrheitswert(trueoderfalse)zurᅵck,obsichaufseinerPositioneinObjektderMarkebefindet.
*EineentsprechendeMeldungerscheintauchaufdemDisplay.
*/
booleanmarkeVorhanden()
{
if(getOneIntersectingObject(Marke.class)!=null)
@@ -206,7 +238,7 @@ voiddisplayAusschalten()
{
getWorld().removeObject(anzeige);
}
protectedvoidaddedToWorld(Worldworld)
voidaddedToWorld(Worldworld)
{
setImage("images/rover.png");
world=getWorld();