Notizen

Notizen von 2019.

28.09.2019 Denkpause. Wer A sagt, muss auch B sagen. Seit zwei Monaten bin ich in den Strudel geraten. Die Datentypen Boolean und Integer hinzugefügt; da musste trace her, um deren Instanzen zu validieren; dann mussten Formatierungsmöglichkeiten her, sie wurden im Textkorpus geschaffen; darauf hin mussten für die alten Datentypen entsprechende Formatierungsoptionen im Textkorpus nachgeholt werden. Zu diesen alten Datentypen gehören auch die Namen aber — oh Schreck — diese verfügen über gar keinen Formatmanager, nicht einmal im Textkorpus sind sie vorhanden. Das Namenssystem muss gründlich überarbeitet werden, wenn er an den neuen Zeiten angepasst werden soll. Was also tun? Namen modernisieren oder erst einmal mit dem unschönen Überbleibsel leben? Einiges spricht gegen eine Neuschreibung. Ich habe noch so viel vor: Algorithmen, Skriptsprache. Das müsste ich zunächst im Groben angehen, später verfeinern. Außerdem: wer will schon diese schönen neuen Möglichkeiten der Namen, wenn es sonst weder Algorithmen nocht Skriptsprache gibt? Andererseits: Mit einer Neuschreibung würde das System besser, auch in seinem Grunddesign. Ich bin außerdem schon mitendrin und kann mich nicht mit halben Sachen begnügen. Vielleicht am Wichtigsten aber: die Implementierung der Namen im Textkorpus wird wohl zu Feuerprobe überhaupt für die Textengine werden. Es kann zu größeren Problemen kommen, seien sie logisch oder performancetechnisch. Ich darf nicht verzagen, solche Herausforderungen so früh wie möglich anzugehen. Diese Schritte sind die, die einem wirklich weiterbringen. Die Entscheidung ist also gefallen: machen!

23.09.2019 Substrings Formatieren. Bislang wurden in der Symbolsprache die Formatmanager der eingebetteten Strings nur fürs Parsen eingesetzt. Beim Formatieren wurde der Inhalt immer in reiner Symbolsprache ausgegeben. Nun ist die Nutzung dieser Formatmanager auch für die Ausgabe möglich. In der Konfiguration von Symbolsprache gibt es jetzt die Option subformat, die diese Nutzung ein- bzw. ausschaltet. Auch die Shell wurde mit entsprechenden Startargumenten versehen. $ tesh <<<’~=i:integer; ~i "123";‘ ~=i ^session :integer; ^session ~i { ~digit #1; ~digit #2; ~digit #3; }; $ tesh -s <<<’~=i:integer; ~i "123";‘ ~=i ^session :integer; ^session ~i "123";

20.09.2019 String-Formatierung. Der Typ string verfügt jetzt über eine Konfigurationsdefinition im Textkorpus. Es gibt einen Typ string format, der Parameter wie Anführungs-, Escapezeichen und Behandlung von Leerraum bestimmt. Durch Instanziierung und Ableitung lassen sich Formatierungsoptionen definieren und mit einer Angabe ~implicit als Standard aktivieren. Es gibt nun auch Funktionen zum Übertragen einer Stringkonfiguration zwischen einer binären C-Repräsentation und dem Textkorpus in beiden Richtungen. Anders als beispielsweise Integer gehören Strings (neben Symbolsprache und Namenssystem) zum Kern der Textengine und werden beim Booten des Systems benötigt. Während Integer und alle sonstigen Typen und ihre Konfigurationen in Symbolsprache definiert werden und bei Bedarf als C-Objekte exportiert werden können, ist es bei Strings umgekehrt. Sie sind anfangs in C definiert und bereits während des Bootprozesses verfügbar. Wenn das System hochgefahren ist, werden die Definitionen (programmatisch) ins Textkorpus gespeichert. Danach sind diese Typen mit ihrer Dualität zwischen Textkorpus und C von allen anderen nicht zu unterscheiden.

16.09.2019 Formatregistrierung. Ich habe die Registrierung der Formatmanager neuimplementiert. Es war bislang so, dass man nur einen Formatmanager mit einer bestimmten Konfiguration für einen Typ registrieren konnte. D.h. in einer Sitzung alle eingebetteten Strings od. Integer mussten dieselbe Konfiguration nutzen. Und das konnte nur programmatisch bestimmt werden. Die Shell tesh beispielsweise hatte nur eine Standardkonfiguration. Hätte man dem Nutzer gewisse Optionen geben wollen, hätte man sie in der Shell extra implementieren und diese etwa mit zusätzlichen Startargumenten versehen müssen. Nach der Überarbeitung registriert sich jedes Formatmanager selbst programmatisch beim System für einen Konfigurationstyp und die Konfiguration wird separat über Symbolsprache registriert. Bei der Definition einer Texteinheit als Instanz eines Konfigurationstyps kann diese mit einer Angabe ~implicit als implizite Konfiguration fürs Parsen und oder fürs Formatieren registriert werden. Man kann auch den Zieltyp bestimmen, so dass die Konfiguration nur für bestimmte Subtypen greift. Eine solche Registrierung gilt von dem Augenblick an, wann sie erfasst wird, bis man sie durch eine andere für denselben Zieltyp ersetzt. Ich habe lange am Entwurf gesessen. Überlegungen haben bis ein generisches dynamisches Dependency-Injection-System mit Trennung von Schnittstellen und den sie implementierenden Modulen gereicht. Es gab eine längere Strecke der immer wieder zu begehenden Gratwanderung zwischen den zwei Fallen des zu viel und zu wenig, des zu früh und zu spät Implementierens.

09.09.2019 Subtyp und Instanz. Die Textengine hat die Besonderheit, dass es in ihr keinen ontologischen Unterschied zwischen Typ und Instanz gibt. Doch manchmal braucht man diese Unterscheidung. Zum Beispiel ist es bei der Validierung von Integern so, dass man keine Instanzen zulassen will, die keine Ziffer haben. Der Typ selbst oder Subtypen von ihm dürfen jedoch keine Ziffer haben. Bislang habe ich mich folgendermaßen abgeholfen: ~=i :integer; ~i "12"; Bei der Einführung vom Subtyp „i” in der ersten Zeile wird kein Wert angegeben, bei der Instanziierung von „i” in der zweiten Zeile wird ein Wert angegeben. Hätte man bei der Einführung des Subtyps einen oder bei der Instanziierung keinen Wert angegeben, so wäre die Validierung fehlgeschlagen und die Aktion abgebrochen. Unterschieden wird hier anhand des Merkmals „Selbstrolle”. Eine Selbstrolle ist eine Texteinheit, die sich selbst als Rolle hat. Da stellt sich die Frage: soll man bei der Selbstrollenfeststellung die symbolische Equivalenz berücksichtigen? Wenn man es nicht tut, so wird dies nicht zugelassen: ~= format { ~= represents; }; ~format =integer format { ~represents #integer; }; Ohne symbolische Equivalenz wird die Einheit ~represents abgewiesen, da sie dann keine Selbstrolle ist, also als Integer-Instanz aufgefasst wird und keinen Wert hat. Wenn man die symbolische Equivalenz gelten lässt, dann ist ~represents #integer schon eine Selbstrolle (weil es integer ist), und die Einheit gilt als Subtyp. So weit so gut. Nur funktioniert dann dieser andere Fall nicht rund: [0] ~= b : boolean; [1] ~b #true; [2] ~b; [3] ~b #integer; Bei der Instanziierung eines Wahrheitswerts will man eins von #true oder #false haben. Zeile 1 ist korrekt und 2 inkorrekt. Aber Zeile 3 gilt unschönerweise auch als korrekt, weil sie wieder als Subtyp-Definition und nicht als Instanziierung aufgefasst wird, da integer eine Selbstrolle ist. Erstmal verzichte ich mangels besserer Ansätze auf die Behebung dieser letzten Unstimmigkeit und bleibe dabei, bei der Selbstrollenfeststellung die symbolische Equivalenz aufzulösen. Es wird sich im Laufe des Projekts herausstellen, ob dieser Ansatz haltbar ist oder ob man den Unterschied zwischen Subtyp und Instanz in irgend einer Form explizit einführen oder anders implizit erkennen muss.

05.09.2019 Format Ausreifung. Nun funktioniert der Integerparser reibungslos mit dem Symbolsprachenparser zusammen. Ich habe das Formatierungssystem völlig überarbeitet — wobei nur was Schnittstellen und Interoperabilität betrift, die Kernfunktionen waren schon gut — etwas abgespeckt und leicht symmetrischer gemacht. Da ich es deshalb momentan klar im Kopf habe, und ich erfahrungsgemäß weiß, dass ich es in ein paar Monaten wieder völlig vergessen haben werde, habe ich die Grundzüge der Implementierung in Impl: Format niedergeschrieben.

29.08.2019 Rück- und Fortschritte. Der Zahlenparser musste nur noch ins System aufgenommen werden, so dass man beim Parsen von Symbolsprache Zahlen in der üblichen Darstellung einbetten kann. ~=i :integer; ~i "142"; Nur gibt es da ein kleines Detail. Momentan ist das so implementiert, dass der eingebettete Parser den Ausdruck inklusive der Anführungszeichen bekommt. Das ist für eingebettete Strings (bisher der einzige Fall) nötig, damit der Stringparser passend reagiert. Nur kann der Integerparser mit Anführungszeichen nichts anfangen — warum sollte er? er erwartet ja nur Zahlen! — und wirft korrekterweise einen Fehler TE_INVALIDTOKEN auf. Das Problem gibt es nur bei der obigen nackten Darstellung des Bezeichners. Wenn man diesen in Anführungszeichen setzt, dann wird der Ausdruck korrekt geparst. ~"i" 142; Da der Namensparser ein Ende des Namens meldet, trifft der Symbolparser auf ein Zeichen 1, der nicht zur Symbolsprache gehört, identifiziert es als Substring und gibt es weiter. ~i 142; Der Versuch, das obige zu parsen, wird mit einem Fehler TE_NOTFOUND quittiert, denn der Parser sucht nach einem Namen „i 142”, den es nicht gibt. Wenn man aber Leerzeichen innerhalb eines Bezeichners ohne Anführungszeichen unterbindet (das kann man über den Konfigurationsparameter quotedchars veranlassen), so wird der Ausdruck erfolgreich geparst. Ich kann aber natürlich eine solche Einschränkung der Konfigurationsmöglichkeiten nicht gutheißen, zumal es mir gerade diese Option der ungeschützten Leerzeichen in Namen sehr am Herzen liegt. Als Lösung könnte man den Integerparser so ausbauen, dass er mit Anführungszeichen umgehen kann. Das wäre aber kein ortogonaler Ansatz, denn der Umgang mit Anführungs-, Leer- und Escapezeichen ist das Kerngeschäft des Stringparsers. Dazu kommt, dass der Symbolparser zwar einen Begriff von Substring hat, aber diesen stiefmütterlich behandelt. Ich werde den Symbolparser so ausbauen, dass er den Substring vollwertig behandelt und einem Stringparser übergibt. Dazu ist es nötig, den Stringparser zu überarbeiten. Ich werde die Gelegenheit nutzen, um den Stringparser umzuschreiben, denn es ist — fällt mir jetzt bei näherer Betrachtung auf — ziemlich umständlich und barock implementiert worden.

27.08.2019 Integer Formatierung. Nun verfügt die Textengine über Unterstützung für die Formatierung von Ganzzahlen, d.h. diese lassen sich als Zeichenketten ein- und ausgeben. Wie die Zahlen beim Parsen erwartet und wie sie beim Formatieren generiert werden, ist konfigurierbar. So sieht beispielsweise eine Konfiguration aus: ~= c :integer format { ~base { ~digit #1; ~digit #0; }; ~order #big-endian; ~padding { ~digit #1; }; ~group { ~separator #chars."."; ~count { ~digit #3; }; ~backward; }; ~chars { ~negative #chars."-"; ~digits "0123456789"; }; }; Das gibt die Zahlen in der üblichen Form aus, der Punkt dient als Tausendertrennzeichen. Man kann auch mit ~order #little-endian die Ziffer in umgekehrter Reihenfolge bekommen, die Zeichen für positive und negative Zahlen jeweils definieren, und die Ziffer nach anderen Kriterien gruppieren. Sehen wir ein Beispiel. Wenn man Basis 4, Padding 4 und die Zeichen „·” für 0, A für 1, B für 2 und C für 3 nimmt, werden die Zahlen 0 bis 12 wie folgt ausgedrückt. Das gilt sowohl fürs Parsen wie fürs Formatieren: ~= c :integer format { ~base { ~digit #4; }; ~order #big-endian; ~padding { ~digit #4; }; ~chars { ~negative #chars."-"; ~digits "·ABC"; }; }; FMT(0) ···· FMT(1) ···A FMT(2) ···B FMT(3) ···C FMT(4) ··A· FMT(5) ··AA FMT(6) ··AB FMT(7) ··AC FMT(8) ··B· FMT(9) ··BA FMT(10) ··BB FMT(11) ··BC FMT(12) ··C· Das System stellt eine Konfiguration bereit, die standardmäßig angewendet wird. Wenn man eine andere Darstellung möchte, kann man entweder eine neue Konfiguration wie die obige definieren, oder von der standardmäßigen ableiten und sie ergänzen. ~= Zahlenformat : default integer format { ~sign { ~after; }; }; Die obige Definition zum Beispiel verursacht eine herkömmliche Zahlendarstellung, nur mit dem Negativzeichen am Schluß. FMT(-346) 346- In C implementiert ist eine Konfiguration als der Typ intcf_t. Man kann eine neue Konfiguration programmatisch erstellen, indem man diesen Typ initialisiert und die Funktion te_store_intcf aufruft. Man kann anders herum mit der Funktion te_retrieve_intcf eine beliebige Konfiguration aus dem Textkorpus als Instanz diesen Typs erhalten.

21.08.2019 Zäh. Bei der Umsetzung der char-Validierung bin ich auf mehrere Fehler gestoßen, und zwar nicht nur im frisch implementierten rollback, sondern — erschreckend — sogar in den unteren Schichten des Systems. Die Funktion is_type, die ermittelt, ob eine Texteinheit eine andere Texteinheit instanziiert (genauer gesagt, die darunter liegende generelle Funktion für alle Basis-Referenzgenera mit Klausur is_brefc), ergab nämlich unter Umständen ein falsches Resultat. Die transitive Klausur nach Referenzgenus und gleichzeitig nach symbolischer Equivalenz durchlief nicht alle Kombinationen. Die Behebung von diesen Fehlern war langwierig und müselig. Positiv: noch einmal haben die Regressionstests die Entwicklung gerettet. Diese sind zwar lange nicht systematisch vollständig, aber sehr nützlich in dem existierenden Umfang. Ohne sie wäre ich sicherlich in ein Sysiphusloch geraten.

18.08.2019 Rollback. So wild war es wieder nicht. Nachdem ich den Implementierungsansatz für unset und unlink entworfen hatte, erkannte ich, dass dies zum einen eine größere Baustelle darstellt, zum anderen aber nicht wirklich nötig ist, um rollback zu implementieren. Denn unter der Voraussetzung, dass man immer von einer bestimmten Texteinheit alle anderen bis zum Ende entfernt, entfällt die Validierung (früher validierte Einheiten können nicht betroffen sein) und wird die Unterstützung für gelöschten Einheiten trivial (gelöschte Einheiten mitendrin, die alle späteren Abfragen beeinflussen, gibt es nicht). Ich habe also erstmal nur die Möglichkeit des Rollbacks eingebaut, damit die Validierung unter Beibehaltung der Datenkonsistenz möglich ist. Es war nicht leicht, aber ein paar Sitzungen haben gereicht, und für das Ergebnis lohnt auf jeden Fall. Boolean und Integer verfügen jetzt über eine solide Validierung. Als nächstes muss ich nur noch auch Chars und Strings mit Validierung versehen (diese war nicht möglich, als sie implementiert wurden), und dann kann ich mich — endlich! — an der Zahlenformatierung heran machen.

16.08.2019 integer. Das ist die Definition von integer, die das Modul integert.c bei der Initialisierung ins System einspeist: ~=digit; =0 ~digit; =1 ~digit; =2 ~digit; =3 ~digit; =4 ~digit; =5 ~digit; =6 ~digit; =7 ~digit; =8 ~digit; =9 ~digit; ~=integer { ~=negative; ~=digit :digit; }; Eine Ganzzahl wird als eine Reihe von Ziffern mit einer Basis 10 erfasst, möglicherweise mit dem Flag, dass es sich um eine negative Zahl handelt. Das Modul installiert außerdem einen Tracer, der dafür sorgt, dass bei jeder Instanziierung man mindestens eine Ziffer und maximal einmal das Zeichen Negativ angibt. Ohne Zahlenparser zu benutzen, lässt sich in der Symbolsprache eine Anzahl=302 so definieren: ~= anzahl :integer; ~anzahl { ~digit #3; ~digit #0; ~digit #2; }; Man muss sich vergegenwärtigen, was das bedeutet. Für die Textengine sind Zahlen keine „binären Daten”. Jede Instanz einer Ganzzahl ist ein Symbol des Typs integer, das aus einer Reihe von Symbolen vom Typ digit besteht. Die übliche kompakte Darstellung in der Form "302" muss nicht darüber hinweg täuschen, dass es sich hier um keine opake Entität handelt, sondern nur um einen symbolischen Ausdruck, den man auch anderweitig (zB wie oben in reiner Symbolsprache) darstellen kann. Welche Basis man nimmt, ist übrigens völlig unerheblich. Man hätte auch 256 nehmen und die Ziffer byte nennen können. In einem C-Programm lassen sich Ganzzahlen über die Funktionen store_integer und retrieve_integer in einem nativen Integer C-Datentyp speichern und ablesen. Ob es sich um ein short, int, long etc. handelt, lässt sich in config.h einstellen. Anders als bei boolean fungiert der Tracer für integer nicht nur als Validierer, sondern generiert eine binäre Darstellung in C der Zahl und cacht sie so, dass beim Abfragen die Funktion retrieve_integer den Textbaum nicht mehr durchläuft.

16.08.2019 boolean. Als erster Datentyp auf der Ebene des Systems wurde boolean implementiert. Es besteht aus einer abstrakten Einheit boolean und zwei Instanzen davon mit den Namen true und false. ~=boolean; ~boolean =true; ~boolean =false; Das Modul booleant.c erstellt diese Einheiten beim Initialisieren und setzt einen Tracer auf den Typ boolean, der bei jeder Instanziierung prüft, ob diese einem der zwei möglichen Werte entspricht. Dieser Tracer garantiert, dass jede Benutzung des Typs konsistent ist und dass man keine weiteren booleschen Werte hinzufügt. Außerdem bietet das Modul zwei C-Funktionen store_boolean und retrieve_boolean, mit denen man den C-booleschen Wert einer Texteinheit (also 0 für #false und 1 für #true) über die Einheits-Id setzen bzw. abfragen kann. ~=zufrieden :boolean; ~zufrieden #true;

16.08.2019 Kirschen. Man will nur eine nehmen, diese ist aber so eng mit den anderen verwoben, dass man gleich auch alle anderen in der Hand hält. Ich wollte nur den Datentyp integer implementieren. Habe dann trace implementiert, damit die Ziffer während der Eingabe automatisch — und nicht etwa am Ende der Sitzung explizit ausgelöst — verarbeitet werden können. Da war aber die Versuchung zu groß, die Tracer als Validierer einzusetzen, so dass eine Fehleingabe sofort erkannt und gemeldet wird. Kein großer Akt. Nur: was tun, wenn die Validierung fehlschlägt? Na dann muss auch ein rollback her, das setzt wiederum voraus, dass man Texteinheiten löschen kann. Also jede Menge Funktionalität, die ich für viel später in der Entwicklung vorgesehen hatte. Naja, was solls, ich tue es jetzt. Version 0.3 wird zwar später erscheinen, dafür aber solider sein. Es ist auch eine extrem interessante Reise. Außerdem passt dies gut zum Vorhaben, in dieser Version sich auf die Datenstruktur zu konzentrieren. Roadmap also noch einmal angepasst.

04.08.2019 Unterbewusstsein. Ich habe trace implementiert. Damit lässt sich auf Änderungen und Transaktionen im Text lauschen, um darauf gezielt zu reagieren. Da ist mir aufgefallen, dass trace keinen Zugang auf gewisse Bereiche der Textengine hat, am auffälligsten wohl der Namensraum. Namen werden nicht im Textkorpus abgelegt, Bezeichner kommen nicht als Texteniheiten mit Typ string und den passenden untergeordneten Einheiten mit Role char vor. Sie sind in C in separaten Datenstrukturen implementiert. Das hat zur Folge, dass man zum Beispiel trace nicht einsetzen kann, um auf das Vorkommen von bestimmten Bezeichnern zu reagieren. Ähnliches geschieht mit den Sitzungen. Sie werden nicht explizit als solche im Textkorpus erfasst. In der aktuellen Textengine gibt es also auch ein Unterbewusstsein, einen Bereich, der durch den Text nicht zugänglich ist. Hier ist keine Reflexion möglich. Das ist eine der Grenzen der aktuellen Implementierung.

27.07.2019 Roadmap. Ich habe die Roadmap überarbeitet. Ich bleibe so wie anfangs bei der Aufteilung in drei Vorabversionen bis zur Version 1.0. Version 0.3 implementiert die Datenstruktur, Version 0.6 die Algorithmen, und Version 0.9 bringt die restlichen wesentlichen Neuerungen, die für das Basissystem geplant sind. Danach muss das System nur noch ausgereift und ergänzt werden, damit Version 1.0 für reale Anwendungen nutzbar ist. Mit der Überarbeitung ist die Veröffentlichung der ersten Version nach hinten gerutscht. Denn ich habe entschieden, dass bereits in Version 0.3 trace (Benachrichtigung bei Textänderungen) und der Datentyp Ganzzahl unterstützt werden sollen. Begründung: beide gehören zur grundlegenden Datenstruktur. Ich hatte bisher an die Zahlen als etwas unwesentliches gedacht, das bei der letzten Ausreifung implementiert werden kann. Doch jetzt sehe ich die Zahlen durchaus als Herausforderung, denn sie erfordern eine etwas andere Umsetzung von Binärdaten als die von Strings. Wenn Zahlen implementiert worden sind, dann ist die Unterstützung von anderen Datentypen wie Zeit, Reale oder Komplexe Zahlen nur noch eine Frage von Fleiß ohne theoretisches Interesse.

24.07.2019 Aktionen in Symbolsprache. Neulich habe ich die goto-Funktion aus der Symbolsprache entfernt. Damit bleibt der ursprüngliche Anlass für ihre Implementierung ungelöst. Wie kann man eine Einheit an anderer Stelle als die Erstdefinition ergänzen? Mein aktueller Ansatz ist folgender: Die Symbolsprache soll grundsätzlich syntaktisch keine Aktionen unterstützen und bei der bloßen Spezifikation von Einheiten bleiben. Ein goto soll über ein Mapping mit Texteinheiten erfolgen. So könnte man etwa eine Einheit session.command bereitstellen, die in der aktuellen Sitzung durchzuführenden Befehle bearbeitet. Ein goto würde man veranlassen durch Erfassen von ^session.command ~goto "=uri". Ähnlich sollen alle anderen Aktionen wie hinzufügen, ändern und entfernen von Namen, symbolische Referenzen oder erfassten Einheiten implementiert werden. Alles soll durch explizite Ausführung von Transformationen geschehen, die durch Erfassen bestimmter Einheiten veranlasst wird. Mit diesem Ansatz bleibt die Symbolsprache aufs theoretische Minimum reduziert, das damit erprobt, verfeinert und zum Schluss belegt werden wird. Darüberhinaus hat das aber einen Lerneffekt, denn eine zusätzliche Syntaxform versteckt interne logische Zusammenhänge und verdunkelt das Verständnis. Die Symbolsprache soll Basis und Anker für die theoretische Durchdringung bleiben. Natürlich kann man in der Skriptsprache und in anderen Sprachen und Notationen die Syntax erweitern. Diese sind Gebrauchssprachen, deren Zweck in der Erfahrung des Nutzers und nicht in der theoretischen Reinheit liegt.

21.07.2019 Goto entfernt. Eingeführt habe ich in der Symbolsprache einmal die goto-Funktion, damit man eine Einheit an mehreren Stellen definieren kann. Das hat sich interessanterweise beim Generieren von Symbolsprache negativ ausgewirkt. Das war der konkrete Fall: ~=uri; ~=gone :uri; #uri { ~=uri :uri; ~=gone :gone; }; Hier wird eine Einheit uri definiert, davon eine Einheit gone abgeleitet, und danach ausgesagt, dass jede Uri wiederum untergeordnete Uris haben kann, darunter welche mit der Rolle gone, deren Typ die eingangs eingeführte Einheit gone ist. Parsen lässt sich das gut. Nur: beim Formatieren des geparsten in der Symbolsprache erhält man dieses: ~=uri { ~=uri :uri; ~=gone :gone; }; ~=gone :uri; Das ist kein gültiger Ausdruck. Das Parsen schlägt in der dritten Zeile fehl, weil der Typ gone nicht definiert ist (er wird nämlich zwei Zeilen später definiert). Dieses Problem hat mich dazu gebracht, die goto-Funktion aus der Symbolsprache zu entfernen. Sie war mir von Anfang an etwas suspekt und die Sprache gewinnt ohne sie wieder an Konsistenz. Wie erfasst man nun solche Einheiten? Einfach so: ~=uri; ~=gone :uri; ~#uri { ~=uri :uri; ~=gone :gone; }; Das Tilde-Zeichen in ~#uri ist der einzige Unterschied und hat zur Folge, dass man hier nicht zu einer bereits existierenden Einheit „springt”, sondern eine neue Einheit erfasst, die aber symbolisch mit der anderen equivalent ist. Der Symbolspracheformatierer generiert den folgenden Ausdruck: ~=uri; ~=gone :uri; ~#uri { ~=uri :uri; ~=gone :gone; }; Dieser lässt sich nun korrekt wieder parsen.

20.07.2019 Unix Toolset. Hatte den Einfall, bei der Veröffentlichung der Textengine 0.3 ein paar kleine Tools bereitzustellen, etwa tselect und tproject, mit denen man auf Unix-Art symbolsprachliche Ausdrücke bearbeiten könnte. Die Idee war, mit ein paar einfachen Mitteln die Textengine mit bash statt mit C-Programmierung einsetzen zu können. Leider hat sich das Vorhaben aber als zu kompliziert entpuppt. Auch wenn man auf jegliche Spezialsyntax verzichtet und nur Symbolsprache eingesetzt hätte, hätte man die Transformationen select und project implementieren müssen. Das hätte aber wesentliche Elemente von der geplanten Version 0.6 vorweggenommen. Außerdem wirft die Benutzung als Unix-Filter die Frage der Definitionen aller verwiesenen Einheiten auf — die Ausgabe jeden Filters muss gültiger Text sein, damit sie vom nächsten Filter geparst und verarbeitet werden kann, so müssen entweder vorausgesetzte Definitionen automatisch ausgegeben oder irgend ein Import/Export-Mechanismus eingeführt werden. Entscheide mich angesichts dessen dafür, bei der vorgesehenen 0.3 zu bleiben, auch wenn sie wenig nützlich ist. Erst diesen Schritt sichern, dann den nächsten gehen.

20.07.2019 Wieder da. Endlich scheine ich wirklich wieder in die Entwicklung einzusteigen. Fast zwei Jahre war leider die Pause lang. Erstes Erfolgserlebnis: die Fehler behoben zu haben, die noch auf der To-Do-Liste standen. Darunter waren Fehler bei der Generierung von symbolsprachlichen Ausdrücken, die mich lange Zeit Kopfschmerzen bereitet haben. Ich fühle mich sehr erleichtert. Geplantes weiteres Vorgehen ist nun: kleine Anpassungen an tesh, was Argumente und Ausgabe angeht; dann weitere Tests mit der Beta bis zur Veröffentlichung. Die Tests bestehen darin, das System auf reale Fälle anzuwenden, um die Datenstruktur an sich sowie ihr Parsen und Generieren zu erproben.

20.07.2019 Unzufriedenstellend. Bugfix aufs Geratewohl — unzufriedenstellend. Blicke nicht durch, welche Einheiten beim Durchlaufen eines Cursors nun mit ENTER und welche ohne besucht werden. Habe einen enter=1 hinzugefügt und damit einen Testfall erfolgreich gelöst, ohne andere Testfälle zu beeinträchtigen. Ich lasse es also hier. Die Lust voranzutreiben oder vielleicht auch die Trägheit, mich die Mühe zu geben, führen dazu, dass ich erstmal das Thema abhake.

28.02.2019 Reflexion. Etwas, das in der Textengine von Anfang an anders ist als in den aktuellen Programmiersprachen, ist die Fähgikeit zur Reflexion. Lädt man eine Funktion oder ein Modul, so kann man den Code durchlaufen und zum Beispiel als String formatieren und ausdrücken, oder darin gezielt nach dem Einsatz oder der Definition von einem bestimmten Symbol suchen. Die heutigen Programmiersysteme tun es sich damit schwer und, falls sie etwas in die Richtung überhaupt anbieten, benötigen sie zusätzliche syntaktische Hilfsmittel oder gar externe Programme. In der Textengine ergibt sich die Möglichkeit der Reflexion auf Anhieb und grundsätzlich über alles, vom Code bis zur aktuellen Sitzung. Außerdem stehen dafür alle Mittel zur Verfügung, die die Textengine für den Umgang mit Text bereitstellt, was ja ihr Kerngeschäft ist. Insgesamt wird allein schon deshalb die Textengine ein ganz anderes Erlebnis beim Programmierer hervorbringen, ein Gefühl von selbsverständlicher Transparenz und tiefer Erkenntnis, das wir heute nicht einmal ansatzweise verspüren. [Nachträgliche Korrektur: Reflexion ist nicht "grundsätzlich über alles" möglich — die aktuelle Textengine hat hier auch Grenzen.]

23.02.2019 Aktueller Stand. Es ist vielleicht ratsam, sich den aktuellen Stand zu vergegenwärtigen und einen Blick auf die nächsten Schritte zu werfen. Vor der extern bedingten Pause stand die Textengine kurz vor der Veröffentlichung einer minimalen Version 0.3, die den Text als Datenstruktur (genauer: Grundeinheiten) implementiert und durchlaufen kann. Das Programm tesh (das die Textengine Shell werden soll) ist in der Lage, Dateien in der Symbolsprache zu lesen und einen symbolsprachlichen Ausdruck zu generieren, der die erfassten Texteinheiten wiedergibt. Einfache Abfragen, die Referenzgenera einschränken, lassen sich programmatisch durchführen. Es funktioniert alles gut bis auf die Generierung von Namen, die momentan mit dem Parsen nicht idempotent ist. Zunächst werde ich daran arbeiten, diese Unstimmigkeiten zu beseitigen und die angekündigte Version 0.3 zu veröffentlichen. Doch richtig spannend wird es sein, sich auf den Weg zu Version 0.6 zu machen. Diese soll in der Lage sein, einige grundlegende Text-Transformationen (Operationen auf dem Text, die selbst als Text erfasst werden) zu implementieren. Hier verlassen wir die Pfade, die das Vorläuferprogramm beging, und betreten wir Neuland. Nun kommen zwei Umstände zum Vorschein. Zum einen wäre es sehr ratsam, Erfahrung mit den Programmiersprachen Lisp und Rebol, die den Code als Daten behandeln, zu sammeln und davon zu lernen. Zum anderen wäre es wahrscheinlich sinnvoll, erst einen Prototyp zu entwickeln und auszureifen, bevor man die Implementierung in C angeht. Aus diesen Gründen spiele ich jetzt mit dem Gedanken, einen Prototyp der Textengine v. 0.6 in Lisp zu implementieren, konkret in Racket, das das an sich schon mächtige Lisp mit aufregenden Möglichkeiten der syntaktischen Gestaltung erweitert.

Textengine

img/feed.png RSS-Feed

Notizen

Denkpause

Substrings Formatieren

String-Formatierung

Formatregistrierung

Subtyp und Instanz

Format Ausreifung

Rück- und Fortschritte

Integer Formatierung

Zäh

Rollback

integer

boolean

Kirschen

Unterbewusstsein

Roadmap

Aktionen in Symbolsprache

Goto entfernt

Unix Toolset

Wieder da

Unzufriedenstellend

Reflexion

Aktueller Stand