1char - Meine kleine Scriptsprache

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
DrChAoS
User
Beiträge: 14
Registriert: Montag 10. März 2008, 16:29

1char - Meine kleine Scriptsprache

Beitragvon DrChAoS » Mittwoch 9. Juli 2008, 14:33

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
DasIch
User
Beiträge: 2423
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Beitragvon DasIch » Mittwoch 9. Juli 2008, 15:08

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
Benutzeravatar
Hyperion
Moderator
Beiträge: 7472
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Beitragvon Hyperion » Mittwoch 9. Juli 2008, 15:19

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)
BlackJack

Beitragvon BlackJack » Mittwoch 9. Juli 2008, 15:44

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[i]`` 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.
DrChAoS
User
Beiträge: 14
Registriert: Montag 10. März 2008, 16:29

Beitragvon DrChAoS » Donnerstag 10. Juli 2008, 17:00

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
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Beitragvon sma » Samstag 12. Juli 2008, 10:55

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
BlackJack

Beitragvon BlackJack » Samstag 12. Juli 2008, 12:38

Und es gibt wohl auch einen Grund, warum Du aus dem Beispielprogramm ein Komma aus einer Zeichenkette entfernt hast. :-P
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Beitragvon sma » Sonntag 13. Juli 2008, 08:31

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

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder