diff --git a/.vscode/launch.json b/.vscode/launch.json index 1ded7e8..e80f071 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,7 +8,7 @@ "name": "Python: Aktuelle Datei", "type": "python", "request": "launch", - "program": "run.py", + "program": "debug.py", "console": "integratedTerminal" } ] diff --git a/interpreter/NassiShneidermann.py b/interpreter/NassiShneidermann.py index d18c2fc..220d206 100644 --- a/interpreter/NassiShneidermann.py +++ b/interpreter/NassiShneidermann.py @@ -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) \ No newline at end of file + itp = JavaInterpreter(filepath) + self.function_scopes = itp.load_instruction_scopes() \ No newline at end of file diff --git a/interpreter/interpret_source.py b/interpreter/interpret_source.py index dddb0db..a356b95 100644 --- a/interpreter/interpret_source.py +++ b/interpreter/interpret_source.py @@ -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""" + for tag in self.function_tags: + function_regex += fr"""{re.escape(tag)}|""" + self.function_pattern = re.compile(function_regex[:-1]+")(?P.*)[(](?P.*)[)][^;]", 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" +# for kw in FUNCTION_IDENTIFIERS: +# function_regex += fr"""{kw}|""" +# function_regex = function_regex[0:-1]+ ")(?P.*)[(](?P.*)[)][^;]" + +# 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 \ No newline at end of file diff --git a/res/input/input_parsed.java b/res/input/input_parsed.java index bc0b4da..babf29a 100644 --- a/res/input/input_parsed.java +++ b/res/input/input_parsed.java @@ -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();