<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>xscDevBlog - LastSharp &#38; Co. &#187; eigene scriptsprache</title>
	<atom:link href="http://dev.xscheme.de/tag/eigene-scriptsprache/feed/" rel="self" type="application/rss+xml" />
	<link>http://dev.xscheme.de</link>
	<description>Der xscheme-DevelopmentBlog</description>
	<lastBuildDate>Sun, 23 May 2010 11:40:10 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Wie entwickle ich meine eigene Scriptsprache? (Teil 3: Syntax)</title>
		<link>http://dev.xscheme.de/2009/08/wie-entwickle-ich-meine-eigene-scriptsprache-teil-3/</link>
		<comments>http://dev.xscheme.de/2009/08/wie-entwickle-ich-meine-eigene-scriptsprache-teil-3/#comments</comments>
		<pubDate>Sun, 30 Aug 2009 11:26:44 +0000</pubDate>
		<dc:creator>WordPress</dc:creator>
				<category><![CDATA[HowTo]]></category>
		<category><![CDATA[Projekte]]></category>
		<category><![CDATA[Theorie]]></category>
		<category><![CDATA[eigene programmiersprache]]></category>
		<category><![CDATA[eigene scriptsprache]]></category>

		<guid isPermaLink="false">http://dev.xscheme.de/?p=952</guid>
		<description><![CDATA[Nun, wie weit sind wir bisher? Wir haben einen Lexer, der unsere Eingabe in Einzelteile spaltet, und wir können einfache Ausdrücke in eine verwertbare Form bringen. Was wir bisher noch nicht (bewusst) gemacht haben, ist, einen Ausdruck zu analysieren und zu prüfen, ob er sinnvoll ist oder nur unzusammenhängend aneinandergereihte Token enthält.
Zur Erinnerung: sowohl &#8220;1+2+3&#8243; [...]]]></description>
			<content:encoded><![CDATA[<p>Nun, wie weit sind wir <a href="http://dev.xscheme.de/2009/07/eigene-programmiersprache-scriptsprach/">bisher</a>? Wir haben einen <a href="http://dev.xscheme.de/2009/08/wie-entwickle-ich-meine-eigene-scriptsprache-teil-1/">Lexer</a>, der unsere Eingabe in Einzelteile spaltet, und wir können einfache Ausdrücke <a href="http://dev.xscheme.de/2009/08/wie-entwickle-ich-meine-eigene-scriptsprache-teil-2/">in eine verwertbare Form bringen</a>. Was wir bisher noch nicht (bewusst) gemacht haben, ist, einen Ausdruck zu analysieren und zu prüfen, ob er sinnvoll ist oder nur unzusammenhängend aneinandergereihte Token enthält.</p>
<p>Zur Erinnerung: sowohl &#8220;1+2+3&#8243; als auch &#8220;1+(&#8221; wären Zeichenfolgen, die der Lexer akzeptieren und ohne zu meckern zerkleinern würde. Das ist auch in Ordnung so, da die <strong>syntaktische Prüfung</strong> ohnehin eher Aufgabe des Parsers ist. Aber was genau gilt es denn dabei zu beachten?</p>
<h2>&#8220;Syntaxfragen&#8221;</h2>
<ul>
<li>Sind genau so viele öffnende wie schließende Klammern vorhanden und passen diese zusammen?</li>
<li>Stimmt die Zahl der Parameter, mit der eine Funktion aufgerufen wird?</li>
<li>Stimmen die Parametertypen, mit denen eine Funktion aufgerufen wird? (&#8220;+&#8221; arbeitet normalerweise nur auf Zahlen, der Punkt z.B. in PHP auf Strings, etc&#8230;)</li>
<li>Befinden sich Operatoren an den richtigen Positionen? (Die Fakultät einer Zahl wird z.B. durch Anhängen eines Ausrufezeichens gekennzeichnet: &#8220;3!&#8221;)</li>
<li>Darf eine Funktion geschachtelt vorkommen? (z.B. wäre &#8220;echo(echo(1))&#8221; eher sinnlos, wenn &#8220;echo&#8221; die Ausgabefunktion ist)</li>
<li>Darf eine Funktion/ein Konstrukt in einer untergeordneten Umgebung vorkommen? (Das wäre z.B. der Fall, wenn man Funktionsdefinitionen innerhalb von Funktionsdefinitionen zulässt.)</li>
</ul>
<p>Neben der Syntax gibt es noch weitere Punkte, die eine Programmier-/Scriptsprache ausmachen. Eine Auflistung findet man z.B. <a href="http://www.pilgerer.org/pw/ProgrammierSprachen">hier</a>.</p>
<p><span id="more-952"></span></p>
<h2>Einiges ist schon erledigt</h2>
<p>Der im vorhergehenden Teil präsentierte Shunting-Yard-Algorithmus nimmt uns die Überprüfung der Klammern, sowie der Position der Parameter bereits ab. Des weiteren ist es unumgänglich zur Erstellung des Syntaxbaumes die Anzahl der Parameter zu kennen, die eine bestimmte Funktion benötigt. Wir können also unsere Liste auf die Hälfte kürzen.</p>
<h2>Einiges noch nicht</h2>
<p>Der Syntaxbaum selbst macht die Syntaxprüfung recht simpel. Aber zuerst müssen wir für jede Funktion, jeden Operator und jedes Terminalsymbol festlegen, welche syntaxktischen Eigenschaften es hat. Das wird letztlich mittels einer Klasse <em>SyntaxDefinition</em> realisiert werden, die die entsprechenden Daten kapselt.</p>
<p>Wichtig ist hierbei: wir betrachten all die genannten Datentypen als Funktionen, auch die Terminalsymbole. Jede Funktion hat einen Rückgabetyp und beliebig viele Parametertypvarianten. Eine &#8220;Zahl&#8221; wäre also eine Funktion mit dem Parametertyp &#8220;Zahl&#8221; und dem Rückgabetyp &#8220;Zahl&#8221;.</p>
<p>Eine beispielhafte Aufstellung für die Syntax einer Sprache wäre die folgende:</p>
<ul>
<li>Terminalsymbol <em>Integer</em>:
<ul>
<li>Eingabe: Integer</li>
<li>Rückgabe: Integer</li>
<li>Verschachtelung erlaubt</li>
<li>Unterordnung erlaubt</li>
</ul>
</li>
<li>Terminalsymbol <em>String</em>:
<ul>
<li>Eingabe: String</li>
<li>Rückgabe: String</li>
<li>Verschachtelung erlaubt</li>
<li>Unterordnung erlaubt</li>
</ul>
</li>
<li>Terminalsymbol <em>Identifier</em>:
<ul>
<li>Eingabe: Identifier</li>
<li>Rückgabe: Identifier</li>
<li>Verschachtelung erlaubt</li>
<li>Unterordnung erlaubt</li>
</ul>
</li>
<li>Funktion <em>DefConstant</em>:
<ul>
<li>Eingabe: (Identifier, Integer) oder (Identifier, String)</li>
<li>Rückgabe: keine</li>
<li>Verschachtelung nicht erlaubt</li>
<li>Unterordnung erlaubt</li>
</ul>
</li>
<li>Operator <em>Plus</em>:
<ul>
<li>Eingabe: (Integer, Integer)</li>
<li>Rückgabe: Integer</li>
<li>Verschachtelung erlaubt</li>
<li>Unterordnung erlaubt</li>
</ul>
</li>
<li>&#8230;</li>
</ul>
<p>Es gibt aber auch Funktionen, deren Rückgabetyp erst zum Ausführungszeitpunkt feststeht. Bestes Beispiel ist hier die Auswertung von Variablen, die ja Werte verschiedenster Typen enthalten können.</p>
<p>Wie würde also nun die Überprüfung der Typkorrektheit und der Verschachtelung ablaufen? Wie bereits erwähnt nutzen wir hierfür den Syntaxbaum und testen jeden einzelnen Knoten unter Berücksichtigung der Kindknoten:</p>
<pre>               operator : +
              /            \
        integer : 1     operator : *
                       /            \
                  integer : 2   function : echo
                                     |
                                constant : e</pre>
<p>Für unseren Test entspräche das dem folgenden Baum (Notation: &#8220;Rückgabe / Verschachtelung erlaubt?&#8221;):</p>
<pre>               integer / ja
              /            \
        integer / ja    integer / ja
                       /            \
                  integer / ja  void / nein
                                     |
                                runtime / ja</pre>
<p>Die Wurzel (das Plus) erwartet zwei Parameter des Typs &#8220;integer&#8221;. Eine Überprüfung der Kindknoten zeigt, dass diese genau diesen Rückgabetyp besitzen &#8211; also alles in Ordnung , auch die Verschachtelung.<br />
Betrachtet man den rechten Ast weiter, sieht man zwei Probleme: die Multiplikation benötigt zwei &#8220;integer&#8221;-Parameter, erhält aber &#8220;integer&#8221; und &#8220;void&#8221; (<em>void</em> ist der Ausdruck für &#8220;keine Rückgabe&#8221;); und die &#8220;echo&#8221;-Funktion kann nicht geschachtelt auftreten, befindet sich aber auf Ebene 2.</p>
<p>Einen dieser Fehler sollte der Parser letztlich ausgeben und abbrechen!</p>
<h2>Einiges muss warten</h2>
<p>Ob ein Ausdruck in einer untergeordneten Umgebung auftritt, kann ein reiner Parser nicht wissen. Man könnte ihm zwar diesbezügliche Informationen zukommen lassen (und sollte das auch, wenn man vorhat, einen Compiler o.Ä. zu schreiben), der einfachste Ort für diese Überprüfung ist jedoch die Auswertung. (Das ist auch scriptsprachentauglich.)</p>
<p>Was die Parametertypen angeht, die erst bei der Ausführung feststehen, kann man entweder mit regulären Ausdrücken arbeiten und mit deren Hilfe die Teilergebnisse untersuchen, bevor man die übergeordnete Funktion aufruft, oder man überlässt das ganz der jeweiligen Funktion selbst. Geschmacksache.</p>
<h2>Spezielle Konstrukte</h2>
<p>In jeder Sprache gibt es bestimmte Schlüsselwörter, die spezielle Konstrukte beschreiben/einleiten. Gemeint ist soetwas wie:</p>
<pre>def a = 2</pre>
<p>Der Shunting-Yard-Algorithmus kommt aber nur mit Operatoren oder mit Funktionen der Form &#8220;f(p1, p2, &#8230;)&#8221; klar.<strong> </strong></p>
<h3><strong>Kein großes Problem?</strong></h3>
<p>Gut, das wäre jetzt kein großes Problem für das oben stehende Beispiel: man definiert &#8220;def&#8221; und &#8220;=&#8221; als Operatoren, wobei &#8220;def&#8221; ein Präfix-Operator mit genau einem Parameter ist und eine höhere Priorität als &#8220;=&#8221; hat. Der Operator &#8220;def&#8221; legt eine leere Variable mit dem übergebenen Namen an und liefert diesen Namen als Rückgabewert.<br />
&#8220;=&#8221; wiederum ist ein Infix-Operator mit zwei Parametern, das die (existierende!) Variable des übergebenen Namens mit dem übergebenen Wert füllt.</p>
<p>Der Syntaxbaum für oben genannten Ausdruck wäre dann:</p>
<pre>              operator : =
              /          \
     operator : def   integer : 2
             |
     identifier : a</pre>
<p>Und schon hätten wir das  gewünschte Verhalten, allerdings auf Kosten der Übersichtlichkeit in der Implementierung, sowie eines sehr großen Freiraums in der Grammatik. Immerhin wäre so etwas dann auch erlaubt:</p>
<pre>(((((def a))))) = 2
def b = def a</pre>
<p>Ein Verhindern von Verschachtelungen ist hier nicht mehr möglich.</p>
<h3><strong>Bessere Lösung</strong></h3>
<p>Wir brauchen einen Mechanismus, der aus &#8220;def a = 2&#8243; so etwas macht wie &#8220;def(a, 2)&#8221;, also einen regulären Funktionsaufruf. Es handelt sich hierbei um eine Mustererkennung auf Basis bereits gelesener Token, d.h. die eigentliche Umwandlung findet nach dem erstmaligen Einlesen der Eingabesequenz durch den Lexer statt:</p>
<pre>keyword : def
identifier : a
operator : =
integer : 2</pre>
<p>wird zu</p>
<pre>function : def
open : (
identifier : a
comma : ,
integer : 2
close : )</pre>
<p>In meinen Augen bietet sich hierfür wieder  der in <a href="http://dev.xscheme.de/2009/08/wie-entwickle-ich-meine-eigene-scriptsprache-teil-1/">Teil 1</a> unter Alternative 4 vorgestellte Algorithmus an, der z.B. mit einem Automaten arbeiten könnte, der die folgende Akzeptanzfunktion besitzt (Vorsicht: Pseudocode!):</p>
<pre>private int tokenNumber = -1;
public bool accept(Token t)
{
    tokenNumber++;
    return
        (tokenNumber == 0 &amp;&amp; "t ist vom Typ 'keyword' und hat den Wert 'def'") ||
        (tokenNumber == 1 &amp;&amp; "t ist vom Typ 'identifier'") ||
        (tokenNumber == 2 &amp;&amp; "t ist vom Typ 'operator' und hat den Wert '='") ||
        tokenNumber &gt; 2;
}</pre>
<p>Gleichzeitig müssten irgendwo die relevanten Token gesichert werden, damit die Umwandlung in eine Funktion später auch reibungslos vonstatten gehen kann.</p>
<p>Ich würde sagen, damit haben wir eine vernünftige Lösung gefunden.</p>
<h2>Fazit</h2>
<p>Wir können nun unseren Syntaxbaum auf syntaktische Merkmale hin untersuchen und spezielle Ausdrücke und Konstrukte berücksichtigen. Langsam sollten wir uns also an die Ausführung eines Ausdrucks machen.</p>
<p>Ein in diesem Artikel häufig verwendetes Wort war &#8220;Umgebung&#8221;. Es handelt sich dabei um den Speicher für Variablen, Funktionen, etc&#8230; Ohne diesen ist die Entwicklung einer Scriptsprache relativ witzlos, weswegen wir an genau dieser Stelle weitermachen werden.</p>
<h2>Inhalt</h2>
<ol>
<li><a href="../2009/07/eigene-programmiersprache-scriptsprach/">Einführung: Ein Abenteuer in Teilen</a></li>
<li><a href="http://dev.xscheme.de/2009/08/wie-entwickle-ich-meine-eigene-scriptsprache-teil-1/">Der Lexer</a></li>
<li><a href="../2009/08/wie-entwickle-ich-meine-eigene-scriptsprache-teil-2/">Grundlagen des Parsens</a></li>
<li><strong>Syntax</strong></li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://dev.xscheme.de/2009/08/wie-entwickle-ich-meine-eigene-scriptsprache-teil-3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Wie entwickle ich meine eigene Scriptsprache? (Teil 2: Grundlagen des Parsens)</title>
		<link>http://dev.xscheme.de/2009/08/wie-entwickle-ich-meine-eigene-scriptsprache-teil-2/</link>
		<comments>http://dev.xscheme.de/2009/08/wie-entwickle-ich-meine-eigene-scriptsprache-teil-2/#comments</comments>
		<pubDate>Sat, 15 Aug 2009 13:51:46 +0000</pubDate>
		<dc:creator>WordPress</dc:creator>
				<category><![CDATA[HowTo]]></category>
		<category><![CDATA[Projekte]]></category>
		<category><![CDATA[Theorie]]></category>
		<category><![CDATA[eigene programmiersprache]]></category>
		<category><![CDATA[eigene scriptsprache]]></category>

		<guid isPermaLink="false">http://dev.xscheme.de/?p=905</guid>
		<description><![CDATA[Einen wichtigen Schritt haben wir an dieser Stelle bereits hinter uns: Der hier beschriebene Lexer verwandelt eine Eingabesequenz wie &#8220;1+3*(4-2)&#8221;  in eine Liste von Tokens mit Typ und Wert:
zahl        :    1
operator    :    +
zahl       [...]]]></description>
			<content:encoded><![CDATA[<p>Einen wichtigen Schritt haben wir an dieser Stelle bereits hinter uns: Der <a href="http://dev.xscheme.de/2009/08/wie-entwickle-ich-meine-eigene-scriptsprache-teil-1/">hier beschriebene</a> Lexer verwandelt eine Eingabesequenz wie &#8220;1+3*(4-2)&#8221;  in eine Liste von Tokens mit Typ und Wert:</p>
<pre>zahl        :    1
operator    :    +
zahl        :    3
operator    :    *
open        :    (
zahl        :    4
operator    :    -
zahl        :    2
close       :    )</pre>
<p>Damit haben wir eine maschinenlesbare Repräsentation des gewünschten Ausdrucks &#8211; allerdings keine Garantie dafür, dass dieser syntaktisch korrekt ist. Ebensowenig kann eine Maschine, die diese Liste nun vorgelegt bekommt, &#8220;einfach mal so&#8221; den Wert des Ausdrucks berechnen, da ihr Informationen zur Auswertungsreihenfolge fehlen. (Wenn 1+3 zuerst berechnet wird, lautet das Ergebnis 8, wenn 3*(4-2) zuerst berechnet wird, lautet es 7.)</p>
<p>Wir brauchen also eine Darstellung unseres Ausdrucks, die bezüglich der Reihenfolge der Auswertung eindeutig ist und eine Maschine, die diese erstellt  &#8211; und wie könnte es anders sein: auch dieses Problem wurde bereits einmal gelöst.</p>
<p>Wandeln wir also auf den Spuren von <a href="http://de.wikipedia.org/wiki/Edsger_Wybe_Dijkstra">Edsger W. Dijkstra</a>.</p>
<p><span id="more-905"></span></p>
<h2>Reverse Polish Notation</h2>
<p>Der polnische Mathematiker Jan Łukasiewicz entwickelte etwa 1920 die sog. <a href="http://en.wikipedia.org/wiki/Polish_notation">Polnische Notation</a> für arithmetische Ausdrücke, die sich dadurch auszeichnete, dass jedem Operator (&#8220;+&#8221;, &#8220;-&#8221;, &#8230;) direkt seine Operanden (Zahlen oder weitere arithmetische Ausdrücke) folgten. Durch diese Präfixnotation ergibt sich die Möglichkeit, auf Klammern, Kommas, etc&#8230; verzichten zu können, wenn man weiß, wie viele Operanden ein Operator benötigt:</p>
<pre>1+2+3         ==&gt;        + 1 + 2 3           (auch: + + 1 2 3)
1+3*(4-2)     ==&gt;        + 1 * 3 - 4 2
(3+4)*(4*3-1) ==&gt;        * + 3 4 - * 4 3 1</pre>
<p>Anhand des letzten Beispiels will ich die Auswertung so eines Ausdrucks beschreiben. Das Prinzip ist: &#8220;Werte den Ausdruck, der nur Zahlen als Operanden hat, als nächstes aus!&#8221;</p>
<pre>
<pre>   * + 3 4 - * 4 3 1        |   Das + bezieht sich auf 3 und 4!
=  * 7     - * 4 3 1        |   Das * bezieht sich auf 4 und 3!
=  * 7     - 12    1        |   Das - bezieht sich auf 12 und 1!
=  * 7     11               |   Das * bezieht sich auf 7 und 11!
=  77</pre>
</pre>
<p>Wie bereits erwähnt,<strong> funktioniert so eine Auswertung nur, wenn man weiß, wie viele Operanden/Parameter ein Operator/eine Funktion benötigt</strong>. Deswegen haben wir uns bei der Entwicklung des Lexers auch die Mühe gemacht, diese Information immer irgendwie bei der Hand zu haben.</p>
<p>Was Dijkstra nun gemacht hat, war, aus der Präfix- eine Postfixnotation zu machen, die sog. <a href="http://en.wikipedia.org/wiki/Reverse_Polish_notation">Reverse Polish Notation (RPN)</a>. Hier stehen nun alle Operanden <em>vor</em> dem Operator zu dem sie gehören:</p>
<pre>1+2+3         ==&gt;        1 2 3 + +          (auch: 1 2 + 3 +)
1+3*(4-2)     ==&gt;        1 3 4 2 - * +
(3+4)*(4*3-1) ==&gt;        3 4 + 4 3 * 1 - *</pre>
<p>Sowohl <em>Polish Notation</em> als auch <em>Reverse Polish Notation</em> beschreiben (unter der Voraussetzung, dass man die Anzahl der Parameter/Operanden kennt) eine <strong>eindeutige Auswertungsreihenfolge</strong>. Bei der letztlichen Auswertung ist die RPN aber ihrem Vorfahren in Bezug auf Speicherverbrauch und Verständlichkeit dann doch voraus. Die Regel: &#8220;Wenn ein Operator auftaucht, werte ihn aus!&#8221;</p>
<p>Wir benötigen hierbei einen <a href="http://de.wikipedia.org/wiki/Stapelspeicher">Stack</a>, der die Zwischenergebnisse speichert und der diese bei Auftreten eines Operators zur Berechnung zur Verfügung stellt.</p>
<pre>
<pre>----------------------------------------------------------------------------------
Eingabe            | Stack    |  Kommentar
----------------------------------------------------------------------------------
3 4 + 4 3 * 1 - *  |          |  Beginn des Algorithmus
4 + 4 3 * 1 - *    | 3        |  Zahl (3): auf den Stack!
+ 4 3 * 1 - *      | 3 4      |  Zahl (4): auf den Stack!
4 3 * 1 - *        | 7        |  Operator (+): hole 3 und 4, berechne, speichere
3 * 1 - *          | 7 4      |  Zahl (4): auf den Stack!
* 1 - *            | 7 4 3    |  Zahl (3): auf den Stack!
1 - *              | 7 12     |  Operator (*): hole 4 und 3, berechne, speichere
- *                | 7 12 1   |  Zahl (1): auf den Stack!
*                  | 7 11     |  Operator (-): hole 12 und 1, berechne, speichere
                   | 77       |  Operator (*): hole 7 und 11, berechne, speichere
                   | 77       |  Eingabe leer, Ergebnis auf Stack.</pre>
</pre>
<p>Dies umzusetzen ist kein großes Problem und spricht in jedem Fall für die <em>Reverse Polish Notation</em> als Darstellung eines auszuwertenden Ausdrucks. Und dann wäre da noch die kleine, aber feine Tatsache, dass es einen Algorithmus gibt, der die <strong>Umwandlung</strong> &#8220;normaler&#8221; (Infix-)Ausdrücke (à la &#8220;1+2*f(3)&#8221;) in diese Notation vornimmt.</p>
<h2>Der Shunting-Yard-Algorithmus</h2>
<p>Der <a href="http://en.wikipedia.org/wiki/Shunting_yard_algorithm">Shunting-Yard-Algorithmus</a> (zu deutsch: <em>Rangierbahnhofalgorithmus</em>) ist eine ebenfalls von Dijkstra entwickelte Methode zur Umwandlung von Infix-Ausdrücken in Postfix-Notation. Sein Name kommt daher, dass seine Funktionsweise den Abläufen in einem Rangierbahnhof entspricht: alle terminalen Symbole (z.B. Zahlen, Variablen, &#8230;) sind Waren und Güter, die von Zügen (den nichtterminalen Symbolen wie Operatoren und Funktionen) mitgenommen werden müssen.</p>
<p>Die folgende Animation zeigt (vereinfacht) das Prinzip des Algorithmus. Er basiert darauf, dass in der Zugwarteschlange immer der Zug mit der höchsten Priorität (in der Realität z.B. die Geschwindigkeit) ganz vorne steht. Will sich ein langsamerer Zug einreihen, müssen also zuerst alle schnelleren Züge losgefahren sein. (Jeder Zug kann beliebig viele Waren transportieren und nimmt beim Abfahren alle