diff --git a/.gitignore b/.gitignore index 9e97696345f95b4fb35fceebdc140e682e52bf91..c4b1d94abee088febd82ef42d7e2a5e5aaa5f1b1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ +src/__pycache__/* __pycache__/* output/* +notes.html diff --git a/generator.py b/generator.py index 0948e36fb9d18d371bb06c37d4d2bee2ab772765..2797b8a5ff186f0a1bc6edd5bb56296cad1a2855 100644 --- a/generator.py +++ b/generator.py @@ -1,18 +1,22 @@ import os import re +import runpy +import importlib +def info(message): + print('[Info]', message) def create_autoilias_script(type, title, author, question, solution, points=9999, answer=None, gapLength=None, number=1): - with open("templates/generic.class.php", "r", encoding='utf-8') as script: + with open('templates/generic.class.php', 'r', encoding='utf-8') as script: script = script.read() if not gapLength: - gapComment = "" + gapComment = '' gapLength = int(gapLength) else: - gapComment = "//" - gapLength = "" + gapComment = '//' + gapLength = '' script = script.format( type=type, title=title, author=author, number=number, points=points, @@ -20,10 +24,16 @@ def create_autoilias_script(type, title, author, question, solution, solution=solution, ) - with open("output/" + title.replace(' ', "_") + ".class.php", 'w', encoding='utf-8') as output: + with open('output/generator__' + title.replace(' ', '_') + '.class.php', 'w', encoding='utf-8') as output: print(script, file=output) -for script in filter(lambda dat: re.search('\d+_\w+\.py', dat), os.listdir('./src')): - with open('./src/' + script) as script_f: - exec(script_f.read()) - create_autoilias_script(**meta) \ No newline at end of file + +def worker(): + # runpy.run_path("src/util.py") + for script in filter(lambda dat: re.search('\d+_\w+\.py', dat), os.listdir('./src')): + info('excuting script ' + script) + meta = runpy.run_module('src.' + script.replace('.py', ''))['meta'] + create_autoilias_script(**meta) + +if __name__ == '__main__': + worker() diff --git a/notes.md b/notes.md new file mode 100644 index 0000000000000000000000000000000000000000..c3e1d7707c18bb5fe36c7bd1f626131d2baa09bc --- /dev/null +++ b/notes.md @@ -0,0 +1,8 @@ +### Notes + +Every task has to follow a certain structur: + +* generate_form +* generate_solution +* generate_task +* other outputs diff --git a/src/01_basics.py b/src/01_basics.py new file mode 100644 index 0000000000000000000000000000000000000000..d733ff197797d9888dd425b848a5073c7cf5f966 --- /dev/null +++ b/src/01_basics.py @@ -0,0 +1,53 @@ +import re + + +def gap(content, points): + return "[gap({points}P)]{gap}[/gap]".format(points=points, gap=content) + +def p(content): + return '<p>{}</p>\n'.format(content) + +def li(content): + return '<li>{}</li>\n'.format(content) + +def tex(content): + return ('<span class="latex">' + content + '</span>').replace('\\', '\\\\') + +def generate_ilias(): + aufgabe = p('Berechnen Sie:') + + aufgabe += li(tex(r'\{a,b\} \cdot \{\varepsilon, cd\} = \{') + gap('a, acd, b, bcd', 0.5) + tex('\}')) + aufgabe += li(tex(r'\{\varepsilon, cd\} \cdot \{a, b\} = \{') + gap('a, b, cda, cdb', 0.5) + tex('\}')) + aufgabe += li(tex(r'\{\varepsilon, a, ab\}^2 = \{') + gap('eps, a, aa, aab, aba, abab', 0.5) + tex('\}')) + aufgabe += li(tex(r'\{\varepsilon\} \cdot \{\varepsilon\} = \{') + gap('eps', 0.5) + tex('\}')) + aufgabe += li(tex(r'\{\varepsilon\} \cdot \emptyset = ') + gap('{}', 0.5)) + aufgabe += li(tex(r'\{\varepsilon\} \cdot \emptyset = ') + gap('{}', 0.5)) + aufgabe += li(tex(r'\emptyset^2 = ') + gap('{}', 0.5)) + aufgabe += li(tex(r'\emptyset^* = \{') + gap('eps', 0.5) + tex('\}')) + aufgabe += li(tex(r'\{\varepsilon\}^* = \{') + gap('eps', 0.5) + tex('\}')) + aufgabe += li(tex(r'\{\varepsilon, a\}^+ = \{') + gap('a*', 0.5) + tex('\}')) + + aufgabe += ''' Verwenden Sie <code>eps</code> für {eps} und + <code>{{}}</code> für {empty}. Geben Sie alle Elemente der neuen Menge in + lexikographischer Reihenfolge an und verwenden Sie Leerzeichen nach einem + Komma. (Z.B. eps, a, ab, acd, ba)'''.format(eps=tex(r'\varepsilon'), empty=tex(r'\emptyset')) + + + return aufgabe.replace(r'"', r'\"') + +def generate_solution(): + solution = "<h3>Musterloesung</h3><br>" + solution += re.sub(r'\[gap\(\d+P\)\](.+)\[\/gap\]', + r'\1', generate_ilias()) + return solution + + +### END OF SCRIPT ######################################################## +meta = { + "type": "GAP", + "title": "Grundlagen", + "author": "Jan Maximilian Michal", + "gapLength": 10, + "question": generate_ilias(), + "solution": generate_solution(), +} diff --git a/src/02_chomsky_grammar.py b/src/02_chomsky_grammar.py index bc2eb47af5820cf1d45150e67ef66fbd306df8ad..092f63d0f496c865dbc8731912f1c16274149cf7 100644 --- a/src/02_chomsky_grammar.py +++ b/src/02_chomsky_grammar.py @@ -1,5 +1,6 @@ from itertools import product + delta = """ S := AB | ABA A := aA | a @@ -23,7 +24,7 @@ class rule: def parse(grammer): - grammer = grammer.strip() + grammer = grammer.strip() terminals = set() non_terms = set() rules = set() diff --git a/src/04_minimize_dea.py b/src/04_minimize_dea.py index 5bc579c4fc73de019c68b81d2f2e2579bf6cdc4a..3c2fa64a42007b21a029e34d23500b768d53aedc 100644 --- a/src/04_minimize_dea.py +++ b/src/04_minimize_dea.py @@ -1,6 +1,16 @@ +import re + from itertools import product from graphviz import Digraph +# relative imports + +def gap(content, points): + return "[gap({points}P)]{gap}[/gap]".format(points=points, gap=content) + +def tex(content): + return ('<span class="latex">' + content + '</span>').replace('\\', '\\\\') + class Automaton: @@ -44,35 +54,39 @@ class Automaton: # Finalize TODO return marker - def generate_ilias(self): - gap = "[gap({points}P)]{luecke}[/gap]" + def generate_form(self): marker = self.minimize() - aufgabe = '''Minimieren Sie den folgenden deterministischen Automaten -[tex]M = {{%s},{%s},\\\\delta,{%s},{%s}}[/tex] oder zeigen Sie, dass der Automat -bereits minimal ist. Geben Sie die Tabelle mit den Zustandspaaren an, sowie den -minimierten Graphen.<br>\n\n''' % (', '.join(self.states), ', '.join(self.sigma), - str(self.q0), ', '.join(self.F)) - - aufgabe += '<p>' + self.graph_output() + '</p>' - - aufgabe += '\n\n<p><table border-collapse: collapse; border-style: hidden; rules=\"all\">\n' - aufgabe += '<tr>\n\t<td> </td>\n\t<td>' + \ + form = '\n\n<p><table border-collapse: collapse; border-style: hidden; rules=\"all\">\n' + form += '<tr>\n\t<td> </td>\n\t<td>' + \ '</td>\n\t<td>'.join(self.states[1:]) + '</td></tr>\n' for i, z1 in enumerate(self.states[:-1]): - aufgabe += '<tr>\n\t<td>{}</td>\n'.format(z1) + form += '<tr>\n\t<td>{}</td>\n'.format(z1) for j, z2 in enumerate(self.states[1:]): content = '\t<td>{}</td>\n' if j < i: content = content.format(' ') elif (z1, z2) not in marker: - content = content.format(gap.format(points=1, luecke='X')) + content = content.format(gap('X', 1)) else: - content = content.format( - gap.format(points=1, luecke=marker[(z1, z2)])) - aufgabe += content - aufgabe += '</tr>\n' - aufgabe += '</table></p>' + content = content.format(gap(marker[(z1, z2)], 1)) + form += content + form += '</tr>\n' + form += '</table></p>' + + return form + + def generate_task(self): + + aufgabe = '''Minimieren Sie den folgenden deterministischen Automaten +{[tex]M = \\{\\{%s\\},\\{%s\\},\\\\delta,\\{%s\\},\\{%s\\}\\}[/tex]} oder zeigen Sie, dass der Automat +bereits minimal ist. Geben Sie die Tabelle mit den Zustandspaaren an, sowie den +minimierten Graphen.<br>\n\n''' % (', '.join(self.states), ', '.join(self.sigma), + str(self.q0), ', '.join(self.F)) + + aufgabe += '<p>' + self.graph_output() + '</p>' + + aufgabe += self.generate_form() return aufgabe.replace(r'"', r'\"') @@ -93,6 +107,12 @@ minimierten Graphen.<br>\n\n''' % (', '.join(self.states), ', '.join(self.sigma) return str(f.pipe(), encoding='utf-8') + def generate_solution(self): + solution = "<h3>Musterloesung</h3><br>" + solution += re.sub(r'\[gap\(\d+P\)\](.+)\[\/gap\]', + r'\1', self.generate_form()) + return solution.replace(r'"', r'\"') + def delta(state, char): return { @@ -139,15 +159,15 @@ states2 = ["s{}".format(i) for i in range(7)] F = ["s2"] F2 = ["s0", "s3", "s4", "s6"] -A = Automaton(states2, "ab", delta2, "s0", F2) +A = Automaton(states, "ab", delta, "s0", F) -### END OF SCRIPT ############################################################## +### END OF SCRIPT ######################################################## meta = { - "type" : "GAP", - "title" : "Minimieren eines deterministischen Automaten", - "author" : "Jan Maximilian Michal", - "gapLength" : 10, - "question" : A.generate_ilias(), - "solution" : "TODO", -} \ No newline at end of file + "type": "GAP", + "title": "Minimieren eines deterministischen Automaten small", + "author": "Jan Maximilian Michal", + "gapLength": 10, + "question": A.generate_task(), + "solution": A.generate_solution(), +} diff --git a/src/05_nea_to_dea.py b/src/05_nea_to_dea.py index 3378d92989f89d110fcfb72aca084588a6a53c62..6d67db55f167f544ead725f9e44c8100d2ad1424 100644 --- a/src/05_nea_to_dea.py +++ b/src/05_nea_to_dea.py @@ -2,6 +2,19 @@ from collections import deque from functools import reduce from graphviz import Digraph +import re + + +def gap(content, points): + return "[gap({points}P)]{gap}[/gap]".format(points=points, gap=content) + +def p(content): + return '<p>{}</p>\n'.format(content) + +def tex(content): + return ('<span class="latex">' + content + '</span>').replace('\\', '\\\\') + + class NEA: """docstring for NEA""" @@ -17,15 +30,13 @@ class NEA: def to_dea(self): done = [] new_steps = deque([{self.q0}]) - table = [] while new_steps: cur = new_steps.popleft() table.append([cur]) for a in self.sigma: - next = reduce(lambda a, b: a | b, (self.delta(s, a) - for s in cur)) + next = reduce(lambda a, b: a | b, (self.delta(s, a) for s in cur)) table[-1].append(next) if next not in done: new_steps.append(next) @@ -34,7 +45,7 @@ class NEA: return table def graph_output(self, file_format='svg'): - f = Digraph('finite_state_machine', format='pdf') + f = Digraph('finite_state_machine', format=file_format, engine='dot') f.body.extend(['rankdir=LR', 'size="8,5"']) f.attr('node', shape='doublecircle') @@ -49,32 +60,44 @@ class NEA: for next in self.delta(state, a): f.edge(str(state), next, label=a) - f.view() - # return str(f.pipe(), encoding='utf-8') + return str(f.pipe(), encoding='utf-8') - def _generate_ilias(self): + def generate_form(self): table = self.to_dea() - - aufgabe = ''' Aufgabentext\n\n''' - - tr = '<tr>\n{}</tr>\n' td = '\t<td>{}</td>\n' - aufgabe += '<table border-collapse: collapse; border-style: hidden; rules=\"all\">\n' - aufgabe += tr.format(''.join(td.format(s) for s in ["delta", "a", "b"])) + aufgabe = '<table border-collapse: collapse; border-style: hidden; rules=\"all\">\n' + aufgabe += tr.format(''.join(td.format(s) + for s in [tex(r'\delta'), "a", "b"])) for row in table: - aufgabe += tr.format(''.join(td.format(','.join(sorted(s))) for s in row)) + first, second, third = map(', '.join, (sorted(s) for s in row)) + aufgabe += tr.format(td.format(first) + + td.format(gap(second, 1)) + + td.format(gap(third, 1))) aufgabe += '</table>' - yield(aufgabe.replace('"', r'\"')) - - yield("{:>25}{:>25}{:>25}".format("delta", "a", "b")) - for row in table: - yield("{:>25}{:>25}{:>25}".format(*map(', '.join, (sorted(s) for s in row)))) + return aufgabe def generate_ilias(self): - return ''.join(self._generate_ilias()) + aufgabe = '''Wandeln Sie M in einen DEA M' um, indem Sie die + Potenzmenge der Zustände aus M in M' als neue Zustände einfügen + (Eine Anleitung befindet sich im Skript von Dr. Müller S. 25).\n\n''' + + aufgabe += p(self.graph_output()) + aufgabe += p('''Füllen Sie diese Tabelle nach dem Vorbild im Müller + Skript aus. Achten Sie dabei darauf alle Zustände in lexikographischer + Reihenfolge aufzulisten (Zum Beispiel: s1, s2, s4) und alle Zustände + müssen mit Komma und Leerzeichen getrennt werden.''') + aufgabe += p(self.generate_form()) + + return aufgabe.replace('"', r'\"') + + def generate_solution(self): + solution = "<h3>Musterloesung</h3><br>" + solution += re.sub(r'\[gap\(\d+P\)\](.+)\[\/gap\]', + r'\1', self.generate_form()) + return solution.replace(r'"', r'\"') # Define one Automaton @@ -99,12 +122,12 @@ F = ["s3"] # create N = NEA(states, "ab", delta, 's0', F) -### END OF SCRIPT ############################################################## +### END OF SCRIPT ######################################################## meta = { - "type" : "GAP", - "title" : "NEA to DEA", - "author" : "Jan Maximilian Michal", - "gapLength" : 10, - "question" : N.generate_ilias(), - "solution" : "TODO", + "type": "GAP", + "title": "NEA in DEA umwandeln", + "author": "Jan Maximilian Michal", + "gapLength": 10, + "question": N.generate_ilias(), + "solution": N.generate_solution(), } diff --git a/src/util.py b/src/util.py new file mode 100644 index 0000000000000000000000000000000000000000..85341592c3fb6a55076f30e9889afd94e87a409e --- /dev/null +++ b/src/util.py @@ -0,0 +1,4 @@ + +def gap(content, points): + return "[gap({points}P)]{gap}[/gap]".format(points=points, gap=content) +