From 28a1e313b356a845d2d23a16d6301691103e1603 Mon Sep 17 00:00:00 2001 From: raj_mathe Date: Thu, 6 May 2021 23:34:25 +0200 Subject: [PATCH] master > master: Schema robuster gemacht; simple class, um Aspekte leichter aufzurufen --- code/aussagenlogik/rekursion.py | 30 ++++--- code/aussagenlogik/schema.py | 144 +++++++++++++++++++++----------- code/data.env | 2 +- code/main.py | 28 ++++--- 4 files changed, 131 insertions(+), 73 deletions(-) diff --git a/code/aussagenlogik/rekursion.py b/code/aussagenlogik/rekursion.py index 8818897..ecbd5bf 100644 --- a/code/aussagenlogik/rekursion.py +++ b/code/aussagenlogik/rekursion.py @@ -5,7 +5,7 @@ # IMPORTS # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -from __future__ import annotations; +from __future__ import annotations from lark import Tree; from typing import List; @@ -15,10 +15,12 @@ from aussagenlogik.schema import isTrueSymbol; from aussagenlogik.schema import isFalseSymbol; from aussagenlogik.schema import isNegation; from aussagenlogik.schema import isConjunction; +from aussagenlogik.schema import isLongConjunction; from aussagenlogik.schema import isDisjunction; +from aussagenlogik.schema import isLongDisjunction; from aussagenlogik.schema import isImplication; -from aussagenlogik.schema import getTeilformeln; from aussagenlogik.schema import getName; +from aussagenlogik.schema import SyntaxBaum; # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # GLOBALE KONSTANTEN @@ -30,8 +32,8 @@ from aussagenlogik.schema import getName; # METHODEN # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -def rekursiv_eval(fml: Tree, I: List[str]) -> int: - teilfml = getTeilformeln(fml); +def rekursiv_eval(fml: SyntaxBaum, I: List[str]) -> int: + subfml = fml.children; if isAtom(fml): name = getName(fml); return 1 if (name in I) else 0; @@ -43,19 +45,25 @@ def rekursiv_eval(fml: Tree, I: List[str]) -> int: elif isFalseSymbol(fml): return 0; elif isNegation(fml): - val0 = rekursiv_eval(teilfml[0], I); + val0 = rekursiv_eval(subfml[0], I); return 1 - val0; elif isConjunction(fml): - val0 = rekursiv_eval(teilfml[0], I); - val1 = rekursiv_eval(teilfml[1], I); + val0 = rekursiv_eval(subfml[0], I); + val1 = rekursiv_eval(subfml[1], I); return min(val0, val1); + elif isLongConjunction(fml): + values = [rekursiv_eval(t, I) for t in subfml]; + return min(values); elif isDisjunction(fml): - val0 = rekursiv_eval(teilfml[0], I); - val1 = rekursiv_eval(teilfml[1], I); + val0 = rekursiv_eval(subfml[0], I); + val1 = rekursiv_eval(subfml[1], I); return max(val0, val1); + elif isLongDisjunction(fml): + values = [rekursiv_eval(t, I) for t in subfml]; + return max(values); elif isImplication(fml): - val0 = rekursiv_eval(teilfml[0], I); - val1 = rekursiv_eval(teilfml[1], I); + val0 = rekursiv_eval(subfml[0], I); + val1 = rekursiv_eval(subfml[1], I); return 0 if val0 == 1 and val1 == 0 else 1; raise Exception('Evaluation nicht möglich!'); diff --git a/code/aussagenlogik/schema.py b/code/aussagenlogik/schema.py index c4f1a08..da4b829 100644 --- a/code/aussagenlogik/schema.py +++ b/code/aussagenlogik/schema.py @@ -11,6 +11,7 @@ from __future__ import annotations; from lark import Lark; from lark import Tree; from typing import List; +from typing import Union; # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # GLOBALE KONSTANTEN @@ -24,42 +25,84 @@ lexerAussagenlogik = Lark( %import common.WORD %ignore WS - ?start: expr + ?start: expr | open - ?expr: atomic | expr_not | expr_and | expr_or | expr_implies - ?literal: atomic | "not" atomic + ?expr: atomic | not | closed + ?open: and | and_long | or | or_long | impl + ?closed: "(" open ")" // atomische Ausdrücke ?atomic: false | true | atom | generic - ?false: "0" -> wahr - ?true: "1" -> falsch - ?atom: /A[0-9]+/ -> atom - ?generic: "{" /((?!({|})).)+/ "}" -> beliebig + ?false: "0" -> kontr + ?true: "1" -> taut + ?atom: /A[0-9]+/ -> atom + ?generic: "{" /((?!({|})).)+/ "}" -> beliebig // Symbole - ?conn_not: "!" -> junktor - ?conn_and: /&+/ -> junktor - ?conn_or: /\\|+/ -> junktor - ?conn_impl: /->|=>/ -> junktor + ?symb_not: /!/ -> symb + ?symb_and: /&+/ -> symb + ?symb_or: /\\|+/ -> symb + ?symb_impl: /->|=>/ -> symb // Junktoren - ?expr_not: conn_not expr -> negation - ?expr_and: "(" expr conn_and expr ")" -> konjunktion - ?expr_or: "(" expr conn_or expr ")" -> disjunktion - ?expr_implies: "(" expr conn_impl expr ")" -> implikation + ?not: symb_not expr -> neg + ?and: expr symb_and expr -> konj + ?and_long: [ expr ( symb_and expr )+ ] -> konj_lang + ?or: expr symb_or expr -> disj + ?or_long: [ expr ( symb_or expr )+ ] -> disj_lang + ?impl: expr symb_impl expr -> impl ''', - start="expr", regex=True ); +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# KLASSE: Syntaxbaum +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +class SyntaxBaum(object): + expr: str; + kind: str; + children: List[SyntaxBaum]; + tree: Tree; + + def __init__(self, fml: Tree): + self.kind = fml.data; + if len(fml.children) == 1 and isinstance(fml.children[0], str): + self.expr = fml.children[0]; + self.children = []; + self.tree = Tree(self.kind, fml.children); + else: + self.children = [ SyntaxBaum(child) for child in fml.children if isinstance(child, Tree) and child.data != 'symb' ]; + self.tree = Tree(self.kind, [child.tree for child in self.children]); + signature_parts = []; + i = 0; + for teilfml in fml.children: + if isinstance(teilfml, str): + signature_parts.append(teilfml); + elif teilfml.data == 'symb': + signature_parts.append(getText(teilfml)); + else: + signature_parts.append('{{{}}}'.format(i)); + i += 1; + signature = ' '.join(signature_parts); + self.expr = signature.format(*self.children); + if self.kind in [ 'konj', 'konj_lang', 'disj', 'disj_lang', 'impl' ]: + self.expr = '( {} )'.format(self.expr); + return; + + def __str__(self): + return self.expr; + + def pretty(self): + return self.tree.pretty(); + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # METHODE: string -> Syntaxbaum # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -def stringToSyntaxbaum(u: str) -> Tree: +def stringToSyntaxbaum(u: str) -> SyntaxBaum: try: - u_lexed = lexerAussagenlogik.parse(u); - return u_lexed; + return SyntaxBaum(lexerAussagenlogik.parse(u)); except: raise Exception('Ausdruck \033[1m{}\033[0m konnte nicht erkannt werden!'.format(u)); @@ -67,47 +110,52 @@ def stringToSyntaxbaum(u: str) -> Tree: # METHODEN: Erkennung von Formeltypen # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -def isAtom(fml: Tree) -> bool: - return fml.data == 'atom'; +def isAtom(fml: SyntaxBaum) -> bool: + return fml.kind == 'atom'; -def isBeliebig(fml: Tree) -> bool: - return fml.data == 'beliebig'; +def isBeliebig(fml: SyntaxBaum) -> bool: + return fml.kind == 'beliebig'; -def isTrueSymbol(fml: Tree) -> bool: - return fml.data == 'wahr'; +def isTrueSymbol(fml: SyntaxBaum) -> bool: + return fml.kind == 'taut'; -def isFalseSymbol(fml: Tree) -> bool: - return fml.data == 'falsch'; +def isFalseSymbol(fml: SyntaxBaum) -> bool: + return fml.kind == 'kontr'; -def isNegation(fml: Tree) -> bool: - return fml.data == 'negation'; +def isNegation(fml: SyntaxBaum) -> bool: + return fml.kind == 'neg'; -def isConjunction(fml: Tree) -> bool: - return fml.data == 'konjunktion'; +def isConjunction(fml: SyntaxBaum) -> bool: + return fml.kind == 'konj'; -def isDisjunction(fml: Tree) -> bool: - return fml.data == 'disjunktion'; +def isLongConjunction(fml: SyntaxBaum) -> bool: + return fml.kind == 'konj_lang'; -def isImplication(fml: Tree) -> bool: - return fml.data == 'implikation'; +def isDisjunction(fml: SyntaxBaum) -> bool: + return fml.kind == 'disj'; + +def isLongDisjunction(fml: SyntaxBaum) -> bool: + return fml.kind == 'disj_lang'; + +def isImplication(fml: SyntaxBaum) -> bool: + return fml.kind == 'impl'; # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# METHODEN: Formel -> Teilformeln +# METHODEN: Formel -> Textinhalt # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -def getTeilformeln(fml: Tree) -> List[Tree]: - return [ - part for part in fml.children - if isinstance(part, Tree) - and not part.data == 'junktor' - ]; - -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -# METHODEN: Formel (Atom/Beliebig) -> Name -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -def getName(fml: Tree) -> str: +def getText(fml: Tree) -> str: text = fml.children[0]; if isinstance(text, str): return text; raise Exception('Konnte Textinhalt nicht ablesen!'); + +def getName(fml: SyntaxBaum) -> str: + return fml.expr; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# METHODEN: Formel -> Textinhalt +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +def prettifyTree(fml: Union[Tree, SyntaxBaum]) -> str: + return fml.pretty(); diff --git a/code/data.env b/code/data.env index 1d4bb50..8e9892b 100644 --- a/code/data.env +++ b/code/data.env @@ -3,7 +3,7 @@ # expr = "( A0 && A1 )" # expr = "( A0 || A1 )" # expr = "( A0 -> A1 )" -expr = "( A0 -> ((A0 && A3) || ! A2) )" +expr = "( A0 -> ((A0 && A3 && A4) || ! A2) )" # expr = "( A0 -> ((A0 && A3) || A2) )" # expr = "(( {G} || !{G} ) -> A5)" interpretation = "[ 'A0', 'A2' ]" diff --git a/code/main.py b/code/main.py index 637c785..f84aa4c 100644 --- a/code/main.py +++ b/code/main.py @@ -5,7 +5,6 @@ # IMPORTS # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -from __future__ import annotations; import os; import sys; # sys.tracebacklimit = 0; @@ -16,7 +15,9 @@ from typing import List; sys.path.insert(0, os.getcwd()); +from aussagenlogik.schema import prettifyTree; from aussagenlogik.schema import stringToSyntaxbaum; +from aussagenlogik.schema import SyntaxBaum; from aussagenlogik.rekursion import rekursiv_eval; from aussagenlogik.rekursion import rekursiv_atoms; from aussagenlogik.rekursion import rekursiv_depth; @@ -37,17 +38,17 @@ def main(): ## Daten einlesen: expr, I = getData(); ## Formel in Teilformeln zerlegen: - tree = stringToSyntaxbaum(expr); + fml = stringToSyntaxbaum(expr); ## Methoden ausführen: results = dict( - eval = rekursiv_eval(tree, I), - atoms = rekursiv_atoms(tree), - d = rekursiv_depth(tree), - l = rekursiv_length(tree), - p = rekursiv_parentheses(tree), + eval = rekursiv_eval(fml, I), + atoms = rekursiv_atoms(fml), + d = rekursiv_depth(fml), + l = rekursiv_length(fml), + p = rekursiv_parentheses(fml), ); ## Resultate anzeigen: - display_results(expr, tree, I, results); + display_results(expr, fml, I, results); return; @@ -61,17 +62,18 @@ def getData(): I = eval(data['interpretation'] or '[]'); return expr, I; -def display_results(expr: str, tree: Tree, I: List[str], results: dict): +def display_results(expr: str, fml: SyntaxBaum, I: List[str], results: dict): print(dedent( ''' Syntaxbaum von F := \033[92;1m{F}\033[0m: - '''.format(F=expr) + '''.format(F=fml) )); - print(tree.pretty()); + print(prettifyTree(fml)); print(dedent( ''' - eval(F, I) = \033[94;1m{eval}\033[0m; + Für I = [{I}] und F wie oben gilt + eval(F, I) = \033[94;1m{eval}\033[0m, \033[2matoms(F) = \033[94;1m{atoms}\033[0m; \033[91;1m<- *\033[0m \033[2mdepth(F) = \033[94;1m{d}\033[0m; \033[91;1m<- *\033[0m \033[2mlength(F) = \033[94;1m{l}\033[0m; \033[91;1m<- *\033[0m @@ -79,7 +81,7 @@ def display_results(expr: str, tree: Tree, I: List[str], results: dict): \033[91;1m*\033[0m \033[2mnoch nicht implementiert!\033[0m \033[1;2;4mChallenge:\033[0m \033[2mschreibe diese Methoden! (siehe README.md)\033[0m - '''.format(**results) + '''.format(**results, I=', '.join(I)) )); return;