A Stratego termátíró rendszer

Példaprogramok

Aritmetikai kifejezésfa kiértékelése

Egy egyszerű aritmetikai kifejezésfában szerepelhetnek bináris összeadás (Plus) és szorzás (Mult) csomópontok valamint konstans egészek (Int). Írjuk le egy ilyen fa formáját és kiértékelési szabályait Stratego nyelven! Használjuk a könnyű tesztelhetőség érdekében az io-wrap adta felületet!

module eval-example imports libstrategolib signature sorts Expr constructors Plus : Expr * Expr -> Expr Mult : Expr * Expr -> Expr Int : Integer -> Expr rules EvalMult : Mult(Int(x),Int(y)) -> Int(<mul>(x,y)) EvalPlus : Plus(Int(x),Int(y)) -> Int(<add>(x,y)) strategies main = io-wrap(Eval) Eval = bottomup(try(EvalMult <+ EvalPlus))

Használtuk a libstrategolib könyvtárt, így a fordítási parancssor: strc -i eval-example.str -la stratego-lib. Az így kapott program a standard bemenetről olvas egy ATermet és a standard kimenetre, ATermként írja ki eredményét (ha minden mintaillesztés sikeres; ha nem, úgy hibakeresési információkat ír ki).

A stratego-shell által is kipróbálható a szabályrendszer. Először adjuk meg a szabályokat és a stratégiát, majd próbáljuk ki azt közvetlenül megadott termen!

stratego> import libstrategolib stratego> EvalMult : Mult(Int(x),Int(y)) -> Int(<mul>(x,y)) stratego> EvalAdd : Add(Int(x),Int(y)) -> Int(<add>(x,y)) stratego> Eval = bottomup(try(EvalMult <+ EvalAdd)) stratego> !Add(Mult(Int(4),Int(5)),Int(6)) Add(Mult(Int(4),Int(5)),Int(6)) stratego> Eval Int(26)

Egy funkcionális programozási nyelv értelmezője

Készítsük el egy egyszerű funkcionális nyelv interpreterét. Legyen moduláris a programunk konstrukciója: alakítsuk azt ki úgy, hogy új nyelvi elemekkel könnyen bővíthessük.

A nyelvtan

A nyelvtant SDF-ben adjuk meg. Ez a nyelv a Stratego/XT részeként lehetőséget ad mind a környezetfüggetlen nyelvtan mind a lexikai elemek megadására. Egy SDF fájl elején megadjuk a modul nevét (kötelezően), az esetlegesen importált modulok listáját, majd következik az exports szakasz. Itt a sorts szakaszban adhatjuk meg az esetlegesen bevezetett nemterminálisokat. Ezután külön-külön szakaszokban (melyek használata mind opcionális) írhatjuk le a lexikai és a környezetfüggetlen nyelvtant. A szakaszokban a produkciós szabályokat fordított formában, "redukciós szabályként" adjuk meg. Általános formájuk: jobboldal -> baloldal {attribútumok}

Lexikai elemek

Először a lexikai elemző szabályait adjuk meg. Legyen a modul neve a0lex; ennek megfelelően a0lex.sdf lesz e forrásfájl neve. A nyelv nem fehér szóköz-érzékeny; ezért az újsor illetve szóköz jelekhez LAYOUT baloldalú produkciós szabályt adunk meg. Az elemzőgenerátor úgy fogja módosítani a környezetfüggetlen nyelvtan jobboldalait, hogy ott bármely két jel között megengedett nulla vagy több LAYOUT.

Nyelvünkben megengedünk egész számokat és logikai értékeket. Lehetőséget adunk függvények névhez kötésére, rekurzív függvényhívásra (ezt külön nem jelöljük). Bevezetjük az if b t f konstrukciót, ami blogikai értéktől függően t-t vagy f-et ad vissza.

module a0lex exports sorts Id IntConst BoolConst Keyword Equals Nl lexical syntax [\ \t] -> LAYOUT [\n\r] -> Nl Nl -> LAYOUT [a-zA-Z][A-Za-z0-9]* -> Id [1-9][0-9]* -> IntConst "true" -> BoolConst "false" -> BoolConst BoolConst -> Keyword "let" -> Keyword "and" -> Keyword "in" -> Keyword "if" -> Keyword "=" -> Equals

Kössük ki, hogy a kulcsszavak védett nevek; azokat azonosítóként nem használhatjuk fel. Ehhez használhatunk "tiltó produkciós szabályt".

Keyword -> Id {reject}

Így még nem egyértelmű a nyelvtan. Például 12 elemezhető [IntConst("1"),IntConst("2")]-ként és [IntConst("12")]-ként is. Vezessünk be egyértelműsítő szabályokat. Az SDF fájl lexical restrictions szakaszában megadhatunk többféle megkötést is. Most a -/- operátort használjuk: a -/- b azt jelenti, hogy a szimbólumot b nem követheti.

lexical restrictions Id -/- [a-zA-Z0-9] Keyword -/- [a-zA-Z0-9] IntConst -/- [0-9] Equals -/- [\=]
A környezetfüggetlen nyelvtan

Az a0syn modulban adjuk meg a környezetfüggetlen nyelvtan szabályrendszerét. Használja a lexikai elemző jeleit, ezért az szerepel az importált modulok listájában.

Egy funkcionális nyelvben "minden kifejezés". Ennek megfelelően a legáltalánosabb nemterminális az Expr. Először bevezetjük a legegyszerűbb önálló kifejezéseket: a literálokat és a változókat.

module a0syn imports a0lex exports sorts Expr Pattern LetDef context-free syntax IntConst -> Expr {cons("IntLiteral")} BoolConst -> Expr {cons("BoolLiteral")} Id -> Expr {cons("Var")}

Az egyes produkciós szabályoknak kapcsos zárójelben attribútumokat adhatunk. A cons attribútummal jelöljük az absztrakt szintaxisfában a vonatkozó csomópont nevét. A szintaxisfa ATermként kerül feldolgozásra: minden egyes cons-szal jelölt részfa egy konstruktoros résztermnek felel meg. A left, right, assoc, non-assoc attribútumokkal jelölhetjük A [...] A -> A formájú szabályok esetén az asszociativitást.

Be kell vezetni a bináris műveleteket. Ezek között prioritási relációt kell felállítani. Erre szolgál a context-free priorities szakasz, ahol szabályokat rendezve adhatunk meg.

context-free priorities {left: Expr "*" Expr -> Expr {cons("Times"), left} Expr "/" Expr -> Expr {cons("Div"), left} Expr "%" Expr -> Expr {cons("Mod"), left} } > {left: Expr "+" Expr -> Expr {cons("Plus"), left} Expr "-" Expr -> Expr {cons("Minus"), left} } > {non-assoc: Expr "==" Expr -> Expr {cons("Equals"), non-assoc} Expr "<" Expr -> Expr {cons("Greater"), non-assoc} Expr ">" Expr -> Expr {cons("Less"), non-assoc} Expr ">=" Expr -> Expr {cons("GreaterEq"), non-assoc} Expr "<=" Expr -> Expr {cons("LessEq"), non-assoc} } > Expr "&&" Expr -> Expr {cons("And"), left} > Expr "||" Expr -> Expr {cons("Or"), left}

Speciális bináris művelet a függvényalkalmazás: itt az operátor epszilon.

> Expr Expr -> Expr {cons("App"), left}

Adjuk meg továbbá az if szintaxisát. Folytassuk a szakaszt ennek megfelelően.

> "if" Expr Expr Expr -> Expr {cons("If")}

A let-kifejezés megadása nem triviális. Lehetőséget kívánunk adni kölcsönösen rekurzív függvények megadására. Az egyes függvényeket Név Paraméterek = Függvénytörzs formában szeretnénk megadni. Ezt emeljük ki egy LetDef baloldalú szabállyá. Ha egy letkifejezésben több függvénydefiníció van, akkor azokat az and kulccszó választja el. Folytassuk a szabályok felsorolását.

> "let" { LetDef "and" }+ "in" Expr -> Expr {cons("Lets")} context-free syntax Id "=" Expr -> LetDef {cons("Let")} Id Pattern+ "=" Expr -> LetDef {cons("Let")} Id -> Pattern {cons("VarPattern")}

Ezután fogjuk össze nyelvtanunk moduljait egy Main modulba. Adjuk meg, hogy nyelvtanunk összesen mely modulokat használja. Adjunk meg kezdőszimbólumot. A program maga egyetlen kifejezés lesz.

module Main imports a0lex a0syn exports sorts Program context-free syntax Expr -> Program {cons("Program")} context-free start-symbols Program
A nyelvtan fordítása, kipróbálása

A nyelvtant először össze kell szerkeszteni egy egységes forrásfájlba. Erre a pack-sdf programot használjuk. E fájlnak a kiterjesztése konvenció szerint ".def".

$ pack-sdf -i Main.sdf -o a0.def including ./Main.sdf including ./a0lex.sdf including ./a0syn.sdf

Az összeszerkesztett fájl alapján generáljuk elemző táblázatokat az sdf2table programmal.

$ sdf2table -i a0.def -o a0.tbl

Ezek után az sglri programmal tetszőleges forrásnyelvi szöveget elemezhetünk. A pp-aterm program az ATermet szebben formázva jeleníti meg.

$ echo "let f x = x*2 in f 5" | sglri -p a0.tbl Program(Lets([Let("f",[VarPattern("x")],Times(Var("x"),IntLiteral("2")))], App(Var("f"),IntLiteral("5")))) $ echo "let f x = x*2 in f 5" | sglri -p a0.tbl | pp-aterm Program( Lets( [ Let( "f" , [VarPattern("x")] , Times(Var("x"), IntLiteral("2")) ) ] , App(Var("f"), IntLiteral("5")) ) )
Szignatúrák generálása

Ahhoz, hogy a nyelvtan elemzése során generált ATerm-eket Stratego programmal feldolgozhassuk, ismernünk kell azok szignatúráját. Ezt nem kell kézzel végeznünk; rendelkezésünkre állnak megfelelő progamok a Stratego/XT keretrendszerben. Először az sdf2rtg programmal a szintaxisfákat leíró reguláris fanyelvtant generálunk, majd ebből a rtg2sig programmal Stratego nyelvű forrásfájlt generálunk.

$ sdf2rtg -i a0.def -o a0.rtg $ rtg2sig -i a0.rtg -o a0sorts.str
Az értelmező felépítése

Először alakítsuk ki az elemző általános infrastruktúráját. A program fő modulját a0i néven nevezzük; konvenció szerint a fájl neve a0i.str lesz. Az interpreter az előzőekben már használt io-wrap stratégia segítségével beolvas egy ATermet a standard bemenetről, elvégzi a megfelelő transzformációkat (a program startkifejezését kiértékeli), majd az eredménnyel vagy hibát jelző válasszal leáll.

module a0i imports libstrategolib a0common rules pretty-print : Const(v, _) -> v pretty-print : Errors(l) -> l collect-errors = ?x; bu-collect(?Error(_)); if(?[], !x, !Errors(<id>)) strategies s-main = ?Program(e); <innermost(eval)> e; collect-errors; try(pretty-print) main = io-wrap(s-main)

Először kibontjuk magát a startkifejezést, majd az eval szabályt - ami a tényleges redukciót végzi - alulról felfele haladva (tehát mohó kiértékelési stratégia szerint) addig alkalmazzuk, míg csak lehet. Az innermost stratégia a standard könyvtár része és bottomup(try(s; innermost(s)))-ként definiáljuk. bottomup(s) a termet alulról felfele bejárva alkalmazza s-t; definíciója: bottomup(s)=all(bottomup(s)); s

Ha a megálláskor kapott kifejezésben vannak hibák, úgy a kiértékelés sikertelen - ezeket összegyűjtjük egy listába. Erre az összegyűjtésre a standard könyvtár bu-collect stratégiáját használjuk. Ha ez a lista nem üres, hibajelzéssel állunk meg.

Ha sikerül konstans értékké redukálni a kifejezést, úgy magát az értéket adjuk eredményül.

A minden modul által használt szortokat, konstruktorokat, stratégiákat, importokat egy közös, a0common nevű modulban foglaljuk össze. Importáljuk az előbb legenerált a0sorts modult; erre mindenhol szükség lesz.

A tovább nem redukálható értékeket Const(v,t) formájú termmel írjuk le, ahol v a tényleges érték, t pedig típusannotáció.

Ha egy részterm hibás, Error(s) formájú termre cseréljük, ahol s egy hibaüzenet.

module a0common imports a0sorts signature sorts Type Const Errors Error constructors Const : a * Type -> Const Errors : List(String) -> Errors Error : String -> Error
Aritmetikai kifejezések

Először valósítsuk meg az egészeken végzett műveleteket. Hozzunk létre egy a0int modult és adjuk hozzá a0i importjaihoz.

Az egész-literálokat konstanssá alakítjuk. A típus jelöléséül az Int() termet használjuk. Az értéket az elemzés során kiolvasott sztring számmá alakításával kapjuk. Erre a dec-string-to-int stratégiát használjuk.

module a0int imports a0common libstratego-lib signature constructors Int : Type rules eval : IntLiteral(e) -> Const(<dec-string-to-int> e, Int())

Egyszerűen megvalósítható az összeadás, kivonás, szorzás művelete. Rendelkezésre állnak megfelelő stratégiák, amik számok rendezett párján elvégzik a megfelelő műveletet.

eval : Times(Const(e1, Int()),Const(e2, Int())) -> Const(<mul> (e1,e2), Int()) eval : Plus(Const(e1, Int()),Const(e2, Int())) -> Const(<add> (e1,e2), Int()) eval : Minus(Const(e1, Int()),Const(e2, Int())) -> Const(<subt> (e1,e2), Int())

Az osztás és maradékszámítás művelete esetén fenn áll a zéróosztás veszélye, ezért bevezetjük a zsafe(s) stratégiát, ami egy (a,b) párt b=0 nulla esetén hibaüzenetre cseréle, s csak ellenkező esetben hajtja végre a paraméterül megadott s stratégiát. Ezzel biztonságossá tehető a beépített div és mod stratégia.

strategies zsafe(s) = if(?(_,0),!Error("zerodiv"),!Const(<s>,Int())) rules eval : Div(Const(e1, Int()),Const(e2, Int())) -> <zsafe(div)> (e1,e2) eval : Mod(Const(e1, Int()),Const(e2, Int())) -> <zsafe(mod)> (e1,e2)

Használtuk az if stratégiát. Ha az első paraméterként megadott stratégia alkalmazható az aktív termre, akkor a második, különben a harmadik paraméterként megadott stratégiát alkalmazza az eredetileg aktív termre. Ezen a ponton különbözik az őrzött választástól. Elemi stratégiák kombinációjaként felírva: if(s,st,sf) = ?x; ((s; !x) < st + sf)

Most már le tudjuk fordítani értelmező programunkat. Erre az strc programot használjuk. Használjuk a standard könyvtárt; ennek összeszerkesztését a -la stratego-lib kapcsolóval kérjük. A fő forrásfájl megadása után a fordító összegyűjti a szükséges modulokat, C nyelvű programot generál, majd gcc-vel lefordítja.

$ strc -i a0i.str -la stratego-lib [ strc | info ] Compiling 'a0i.str' [ strc | info ] Front-end succeeded : [user/system] = [1.32s/0.06s] [ strc | info ] Optimization succeeded -O 2 : [user/system] = [0.04s/0.01s] [ strc | info ] Back-end succeeded : [user/system] = [0.83s/0.05s] libtool: compile: gcc -I /usr/local/include -I /usr/local/include -I /usr/local/include -Wall -Wno-unused-label -Wno-unused-variable -Wno-unused-function -Wno-unused-parameter -DSIZEOF_VOI D_P=4 -DSIZEOF_LONG=4 -DSIZEOF_INT=4 -c a0i.c -fPIC -DPIC -o .libs/a0i.o libtool: compile: gcc -I /usr/local/include -I /usr/local/include -I /usr/local/include -Wall -Wno-unused-label -Wno-unused-variable -Wno-unused-function -Wno-unused-parameter -DSIZEOF_VOI D_P=4 -DSIZEOF_LONG=4 -DSIZEOF_INT=4 -c a0i.c -o a0i.o >/dev/null 2>&1 libtool: link: gcc .libs/a0i.o -o a0i -L/usr/local/lib /usr/local/lib/libstratego-lib.so /usr /local/lib/libstratego-lib-native.so /usr/local/lib/libstratego-runtime.so /usr/local/lib/libA Term.so -lm -Wl,-rpath -Wl,/usr/local/lib -Wl,-rpath -Wl,/usr/local/lib [ strc | info ] C compilation succeeded : [user/system] = [1.25s/0.68s] [ strc | info ] Compilation succeeded : [user/system] = [3.44s/0.80s]

Az így kapott futtatható a0i program szöveges reprezentációjú ATermeken dolgozik. Elemezzünk egy forrásnyelvi szövegrészletet és futtassuk le rajta elemzőnket eddigi állapotában!

$ echo "4+5*6-7" | sglri -p a0.tbl | ./a0i 27 $ echo "10/(5*6-6*5)" | sglri -p a0.tbl | ./a0i [Error("zerodiv")]
Logikai kifejezések

Egészítsük ki nyelvünket logikai kifejezéseken végezhető műveletekkel! Definiálunk egy BoolVal szortot, felveszünk megfelelő konstruktorokat. A Bool() termmel jelöljük egy konstansról, hogy logikai érték. Adjuk meg a logikai műveleteket BoolValok felett; ezek alapján adjuk meg a logikai műveleteket megadó termek redukciós szabályait.

module a0bool imports a0common libstratego-lib signature sorts BoolVal constructors True : BoolVal False : BoolVal Bool : Type rules and-b : (False(), _) -> False() and-b : (a, True()) -> a or-b : (True(), _) -> True() or-b : (a, False()) -> a neg-b : True() -> False() neg-b : False() -> True() rules eval : BoolLiteral("true") -> Const(True(), Bool()) eval : BoolLiteral("false") -> Const(False(), Bool()) eval : And(Const(e1, Bool()), Const(e2, Bool())) -> Const(<and-b> (e1,e2), Bool()) eval : Or(Const(e1, Bool()), Const(e2, Bool())) -> Const(<or-b> (e1,e2), Bool()) eval : Negate(Const(e1, Bool())) -> Const(<neg-b> e1, Bool())

Készítsük el a relációs jelekhez tartozó operátorok szabályait. A beépített összehasonlító stratégiák egy (a,b) számpáron végeznek transzformációt. Ha a reláció teljesül, az aktív termet nem változtatja; ha nem teljesül, a transzformáció sikertelen. E sikeres/sikeretlen logikát át kell írni True() ill. False() termekre; ehhez bevezetjük a bool-b stratégiát.

strategies bool-b(s) = if(s, !True(), !False())

A tényleges összehasonlítást a beépített gt, lt, geq, leq stratégiákkal végezzük. Speciális eset az egyenlőség; ez teljesen általános, nem csak számokra értelmezzük. Termek közötti strukturális ekvivalenciát a eq = ?(x,x); !(x,x) stratégia állapít meg.

rules eval : Greater(Const(e1, Int()), Const(e2,Int())) -> Const(<bool-b(gt)> (e1,e2), Bool()) eval : Less(Const(e1, Int()), Const(e2,Int())) -> Const(<bool-b(lt)> (e1,e2), Bool()) eval : GreaterEq(Const(e1, Int()), Const(e2,Int())) -> Const(<bool-b(geq)> (e1,e2), Bool()) eval : LessEq(Const(e1, Int()), Const(e2,Int())) -> Const(<bool-b(leq)> (e1,e2), Bool()) eval : Equals(e1, e2) -> Const(<bool-b(eq)> (e1, e2), Bool())

Logikai művelet még az esetszétválasztás. Az If termek redukciójának definíciója triviális.

rules eval : If(Const(True(), Bool()), e1, _) -> e1 eval : If(Const(False(), Bool()), _, e2) -> e2

Ezek után elegendő kiegészítenünk a0i importjainak listáját. A különböző modulokban található eval szabályokat a fordító nemdeterminisztikus választással kapcsolja össze. Ha lefordítjuk a programunkat, egyszerű állításokat már el tudunk dönteni.

Let-kifejezések értelmezése

Egy let-kifejezés általános formája: let definíció1 and definíció2 ... and definíción in törzs. Az egyes definíciók nevekhez értéket rendelnek. A let-kifejezést úgy redukáljuk, hogy a törzsben a neveket behelyettesítjük, majd a törzset redukáljuk. Tesszük ezt mindaddig, míg nem konstans a törzs.

Az értelmező bővítése let-kifejezésekkel nemtriviális. Feladatunk immár előfeldolgozási lépéseket is igényel. A szintaxis megengedi, hogy konstans értéket és függvényt is köthessünk névhez. Előbbiek Let(név,törzs), utóbbiak Let(név,[paraméterek],törzs) formájú termek. Előfeldolgozási lépésként a függvényabsztrakciókat névtelen függvénnyé alakítjuk.

A névtelen függvény elsőrendű érték a nyelvünkben. Function([szabad-változók,törzs,[kötött-változók]) formájú termmel reprezentáljuk. Így a a0let modul első néhány sora:

module a0let imports a0common libstratego-lib signature constructors Function : List(Pattern) * Expr * List(LetDef) -> Expr rules transform-function : Let(n,p,e) -> Let(n, Function(p,e,[])) strategies transform-functions = innermost(transform-function)

A kiértékelés megkezdése előtt meg kell vizsgálnunk, hogy minden változónevet deklaráltunk-e. Az ellenőrzés során az egy adott pontig megismert neveket listába gyűjtjük. Ha a listában nem szerepel egy adott változónév - Var(x) term x résztermje - akkor a változó csomópontját egy Error("x undef") formájú termmel cseréljük le.

strategies check-vars = check-vars(|[]) check-vars(|xs) = check-lets(|xs) <+ check-var(|xs) <+ check-functions(|xs) <+ all(check-vars(|xs)) <+ id check-var(|xs) = ?Var(n); if((<filter(?n)> xs; ?[]), !Error(<conc-strings> (n, " undefined")), !Var(n)) check-lets(|xs) = ?Lets(ls, e); !ls; with-let-names(|xs); ?ns; !Lets(<check-vars(|ns)> ls, <check-vars(|ns)> e) check-functions(|xs) = ?Function(p, e, fs); !p; with-var-names(|xs); ?p1; !Function(p, <check-vars(|p1)> e, fs) with-var-names(|xs) = bu-collect(?VarPattern(_)); map(\ VarPattern(n) -> n\); !Conc(<id>,xs) rules with-let-names(|xs) : [] -> xs with-let-names(|xs) : [Let(n,_)|ls] -> <with-let-names(|[n|xs])> ls

Egészítsük ki a0i modulban a kiértékelési stratégiát úgy, hogy e két előfeldolgozási lépést is végrehajtsuk! Az új vezérlő stratégia:

s-main = transform-functions; check-vars; ?Program(e); <innermost(eval)> e; collect-errors; try(pretty-print)

Ha egy let-kifejezés törzsében konstans szerepel, úgy a let-kifejezés értéke ez a konstans. Ellenkező esetben megkíséreljük behelyettesíteni a definíciós részben kötött változókat. Ha ilyet nem tudunk, akkor a let-kifejezés nem redukálható. Ha a változónév ismeretlen, akkor egymásba ágyazott let-kifejezésekről lehet szó. Ebben az esetben a redukció egy szinttel feljebb folytatódik a kiértékelési stratégia miatt.

rules eval : Lets(ls, e) -> Lets(ls,<fill-vars(|ls)> e) eval : Lets(_, Const(v,t)) -> Const(v,t) fill-var(|ls) : Var(n) -> <fetch-elem(find-let(|n))> ls fill-lets-var(|ls) : Lets(ls1, e) -> Lets(<with-lets> (ls1,ls), e) with-lets : ([], ls) -> ls with-lets : ([Let(p,v)|ls1], ls) -> <with-lets> (ls1, <with-let(|p,v)> ls) find-let(|p,v) : Let(p,_) -> Let(p,v) find-let(|p) : Let(p,b) -> b strategies with-let(|p,v) = fetch(find-let(|p,v)) <+ ![Let(p,v)|<id>] fill-vars(|ls) = fill-var(|ls) <+ fill-lets-var(|ls) <+ some(fill-vars(|ls))

Már csak a függvények kiértékelése van hátra. A függvényapplikációt úgy értékeljük ki, hogy a következő szabad változóhoz hozzárendeljük a kötött értéket. Ha elfogytak a szabad változók, úgy a függvény egy egyszerű let-kifejezéssé alakítható, ahol a definíciós részben az egyes kötött változókhoz a kötött értékeket rendeljük, a törzs pedig maga a függvénytörzs.

strategies with-let(|p,v) = fetch(find-let(|p,v)) <+ ![Let(p,v)|<id>] fill-vars(|ls) = fill-var(|ls) <+ fill-lets-var(|ls) <+ some(fill-vars(|ls)) rules eval : App(Function([VarPattern(p)|ps],b,fs), v) -> Function(ps,b,<with-let(|p,v)> fs) eval : App(Lets(ls,Function(ps,b,fs)),v) -> Lets(ls,App(Function(ps,b,fs),v)) eval : Function([],b,fs) -> Lets(fs, b)

Próbáljuk ki kész értelmezőnket! Valósítsuk meg nyelvünkben a Fibonacci-függvényt!

$ cat fib.a0s let fib x = if x < 2 1 (fib x-1) + (fib x-2) in fib 4 $ cat peldak/fib.a0s | sglri -p a0.tbl | ./a0i 5

Ezzel láthatjuk, hogy Strategoban könnyen, kevés kóddal, modulárisan tudunk fordítóprogramokat és egyébb szoftvertranszformációs rendszereket készíteni.

Forrásfájlok

A forrásfájlokat Stratego/XT 0.17-tel le lehet fordítani; ezzel már kipróbálásra kerültek. A fordítást egyszerűsítendő található benne egy Makefile. A kipróbálást segíti a parse.sh és a interpret.sh melyek a standard bemeneten kapott forrásfájlt elemzik illetve értelmezik. A peldak alkönyvtárban olyan rövid programok találhatók, melyeket az itt elkészített értelmezővel le tudunk futtatni.

Letöltés .tar.gz

Összetettebb példák

A Stratego részét képezi néhány jól kidolgozott példanyelv.

A Spoofax/IMP rendszer új projekt létrehozásakor a projektet feltölti egy entitások és kapcsolataik leírására alkalmas nyelv és annak Java nyelvű generátora leírásával.