diff --git a/codego/.gitignore b/codego/.gitignore new file mode 100644 index 0000000..38df4b3 --- /dev/null +++ b/codego/.gitignore @@ -0,0 +1,25 @@ +* +!/.gitignore + +!/data.env +!/README.md +!/run.sh +!/test.sh + +## Go Source +!/aussagenlogik +!/aussagenlogik/rekursion +!/aussagenlogik/schema +!/aussagenlogik/syntaxbaum +!/core +!/core/environment +!/core/utils +!/grammars +!/**/*.go +!/go.mod +!/go.sum + +## Für Erzeugung von Grammatiken: +!/grammars/antlr.jar +!/grammars/README.md +!/grammars/*.g4 diff --git a/codego/README.md b/codego/README.md new file mode 100644 index 0000000..9fb1b5e --- /dev/null +++ b/codego/README.md @@ -0,0 +1,64 @@ +# Code in golang # + +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 an, 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). +- golang (mind. 1.6.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 Kompilierung des Go-Projektes notwendigen Module über **go** 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 +go build main.go && ./main +``` +ausführen. + +## Offene Challenges ## + +In der Datei `aussagenlogik/rekursion.go` (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 existieren parallel zu jedem Modul und folgen dem Namensschema `..._test.go`. + +- In der Console (wenn noch nicht geschehen) folgenden Befehl einmalig ausführen: + ```bash + chmod +x test.sh + ``` +- In `aussagenlogik/rekursion/rekursion_test.go` beim relevanten Testteil eine oder mehrere der Zeilen + ```go + test.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. diff --git a/codego/aussagenlogik/rekursion/rekursion.go b/codego/aussagenlogik/rekursion/rekursion.go new file mode 100644 index 0000000..7164228 --- /dev/null +++ b/codego/aussagenlogik/rekursion/rekursion.go @@ -0,0 +1,82 @@ +package rekursion + +import ( + "log" + "logik/aussagenlogik/syntaxbaum" + "logik/core/utils" +) + +/* ---------------------------------------------------------------- * + * EXPORTS + * ---------------------------------------------------------------- */ + +func RekursivEval(tree syntaxbaum.SyntaxBaum, I []string) int { + var children = tree.GetChildren() + switch tree.Kind { + case "atom", "generic": + if utils.StrListContains(I, tree.Expr) { + return 1 + } + return 0 + case "taut": + return 1 + case "contradiction": + return 0 + case "not": + subtree0, _ := tree.GetChild() + val0 := RekursivEval(subtree0, I) + return 1 - val0 + case "and2": + val0 := RekursivEval(children[0], I) + val1 := RekursivEval(children[1], I) + return utils.Min2(val0, val1) + case "and": + var val = 1 + for _, subtree := range children { + var val_ = RekursivEval(subtree, I) + val = utils.Min2(val, val_) + } + return val + case "or2": + val0 := RekursivEval(children[0], I) + val1 := RekursivEval(children[1], I) + return utils.Max2(val0, val1) + case "or": + var val = 0 + for _, subtree := range children { + var val_ = RekursivEval(subtree, I) + val = utils.Max2(val, val_) + } + return val + case "implies": + val0 := RekursivEval(children[0], I) + val1 := RekursivEval(children[1], I) + if val0 <= val1 { + return 1 + } + return 0 + default: + log.Fatal("Could not evaluate expression!") + return 0 + } +} + +func RekursivAtoms(tree syntaxbaum.SyntaxBaum) []string { + // Herausforderung: schreibe diese Funktion! + return []string{} +} + +func RekursivDepth(tree syntaxbaum.SyntaxBaum) int { + // Herausforderung: schreibe diese Funktion! + return 0 +} + +func RekursivLength(tree syntaxbaum.SyntaxBaum) int { + // Herausforderung: schreibe diese Funktion! + return 0 +} + +func RekursivParentheses(tree syntaxbaum.SyntaxBaum) int { + // Herausforderung: schreibe diese Funktion! + return 0 +} diff --git a/codego/aussagenlogik/rekursion/rekursion_test.go b/codego/aussagenlogik/rekursion/rekursion_test.go new file mode 100644 index 0000000..b38cf58 --- /dev/null +++ b/codego/aussagenlogik/rekursion/rekursion_test.go @@ -0,0 +1,287 @@ +package rekursion_test + +/* ---------------------------------------------------------------- * + * UNIT TESTING + * ---------------------------------------------------------------- */ + +import ( + "logik/aussagenlogik/rekursion" + "logik/aussagenlogik/schema" + "logik/aussagenlogik/syntaxbaum" + "logik/core/utils" + "testing" + + "github.com/stretchr/testify/assert" +) + +/* ---------------------------------------------------------------- * + * TESTCASE eval(·, ·) + * ---------------------------------------------------------------- */ + +func TestRekursivEvalLiteral(test *testing.T) { + var assert = assert.New(test) + var tree syntaxbaum.SyntaxBaum + var I []string + var val int + tree, _ = schema.ParseExpr("A0") + I = []string{"A0"} + val = rekursion.RekursivEval(tree, I) + assert.Equal(val, 1) + tree, _ = schema.ParseExpr("A0") + I = []string{} + val = rekursion.RekursivEval(tree, I) + assert.Equal(val, 0) + tree, _ = schema.ParseExpr("! A0") + I = []string{"A0"} + val = rekursion.RekursivEval(tree, I) + assert.Equal(val, 0) + tree, _ = schema.ParseExpr("! A0") + I = []string{} + val = rekursion.RekursivEval(tree, I) + assert.Equal(val, 1) +} + +func TestRekursivEvalComplex1(test *testing.T) { + var assert = assert.New(test) + var tree syntaxbaum.SyntaxBaum + var I []string + var val int + tree, _ = schema.ParseExpr("( ! A0 || (( A0 && A3 ) || A2 ))") + I = []string{"A0", "A2"} + val = rekursion.RekursivEval(tree, I) + assert.Equal(val, 1) + I = []string{"A0", "A3"} + val = rekursion.RekursivEval(tree, I) + assert.Equal(val, 1) + I = []string{"A0"} + val = rekursion.RekursivEval(tree, I) + assert.Equal(val, 0) + I = []string{"A4", "A8"} + val = rekursion.RekursivEval(tree, I) + assert.Equal(val, 1) +} + +func TestRekursivEvalComplex2(test *testing.T) { + var assert = assert.New(test) + var tree syntaxbaum.SyntaxBaum + var I []string + var val int + tree, _ = schema.ParseExpr("( ! A0 || (( A0 && A3 ) || ! A2 ))") + I = []string{"A0", "A2"} + val = rekursion.RekursivEval(tree, I) + assert.Equal(val, 0) + I = []string{"A0", "A3"} + val = rekursion.RekursivEval(tree, I) + assert.Equal(val, 1) +} + +/* ---------------------------------------------------------------- * + * TESTCASE Atoms(·) + * ---------------------------------------------------------------- */ + +func TestRekursivAtomsNoduplicates(test *testing.T) { + test.Skip("Methode noch nicht implementiert") + var assert = assert.New(test) + var tree syntaxbaum.SyntaxBaum + var val []string + tree, _ = schema.ParseExpr("( A4 && ( A4 || A4 ))") + val = rekursion.RekursivAtoms(tree) + var n int = len(utils.FilterStrings(&val, func(x string) bool { return x == "A4" })) + assert.Equal(n, 1, "Atome dürfen nicht mehrfach vorkommen!") +} + +func TestRekursivAtomsNononatoms(test *testing.T) { + test.Skip("Methode noch nicht implementiert") + test.Skip("Syntax for generic expressions in ANTLR4 g4 needs to be implemented.") + var assert = assert.New(test) + var tree syntaxbaum.SyntaxBaum + var val []string + tree, _ = schema.ParseExpr("( {F} || A3 )") + val = rekursion.RekursivAtoms(tree) + utils.SortStrings(&val) + assert.NotContains(val, "F", "Nichtatomare Formeln dürfen nicht vorkommen!") +} + +func TestRekursivAtomsCalc1(test *testing.T) { + test.Skip("Methode noch nicht implementiert") + var assert = assert.New(test) + var tree syntaxbaum.SyntaxBaum + var val []string + tree, _ = schema.ParseExpr("A0") + val = rekursion.RekursivAtoms(tree) + utils.SortStrings(&val) + assert.Equal(val, []string{"A0"}) +} + +func TestRekursivAtomsCalc2(test *testing.T) { + test.Skip("Methode noch nicht implementiert") + var assert = assert.New(test) + var tree syntaxbaum.SyntaxBaum + var val []string + tree, _ = schema.ParseExpr("((( ! A8 && A3 ) || A4 ) && A0 )") + val = rekursion.RekursivAtoms(tree) + utils.SortStrings(&val) + assert.Equal(val, []string{"A0", "A3", "A4", "A8"}) +} + +/* ---------------------------------------------------------------- * + * TESTCASE depth(·, ·) + * ---------------------------------------------------------------- */ + +func TestRekursivDepthCalc1(test *testing.T) { + test.Skip("Methode noch nicht implementiert") + var assert = assert.New(test) + var tree syntaxbaum.SyntaxBaum + var val int + tree, _ = schema.ParseExpr("A0") + val = rekursion.RekursivDepth(tree) + assert.Equal(val, 0) +} + +func TestRekursivDepthCalc2(test *testing.T) { + test.Skip("Methode noch nicht implementiert") + var assert = assert.New(test) + var tree syntaxbaum.SyntaxBaum + var val int + tree, _ = schema.ParseExpr("!! A8") + val = rekursion.RekursivDepth(tree) + assert.Equal(val, 2) +} + +func TestRekursivDepthCalc3(test *testing.T) { + test.Skip("Methode noch nicht implementiert") + var assert = assert.New(test) + var tree syntaxbaum.SyntaxBaum + var val int + tree, _ = schema.ParseExpr("( ! A0 && A3 )") + val = rekursion.RekursivDepth(tree) + assert.Equal(val, 2) +} + +func TestRekursivDepthCalc4(test *testing.T) { + test.Skip("Methode noch nicht implementiert") + var assert = assert.New(test) + var tree syntaxbaum.SyntaxBaum + var val int + tree, _ = schema.ParseExpr("((( ! A0 && A3 ) || A4 ) && A8 )") + val = rekursion.RekursivDepth(tree) + assert.Equal(val, 4) +} + +func TestRekursivDepthCalc5(test *testing.T) { + test.Skip("Methode noch nicht implementiert") + var assert = assert.New(test) + var tree syntaxbaum.SyntaxBaum + var val int + tree, _ = schema.ParseExpr("! ((( ! A0 && A3 ) || A4 ) && A8 )") + val = rekursion.RekursivDepth(tree) + assert.Equal(val, 5) +} + +/* ---------------------------------------------------------------- * + * TESTCASE length(·) + * ---------------------------------------------------------------- */ + +func TestRekursivLengthCalc1(test *testing.T) { + test.Skip("Methode noch nicht implementiert") + var assert = assert.New(test) + var tree syntaxbaum.SyntaxBaum + var val int + tree, _ = schema.ParseExpr("A0") + val = rekursion.RekursivLength(tree) + assert.Equal(val, 1) +} + +func TestRekursivLengthCalc2(test *testing.T) { + test.Skip("Methode noch nicht implementiert") + var assert = assert.New(test) + var tree syntaxbaum.SyntaxBaum + var val int + tree, _ = schema.ParseExpr("!! A8") + val = rekursion.RekursivLength(tree) + assert.Equal(val, 3) +} + +func TestRekursivLengthCalc3(test *testing.T) { + test.Skip("Methode noch nicht implementiert") + var assert = assert.New(test) + var tree syntaxbaum.SyntaxBaum + var val int + tree, _ = schema.ParseExpr("( ! A0 && A3 )") + val = rekursion.RekursivLength(tree) + assert.Equal(val, 4) +} + +func TestRekursivLengthCalc4(test *testing.T) { + test.Skip("Methode noch nicht implementiert") + var assert = assert.New(test) + var tree syntaxbaum.SyntaxBaum + var val int + tree, _ = schema.ParseExpr("((( ! A0 && A3 ) || A4 ) && A8 )") + val = rekursion.RekursivLength(tree) + assert.Equal(val, 8) +} + +func TestRekursivLengthCalc5(test *testing.T) { + test.Skip("Methode noch nicht implementiert") + var assert = assert.New(test) + var tree syntaxbaum.SyntaxBaum + var val int + tree, _ = schema.ParseExpr("! ((( ! A0 && A3 ) || A4 ) && A8 )") + val = rekursion.RekursivLength(tree) + assert.Equal(val, 9) +} + +/* ---------------------------------------------------------------- * + * TESTCASE #Parentheses(·) + * ---------------------------------------------------------------- */ + +func TestRekursivParenthesesCalc1(test *testing.T) { + test.Skip("Methode noch nicht implementiert") + var assert = assert.New(test) + var tree syntaxbaum.SyntaxBaum + var val int + tree, _ = schema.ParseExpr("A0") + val = rekursion.RekursivParentheses(tree) + assert.Equal(val, 0) +} + +func TestRekursivParenthesesCalc2(test *testing.T) { + test.Skip("Methode noch nicht implementiert") + var assert = assert.New(test) + var tree syntaxbaum.SyntaxBaum + var val int + tree, _ = schema.ParseExpr("!! A8") + val = rekursion.RekursivParentheses(tree) + assert.Equal(val, 0) +} + +func TestRekursivParenthesesCalc3(test *testing.T) { + test.Skip("Methode noch nicht implementiert") + var assert = assert.New(test) + var tree syntaxbaum.SyntaxBaum + var val int + tree, _ = schema.ParseExpr("( ! A0 && A3 )") + val = rekursion.RekursivParentheses(tree) + assert.Equal(val, 2) +} + +func TestRekursivParenthesesCalc4(test *testing.T) { + test.Skip("Methode noch nicht implementiert") + var assert = assert.New(test) + var tree syntaxbaum.SyntaxBaum + var val int + tree, _ = schema.ParseExpr("((( ! A0 && A3 ) || A4 ) && A8 )") + val = rekursion.RekursivParentheses(tree) + assert.Equal(val, 6) +} + +func TestRekursivParenthesesCalc5(test *testing.T) { + test.Skip("Methode noch nicht implementiert") + var assert = assert.New(test) + var tree syntaxbaum.SyntaxBaum + var val int + tree, _ = schema.ParseExpr("! ((( ! A0 && A3 ) || A4 ) && A8 )") + val = rekursion.RekursivParentheses(tree) + assert.Equal(val, 6) +} diff --git a/codego/aussagenlogik/schema/schema.go b/codego/aussagenlogik/schema/schema.go new file mode 100644 index 0000000..5fa3e5d --- /dev/null +++ b/codego/aussagenlogik/schema/schema.go @@ -0,0 +1,175 @@ +package schema + +import ( + "errors" + "logik/aussagenlogik/syntaxbaum" + parser "logik/grammars/aussagenlogik" + "strings" + + "github.com/antlr/antlr4/runtime/Go/antlr" +) + +/* ---------------------------------------------------------------- * + * EXPORTS + * ---------------------------------------------------------------- */ + +func ParseExpr(u string) (syntaxbaum.SyntaxBaum, error) { + var lexer = createLexer(u) + var tokenStream = lexerToTokenStream(lexer) + var prs = parser.NewaussagenlogikParser(tokenStream) + var t = prs.Start() + tree, err := createSyntaxBaum(t, prs) + return tree, err +} + +/* ---------------------------------------------------------------- * + * PRIVATE + * ---------------------------------------------------------------- */ + +func exprToStream(u string) *antlr.InputStream { + return antlr.NewInputStream(u) +} + +func lexerToTokenStream(lexer antlr.Lexer) antlr.TokenStream { + return antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel) +} + +func createLexer(u string) antlr.Lexer { + stream := exprToStream(u) + return parser.NewaussagenlogikLexer(stream) +} + +func createSyntaxBaum(tree antlr.Tree, parser antlr.Parser) (syntaxbaum.SyntaxBaum, error) { + var ant = antlrTree{tree: tree, parser: &parser} + return ant.toSyntaxBaum() +} + +/* ---------------------------------------------------------------- * + * Struct: antlrTree + Methods + * ---------------------------------------------------------------- */ + +type antlrTree struct { + tree antlr.Tree + parser *antlr.Parser +} + +func (ant antlrTree) getChildren() []antlrTree { + var nodes = ant.tree.GetChildren() + var subants = make([]antlrTree, len(nodes)) + for i, node := range nodes { + subants[i] = antlrTree{tree: node, parser: ant.parser} + } + return subants +} + +func (ant antlrTree) getLabel() string { + return antlr.TreesGetNodeText(ant.tree, []string{}, *ant.parser) +} + +func (ant antlrTree) getTextContent() string { + var expr string = ant.getLabel() + for _, subant := range ant.getChildren() { + expr += subant.getTextContent() + } + return expr +} + +func (ant antlrTree) getTextContentLeaves() string { + var expr string = "" + var subants = ant.getChildren() + if len(subants) == 0 { + expr = ant.getLabel() + } else { + for _, subant := range subants { + expr += subant.getTextContent() + } + } + return expr +} + +func (ant antlrTree) toSyntaxBaum() (syntaxbaum.SyntaxBaum, error) { + var tree syntaxbaum.SyntaxBaum + var err error + var label string = ant.getLabel() + var subants = ant.getChildren() + var nChildren = len(subants) + + switch label { + case "start": + if nChildren == 1 { + return subants[0].toSyntaxBaum() + } + case "open": + if nChildren == 1 { + return subants[0].toSyntaxBaum() + } + case "closed": + switch nChildren { + case 1: + return subants[0].toSyntaxBaum() + case 3: + return subants[1].toSyntaxBaum() + } + case "atomic": + if nChildren == 1 { + subant := subants[0] + tree = syntaxbaum.SyntaxBaum{} + tree.Expr = subant.getTextContentLeaves() + tree.Kind = subant.getLabel() + tree.Children = [](*syntaxbaum.SyntaxBaum){} + tree.Valence = 0 + return tree, nil + } + case "not": + if nChildren == 2 { // Children: [NotSymbol, Teilformel] + subtree, err := subants[1].toSyntaxBaum() + tree = syntaxbaum.SyntaxBaum{} + tree.Expr = subants[0].getTextContent() + " " + subtree.Expr + tree.Kind = label + tree.Children = [](*syntaxbaum.SyntaxBaum){&subtree} + tree.Valence = 1 + return tree, err + } + case "and2", "and", "or2", "or", "implies": + var n int = int((len(subants) + 1) / 2) + if nChildren == 2*n-1 && n >= 2 { + var isSymb bool = false + var subtrees = make([](*syntaxbaum.SyntaxBaum), n) + var i int = 0 + var expr string = "" + for _, subant := range subants { + if isSymb { + expr += " " + subant.getTextContent() + " " + } else { + subtree, err_ := subant.toSyntaxBaum() + if err_ != nil { + err = err_ + } + subtrees[i] = &subtree + expr += " " + subtree.Expr + " " + i++ + } + isSymb = !isSymb + } + expr = strings.Trim(expr, " ") + var lbrace string = "(" + var rbrace string = ")" + // var lbrace string = "( " + // var rbrace string = " )" + // if strings.HasPrefix(expr, "(") { + // lbrace = "(" + // } + // if strings.HasSuffix(expr, ")") { + // rbrace = ")" + // } + tree = syntaxbaum.SyntaxBaum{} + tree.Expr = lbrace + expr + rbrace + tree.Kind = label + tree.Children = subtrees + tree.Valence = n + return tree, err + } + } + + return tree, errors.New("Could not parse expression") +} diff --git a/codego/aussagenlogik/schema/schema_test.go b/codego/aussagenlogik/schema/schema_test.go new file mode 100644 index 0000000..8b175ee --- /dev/null +++ b/codego/aussagenlogik/schema/schema_test.go @@ -0,0 +1,22 @@ +package schema_test + +/* ---------------------------------------------------------------- * + * UNIT TESTING + * ---------------------------------------------------------------- */ + +import ( + "logik/aussagenlogik/schema" + "testing" + + "github.com/stretchr/testify/assert" +) + +/* ---------------------------------------------------------------- * + * TESTCASE ParseExpr + * ---------------------------------------------------------------- */ + +func TestParseExpr(test *testing.T) { + var assert = assert.New(test) + assert.Equal(0, 0) + schema.ParseExpr("A0") +} diff --git a/codego/aussagenlogik/syntaxbaum/syntaxbaum.go b/codego/aussagenlogik/syntaxbaum/syntaxbaum.go new file mode 100644 index 0000000..17eb946 --- /dev/null +++ b/codego/aussagenlogik/syntaxbaum/syntaxbaum.go @@ -0,0 +1,136 @@ +package syntaxbaum + +import ( + "errors" + "fmt" + "strings" +) + +type SyntaxBaum struct { + Kind string + Expr string + Valence int + Children [](*SyntaxBaum) +} + +/* ---------------------------------------------------------------- * + * METHODS + * ---------------------------------------------------------------- */ + +func (tree SyntaxBaum) GetChildren() []SyntaxBaum { + var n int = tree.Valence + var children = make([]SyntaxBaum, n) + for i, subtreePtr := range tree.Children { + children[i] = *subtreePtr + } + return children +} + +func (tree SyntaxBaum) GetChild(indexOpt ...int) (SyntaxBaum, error) { + var index int = 0 + if len(indexOpt) > 0 { + index = indexOpt[0] + } + var subtree SyntaxBaum + var err error + if 0 <= index && index < tree.Valence { + subtree = *(tree.Children[index]) + } else { + err = errors.New(fmt.Sprintf("Instance has no child of index %d !", index)) + } + return subtree, err +} + +func (tree SyntaxBaum) Pretty(preindentOpt ...string) string { + var preindent string = "" + if len(preindentOpt) > 0 { + preindent = preindentOpt[0] + } + return tree.pretty(preindent, " ", "", 0) +} + +func (tree SyntaxBaum) pretty(preindent string, tab string, prepend string, depth int) string { + var indent string = preindent + strings.Repeat(tab, depth) + switch tree.Valence { + case 0: + switch kind := tree.Kind; kind { + case "atom", "generic": + return indent + prepend + kind + " " + tree.Expr + default: + return indent + prepend + kind + } + default: + var lines string = indent + prepend + tree.Kind + prepend = "|__ " + for _, subtree := range tree.Children { + lines += "\n" + subtree.pretty(preindent, tab, prepend, depth+1) + } + return lines + } +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// METHODS: Recognitong of Formula-Types +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +func (tree SyntaxBaum) isAtom() bool { + return tree.Kind == "atom" +} + +func (tree SyntaxBaum) isLiteral() bool { + if tree.isAtom() { + return true + } else if tree.isNegation() { + subtree, err := tree.GetChild() + if err == nil { + return subtree.isAtom() + } + } + return false +} + +func (tree SyntaxBaum) isBeliebig() bool { + return tree.Kind == "generic" +} + +func (tree SyntaxBaum) isTrueSymbol() bool { + return tree.Kind == "taut" +} + +func (tree SyntaxBaum) isFalseSymbol() bool { + return tree.Kind == "contradiction" +} + +func (tree SyntaxBaum) isNegation() bool { + return tree.Kind == "not" +} + +func (tree SyntaxBaum) isConjunction() bool { + return tree.Kind == "and2" +} + +func (tree SyntaxBaum) isLongConjunction() bool { + switch tree.Kind { + case "and", "and2": + return true + default: + return false + } +} + +func (tree SyntaxBaum) isDisjunction() bool { + return tree.Kind == "or2" +} + +func (tree SyntaxBaum) isLongDisjunction() bool { + switch tree.Kind { + case "or", "or2": + return true + default: + return false + } +} + +func (tree SyntaxBaum) isImplication() bool { + return tree.Kind == "implies" +} diff --git a/codego/core/environment/environment.go b/codego/core/environment/environment.go new file mode 100644 index 0000000..1b63526 --- /dev/null +++ b/codego/core/environment/environment.go @@ -0,0 +1,71 @@ +package environment + +import ( + "errors" + "fmt" + "log" + "os" + + "github.com/joho/godotenv" +) + +var ENV_FILE_NAME string + +/* ---------------------------------------------------------------- * + * EXPORTS + * ---------------------------------------------------------------- */ + +func ReadEnvKey(key string, optional ...string) string { + value, err := readEnvKey(key, optional...) + if err != nil { + log.Fatal(err) + } + return value +} + +func ReadEnvKeyAllowMissing(key string, optional ...string) string { + value, _ := readEnvKey(key, optional...) + return value +} + +func ReadEnvKeyDefault(key string, valueDefault string, optional ...string) string { + value, _ := readEnvKey(key, optional...) + if value == "" { + return valueDefault + } + return value +} + +/* ---------------------------------------------------------------- * + * PRIVATE + * ---------------------------------------------------------------- */ + +func loadEnvFile(path string) error { + if path == "" { + log.Fatal("Path name to environment file is not allowed to be empty!") + } + return godotenv.Load(path) +} + +func getEnvKey(key string) string { + return os.Getenv(key) +} + +func readEnvKey(key string, optional ...string) (string, error) { + var path string = ENV_FILE_NAME + var err error + var value string + if len(optional) > 0 { + path = optional[0] + } + err = loadEnvFile(path) + if err != nil { + log.Fatalf("Could not read environment file, \033[1m%s\033[0m!\n", path) + } + err = nil + value = getEnvKey(key) + if value == "" { + err = errors.New(fmt.Sprintf("Environment key \033[1m%s\033[0m missing or empty in environment file \033[1m%s\033[0m!", key, path)) + } + return value, err +} diff --git a/codego/core/utils/utils.go b/codego/core/utils/utils.go new file mode 100644 index 0000000..b4fa4cf --- /dev/null +++ b/codego/core/utils/utils.go @@ -0,0 +1,84 @@ +package utils + +import ( + "sort" + "strings" +) + +/* ---------------------------------------------------------------- * + * EXPORTS + * ---------------------------------------------------------------- */ + +func StrListContains(list []string, x string) bool { + for _, obj := range list { + if obj == x { + return true + } + } + return false +} + +func Min2(x int, y int) int { + if x <= y { + return x + } + return y +} + +func Max2(x int, y int) int { + if x >= y { + return x + } + return y +} + +func SortStrings(list *[]string) { + sort.Slice(*list, func(i int, j int) bool { + u := strings.ToLower((*list)[i]) + v := strings.ToLower((*list)[j]) + cmp := strings.Compare(u, v) + return (cmp < 0) + }) +} + +func FilterStrings(list *[]string, f func(string) bool) []string { + var listFiltered = []string{} + for _, val := range *list { + if f(val) { + listFiltered = append(listFiltered, val) + } + } + return listFiltered +} + +func UnionStrings2(list1 []string, list2 []string) []string { + var mark = make(map[string]bool) + for _, item := range list1 { + mark[item] = true + } + for _, item := range list2 { + mark[item] = true + } + var list = make([]string, len(mark)) + var i int = 0 + for item, _ := range mark { + list[i] = item + i++ + } + return list +} + +func UnionStringsTo(listTo *[]string, listFrom []string) { + var mark = make(map[string]bool) + for _, item := range listFrom { + mark[item] = true + } + for _, item := range *listTo { + mark[item] = false // signals suppression of duplicate addition + } + for item, isNew := range mark { + if isNew { + *listTo = append(*listTo, item) + } + } +} diff --git a/codego/core/utils/utils_test.go b/codego/core/utils/utils_test.go new file mode 100644 index 0000000..59d7614 --- /dev/null +++ b/codego/core/utils/utils_test.go @@ -0,0 +1,57 @@ +package utils_test + +/* ---------------------------------------------------------------- * + * UNIT TESTING + * ---------------------------------------------------------------- */ + +import ( + "logik/core/utils" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +/* ---------------------------------------------------------------- * + * TESTCASE SortStrings + * ---------------------------------------------------------------- */ + +func TestSortStrings(test *testing.T) { + var assert = assert.New(test) + var list = []string{"katze", "Hund", "baby", "Pluto", "Saturn", "Mond"} + utils.SortStrings(&list) + assert.Equal(list, []string{"baby", "Hund", "katze", "Mond", "Pluto", "Saturn"}) +} + +/* ---------------------------------------------------------------- * + * TESTCASE SortStrings + * ---------------------------------------------------------------- */ + +func TestFilterStrings(test *testing.T) { + var assert = assert.New(test) + var list = []string{"abram", "aaron", "aardvark", "aarhus", "alaska", "eel", "aal"} + var list2 = utils.FilterStrings(&list, func(x string) bool { return strings.HasPrefix(x, "aa") }) + assert.Equal(list2, []string{"aaron", "aardvark", "aarhus", "aal"}) +} + +/* ---------------------------------------------------------------- * + * TESTCASE UnionStrings2, UnionStringsTo + * ---------------------------------------------------------------- */ + +func TestUnionStrings2(test *testing.T) { + var assert = assert.New(test) + var list1 = []string{"red", "blue", "blue", "green", "blue", "grey", "black", "green"} + var list2 = []string{"yellow", "orange", "lila", "red"} + var list = utils.UnionStrings2(list1, list2) + utils.SortStrings(&list) + assert.Equal(list, []string{"black", "blue", "green", "grey", "lila", "orange", "red", "yellow"}) +} + +func UnionStringsTo(test *testing.T) { + var assert = assert.New(test) + var list1 = []string{"red", "blue", "blue", "green"} + var list2 = []string{"yellow", "red", "black"} + utils.UnionStringsTo(&list1, list2) + utils.SortStrings(&list1) + assert.Equal(list1, []string{"black", "blue", "green", "red", "yellow"}) +} diff --git a/codego/data.env b/codego/data.env new file mode 100644 index 0000000..42460ad --- /dev/null +++ b/codego/data.env @@ -0,0 +1,12 @@ +# expr = 'A0' +# expr = '! A0' +# expr = 'A9341' +# expr = '!!! A84' +# expr = '( A0 || A1 )' +# expr = '( A0 -> A1 )' +# expr = '( A0 && ! (0 || A8 || 1) ) -> A5' +expr = '( A0 -> ((A0 && A3 && A4) || ! A2) )' +# expr = '( A0 -> ((A0 && A3 && A4) || A2) )' +# expr = '( A0 -> ((A0 && A3) || A2) )' +# expr = '(( {G} || !{G} ) -> A5)' +interpretation = [ "A0", "A2" ] diff --git a/codego/go.mod b/codego/go.mod new file mode 100644 index 0000000..394c16c --- /dev/null +++ b/codego/go.mod @@ -0,0 +1,13 @@ +module logik + +go 1.16 + +require ( + github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210506161523-0a1c3e3ce1ca + github.com/joho/godotenv v1.3.0 + github.com/lithammer/dedent v1.1.0 + // siehe https://pkg.go.dev/github.com/stretchr/testify/assert + // und https://github.com/stretchr/testify + github.com/stretchr/testify v1.7.0 + golang.org/x/tools v0.1.0 +) diff --git a/codego/go.sum b/codego/go.sum new file mode 100644 index 0000000..0ff4c5a --- /dev/null +++ b/codego/go.sum @@ -0,0 +1,40 @@ +github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210506161523-0a1c3e3ce1ca h1:cZZsTEHO/8dZcxfjctHHbnEDLKdvyjUjWLNchKdgO94= +github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210506161523-0a1c3e3ce1ca/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY= +github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/codego/grammars/README.md b/codego/grammars/README.md new file mode 100644 index 0000000..c88ab85 --- /dev/null +++ b/codego/grammars/README.md @@ -0,0 +1,5 @@ +# ANTLR4 # + +Gebrauchsanleitung + +_Hinweis:_ In dieser Repo wurden die in der o.s. Webseite erwähnten Schritte bereits in den bash Skripten berücksichtigt. diff --git a/codego/main.go b/codego/main.go new file mode 100644 index 0000000..af65904 --- /dev/null +++ b/codego/main.go @@ -0,0 +1,95 @@ +package main + +import ( + "encoding/json" + "fmt" + "logik/aussagenlogik/rekursion" + "logik/aussagenlogik/schema" + "logik/aussagenlogik/syntaxbaum" + env "logik/core/environment" + "strings" + + "github.com/lithammer/dedent" +) + +var DATA_ENV string = "data.env" + +type dataType struct { + expr string + interpretation []string +} + +type resultsType struct { + eval int + atoms []string + depth int + length int + nParentheses int +} + +var data dataType + +func main() { + // Extrahiere Daten + err := getData() + if err != nil { + return + } + // Ausdruck -> Syntaxbaum + tree, err := schema.ParseExpr(data.expr) + if err != nil { + return + } + // Methoden ausführen: + results := resultsType{ + eval: rekursion.RekursivEval(tree, data.interpretation), + atoms: rekursion.RekursivAtoms(tree), + depth: rekursion.RekursivDepth(tree), + length: rekursion.RekursivLength(tree), + nParentheses: rekursion.RekursivParentheses(tree), + } + // Resultate anzeigen: + displayResults(tree, results) +} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// SONSTIGE METHODEN +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +func getData() error { + env.ENV_FILE_NAME = DATA_ENV + data.expr = env.ReadEnvKey("expr") + s := env.ReadEnvKey("interpretation") + err := json.Unmarshal([]byte(s), &data.interpretation) + return err +} + +func displayResults(tree syntaxbaum.SyntaxBaum, results resultsType) { + fmt.Println(fmt.Sprintf( + dedent.Dedent(` + Syntaxbaum von + F := %[1]s: + + %[2]s + + Für I = {%[3]s} und F wie oben gilt + eval(F, I) = %[4]d; + atoms(F) = {%[5]s}; <- * + depth(F) = %[6]d; <- * + length(F) = %[7]d; <- * + #parentheses(F) = %[8]d; <- * + + * noch nicht implementiert! + Challenge: schreibe diese Methoden! (siehe README.md) + `), + tree.Expr, + tree.Pretty(" "), + strings.Join(data.interpretation, ", "), + results.eval, + // string(results.atoms), + strings.Join(results.atoms, ", "), + results.depth, + results.length, + results.nParentheses, + )) +} diff --git a/codego/run.sh b/codego/run.sh new file mode 100755 index 0000000..5247e2f --- /dev/null +++ b/codego/run.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash + +################################################################################################ +# NOTE: `chmod +x run.sh` vorher ausführen, um dieses Skript benutzen zu können. +################################################################################################ + +################################ +# HILFSMETHODEN +################################ + +function call_go() { + go $@; +} + +function check_requirements() { + [ -f "go.sum" ] && rm "go.sum"; + call_go get "$( cat requirements )"; +} + +function precompile_grammars() { + local fname; + local name; + pushd grammars >> /dev/null; + while read fname; do + ( [ "$fname" == "" ] || ! [ -f "$fname" ] ) && continue; + name="$( echo "$fname" | sed -E "s/^(.*)\.g4$/\1/g" )"; + echo -e "\033[92;1mANTLR4\033[0m präkompiliert Grammatik \033[1m${fname}\033[0m"; + java -jar antlr.jar -Dlanguage=Go "$fname" -o "$name"; + done <<< "$( ls *.g4 2> /dev/null )" + popd >> /dev/null +} + +function compile_programme() { + [ -f "main" ] && rm "main"; + echo -e "\033[92;1mGO\033[0m kompiliert \033[1mmain.go\033[0m"; + call_go build "main.go"; +} + +function run_programme() { + echo -e "\033[92;1mGO\033[0m kompiliertes Programm wird ausgeführt"; + ./main; +} + +################################ +# HAUPTVORGÄNGE +################################ + +# Kann auskommentiert werden, wenn nötige Module schon installiert: +check_requirements; + +# Code als Programm kompilieren und ausführen: +precompile_grammars; +compile_programme; +run_programme; diff --git a/codego/test.sh b/codego/test.sh new file mode 100755 index 0000000..0de9535 --- /dev/null +++ b/codego/test.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +################################################################################################ +# NOTE: `chmod +x test.sh` vorher ausführen, um dieses Skript benutzen zu können. +################################################################################################ + +################################ +# HILFSMETHODEN +################################ + +function call_go() { + go $@; +} + +function check_requirements() { + [ -f "go.sum" ] && rm "go.sum"; + call_go get "$( cat requirements )"; +} + +function run_unittests(){ + echo -e "\033[1mUNITTESTS\033[0m\n"; + call_go test -v -timeout 60s -count 1 -run "^Test[A-Z].*" "logik" "./..."; +} + +################################ +# HAUPTVORGÄNGE +################################ + +# Kann auskommentiert werden, wenn nötige Module schon installiert: +check_requirements; + +# Code testen (unittests): +run_unittests;