Elementary Semantics Expressed in Pascal
See L. Allison, A Practical Introduction to Denotational Semantics, CUP, Cambridge Computer Science Texts V23, 1986.
These semantics are for a "pure" imperative language where side-effects are impossible in expressions.
Note function EE
and
function CC
in particular.
program ch5(input, output); label 99; type {lexical objects} alfa = packed array [1..10] of char; symbol = (plussy, minussy, timessy, oversy, eqsy, nesy, ltsy, lesy, gtsy, gesy, ident, numeral, ifsy, thensy, elsesy, whilesy, dosy, beginsy, endsy, skipsy, semisy, becomessy, open, close, stopsy); {syntactic domains} exptype = (bexp, uexp, varr, int); opr = (plus, minus, times, over, eq, ne, lt, le, gt, ge, neg); cmdtype = (assign, semi, ifstat, whiles, skip); exp = ^ enode; enode = record case tag:exptype of bexp :(o:opr; left, right:exp); uexp :(u:opr; son:exp); varr :(id:alfa); int :(i:integer); end; cmd = ^ cnode; cnode = record case tag:cmdtype of assign :(id:alfa; e:exp); semi :(left,right:cmd); ifstat :(b:exp; gtrue,gfalse:cmd); whiles :(bw:exp; g:cmd); skip :() end; {semantic domains} Values = integer; State = ^ avar; avar = record ident:alfa; v:Values; next:State end; var lineno:integer; ch : char {current character}; sy : symbol {current symbol class}; word : alfa {current keyword, identifier or operator}; n : integer {value of current numeral}; {------------------------------------------------------------lexical-----} procedure error(a:alfa); begin writeln; writeln(' error:', a,' ch=[' ,ch, '] word=[' ,word,'] n=',n:1); goto 99 end; function nextch:char; var ch:char; begin if eof then error('prem'' eof ') else if eoln then begin readln; writeln; lineno:=lineno+1; write(lineno:4,':'); nextch:=' ' end else begin read(ch); write(ch); nextch:=ch end end; procedure insymbol; var l:integer; ch2:char; begin while ch=' ' do ch:=nextch; if ch in ['a'..'z','A'..'Z'] then begin l:=0; word:=' '; while ch in ['a'..'z','A'..'Z','0'..'9'] do begin l:=l+1; if l <= 10 then word[l]:=ch; ch:=nextch end; if word = 'begin ' then sy:=beginsy else if word = 'end ' then sy:=endsy else if word = 'if ' then sy:=ifsy else if word = 'then ' then sy:=thensy else if word = 'else ' then sy:=elsesy else if word = 'while ' then sy:=whilesy else if word = 'do ' then sy:=dosy else if word = 'skip ' then sy:=skipsy else sy:= ident end else if ch in ['0'..'9'] then begin n:=0; while ch in ['0'..'9'] do begin n:=n*10+ord(ch)-ord('0'); ch:=nextch end; sy:=numeral end else if ch in ['+', '-', '*', '/', '=', '(', ')', ';', '.'] then begin case ch of '+': sy:=plussy; '-': sy:=minussy; '*': sy:=timessy; '/': sy:=oversy; '=': sy:=eqsy; '(': sy:=open; ')': sy:=close; ';': sy:=semisy; '.': sy:=stopsy end; ch:=nextch end else if ch in ['<' , '>', ':'] then begin ch2:=ch; ch:=nextch; case ch2 of '<' : if ch='=' then sy:=lesy else if ch='>' then sy:=nesy else sy:=ltsy; '>': if ch='=' then sy:=gesy else sy:=gtsy; ':': if ch='=' then sy:=becomessy else error('no = in :=') end; if sy in [lesy,nesy,gesy,becomessy] then ch:=nextch end else error('insymbol ') end {insymbol}; procedure check(s:symbol; chs:alfa); var m:alfa; begin if sy=s then insymbol else begin m:='chck( )'; m[6]:=chs[1]; m[7]:=chs[2]; m[8]:=chs[3]; m[9]:=chs[4]; error(m) end end; function nextsymis(s:symbol):boolean; begin nextsymis:=true; if sy=s then insymbol else nextsymis:=false end; function consexp(t:exptype; f:opr; e1,e2:exp):exp; var e:exp; begin new(e); consexp:=e; with e^ do begin tag := t; o:=f;{hope pc doesn't check} left:=e1; right:=e2 end end; function conscmd(t:cmdtype; ctrle:exp; g1,g2:cmd):cmd; var c:cmd; begin new(c); conscmd:=c; with c^ do begin tag:=t; case t of assign: error('cons cmd '); semi: begin left:=g1; right:=g2 end; ifstat: begin b:=ctrle; gtrue:=g1; gfalse:=g2 end; whiles: begin bw:=ctrle;g:=g1 end; skip: end end end; {-----------------------------------------------------parser--------------} function parser:cmd; function pexp:exp; var e:exp; o:opr; function pexp1:exp; var e:exp; o:opr; function pexp2:exp; var e:exp; o:opr; function pexp3:exp; var e:exp; begin if sy=open then begin insymbol; e:=pexp; check(close, ') ') end else if sy=minussy then begin insymbol; e:=consexp(uexp, neg, pexp3, nil) end else if sy=ident then begin new(e); with e^ do begin tag:=varr; id:=word end; insymbol end else if sy=numeral then begin new(e); with e^ do begin tag:=int; i:=n end; insymbol end else error('pexp3 '); pexp3:=e end {pexp3}; begin {pexp2} e:=pexp3; while sy in [timessy,oversy] do begin if sy=timessy then o:=times else o:=over; insymbol; e:=consexp(bexp, o, e, pexp3) end; pexp2:=e end {pexp2}; begin {pexp1} e:=pexp2; while sy in [plussy,minussy] do begin if sy=plussy then o:=plus else o:=minus; insymbol; e:=consexp(bexp, o, e, pexp2) end; pexp1:=e end {pexp1}; begin {pexp} e:=pexp1; if sy in [eqsy .. gesy] then begin case sy of eqsy: o:=eq; nesy: o:=ne; ltsy: o:=lt; lesy: o:=le; gtsy: o:=gt; gesy: o:=ge end; insymbol; e:=consexp(bexp, o, e, pexp1) end; pexp:=e end {pexp}; function pcmd:cmd; var c:cmd; x:alfa; function ifcmd:cmd; var e:exp; g1:cmd; begin e:=pexp; check(thensy, 'then '); g1:=pcmd; check(elsesy, 'else '); ifcmd:=conscmd(ifstat,e,g1,pcmd) end {ifcmd}; function whilecmd:cmd; var e:exp; begin e:=pexp; check(dosy, 'do '); whilecmd:=conscmd(whiles, e, pcmd,nil) end; begin if sy=ident then begin x:=word; insymbol; check(becomessy, ':= '); new(c); with c^ do {x:=rhs} begin tag:=assign; id:=x; e:=pexp end end else if nextsymis(ifsy) then c:=ifcmd else if nextsymis(whilesy) then c:=whilecmd else if nextsymis(beginsy) then begin c:=pcmd; while nextsymis(semisy) do c:=conscmd(semi, nil, c, pcmd); check(endsy, 'end ') end else if nextsymis(skipsy) then c:=conscmd(skip, nil,nil,nil) else error('parse cmd '); pcmd := c end {pcmd}; begin {parser} parser:=pcmd; if sy <> stopsy then error('no . ') end {parser}; {-------------------------------------------------------------semantics--} procedure display(s:State); begin if s=nil then writeln(' finish') else begin writeln(s^.ident,'=',s^.v:3); display(s^.next) end end; function undefined:Values; {not a function in the semantics; a fn here to get side-effect of stop} begin undefined:=0 {else pc complains}; error('run:undef ') end; function update(s:State; id:alfa; val:Values):State; { : State x Ide x Values -> State } var p:State; begin new(p); p^.next:=s; update:=p; p^.ident:=id; p^.v:=val end; function applyState(s:State; id:alfa):Values; begin if s=nil then applyState:=undefined else if s^.ident = id then applyState:=s^.v else applyState:=applyState(s^.next, id) end; function EE(e:exp; s:State):Values; {: exp x State -> Values } function OO(o:opr; v1, v2 :Values):Values; begin case o of plus: OO:=v1+v2; minus:OO:=v1-v2; times:OO:=v1*v2; over: OO:=v1 div v2; eq: OO:=ord(v1=v2); ne: OO:=ord(v1 <> v2); lt: OO:=ord(v1 < v2); le: OO:=ord(v1 <= v2); gt: OO:=ord(v1 > v2); ge: OO:=ord(v1 >= v2) end {case} end {OO}; begin {EE} case e^.tag of varr: EE:=applyState(s, e^.id); int: EE:=e^.i; uexp: EE:= - EE(e^.son, s) {- only unary op}; bexp: EE:= OO( e^.o, EE(e^.left, s), EE(e^.right,s)) end {case} end {EE}; function CC(g:cmd; s:State):State; {: cmd x State -> State} begin {Main interpreter routine} case g^.tag of assign: CC:=update(s, g^.id, EE(g^.e, s)); semi: CC:=CC(g^.right, CC(g^.left, s)); ifstat: if EE(g^.b,s)=1 then CC:=CC(g^.gtrue,s) else CC:=CC(g^.gfalse,s); whiles: if EE(g^.bw,s)=1 then CC:=CC(g, CC(g^.g,s)) else CC:=s; skip: CC:=s end {case} end {CC}; {-----------------------------------------------------------------main---} begin writeln(' A Simple Language, L.Allison U.W.A.'); lineno:=1; write(lineno:4, ':'); word:='-starting-'; n:=0; ch:=nextch; insymbol; display( CC(parser, {startState=}nil ) ); 99: {fin} end.