123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939(* Claude Code
*
* Copyright (C) 2026 Yoann Padioleau
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License
* (LGPL) as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*)openPascal_lexeropenPcode(* See Pascal_compile.mli *)typeerror={line:int;col:int;message:string}exceptionFailedoferror(*****************************************************************************)(* Types and names *)(*****************************************************************************)typety=|Integer|Boolean|Character|Text(* a string constant: only written *)|Subrangeofty*int*int|Arrayofint*int*ty(* its index's bounds, its elements *)|Recordof(string*int*ty)list(* the fields, their offsets *)letrecsize(t:ty):int=matchtwith|Array(lo,hi,elt)->(hi-lo+1)*sizeelt|Recordfields->List.fold_left(funn(_,_,t)->n+sizet)0fields|Text->0|Integer|Boolean|Character|Subrange_->1(* a subrange is its base type, with bounds to check *)letrecbase(t:ty):ty=matchtwithSubrange(b,_,_)->baseb|t->tletrectype_name(t:ty):string=matchtwith|Integer->"integer"|Boolean->"boolean"|Character->"char"|Text->"string"|Subrange(_,lo,hi)->Printf.sprintf"%d..%d"lohi|Array(lo,hi,elt)->Printf.sprintf"array[%d..%d] of %s"lohi(type_nameelt)|Record_->"record"letscalar(t:ty):bool=matchbasetwithInteger|Boolean|Character->true|_->falsetypeparam={pname:string;pty:ty;by_ref:bool}typeproc={plevel:int;(* the level of its body: one more than where it is declared *)label:int;params:paramlist;result:tyoption;(* a function's *)mutabledefined:bool;(* its body compiled; false after a forward *)at:int*int;(* where its name was declared, for the error when its body never comes *)}typeentry=|Constofty*int|Const_textofstring|Varof{vty:ty;vlevel:int;offset:int;var_param:bool}|Procofproc|Typeofty|Standardofstring(* write, abs, ...: compiled each its own way *)(*****************************************************************************)(* The compiler's state *)(*****************************************************************************)typestate={toks:tokenarray;mutablepos:int;mutablelast:token;(* the last token read: its line the instructions' *)mutablecode:instrarray;mutablelines:intarray;mutablen:int;(* instructions emitted *)mutablelabels:intarray;(* a label's address, -1 until placed *)mutablenlabels:int;mutablescopes:(string*entry)listlist;(* the innermost first *)mutablelevel:int;mutableframe:int;(* the words the current frame takes so far *)mutablefunctions:proclist;(* the functions whose body is being compiled *)(* the debugger's information (Pcode.mli): the statements' addresses
and lines, the procedures compiled, and the one being compiled *)mutablemarks:(int*int)list;mutableprocedures:(int*Pcode.procedure)list;mutablenprocedures:int;mutablecurrent:int;}lettok(s:state):token=s.toks.(s.pos)letkind(s:state):kind=(toks).kindletadvance(s:state):unit=s.last<-toks;ifs.pos<Array.lengths.toks-1thens.pos<-s.pos+1letfail(s:state)(message:string):'a=lett=toksinraise(Failed{line=t.line;col=t.col;message})letis_symbol(s:state)(sym:string):bool=kinds=Symbolsymletis_keyword(s:state)(k:string):bool=kinds=Keywordkletexpect_symbol(s:state)(sym:string):unit=ifis_symbolssymthenadvanceselsefails(Printf.sprintf"'%s' expected, not %s"sym(Pascal_lexer.show(kinds)))letexpect_keyword(s:state)(k:string):unit=ifis_keywordskthenadvanceselsefails(Printf.sprintf"%s expected, not %s"k(Pascal_lexer.show(kinds)))letname(s:state):string=matchkindswith|Namen->advances;n|k->fails("identifier expected, not "^Pascal_lexer.showk)(* code *)letemit(s:state)(i:instr):unit=ifs.n=Array.lengths.codethenbeginletgrowafill=Array.appenda(Array.make(Array.lengtha+16)fill)ins.code<-grows.codeStp;s.lines<-grows.lines0end;s.code.(s.n)<-i;s.lines.(s.n)<-s.last.line;s.n<-s.n+1letnew_label(s:state):int=ifs.nlabels=Array.lengths.labelsthens.labels<-Array.appends.labels(Array.make(s.nlabels+16)(-1));s.nlabels<-s.nlabels+1;s.nlabels-1letplace(s:state)(l:int):unit=s.labels.(l)<-s.n(* names *)letlookup(s:state)(n:string):entry=matchList.find_map(List.assoc_optn)s.scopeswith|Somee->e|None->(* where the name is, read already or not *)lett=ifs.last.kind=Namenthens.lastelsetoksinraise(Failed{line=t.line;col=t.col;message="Unknown identifier "^n})letdeclare(s:state)(n:string)(e:entry):unit=matchs.scopeswith|scope::outer->ifList.mem_assocnscopethenfails("Duplicate identifier "^n);s.scopes<-((n,e)::scope)::outer|[]->assertfalse(* a word (or more) of the current frame, for a variable or a
compiler's temporary: the for loop's limit, the case's selector *)letallocate(s:state)(t:ty):int=leto=s.frameins.frame<-s.frame+sizet;o(* [at]: where the value checked starts, where the error is shown *)letcheck_type?at(s:state)~(expected:ty)(got:ty):unit=letok=match(baseexpected,basegot)with|(Integer|Boolean|Character),_->baseexpected=basegot|e,g->e=ginifnotokthenlett=Option.valueat~default:(toks)inraise(Failed{line=t.line;col=t.col;message=Printf.sprintf"Type mismatch: %s expected, not %s"(type_nameexpected)(type_namegot)})(* a value about to be stored in a subrange: checked at run time *)letrange_check(s:state)(t:ty):unit=matchtwithSubrange(_,lo,hi)->emits(Chk(lo,hi))|_->()(*****************************************************************************)(* Constants and types *)(*****************************************************************************)letconstant(s:state):ty*int=letsign=ifis_symbols"-"then(advances;-1)else(ifis_symbols"+"thenadvances;1)inmatchkindswith|Intn->advances;(Integer,sign*n)|Charcwhensign=1->advances;(Character,Char.codec)|Namen->(matchlookupsnwith|Const(t,v)->advances;ifsign=-1&&baset<>Integerthenfails"Integer constant expected";(t,sign*v)|_->fails"Constant expected")|k->fails("Constant expected, not "^Pascal_lexer.showk)letrectype_(s:state):ty=matchkindswith|Keyword"array"->advances;expect_symbols"[";(* array[1..3, 1..4] of t is array[1..3] of array[1..4] of t *)letrecboundsacc=letb=index_typesinifis_symbols","then(advances;bounds(b::acc))elseList.rev(b::acc)inletbs=bounds[]inexpect_symbols"]";expect_keywords"of";letelt=type_sinList.fold_right(fun(lo,hi)t->Array(lo,hi,t))bselt|Keyword"record"->advances;letrecfieldsoffsetacc=ifis_keywords"end"thenList.revaccelseletnames=name_listsinexpect_symbols":";lett=type_sinletacc,offset=List.fold_left(fun(acc,offset)n->ifList.exists(fun(m,_,_)->m=n)accthenfails("Duplicate field "^n);((n,offset,t)::acc,offset+sizet))(acc,offset)namesinifis_symbols";"thenadvances;fieldsoffsetaccinletfs=fields0[]inexpect_keywords"end";Recordfs|Namenwhen(matchlookupsnwithType_->true|_->false)->(advances;matchlookupsnwithTypet->t|_->assertfalse)|_->(* a subrange: lo..hi *)lett,lo=constantsinexpect_symbols"..";lett',hi=constantsincheck_types~expected:tt';ifhi<lothenfails"Lower bound greater than upper bound";Subrange(baset,lo,hi)(* an array's index: a subrange, written or named, or char, or boolean *)andindex_type(s:state):int*int=matchtype_swith|Subrange(_,lo,hi)->(lo,hi)|Character->(0,255)|Boolean->(0,1)|_->fails"an array's index must be a subrange (1..10)"andname_list(s:state):stringlist=letfirst=namesinifis_symbols","then(advances;first::name_lists)else[first](*****************************************************************************)(* Variables: where they are *)(*****************************************************************************)(* A designator (a, a[i], r.f) is either a variable the instructions
reach directly, lod d,o and str d,o, or an address computed on the
stack, for ind and sto *)typeplace=Directofint*int|Addressletto_address(s:state)(p:place):unit=matchpwithDirect(d,o)->emits(Lda(d,o))|Address->()(* the selectors after a variable's name: [i, j] and .f *)letrecselectors(s:state)(t:ty)(p:place):ty*place=ifis_symbols"["thenbeginadvances;letrecindicest=matchtwith|Array(lo,hi,elt)->ifnot(scalar(expressions))thenfails"An index is an integer, a character or a boolean";emits(Chk(lo,hi));iflo<>0thenemits(Inc(-lo));emits(Ixa(sizeelt));ifis_symbols","then(advances;indiceselt)elseelt|_->fails"Array expected"into_addresssp;letelt=indicestinexpect_symbols"]";selectorsseltAddressendelseifis_symbols"."thenbeginadvances;matchtwith|Recordfields->(letf=namesinmatchList.find_opt(fun(n,_,_)->n=f)fieldswith|Some(_,offset,ft)->to_addresssp;ifoffset<>0thenemits(Incoffset);selectorssftAddress|None->fails("Unknown field "^f))|_->fails"Record expected"endelse(t,p)(* a variable named [n], already read: its type and place *)andvariable(s:state)(n:string):ty*place=matchlookupsnwith|Varv->letd=s.level-v.vlevelinifv.var_paramthenbegin(* a var parameter holds the address of the variable given *)emits(Lod(d,v.offset));selectorssv.vtyAddressendelseselectorssv.vty(Direct(d,v.offset))|_->fails"Variable identifier expected"(* the value of a variable, on the stack *)andload(s:state)(t:ty)(p:place):unit=match(p,sizet)with|Direct(d,o),1->emits(Lod(d,o))|Address,1->emits(Ind0)|p,n->to_addresssp;emits(Ldmn)(*****************************************************************************)(* Expressions *)(*****************************************************************************)(* expression = simple [relation simple] *)andexpression(s:state):ty=lett=simplesinletrelation=function|Symbol"="->SomeEqu|Symbol"<>"->SomeNeq|Symbol"<"->SomeLes|Symbol"<="->SomeLeq|Symbol">"->SomeGrt|Symbol">="->SomeGeq|_->Noneinmatchrelation(kinds)with|Someop->advances;letat=toksinlett'=simplesinifnot(scalart)thenfails"Only integers, characters and booleans can be compared";check_type~ats~expected:tt';emitsop;Boolean|None->t(* simple = [+|-] term {(+ | - | or) term} *)andsimple(s:state):ty=letnegate=is_symbols"-"inifnegate||is_symbols"+"thenadvances;lett=termsinifnegatethenbegincheck_types~expected:Integert;emitsNgiend;letrecmoret=letop=matchkindswithSymbol"+"->Some(Adi,Integer)|Symbol"-"->Some(Sbi,Integer)|Keyword"or"->Some(Ior,Boolean)|_->Noneinmatchopwith|Some(i,operand)->check_types~expected:operandt;advances;(letat=toksincheck_type~ats~expected:operand(terms));emitsi;moreoperand|None->tinmoret(* term = factor {(times | div | mod | and) factor} *)andterm(s:state):ty=lett=factorsinletrecmoret=letop=matchkindswithSymbol"*"->Some(Mpi,Integer)|Keyword"div"->Some(Dvi,Integer)|Keyword"mod"->Some(Mod,Integer)|Keyword"and"->Some(And,Boolean)|Symbol"/"->fails"No reals in this Pascal: div divides integers"|_->Noneinmatchopwith|Some(i,operand)->check_types~expected:operandt;advances;(letat=toksincheck_type~ats~expected:operand(factors));emitsi;moreoperand|None->tinmoretandfactor(s:state):ty=matchkindswith|Intn->advances;emits(Ldcn);Integer|Charc->advances;emits(Ldc(Char.codec));Character|String_->fails"A string can only be written (write)"|Symbol"("->advances;lett=expressionsinexpect_symbols")";t|Keyword"not"->advances;(letat=toksincheck_type~ats~expected:Boolean(factors));emitsNot;Boolean|Namen->(advances;matchlookupsnwith|Const(t,v)->emits(Ldcv);t|Const_text_->fails"A string can only be written (write)"|Var_->lett,p=variablesninloadstp;t|Proc({result=Somet;_}asp)->callsnp;t|Proc_->fails(n^" is a procedure: it has no value")|Standardf->standard_functionsf|Type_->fails"Error in expression")|k->fails("Error in expression: "^Pascal_lexer.showk)(* a call: the mark (its static link [d] levels out), the arguments,
then cup *)andcall(s:state)(n:string)(p:proc):unit=emits(Mst(s.level-(p.plevel-1)));letwords=ref0inletargs=ifis_symbols"("then(advances;true)elsefalseinList.iteri(funi(param:param)->ifi>0thenexpect_symbols",";ifnotargsthenfails(Printf.sprintf"%s needs %d arguments"n(List.lengthp.params));ifparam.by_refthenbegin(* var: the variable's address, and exactly its type *)lett,pl=variables(names)inift<>param.ptythenfails(Printf.sprintf"Type mismatch: a var parameter must be %s"(type_nameparam.pty));to_addressspl;incrwordsendelsebegin(letat=toksincheck_type~ats~expected:param.pty(expressions));range_checksparam.pty;words:=!words+sizeparam.ptyend)p.params;ifargsthenbeginifp.params=[]thenfails(n^" takes no arguments");expect_symbols")"end;emits(Cup(!words,p.label))andone_argument(s:state):ty=expect_symbols"(";lett=expressionsinexpect_symbols")";tandstandard_function(s:state)(f:string):ty=matchfwith|"abs"|"sqr"->(letat=toksincheck_type~ats~expected:Integer(one_arguments));emits(iff="abs"thenAbielseSqi);Integer|"odd"->(letat=toksincheck_type~ats~expected:Integer(one_arguments));emitsOdd;Boolean|"ord"->lett=one_argumentsinifnot(scalart)thenfails"ord of an integer, a character or a boolean";Integer|"chr"->(letat=toksincheck_type~ats~expected:Integer(one_arguments));emits(Chk(0,255));Character|"succ"|"pred"->lett=one_argumentsinifnot(scalart)thenfails(f^" of an integer, a character or a boolean");emits(Inc(iff="succ"then1else-1));baset|"random"->(letat=toksincheck_type~ats~expected:Integer(one_arguments));emits(CspRnd);Integer|"eoln"->emits(CspEol);Boolean|_->fails(f^" is a procedure: it has no value")(*****************************************************************************)(* Statements *)(*****************************************************************************)(* where a statement's code begins, and its line: where the debugger's
steps stop *)letmark(s:state)(line:int):unit=s.marks<-(s.n,line)::s.marksletrecvtype(t:ty):Pcode.vtype=matchtwith|Integer|Text->Vint|Boolean->Vbool|Character->Vchar|Subrange(b,_,_)->vtypeb|Array(lo,hi,elt)->Varray(lo,hi,vtypeelt)|Recordfields->Vrecord(List.map(fun(n,o,t)->(n,o,vtypet))fields)letrecstatement(s:state):unit=(matchkindswith|Name_|Keyword("if"|"while"|"repeat"|"for"|"case")->marks(toks).line|_->());matchkindswith|Namen->(advances;matchlookupsnwith|Var_->assignmentsn|Proc({result=Somet;_}asp)whenis_symbols":="->(* a function's name assigned: its result, in its frame's
first word *)ifnot(List.memqps.functions)thenfails("Cannot assign to function "^n^" outside of it");advances;(letat=toksincheck_type~ats~expected:t(expressions));range_checkst;emits(Str(s.level-p.plevel,0))|Proc({result=None;_}asp)->callsnp|Proc_->fails(n^" is a function: its value must be used")|Standardf->standard_proceduresf|_->fails("Statement expected, not "^n))|Keyword"begin"->compounds|Keyword"if"->advances;(letat=toksincheck_type~ats~expected:Boolean(expressions));expect_keywords"then";letotherwise=new_labelsinemits(Fjpotherwise);statements;ifis_keywords"else"thenbeginadvances;letfinish=new_labelsinemits(Ujpfinish);placesotherwise;statements;placesfinishendelseplacesotherwise|Keyword"while"->advances;lettop=new_labelsandfinish=new_labelsinplacestop;(letat=toksincheck_type~ats~expected:Boolean(expressions));expect_keywords"do";emits(Fjpfinish);statements;emits(Ujptop);placesfinish|Keyword"repeat"->advances;lettop=new_labelsinplacestop;statementss;expect_keywords"until";(letat=toksincheck_type~ats~expected:Boolean(expressions));emits(Fjptop)|Keyword"for"->for_loops|Keyword"case"->cases|_->()(* the empty statement: begin end, or a ; too many *)andstatements(s:state):unit=statements;ifis_symbols";"thenbeginadvances;statementssendandcompound(s:state):unit=expect_keywords"begin";statementss;expect_keywords"end"andassignment(s:state)(n:string):unit=lett,p=variablesninexpect_symbols":=";ifsizet=1thenbeginletat=toksinlett'=expressionsincheck_type~ats~expected:tt';range_checkst;matchpwithDirect(d,o)->emits(Str(d,o))|Address->emitsStoendelsebegin(* an array or a record: its words, all of them *)to_addresssp;(letat=toksincheck_type~ats~expected:t(expressions));emits(Stm(sizet))end(* for v := a to b do s: the limit kept in a temporary, computed once *)andfor_loop(s:state):unit=advances;letv=namesinlett,p=variablesvinletd,o=matchpwithDirect(d,o)whenscalart->(d,o)|_->fails"A for loop's variable must be a simple variable"inexpect_symbols":=";(letat=toksincheck_type~ats~expected:t(expressions));range_checkst;emits(Str(d,o));letup=ifis_keywords"to"thentrueelseifis_keywords"downto"thenfalseelsefails"to or downto expected"inadvances;letlimit=allocatesIntegerin(letat=toksincheck_type~ats~expected:t(expressions));emits(Str(0,limit));expect_keywords"do";lettop=new_labelsandfinish=new_labelsinplacestop;emits(Lod(d,o));emits(Lod(0,limit));emits(ifupthenLeqelseGeq);emits(Fjpfinish);statements;emits(Lod(d,o));emits(Inc(ifupthen1else-1));emits(Str(d,o));emits(Ujptop);placesfinish(* case e of 1, 2: s; 3: s' else s'' end: the selector kept in a
temporary, compared with each label in turn (Pascal-P jumped
through a table, xjp: an exercise) *)andcase(s:state):unit=advances;lett=expressionsinifnot(scalart)thenfails"case of an integer, a character or a boolean";letselector=allocatesIntegerinemits(Str(0,selector));expect_keywords"of";letfinish=new_labelsinletrecarms()=ifis_keywords"end"then()elseifis_keywords"else"thenbeginadvances;statementssendelsebeginletreclabelsfirst=letlt,v=constantsincheck_types~expected:tlt;emits(Lod(0,selector));emits(Ldcv);emitsEqu;ifnotfirstthenemitsIor;ifis_symbols","then(advances;labelsfalse)inlabelstrue;expect_symbols":";letnext=new_labelsinemits(Fjpnext);statements;emits(Ujpfinish);placesnext;ifis_symbols";"thenadvances;arms()endinarms();expect_keywords"end";placesfinishandstandard_procedure(s:state)(f:string):unit=matchfwith|"write"|"writeln"->ifis_symbols"("thenbeginadvances;letrecargsfirst=ifnotfirstthenexpect_symbols",";write_arguments;ifnot(is_symbols")")thenargsfalseinargstrue;expect_symbols")"end;iff="writeln"thenemits(CspWln)|"read"|"readln"->ifis_symbols"("thenbeginadvances;letrecargsfirst=ifnotfirstthenexpect_symbols",";lett,p=variables(names)into_addresssp;(matchbasetwith|Integer->emits(CspRdi)|Character->emits(CspRdc)|_->fails"Only integers and characters can be read");ifnot(is_symbols")")thenargsfalseinargstrue;expect_symbols")"end;iff="readln"thenemits(CspRln)|_->fails(f^" is a function: its value must be used")(* write's argument: a value, and after a colon the width it is
right-aligned in *)andwrite_argument(s:state):unit=letwidth()=ifis_symbols":"then(advances;(letat=toksincheck_type~ats~expected:Integer(expressions)))elseemits(Ldc0)inlettextt=width();emits(Csp(Wrst))inmatchkindswith|Stringt->advances;textt|Namenwhen(matchlookupsnwithConst_text_->true|_->false)->(advances;matchlookupsnwithConst_textt->textt|_->assertfalse)|_->(lett=expressionsinwidth();matchbasetwith|Integer->emits(CspWri)|Character->emits(CspWrc)|Boolean->emits(CspWrb)|_->fails"Only integers, characters, booleans and strings can be written")(*****************************************************************************)(* Blocks *)(*****************************************************************************)(* the declarations, then the body at the label [entry]: its frame's
size known only at the end, ent patched then *)letrecblock(s:state)~(entry:int)~(id:int)~(parent:int)~(pname:string)~(params:stringlist)~(finish:instr):unit=ifis_keywords"const"thenbeginadvances;letrecconsts()=matchkindswith|Namen->advances;expect_symbols"=";(matchkindswith|Stringt->advances;declaresn(Const_textt)|_->lett,v=constantsindeclaresn(Const(t,v)));expect_symbols";";consts()|_->()inconsts()end;ifis_keywords"type"thenbeginadvances;letrectypes()=matchkindswith|Namen->advances;expect_symbols"=";declaresn(Type(type_s));expect_symbols";";types()|_->()intypes()end;ifis_keywords"var"thenbeginadvances;letrecvars()=matchkindswith|Name_->letnames=name_listsinexpect_symbols":";lett=type_sinList.iter(funn->declaresn(Var{vty=t;vlevel=s.level;offset=allocatest;var_param=false}))names;expect_symbols";";vars()|_->()invars()end;whileis_keywords"procedure"||is_keywords"function"doproceduresdone;placesentry;letent=s.nin(* the debugger stops at the begin, and at the end *)marks(toks).line;emits(Ent0);compounds;s.code.(ent)<-Ents.frame;markss.last.line;emitsfinish;letvariables=List.rev(List.filter_map(fun(vname,e)->matchewith|Varv->Some{Pcode.vname;offset=v.offset;vtype=vtypev.vty;by_ref=v.var_param;param=List.memvnameparams}|_->None)(List.hds.scopes))inletinfo={Pcode.pname;level=s.level;parent;first=ent;last=s.n-1;variables}ins.procedures<-(id,info)::s.procedures(* procedure p(a: integer; var b: t); block; -- or forward; *)andprocedure(s:state):unit=letis_function=is_keywords"function"inadvances;letat=((toks).line,(toks).col)inletn=namesinletearlier=matchList.assoc_optn(List.hds.scopes)withSome(Procp)whennotp.defined->Somep|_->Noneinletp=matchearlierwith|Somep->p(* its heading was given before, with forward *)|None->letparams=ifis_symbols"("thenbeginadvances;letrecgroups()=letby_ref=is_keywords"var"inifby_refthenadvances;letnames=name_listsinexpect_symbols":";lett=matchkindswithNametn->(matchlookupstnwithTypet->advances;t|_->fails"Type identifier expected")|_->fails"Type identifier expected"inletps=List.map(funpname->{pname;pty=t;by_ref})namesinifis_symbols";"then(advances;ps@groups())elsepsinletps=groups()inexpect_symbols")";psendelse[]inletresult=ifis_functionthenbeginexpect_symbols":";matchkindswith|Nametn->(matchlookupstnwith|Typetwhenscalart->advances;Somet|_->fails"A function returns an integer, a character or a boolean")|_->fails"Type identifier expected"endelseNoneinletp={plevel=s.level+1;label=new_labels;params;result;defined=false;at}indeclaresn(Procp);pinexpect_symbols";";ifis_keywords"forward"thenbeginifearlier<>Nonethenfails("Duplicate forward "^n);advances;expect_symbols";"endelsebeginp.defined<-true;(* the body: a scope of its own, one level in, the parameters after
the mark *)letsaved_frame=s.frameandsaved_functions=s.functionsins.level<-s.level+1;s.frame<-Pcode.mark;s.scopes<-[]::s.scopes;ifis_functionthens.functions<-p::s.functions;List.iter(fun(param:param)->letoffset=allocates(ifparam.by_refthenIntegerelseparam.pty)indeclaresparam.pname(Var{vty=param.pty;vlevel=s.level;offset;var_param=param.by_ref}))p.params;letid=s.nproceduresins.nprocedures<-id+1;letparent=s.currentins.current<-id;blocks~entry:p.label~id~parent~pname:n~params:(List.map(fun(q:param)->q.pname)p.params)~finish:(ifis_functionthenRetfelseRetp);s.current<-parent;s.scopes<-List.tls.scopes;s.level<-s.level-1;s.frame<-saved_frame;s.functions<-saved_functions;expect_symbols";"endletstandard_names=[("integer",TypeInteger);("boolean",TypeBoolean);("char",TypeCharacter);("true",Const(Boolean,1));("false",Const(Boolean,0));("maxint",Const(Integer,32767))]@List.map(funf->(f,Standardf))["write";"writeln";"read";"readln";"abs";"sqr";"odd";"ord";"chr";"succ";"pred";"random";"eoln"](* program name (input, output); block. *)letprogram(s:state):Pcode.program=expect_keywords"program";letpname=namesinifis_symbols"("thenbeginadvances;ignore(name_lists);expect_symbols")"end;expect_symbols";";letmain=new_labelsinemits(Ujpmain);s.scopes<-[]::s.scopes;s.nprocedures<-1;blocks~entry:main~id:0~parent:(-1)~pname~params:[]~finish:Stp;expect_symbols".";(* forward procedures never given a body *)List.iter(funscope->List.iter(fun(n,e)->matchewith|Proc{defined=false;at=line,col;_}->raise(Failed{line;col;message=n^" was declared forward, but its body never came"})|_->())scope)s.scopes;(* the labels replaced by their addresses *)letatl=s.labels.(l)inletcode=Array.map(functionUjpl->Ujp(atl)|Fjpl->Fjp(atl)|Cup(n,l)->Cup(n,atl)|i->i)(Array.subs.code0s.n)inletstatements=Array.makes.n(-1)in(* the first mark of an address wins: a statement's, not those of
the statements nested in it that start at the same place *)List.iter(fun(a,l)->ifa<s.nthenstatements.(a)<-l)s.marks;letprocedures=Array.of_list(List.mapsnd(List.sortcompares.procedures))in{code;lines=Array.subs.lines0s.n;statements;procedures}letcompile(text:string):(Pcode.program,error)result=matchPascal_lexer.tokenstextwith|exceptionPascal_lexer.Error(line,col,message)->Error{line;col;message}|toks->(lets={toks=Array.of_listtoks;pos=0;last=List.hdtoks;code=Array.make64Stp;lines=Array.make640;n=0;labels=Array.make16(-1);nlabels=0;scopes=[standard_names];level=0;frame=Pcode.mark;functions=[];marks=[];procedures=[];nprocedures=0;current=0}inmatchprogramswithp->Okp|exceptionFailede->Errore)