NBNBNB: Dette dokument er produceret vha. »Save as HTML« fra Word, så der kræves en del kvalificeret gætværk fra læserens side for at finde ud af, hvad der skulle have stået. (((Projektforslag: Skriv et velfungerende program, der omsætter Worddokumenter til HTML.)))
© 2000, Henning Christiansen
Roskilde Universitetscenter, Datalogi Forår 1999
Modul 2 kursus Notat nr. 8
Afleveringsopgave, del 1:
Typecheck og fortolker for sproget Pax
Pax er et lille Pascal-inspireret programmeringssprog, som er opfundet med henblik på denne opgave. Opgaven går ud på vha. Prolog at konstruere en type-checker og en fortolker (evt. også debugger) for sproget. Fortolkeren opbygges efter principperne beskrevet i ªSprog og abstrakte maskiner´, og omkring typecheck indeholder dette notat tilstrækkelig introduktion.
Det kan måske se besværligt ud at programmere en separat typechecker, men det gør det en hel del nemmere at skrive fortolkeren, da man i denne ikke behøver tage højde for alle mulige fejlsituationer.
I det følgende beskriver vi først sproget på overordnet vis, hvorefter (afsnit 2) vi giver en introduktion til typecheck og antyder, hvordan det kan implementeres i Prolog. Afsnit 3 giver hints til, hvordan en fortolker kan struktureres, og der introduceres nogle hjælpeprædikater, som er tilgængelige til brug ved opgavens løsning. Disse og andre hjælpeprædikater er tilgængelige på modul-2-kursets server sammen med nogle eksempelprogrammer, som vises nedenfor. Opgaveformuleringen er at finde i afsnit 4, hvor der også er nogel gode råd til arbejdet med opgaven.
1. Pax: Et imperativt sprog med procedurer
Her gives en kort introduktion til sproget. Semantikken skitseres, og i øvrigt forventes denne at være ªden sædvanlige´.
Pax er et programmeringssprog som arbejder med heltal, boolske værdier og heltalsarrays, og det har et udvalg af sædvanlige kontrolstrukturer og udtryk. Der kan erklæres procedurer, som kan være rekursive: En given procedure kan kalde sig selv og de procedurer, som er erklæret tidligere i programteksten. Der findes ikke lokale procedurer (dvs. procedurer i procedurer).
Vi ser bort fra konkret syntaks og syntaksanalyse og repræsenterer Pax-programmer som Prolog-termer efter principperne beskrevet i ªSprog og abstrakte maskiner´. Vi genbruger eksisterende operatorer i Sicstus Prolog samt en enkelt, der defineres således:
:- op(800,xfx, :=).
Et program repræsenteres ved en struktur af formen
program(
variabel-erklæringer, procedure-erklæringer,sætning )Her følger et eksempel på et program med tomme procedureerklæringer. Programmet erklærer to heltalsvariable, indsætter værdier i dem, og udregner største fælles divisor vha. Euklids algoritme.
program(
(var(a,int) ; var(b,int)),
tom,
( a:= 221 ; b:= 493 ;
while( a =\= b,
if( a > b,
a:= a-b,
b:= b-a)) ;
write_text('Resultat: ') ;
write(a) ;
nl))
Variabelerklæringer sættes sammen vha. semikolon og hvis der ikke er nogen, skrives blot
tom. Tilsvarende benyttes tom også, når der ikke er procedurer at erklære, og tilsvarende for den tomme sætning. Det korteste Pax-program, der eksisterer, ser altså således ud:program(tom, tom, tom )
Der kan erklæres variable af tre forskellige typer: heltal, bool'ske værdier og arrays af heltal. Eksempler:
var(a,int)
var(b,bool)
var(c,int_array(7))
Heltalsvariable kan rumme vilkårlige heltal (indenfor det omfang, Prolog kan præstere): ...,
-3, -2, -1, 0, 1, 2, 3, .....Boolske variable kan antage værdierne
true og false.Et array har celler nummereret fra
1 til den øvre grænse som angives ved heltalskonstanten i erklæringen, og den øvre grænse skal være mindst 2 (ud fra princippet om, at arrays med længde 0 eller 1 ikke er megen nytte til). Hver celle i et array kan rumme et heltal. Variable kan indgå i udtryk på sædvanlig måde, og vha. ª:=´ operatoren kan variablen gives en ny værdi. Hele arrays kan noteres ved lister, som det vil fremgå nedenfor. Såvel et helt array som dets enkeltceller kan benyttes som variable, og enkeltcellerne (arrayopslag) noteres vha. en ^-operator. Eksempler:a:= 1
b:= true
c:= [11,22,33,44,55,66,77]
c^3 := 3333
(og vi må forventec= [11,22,3333,44,55,66,77]
)c^(a+2) := c^3 * 5
(svarende til (c^3) * 5)Variable initialiseres automatisk, når de erklæres, (afhængig af deres type) til
0, false eller [0, ..., 0].Som variabelnavne kan benyttes alt hvad Prologs
atom(-) accepterer, bortset fra true og false.I Pax har vi udtryk med typer svarende til de, som kunne benyttes i variabelerklæringerne. Logiske betingelser som benyttes i f.eks.
if og while skal ikke opfattes som værende af kategori betingelse men som udtryk af type bool; flere detaljer om typer senere.For heltal kan vi benytte
+, -, *, // (for heltalsdivision) og prefiks-minus. Vi har sammenligningerne mellem tal (hvis resultat er af type bool): =, >, >=, <, =<, =\=. For boolske værdier kan benyttes /\, \/, \+ med betydningerne ªog´, ªeller´ og ªikke´. Bemærk af hensyn til opgavens løsning, at alle operatorer pånær de tre sidste, er valgt så de svarer til lignende faciliteter i Prolog: operatorer som forstås af ªis´, eller prædikater, som kan fortolkes direkte af Prolog (=,> osv.).Her følger et eksempel på på et Pax-program med en procedureklæring. Proceduren indeholder en rekursiv implementation af quicksort. Vi benytter procenttegn til at angive kommentarer som i Prolog; disse kommentarer er ikke en del af programteksten.
program(
(var(n,int); var(a, int_array(4))),
proc( qsort, left, right,
(var(i,int); var(j,int);
var(x,int); var(w,int)),
(i:= left; j:= right;
x:= a^( (left+right)//2) ;
repeat( (while(a^i<x, i:= i+1) ;
while(x<a^j, j:=j-1) ;
if(i=<j,
(w:=a^i; a^i:= a^j; a^j:= w;
i:= i+1; j:= j-1))
),
% until
i > j); % end repeat
if( left<j, proc(sort,left, j)) ;
if( i < right, proc(sort,i,right)) )
), % end proc qsort
% hovedprogram:
(n:= 4 ;
a:= [30,10,40,20] ;
proc(qsort,1,n);
write(a)) )
Vi bemærker, at proceduren har to parametre (her kaldet
left og right), et sæt lokale variable (her i, j, x, w) samt en krop.Det generelle mønster for er procedureerklæringer således idet, vi kan have procedurer med netop én eller netop to parametre.
proc(
navn, parameter, lokale-variable, sætning)proc(
navn, parameter1, parameter2, lokale-variable, sætning)Når en procedure kaldes sker det (som man kan se ovenfor) vha. en konstruktion som således ud:
proc(
navn, aktuel-parameter)proc(
navn, aktuel-parameter1, aktuel-parameter2)De aktuelle parametre kan være vilkårlige heltalsudtryk, og parameteroverførsel sker ved call-by-value, dvs. de aktuelle parameter udregnes først, og inde i proceduren bindes de til parametrene, der så fungerer som lokale variable inde i proceduren.
I fortolkeren kan der hensigtmæssigt benyttes en rekursionsstak til opbevaring af de forskellige variable; dette beskrives i afsnit 3 nedenfor, og der stilles i forvejen programmerede faciliteter til rådighed til håndtering af rekursionsstakken.
Udover lokale variable (der som nævnt også inkluderer parametrene der optræder i procedureerklæringen) kan man referere globale variable. Proceduren
qsort ovenfor refererer eksempelvis til det globale array a.Pax indeholder følgende sætningsformer, hvoraf de fleste er beskrevet i et mindre eksempel (while-programmer) i ªSprog og abstrakte maskiner´, og disse kan sammensættes vha. semikolon som vist i de to eksempelprogrammer ovenfor.
tom
foretager intetvariabel
:= udtryk sædvanlig assignmentif(
udtryk, sætning1, sætning2) sædvanlig if-then-elseif(
udtryk, sætning) sædvanlig if-thenwhile(
udtryk, sætning) sædvanlig while-løkkerepeat(
Sætningen udføres indtil
udtrykket evaluerer til
true
. Modsat while, udføres kroppen mindst én gang ved repeat.write(
udtryk ) Evaluerer udtryk og udskriver værdiwrite_text(
Prologterm ) Udskriver vilkårligProlog-term, f.eks. ªquoted atoms´
nl
udskriver et linjeskiftVi vil nu diskutere synlighed af erklæringer (også kaldet virkefelt) i Pax, der er valgt bl.a. med henblik på, at det skal være nemt at implementere. Som nævnt kan en procedure kun kalde procedurer erklæret tidligere samt sig selv. Betragt følgende skitse på et program:
program( tom,
( proc( p, ...) ;
proc( q, ...) ),
...)
Hovedprogrammet kan kalde såvel p som q, kroppen for q kan kalde p og q, og p kan kun kalde p.
Der indføres ikke et forbud mod at erklære flere procedurer eller variable med sammenfaldende navne; det vil blot være det senest erklærede emne, som er det aktuelt gældende.
2. Om typecheck og typebetingelser for Pax
Pax er et sprog med typer og erklæringer, og det sætter noget begrænsninger på, hvad der kan accepteres som et legalt program. Her skitseres først disse begrænsninger, og dernæst antydes, hvordan en abstrakt syntaksbeskrivelse i Prolog kan udvides, så den medtager disse begrænsninger.
Den nemmeste måde at illustrere begrænsningerne på, er måske ved et program, som ikke overholder dem.
program( ( var(n, int); var(b, bool);
var(c,int_array(7) ),
( proc(p1,x,var(i,int), i:= b) ;
proc(p2,x,y,var(a,int_array(4)),
if(a, a^3:= false, x:= [1,2,3])),
(p1(c) ; p2(4) ;
c:= [1,2,n] ; xxx:= yyy) )
Dette program benytter sprogets ªabstrakte´ operatorer, men har mange ting, som vi ud fra et typesynspunkt vil kalde forkerte.
I proceduren
p1 forsøges en boolsk værdi (angivet ved den globalevariabel b) lagt over i en heltalsvariablen i. I p2 erklæres en lokal variabel a, som er et heltalsarray, og der forsøges lagt værdien false ind på arrayets 3. plads. Parametren x optræder på venstre side i en assignment, hvilket sådan set er OK, men højresiden skulle i såfald have en type svarende til heltal og ikke som her en type svarende til int_array(3). I hovedprogrammet er det også helt galt, alle procedurer havde pr. definition heltalsparametre, men her kaldes p1 med arrayet c som parameter, p2 kaldes med et forkert antal parametre, c som er af type svarende til int_array(7) forsøges assignet en værdi af type int_array(3). Endelig er sætningen xxx:= yyy ikke tilladt, da den benytter to variable, som ikke er erklæret.Ethvert korrekt udtryk i Pax har en veldefineret type , og følgende typer er mulige:
int, bool, int_array(2), int_array(3), int_array(4), ... .Vi kan beskrive principperne for korrekt ªtypning´ på følgende måde:
Som vi har beskrevet i ªSprog og abstrakte maskiner´ afs. 4.3.1, kan man beskrive den abstrakte syntak af et sprog ved prædikater, som accepterer netop sprogets abstrakte syntakstræer. Eksempelvis kan vi beskrive reglen for plus ved følgende regel:
udtryk(U1+U2):- udtryk(U1), udtryk(U2).
Hvis vi for en stund ser bort fra variable i udtrykkene, kan vi beskrive typebetingelsen ved at tilføje et ekstra argument til de syntaktiske prædikater, som holder typen. Vi ændrer prædikaternes navne ved at sætte ªt_´ foran, så man kan se, at det er et nyt prædikat, som foretager typecheck. I eksemplet med plus-udtrykket kommer reglen til at se således ud:
t_udtryk(U1+U2,int):-
t_udtryk(U1,int), t_udtryk(U2,int).
Men nu var vi brug for at sammenholde forekomster af variable i udtryk med de typer, variablene fik ved deres erklæringer. Til det formål indføres en symboltabel, som skabes ved erklæringerne og transporteres ud til udtrykkene.
Hvis vi har en erklæring var(n, int), er det relevant, at der produceres en symboltabel, som indeholder et par (x, int). For at transportere symboltabellerne ud i alle udtryk og deludtryk kan type-checkprædikaterne udstyres med nok et argument, som indeholder den aktuelle symboltabel. Plus-reglen kommer nu til at se således ud:
t_udtryk(U1+U2,SymbolTabel,int):-
t_udtryk(U1, SymbolTabel, int),
t_udtryk(U2, SymbolTabel, int).
Når vi når ned til en variabel et eller andet sted i et udtryk kan vi teste sammenholde erklæring og anvendelse ved følgende regel, idet vi antager, at symboltabellen er repræsenteret som en liste af de omtalte slags par.
t_udtryk(Variabel, SymbolTabel, Type):-
atom(Variabel), !,
member((Variabel, Type), SymbolTabel).
På denne måde vil vi opnå en typechecker, som accepterer korrekt typede programmer, og som fejler (dvs. Prolog siger ªno´, hvis programmet ikke er korrekt.
Af hensyn til at lokalisere typefejl og de programmeringsfejl, man utvivlsomt vil lave under løsning af opgaven foreslås det, at man erstatter kaldet af member med er prædikat fremstillet til formålet kaldet symbol_tabel_find, så reglen kommer til at se sådan ud:
t_udtryk(Variabel, SymbolTabel, Type):-
atom(Variabel), !,
symbol_tabel_find(Variabel, SymbolTabel, Type).
Dette hjælpeprædikat vil udsende en forklarende fejlmeddelelse i tilfælde af, at et symbol ikke findes i tabellen eller er registreret med en anden type end den, man i kaldet angiver, at man ønsker at finde. Prædikatet er tilgængeligt på kursets server sammen med andre hjælpeværktøjer, som beskrives i det følgende.
Her er eksempler på fejlmeddelelser produceret af symbol_tabel_find ved typecheck at fejlagtige programmer.
Typefejl for b: forventede int, fandt bool
Fejl: bb ikke erklæret: forventede int
Illegal term anvendt som variabel: [1,2,3]
Sammenhængen mellem procedurers parameterantal i erklæring og anvendelse kan beskrives ved knytte nogle typer vi vil kalde proc1 og proc2 (for procedurer med hhv. én og med to heltalsparametre), der opbevares i symboltabellen helt ligesom variables værdier. Så erklæringen af en procedure ved navn p med to argumenter vil resultere i en indgang (p,proc2) i symboltabellen.
Sætninger og udtryk fungerer som ªforbrugere´ af symboltabeller, hvor erklæringer vil være ªproducenter´. For procedureerklæringer må der lidt omhyggelighed til, for at sørge for, at den kendte tabel (m. globale variable og forudgående samt aktuelle procedurer optræder) bliver tilgængelig inde i kroppen og at parametre og lokale variable gøres tilgængelige lokalt (og kun lokalt).
Det er nemmest, hvis de prædikater, som producerer tabeller udstyrese med to argumenter, en tabel-før og en tabel-efter. Så hvis vi eksempelvis har en symboltabel [(x,int)] før vi processerer erklæringen var(b, bool), vil vi efterfølgende få en tabel
[(b, bool), (x,int)].
Hvis denne tabel senere når hen til en procedureerklæring
proc(p,x,y,var(a,int_array(3)),
krop),vil der efter denne være opbygget en tabel af formen
[(p,proc2),(b, bool), (x,int)].
Inde i kroppen af prædikatet er det relevant med denne her tabel:
[(a, int_array(3)), (y,int), (x, int),
(p,proc2),(b, bool), (x,int)]
Dette kunne give anledning til en regel for typecheck af hele programmer, der ser således ud:
t_program( program(VarErk, ProcErk, Saetning)):-
t_var_erk(VarErk, [], SymbolTabel1),
t_proc_erk(ProcErk, SymbolTabel1, SymbolTabel2),
t_saetning(Saetning, SymbolTabel2).
3. Skitse af en fortolker for Pax; hjælpeværktøj.
Idéen med typecheckeren er, at den kører som en selvstændig proces, som går forud for fortolkningen, således, at når vi skriver fortolkeren kan vi koncentrere os om korrekt typede programmer, hvor der ikke skal skænkes en tanke til symboltabeller. Når et Pax-program er blevet godkendt af typecheckeren (hvis man altså har programmeret sin typechecker korrekt!), ved man, at de argumenter, som kommer frem til f.eks. regneoperationer har de rette typer.
Den eneste egenskab, som fortolkeren må kontrollere løbende er index-check for arrays, dvs. hvis eksempelvis
a er bundet til array-værdien [1,2,3,4], og i er en heltalsvariabel, så is værdi udregnes, og det skal kontrolleres, at denne værdi ligger mellem 1 og 4 for at a^i kan udregnes Alt vdr. indekscheck er indbygget i prædikater, der er givet på forhånd til håndtering af rekursionsstakken men nu er fænomenet nævnt for en ordens skyld.Fortolkeren kan grundlæggende opbygges, som beskrevet i afsnit 4.3.2 i ªSprog og abstrakte maskiner´ og benyttet for while-programmer i 6.2. Blot er der brug for en mere detaljeret model af et lager i form af en rekursionsstak, da vi her har procedurekald med rekursion og arrays. Prædikater til behandling af rekursionsstakken er tilgængelige til opgavens løsning, og nu forklares den valgte repræsentation for rekursionsstakken.
Stakken repræsenteres som en liste af stakafsnit, hvor vi tænker på bunden af stakken som det sidste (= ªhøjreste´) element, og det indeholder de globale variable. Der hægtes så nye afsnit på, når der kaldes en procedure, og stakkes af, når en procedure forlades. Bindinger til variable repræsenteres ved par af formen
(Variabel-navn, Værdi). Det er vigtigt at bemærke forskellen mellem bindingerne i rekursionsstakken og informationen gemt i en symboltabel: Symboltabellen vedrører statiske egenskaber ved symbolerne, i vores tilfælde deres type, hvorimod stakafsnittene i rekursionsstakken rummer bindinger af variable til deres dynamisk genererede værdier.Betragt som eksempel quicksortprogrammet vist i afsnit 1. Lige inden procedurekaldet
proc(qsort,1,n) ser rekursionsstakken således ud:[ [(a,[30,10,40,20]),(n,4)] ]
Nu kaldes proceduren, parametrenes værdier udregnes og overføres til lokale variable (kaldet
left og right) til dette procedurekalds stakafsnit, og der afsættes plads til de lokale variable i, j, x og w. Stakken ser nu således ud:[ [(w,0),(x,0),(j,0),(i,0),(right,4),(left,1)],
[(a,[30,10,40,20]),(n,4)] ]
Nu går procedurens sorteringsalgoritme igang, og når
repeat-sætningen er udført og lige inden de to if-sætninger ser stakken således ud; der er de samme stakafsnit, men nogle variables værdier er ændret:[ [(w,30),(x,10),(j,1),(i,2),(right,4),(left,1)],
[(a,[10,30,40,20]),(n,4)] ]
Man kan se videre, at betingelse i første
if-sætning vil evaluere til false, mens betingelsen for den anden bliver til true, hvilket giver anledning til et rekursivt procedurekald. Når parametrene er udregnet og overført, og der er afsat plads til et nyt sæt lokale variable, har vi følgende stak:[ [(w,0),(x,0),(j,0),(i,0),(right,4),(left,2)],
[(w,30),(x,10),(j,1),(i,2),(right,4),(left,1)],
[(a,[10,30,40,20]),(n,4)] ]
Vi har nu stakafsnit svarende til to inkarnationer af proceduren
qsort, dvs. der ligger to indgange i stakken for (eksempelvis) parametren right, den øverste for det seneste og nu aktive kald, og en ªlavere-liggende´ for det første procedurekald. Der vil blive aktiveret endnu flere rekursive kald, hvilket resulterer i endnu flere stakafsnit op på et tidspunkt, når de har afsluttet og deres stakafsnit er afstakket, når vi (hvis ellers fortolkeren er programmeret korrekt) tilbage til en stak:[ [(w,30),(x,10),(j,1),(i,2),(right,4),(left,1)],
[(a,[10,20,30,40]),(n,4)] ]
Endelig returnerer det sidste kald til hovedprogrammet, det smides nok en stakafsnit, og vi ender med en stak med ét afsnit (det globale):
[ [(a,[10,20,30,40]),(n,4)] ]
Og som det kan ses, har sætningerne i de rekursive kald sørget for, at det globalt erklærede array er blevet sorteret.
Vi kan se, at sætningerne har brug for at konsultere stakken når
En given aktivering af en procedure har kun brug for at konsultere to stakafsnit:
På grund af sprogets struktur (ingen lokale procedurer inde i procedurer)giver det ikke mening at konsultere de mellemliggende stakafsnit.
Ydermere er virkefeltsreglerne indrettet så visseligt at hvis der i en given aktivering af en procedure refereres til en variabel
x, såBemærk at et forudgående typecheck garanterer, at den må findes et af de to steder.
Som eksempel på en global variabel har vi arrayet
a i quicksortprogrammet, hvor alle aflæsninger og tilskrivninger i de mange rekursive kald netop (og heldigvis) nåede ned til det globale afsnit.Der er givet to prædikater til at operere på stakke i det beskrevne format:
hent_var(
Var,Stak) oggem_var(
Var, Værdi, StakFør, StakEfter)Variable kan angives som atomer (og referere til simple variable eller hele arrays) og som enkelt-celler i et array ved VariabelNavn
^Index, hvor Index er et heltal.Den regel i fortolkeren, som skal behandle en arrayindicering (som et udtryk) som a^(i+j), skal først udregne i+j, og hvis det nu f.eks. giver resultatet 7, er det relevant at kalde
hent_var(a^7, Stak)
Tilsvarende, for en sætning af formen a^(i+j):= (x+y)//z. Her skal i+j igen udregnes (og vi antager, den stadig er 7), og (x+y)//z skal også udregnes, skal vi sige med værdi 117, så kan vi derefter kalde
gem_var(a^7, 117, Stak1, Stak2)
hvor Stak1 refererer til den aktuelle stak og Stak2 til den opdaterede, som gives videre til næste sætning.
Disse to prædikater er indrettet, så de selv finder ud af, om den refererede variabel er lokal eller global. Af hensyn til de fejl, som med garanti vil opstå under udarbejdelse af en løsning på opgaven, er disse prædikater udstyret med et omfattende sæt af tests, som rapporterer en lang række fejlsituationer, som kan forekommer. Eksempelvis skal en given variabel findes i stakken allerede for at man kan få lov til at gemme en værdi i den, i modsat fald får man en fejlmeddelelse.
Udover at sende rekursionsstakken med rundt til alle sætninger og udtryk, er der også behov for information om de erklærede procedurer: Der skal være en måde, hvorpå en sætning som proc(qsort, 1, n) kan finde ud af, hvad den skal foretage sig. Vi kan her vælge at bruge en proceduretabel, som genereres ud fra procedureerklæringerne, og som ganske enkelt er en liste med alle procedurefinitioner (dvs. deres abstrakte syntakstræ, som det nu engang er givet). I quicksortprogrammet, hvor der kun er én procedure, vil proceduretabellen se således ud:
[proc( qsort, left, right, (var(i,int) ;
...),(i:= left ; j:= right ;
...))]Reglen for sætningsformen proc(...) kan så i eksemplet med quicksort slå op i tabellen ved et kald som følger, idet Pt forventes at være instantieret til den nævnte tabel:
member( proc(qsort,ParId1, ParId2, VarErkl, Saetning), Pt)
Vi får da
ParId1 = left
ParId2 = right
VarErkl = (var(i,int) ;
...)Saetning = (i:= left ; j:= right ;
...)Denne generelle for procedurekald kan dernæst evaluere de udtryk, der stå for de aktuelle parametre (her ª1´ og ªn´), og sætte et nyt stakafsnit op, som binder parametrenes værdier til de formelle parametre, samt rummer pladser til de (øvrige) lokale variable sådan som vi så det i rekursionsstakkene vist for et par sider siden.
Når det rette stakafsnit er opbygget og sat i forrest=øverst på stakken kan vi kalde den normale procedure for fortolkning af sætninger med procedurens krop som argument. Når dette fortolkerkald har afsluttet, skal vi så sørge for, at den stak som proc(qsort, 1, n) totalt har produceret er den rette, dvs. med det øverste stakafsnit pillet af igen, men hvor resten af stakken (med de indtrufne sideeffekter på global variable) er bevaret.
Til at fortolke sætninger generelt kunne man foreslå et prædikat med følgende form, idet vi generelt skelner fortolkerprædikaterne fra andre ved at sætte ªf_´ foran navnet på den syntaktiske kategori:
f_sætning(
Sætning, StakFør, ProcedureTabel, StakEfter)Proceduretabellen skal så blot kopieres uændret rundt til alle delsætninger, så den kan nås i anvendelser af denne regel:
f_sætning( proc(ParId1, ParId2, ...), St1, PT, St2):-
...Vi kan spørge om, hvad den ªoperationelle mening´ af variabel- og procedureerklæringer i et program er. For variabelerklæringerne handler det om at generere et stakafsnit med ªpladser´ for de erklærede variable, samt at initialisere disse til den startværdi, som variablenes type udsiger.
Tag som eksempel de globale variabelerklæringer i quicksortprogrammet (var(n,int) ; var(a, int_array(4))). Det, at ªudføre´ eller fortolke dem bør give anledning til følgende endnu uberørte stakafsnit:
[(a,[0,0,0,0]),(n,0)]
Til fortolkning af variabelerklæringer kunne man foreslå et prædikat af følgende form:
f_var_erk(
VariabelErklæringer, StakAfsnitFør, StakAfsnitEfter)Tilsvarende for procedureerklæringer, her skal blot omsamles en proceduretabel.
Dette kunne alt i alt give anledning til en regel for fortolkning af hele programmer, der ser således ud:
f_program( program(Var, Proc, Saetning)):-
f_var_erk(Var, [], Stakafsnit),
f_proc_erk(Proc, [], ProcTabel),
f_saetning(Saetning, [Stakafsnit], ProcTabel, _).
Bemærk, at den afsluttende rekursionsstak ignoreres, et Pax-program forventes selv at kunne præsentere sit resultat til omverdenen ved hjælp passende brug ad write og write_text.
4. Præcisering af opgaven
Opgaven lyder som følger:
Skriv en typechecker og en fortolker for sproget Pax, efter de principper, som er beskrevet i dette skrift og i ªSprog og abstrakte maskiner´
Det anbefales, at man arbejder sammen i grupper på to eller tre personer; énmandsgrupper frarådes, med mindre man har en stor erfaring i Prologprogrammering, og grupper på over tre frarådes, da det så bliver svært for alle at få ªfingrene i koden´, hvilke er en af de vigtigste pointer med opgaven.
Aflevering: Opgaven afleveres senest mandag 15. marts om morgenen, som det seneste, men det anbefales, at man sigter efter eftermiddagen fredag 12. marts. Opgavebesvarelsen afleveres i papirform i Hennings postkasse i hus 20.1. Opgaverne rettes af hjælpelærerne, evt. i samarbejde med kursuslæreren.
Gode råd omkring fremgangsmåde:
Altså i stedet for at forsøge at løse hele opgaven i ét huk, anbefales det, at man arbejder langsomt og støt bottom-up og gradvist udvider sin løsning til at omfatte større og større dele af Pax.
Krav til en besvarelse på opgaven:
Omkring opgavens omfang kan der være individuelle udsving i, hvor lang tid hver enkelt eller grupperne har behov for, men det er hensigten, at den skulle kunne løses indenfor den tid, man ellers skulle have brugt på kurset (hvis man ellers griber det rigtigt an). Hvis der opstår problemer, så tag kontakt med lærer eller hjælpelærere hellere tidligt end sent.
Det kan oplyses, at for testløsninger udviklet i forbindelse med design af opgaven fylder typechecker og fortolker for hele Pax hver især ca. 3 siders Prologkode, 181 resp. 225 linjer med et gennemsnit af 22 tegn pr. linje.
Hvis opgaven forekommer for nem for nogen, og som måtte finde interesse i at udvide den kan der foreslås følgende:
Dette er faktisk slet ikke så kompliceret, som det lyder, hvis man vel og mærket har forstået (= for alvor internaliseret) hvad kontrol er i Prolog. Find evt. på andre interessante kommandoer, f.eks. så man kan spørge på og ændre variables værdier i forhold til den aktuelle rekursionsstak.