[olvasható főoldal]

Háttérinformáció az olvasható fordítóról

Az alábbi szöveget lényegében egy, a fordítóról szóló levélből vettem át. Ebben többek között megtalálja az olvasható nyelv szintaxisát.

Background information about the olvashato compiler

The following are (slightly changed) quotes from an email I wrote about the olvashato compiler. Among other things, they describe the syntax of the language.

This material is only available in hungarian.

Miért írtam fordítót?

Nyilván tudod, hogy ugyanazt a höföt
(http://dp.iit.bme.hu/dp-current/dp06a-nhf.html) kell beadni sml-ben és
prologban.  Persze a legtöbb ember lusta teljesen külön megírni a kettőt,
így azt csinálja, hogy először sml programot ír side effektek nélkül (a
mutable táblák amúgy ehhez a feladathoz kényelmesek lennének), utána kézzel
prologgá fordítja.  Ezzel persze a prologot sem használja ki teljesen,
hiszen nem használja a backtrackinget vagy a nyílt végű listákat.

No most az sml kódot prologra fordítani elég unalmas munka (persze
még mindig gyorsabb lenne, mint a fordítót megírni, de unalmasabb:).
Tudniillik ahol egymásba ágyazott függvények szerepelnek, ott az összes
közbülső eredménynek Nevet kell adni (kivéve az aritmetikát).  Persze sok
optimizációt így is meg lehetne csinálni a fordításnál, és ez tényleg
segítene a prologot megérteni.

Az sml-ben azonban szerintem semmit sem segített volna, ha kézzel írom,
mivel az a nyelv, amiről fordítok, majdnem teljesen sml, csak egyszerűbb
szintakszissal.  

A fordító nyelvének a leírása

A fordító nem teljes, csak annyit csináltam meg belőle, ami a házihoz
elég volt (sőt, még annál is kevesebbet, mert egy-két dolgot, ami csak
egyszer kellett, kézzel megírtam prologban és sml-ben).  Ebből adódóan a
fordító -- úgy, ahogy most van -- a nagyházi kivételével a többi példát
nem is fogja tudni sml-re lefordítani, csak prologra.  (A hiányzó részeket
nem lenne nehéz megírni.)  Az svn repoban van valahol egy olyan verzió,
ami még csak prologra fordít, azzal ezek a példák is működtek.

Nos, a nyelvet, amiről fordítok, olvashatónak hívom.  (Eredtileg azért, mert
a cél az, hogy olvasható prolog és olvasható sml kimenetet kapjak, tehát a
nyelv ezeknek a közös része.  Most már inkább azért, mert az olvasható
forrás sokkal olvashatóbb, mint a prolog kód amit kapok belőle.)

Az olv nyelv szintaxisa S-expressionökből áll.  A program deklarációk
sorozata.  A legfontosabb deklaráció a defun:

 (defun NAME (PARM1 ...) EXPR DOCSTRING)
   Ez egy függvényt definiál NAME néven, (PARM1, PARM2, ...) formális
   paraméterekkel, aminek a törzse EXPR. A DOCSTRING a függvény
   dokumentációját tartalmazza, aminek prologban és sml-ben is értelmesnek
   kell lennie, ezért némi helyettesítésen megy át.

További deklarációk a következők:

 (impfun NAME (PARM1 ...))
   Meglévő (nem olvasható-ban definiált) függvény importálása.
 (mutual (defun NAME (PARM1 ...) EXPR DOCSTR) ...)
   Kölcsönösen rekurzív függvénydefiníció.  Csak a prolog fordító
   támogatja, pedig csak sml-ben lenne értelme.
 (deftype TYPENAME (CONSNAME ELT1 ...) ...)
   Definiál egy új konkrét adattípust.  TYPENAME egy string, ami a típus
   sml nevét tartalmazza, esetleg típusparaméterekkel.  CONSNAME a típus
   egy konstruktorának nevét adja meg, ez egy szimbólum, amit az olv kód
   később felhasználhat.  A konstruktornak nulla vagy több paramétere
   lehet, ezeknek sml típusa rendre ELT1, ELT2, ...
 (imptype TYPENAME (CONSNAME ELT1 ...) ...)
   Ugyanaz, mint a fenti, de nem definiálja a típust, hanem feltételezi,
   hogy már definiálva van.
 (export OLVNAME NAME)
   Azt mondja meg, hogy ha később definiálsz vagy deklarálsz egy globális
   OLVNAME szimbólumot (ami lehet konstruktor vagy függvény), akkor azt a
   prolog ill sml kódban a NAME névvel kell jelölni.  Az export híján a
   fordító maga választ egy (általában) megfelelő nevet.  Az export
   deklarációt akkor kell használni, ha nem olv kódban is hivatkozol
   ugyanarra a névre, vagy ha a nevet egyenesen nem olv kód definiálja.
 (comment STR)
   Megjegyzést szúr be.
 (prolog CODE)
 (sml CODE)
   Kézzel írt prolog illetve sml kódot szúr be.
 (!!ifdef PREPROCESSOR_SYMBOL DECL ...)
 (!!ifdef PREPROCESSOR_SYMBOL DECL ... !!else DECL ...)
 (!!ifndef PREPROCESSOR_SYMBOL DECL ...)
 (!!ifndef PREPROCESSOR_SYMBOL DECL ... !!else DECL ...)
   Feltételes fordítás.  Azért van rá szükség, hogy a prolog és sml program
   különbözhessen.

A kifejezések a következőképpen nézhetnek ki (remélem, nem hagyok ki semmit).

 SYMBOL
   Lokális változó értékét adja eredményül.  Lokális változót három dolog
   hozhat létre: a defun formális paraméterei, és a később ismertetendő let
   és lambda kif-ek.
 INTEGER
   Egész szám literál.
 (FUNCNAME ARGS ...)
   Defunnal vagy impfunnal definiált függvény hívása.
 (CONSNAME ARGS ...)
   Deftype-pal vagy imptype-pal definiált konstruktor hívása.
   A CONSNAME továbbá lehet az alábbi beépített konstruktorok egyike is:
     rec -- tuple
     list -- lista
     cons -- pontozott lista
     true -- boolean igaz
     false -- boolean hamis
 (+ X ...)
 (- X)
 (- X Y)
 (* X ...)
 (div X Y)
   Aritmetika.  Negáció csak prologban.
 (< X Y)
 (<= X Y)
 (= X Y)
 (/= X Y)
   Számok összehasonlítása.
 (equal X Y)
   Tetszőleges kifejezések mély összehasonlítása.
 (and BOOL ...)
 (or BOOL ...)
 (not BOOL)
   Boolean műveletek, az első kettő short circuites.  Csak prologban.
 (abort)
   Fatális hiba.
 (if COND THENEXPR ELSEEXPR)
 (if COND1 THENEXPR1 COND2 THENEXPR2 ELSEEXPR)
 ...
   Feltételes kifejezés.
 (let (EXPR) (VAR) BODY)
   A VAR új lokális változó értéke EXPR lesz a BODY kifejezésben.
 (let (EXPR) (PATT) BODY)
 (let (EXPR) (PATT1) BODY1 (PATT2) BODY2)
 ...
   Összetett kifejezéseket bont szét darabokra.  Az első olyan BODY
   hajtódik végre, ahol a PATT minta illeszkedik az EXPR kifejezés
   értékére.  Valamelyik mintának illeszkednie kell.    A PATT
   általában tetszőleges minta lehet, ami új változónevekből és
   konstruktorhívásokból áll.  Ha egy változónév többször is előfodrul,
   attól még különböző változókat jelent, vagyis pl a (rec x x) nem
   csak olyan tuplere illeszkedik, ami két azonos elemet tartalmaz, hanem
   akármilyen tuple-re, és az x értéke valamelyik lesz a kettő közül.
 (let (EXPR1 EXPR2) (PATT1 PATT2) BODY)
 (let (EXPR1 EXPR2) (PATT11 PATT21) BODY1 (PATT12 PATT22) BODY2)
 ...
   Több kifejezést illeszt egyszerre.
 (lambda (PARM ...) BODY)
   Névtelen függvényt hoz létre.
 (call FUN ARG ...)
   Meghív egy névtelen függvényt.

A nyelv valójában statically typed, de a fordító erről nem tud, ő
lényegében úgy fordítja, mintha dynamically typed lenne.  A típusokat
az sml fordító ellenőrzni.


Zsbán Ambrus, 2006. december 23.

Az olvasható fordító főoldala