Program struktúra
A program egy fordítási egysége lehet blokk, vagy deklarációs lista. Ha ez egy
blokk, akkor "program", a szó hagyományos értelmében. Ha ez egy deklarációs
lista, akkor a hatásköre rendszerfüggõ.
<compilation unit> ::= begin <block> end | <exec decl list>
<block> ::= {<exec decl list>;}#{<stmt>};+ {;}#
<exec decl list> ::= {<exec decl>};*
<exec decl> ::= <var decl> | <const dec> | <proc decl> |
<form decl> | <label decl>
Egy blokk végrehajtása az alábbi sorrendben történik:
1. Deklarációk kiértékelése a megadás sorrendjében.
2. Az utasítások szekvenciális végrehajtása.
3. Objektumok felszabadítása a deklarációval ellentétes sorrendben
Kifejezések és utasítások:
A kifejezések és utasítások definiálják a végrehajtandó akciókat. Ezek
kiértékelése eredményeként változik a program állapota. A kifejezések abban
különböznek az utasításoktól, hogy van visszatérési értékük, míg az
utasításoknak nincsen. Ezáltal minden kifejezés utasítás is egyben. Így ha
ilyen környezetben használjuk, akkor a visszatérési érték érvénytelenítõdik.
<expression> ::= <invocation> | <conditional expression> |
<value expression> | <with expression> |
<first expression>
<stmt> ::= <expression> | <loop stmt> | <exit stmt> |
<null stmt> | <inner block> | <labelled stmt> |
<assert stmt>
<labelled stmt> ::= <identifier> : <stmt>
A címkézett utasítások utasításokból történõ kilépésre használatosak. A
kilépés hatására a vezérlés azonnal átkerül a címkét követõ utasításra. A
címkéket deklarálni kell, és hatáskörükön belül egyszer alkalmazhatók.
Eljáráshívások
<invocation> ::= <special literal> |
<simple invocation>{<actuals>#|}(<invocation>)
<actuals> ::= ({<actual>},*)
<actual> ::= <expression> | <type description>
<simple invocation> ::= {<identifier>$}*<identifier> |
<special identifier>
Példák:
sin(x)
integrate(F,a,b,eps)
&:=(a,&+(b,c))
&:=(x,&subscript(V,i))
vector(int,1,unk)
Egy P paraméterek nélküli eljárás hívása:
P( ).
Feltételes kifejezések
<conditional expression> ::= <if expression> | <case expression>
<if expression> ::= if <expression> then <block>
{elif <expression> then <block>}* {else <block>}# fi
<case expression> ::= case <expression> of <case> {elof <case>}*
{else <block>}# esac
<case> ::= {<expression>},+ :: <block>
Példák:
if a[i]>max then maxp:=i; max:=a[i]; fi
y := if x>z then z else z fi
if a<b then t:=1 elif a<c then t:=1
else t:=3 fi
case IC of
ADD::MB:=C[EA}; R:=R+MB elof
SUB::MB:=C[EA}; R:=R-MB elof
MUL::MB:=C[EA}; R:=R*MB else
ERROR
esac
T:=case n of 1::MALE elof 2::FEMALE else NEUTER
esac
case i of
0,2,4::R:=0 elof
1,3::R:=1 elof
esac
A feltételes kifejezések kifejezésként és utasításként is viselkedhetnek. Egy
ilyen kifejezésnek akkor van értéke, ha:
a) Minden blokk egy egyszerû kifejezés.
b) Ezek a kifejezések azonos típusúak (ez lesz a kifejezés típusa).
c) Van else ág.
Az alábbi két kifejezés ekvivalens:
1.
if B1 then S1 elif B2 then S2 ... elif
Bn then Sn else S0 fi
2. if B1 then S1 else
if B2 then S2 else
...
if Bn then Sn else S0 fi
...
fi
fi
A value kifejezés
<value expression> ::= value <identifier>{:<obk type>}# of <block>
fo
Példák:
S:=value y:int of y:=0; for x:invec(A) do y:=y+1 od
fo
value A of Munge(A,43) fo
Egy
value kifejezés utasítások szekvenciáját, illetve blokkokat
konvertál át egy értékátadás kifejezéssé. A
value x:T of S fo
kifejezésben az x változó (aminek hatásköre S) definiálódik, és S
végrehajtódik. Ha a hiányzik, mint a
value x of S fo
kifejezésben, akkor a meglévõ x változót használjuk. A value kifejezés értéke
mindkét esetben Obj(x).
A with kifejezés
<with expression> ::= with <with list> in <block> ni
<with list> ::= {<identifer>:<invocation>}+
Példák:
with Z:A[i].son[k] in Z.age:=0; Z.number:=k ni
with R:x.y.z, Q:x.y.w in var s:T; s:=Q; Q:=R;R:=s ni
A
with kifejezés bonyolultabb hívások egyszerûsítésére szolgál. A
with x:R in S ni
kifejezésben, ami R számítására szolgál, létrejön egy kötés x és R között,
majd S ezzel a kötéssel értékelõdik ki. Ha a <block> egy egyszerû
kifejezés, akkor a
with kifejezésnek van értéke (a <block>
értéke).
A first kifejezés
<first expression> ::= first <template> suchthat <expression>
{then <block>}# {else <block>}# fi
<template> ::= <identifier> from <invocation> |
<identifier> from {<identifier>:}#<type description>
Példák:
first i from upto(1,n) suchthat A[i]>max then
max:=A[i]; jmax:=i; fi
y:=first x from invec(A) suchthat x>max then x
else 0 fi
A
first kifejezés a template-ben generált generátort (lásd késõbb)
használja érték sorozatának létrehozására. Ezeket az értékeket a
suchthat kulcsszó utáni logikai kifejezés ellenõrzi. Ha a
first x from g:Q suchthat B then S1
else S2 fi
first kifejezés értékére nincs szükség, akkor az alábbiakkal ekvivalens:
L1: begin
L2: begin
var g:Q; &start(g);
do
if &done(g) then &finish(g); leave L2 fi
with x:&value(g) in if B then S1; &finish(g);
leave L1 fi ni
&next(g)
od
end L2
S2
end L1
ahol &start, &done, stb ... a Q
form (vagy
generator) által
nyújtott szolgáltatások. Ha mind a
then mind az
else ág
hiányzik, akkor az alapértelmezett
skip helyettesítõdik be. Ha g:
hiányzik, akkor egy sehol máshol nem használt azonosító helyettesítõdik be.
Vegyük észre, hogy S2 kiesik x és g hatáskörébõl. (Ezért ezeket itt nem
használhatjuk.)
Ha a
first kifejezésnek eredményt kell visszaadnia, akkor S1 és S2
egyszerû kifejezések kell legyenek, azonos visszatérési értékkel (legyen ez
T):
value t:T of first x from g:Q suchthat B then
t:=S1 else t:=S2 fi fo
Ciklusok
4 lehetõség van ciklus létrehozására:
(a) A do ciklus addig ismétli a törzset, amíg az egy exit hívással ki nem
lép a ciklusból.
(b) Egy aritmetikai for ciklus, ami hasonló a megszokotthoz, eltérés csak a
ciklusváltozóban van, annak ún. generátorformának kell lennie.
(c) A first kifejezéssel definiált ciklus az adatszerkezetek kezeléséhez ad
segítséget. (Egy adott tulajdonságú elem létezése alapján hajtja végre az
utasítást.)
(d) A negyedik utasítás a szokásos while ciklus.
<loop stmt> ::= <simple loop> | <while stmt> | <for stmt>
<simple loop> ::= do <block> od
<while stmt> ::= while <expression> <simple loop>
<for stmt> ::= for <template> <single loop>
Példák:
do
if x=y then exitloop fi
if x>y then x:=x-y else y:=y-x fi
od
while x<>nil do P(car(x)); x:=cdr(x); od
for x from invec(a) do x::=0 od
A
"do S od"
egyszerû ciklus S-et ismételi folyamatosan. Akkor
terminál, ha egy kilépés utasítás (lásd késõbb) hívódik meg. A
"while B do S od"
ciklus szemantikailag ekvivalens az
alábbi ciklussal:
do if not(B) then exitloop fi; S od
A
"for x from g:Q do S od"
ciklus szemantikailag ekvivalens az alábbi utasítással:
begin
var g:Q; &start(g)
do
if &done(g) then exitloop fi
with x: &value(g) in S ni
&next(g)
od
&finish(g)
end
A kilépés utasítás
<exit stmt> ::= exitloop | leave <identifier>
Például:
leave L
exitloop
A
leave L utasítás egy L -el címkézett utasításon belül érvényes,
terminálja az utasítást.
Az
exitloop ciklusok belsejében érvényes, terminálja a ciklust.
Az üres utasítás
<null stmt> ::= skip
Az üres utasítást ki kell írni.
Belsõ blokk
<inner block> ::= begin <block> end | begin <block>
endof {<identifier>#}
Ha az opcionális azonosítót kiírjuk, akkor a blokkot ezzel az azonosítóval
kell címkézni, vagy a blokk egy ilyen nevû eljárás törzse kell legyen.
Az assert utasítás
<assert stmt> ::= assert <assertion>
Például:
do assert {GCD(x,y)=GCD(s0,y0)};
if x=y then exitloop fi
if x>y then x-:=y else y-:=x fi
od
Az
assert utasítás egy feltételt ad meg, ami akkor értékelõdik ki,
amikor a vezérlés az utasításra kerül. Helyességbizonyítási eszköz.
Az <assertion>-t nem definiálja pontosan a nyelv.
Operátorok
Az Alphardban nem a hagyományos módon tekintjük az operátorokat, hanem úgy
tekintjük az alkalmazásukat, mint függvényhívásokat. Így a programozó
felüldefiniálhatja ezeket. Két fontos tulajdonság azonban így is igaz kell
maradjon:
1. Asszociativitás: a legmagasabb és legalacsonyabb precedenciájú
operátorok jobb, a többiek bal asszociatívak.
2. Precedencia:
^ (legmagasabb precedencia)
* / div rem
+ -
<= < = <> > >=
not
and cand
or cor
imp
:= +:= -:= *:= stb... (legalacsonyabb precedencia)
A nyelvben az alábbi leképezések megengedettek (ha A egy unáris, B pedig
bináris operátor):
A <term> --> &A(<term>)
<term1> B <term2> --> &B(<term1>,<term2>)
<term1> B:= <term2> --> &:=(<term1>,&B(<term1>,<term2>))
Az alábbi operátorok automatikusan felülíródnak felüldefiniálás esetén, mint
negációk:
&<>(t1,t2) --> ¬(&=(t1,t2))
&<=(t1,t2) --> ¬(&>(t1,t2))
&>=(t1,t2) --> ¬(&<(t1,t2))
Az alábbi operátorok automatikusan felülíródnak felüldefiniálás esetén, mint
logikai operátorok:
t1 cand t2 --> if t1 then t2 else false fi
t1 cor t2 --> if t1 then true else t2 fi
Operátorok felüldefiniálása csak új típusok definiálása esetén megengedett,
meglévõ típusok operátorainak felüldefiniálására nincs lehetõség.