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", "name": "Python: Aktuelle Datei",
"type": "python", "type": "python",
"request": "launch", "request": "launch",
"program": "run.py", "program": "debug.py",
"console": "integratedTerminal" "console": "integratedTerminal"
} }
] ]

View File

@@ -6,7 +6,7 @@ import os.path
import secrets import secrets
from draw.Iinstruction import Iinstruction 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 from draw.code_to_image_wrapper import NSD_writer
import draw.code_to_image as cti import draw.code_to_image as cti
@@ -70,4 +70,5 @@ class NassiShneidermanDiagram:
raise raise
def load_from_file(self, filepath:str): 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 import logging
from os import remove
import re import re
from re import split from typing import Dict, List, Match, Tuple
from typing import List, Match, Tuple
from errors.custom import InterpreterException, JavaSyntaxError, ScopeNotFoundException from errors.custom import InterpreterException, JavaSyntaxError, ScopeNotFoundException
from draw.Iinstruction import * from draw.Iinstruction import *
@@ -28,215 +26,281 @@ class Function_scope(Iterable):
def __iter__(self): def __iter__(self):
return self.contents.__iter__() return self.contents.__iter__()
COMMENT_PATTERN = re.compile(r"""^(//|--|#).*""") class JavaInterpreter:
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 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) def reset_tags(self, additional_tags: Dict[str, List[str]]=None):
remove_pattern = re.compile("|".join(REPLACE.keys()), flags=re.MULTILINE) """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 = "^(" if additional_tags:
for kw in VARIABLE_TAGS: if "comments" in additional_tags.keys():
variable_regex += fr"""{kw}|""" self.comment_tags.extend(additional_tags["comments"])
variable_pattern = re.compile(variable_regex[0:-1]+")(.*)=?(.*)", flags=re.MULTILINE) 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 = "^(" def _init_regex(self):
for kw in FUNCTION_IDENTIFIERS: """Initialize all tag lists to their default state"""
function_regex += fr"""{kw}|"""
function_regex = function_regex[0:-1]+ ").*([(].*[)].*)"
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): def _recompile_regex(self):
return remove_pattern.sub(lambda m: REPLACE[re.escape(m.group(0))], org) """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]: remove_regex = r"""^("""
lines: List[str] = [] for tag in self.remove_tags:
brace_open_count, brace_closed_count = 0,0 remove_regex += fr"""{re.escape(tag)}|"""
try: self.remove_pattern = re.compile(remove_regex[:-1]+')', flags=re.MULTILINE)
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!")
if brace_open_count != brace_closed_count: variable_regex = r"""^("""
raise JavaSyntaxError("Number of opened braces does not match number of closed ones. Program is ill-formed!") for tag in self.type_tags:
variable_regex += fr"""{re.escape(tag)}|"""
self.variable_pattern = re.compile(variable_regex[:-1]+")(.*=|.*;)(.*)", flags=re.MULTILINE)
return lines 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(src: List[str], line_index: int, tag: str) -> bool: def _check_src(self, idx: int, tag: str) -> bool:
if line_index >= len(src): if idx >= len(self.lines):
return False return False
return tag in src[line_index] return tag in self.lines[idx]
def check_line_start(src: List[str], line_index: int, tag: str) -> bool: def _check_line_start(self, idx: int, tag: str) -> bool:
if line_index >= len(src): if idx >= len(self.lines):
return False return False
return src[line_index].startswith(tag) return self.lines[idx].startswith(tag)
def get_next_occurence_of(src: List[str], start_idx:int, tag:str) -> int: def _get_scope_start_offset(self, i: int) -> int:
"""Returns the index of the next occurence of tag in src from start_idx""" if self._check_src(i, "{"):
i = start_idx return 1
while i < len(src): elif self._check_src(i+1, "{"):
if check_src(src, i, tag): return 2
break raise ScopeNotFoundException("Unable to find scope start. Is the program ill-formed?")
i += 1
return i
def get_scope_start_offset(src: List[str], i: int) -> int: def _handle_while(self, line: str, idx: int):
if check_src(src, i, "{"): bracket_idx = line.rindex(')') # throws if while contruct is illformed
return 1
elif check_src(src, i+1, "{"): instruction_txt = line[6:bracket_idx]
return 2 brace_offset = self._get_scope_start_offset(idx)
raise ScopeNotFoundException("Unable to find scope start. Is the program ill-formed?") child_instructions, idx = self._get_instructions_in_scope(idx+brace_offset)
return while_instruction_front(("while" + instruction_txt), child_instructions), idx
def _handle_else(self, idx: int):
brace_offset = self._get_scope_start_offset(idx)
return self._get_instructions_in_scope(idx+brace_offset)
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 handle_while(line: str, src: List[str], i: int) -> Tuple[Iinstruction, int]: def _handle_if(self, line: str, idx: int):
bracket_idx = line.rindex(')') # throws if while contruct is illformed bracket_idx = line.rindex(')') # throws if the contruct is illformed
instruction_txt = line[3:bracket_idx]
instruction_txt = line[6:bracket_idx] brace_offset = self._get_scope_start_offset(idx)
brace_offset = get_scope_start_offset(src, i) true_instructions, idx = self._get_instructions_in_scope(idx+brace_offset)
child_instructions, i = get_instructions_in_scope(src, i+brace_offset)
return while_instruction_front((WHILE_TAG + instruction_txt), child_instructions), i
def handle_else(src: List[str], i: int) -> Tuple[List[Iinstruction], int]: false_instructions = self._get_else_scope(idx)
brace_offset = get_scope_start_offset(src, i)
return get_instructions_in_scope(src, i+brace_offset)
def handle_if(line: str, src: List[str], i: int) -> Tuple[Iinstruction, int]: return if_instruction(instruction_txt, true_instructions, false_instructions), idx
bracket_idx = line.rindex(')') # throws if the contruct is illformed
instruction_txt = line[3:bracket_idx]
brace_offset = get_scope_start_offset(src, i) def _handle_do_while(self, line: str, idx: int):
true_instructions, i = get_instructions_in_scope(src, i+brace_offset) brace_offset = self._get_scope_start_offset(idx)
child_instructions, idx = self._get_instructions_in_scope(idx+brace_offset)
false_instructions = None 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!")
#check for else statements return while_instruction_back(instruction_txt, child_instructions), idx
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"): def _handle_variable(self, line: str, idx: int):
logging.debug("found else construct in line: %i", i+2) groups = self.variable_pattern.match(line).groups()
false_instructions, i = handle_else(src, i+1) 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
return if_instruction(instruction_txt, true_instructions, false_instructions), i 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)
def handle_do_while(line: str, src: List[str], i: int) -> Tuple[Iinstruction, int]: elif line.startswith("if("):
brace_offset = get_scope_start_offset(src, i) logging.debug("Found if construct in line: %i", idx+1)
child_instructions, i = get_instructions_in_scope(src, i+brace_offset) return self._handle_if(line, idx)
instruction_txt = None elif line.startswith("do"):
if check_line_start(src, i, "}while("): logging.debug("Found do-while construct in line: %i", idx+1)
end_line = src[i] return self._handle_do_while(line, idx)
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 elif self.variable_pattern.match(line):
logging.debug("Found variable in line %i", idx+1)
return self._handle_variable(line, idx)
def handle_variable(line:str, src: List[str], i: int) -> Tuple[Iinstruction, int]: else:
groups = variable_pattern.match(line).groups() logging.debug("Found generic instruction in line %i", idx+1)
var_type = groups[0] return generic_instruction(line), idx
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 _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]
def handle_instruction(line: str, src: List[str], i: int) -> Tuple[Iinstruction, int]: if self._check_src(i, '}'):
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
break break
instruction, i = handle_instruction(line, src, i) instruction, i = self._handle_instruction(line, i)
outer_scope.append(instruction) scope.append(instruction)
except Exception as e: i += 1
logging.error("Encountered error in line %i: %s", i+1, str(e)) return scope, i
raise
except:
logging.fatal("Encountered unexpected error in line: %i", i+1)
raise
i += 1
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]: def _get_function_info(self, match: Match[str]) -> Tuple[str, str, List[str]]:
groups = match.groups() groups = match.groupdict()
ftype = groups[0] ftype = groups["return_type"]
fargs = groups[1] fargs = groups["args"]
fname = line.removeprefix(ftype).removesuffix(fargs) #remove return type and argument list to get the function name fname = groups["name"]
return ftype, fname, fargs return ftype, fname, fargs.split(',')
def get_function_scopes(src: List[str]) -> List[Function_scope]: def _get_function_instructions(self, function_header: str) -> List[Iinstruction]:
functions = [] 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 def _get_function_scopes(self) -> List[Function_scope]:
while i < len(src): scopes = []
line = src[i] for match in self.function_pattern.finditer(self.src):
if match:=function_pattern.match(line): span = match.span()
function_return_type, function_name, function_args = get_function_info(match, line) header = self.src[span[0]:span[1]]
brace_offset = get_scope_start_offset(src, i) rtype, name, args = self._get_function_info(match)
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
def remove_keywords(src: str) -> str: child_instructions = self._get_function_instructions(header)
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)
def load_scoped_instructions(filepath: str) -> List[Function_scope]: scopes.append(Function_scope(child_instructions, name, rtype, args))
src = load_src(filepath) return scopes
return get_function_scopes(src)
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) importgreenfoot.*;//(World,Actor,GreenfootImage,GreenfootandMouseInfo)
classRoverextendsActor classRoverextendsActor
{ {
Displayanzeige; Displayanzeige;
/**
*thisfunctionistobeimplementedbytheuser
*dependingontheneededactions
*/
voidact() voidact()
{ {
S66Nr3(7); S66Nr3(7);
@@ -81,6 +84,11 @@ fahreZeileDreheRunter(true);
} }
fahreZeileDreheRunter(false); fahreZeileDreheRunter(false);
} }
/**
*DerRoverbewegtsicheinFeldinFahrtrichtungweiter.
*SolltesichinFahrtrichtungeinObjektderKlasseHuegelbefindenoderersichanderGrenzederWeltbefinden,
*dannerscheinteineentsprechendeMeldungaufdemDisplay.
*/
voidfahre() voidfahre()
{ {
intposX=getX(); intposX=getX();
@@ -103,6 +111,10 @@ if(posX==getX()&&posY==getY()&&!huegelVorhanden("vorne"))
nachricht("Ichkannmichnichtbewegen"); nachricht("Ichkannmichnichtbewegen");
} }
} }
/**
*DerRoverdrehtsichum90GradindieRichtung,diemitrichtung(ᅵlinksᅵoderᅵrechtsᅵ)ᅵbergebenwurde.
*SollteeinandererText(String)als"rechts"oder"links"ᅵbergebenwerden,dannerscheinteineentsprechendeMeldungaufdemDisplay.
*/
voiddrehe(Stringrichtung) voiddrehe(Stringrichtung)
{ {
if(richtung.equals("rechts")){ if(richtung.equals("rechts")){
@@ -115,6 +127,10 @@ setRotation(getRotation()+180);
nachricht("KeinenKorrekteRichtunggegeben!"); nachricht("KeinenKorrekteRichtunggegeben!");
} }
} }
/**
*DerRovergibtdurcheinenWahrheitswert(trueoderfalse)zurᅵck,obsichaufseinerPositioneinObjektderKlasseGesteinbefindet.
*EineentsprechendeMeldungerscheintauchaufdemDisplay.
*/
booleangesteinVorhanden() booleangesteinVorhanden()
{ {
if(getOneIntersectingObject(Gestein.class)!=null) if(getOneIntersectingObject(Gestein.class)!=null)
@@ -124,6 +140,11 @@ returntrue;
} }
returnfalse; returnfalse;
} }
/**
*DerRoverᅵberprᅵft,obsichinrichtung("rechts","links",oder"vorne")einObjektderKlasseHuegelbefindet.
*DasErgebniswirdaufdemDisplayangezeigt.
*SollteeinandererText(String)als"rechts","links"oder"vorne"ᅵbergebenwerden,dannerscheinteineentsprechendeMeldungaufdemDisplay.
*/
booleanhuegelVorhanden(Stringrichtung) booleanhuegelVorhanden(Stringrichtung)
{ {
introt=getRotation(); introt=getRotation();
@@ -161,6 +182,10 @@ nachricht("Befehlnichtkorrekt!");
} }
returnfalse; returnfalse;
} }
/**
*DerRoverermitteltdenWassergehaltdesGesteinsaufseinerPositionundgibtdiesenaufdemDisplayaus.
*SolltekeinObjektderKlasseGesteinvorhandensein,dannerscheinteineentsprechendeMeldungaufdemDisplay.
*/
voidanalysiereGestein() voidanalysiereGestein()
{ {
if(gesteinVorhanden()) if(gesteinVorhanden())
@@ -174,10 +199,17 @@ else
nachricht("HieristkeinGestein"); nachricht("HieristkeinGestein");
} }
} }
/**
*DerRovererzeugteinObjektderKlasseᅵMarkierungᅵaufseinerPosition.
*/
voidsetzeMarke() voidsetzeMarke()
{ {
getWorld().addObject(newMarke(),getX(),getY()); getWorld().addObject(newMarke(),getX(),getY());
} }
/**
**DerRovergibtdurcheinenWahrheitswert(trueoderfalse)zurᅵck,obsichaufseinerPositioneinObjektderMarkebefindet.
*EineentsprechendeMeldungerscheintauchaufdemDisplay.
*/
booleanmarkeVorhanden() booleanmarkeVorhanden()
{ {
if(getOneIntersectingObject(Marke.class)!=null) if(getOneIntersectingObject(Marke.class)!=null)
@@ -206,7 +238,7 @@ voiddisplayAusschalten()
{ {
getWorld().removeObject(anzeige); getWorld().removeObject(anzeige);
} }
protectedvoidaddedToWorld(Worldworld) voidaddedToWorld(Worldworld)
{ {
setImage("images/rover.png"); setImage("images/rover.png");
world=getWorld(); world=getWorld();