Seite 1 von 1

1char - Meine kleine Scriptsprache

Verfasst: Mittwoch 9. Juli 2008, 14:33
von DrChAoS
Hallo alle zusammen,
Den kleinen Interpreter den ich ja praktisch schon im "Allgemeine Fragen" Forum "angekündigt" habe ist jetzt so weit fertig dass ich ihn hier nochmal komplett und funktionierend vorstellen möchte.

Ich habe die Sprache 1char (gesprochen onechar) genannt, da das erste Zeichen in einer Zeile immer der Befehl ist.

Die Syntax: [BEFEHL][PARAMETER 1],[PARAMETER 2],[PARAMETER 3],usw...
Wichtig ist hierbei dass zwischen dem Befehl und den Parametern keine Leerzeichen stehen.

Variablen:
Man hat ein Array Names x zur Verfügung. auf die einzelnen Elemente greift man zu in dem man einfach die Zahl des Feldes hinten anhängt. z.B: x3

Die Befehle:
. Der Punkt gibt einen Text oder eine Variable aus.
Beispiel:
."Hallo",x5
Schreibt man statt x5, a5 wird der Integer Wer in der Variablen x5 als ASCII Zeichen ausgegeben.

, Ließt einen Integer Wert in eine Variable ein.
Beispiel:
,x3

+ Addiert zum Parameter 1 den Parameter 2 dazu.
Beispiel:
+x6,x3

- Subtrahiert den 2. Paramter von dem 1. Parameter.
Beispiel:
-x6,x3

* Multipliziert beide Parameter miteinander.
Beispiel:
*x3,x2

/ Dividiert den 1. Parameter durch den 2.
Beispiel:
/x3,x8

= Ordnet dem Parameter 1 den Wert von Parameter 2 zu.
Beispiel:
=x2,5

< Überspringt die nächste Zeile wenn der Parameter 1 größer als der Parameter 2 ist.
Beispiel:
<x1,x2

> Überspringt die nächste Zeile wenn der Parameter 1 kleiner als der Parameter 2 ist.
Beispiel:
>x1,x2

j Springt zu einer Sprungmarke.
Beispiel:
j:start

: Markiert eine Sprungmarke.
Beispiel:
:start

e Beendet das Programm
Beispiel:
e

Ich habe hier als Beispiel das Programm 99 Bottles of Beer geschrieben:

Code: Alles auswählen

#99 Bottles of Beer 
#von Dr.ChAoS
#geschrieben in 1char
=x1,99
:start
.x1," bottles of beer on the wall"
.x1," bottles of beer!"
."You take one down, and pass it around"
-x1,1
<x1,1
j:nomore
.x1," bottles of beer on the wall!"
.""
j:start

:nomore
."No bottles of beer on the wall"
e
Und zuletzt dann noch den Interpreter: http://paste.pocoo.org/show/78983/
Um ein Programm zu interpretieren einfach folgendes in die Konsole eingeben:

python 1char.py 1char_code.txt

Warscheinlich ist nicht alles "Pythonisch" geschrieben, bzw ganz sicher sogar, aber das war praktisch nur eine Übung für mich, da Python für mich noch neu ist. Aber ich muss sagen die Art mit Python zu programmieren hat einen gewissen Reiz. (Wobei ich trotzdem gerne mit C/C++ Programm schreibe).
MfG
Dr.ChAoS

Verfasst: Mittwoch 9. Juli 2008, 15:08
von DasIch
Für Fehler sollte man imho besser eigene Exceptions definieren und dann raise benutzen. Das geht z.B. so:

Code: Alles auswählen

class AnyError(Exception):
    def __init__(self, anything):
        self.anything = anything
    def __str__(self):
        return '%s happend' % self.anything
        
raise AnyError('FooBar')
#Ausgabe:
#Traceback (most recent call last):
  #File "test.py", line 7, in <module>
    #raise AnyError('FooBar')
#__main__.AnyError: FooBar happend

Verfasst: Mittwoch 9. Juli 2008, 15:19
von Hyperion
Nette Idee :-)

Meine Mini-Kritik:
- Kaum Kommentare
- Code-Ausführung auf Modul-Ebene ([wiki]Import)[/wiki]
- Parameter besser abfragen (http://docs.python.org/lib/module-optparse.html)

Verfasst: Mittwoch 9. Juli 2008, 15:44
von BlackJack
Das sieht ziemlich gruselig aus. So überhaupt nicht nach Python. Und auch vom Konzept her ist das ständige neu parsen von Zeilen nicht so der Hit. Man sollte das mindestens in zwei Phasen aufteilen: Kompilieren und Ausführen. Einfach die Zeilen in Tupel/Listen mit Kommando und eventuelle Argumente aufteilen reicht ja schon.

Ansonsten ist das Kopieren von Speicherinhalten etwas kompliziert, weil '=' ja anscheinend nur literale Werte als zweites Argument erlaubt. Um von `x0` nach `x1` zu verschieben muss man zwei Befehle bemühen:

Code: Alles auswählen

# Zuweisung x1 := x0
=x1,0
+x1,x0
Das ständige Widerholen von ``lines`` sollte man pro Schleife nur einmal machen und das Ergebnis an einen Namen binden.

Bessere Namen wären nicht schlecht. `current_line_nr` statt `i`, `variables` statt `x`. `jmp` hiesse besser `labels` oder `targets`. Was ist `op`? Warum ist das global? Warum ein `dict` und keine Liste, die an anderen Stellen einige Indexe ersparen würde?

Warum sind die Variablen in einem `dict` gespeichert, wenn sie doch durch ganze Zahlen angesprochen werden? Klingt auch eher nach einer Liste. `lines` ebenfalls.

Du magst anscheinend keine Listen. Denn auch `tok` in `token()` sollte eine sein. Auch hier kann man sich dadurch wieder unnötige Indexe sparen. Was die Funktion genau macht, sollte man dokumentieren, die ist nämlich nicht besonders übersichtlich.

`tmp_lines` ist überflüssig, man kann über Dateiobjekte direkt iterieren. Und für die Zeilennummern gibt's `enumerate()`, das braucht man auch nicht "per Hand" hoch zählen.

Wenn man an die eingelesene Datei im Programm einfach ein 'e' als letzten Befehl anhängt, kann man eine Endlosschleife ohne Bedingung um den Interpreter legen.

Die Interpreter-Schleife ist IMHO ein zu grosser Brocken. Wenn man die Ausführung der einzelnen Befehle in eigene Funktionen steckt, kann man wunderbar mit einem Dictionary über das erste Zeichen, also das Kommando, an die entsprechende Funktion "dispatch"en.

Sämtliche `re`\s sind überflüssig und können durch ``in`` ersetzt werden. Also zum Beispiel:

Code: Alles auswählen

    if re.match(r"[-+*/=]",lines[i][:1]):
    # =>
    if line[1] in '-+*/=':
Anstelle von ``s[:1] == 'x'`` ist ``s.startswith('x')`` wesentlich lesbarer.

Verfasst: Donnerstag 10. Juli 2008, 17:00
von DrChAoS
Ich glaub ich hab das allgemein zu kompliziert programmiert. Es wäre warscheinlich das beste wenn ich mir erstmal die Python Doku durchlese, da das Programmieren mit Python doch recht anders ist als das Programmieren mit C++ z.B.
MfG
Dr.ChAoS

Verfasst: Samstag 12. Juli 2008, 10:55
von sma
Ich biete eine alternative Implementierung. Auch nicht der beste Stil weil ich mich auf einbuchstabige Variablennamen beschränkt habe, aber kürzer ;) Aus Bequemlichkeit - die print-Funktion war eh schon kompliziert - habe ich auch die "a"-Variante verzichtet.

Stefan

Verfasst: Samstag 12. Juli 2008, 12:38
von BlackJack
Und es gibt wohl auch einen Grund, warum Du aus dem Beispielprogramm ein Komma aus einer Zeichenkette entfernt hast. :-P

Verfasst: Sonntag 13. Juli 2008, 08:31
von sma
BlackJack hat geschrieben:Und es gibt wohl auch einen Grund, warum Du aus dem Beispielprogramm ein Komma aus einer Zeichenkette entfernt hast. :-P
;) Ich hatte einfach keine Lust auf einen Ausdruck wie `re.findall(r'".*?"|:\w+|[ax]\d+|\d+", line[1:])` statt des `split`. Zu mühsam...

Stefan