Direct Semantics Expressed in Pascal
See L. Allison, A Practical Introduction to Denotational Semantics, CUP, Cambridge Computer Science Texts V23, 1986.
These semantics include declarations and also allow side-effects within expressions (which are common in imperative languages, if dangerous).
Note function EE
and
function CC
in particular.
program ch6(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,writesy, beginsy, endsy, skipsy, varsy, procsy, semisy, becomessy, open, close, stopsy); {syntactic domains} exptype = (bexp, uexp, varr, int, ifexp); opr = (plus, minus, times, over, eq, ne, lt, le, gt, ge, neg); cmdtype = (block,call,assign, semi, ifstat, whiles, writte, skip); dectype = (declist, vardec, procdec); 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); ifexp :(e1, e2, e3 :exp) end; cmd = ^ cnode; dec = ^ dnode; dnode = record case tag:dectype of declist :(left,right:dec); vardec :(vid:alfa); procdec :(id:alfa; g:cmd) end; cnode = record case tag:cmdtype of block :(d:dec; body:cmd); call :(pid:alfa); assign :(lhs, e :exp); semi :(left,right:cmd); ifstat :(b:exp; gtrue,gfalse:cmd); whiles :(bw:exp; g:cmd); writte :(op:exp); skip :() end; {-----------------------------------------------------------semantic domains} Locn = integer; valtype = (isInt, isLocn, isProc); Ev = ^Evrec; {Ev = Locn + Int} {expressible values} Evrec = record case tag:valtype of isLocn: (l:Locn); isInt: (i:integer) end; Answer =^ansrec; { Answer = integer^* answers or output} ansrec = record v:integer; next:Answer end; Store = ^ storec; { Store = Ide -> Ev } storec = record l:Locn; v:Ev; next:Store end; State = ^starec; { State = Int x Store x Answer } starec = record free:integer; sto:Store; ans:Answer end; Dv = ^Dvrec; { Dv = Locn + Proc } {denotable values} Env = ^ envrec; { Env = Ide -> Dv } Dvrec = record case tag:valtype of isLocn : (l:Locn); isProc : (body:cmd; e:Env) end; envrec = record id:alfa; binding:Dv; next:Env end; EnvxState = ^exsrec; exsrec = record e:Env; s:State end; EvxState = ^evxsrec; evxsrec= record v:Ev; s: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}; startState:State; {------------------------------------------------------------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 if word = 'var ' then sy:=varsy else if word = 'proc ' then sy:=procsy else if word = 'write ' then sy:=writesy 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; ee1,ee2:exp):exp; var e:exp; begin new(e); consexp:=e; with e^ do begin tag := t; o:=f;{hope pc doesn't check} left:=ee1; right:=ee2 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 block,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; writte: op:=ctrle; skip: end end end; function consdec(t:dectype; i:alfa; gg:cmd; d1, d2:dec):dec; var d:dec; begin new(d); consdec:=d; with d^ do begin tag:=t; case t of declist : begin left:=d1; right:=d2 end; vardec : vid:=i; procdec : begin id:=i; g:=gg end end end end; {-----------------------------------------------------parser--------------} function parser:cmd; function pexp:exp; var e, eone, etwo :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} if nextsymis(ifsy) then begin eone:=pexp; check(thensy,'Cexp '); etwo:=pexp; check(elsesy,'Cexp '); new(e); with e^ do begin tag:=ifexp; e1:=eone; e2:=etwo; e3:=pexp end end else begin 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; end; pexp:=e end {pexp}; function pcmd:cmd;forward; function pdec:dec; var d:dec; x:alfa; begin if sy in [procsy, varsy] then begin if nextsymis(varsy) then begin d:=consdec(vardec, word, nil, nil, nil); insymbol end else {procsy} begin insymbol; x:=word; insymbol; check(eqsy,'pr=c '); d:=consdec(procdec, x, pcmd, nil, nil) end; check(semisy,'dec; '); d:=consdec(declist, ' ', nil, d, pdec) end else d:=nil; pdec:=d end; function pcmd {:cmd forward-ed}; var c, cpart :cmd; lh :exp; dpart :dec; 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; if nextsymis(becomessy) then begin new(lh); with lh^ do begin tag:=varr; id:=x end; new(c); with c^ do {lh:=rhs} begin tag:=assign; lhs:=lh; e:=pexp end end else {call} begin new(c); with c^ do begin tag:=call; pid:=x end end end else if sy = open then { (exp):=... or syntax not LL(n) } begin lh:=pexp; check(becomessy,'no:= '); new(c); with c^ do {lhs:=rhs} begin tag:=assign; lhs:=lh; e:=pexp end end else if nextsymis(ifsy) then c:=ifcmd else if nextsymis(whilesy) then c:=whilecmd else if nextsymis(writesy) then c:=conscmd(writte, pexp, nil, nil) else if nextsymis(beginsy) then begin dpart:=pdec; cpart:=pcmd; while nextsymis(semisy) do cpart:=conscmd(semi, nil, cpart, pcmd); check(endsy, 'end '); new(c); with c^ do begin tag:=block; d:=dpart; body:=cpart 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); procedure disp(m:Store); begin if m=nil then writeln(' finish') else begin write(' [', m^.l:3, ']=', m^.v^.i:3); disp(m^.next) end end; procedure prout(a:Answer); begin if a <> nil then begin write(a^.v:4); prout(a^.next) end end; begin write('ans=['); prout(s^.ans); writeln(']'); disp(s^.sto) end; function consSta(f:integer; s:Store; a:Answer):State; var p:State; begin new(p); consSta:=p; with p^ do begin free:=f; sto:=s; ans:=a end end; function pairEnvSta(e:Env; s:State):EnvxState; var p:EnvxState; begin new(p); pairEnvSta:=p; p^.e:=e; p^.s:=s end; function pairEvSta(v:Ev; s:State):EvxState; var p:EvxState; begin new(p); pairEvSta:=p; p^.v:=v; p^.s:=s end; function LocnDv(l:Locn):Dv; var p:Dv; begin new(p); LocnDv:=p; p^.tag:=isLocn; p^.l:=l end; function ProcDv(e:Env;g:cmd):Dv; var p:Dv; begin new(p); ProcDv:=p; p^.tag:=isProc; p^.e:=e; p^.body:=g end; function IntEv(i:integer):Ev; var p:Ev; begin new(p); IntEv:=p; p^.tag:=isInt; p^.i:=i end; function LocnEv(l:Locn):Ev; var p:Ev; begin new(p); LocnEv:=p; p^.tag:=isLocn; p^.l:=l end; function undefEv:Ev; {not a function in the semantics; a fn here to get side-effect of stop} begin undefEv:=nil {else pc complains}; error('run:undfEv') end; function undefDv:Dv; {see above} begin undefDv:=nil; error('run:undfDv') end; procedure wrong; {run time errors} begin error('run: wrong') end; function updateSto(s:Store; l:Locn; val:Ev):Store; var p:Store; begin new(p); p^.next:=s; updateSto:=p; p^.l:=l; p^.v:=val end; function applySto(s:Store; l:Locn):Ev; begin if s=nil then applySto := undefEv else if s^.l = l then applySto:=s^.v else applySto:=applySto(s^.next, l) end; function reserve(s:State):State; var p:State; begin new(p); p^:=s^; p^.free:=p^.free+1; reserve:=p end; function updateEnv(e:Env; id:alfa; b:Dv):Env; var newe:Env; begin new(newe); newe^.next:=e; updateEnv:=newe; newe^.id:=id; newe^.binding:=b end; function YYEnv(e:Env):Env; { for a recursive function } begin e^.binding^.e := e; YYEnv:=e end; function applyEnv(e:Env; id:alfa):Dv; begin if e=nil then applyEnv:=undefDv {undeclared identifier} else if e^.id=id then applyEnv:=e^.binding else applyEnv:=applyEnv(e^.next, id) end; function appendAns(a:Answer; v:Ev):Answer; var p:Answer; begin new(p); appendAns:=p; if v^.tag <> isInt then wrong; if a=nil then begin p^.v:=v^.i; p^.next:=nil end else begin p^.v:=a^.v; p^.next:=appendAns(a^.next, v) end end; function LL(e:exp; exs:EnvxState):EvxState; forward; function RR(e:exp; exs:EnvxState):EvxState; forward; function EE(e:exp; exs:EnvxState):EvxState; {: exp x Env x State -> EvxState } var denv:Dv; vxst:EvxState; v1xs1, v2xs2 :EvxState; function OO(o:opr; v1, v2 :Ev):Ev; { :opr x Ev x Ev -> Ev } var i1, i2 :integer; begin if (v1^.tag <> isInt) or (v2^.tag <> isInt) then wrong else begin i1:=v1^.i; i2:=v2^.i; case o of plus: OO:= IntEv(i1+i2); minus:OO:= IntEv(i1-i2); times:OO:= IntEv(i1*i2); over: OO:= IntEv(i1 div i2); eq: OO:= IntEv(ord(i1=i2)); ne: OO:= IntEv(ord(i1 <> i2)); lt: OO:= IntEv(ord(i1 < i2)); le: OO:= IntEv(ord(i1 <= i2)); gt: OO:= IntEv(ord(i1 > i2)); ge: OO:= IntEv(ord(i1 >= i2)) end {case} end end {OO}; begin {EE} case e^.tag of varr: begin denv:=applyEnv(exs^.e, e^.id); case denv^.tag of isLocn: EE:=pairEvSta(LocnEv(denv^.l), exs^.s); isProc: wrong end end; int: EE:= pairEvSta(IntEv(e^.i), exs^.s); uexp: begin vxst:= RR(e^.son, exs); EE:= pairEvSta( IntEv( -vxst^.v^.i), vxst^.s) end; bexp: begin v1xs1:= RR(e^.left, exs); v2xs2:= RR(e^.right, pairEnvSta(exs^.e,v1xs1^.s)); EE:=pairEvSta( OO(e^.o,v1xs1^.v,v2xs2^.v), v2xs2^.s) end; ifexp: begin vxst:=RR(e^.e1, exs); if vxst^.v^.i = 1 then EE:=EE(e^.e2, pairEnvSta(exs^.e,vxst^.s)) else EE:=EE(e^.e3, pairEnvSta(exs^.e,vxst^.s)) end end {case} end {EE}; function LL; { :exp -> Env -> State -> Ev x State } var vxs :EvxState; begin vxs := EE(e, exs); case vxs^.v^.tag of isInt: wrong; isLocn: LL:=vxs end end; function RR; { :exp -> Env -> State -> Ev x State } var vxs : EvxState; contents:Ev; begin vxs := EE(e, exs); case vxs^.v^.tag of isInt: RR:=vxs; isLocn: begin contents:=applySto(vxs^.s^.sto,vxs^.v^.l); RR:=pairEvSta(contents, vxs^.s) end end end; function DD(d:dec; exs:EnvxState):EnvxState; begin if d=nil then DD:=exs {block may have no declns} else case d^.tag of declist:{d1; d2}DD:=DD(d^.right, DD(d^.left, exs)); vardec :{var x} DD:=pairEnvSta(updateEnv(exs^.e,d^.vid,LocnDv(exs^.s^.free)), reserve(exs^.s)); procdec:{proc id=g fixed-pt for recursive procs} DD:=pairEnvSta(YYEnv(updateEnv(exs^.e, d^.id, ProcDv(nil{YY},d^.g))), exs^.s) end end; { State = Int x Store x Answer } function CC(g:cmd; exs:EnvxState):State; {: cmd x Env x State -> State} var denv:Dv; v1xs1, v2xs2 :EvxState; begin {Main interpreter routine} case g^.tag of block: CC:=CC(g^.body, DD(g^.d, exs)); call: begin denv:=applyEnv(exs^.e, g^.pid); case denv^.tag of isLocn:wrong; isProc:CC:=CC(denv^.body,pairEnvSta(denv^.e,exs^.s)) end end; assign: begin v1xs1:=LL(g^.lhs, exs); case v1xs1^.v^.tag of isInt: wrong; isLocn:begin v2xs2:=RR(g^.e,pairEnvSta(exs^.e,v1xs1^.s)); CC:=consSta(v2xs2^.s^.free, updateSto(v2xs2^.s^.sto,v1xs1^.v^.l,v2xs2^.v), v2xs2^.s^.ans) end end end; semi: CC:=CC(g^.right, pairEnvSta(exs^.e, CC(g^.left, exs)) ); ifstat: begin v1xs1:=RR(g^.b, exs); if v1xs1^.v^.i = 1 then CC:=CC(g^.gtrue, pairEnvSta(exs^.e, v1xs1^.s)) else CC:=CC(g^.gfalse,pairEnvSta(exs^.e, v1xs1^.s)) end; whiles: begin v1xs1:=RR(g^.bw, exs); if v1xs1^.v^.i = 1 then CC:=CC(g, pairEnvSta(exs^.e, CC(g^.g, pairEnvSta(exs^.e, v1xs1^.s)) )) else CC:=exs^.s end; writte: begin v1xs1 := RR(g^.op, exs); CC:= consSta(v1xs1^.s^.free, v1xs1^.s^.sto, appendAns( v1xs1^.s^.ans, v1xs1^.v) ) end; skip: CC:=exs^.s end {case} end {CC}; {-----------------------------------------------------------------main---} begin writeln(' A Pure Language, L.Allison U.W.A.'); lineno:=1; write(lineno:4, ':'); word:='-starting-'; n:=0; ch:=nextch; insymbol; new(startState); with startState^ do begin free:=0; sto:=nil; ans:=nil end; display( CC(parser, pairEnvSta({Env=}nil,startState) ) ); 99: {fin} end.