master > master: struktur + unit tests

This commit is contained in:
RD 2021-05-06 12:44:29 +02:00
parent 900befbb4e
commit 3c2e48ccde
12 changed files with 442 additions and 91 deletions

8
code/.gitignore vendored
View File

@ -1,6 +1,12 @@
*
!/.gitignore
!/*.py
!/README.md
!/run.sh
!/test.sh
!/data.env
!/requirements
!/aussagenlogik
!/utests
!/**/*.py

65
code/README.md Normal file
View File

@ -0,0 +1,65 @@
# Code #
Die Inhalte dieses Ordners sind absolut **kein Pflichtbestandteil** des Kurses.
Diese dienen nur zur Demonstration / konkreten Verwirklichung von Verfahren,
die im Kurs auftauchen. Für Wissbegierige mit auch grundlegenden Programmierkenntnissen bietet sich dies als Möglichkeit, um sich selbst zu überzeugen, dass strukturelle Rekursion funktioniert.
Der Gebrauch dieser Skripte unterliegt der Eigenverantwortung von Studierenden.
Da ich kein Informatiker bin, sind auch einige Aspekt bestimmt nicht optimal programmiert/strukturiert.
## Systemvoraussetzungen ##
- bash (auch bash-for-windows).
- python (mind. 3.9.x)
## Voreinstellungen ##
- In einer bash-console zu diesem Ordner navigieren und folgenden Befehl ausführen:
```bash
chmod +x run.sh test.sh
## oder
chmod +x *.sh
```
- In `run.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 ##
In `data.env` kann man Daten (aktuell: auszuwertenden Ausdruck + Interpretation/Modell) eintragen. Man beachte dabei die Syntax.
## Gebrauchshinweise ##
In einer bash-console zu diesem Ordner navigieren und
```bash
./run.sh
## oder (für Linux)
python3 main.py
## oder (für Windows)
py -3 main.py
```
ausführen.
Man kann natürlich alles ohne bash machen, wenn man PyCharm o.Ä. besitzt.
## Offene Challenges ##
In der Datei `aussagenlogik/rekursion.py` (relativ zu diesem Ordner) findet man mehrere leere Methoden (mit dem Kommentar `## Herausforderung...`). Wer es mag, kann versuchen, an seinem Rechner diese Methoden zu definieren und auszuprobieren.
## Händisch testen ###
Probiere es mit Stift-und-Zettel und anhand von Beispielen die Werte händisch zu berechnen. Vergleiche dies mit den durch den Code rekursiv berechneten Werten. Stimmt alles überein?
### Automatisierte Tests ###
Wer etwas standardisierter seine Methoden testen will, kann automatisiertes Testing tätigen. Diese Tests sind im Unterordner `utests` eingetragen.
- In der Console
- In `utests/u_rekursion.py` beim relevanten Testteil eine oder mehrere der Zeilen
```python
@unittest.skip('Methode noch nicht implementiert')
```
rauskommentieren/löschen.
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 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

@ -0,0 +1,79 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from __future__ import annotations;
from lark import Tree;
from typing import List;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# GLOBALE KONSTANTEN
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# METHODEN
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def rekursiv_atoms(fml: Tree) -> List[str]:
## Herausforderung: schreibe diese Funktion!
return [];
def rekursiv_depth(fml: Tree) -> int:
## Herausforderung: schreibe diese Funktion!
return 0;
def rekursiv_length(fml: Tree) -> int:
## Herausforderung: schreibe diese Funktion!
return 0;
def rekursiv_parentheses(fml: Tree) -> int:
## Herausforderung: schreibe diese Funktion!
return 0
def rekursiv_eval(fml: Tree, I: List[str]) -> int:
teilfml = getTeilformeln(fml);
if fml.data in ['atom', 'beliebig']:
name = getText(fml);
return 1 if (name in I) else 0;
elif fml.data == 'wahr':
return 1;
elif fml.data == 'falsch':
return 0;
elif fml.data == 'negation':
val1 = rekursiv_eval(teilfml[0], I);
return 1 - val1;
elif fml.data == 'konjunktion':
val1 = rekursiv_eval(teilfml[0], I);
val2 = rekursiv_eval(teilfml[1], I);
return min(val1, val2);
elif fml.data == 'disjunktion':
val1 = rekursiv_eval(teilfml[0], I);
val2 = rekursiv_eval(teilfml[1], I);
return max(val1, val2);
elif fml.data == 'implikation':
val1 = rekursiv_eval(teilfml[0], I);
val2 = rekursiv_eval(teilfml[1], I);
return 0 if val1 == 1 and val2 == 0 else 1;
raise Exception('Evaluation nicht möglich!');
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# HILFSMETHODEN
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def getText(fml: Tree) -> str:
text = fml.children[0];
if isinstance(text, str):
return text;
raise Exception('Konnte Textinhalt nicht ablesen!');
def getTeilformeln(fml: Tree) -> List[Tree]:
return [
part for part in fml.children
if isinstance(part, Tree)
and not part.data == 'junktor'
];

View File

@ -13,7 +13,7 @@ from lark import Lark;
from lark import Tree;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# GLOBAL CONSTANTS
# GLOBALE KONSTANTEN
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## lexer

9
code/data.env Normal file
View File

@ -0,0 +1,9 @@
# expr = "A0"
# expr = "! A0"
# expr = "( A0 && A1 )"
# expr = "( A0 || A1 )"
# expr = "( A0 -> A1 )"
expr = "( A0 -> ((A0 && A3) || ! A2) )"
# expr = "( A0 -> ((A0 && A3) || A2) )"
# expr = "(( {G} || !{G} ) -> A5)"
interpretation = "[ 'A0', 'A2' ]"

View File

@ -8,43 +8,65 @@
from __future__ import annotations;
import os;
import sys;
sys.tracebacklimit = 0;
# sys.tracebacklimit = 0;
from dotenv import dotenv_values;
from lark import Tree;
from textwrap import dedent;
from typing import List;
sys.path.insert(0, os.getcwd());
from schema import string_to_parts;
from aussagenlogik.schema import string_to_parts;
from aussagenlogik.rekursion import rekursiv_eval;
from aussagenlogik.rekursion import rekursiv_atoms;
from aussagenlogik.rekursion import rekursiv_depth;
from aussagenlogik.rekursion import rekursiv_length;
from aussagenlogik.rekursion import rekursiv_parentheses;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# GLOBAL CONSTANTS
# GLOBALE KONSTANTEN
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# zeichenkette = 'A0';
# zeichenkette = '! A0';
# zeichenkette = '( A0 && A1 )';
# zeichenkette = '( A0 || A1 )';
# zeichenkette = '( A0 -> A1 )';
zeichenkette = '( A0 -> ((A0 && A3) || ! A2) )';
# zeichenkette = '( A0 -> ((A0 && A3) || A2) )';
# zeichenkette = '(( {G} || !{G} ) -> A5)';
I = ['A0', 'A2'];
DATA_ENV = "data.env"; # Pfad zu Daten.
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# HAUPTVORGANG
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def main():
tree = string_to_parts(zeichenkette);
## Daten einlesen:
expr, I = getData();
## Formel in Teilformeln zerlegen:
tree = string_to_parts(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),
);
## Resultate anzeigen:
display_results(expr, tree, I, results);
return;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# SONSTIGE METHODEN
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def getData():
data = dotenv_values(dotenv_path=DATA_ENV);
expr = data['expr'] or '0';
I = eval(data['interpretation'] or '[]');
return expr, I;
def display_results(expr: str, tree: Tree, I: List[str], results: dict):
print(dedent(
'''
Syntaxbaum von
F := \033[92;1m{F}\033[0m:
'''.format(
F=zeichenkette,
)
'''.format(F=expr)
));
print(tree.pretty());
print(dedent(
@ -56,74 +78,11 @@ def main():
\033[2m#parentheses(F) = \033[94;1m{p}\033[0m; \033[91;1m<- *\033[0m
\033[91;1m*\033[0m \033[2mnoch nicht implementiert!\033[0m
\033[1;2;4mChallenge:\033[0m \033[2mschreibe diese Methoden. Probiere mit Stift-und-Zettel die Methoden händisch auszuführen und vergleiche mit dem Code-Output.\033[0m
'''.format(
eval = rekursiv_eval(tree, I),
atoms = rekursiv_atoms(tree),
d = rekursiv_depth(tree),
l = rekursiv_length(tree),
p = rekursiv_parentheses(tree),
)
\033[1;2;4mChallenge:\033[0m \033[2mschreibe diese Methoden! (siehe README.md)\033[0m
'''.format(**results)
));
return;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# SEKUNDÄRVORGÄNGE
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def rekursiv_atoms(fml: Tree) -> List[str]:
## Herausforderung: schreibe diese Funktion!
return [];
def rekursiv_depth(fml: Tree) -> int:
## Herausforderung: schreibe diese Funktion!
return 0;
def rekursiv_length(fml: Tree) -> int:
## Herausforderung: schreibe diese Funktion!
return 0;
def rekursiv_parentheses(fml: Tree) -> int:
## Herausforderung: schreibe diese Funktion!
return 0;
def rekursiv_eval(fml: Tree, I: List[str]) -> int:
teilfml = getTeilformeln(fml);
if fml.data in ['atom', 'beliebig']:
name = fml.children[0];
return 1 if (name in I) else 0;
elif fml.data == 'wahr':
return 1;
elif fml.data == 'falsch':
return 0;
elif fml.data == 'negation':
val1 = rekursiv_eval(teilfml[0], I);
return 1 - val1;
elif fml.data == 'konjunktion':
val1 = rekursiv_eval(teilfml[0], I);
val2 = rekursiv_eval(teilfml[1], I);
return min(val1, val2);
elif fml.data == 'disjunktion':
val1 = rekursiv_eval(teilfml[0], I);
val2 = rekursiv_eval(teilfml[1], I);
return max(val1, val2);
elif fml.data == 'implikation':
val1 = rekursiv_eval(teilfml[0], I);
val2 = rekursiv_eval(teilfml[1], I);
return 0 if val1 == 1 and val2 == 0 else 1;
raise Exception('Evaluation nicht möglich!');
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# SONSTIGE METHODEN
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def getTeilformeln(fml: Tree) -> List[Tree]:
return [
part for part in fml.children
if isinstance(part, Tree)
and not part.data == 'junktor'
];
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# CODE AUSFÜHREN
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -1,4 +1,5 @@
lark
lark-parser
lark-parser[regex]
python-dotenv
typing

View File

@ -1,23 +1,31 @@
#!/usr/bin/env bash
################################################################################################
# NOTE: `chmod +x *.sh` vorher ausführen, um dieses Skript benutzen zu können.
# NOTE: `chmod +x run.sh` vorher ausführen, um dieses Skript benutzen zu können.
################################################################################################
function run_python() {
################################
# HILFSMETHODEN
################################
function call_python() {
[ "$OSTYPE" == "msys" ] && py -3 $@ || python3 $@;
}
function run_pip() {
run_python -m pip $@;
function run_check_requirements() {
call_python -m pip install "$( cat requirements )" >> /dev/null;
}
function run_check_requirements() {
run_pip install "$( cat requirements )" >> /dev/null;
function run_code() {
call_python main.py;
}
################################
# HAUPTVORGÄNGE
################################
# Kann auskommentiert werden, wenn nötige Module schon installiert:
run_check_requirements;
# Code ausführen:
run_python main.py;
run_code

38
code/test.sh Executable file
View File

@ -0,0 +1,38 @@
#!/usr/bin/env bash
################################################################################################
# NOTE: `chmod +x test.sh` vorher ausführen, um dieses Skript benutzen zu können.
################################################################################################
################################
# HILFSMETHODEN
################################
function call_python() {
[ "$OSTYPE" == "msys" ] && py -3 $@ || python3 $@;
}
function run_check_requirements() {
call_python -m pip install "$( cat requirements )" >> /dev/null;
}
function run_unittests(){
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)";
echo -e "$output";
if ( echo "$output" | grep -E -q "^[[:space:]]*(FAIL:|FAILED)" ); then
echo -e "[\033[91;1mERROR\033[0m] Unit tests versagt!" && return 1;
else
echo -e "[\033[94;1mINFO\033[0m] Unit tests erfolgreich!" && return 0;
fi
}
################################
# HAUPTVORGÄNGE
################################
# Kann auskommentiert werden, wenn nötige Module schon installiert:
run_check_requirements;
# Code testen (unittests):
run_unittests;

0
code/utests/__init__.py Normal file
View File

186
code/utests/u_rekursion.py Normal file
View File

@ -0,0 +1,186 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
import unittest;
from unittest import TestCase;
from aussagenlogik.schema import string_to_parts;
from aussagenlogik.rekursion import rekursiv_atoms;
from aussagenlogik.rekursion import rekursiv_depth;
from aussagenlogik.rekursion import rekursiv_length;
from aussagenlogik.rekursion import rekursiv_parentheses;
from aussagenlogik.rekursion import rekursiv_eval;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# GLOBALE KONSTANTEN
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# TESTFALL Atome(·)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@unittest.skip("Methode noch nicht implementiert")
class TestRekursivAtoms(TestCase):
def test_noduplicates(self):
fml = string_to_parts('( A4 && ( A4 || A4 ) )');
val = sorted(rekursiv_atoms(fml));
assert len([_ for _ in val if _ == 'A4']) == 1, 'Atome dürfen nicht mehrfach vorkommen!';
def test_nononatoms(self):
fml = string_to_parts('( {F} || A3 )');
val = sorted(rekursiv_atoms(fml));
assert 'F' not in val, 'Nichtatomare Formeln dürfen nicht vorkommen!';
def test_calc1(self):
fml = string_to_parts('A0');
val = sorted(rekursiv_atoms(fml));
assert val == ['A0'], 'computed {}'.format(val);
def test_calc2(self):
fml = string_to_parts('((( ! A0 && A3 ) || A4 ) && A8 )');
val = sorted(rekursiv_atoms(fml));
assert val == ['A0', 'A3', 'A4', 'A8'], 'computed {}'.format(val);
pass;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# TESTFALL Depth(·)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@unittest.skip("Methode noch nicht implementiert")
class TestRekursivDepth(TestCase):
def test_calc1(self):
fml = string_to_parts('A0');
val = rekursiv_depth(fml);
assert val == 0, 'computed {}'.format(val);
def test_calc2(self):
fml = string_to_parts('!! A8');
val = rekursiv_depth(fml);
assert val == 2, 'computed {}'.format(val);
def test_calc3(self):
fml = string_to_parts('(! A0 && A3 )');
val = rekursiv_depth(fml);
assert val == 2, 'computed {}'.format(val);
def test_calc4(self):
fml = string_to_parts('((( ! A0 && A3 ) || A4 ) && A8 )');
val = rekursiv_depth(fml);
assert val == 4, 'computed {}'.format(val);
def test_calc5(self):
fml = string_to_parts('! ((( ! A0 && A3 ) || A4 ) && A8 )');
val = rekursiv_depth(fml);
assert val == 5, 'computed {}'.format(val);
pass;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# TESTFALL Länge(·)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@unittest.skip("Methode noch nicht implementiert")
class TestRekursivLength(TestCase):
def test_calc1(self):
fml = string_to_parts('A0');
val = rekursiv_length(fml);
assert val == 1, 'computed {}'.format(val);
def test_calc2(self):
fml = string_to_parts('!! A8');
val = rekursiv_length(fml);
assert val == 3, 'computed {}'.format(val);
def test_calc3(self):
fml = string_to_parts('(! A0 && A3 )');
val = rekursiv_length(fml);
assert val == 4, 'computed {}'.format(val);
def test_calc4(self):
fml = string_to_parts('((( ! A0 && A3 ) || A4 ) && A8 )');
val = rekursiv_length(fml);
assert val == 8, 'computed {}'.format(val);
def test_calc5(self):
fml = string_to_parts('! ((( ! A0 && A3 ) || A4 ) && A8 )');
val = rekursiv_length(fml);
assert val == 9, 'computed {}'.format(val);
pass;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# TESTFALL Anzahl Klammern(·)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@unittest.skip("Methode noch nicht implementiert")
class TestRekursivParentheses(TestCase):
def test_calc1(self):
fml = string_to_parts('A0');
val = rekursiv_parentheses(fml);
assert val == 0, 'computed {}'.format(val);
def test_calc2(self):
fml = string_to_parts('!! A8');
val = rekursiv_parentheses(fml);
assert val == 0, 'computed {}'.format(val);
def test_calc3(self):
fml = string_to_parts('(! A0 && A3 )');
val = rekursiv_parentheses(fml);
assert val == 2, 'computed {}'.format(val);
def test_calc4(self):
fml = string_to_parts('((( ! A0 && A3 ) || A4 ) && A8 )');
val = rekursiv_parentheses(fml);
assert val == 6, 'computed {}'.format(val);
def test_calc5(self):
fml = string_to_parts('! ((( ! A0 && A3 ) || A4 ) && A8 )');
val = rekursiv_parentheses(fml);
assert val == 6, 'computed {}'.format(val);
pass;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# TESTFALL eval(·, ·)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# @unittest.skip("Methode noch nicht implementiert")
class TestRekursivEval(TestCase):
def test_literale(self):
fml = string_to_parts('A0');
val = rekursiv_eval(fml, [ 'A0' ]);
assert val == 1;
fml = string_to_parts('A0');
val = rekursiv_eval(fml, []);
assert val == 0;
fml = string_to_parts('! A0');
val = rekursiv_eval(fml, [ 'A0' ]);
assert val == 0;
fml = string_to_parts('! A0');
val = rekursiv_eval(fml, []);
assert val == 1;
def test_complex1(self):
fml = string_to_parts('( ! A0 || (( A0 && A3 ) || A2 ) )');
val = rekursiv_eval(fml, [ 'A0', 'A2' ]);
assert val == 1;
val = rekursiv_eval(fml, [ 'A0', 'A3' ]);
assert val == 1;
val = rekursiv_eval(fml, [ 'A0' ]);
assert val == 0;
val = rekursiv_eval(fml, [ 'A4', 'A8' ]);
assert val == 1;
def test_complex2(self):
fml = string_to_parts('( ! A0 || (( A0 && A3 ) || ! A2) )');
val = rekursiv_eval(fml, [ 'A0', 'A2' ]);
assert val == 0;
val = rekursiv_eval(fml, [ 'A0', 'A3' ]);
assert val == 1;
pass;