master > master: allgemeine Aufräumung

This commit is contained in:
RD 2021-05-10 14:32:52 +02:00
parent a0dee82659
commit 61ec2d7df3
17 changed files with 303 additions and 244 deletions

19
code/.gitignore vendored
View File

@ -1,12 +1,21 @@
* *
!/.gitignore !/.gitignore
!/README.md
!/build.sh
!/test.sh
!/data.env !/data.env
!/requirements !/README.md
## Scripts
!/scripts
!/scripts/requirements
!/scripts/build.sh
!/scripts/test.sh
## Für Erzeugung von Grammatiken:
!/grammars
!/grammars/README.md
!/grammars/*.lark
## Python Source
!/aussagenlogik !/aussagenlogik
!/utests !/utests
!/**/*.py !/**/*.py

View File

@ -6,24 +6,23 @@ Diese dienen nur zur Demonstration / konkreten Verwirklichung von Verfahren, die
Der Gebrauch dieser Skripte unterliegt der Eigenverantwortung von Studierenden. Der Gebrauch dieser Skripte unterliegt der Eigenverantwortung von Studierenden.
Da ich kein Informatiker bin, sind auch einige Aspekt bestimmt nicht optimal programmiert/strukturiert. Da ich kein Informatiker bin,
sind auch einige Aspekt bestimmt nicht optimal programmiert/strukturiert.
Dafür kann jeder in seiner Kopie einfach alles anpassen.
Das hier soll einfach funktionieren.
## Systemvoraussetzungen ## ## Systemvoraussetzungen ##
- bash (auch bash-for-windows). - bash (auch bash-for-windows).
- python (mind. 3.9.x) - python (mind. 3.9.x)
Natürlich wäre eine Implementierung in einer besseren Sprache wie **go** idealer, aber vielleicht in Zukunft.
## Voreinstellungen ## ## Voreinstellungen ##
- In einer bash-console zu diesem Ordner navigieren und folgenden Befehl ausführen: - In einer bash-console zu diesem Ordner navigieren und folgenden Befehl ausführen:
```bash ```bash
chmod +x build.sh test.sh chmod +x scripts/*.sh
## oder
chmod +x *.sh
``` ```
- In `build.sh` gibt es eine Zeile, die zur Ausführung der Python-Skripte notwendigen Module über PIP installieren lässt. (Die Liste der Packages findet man in der Datei `requirements`). Diese Zeile kann man ruhig nach der ersten Ausführung rauskommentieren. - In `scripts/build.sh` gibt es eine Zeile, die zur Ausführung der Python-Skripte notwendigen Module über PIP installieren lässt. (Die Liste der Packages findet man in der Datei `requirements`). Diese Zeile kann man ruhig nach der ersten Ausführung rauskommentieren.
## Daten ## ## Daten ##
@ -33,7 +32,7 @@ In `data.env` kann man Daten (aktuell: auszuwertenden Ausdruck + Interpretation/
In einer bash-console zu diesem Ordner navigieren und In einer bash-console zu diesem Ordner navigieren und
```bash ```bash
./build.sh source build.sh
## oder (für Linux) ## oder (für Linux)
python3 main.py python3 main.py
## oder (für Windows) ## oder (für Windows)
@ -56,14 +55,19 @@ Wer etwas standardisierter seine Methoden testen will, kann automatisiertes Test
- In der Console (wenn noch nicht geschehen) folgenden Befehl einmalig ausführen: - In der Console (wenn noch nicht geschehen) folgenden Befehl einmalig ausführen:
```bash ```bash
chmod +x test.sh chmod +x scripts/test.sh
``` ```
- In `utests/u_rekursion.py` beim relevanten Testteil eine oder mehrere der Zeilen - In `utests/rekursion_test.py` beim relevanten Testteil eine oder mehrere der Zeilen
```python ```python
@unittest.skip('Methode noch nicht implementiert') @unittest.skip('Methode noch nicht implementiert')
``` ```
rauskommentieren/löschen. rauskommentieren/löschen.
- Jetzt
```bash
source test.sh
```
ausführen.
Jetzt `test.sh` ausführen. Die unittests testen Methoden gegen mehrere vorkonstruierte Testfälle samt erwarteten Ergebnissen geprüft. Sollten einige Tests scheitern, dann Fehlermeldung durchlesen, und Methode entsprechend der Kritik überarbeiten. Die unit tests testen Methoden gegen mehrere vorkonstruierte Testfälle samt erwarteten Ergebnissen geprüft. Sollten einige Tests scheitern, dann Fehlermeldung durchlesen, und Methode entsprechend der Kritik überarbeiten.
Die geschriebenen Unittests sind natürlich nicht ausführlich. Man kann diese nach Bedarf ergänzen. Am sinnvollsten baut man welche, die wirklich scheitern können, sonst sagen die Tests nichts aus. Die geschriebenen unit tests sind natürlich nicht ausführlich. Man kann diese nach Bedarf ergänzen. Am sinnvollsten baut man welche, die wirklich scheitern können, sonst sagen die Tests nichts aus.

View File

@ -1,37 +0,0 @@
%import common.WS
%import common.NUMBER
%import common.WORD
%ignore WS
// Schemata für Ausdrücke
// 'open' = äußerste Klammern fehlen, wenn nötig.
// 'closed' = sonst
?expr: open | closed
?closed: atomic | not | "(" open ")"
?open: and | and_long | or | or_long | impl
// Schemata für atomische Ausdrücke
?atomic: false | true | atom | generic
?false: /0|false/ -> kontr
?true: /1|true/ -> taut
?atom: /A[0-9]+/ -> atom
// als 'generische' Formeln schreibe bspw. {F}, {G}, {F1}, usw.
?generic: "{" /((?!({|})).)+/ "}" -> beliebig
// Symbole (erlaube mehrere Varianten)
?symb_not: /!|~|not/ -> symb
?symb_and: /&+|\^|and/ -> symb
?symb_or: /\|+|v|or/ -> symb
?symb_impl: /->|=>/ -> symb
// Schema für Negation: ¬ F
?not: symb_not closed -> neg
// Schemata für Konjunktion: F1 ⋀ F2 bzw. F1 ⋀ F2 ⋀ ... ⋀ Fn
?and: closed symb_and closed -> konj
?and_long: [ closed ( symb_and closed )+ ] -> konj_lang
// Schemata für Disjunktion: F1 F2 bzw. F1 F2 ... Fn
?or: closed symb_or closed -> disj
?or_long: [ closed ( symb_or closed )+ ] -> disj_lang
// Schema für Implikation: F1 ⟶ F2
?impl: closed symb_impl closed -> impl

View File

@ -5,21 +5,9 @@
# IMPORTS # IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from __future__ import annotations
from lark import Tree;
from typing import List; from typing import List;
from aussagenlogik.schema import isAtom; from aussagenlogik.syntaxbaum import SyntaxBaum;
from aussagenlogik.schema import isBeliebig;
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 SyntaxBaum;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# GLOBALE KONSTANTEN # GLOBALE KONSTANTEN
@ -31,53 +19,69 @@ from aussagenlogik.schema import SyntaxBaum;
# METHODEN # METHODEN
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def rekursiv_eval(fml: SyntaxBaum, I: List[str]) -> int: def rekursivEval(fml: SyntaxBaum, I: List[str]) -> int:
subfml = fml.children; subfml = fml.children;
if isAtom(fml): ########
# FÄLLE AUS DER VORLESUNG
########
# Fall Atom
if fml.isAtom():
name = fml.expr; name = fml.expr;
return 1 if (name in I) else 0; return 1 if (name in I) else 0;
if isBeliebig(fml): # Fall ¬F
name = fml.expr; elif fml.isNegation():
return 1 if (name in I) else 0; val0 = rekursivEval(subfml[0], I);
elif isTrueSymbol(fml):
return 1;
elif isFalseSymbol(fml):
return 0;
elif isNegation(fml):
val0 = rekursiv_eval(subfml[0], I);
return 1 - val0; return 1 - val0;
elif isConjunction(fml): # Fall F1 ⋀ F2
val0 = rekursiv_eval(subfml[0], I); elif fml.isConjunction2():
val1 = rekursiv_eval(subfml[1], I); val0 = rekursivEval(subfml[0], I);
val1 = rekursivEval(subfml[1], I);
return min(val0, val1); return min(val0, val1);
elif isLongConjunction(fml): # Fall F1 F2
values = [rekursiv_eval(t, I) for t in subfml]; elif fml.isDisjunction2():
return min(values); val0 = rekursivEval(subfml[0], I);
elif isDisjunction(fml): val1 = rekursivEval(subfml[1], I);
val0 = rekursiv_eval(subfml[0], I);
val1 = rekursiv_eval(subfml[1], I);
return max(val0, val1); return max(val0, val1);
elif isLongDisjunction(fml): ########
values = [rekursiv_eval(t, I) for t in subfml]; # WEITERE FÄLLE NICHT IN VORLESUNG
return max(values); ########
elif isImplication(fml): # Sonderfall: generische Formel als Variable
val0 = rekursiv_eval(subfml[0], I); if fml.isGeneric():
val1 = rekursiv_eval(subfml[1], I); name = fml.expr;
return 1 if (name in I) else 0;
# Sonderfall: Tautologiesymbol
elif fml.isTautologySymbol():
return 1;
# Sonderfall: Kontradiktionssymbol
elif fml.isContradictionSymbol():
return 0;
# Fall Implikation: F1 ⟶ F2
elif fml.isImplication():
val0 = rekursivEval(subfml[0], I);
val1 = rekursivEval(subfml[1], I);
return 0 if val0 == 1 and val1 == 0 else 1; return 0 if val0 == 1 and val1 == 0 else 1;
# Fall F1 ⋀ F2 ⋀ ... ⋀ Fn
elif fml.isConjunction():
values = [rekursivEval(t, I) for t in subfml];
return min(values);
# Fall F1 F2 ... Fn
elif fml.isDisjunction():
values = [rekursivEval(t, I) for t in subfml];
return max(values);
raise Exception('Evaluation nicht möglich!'); raise Exception('Evaluation nicht möglich!');
def rekursiv_atoms(fml: Tree) -> List[str]: def rekursivAtoms(fml: SyntaxBaum) -> List[str]:
## Herausforderung: schreibe diese Funktion! ## Herausforderung: schreibe diese Funktion!
return []; return [];
def rekursiv_depth(fml: Tree) -> int: def rekursivDepth(fml: SyntaxBaum) -> int:
## Herausforderung: schreibe diese Funktion! ## Herausforderung: schreibe diese Funktion!
return 0; return 0;
def rekursiv_length(fml: Tree) -> int: def rekursivLength(fml: SyntaxBaum) -> int:
## Herausforderung: schreibe diese Funktion! ## Herausforderung: schreibe diese Funktion!
return 0; return 0;
def rekursiv_parentheses(fml: Tree) -> int: def rekursivParentheses(fml: SyntaxBaum) -> int:
## Herausforderung: schreibe diese Funktion! ## Herausforderung: schreibe diese Funktion!
return 0 return 0

View File

@ -6,8 +6,6 @@
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from __future__ import annotations; from __future__ import annotations;
# install: lark; lark-parser; lark-parser[regex].
# https://lark-parser.readthedocs.io/en/latest/grammar.html
from lark import Lark; from lark import Lark;
from aussagenlogik.syntaxbaum import SyntaxBaum; from aussagenlogik.syntaxbaum import SyntaxBaum;
@ -16,10 +14,10 @@ from aussagenlogik.syntaxbaum import SyntaxBaum;
# GLOBALE KONSTANTEN # GLOBALE KONSTANTEN
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## lexer # lexer durch LARK erzeugen
with open('aussagenlogik/grammar.lark', 'r') as fp: with open('grammars/grammar.lark', 'r') as fp:
grammar = ''.join(fp.readlines()); grammar = ''.join(fp.readlines());
lexerAussagenlogik = Lark(grammar, start='expr', regex=True); lexer = Lark(grammar, start='expr', regex=True);
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# METHODE: string -> Syntaxbaum # METHODE: string -> Syntaxbaum
@ -27,43 +25,6 @@ with open('aussagenlogik/grammar.lark', 'r') as fp:
def stringToSyntaxbaum(expr: str) -> SyntaxBaum: def stringToSyntaxbaum(expr: str) -> SyntaxBaum:
try: try:
return SyntaxBaum(lexerAussagenlogik.parse(expr)); return SyntaxBaum(lexer.parse(expr));
except: except:
raise Exception('Ausdruck \033[1m{}\033[0m konnte nicht erkannt werden!'.format(expr)); raise Exception('Ausdruck \033[1m{}\033[0m konnte nicht erkannt werden!'.format(expr));
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# METHODEN: Erkennung von Formeltypen
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def isAtom(fml: SyntaxBaum) -> bool:
return fml.kind == 'atom';
def isLiteral(fml: SyntaxBaum) -> bool:
return isAtom(fml) or (isNegation(fml) and isAtom(fml.child));
def isBeliebig(fml: SyntaxBaum) -> bool:
return fml.kind == 'beliebig';
def isTrueSymbol(fml: SyntaxBaum) -> bool:
return fml.kind == 'taut';
def isFalseSymbol(fml: SyntaxBaum) -> bool:
return fml.kind == 'kontr';
def isNegation(fml: SyntaxBaum) -> bool:
return fml.kind == 'neg';
def isConjunction(fml: SyntaxBaum) -> bool:
return fml.kind == 'konj';
def isLongConjunction(fml: SyntaxBaum) -> bool:
return fml.kind in ['konj', 'konj_lang'];
def isDisjunction(fml: SyntaxBaum) -> bool:
return fml.kind == 'disj';
def isLongDisjunction(fml: SyntaxBaum) -> bool:
return fml.kind in ['disj', 'disj_lang'];
def isImplication(fml: SyntaxBaum) -> bool:
return fml.kind == 'impl';

View File

@ -6,7 +6,7 @@
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from __future__ import annotations; from __future__ import annotations;
from lark import Tree; from lark import Tree as larkTree;
from typing import Generator; from typing import Generator;
from typing import List; from typing import List;
@ -21,37 +21,31 @@ from typing import List;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class SyntaxBaum(object): class SyntaxBaum(object):
expr: str;
kind: str; kind: str;
expr: str;
valence: int;
children: List[SyntaxBaum]; children: List[SyntaxBaum];
tree: Tree;
def __init__(self, fml: Tree): def __init__(self, fml: larkTree):
self.kind = fml.data; self.kind = fml.data;
if len(fml.children) == 1 and isinstance(fml.children[0], str): self.children = [];
self.expr = fml.children[0]; self.valence = 0;
self.children = []; expr_parts = []
self.tree = Tree(self.kind, fml.children); for child in fml.children:
else: if isinstance(child, str):
self.children = [ SyntaxBaum(child) for child in fml.children if isinstance(child, Tree) and child.data != 'symb' ]; expr_parts.append(child);
self.tree = Tree(self.kind, [child.tree for child in self.children]); ## subfml is instance larkTree:
signature_parts = []; elif child.data == 'symb':
i = 0; symb = str(child.children[0]);
for subfml in fml.children: expr_parts.append(symb);
if isinstance(subfml, str): else:
signature_parts.append(subfml); subtree = SyntaxBaum(child);
elif subfml.data == 'symb': self.children.append(subtree);
symb = str(subfml.children[0]); self.valence += 1;
signature_parts.append(symb); expr_parts.append(subtree.expr);
else: self.expr = ' '.join(expr_parts);
signature_parts.append('{{{}}}'.format(i)); if self.valence > 1:
i += 1; self.expr = '(' + self.expr + ')';
signature = ' '.join(signature_parts);
self.expr = signature.format(*self.children);
if self.kind in [ 'konj', 'konj_lang', 'disj', 'disj_lang', 'impl' ]:
lbrace = '(' if self.expr.startswith('(') else '( ';
rbrace = ')' if self.expr.endswith(')') else ' )';
self.expr = lbrace + self.expr + rbrace;
return; return;
def __str__(self): def __str__(self):
@ -65,5 +59,58 @@ class SyntaxBaum(object):
def child(self) -> SyntaxBaum: def child(self) -> SyntaxBaum:
return self.children[0]; return self.children[0];
def pretty(self): # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
return self.tree.pretty(indent_str='- '); # METHOD: Pretty
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def pretty(self, preindent: str = '', tab: str = ' ', prepend: str = '', depth: int = 0) -> str:
indent = preindent + tab*depth;
if self.valence == 0 and self.kind in [ 'atom', 'generic' ]:
return indent + prepend + self.kind + ' ' + self.expr;
return '\n'.join(
[indent + prepend + self.kind] \
+ [subtree.pretty(preindent, tab, '|__ ', depth+1) for subtree in self.children]
);
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# METHODS: Erkennung von Formeltypen
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def isIrreducible(self) -> bool:
return self.valence == 0;
def isAtom(self) -> bool:
return self.kind == 'atom';
def isLiteral(self) -> bool:
return self.isAtom() or (self.isNegation() and self.child.isAtom());
def isGeneric(self) -> bool:
return self.kind == 'generic';
def isTautologySymbol(self) -> bool:
return self.kind == 'taut';
def isContradictionSymbol(self) -> bool:
return self.kind == 'contradiction';
def isConnective(self) -> bool:
return self.valence > 0;
def isNegation(self) -> bool:
return self.kind == 'not';
def isConjunction2(self) -> bool:
return self.kind == 'and2';
def isConjunction(self) -> bool:
return self.kind in ['and', 'and2'];
def isDisjunction2(self) -> bool:
return self.kind == 'or2';
def isDisjunction(self) -> bool:
return self.kind in ['or', 'or2'];
def isImplication(self) -> bool:
return self.kind == 'implies';

9
code/grammars/README.md Normal file
View File

@ -0,0 +1,9 @@
# LARK #
Die Grammatik wird hier in `.lark` Format präsentiert und gelext+geparsed.
Für Python braucht man `lark`, `lark-parser`, `lark-parser[regex]` über **PIP** zu installieren.
Siehe
<https://lark-parser.readthedocs.io/en/latest/grammar.html>
und
<https://github.com/lark-parser/lark>
für mehr Informationen zu **LARK**.

View File

@ -0,0 +1,35 @@
%import common.WS
%import common.NUMBER
%import common.WORD
%ignore WS
// Schemata für Ausdrücke
?expr: open | closed
?closed: atomic | not | "(" open ")"
?open: and | and2 | or | or2 | implies
// Schemata für atomische Ausdrücke
?atomic: taut | contradiction | atom | generic
?taut: /1|true/ -> taut
?contradiction: /0|false/ -> contradiction
?atom: /A[0-9]+/ -> atom
// als 'generische' Formeln schreibe bspw. {F}, {G}, {F1}, usw.
?generic: "{" /((?!({|})).)+/ "}" -> generic
// Symbole (erlaube mehrere Varianten)
?symb_not: /!|~|not/ -> symb
?symb_and: /&+|\^|and/ -> symb
?symb_or: /\|+|v|or/ -> symb
?symb_impl: /->|=>/ -> symb
// Schema für Negation: ¬ F
?not: symb_not closed
// Schemata für Konjunktion: F1 ⋀ F2 bzw. F1 ⋀ F2 ⋀ ... ⋀ Fn
?and2: closed symb_and closed
?and: [ closed ( symb_and closed )+ ]
// Schemata für Disjunktion: F1 F2 bzw. F1 F2 ... Fn
?or2: closed symb_or closed
?or: [ closed ( symb_or closed )+ ]
// Schema für Implikation: F1 ⟶ F2
?implies: closed symb_impl closed

View File

@ -17,11 +17,11 @@ sys.path.insert(0, os.getcwd());
from aussagenlogik.schema import stringToSyntaxbaum; from aussagenlogik.schema import stringToSyntaxbaum;
from aussagenlogik.syntaxbaum import SyntaxBaum; from aussagenlogik.syntaxbaum import SyntaxBaum;
from aussagenlogik.rekursion import rekursiv_eval; from aussagenlogik.rekursion import rekursivEval;
from aussagenlogik.rekursion import rekursiv_atoms; from aussagenlogik.rekursion import rekursivAtoms;
from aussagenlogik.rekursion import rekursiv_depth; from aussagenlogik.rekursion import rekursivDepth;
from aussagenlogik.rekursion import rekursiv_length; from aussagenlogik.rekursion import rekursivLength;
from aussagenlogik.rekursion import rekursiv_parentheses; from aussagenlogik.rekursion import rekursivParentheses;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# GLOBALE KONSTANTEN # GLOBALE KONSTANTEN
@ -37,17 +37,17 @@ def main():
## Daten einlesen: ## Daten einlesen:
expr, I = getData(); expr, I = getData();
## Formel in Teilformeln zerlegen: ## Formel in Teilformeln zerlegen:
fml = stringToSyntaxbaum(expr); tree = stringToSyntaxbaum(expr);
## Methoden ausführen: ## Methoden ausführen:
results = dict( results = dict(
eval = rekursiv_eval(fml, I), eval = rekursivEval(tree, I),
atoms = rekursiv_atoms(fml), atoms = rekursivAtoms(tree),
d = rekursiv_depth(fml), depth = rekursivDepth(tree),
l = rekursiv_length(fml), length = rekursivLength(tree),
p = rekursiv_parentheses(fml), nParentheses = rekursivParentheses(tree),
); );
## Resultate anzeigen: ## Resultate anzeigen:
display_results(expr, fml, I, results); display_results(tree, I, results);
return; return;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -60,22 +60,23 @@ def getData():
I = eval(data['interpretation'] or '[]'); I = eval(data['interpretation'] or '[]');
return expr, I; return expr, I;
def display_results(expr: str, fml: SyntaxBaum, I: List[str], results: dict): def display_results(tree: SyntaxBaum, I: List[str], results: dict):
print(dedent( print(dedent(
''' '''
Syntaxbaum von Syntaxbaum von
F := \033[92;1m{F}\033[0m: F := \033[92;1m{expr}\033[0m:
'''.format(F=fml) '''.format(expr=tree.expr)
)); ));
print(fml.pretty()); print(tree.pretty(' '))
print(dedent( print(dedent(
''' '''
Für I = [{I}] und F wie oben gilt Für I = [{I}] und F wie oben gilt
eval(F, I) = \033[94;1m{eval}\033[0m, eval(F, I) = \033[94;1m{eval}\033[0m,
\033[2matoms(F) = \033[94;1m{atoms}\033[0m; \033[91;1m<- *\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[2mdepth(F) = \033[94;1m{depth}\033[0m; \033[91;1m<- *\033[0m
\033[2mlength(F) = \033[94;1m{l}\033[0m; \033[91;1m<- *\033[0m \033[2mlength(F) = \033[94;1m{length}\033[0m; \033[91;1m<- *\033[0m
\033[2m#parentheses(F) = \033[94;1m{p}\033[0m; \033[91;1m<- *\033[0m \033[2m#parentheses(F) = \033[94;1m{nParentheses}\033[0m; \033[91;1m<- *\033[0m
\033[91;1m*\033[0m \033[2mnoch nicht implementiert!\033[0m \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 \033[1;2;4mChallenge:\033[0m \033[2mschreibe diese Methoden! (siehe README.md)\033[0m

View File

@ -18,7 +18,7 @@ function check_requirements() {
function run_unittests(){ function run_unittests(){
echo -e "\033[1mUNITTESTS\033[0m\n"; echo -e "\033[1mUNITTESTS\033[0m\n";
local output="$(call_python -m unittest discover -v --top-level-directory "." --start-directory "utests" --pattern "u_*.py" 2>&1)"; local output="$(call_python -m unittest discover -v --top-level-directory "." --start-directory "utests" --pattern "*_test.py" 2>&1)";
echo -e "$output"; echo -e "$output";
if ( echo "$output" | grep -E -q "^[[:space:]]*(FAIL:|FAILED)" ); then if ( echo "$output" | grep -E -q "^[[:space:]]*(FAIL:|FAILED)" ); then
echo -e "[\033[91;1mERROR\033[0m] Unit tests versagt!" && return 1; echo -e "[\033[91;1mERROR\033[0m] Unit tests versagt!" && return 1;

View File

@ -9,11 +9,11 @@ import unittest;
from unittest import TestCase; from unittest import TestCase;
from aussagenlogik.schema import stringToSyntaxbaum; from aussagenlogik.schema import stringToSyntaxbaum;
from aussagenlogik.rekursion import rekursiv_atoms; from aussagenlogik.rekursion import rekursivEval;
from aussagenlogik.rekursion import rekursiv_depth; from aussagenlogik.rekursion import rekursivAtoms;
from aussagenlogik.rekursion import rekursiv_length; from aussagenlogik.rekursion import rekursivDepth;
from aussagenlogik.rekursion import rekursiv_parentheses; from aussagenlogik.rekursion import rekursivLength;
from aussagenlogik.rekursion import rekursiv_eval; from aussagenlogik.rekursion import rekursivParentheses;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# GLOBALE KONSTANTEN # GLOBALE KONSTANTEN
@ -29,34 +29,34 @@ from aussagenlogik.rekursion import rekursiv_eval;
class TestRekursivEval(TestCase): class TestRekursivEval(TestCase):
def test_literale(self): def test_literale(self):
fml = stringToSyntaxbaum('A0'); fml = stringToSyntaxbaum('A0');
val = rekursiv_eval(fml, [ 'A0' ]); val = rekursivEval(fml, [ 'A0' ]);
assert val == 1; assert val == 1;
fml = stringToSyntaxbaum('A0'); fml = stringToSyntaxbaum('A0');
val = rekursiv_eval(fml, []); val = rekursivEval(fml, []);
assert val == 0; assert val == 0;
fml = stringToSyntaxbaum('! A0'); fml = stringToSyntaxbaum('! A0');
val = rekursiv_eval(fml, [ 'A0' ]); val = rekursivEval(fml, [ 'A0' ]);
assert val == 0; assert val == 0;
fml = stringToSyntaxbaum('! A0'); fml = stringToSyntaxbaum('! A0');
val = rekursiv_eval(fml, []); val = rekursivEval(fml, []);
assert val == 1; assert val == 1;
def test_complex1(self): def test_complex1(self):
fml = stringToSyntaxbaum('( ! A0 || (( A0 && A3 ) || A2 ))'); fml = stringToSyntaxbaum('( ! A0 || (( A0 && A3 ) || A2 ))');
val = rekursiv_eval(fml, [ 'A0', 'A2' ]); val = rekursivEval(fml, [ 'A0', 'A2' ]);
assert val == 1; assert val == 1;
val = rekursiv_eval(fml, [ 'A0', 'A3' ]); val = rekursivEval(fml, [ 'A0', 'A3' ]);
assert val == 1; assert val == 1;
val = rekursiv_eval(fml, [ 'A0' ]); val = rekursivEval(fml, [ 'A0' ]);
assert val == 0; assert val == 0;
val = rekursiv_eval(fml, [ 'A4', 'A8' ]); val = rekursivEval(fml, [ 'A4', 'A8' ]);
assert val == 1; assert val == 1;
def test_complex2(self): def test_complex2(self):
fml = stringToSyntaxbaum('( ! A0 || (( A0 && A3 ) || ! A2 ))'); fml = stringToSyntaxbaum('( ! A0 || (( A0 && A3 ) || ! A2 ))');
val = rekursiv_eval(fml, [ 'A0', 'A2' ]); val = rekursivEval(fml, [ 'A0', 'A2' ]);
assert val == 0; assert val == 0;
val = rekursiv_eval(fml, [ 'A0', 'A3' ]); val = rekursivEval(fml, [ 'A0', 'A3' ]);
assert val == 1; assert val == 1;
pass; pass;
@ -68,22 +68,22 @@ class TestRekursivEval(TestCase):
class TestRekursivAtoms(TestCase): class TestRekursivAtoms(TestCase):
def test_noduplicates(self): def test_noduplicates(self):
fml = stringToSyntaxbaum('( A4 && ( A4 || A4 ))'); fml = stringToSyntaxbaum('( A4 && ( A4 || A4 ))');
val = sorted(rekursiv_atoms(fml)); val = sorted(rekursivAtoms(fml));
assert len([_ for _ in val if _ == 'A4']) == 1, 'Atome dürfen nicht mehrfach vorkommen!'; assert len([_ for _ in val if _ == 'A4']) == 1, 'Atome dürfen nicht mehrfach vorkommen!';
def test_nononatoms(self): def test_nononatoms(self):
fml = stringToSyntaxbaum('( {F} || A3 )'); fml = stringToSyntaxbaum('( {F} || A3 )');
val = sorted(rekursiv_atoms(fml)); val = sorted(rekursivAtoms(fml));
assert 'F' not in val, 'Nichtatomare Formeln dürfen nicht vorkommen!'; assert 'F' not in val, 'Nichtatomare Formeln dürfen nicht vorkommen!';
def test_calc1(self): def test_calc1(self):
fml = stringToSyntaxbaum('A0'); fml = stringToSyntaxbaum('A0');
val = sorted(rekursiv_atoms(fml)); val = sorted(rekursivAtoms(fml));
assert val == ['A0'], 'computed {}'.format(val); assert val == ['A0'], 'computed {}'.format(val);
def test_calc2(self): def test_calc2(self):
fml = stringToSyntaxbaum('((( ! A0 && A3 ) || A4 ) && A8 )'); fml = stringToSyntaxbaum('((( ! A0 && A3 ) || A4 ) && A8 )');
val = sorted(rekursiv_atoms(fml)); val = sorted(rekursivAtoms(fml));
assert val == ['A0', 'A3', 'A4', 'A8'], 'computed {}'.format(val); assert val == ['A0', 'A3', 'A4', 'A8'], 'computed {}'.format(val);
pass; pass;
@ -95,27 +95,27 @@ class TestRekursivAtoms(TestCase):
class TestRekursivDepth(TestCase): class TestRekursivDepth(TestCase):
def test_calc1(self): def test_calc1(self):
fml = stringToSyntaxbaum('A0'); fml = stringToSyntaxbaum('A0');
val = rekursiv_depth(fml); val = rekursivDepth(fml);
assert val == 0, 'computed {}'.format(val); assert val == 0, 'computed {}'.format(val);
def test_calc2(self): def test_calc2(self):
fml = stringToSyntaxbaum('!! A8'); fml = stringToSyntaxbaum('!! A8');
val = rekursiv_depth(fml); val = rekursivDepth(fml);
assert val == 2, 'computed {}'.format(val); assert val == 2, 'computed {}'.format(val);
def test_calc3(self): def test_calc3(self):
fml = stringToSyntaxbaum('( ! A0 && A3 )'); fml = stringToSyntaxbaum('( ! A0 && A3 )');
val = rekursiv_depth(fml); val = rekursivDepth(fml);
assert val == 2, 'computed {}'.format(val); assert val == 2, 'computed {}'.format(val);
def test_calc4(self): def test_calc4(self):
fml = stringToSyntaxbaum('((( ! A0 && A3 ) || A4 ) && A8 )'); fml = stringToSyntaxbaum('((( ! A0 && A3 ) || A4 ) && A8 )');
val = rekursiv_depth(fml); val = rekursivDepth(fml);
assert val == 4, 'computed {}'.format(val); assert val == 4, 'computed {}'.format(val);
def test_calc5(self): def test_calc5(self):
fml = stringToSyntaxbaum('! ((( ! A0 && A3 ) || A4 ) && A8 )'); fml = stringToSyntaxbaum('! ((( ! A0 && A3 ) || A4 ) && A8 )');
val = rekursiv_depth(fml); val = rekursivDepth(fml);
assert val == 5, 'computed {}'.format(val); assert val == 5, 'computed {}'.format(val);
pass; pass;
@ -127,27 +127,27 @@ class TestRekursivDepth(TestCase):
class TestRekursivLength(TestCase): class TestRekursivLength(TestCase):
def test_calc1(self): def test_calc1(self):
fml = stringToSyntaxbaum('A0'); fml = stringToSyntaxbaum('A0');
val = rekursiv_length(fml); val = rekursivLength(fml);
assert val == 1, 'computed {}'.format(val); assert val == 1, 'computed {}'.format(val);
def test_calc2(self): def test_calc2(self):
fml = stringToSyntaxbaum('!! A8'); fml = stringToSyntaxbaum('!! A8');
val = rekursiv_length(fml); val = rekursivLength(fml);
assert val == 3, 'computed {}'.format(val); assert val == 3, 'computed {}'.format(val);
def test_calc3(self): def test_calc3(self):
fml = stringToSyntaxbaum('( ! A0 && A3 )'); fml = stringToSyntaxbaum('( ! A0 && A3 )');
val = rekursiv_length(fml); val = rekursivLength(fml);
assert val == 4, 'computed {}'.format(val); assert val == 4, 'computed {}'.format(val);
def test_calc4(self): def test_calc4(self):
fml = stringToSyntaxbaum('((( ! A0 && A3 ) || A4 ) && A8 )'); fml = stringToSyntaxbaum('((( ! A0 && A3 ) || A4 ) && A8 )');
val = rekursiv_length(fml); val = rekursivLength(fml);
assert val == 8, 'computed {}'.format(val); assert val == 8, 'computed {}'.format(val);
def test_calc5(self): def test_calc5(self):
fml = stringToSyntaxbaum('! ((( ! A0 && A3 ) || A4 ) && A8 )'); fml = stringToSyntaxbaum('! ((( ! A0 && A3 ) || A4 ) && A8 )');
val = rekursiv_length(fml); val = rekursivLength(fml);
assert val == 9, 'computed {}'.format(val); assert val == 9, 'computed {}'.format(val);
pass; pass;
@ -159,26 +159,26 @@ class TestRekursivLength(TestCase):
class TestRekursivParentheses(TestCase): class TestRekursivParentheses(TestCase):
def test_calc1(self): def test_calc1(self):
fml = stringToSyntaxbaum('A0'); fml = stringToSyntaxbaum('A0');
val = rekursiv_parentheses(fml); val = rekursivParentheses(fml);
assert val == 0, 'computed {}'.format(val); assert val == 0, 'computed {}'.format(val);
def test_calc2(self): def test_calc2(self):
fml = stringToSyntaxbaum('!! A8'); fml = stringToSyntaxbaum('!! A8');
val = rekursiv_parentheses(fml); val = rekursivParentheses(fml);
assert val == 0, 'computed {}'.format(val); assert val == 0, 'computed {}'.format(val);
def test_calc3(self): def test_calc3(self):
fml = stringToSyntaxbaum('( ! A0 && A3 )'); fml = stringToSyntaxbaum('( ! A0 && A3 )');
val = rekursiv_parentheses(fml); val = rekursivParentheses(fml);
assert val == 2, 'computed {}'.format(val); assert val == 2, 'computed {}'.format(val);
def test_calc4(self): def test_calc4(self):
fml = stringToSyntaxbaum('((( ! A0 && A3 ) || A4 ) && A8 )'); fml = stringToSyntaxbaum('((( ! A0 && A3 ) || A4 ) && A8 )');
val = rekursiv_parentheses(fml); val = rekursivParentheses(fml);
assert val == 6, 'computed {}'.format(val); assert val == 6, 'computed {}'.format(val);
def test_calc5(self): def test_calc5(self):
fml = stringToSyntaxbaum('! ((( ! A0 && A3 ) || A4 ) && A8 )'); fml = stringToSyntaxbaum('! ((( ! A0 && A3 ) || A4 ) && A8 )');
val = rekursiv_parentheses(fml); val = rekursivParentheses(fml);
assert val == 6, 'computed {}'.format(val); assert val == 6, 'computed {}'.format(val);
pass; pass;

19
codego/.gitignore vendored
View File

@ -3,8 +3,18 @@
!/data.env !/data.env
!/README.md !/README.md
!/build.sh
!/test.sh ## Scripts
!/scripts
!/scripts/requirements
!/scripts/build.sh
!/scripts/build.sh
!/scripts/test.sh
## Für Erzeugung von Grammatiken:
!/grammars
!/grammars/README.md
!/grammars/*.g4
## Go Source ## Go Source
!/aussagenlogik !/aussagenlogik
@ -14,11 +24,6 @@
!/core !/core
!/core/environment !/core/environment
!/core/utils !/core/utils
!/grammars
!/**/*.go !/**/*.go
!/go.mod !/go.mod
!/go.sum !/go.sum
## Für Erzeugung von Grammatiken:
!/grammars/README.md
!/grammars/*.g4

View File

@ -6,24 +6,30 @@ Diese dienen nur zur Demonstration / konkreten Verwirklichung von Verfahren, die
Der Gebrauch dieser Skripte unterliegt der Eigenverantwortung von Studierenden. Der Gebrauch dieser Skripte unterliegt der Eigenverantwortung von Studierenden.
Da ich kein Informatiker bin, sind auch einige Aspekt bestimmt nicht optimal programmiert/strukturiert. Da ich kein Informatiker bin,
sind auch einige Aspekt bestimmt nicht optimal programmiert/strukturiert.
Dafür kann jeder in seiner Kopie einfach alles anpassen.
Das hier soll einfach funktionieren.
## Systemvoraussetzungen ## ## Systemvoraussetzungen ##
- bash (auch bash-for-windows). - bash (auch bash-for-windows).
- golang (mind. 1.6.x) - golang (mind. 1.6.x)
- Java11 - Java8+
Um Schemata in Lexer und Parser zu verwandeln, wird **ANTLR4** gebraucht,
(welches wiederum mithilfe eines Java-Archives kompiliert wird, weshalb man Java benötigt).
Siehe
<https://blog.gopheracademy.com/advent-2017/parsing-with-antlr4-and-go/>
für weitere Informationen dazu.
Dieses Projekt macht von ANTLR4 Gebrauch, um Schemata in Lexer und Parser zu verwandeln. Siehe <https://blog.gopheracademy.com/advent-2017/parsing-with-antlr4-and-go/> für mehr Informationen dazu.
## Voreinstellungen ## ## Voreinstellungen ##
- In einer bash-console zu diesem Ordner navigieren und folgenden Befehl ausführen: - In einer bash-console zu diesem Ordner navigieren und folgenden Befehl ausführen:
```bash ```bash
chmod +x build.sh test.sh chmod +x scripts/*.sh
## oder
chmod +x *.sh
``` ```
- In `build.sh` gibt es eine Zeile, die zur Kompilierung des Go-Projektes notwendigen Module über **go** installieren lässt. - In `scripts/build.sh` gibt es eine Zeile, die zur Kompilierung des Go-Projektes notwendigen Module über **go** installieren lässt.
(Die Liste der Packages findet man in der Datei `requirements`). (Die Liste der Packages findet man in der Datei `requirements`).
Diese Zeile kann man ruhig nach der ersten Ausführung rauskommentieren. Diese Zeile kann man ruhig nach der ersten Ausführung rauskommentieren.
- Dazu kommt, dass **antlr4.jar** heruntergeladen wird. - Dazu kommt, dass **antlr4.jar** heruntergeladen wird.
@ -37,7 +43,7 @@ In `data.env` kann man Daten (aktuell: auszuwertenden Ausdruck + Interpretation/
In einer bash-console zu diesem Ordner navigieren und In einer bash-console zu diesem Ordner navigieren und
```bash ```bash
./build.sh source scripts/build.sh
## oder ## oder
go build main.go && ./main go build main.go && ./main
``` ```
@ -72,14 +78,20 @@ Wer etwas standardisierter seine Methoden testen will, kann automatisiertes Test
- In der Console (wenn noch nicht geschehen) folgenden Befehl einmalig ausführen: - In der Console (wenn noch nicht geschehen) folgenden Befehl einmalig ausführen:
```bash ```bash
chmod +x test.sh chmod +x scripts/test.sh
``` ```
- In `aussagenlogik/rekursion/rekursion_test.go` beim relevanten Testteil eine oder mehrere der Zeilen - In `aussagenlogik/rekursion/rekursion_test.go` beim relevanten Testteil eine oder mehrere der Zeilen
```go ```go
test.Skip("Methode noch nicht implementiert") test.Skip("Methode noch nicht implementiert")
``` ```
rauskommentieren/löschen. rauskommentieren/löschen.
- Jetzt
```bash
source scripts/test.sh
```
ausführen.
Jetzt `test.sh` ausführen. Die unittests testen Methoden gegen mehrere vorkonstruierte Testfälle samt erwarteten Ergebnissen geprüft. Sollten einige Tests scheitern, dann Fehlermeldung durchlesen, und Methode entsprechend der Kritik überarbeiten. Die unittests testen Methoden gegen mehrere vorkonstruierte Testfälle samt erwarteten Ergebnissen geprüft.
Sollten einige Tests scheitern, dann Fehlermeldung durchlesen, und Methode entsprechend der Kritik überarbeiten.
Die geschriebenen Unittests sind natürlich nicht ausführlich. Man kann diese nach Bedarf ergänzen. Am sinnvollsten baut man welche, die wirklich scheitern können, sonst sagen die Tests nichts aus. Die geschriebenen Unittests sind natürlich nicht ausführlich. Man kann diese nach Bedarf ergänzen. Am sinnvollsten baut man welche, die wirklich scheitern können, sonst sagen die Tests nichts aus.

View File

@ -17,7 +17,7 @@ function call_go() {
function check_requirements() { function check_requirements() {
[ -f "go.sum" ] && rm "go.sum"; [ -f "go.sum" ] && rm "go.sum";
call_go get "$( cat requirements )"; call_go get "$( cat scripts/requirements )";
} }
function get_antlr() { function get_antlr() {
@ -37,6 +37,7 @@ function precompile_grammars() {
local name; local name;
! [ -f "grammars/antlr.jar" ] && get_antlr; # <- lädt antl.jar herunter, wenn fehlt ! [ -f "grammars/antlr.jar" ] && get_antlr; # <- lädt antl.jar herunter, wenn fehlt
pushd grammars >> $NULL; pushd grammars >> $NULL;
rm -rf .antlr; # <- wird automatisch erzeugt, aber wir brauche dies nicht
while read fname; do while read fname; do
( [ "$fname" == "" ] || ! [ -f "$fname" ] ) && continue; ( [ "$fname" == "" ] || ! [ -f "$fname" ] ) && continue;
name="$( echo "$fname" | sed -E "s/^(.*)\.g4$/\1/g" )"; name="$( echo "$fname" | sed -E "s/^(.*)\.g4$/\1/g" )";
@ -50,11 +51,14 @@ function compile_programme() {
[ -f "main" ] && rm "main"; [ -f "main" ] && rm "main";
echo -e "\033[92;1mGO\033[0m kompiliert \033[1mmain.go\033[0m"; echo -e "\033[92;1mGO\033[0m kompiliert \033[1mmain.go\033[0m";
call_go build "main.go"; call_go build "main.go";
! [ -f "main" ] && exit 1;
! [ -d "dist" ] && mkdir "dist";
mv "main" "dist";
} }
function run_programme() { function run_programme() {
echo -e "\033[92;1mGO\033[0m kompiliertes Programm wird ausgeführt"; echo -e "\033[92;1mGO\033[0m kompiliertes Programm wird ausgeführt";
./main; ./dist/main;
} }
################################ ################################

View File

@ -0,0 +1,5 @@
github.com/joho/godotenv@v1.3.0
github.com/lithammer/dedent
github.com/antlr/antlr4/runtime/Go/antlr
github.com/stretchr/testify
golang.org/x/tools

View File

@ -17,7 +17,7 @@ function call_go() {
function check_requirements() { function check_requirements() {
[ -f "go.sum" ] && rm "go.sum"; [ -f "go.sum" ] && rm "go.sum";
call_go get "$( cat requirements )"; call_go get "$( cat scripts/requirements )";
} }
function run_unittests(){ function run_unittests(){