A Clojure programok mint minden más programozási nyelvben, életútjukat szöveges formában (akár fájlokban, akár egy REPL felületen direkt inputként) kezdik. Más programozási nyelvekkel ellentétben, a szöveges reprezentációt először nem a fordító kezdi el beolvasni, hanem egy beolvasó modul, amit reader-nek hívunk. A reader a programszövegből egy adatszerkezetet állít elő, amit átad a fordítónak. Ez tehát azt jelenti, hogy minden Clojure program valójában egy adatszerkezet. A Clojure nyelv szintaxisát tehát maga a reader implementációja határozza meg, hiszen ez alakítja át a szöveget a megfelelő adatszerkezetre. Írható lenne olyan reader is, ami XML dokumentumokból, JSON-ból, stb. olvassa fel a programkódot, és ezt alakítaná a fordító számára is érthető adatszerkezetté. De a Clojure beépített reader-e (read függvény) nem csinál ilyesmit, így a nyelv szintaxisának leírásakor tehát erre hagyatkozunk. A reader-re tehát tekintsünk úgy, mint egy egyszerű parser-re, ami a programszöveget felolvasva különböző műveleteket végez, kimenete pedig a kódnak megfelelő adatszerkezet.
Makró karaktereknek hívjuk azokat a karaktereket, amik befolyásolják a reader működését: beolvasásukkor a beolvasott karakternek megfelelő, ún. reader makrók hajtódnak végre.
Makró karakterek: | |
---|---|
Idézőjelezés (Quote): | 'form |
Karakterek: | \a \b \c |
Kommentek: | ; komment |
Metaadatok: | ^metaadat vagy ^{kulcs ertek} |
Dereferencia: | @form |
Syntax-quote: | ` ("backtick" karakter) |
Unquote: | ~ |
Unquote-splicing: | ~@ |
A #-hez tartozó makró karakterek: | |
Halmazok: | #{:a :b :c} |
Regexpek: | #"pattern" |
Var-quote: | #'x |
Anonymous függvény: | #(...) |
Következő reader form teljesen figyelmen kívül hagyása: | #_ |
Reader form-ok | ||
---|---|---|
Szimbólumok: | Pl.: abcd, *abcd*, +*!?, stb. | Nem-numerikus karakterrel kezdődnek, és alfanumerikus vagy *, +, !, -, _, ? karaktereket tartalmazhatnak. |
Literálok: | Pl.: 42, "abcd", nil, true, false, \c, :foo | A példákban rendre: szám-, szöveg-literálok, nil érték, logikai értékek, a "c" karakter, és egy kulcsszó (kulcsszavak szintaxisa megegyezik a szimbólumokéval, a kettőspontot leszámítva) |
Listák: | Pl.: (a b c), (42 "b" nil), (\c :alma nil), stb. | Heterogén típusú listák. Szintaxis: az elemek zárójelek között. |
Vektorok: | Pl.: [a b c], [42 "b" nil], [\c :alma nil], stb. | Heterogén típusú vektorok. Szintaxis: az elemek szögletes zárójelek között. |
Hash map-ek: | Pl.: {:key1 "value1" :key2 "value2"}, {1 :value1 "key2" "value2"}, stb. | Kulcs-érték párok, kapcsoszárójelek között felsorolva. Heterogén: tetszőleges kulcsok, tetszőleges értékek. |
Halmazok: | Pl.: #{:a :b :c}, #{:a 23 [2 3 4 5]} stb. | Heterogén típusú halmazok. A #{...} szintaxist feltüntettük a reader makróknál is. |
user=> (* (+ 4 5) (/ 7 5)) ; operátor-precedenciával sem kell foglalkozni 63/5
A reader továbbá sokszor úgynevezett meta-információval lát el bizonyos kódrészleteket. Például a meta-információ tartalmazhatja, hogy az adott var melyik fájlban és melyik sorban került definiálásra. Sokszor a metainformációba kerül a függvények docstring-je, azaz a dokumentációt tartalmazó string, ami ilyen módon szintén része lesz a kódot tartalmazó adatszerkezetnek. A függvények elő- és utófeltétele, és a tesztesetei is ide kerülnek. Egy objektum meta-információját tartalmazó hash-map lekérdezhető a meta függvénnyel, ami, ha nincs ilyen információ, akkor nil-t ad vissza. A metainformáció módosítása úgy történik, hogy a with-meta függvény egy olyan objektumot ad vissza, ami már az új meta-információval rendelkezik. Az immutabilitás miatt azonban a régi objektum továbbra is a régi metainformációt tartalmazza.