Lisp nach Javascript

Wegen unkontrollierbarem, auswucherndem Idlen heute nur ein kleiner Einblick in die Konvertierung von Lisp nach Javascript.

Klammeraffe

Ein nettes Feature bei Lisp sind die Klammern. Ja, die Klammern, diese kleinen Rattenschwänze die die meisten Leute zuerst abturnen, wenn sie Lisp-Code sehen. “Ieeh, das ist ja unlesbar, wie soll man sich da zurechtfinden?”. Das dachte auch John McCarthy bei der Entwicklung von Lisp, und hatte eigentlich fest eingeplant, da später mal eine “normale” Infixe Syntax drüber zu kippen. Doch wichtiger war zuerst überhaupt eine funktionnierende Implementierung, und die benutzte intern wie praktisch jede andere Programmiersprache einen abstrakten Syntaxbaum (AST – Abstract Syntax Tree), das man auf einem “linearen” Medium wie Text einfach durch Postfixausdrücke niederschreiben kann. Bei Lisp waren das die SEXPs (S-Expression, ich glaube das S steht für Syntax). Doch diese SEXPs erwiesen sich als ungemein praktisch, weil sie eigentlich selber Listen sind. Und die Liste ist der primitive Datentyp von Lisp (LISP heisst ja auch nichts anderes als LISt Processing). Das heisst, das man in Lisp ohne grosse Hürden Lisp-Code manipulieren kann. Klingt nach wilden Voodo-Opfer-Parties im dunklen Bayou, und ich will auch nicht verschweigen, dass es manchmal auch so ist. Aber in der Praxis heisst das dann meistens folgendes:

  • Fehlt ein Konstrukt in der Sprache, lässt es sich einfach herbeizaubern
  • Will man Lisp-Code oder sowas ähnliches transformieren, braucht man keinen Parser und Lexer und Compiler zu schreiben, sondern man wirft einfach ein paar Lisp-Funktionen und noch 2-3 Makros zum abrunden und fertig ist die Software.

Wie das genau funktionniert lässt sich ganz schön anhand des folgenden Beispiels zeigen. Angenommen, man hat eine Webplattform, die in Lisp geschrieben ist. Die Plattform generiert dynamisch Webseiten, die aus HTML bestehen. In den meisten Programmiersprachen macht man das mit einem Wust von “print”-Statements, in LISP schreibt man 1 Makro und brauch nur noch Listen zu generieren. Kleiner Einblick:

Dieses Macro definiert die Spezialform, um ein CMS-internen Link zu erzeugen.

(defmacro cmslink (url &body body)
  `(html ((:a :class "cmslink" :href ,url)
          ,@body)))

Wenn man jetzt aber Javascript noch einführen will, dann wird das ganz schön anstrengend, denn so einfach geformt wie HTML ist javascript nun wieder nicht. Aber auf ganz viele “print”, bzw FORMAT Aufrufe zurückzugreifen, ne, wirklich, das gehört sich nicht. Und den Javascript-Code aufzutrennen in zusätzliche Dateien ist je nachdem auch ein bisschen zuviel Aufwand. Und was ist, wenn Javascript-Code dynamisch generiert werden soll, z.B. um Inhalte aus der Datenbank “effizient” in Javascript zu mappen (anstatt jetzt wilde XML/RPC Orgien in javascript zu veranstalten). Nun, ein komplizierte Sprache ist Javascript nicht wirklich, man könnte doch sowas ähnliches wie Linj bauen. Linj ist eine Art “Compiler”, der eine Lisp-ähnliche Sprache nach lesbarem Java konvertiert. Lisp-ähnlich heisst hier eher sowas wie ein “Subset” von Lisp, was nach Java “umgewandelt” wird. Also nix mit Compilen nach Java-Bytecode, sondern Java-Quellcode, und Quellcode den man noch lesen kann. Der Ansatz ist hier praktisch alle Java-Konstrukte in Lisp nachzumodellieren, bzw. die Lisp-Konstrukte wie DO, DEFUN, usw… nach Java umzuwandeln.

Der erste Schritt ist hier die Variablennennung, die bei Javascript vielleicht noch wichtiger ist, weil Javascript meistens sehr sehr viel auf die schon existierenden Element im DOM-Baum zurückgreift. Problem ist, Lisp (oder zumindest der Lisp-Reader) ist case-insensitive. Wenn ich also foObAr oder FOOBAR oder fooBar oder foobar schreibe, der Lisp-Reader macht daraus immer FOOBAR. Wenn ich jetzt wirklich mit camelCase was machen will, dann muss ich mir da echt Mühe geben, und z.B. |camelCase| schreiben, also überall so kleine hässliche Pipes reinbauen. Das ist auch keinem Entwickler (oder Entwicklerin) zumutbar, da muss was anderes her. Da hab ich mir was ganz ungeniert bei Linj abgeschaut. Bei Linj wird die LISP-übliche Variablen und Funktionsnennung nach Java konvertiert, bzw. andersrum: getFoobar in Java wird da zu get-foobar in Lisp. Die Konstante +MAX-SIZE+ in Lisp wird zu MAXSIZE in Java, WTF?#! in Lisp wird zu WTFQuestionHashBang in Java. Um das zu machen bedarf es ein bisschen Magie, die ich hier mal *hust* erläutern werde. Ach ja, der Code hier ist nicht wirklich fein, nicht wirklich bugfrei, nicht wirklich getestet, aber ich wollte ja heute noch unbedingt etwas posten:

(defvar *special-chars*
  '((#\! . "Bang")
    (#\? . "What")
    (#\# . "Hash")
    (#\@ . "At")
    (#\% . "Percent")
    (#\+ . "Plus")))

(defun string-chars (string)
  (coerce string 'list))

(defun constant-string-p (string)
  (let ((len (length string))
        (constant-chars '(#\+ #\*)))
    (and (> len 2)
         (member (char string 0) constant-chars)
         (member (char string (1- len)) constant-chars))))

(defun symbol-to-js (symbol)
  (when (symbolp symbol)
    (setf symbol (symbol-name symbol)))
  (let (res
        (lowercase t)
        (all-uppercase nil))
    (when (constant-string-p symbol)
      (setf all-uppercase t
            symbol (subseq symbol 1 (1- (length symbol)))))
    (flet ((reschar (c)
             (push (if (and lowercase (not all-uppercase))
                       (char-downcase c)
                       (char-upcase c)) res)
             (setf lowercase t)))
      (dotimes (i (length symbol))
        (let ((c (char symbol i)))
          (case c
            (#\- (setf lowercase (not lowercase)))
            ('(#\! #\? #\+ #\$ #\@ #\# #\%)
             (dolist (i (coerce (cdr (assoc c *special-chars*)) 'list))
               (reschar i)))
            (t (reschar c))))))
    (coerce (nreverse res) 'string)))

Als erstes haben wir hier eine Assoziationsliste namens *SPECIAL-CHARS*, die die Zuweisung von “schrägen” Buchstaben in Lispnotation zu ihrem verbosen Bloatäquivalent in Javascript festhält. Eine Assoziationsliste ist so etwas wie ein “Poor Man’s Hash-Table”, in Lisp gibt es da eine Funktion ASSOC mit der man da einfach drinsuchen kann. Weiter geht’s mit der Funktion STRING-CHARS, die ein String zu einer Listen von Buchstaben konvertiert, eigentlich nur da weil mich das Rumgehampel mit COERCE genervt hat, es gibt da aber bestimmt bessere Wege, über ein String zu iterieren. Die Funktion CONSTANT-STRING-P guckt eigentlich nur nach, ob der vorliegende String in Lispnotation sowas wie eine “Lisp-Konstante” ist, also sowas wie +KONSTANTE+ oder *KONSTANTE*. Das -P am Ende der Funktion steht für “Predicate” und heisst, dass CONSTANT-STRING-P irgendwas auf Wahrheit überprüft (in dem Fall ob der String eine Konstante nennt), und T (für Wahr) oder NIL (für Falsch) zurückgibt. So, und SYMBOL-TO-JS ist das Prachtstück, und ich bin gerade zu müde, um diesen Bloatcode zu erklären, gehe deshalb jetzt schlafen, und mache morgen weiter. In der Zeit dürft ihr ja untersuchen, was da genau passiert. Wer ineffiziente Grütze findet darf sie ja gerne behalten, oder vielleicht ein bisschen in die Kommentare unten schmieren (die jetzt wieder gehen müssten, und vor Blinden und Spammern effektiv geschützt sind durch MT Secure Code).

Posted in Lispstaub | 2 Comments

Radio Chaotica – Leben am Existenzmaximum

Julian hat hier die neue Radio Chaotica folge announced:

Radio Chaotica – Leben am Existenzmaximum.

Zum Ziehen auf dem elitären bl0rg-tracker.

Posted in Netzstaub | 2 Comments

Chevelle

Chevelle

Chevelle ist eine Pop/Schrammel Band, erinnert leicht an Weezer, lässt sich hören. Ich hab erfahren, dass es bei Opendir anscheinend einen Link zu ein paar Alben von denen gab. Auf einer seltsamen georgischen Seite. Samples von allen Liedern kann man im iTunes Store oder auf ihrer Webseite hören (leider alles flash).

(Herm, Weezer schreibt man Weezer, jaja).

Posted in Musikstaub | Leave a comment

Bits sind zum kopieren da

Die Fairsharing Initiative hat eine Unterschriftenkampagne, analog zu der von dem Nouvel Observateur, gestartet.

Faisharing-Banner

Posted in Musikstaub, Netzstaub | Leave a comment

Kleine Artikelreihe

250Px-Mad Scientist Caricature

Damit es hier nicht zu öde und langweilig wird, werde ich in den nächsten Tagen versuchen täglich mindestens einen interessanten, technischen Beitrag zu schreiben. Ich kränkel im Moment rum, und kann eigentlich eh nichts wirklich gescheites tun. Heute geplant:

  • Ein Post über UDP NAT-Traversal (mit c0d3)
  • Ein Post über Lisp-nach-Javascript “compilen” (na klar, auch mit c0d3)
Posted in Computerstaub, Netzstaub | Leave a comment

Nach zwei Tagen Ruby

Alt Tag Added during WordPress Conversion

Nach 2 Tagen Ruby hacken muss ich jetzt schnell meine Vorurteile in Schriftform festhalten: Ruby ist das schlechtere Lisp. Hat zwar viele nette Features von Lisp, aber bis auf die nette FFI Syntax habe ich jetzt nichts gesehen, was mich wirklich überzeugen würde. Es ist langsamer, die Syntax ist erhm, “wischi-waschi” (wo sind meine Klammern?), Macros gehen damit auch nicht wirklich, und die ganzen netten Funktionen die man haben will muss man auch erst wieder woher holen (SOME, EVERY, …). Und die Emacs-Oberfläche ist nicht auf dem Niveau von Slime. Naja, mal ein paar Tage mehr mit arbeiten (insofern das aus Performance-Gründen überhaupt geht). Jetzt aber mal in Lisp nachcoden (es geht um unser supergeheimes Projekt voodoostreamer, dass in ein paar Wochen die Welt erobern wird).

Posted in Computerstaub, Lispstaub | Leave a comment

Und in dieser Schachtel ist noch so eine drin

Die neuste Kurzgeschichte “I, Robot…” von Cory Doctorow hat bestimmt schon die halbe Welt verlinkt, aber ich finde sie auch recht gelungen. Der Schreibtstil von Doctorow ist bestechend von Klarheit, und die Welt, die er schildert, ist sehr von Philip K. Dick inspiriert. Bullen, Roboter, kalter Krieg, dazu noch eine Prise pubertierender Teenies, die ein Netzwerk aufgebaut haben, um Entschuldigungen ihrer Eltern oder Ärzte zu faken.

Passend zur Kurzgeschichte noch ein Hiphop-Clip von der UV/PHACTORY.

Posted in Buchstaub, Musikstaub, Netzstaub | Leave a comment

Simon Singh – Fermats letzter Satz

Das Buch habe ich mir bei einem Drogenrauschkauf in der Buchhandlung nebenan gekauft (Drogenrauschkauf heisst: eigentlich nur ganz kurz reingehen, um sich die Buchhandlung anzuschauen, und mit 10 Büchern rausgehen). Seit längerer Zeit such ich nach einer Biographie von Jack London, weil der Held in Haruki Murakamis Roman “Tanz mit dem Schafsmann” (ich glaub das war der Roman, und nicht “Sputnik Sweethearts”) zwischendurch in einem Cafe rumhockt und gerade so eine Biographie liest. Die habe ich dann auch in der Buchhandlung gefunden, aber leider eher so ein Bilderband mit ein bisschen Text.

Nun aber zum Thema des Postings. Zusätzlich zur Biographie von Jack London habe ich das Buch “Fermats letzter Satz” von Simon Singh gekauft. Zu Weihnachten hatte ich das Buch über Kryptographie von ihm verschenkt, und mir wurde gesagt, dass es wirklich unterhaltsam sei. Also gut, Fermats letzter Satz klingt ja auch halbwegs spannend. Das Spannendste an dem Roman war aber die Lebensgeschichte von Evariste Galois, die ich aus dem Internet schon kannte. Evariste Galois ist der Mathematiker, dem man die sogenannten “Galois-Gruppen” verdankt. Ich bin jetzt nicht so der Mathe-fan, und versteh eigentlich kaum was auf dem Gebiet, aber Galois-Gruppen sind für die Informatik von ganz besonderem Interesse, weil sich bestimmte Galois-Körper (Körper in der die Elemente praktisch Polynome sind, und die Operation + und * auf diesen Polynomen agiert) sehr einfach mittels Bit-operationen modellieren lassen. Das ist jetzt ein paar Jahre her, dass ich das benutzt habe, aber bei der FEC-Kodierung für poc wird gerade in einem Galois-Körper gerechnet, der mit Integers von 8 Bit dargestellt. Hierzu ein Kapitel aus meiner gescheiterten Studienarbeit, und die passenden C files dazu.

Evariste Galois ist sowas wie eine tragische Figur der französischen Mathematik des 19. Jahrhunderts. 1811 geboren, wurde er wegen seiner besonderen Begabungen schon im jüngsten Alter in das Lycée Louis le Grand in Paris aufgenommen, wo er mit 16 die Bekanntschaft der Mathematik machte. In den 6 folgenden Jahren gelang es ihm dann, mehrere Durchbrüche auf dem Gebiet der Mathematik zu erlangen. Doch wegen seiner leicht gehetzten Art und seinen rebellischen Tendenzen wurde er meistens von den älteren Mathematikern abgewiesen, und wandte sich auch bald von der Mathematik an sich ab, und kämpfte auf der Seite der revolutionären Republikaner gegen den neuen König Louis-Philippe. Auch sein Vater war Republikaner, und wurde anscheinend von den Gerüchten, die die Kirche in seiner Stadt verbreiteten, zum Selbstmord gezwungen. Dies setzte Evariste ziemlich zu, bald kam er auch wegen einer Rede mit gezogenem Dolch gegen Louis-Philippe ins Gefängnis. Im Jahre 1832 schnappte er anscheinend einem Adeligen die Verlobte weg, und wurde zum Duell herausgefordert. Da er mit Waffen kaum umgehen konnte, war ihm der Tod ziemlich sicher, und der Legende nach schrieb er in der wilden Nacht vor dem Duell all seine Erkenntnisse zu der Lösung von Polynomen höherer Ordnung hastig nieder. Am nächsten Tag wurde er tödlich verwundet, und kam sehr bald im Krankenhaus ums Leben. 16 Jahre später wurden diese leicht verworrenen Kritzelein von dem Mathematiker Louiville entdeckt, der sich die Mühe machte, sie zu verstehen und ins Klare zu ziehen. Danke diesem Werke, das man sich bei der Bibliotheque Nationale de France besorgen kann, wurde die Theorie der Galois-Felder weltweit bekannt.

Posted in Computerstaub, Denkstaub | Leave a comment

Und noch eine Teenie-Band: Smoosh

Im Vergleich zu der unsagbar schlechten und rassistischen Band Prussian Blue ist Smoosh, zwei Schwestern aus Oregon, eine richtig passable Indie-Rock Band. Noch nicht so das Geilste (der Gesang ist nicht wirklich sicher), was man kennt, aber ich finde die Lieder richtig schön aufgebaut. Eine CD haben sie auch schon: She Like Electric (auf Amazon bestellen). Und so ein Augenkrebs hatte ich seit langem nicht mehr 🙂 (Über See You In The Pit).

Posted in Musikstaub | 4 Comments

Audio unter MacOSX

Ich habe mir jetzt mal vorgenommen, alle technische Probleme, denen ich begegne, hier mal zu dokumentieren. Dann brauch ich nächstes mal nicht stundenlang nachzudenken “da hatte ich doch mal”, sondern brauch nur Google kurz zu bemühen. Und wichtig ist da auch der Hintergedanke “das bringt mir totale Hackerleetness, Fame und ganz viele Comments” 🙂

Als erster Beitrag in der Reihe “Coden für Gedächtnisbeschädigte”: Audio unter MacOSX. Hintergrund ist folgender: ich will für mein FEC Protokoll jetzt gleich den MP3 Player integrieren, damit diese lästige Konvertiererei zwischen MP3 Frames und MP3 ADUs wegfällt, und damit ich auch die Pausen bei Packet Loss genauer kontrollieren kann. Dazu würde ich gerne die Mad Library verwenden, weil der Decoder in mpg123 (den man übrigens in den meisten MP3 Playern unter Unix wiederfindet) wirklich unter aller Sau ist. Genau mit der Library hatte ich vor kurzem was programmiert, und zwar eine Wrapperlibrary um Lame, um mit einer “simplistischen API” MP3s abspielen zu können. Die Library hatte ich ursprünglich unter Linux und FreeBSD geschrieben, wo man zum Ausgeben von Klängen meistens nur “/dev/dsp” öffnen muss. Im Moment arbeite ich wieder auf meinem Powerbook, und da geht das leider nicht so einfach.

Unter MacOSX gibt es verschiedene Möglichkeiten, Rumzuquaken: Carbon, oder CoreAudio, oder Quicktime. Quicktime lassen wir mal aussen aus, und Carbon auch, wir sind ja hier totale Darwin-Cocoa-h4x0r. Bei CoreAudio kann man sich die ID von einem AudioDevice schnappen, auf diesem Device dann verschiedene Properties verändern, und dann eine IO Procedure registrieren. Nachdem das AudioDevice dann “gestartet” wurde, ruft das CoreAudio Framework die IO Procedure regelmässig in einem zusätzlichen Thread auf, und erwartet, dass diese ihr Samples zum abspielen gibt. Hintergrund von diesem ganzen Aufwand ist, dass somit Buffering und ruckelloses Abspielen von Audiodaten viel einfacher geht, als wenn man “PUSH”-mässig die Audiodaten auf ein Device schreibt (und man braucht die Soundausgabe nicht selber zu schedulen). Wenn man nur schnell mal was abspielen will, ist sowas aber schon recht ärgerlich. Erstens muss man die Audiosamples aus dem Decoder (hier aus der Mad-Library) irgendwozwischenspeichern, dann noch auf diese Daten zugreifen können, und zwar Threadsafe (der Audiothread läuft parallel zum Decoden), und schliesslich will man auch noch ein bisschen zwischenpuffern.

Da ich so faul bin, habe ich erstmal geguckt ob ich bei mpg123 und bei madplay, die MacOSX-Backends haben, nicht den code schnell mal rausrippen könnte, aber so in der Form, wie man dort diesen Ansatz vorfindet, will man das eigentlich nicht. Deswegen, neu schreiben (nach dem ADAM-Prinzip: Alles Deppen Ausser Mir):

  • als erstes brauchen wir eine Datenstruktur, um die dekodierten Samples abzuspeichern, wenn sie aus der Mad-Library kommen. Dazu verwenden wir einen Ringpuffer.
  • als zweites: diese Datenstruktur muss thread-safe sein, dazu verwenden wir Pthread Mutexe und Condition Variables.
  • als drittes: drumherum noch ein bisschen CoreAudio Kram.

Die Ringpufferstruktur habe ich so implementiert, dass ich sie später mal kurz Copy-pasten kann, ein bisschen verändere, und dann wiederverwende, weil sowas braucht man oft schnell mal. Der Code (besonders für den Fall, bei dem die eingefügten Daten über die Puffergrenze fallen) ist nicht so ganz prickelnd, aber nun gut. In der Datei rb.h wird die Ringpuffer Datenstruktur definiert, dazu noch ein paar Compile-Time-Konstante und Typen wie rb_elt_t (der Typ der Elemente in dem Ringpuffer), die Grösse des Puffers, und ob der Puffer Threadsafe sein soll oder nicht. Die rb_t Struktur selber ist dann relativ straightforward:

typedef struct rb_s {
  rb_elt_t buf[RB_SIZE];
  unsigned long start;
  unsigned long count;

#ifdef USE_PTHREAD
  pthread_mutex_t mutex;
  pthread_cond_t  cond;
#endif
} rb_t;

Die API dementsprechend auch. rb_init resettet die Felder einer rb_t Struktur, rb_destroy macht dann das Umgekehrte. rb_enqueue fügt Daten in den Ringpuffer ein, und gibt 1 zurück wenn es geklappt hat. rb_dequeue liest Daten aus dem Ringpuffer, und gibt 1 zurück wenn es geklappt hat. Ein kleiner Trick jedoch, rb_enqueue wartet auf einen freien Pufferplatz, wenn Pthreads eingeschaltet ist (das ist der Spezialfall beim Schreiben von Audiodaten: die dürfen auf keinem Fall verloren gehen). Dazu ist dann auch gleich die Funktion rb_wait da. Die Implementierung dieser API könnt ihr in rb.c finden.

void rb_init(rb_t *rb);
int  rb_enqueue(rb_t *rb, rb_elt_t *data, unsigned long count);
int  rb_dequeue(rb_t *rb, rb_elt_t *dest, unsigned long count);
void rb_destroy(rb_t *rb);

#ifdef USE_PTHREAD
void rb_wait(rb_t *rb, unsigned long count);
#endif

Nun geht es weiter mit dem eigentlich Audiocode. Von der Mad-Library wird ein Callback von uns aufgerufen, wenn ein Frame fertig decodet worden ist. Ein MP3 Frame besteht aus 1152 Samples (ist im Standard so festgeschrieben). Mad verwendet intern nur Fixkommazahlen, die den Typ mad_fixed_t haben. Unser Callback kriegt als Argument eine Struktur mad_pcm, die genau 1152 dieser Fixkommazahlen beinhält (bzw. 1152 * 2 wenn es 2 Kanäle gibt). Diese Fixkommazahlen müssen erstmal nach float konvertiert werden, weil CoreAudio float Zahlen haben will. Nachdem diese Zahlen konvertiert worden sind, werden sie in unserem extra an float-Zahlen und MP3 Frames angepassten Ringpuffer eingefügt. Das Ganze sieht dann so aus:

  int ret;
  float buf[1152 * pcm->channels];
  float *ptr = buf;
  int i;
  mad_fixed_t const *left_ch, *right_ch;
  left_ch  = pcm->samples[0];
  right_ch = pcm->samples[1];
  for (i = 0; i length; i++) {
    signed int sample;
    *ptr++ = mad_scale(*left_ch++) / 32768.0;
    *ptr++ = mad_scale(*right_ch++) / 32768.0;
  }
  ret = rb_enqueue(&audio.rb, buf, 1152 * pcm->channels);

Und im Audio-Callback:

  int i;
  for(i = 0; i mNumberBuffers; i++) {
    AudioBuffer *buffer = outOutputData->mBuffers + i;

    if ((buffer->mDataByteSize / sizeof(float)) != (1152 * 2)) {
      memset(buffer->mData, 0, 1152 * 2 * sizeof(float));
      continue;
    }

    int ret;
    ret = rb_dequeue(&audio.rb, buffer->mData, 1152 * 2);
    if (ret == 0)
      memset(buffer->mData, 0, 1152 * 2 * sizeof(float));
  }

Den kompletten Code könnt ihr hier nachchecken, aber das ist im Moment so eine Baustelle :).

Posted in Programmstaub | Leave a comment