| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988 |
- IMPLEMENTATION MODULE Compiler ;
- (* Turbo Pascal 3-style single-pass Pascal -> 8086 compiler in
- GNU Modula-2 (-fiso), following the structure of the original
- TPSRC6 'turbo' entry / TPSRC7-10:
- Inittur reset state, pre-defined types, scratch temporaries
- Skip lexer: blanks, comments { } and (* *), directives {$ }
- GetWord/WddTok/MatchKey word lexing with a keyword table (kName/kTk)
- PeekKw lookahead keyword check WITHOUT consuming (via saved
- srcPos) - needed because declarations and compound
- statements peek at END/ELSE/etc
- RdIntConst/RdConst integer, hex and char constants
- Search symbol table lookup filtered by lexical level
- ParseExpr -> ParseCmp -> ParseAdd -> ParseMul -> ParseNeg
- -> ParseAtom precedence climb (TPSRC9)
- Statmnt statements: if/while/repeat/for/case/goto/exit/begin
- assignment and calls (TPSRC8)
- ParseType/Decls ARRAY, STRING, scalar/subrange types; variable,
- constant, label and procedure/function definitions
- Compile driver: optional PROGRAM header, DefPart, progpart,
- final '.', header size patch, patch resolution.
- Ebyte/Eword/Ecall/Ejmp + patch list code emission (TPSRC10).
- The emitted image is a byte array (mode word, CS/DS, size words,
- CALL initmem, MOV BP,SP, then generated code). Forward labels and
- forward procedure calls resolve through a patch list (ptc records).
- Working subset (v0.4): integer/char/boolean/byte scalars, constants
- with folding, globals, locals, value parameters, procedures and
- scalar-result functions, ARRAY[const..const] with constant indexing,
- control flow, GOTO/EXIT, and the standard procedures WRITE, WRITELN,
- READ, READLN, HALT (DefBuiltins + IoCall).
- The standard procedures are dispatched per argument, as the original
- does (TPSRC8 pwriteln/pwrloop inspects each argument's class and emits
- a different call per type), so the runtime is handed a value and never
- a descriptor.
- Still not implemented: real/set/record/file and the string runtime
- raise Err (ENoLib) - the original's "not implemented" path. The
- runtime blob itself, the linker that rebases the TU_* entry offsets
- by the runtime's size, and CmdRun (the interpreter) are still pending,
- so a compiled image cannot be executed yet. *)
- FROM TextBuf IMPORT Length, CharAt ;
- FROM SYSTEM IMPORT BYTE ;
- (* ---------------------------------------------------------------- *)
- (* constants *)
- (* ---------------------------------------------------------------- *)
- CONST
- MaxLine = 128 ;
- MaxName = 31 ;
- MaxCode = 24000 ;
- MaxSym = 3000 ;
- MaxPatch = 2000 ;
- MaxPend = 400 ;
- (* type codes (TP3 vartp) *)
- TNone = 0 ; TArray = 1 ; TRecord = 2 ; TSet = 3 ;
- TPtr = 4 ; TFile = 5 ; TText = 6 ; TUntyp = 7 ;
- TString = 8 ; TReal = 9 ; TScalar = 10 ; TBool = 11 ;
- (* symbol kinds *)
- KLabel = 100H ; KConst = 200H ; KType = 300H ;
- KVar = 400H ; KProc = 500H ; KFunc = 600H ;
- KBuiltin = 700H ; (* standard procedure, see BI_* below *)
- (* which standard procedure a KBuiltin symbol denotes *)
- BI_Write = 0 ; BI_WriteLn = 1 ; BI_Read = 2 ;
- BI_ReadLn = 3 ; BI_Halt = 4 ;
- (* keyword tokens *)
- TkNone = 0 ; TkProgram = 1 ; TkBegin = 2 ; TkEnd = 3 ;
- TkIf = 4 ; TkThen = 5 ; TkElse = 6 ; TkWhile = 7 ;
- TkDo = 8 ; TkRepeat = 9 ; TkUntil = 10 ; TkFor = 11 ;
- TkTo = 12 ; TkDownto = 13 ; TkCase = 14 ; TkOf = 15 ;
- TkGoto = 16 ; TkExit = 17 ; TkWith = 18 ; TkVar = 19 ;
- TkConst= 20 ; TkType = 21 ; TkLabel = 22 ; TkProcedure = 23 ;
- TkFunction = 24 ; TkNil = 25 ; TkAnd = 26 ; TkOr = 27 ;
- TkNot = 28 ; TkDiv = 29 ; TkMod = 30 ; TkIn = 31 ;
- TkFile = 32 ; TkText = 33 ; TkRecord = 34 ; TkArray = 35 ;
- TkSet = 36 ; TkPacked = 37 ; TkForward = 38 ; TkExternal = 39 ;
- TkAbsolute = 40 ; TkOverlay = 41 ; TkString = 42 ;
- (* Runtime entry offsets in the emitted image.
- Standard-procedure entries: TP3 does NOT pass a descriptor -
- TPSRC8 pwriteln/pwrloop inspects each argument's class in CL and emits
- a *different* call per type, so the type is fixed at compile time and
- the runtime needs only the value. Mirrored here.
- These offsets are still PLACEHOLDERS on a fixed ladder. The real
- offsets are known - Runtime.RT_Entry derives them from the assembled
- blob (wrtinl is really at 8EH) - but the compiler does not copy the
- runtime into its code buffer yet, so it cannot call it, and using the
- true offsets here would only look like it works. They all get replaced
- by RT_Entry in one go when the runtime is wired in. *)
- TU_InitMem = 8H ;
- TU_ProgEnd = 10H ;
- TU_StackChk = 18H ;
- TU_WrInt = 20H ; TU_WrChar = 28H ; TU_WrBool = 30H ;
- TU_WrReal = 38H ; TU_WrLn = 40H ;
- TU_RdInt = 48H ; TU_RdChar = 50H ; TU_RdBool = 58H ;
- TU_RdLn = 60H ; TU_Halt = 68H ;
- TU_WrInl = 70H ; (* inline string literal; takes NO stack argument *)
- (* TP3 error numbers *)
- ENoSemi = 1 ; EPointExp = 10 ; ESimpType = 30 ;
- EUnknown = 41 ; EConstRange = 45 ; EMemOvf = 98 ;
- ECompOvf = 99 ; ENoLib = 102 ; ETypeErr = 56 ;
- AposC = 39 ; (* ORD ("'") - avoids quote-in-quote *)
- (* ---------------------------------------------------------------- *)
- (* types *)
- (* ---------------------------------------------------------------- *)
- TYPE
- SymEntry =
- RECORD
- name : ARRAY [0..MaxName] OF CHAR ;
- tag : CARDINAL ;
- cls : CARDINAL ;
- size : CARDINAL ;
- elem : CARDINAL ;
- off : CARDINAL ;
- lval : LONGINT ;
- level : CARDINAL ;
- local : BOOLEAN ;
- resvar : CARDINAL ;
- goPos : CARDINAL ;
- defnd : BOOLEAN ;
- fwd : BOOLEAN ;
- END ;
- PatchRec =
- RECORD
- place : CARDINAL ;
- target : CARDINAL ;
- filled : BOOLEAN ;
- END ;
- PendRec =
- RECORD
- kind : CARDINAL ; (* 0 goto, 1 call *)
- who : CARDINAL ;
- place : CARDINAL ; (* patch slot index *)
- END ;
- ERes =
- RECORD
- cls : CARDINAL ;
- kind : CARDINAL ; (* 0 const, 1 var, 2 value in AX,
- 3 string literal (cls = TString) *)
- imm : LONGINT ;
- idx : CARDINAL ;
- boff : CARDINAL ; (* constant fold-in for subscripts *)
- chr : BOOLEAN ; (* single-quoted literal, e.g. 'a' *)
- strx : CARDINAL ; (* string literal: index into strPool *)
- END ;
- DirRec = RECORD rng, chk : BOOLEAN END ;
- (* ---------------------------------------------------------------- *)
- (* state *)
- (* ---------------------------------------------------------------- *)
- VAR
- srcPos, srcLen : CARDINAL ;
- wrd : ARRAY [0..MaxName] OF CHAR ;
- (* String-literal pool.
- TP3 does not put a literal in the data segment at all: it emits the
- literal *inline in the code stream* as <length byte><characters>, and
- the runtime entry "wrtinl" reads the length from the return address and
- returns to just past the last character (TPSRC4 xwrtinl, TPSRC10
- estring). So nothing here ends up in the image as data - the pool only
- has to survive from the moment the literal is scanned until IoCall
- decides to emit it, because by then the parser has moved on. *)
- strPool : ARRAY [0..4095] OF CHAR ;
- strOff : ARRAY [0..255] OF CARDINAL ;
- strLen : ARRAY [0..255] OF CARDINAL ;
- strTop : CARDINAL ; (* next free byte in strPool *)
- strCnt : CARDINAL ; (* literals collected so far *)
- rdStrX : CARDINAL ; (* pool index of the literal RdConst just read *)
- symtab : ARRAY [0..MaxSym - 1] OF SymEntry ;
- symTop : CARDINAL ;
- patches : ARRAY [0..MaxPatch - 1] OF PatchRec ;
- nPatch : CARDINAL ;
- pend : ARRAY [0..MaxPend - 1] OF PendRec ;
- nPend : CARDINAL ;
- exitPatch : ARRAY [0..63] OF CARDINAL ;
- exitCnt : CARDINAL ;
- brkSave : ARRAY [0..15] OF CARDINAL ;
- loopTy : ARRAY [0..15] OF CARDINAL ; (* 1 while/repeat, 2 for *)
- brkN : CARDINAL ;
- caseJmp : ARRAY [0..63] OF CARDINAL ;
- caseN : CARDINAL ;
- pc, dc : CARDINAL ;
- varspc : CARDINAL ;
- cbuf : ARRAY [0..MaxCode - 1] OF BYTE ;
- codeSz, dataSz : CARDINAL ;
- abortFac : BOOLEAN ;
- errNum : CARDINAL ; (* NOT "errNo": Compile's formal of that
- name would shadow it, and the caller's
- errNo would never be filled in *)
- txerrPos : CARDINAL ;
- lexnest : CARDINAL ;
- curIsFunc : BOOLEAN ;
- resultVar : CARDINAL ;
- locFree : CARDINAL ; (* next local slot (BP-relative, 8-bit) *)
- locBytes : CARDINAL ; (* frame size for SUB SP *)
- parmOff : CARDINAL ; (* next parameter slot (BP-relative) *)
- dirs : DirRec ;
- tmpA, tmpB : CARDINAL ; (* global scratch word addresses *)
- hdrFlag, hdrCS, hdrDS, hdrHeap, hdrMax : CARDINAL ;
- kName : ARRAY [0..42] OF ARRAY [0..15] OF CHAR ;
- kTk : ARRAY [0..42] OF CARDINAL ;
- (* ---------------------------------------------------------------- *)
- (* small char helpers *)
- (* ---------------------------------------------------------------- *)
- PROCEDURE CurCh () : CHAR ;
- BEGIN
- IF srcPos >= srcLen THEN
- RETURN 0C
- END ;
- RETURN CharAt (srcPos)
- END CurCh ;
- PROCEDURE GetCh () : CHAR ;
- VAR ch : CHAR ;
- BEGIN
- ch := CurCh () ;
- IF srcPos < srcLen THEN
- INC (srcPos)
- END ;
- RETURN ch
- END GetCh ;
- PROCEDURE PeekAhead (k : CARDINAL) : CHAR ;
- BEGIN
- IF srcPos + k >= srcLen THEN
- RETURN 0C
- END ;
- RETURN CharAt (srcPos + k)
- END PeekAhead ;
- PROCEDURE Digit (ch : CHAR) : BOOLEAN ;
- BEGIN
- RETURN (ch >= '0') AND (ch <= '9')
- END Digit ;
- PROCEDURE Alpha (ch : CHAR) : BOOLEAN ;
- BEGIN
- RETURN ((ch >= 'A') AND (ch <= 'Z')) OR ((ch >= 'a') AND (ch <= 'z'))
- OR (ch = '_')
- END Alpha ;
- PROCEDURE AlphaNum (ch : CHAR) : BOOLEAN ;
- BEGIN
- RETURN (Alpha (ch)) OR (Digit (ch))
- END AlphaNum ;
- PROCEDURE IsHexCh (ch : CHAR) : BOOLEAN ;
- BEGIN
- RETURN (Digit (ch)) OR ((ch >= 'A') AND (ch <= 'F'))
- OR ((ch >= 'a') AND (ch <= 'f'))
- END IsHexCh ;
- PROCEDURE Upper (ch : CHAR) : CHAR ;
- BEGIN
- IF (ch >= 'a') AND (ch <= 'z') THEN
- RETURN CHR (ORD (ch) - ORD ('a') + ORD ('A'))
- END ;
- RETURN ch
- END Upper ;
- PROCEDURE W16 (x : LONGINT) : CARDINAL ;
- (* fold x modulo 10000H, handling negatives (no negative MOD) *)
- VAR m : CARDINAL ;
- BEGIN
- IF x >= 0 THEN
- RETURN VAL (CARDINAL, x MOD 10000H)
- END ;
- m := VAL (CARDINAL, (0 - x) MOD 10000H) ;
- RETURN (10000H - m) MOD 10000H
- END W16 ;
- PROCEDURE DropCh (v : CHAR) ;
- BEGIN
- END DropCh ;
- PROCEDURE DropB (v : BOOLEAN) ;
- BEGIN
- END DropB ;
- PROCEDURE DropC (v : CARDINAL) ;
- BEGIN
- END DropC ;
- PROCEDURE BitAnd (a, b : LONGINT) : LONGINT ;
- BEGIN
- RETURN VAL (LONGINT, CARDINAL (VAL (BITSET, W16 (a))
- * VAL (BITSET, W16 (b))))
- END BitAnd ;
- PROCEDURE BitOr (a, b : LONGINT) : LONGINT ;
- BEGIN
- RETURN VAL (LONGINT, CARDINAL (VAL (BITSET, W16 (a))
- + VAL (BITSET, W16 (b))))
- END BitOr ;
- PROCEDURE BitNot (a : LONGINT) : LONGINT ;
- BEGIN
- RETURN VAL (LONGINT, CARDINAL (VAL (BITSET, 0FFFFH)
- - VAL (BITSET, W16 (a))))
- END BitNot ;
- (* ---------------------------------------------------------------- *)
- (* errors *)
- (* ---------------------------------------------------------------- *)
- PROCEDURE Err (n : CARDINAL) ;
- BEGIN
- IF NOT abortFac THEN
- abortFac := TRUE ;
- errNum := n ;
- txerrPos := srcPos
- END
- END Err ;
- PROCEDURE OK () : BOOLEAN ;
- BEGIN
- RETURN NOT abortFac
- END OK ;
- (* ---------------------------------------------------------------- *)
- (* emission : ebyte / eword / ecall / ejump *)
- (* ---------------------------------------------------------------- *)
- PROCEDURE Ebyte (b : BYTE) ;
- BEGIN
- IF pc >= MaxCode THEN
- Err (EMemOvf)
- ELSE
- cbuf [pc] := b ;
- INC (pc)
- END
- END Ebyte ;
- PROCEDURE Eword (w : CARDINAL) ;
- BEGIN
- Ebyte (VAL (BYTE, w MOD 100H)) ;
- Ebyte (VAL (BYTE, (w DIV 100H) MOD 100H))
- END Eword ;
- PROCEDURE AddPatch (place, target : CARDINAL) ;
- BEGIN
- IF nPatch < MaxPatch THEN
- patches [nPatch].place := place ;
- patches [nPatch].target := target ;
- patches [nPatch].filled := FALSE ;
- INC (nPatch)
- ELSE
- Err (ECompOvf)
- END
- END AddPatch ;
- PROCEDURE SetPatTgt (idx, t : CARDINAL) ;
- BEGIN
- IF idx < nPatch THEN
- patches [idx].target := t
- END
- END SetPatTgt ;
- PROCEDURE EmCall (target : CARDINAL) : CARDINAL ;
- (* E8 rel16 near call; target = 0 => forward (patched later).
- Returns the patch slot, or 0 when resolved directly. *)
- VAR rel, p : CARDINAL ;
- BEGIN
- Ebyte (0E8H) ;
- IF target = 0 THEN
- Eword (0) ;
- p := nPatch ;
- AddPatch (pc - 2, 0) ;
- RETURN p
- END ;
- (* rel16 is measured from the END of the instruction. Here pc already
- points past the opcode(s) and at the displacement field, so the
- instruction ends at pc+2 - the same convention ResolvePatches uses
- with "place + 2". Omitting the +2 lands every direct call/jump 2 bytes
- past its target. *)
- rel := (target + 10000H - (pc + 2)) MOD 10000H ;
- Eword (rel) ;
- RETURN 0
- END EmCall ;
- PROCEDURE EmJmpNear (target : CARDINAL) : CARDINAL ;
- (* E9 rel16; target = 0 => forward. Returns patch slot or 0. *)
- VAR rel, p : CARDINAL ;
- BEGIN
- Ebyte (0E9H) ;
- IF target = 0 THEN
- Eword (0) ;
- p := nPatch ;
- AddPatch (pc - 2, 0) ;
- RETURN p
- END ;
- rel := (target + 10000H - (pc + 2)) MOD 10000H ; (* see EmCall *)
- Eword (rel) ;
- RETURN 0
- END EmJmpNear ;
- PROCEDURE EmJcc (cc : BYTE ; target : CARDINAL) : CARDINAL ;
- (* 0F 8x rel16 near conditional; target = 0 => forward. *)
- VAR rel, p : CARDINAL ;
- BEGIN
- Ebyte (0FH) ;
- Ebyte (cc) ;
- IF target = 0 THEN
- Eword (0) ;
- p := nPatch ;
- AddPatch (pc - 2, 0) ;
- RETURN p
- END ;
- rel := (target + 10000H - (pc + 2)) MOD 10000H ; (* see EmCall *)
- Eword (rel) ;
- RETURN 0
- END EmJcc ;
- PROCEDURE ResolvePatches () ;
- VAR i : CARDINAL ;
- rel : CARDINAL ;
- BEGIN
- i := 0 ;
- WHILE i < nPatch DO
- IF NOT patches [i].filled THEN
- rel := (patches [i].target + 10000H - (patches [i].place + 2))
- MOD 10000H ;
- cbuf [patches [i].place] := VAL (BYTE, rel MOD 100H) ;
- cbuf [patches [i].place + 1] := VAL (BYTE, (rel DIV 100H) MOD 100H) ;
- patches [i].filled := TRUE
- END ;
- INC (i)
- END
- END ResolvePatches ;
- (* 1-byte const emitters *)
- PROCEDURE EmMovAxi (imm : CARDINAL) ;
- BEGIN
- Ebyte (0B8H) ; Eword (imm)
- END EmMovAxi ;
- PROCEDURE EmMovBpSp () ;
- BEGIN
- Ebyte (8BH) ; Ebyte (0ECH)
- END EmMovBpSp ;
- PROCEDURE EmMovAh0 () ;
- BEGIN
- Ebyte (0B4H) ; Ebyte (0H)
- END EmMovAh0 ;
- PROCEDURE EmMovAxSp () ;
- (* MOV AX,[SP]. Needs a SIB byte, and [SP] cannot be encoded with mod=00
- (that would compute BP+SP), so it is mod=01 / SIB=24h / disp8=0. Emitting
- just "8B 04" left the SIB slot unfilled, which silently ate the *next*
- instruction - the case-label CMP - and turned every case comparison into
- a load from a garbage address. *)
- BEGIN
- Ebyte (8BH) ; Ebyte (44H) ; Ebyte (24H) ; Ebyte (0)
- END EmMovAxSp ;
- PROCEDURE EmMovCxSp () ;
- BEGIN
- Ebyte (8BH) ; Ebyte (0CH)
- END EmMovCxSp ;
- PROCEDURE EmPushAx () ;
- BEGIN
- Ebyte (50H)
- END EmPushAx ;
- PROCEDURE EmPopCx () ;
- BEGIN
- Ebyte (59H)
- END EmPopCx ;
- PROCEDURE EmPopDx () ;
- BEGIN
- Ebyte (5AH)
- END EmPopDx ;
- PROCEDURE EmXchgAxCx () ;
- BEGIN
- Ebyte (93H)
- END EmXchgAxCx ;
- PROCEDURE EmXorAxAx () ;
- BEGIN
- Ebyte (33H) ; Ebyte (0C0H)
- END EmXorAxAx ;
- PROCEDURE EmAddAxCx () ;
- BEGIN
- Ebyte (3H) ; Ebyte (0C1H)
- END EmAddAxCx ;
- PROCEDURE EmSubAxCx () ;
- BEGIN
- Ebyte (2BH) ; Ebyte (0C1H)
- END EmSubAxCx ;
- PROCEDURE EmMulAxCx () ;
- BEGIN
- Ebyte (0F7H) ; Ebyte (0E9H)
- END EmMulAxCx ;
- PROCEDURE EmIDivAxCx () ;
- BEGIN
- Ebyte (99H) ; Ebyte (0F7H) ; Ebyte (0F9H)
- END EmIDivAxCx ;
- PROCEDURE EmAndAxCx () ;
- BEGIN
- Ebyte (23H) ; Ebyte (0C1H)
- END EmAndAxCx ;
- PROCEDURE EmOrAxCx () ;
- BEGIN
- Ebyte (0BH) ; Ebyte (0C1H)
- END EmOrAxCx ;
- PROCEDURE EmNegAx () ;
- BEGIN
- Ebyte (0F7H) ; Ebyte (0D8H)
- END EmNegAx ;
- PROCEDURE EmNotAx () ;
- BEGIN
- Ebyte (0F7H) ; Ebyte (0D0H)
- END EmNotAx ;
- PROCEDURE EmCmpAxCx () ;
- BEGIN
- Ebyte (3BH) ; Ebyte (0C1H)
- END EmCmpAxCx ;
- PROCEDURE EmCmpAxi (imm : CARDINAL) ;
- BEGIN
- Ebyte (03DH) ; Eword (imm)
- END EmCmpAxi ;
- PROCEDURE EmSetcc (cc : BYTE) ;
- BEGIN
- Ebyte (0FH) ; Ebyte (cc) ; Ebyte (0C0H) ;
- EmMovAh0 ()
- END EmSetcc ;
- PROCEDURE EmIncAx () ;
- BEGIN
- Ebyte (40H)
- END EmIncAx ;
- PROCEDURE EmDecAx () ;
- BEGIN
- Ebyte (48H)
- END EmDecAx ;
- PROCEDURE EmLoadVar (local : BOOLEAN ; off, nbytes : CARDINAL) ;
- VAR disp : CARDINAL ;
- BEGIN
- disp := off MOD 100H ;
- IF nbytes = 1 THEN
- IF local THEN
- Ebyte (8AH) ; Ebyte (46H) ; Ebyte (VAL (BYTE, disp))
- ELSE
- Ebyte (0A0H) ; Eword (off)
- END
- ELSE
- IF local THEN
- Ebyte (8BH) ; Ebyte (46H) ; Ebyte (VAL (BYTE, disp))
- ELSE
- Ebyte (0A1H) ; Eword (off)
- END
- END
- END EmLoadVar ;
- PROCEDURE EmStoreVar (local : BOOLEAN ; off, nbytes : CARDINAL) ;
- VAR disp : CARDINAL ;
- BEGIN
- disp := off MOD 100H ;
- IF nbytes = 1 THEN
- IF local THEN
- Ebyte (88H) ; Ebyte (46H) ; Ebyte (VAL (BYTE, disp))
- ELSE
- Ebyte (0A2H) ; Eword (off)
- END
- ELSE
- IF local THEN
- Ebyte (89H) ; Ebyte (46H) ; Ebyte (VAL (BYTE, disp))
- ELSE
- Ebyte (0A3H) ; Eword (off)
- END
- END
- END EmStoreVar ;
- PROCEDURE EmPushVarAddr (local : BOOLEAN ; off : CARDINAL) ;
- (* LEA AX,[BP+disp] / LEA AX,[off] then PUSH AX - READ passes the address of
- a variable, not its value. 8D 46 disp is LEA AX,[BP+disp] and 8D 06 off
- is LEA AX,[off] (mod=00 rm=110 = direct disp16), both 8086-legal. *)
- VAR disp : CARDINAL ;
- BEGIN
- disp := off MOD 100H ;
- IF local THEN
- Ebyte (8DH) ; Ebyte (46H) ; Ebyte (VAL (BYTE, disp))
- ELSE
- Ebyte (8DH) ; Ebyte (06H) ; Eword (off)
- END ;
- EmPushAx ()
- END EmPushVarAddr ;
- PROCEDURE EmSubSp (n : CARDINAL) ;
- BEGIN
- Ebyte (81H) ; Ebyte (0ECH) ; Eword (n MOD 10000H)
- END EmSubSp ;
- PROCEDURE EmAddSp (n : CARDINAL) ;
- BEGIN
- IF n <= 126 THEN
- Ebyte (83H) ; Ebyte (0C4H) ; Ebyte (VAL (BYTE, n))
- ELSE
- Ebyte (81H) ; Ebyte (0C4H) ; Eword (n MOD 10000H)
- END
- END EmAddSp ;
- PROCEDURE EmPushBp () ;
- BEGIN
- Ebyte (55H)
- END EmPushBp ;
- PROCEDURE EmLeave () ;
- BEGIN
- Ebyte (0C9H)
- END EmLeave ;
- PROCEDURE EmRet () ;
- BEGIN
- Ebyte (0C3H)
- END EmRet ;
- (* ---------------------------------------------------------------- *)
- (* symbol table *)
- (* ---------------------------------------------------------------- *)
- PROCEDURE NameEq (a : ARRAY OF CHAR ; b : ARRAY OF CHAR) : BOOLEAN ;
- VAR i : CARDINAL ;
- BEGIN
- i := 0 ;
- LOOP
- IF i > HIGH (a) THEN
- RETURN FALSE
- END ;
- IF i > HIGH (b) THEN
- RETURN FALSE
- END ;
- IF a [i] # b [i] THEN
- RETURN FALSE
- END ;
- IF a [i] = 0C THEN
- RETURN TRUE
- END ;
- INC (i)
- END
- END NameEq ;
- PROCEDURE CopyStr (VAR dst : ARRAY OF CHAR ; src : ARRAY OF CHAR) ;
- VAR i : CARDINAL ;
- BEGIN
- i := 0 ;
- LOOP
- IF i > HIGH (dst) THEN
- dst [HIGH (dst)] := 0C ;
- RETURN
- END ;
- IF i > HIGH (src) THEN
- dst [i] := 0C ;
- RETURN
- END ;
- dst [i] := src [i] ;
- IF src [i] = 0C THEN
- RETURN
- END ;
- INC (i)
- END
- END CopyStr ;
- PROCEDURE CopyWord (name : ARRAY OF CHAR) ;
- (* stash current word into global wrd (uppercased) *)
- VAR i : CARDINAL ;
- BEGIN
- i := 0 ;
- LOOP
- IF i > HIGH (name) THEN
- wrd [i] := 0C ;
- RETURN
- END ;
- IF i > MaxName THEN
- wrd [MaxName] := 0C ;
- RETURN
- END ;
- IF name [i] = 0C THEN
- wrd [i] := 0C ;
- RETURN
- END ;
- wrd [i] := Upper (name [i]) ;
- INC (i)
- END
- END CopyWord ;
- PROCEDURE SaveWord (VAR dst : ARRAY OF CHAR) ;
- (* copy wrd into dst *)
- VAR i : CARDINAL ;
- BEGIN
- i := 0 ;
- LOOP
- IF i > HIGH (dst) THEN
- dst [HIGH (dst)] := 0C ;
- RETURN
- END ;
- IF i > MaxName THEN
- dst [MaxName] := 0C ;
- RETURN
- END ;
- dst [i] := wrd [i] ;
- IF wrd [i] = 0C THEN
- RETURN
- END ;
- INC (i)
- END
- END SaveWord ;
- PROCEDURE NumToName (n : CARDINAL ; VAR dst : ARRAY OF CHAR) ;
- VAR buf : ARRAY [0..9] OF CHAR ;
- i, j : CARDINAL ;
- BEGIN
- IF n = 0 THEN
- dst [0] := '0' ;
- dst [1] := 0C ;
- RETURN
- END ;
- i := 0 ;
- WHILE n > 0 DO
- IF i <= 9 THEN
- buf [i] := CHR (ORD ('0') + (n MOD 10)) ;
- INC (i)
- END ;
- n := n DIV 10
- END ;
- j := 0 ;
- WHILE i > 0 DO
- DEC (i) ;
- IF j <= HIGH (dst) THEN
- dst [j] := buf [i] ;
- INC (j)
- END
- END ;
- IF j <= HIGH (dst) THEN
- dst [j] := 0C
- END
- END NumToName ;
- PROCEDURE NewSym (name : ARRAY OF CHAR ; tag : CARDINAL ;
- cls, size, elem, off : CARDINAL ; v : LONGINT ;
- local : BOOLEAN) : CARDINAL ;
- VAR e : SymEntry ;
- BEGIN
- IF symTop >= MaxSym THEN
- Err (ECompOvf) ;
- RETURN 0
- END ;
- CopyWord (name) ;
- CopyStr (e.name, wrd) ;
- e.tag := tag ;
- e.cls := cls ;
- e.size := size ;
- e.elem := elem ;
- e.off := off ;
- e.lval := v ;
- e.level := lexnest ;
- e.local := local ;
- e.resvar := 0 ;
- e.goPos := 0 ;
- e.defnd := FALSE ;
- e.fwd := FALSE ;
- symtab [symTop] := e ;
- INC (symTop) ;
- RETURN symTop - 1
- END NewSym ;
- PROCEDURE Search (nm : ARRAY OF CHAR ; VAR idx : CARDINAL) : BOOLEAN ;
- (* find nm among symbols visible at the current lexical level *)
- VAR p : CARDINAL ;
- BEGIN
- CopyWord (nm) ;
- p := symTop ;
- WHILE p > 0 DO
- DEC (p) ;
- IF symtab [p].level <= lexnest THEN
- IF NameEq (symtab [p].name, wrd) THEN
- idx := p ;
- RETURN TRUE
- END
- END
- END ;
- RETURN FALSE
- END Search ;
- PROCEDURE DupTest (nm : ARRAY OF CHAR) ;
- VAR i : CARDINAL ;
- BEGIN
- IF Search (nm, i) THEN
- Err (EUnknown)
- END
- END DupTest ;
- (* ---------------------------------------------------------------- *)
- (* lexer *)
- (* ---------------------------------------------------------------- *)
- PROCEDURE InitKeys () ;
- VAR i : CARDINAL ;
- BEGIN
- FOR i := 0 TO 42 DO
- kTk [i] := 0 ;
- kName [i] [0] := 0C
- END ;
- kName [1] := "PROGRAM" ; kTk [1] := TkProgram ;
- kName [2] := "BEGIN" ; kTk [2] := TkBegin ;
- kName [3] := "END" ; kTk [3] := TkEnd ;
- kName [4] := "IF" ; kTk [4] := TkIf ;
- kName [5] := "THEN" ; kTk [5] := TkThen ;
- kName [6] := "ELSE" ; kTk [6] := TkElse ;
- kName [7] := "WHILE" ; kTk [7] := TkWhile ;
- kName [8] := "DO" ; kTk [8] := TkDo ;
- kName [9] := "REPEAT" ; kTk [9] := TkRepeat ;
- kName [10] := "UNTIL" ; kTk [10] := TkUntil ;
- kName [11] := "FOR" ; kTk [11] := TkFor ;
- kName [12] := "TO" ; kTk [12] := TkTo ;
- kName [13] := "DOWNTO" ; kTk [13] := TkDownto ;
- kName [14] := "CASE" ; kTk [14] := TkCase ;
- kName [15] := "OF" ; kTk [15] := TkOf ;
- kName [16] := "GOTO" ; kTk [16] := TkGoto ;
- kName [17] := "EXIT" ; kTk [17] := TkExit ;
- kName [18] := "WITH" ; kTk [18] := TkWith ;
- kName [19] := "VAR" ; kTk [19] := TkVar ;
- kName [20] := "CONST" ; kTk [20] := TkConst ;
- kName [21] := "TYPE" ; kTk [21] := TkType ;
- kName [22] := "LABEL" ; kTk [22] := TkLabel ;
- kName [23] := "PROCEDURE" ; kTk [23] := TkProcedure ;
- kName [24] := "FUNCTION" ; kTk [24] := TkFunction ;
- kName [25] := "NIL" ; kTk [25] := TkNil ;
- kName [26] := "AND" ; kTk [26] := TkAnd ;
- kName [27] := "OR" ; kTk [27] := TkOr ;
- kName [28] := "NOT" ; kTk [28] := TkNot ;
- kName [29] := "DIV" ; kTk [29] := TkDiv ;
- kName [30] := "MOD" ; kTk [30] := TkMod ;
- kName [31] := "IN" ; kTk [31] := TkIn ;
- kName [32] := "FILE" ; kTk [32] := TkFile ;
- kName [33] := "TEXT" ; kTk [33] := TkText ;
- kName [34] := "RECORD" ; kTk [34] := TkRecord ;
- kName [35] := "ARRAY" ; kTk [35] := TkArray ;
- kName [36] := "SET" ; kTk [36] := TkSet ;
- kName [37] := "PACKED" ; kTk [37] := TkPacked ;
- kName [38] := "FORWARD" ; kTk [38] := TkForward ;
- kName [39] := "EXTERNAL" ; kTk [39] := TkExternal ;
- kName [40] := "ABSOLUTE" ; kTk [40] := TkAbsolute ;
- kName [41] := "OVERLAY" ; kTk [41] := TkOverlay ;
- kName [42] := "STRING" ; kTk [42] := TkString
- END InitKeys ;
- PROCEDURE IsBlank (ch : CHAR) : BOOLEAN ;
- BEGIN
- RETURN (ch = ' ') OR (ORD (ch) = 09H) OR (ORD (ch) = 0DH) OR (ORD (ch) = 0AH) OR (ORD (ch) = 0CH)
- END IsBlank ;
- PROCEDURE Skip () ;
- (* blanks, comments { } and (* *), compiler directives {$ } / (*$ *)
- letter + sign toggles rng/chk *)
- VAR ch : CHAR ;
- letter : CHAR ;
- BEGIN
- WHILE NOT abortFac DO
- WHILE IsBlank (CurCh ()) DO
- ch := GetCh ()
- END ;
- IF CurCh () = '{' THEN
- ch := GetCh () ;
- IF CurCh () = '$' THEN
- ch := GetCh () ;
- letter := GetCh () ;
- ch := GetCh () ;
- IF ch = '+' THEN
- IF letter = 'R' THEN dirs.rng := TRUE END ;
- IF letter = 'I' THEN dirs.chk := TRUE END
- ELSIF ch = '-' THEN
- IF letter = 'R' THEN dirs.rng := FALSE END ;
- IF letter = 'I' THEN dirs.chk := FALSE END
- END
- END ;
- WHILE (CurCh () # '}') AND (CurCh () # 0C) DO
- ch := GetCh ()
- END ;
- IF CurCh () = '}' THEN
- ch := GetCh ()
- END
- ELSIF (CurCh () = '(') AND (PeekAhead (1) = '*') THEN
- ch := GetCh () ;
- ch := GetCh () ;
- IF CurCh () = '$' THEN
- ch := GetCh () ;
- letter := GetCh () ;
- ch := GetCh () ;
- IF ch = '+' THEN
- IF letter = 'R' THEN dirs.rng := TRUE END
- ELSIF ch = '-' THEN
- IF letter = 'R' THEN dirs.rng := FALSE END
- END
- END ;
- LOOP
- IF (CurCh () = '*') AND (PeekAhead (1) = ')') THEN
- ch := GetCh () ;
- ch := GetCh () ;
- EXIT
- END ;
- IF CurCh () = 0C THEN
- EXIT
- END ;
- ch := GetCh ()
- END
- ELSE
- RETURN
- END
- END
- END Skip ;
- PROCEDURE GetWord () ;
- (* read identifier into wrd (uppercased); next char must be alpha *)
- VAR i : CARDINAL ;
- ch : CHAR ;
- BEGIN
- i := 0 ;
- ch := GetCh () ;
- LOOP
- IF i > MaxName THEN
- wrd [MaxName] := 0C ;
- RETURN
- END ;
- wrd [i] := Upper (ch) ;
- INC (i) ;
- ch := CurCh () ;
- IF NOT AlphaNum (ch) THEN
- wrd [i] := 0C ;
- RETURN
- END ;
- ch := GetCh ()
- END
- END GetWord ;
- PROCEDURE WddTok () : CARDINAL ;
- (* map wrd -> keyword token *)
- VAR i : CARDINAL ;
- BEGIN
- i := 1 ;
- WHILE i <= 42 DO
- IF kName [i] [0] # 0C THEN
- IF NameEq (wrd, kName [i]) THEN
- RETURN kTk [i]
- END
- END ;
- INC (i)
- END ;
- RETURN TkNone
- END WddTok ;
- PROCEDURE KwAhead (word : ARRAY OF CHAR) : BOOLEAN ;
- (* does the next token (past blanks/comments) equal the keyword 'word',
- without consuming it? srcPos is saved and restored. *)
- VAR save : CARDINAL ;
- k : BOOLEAN ;
- BEGIN
- save := srcPos ;
- Skip () ;
- k := FALSE ;
- IF Alpha (CurCh ()) THEN
- GetWord () ;
- k := NameEq (wrd, word)
- END ;
- srcPos := save ;
- RETURN k
- END KwAhead ;
- PROCEDURE PeekKw (VAR tok : CARDINAL) ;
- (* peek at the next keyword token without consuming it *)
- VAR i : CARDINAL ;
- BEGIN
- tok := TkNone ;
- i := 1 ;
- WHILE i <= 42 DO
- IF kName [i] [0] # 0C THEN
- IF KwAhead (kName [i]) THEN
- tok := kTk [i] ;
- RETURN
- END
- END ;
- INC (i)
- END
- END PeekKw ;
- PROCEDURE MatchKey (VAR tok : CARDINAL) : BOOLEAN ;
- (* skip; if next symbol is a word, read it into wrd and set its token.
- Returns TRUE when a word was read (tok = TkNone for plain ids). *)
- BEGIN
- tok := TkNone ;
- Skip () ;
- IF NOT Alpha (CurCh ()) THEN
- RETURN FALSE
- END ;
- GetWord () ;
- tok := WddTok () ;
- RETURN TRUE
- END MatchKey ;
- PROCEDURE MatchDelim (ch : CHAR) : BOOLEAN ;
- BEGIN
- Skip () ;
- IF CurCh () = ch THEN
- DropCh (GetCh ()) ;
- RETURN TRUE
- END ;
- RETURN FALSE
- END MatchDelim ;
- PROCEDURE MatchAssign () : BOOLEAN ;
- BEGIN
- Skip () ;
- IF (CurCh () = ':') AND (PeekAhead (1) = '=') THEN
- DropCh (GetCh ()) ;
- DropCh (GetCh ()) ;
- RETURN TRUE
- END ;
- RETURN FALSE
- END MatchAssign ;
- PROCEDURE MatchRange () : BOOLEAN ;
- BEGIN
- Skip () ;
- IF (CurCh () = '.') AND (PeekAhead (1) = '.') THEN
- DropCh (GetCh ()) ;
- DropCh (GetCh ()) ;
- RETURN TRUE
- END ;
- RETURN FALSE
- END MatchRange ;
- PROCEDURE ExpectDelim (ch : CHAR ; n : CARDINAL) ;
- BEGIN
- Skip () ;
- IF CurCh () = ch THEN
- DropCh (GetCh ())
- ELSE
- Err (n)
- END
- END ExpectDelim ;
- PROCEDURE HexVal (ch : CHAR) : CARDINAL ;
- BEGIN
- IF (ch >= '0') AND (ch <= '9') THEN
- RETURN ORD (ch) - ORD ('0')
- ELSIF (ch >= 'A') AND (ch <= 'F') THEN
- RETURN ORD (ch) - ORD ('A') + 10
- END ;
- RETURN ORD (ch) - ORD ('a') + 10
- END HexVal ;
- PROCEDURE RdIntConst (VAR v : LONGINT) ;
- (* bare integer constant; current char is digit or '$' *)
- VAR acc : LONGINT ;
- BEGIN
- acc := 0 ;
- IF CurCh () = '$' THEN
- DropCh (GetCh ()) ;
- WHILE IsHexCh (CurCh ()) DO
- acc := acc * 16 + VAL (LONGINT, HexVal (CurCh ())) ;
- DropCh (GetCh ())
- END
- ELSE
- WHILE Digit (CurCh ()) DO
- acc := acc * 10 + VAL (LONGINT, ORD (CurCh ()) - ORD ('0')) ;
- DropCh (GetCh ())
- END
- END ;
- v := acc
- END RdIntConst ;
- PROCEDURE StrNew (first : CARDINAL ; hasFirst : BOOLEAN ) : CARDINAL ;
- (* Open a pool slot for a string literal, optionally pre-seeded with its
- first character.
- RdConst has to read the first character before it can tell a one-character
- literal from a longer one - the "is the next character another quote?"
- test only makes sense once something has been read - so the seeding has to
- happen here and has to advance strTop. Leaving strTop alone and letting the
- caller write the character by hand is a trap: strTop is the next FREE byte,
- so the first StrPut lands on top of the seeded character and overwrites it.
- (That bug shipped the literal 'hi' as 69 00 - 'i' then NUL.) *)
- VAR x : CARDINAL ;
- BEGIN
- IF strCnt > HIGH (strOff) THEN
- Err (ECompOvf) ; (* too many literals in one unit *)
- RETURN 0
- END ;
- x := strCnt ;
- strOff [x] := strTop ;
- strLen [x] := 0 ;
- INC (strCnt) ;
- IF hasFirst THEN
- strPool [strTop] := CHR (first) ;
- INC (strTop) ;
- strLen [x] := 1
- END ;
- rdStrX := x ;
- RETURN x
- END StrNew ;
- PROCEDURE StrPut (x : CARDINAL ) ;
- (* append the current source character to pool slot x *)
- BEGIN
- IF x > HIGH (strOff) THEN
- RETURN
- END ;
- IF strTop > HIGH (strPool) THEN
- Err (ECompOvf) ; (* literal longer than the pool *)
- RETURN
- END ;
- strPool [strTop] := CurCh () ;
- INC (strTop) ;
- INC (strLen [x])
- END StrPut ;
- PROCEDURE RdConst (VAR v : LONGINT ; VAR cls : CARDINAL ;
- VAR isStr : BOOLEAN) ;
- (* scalar or string constant. A string literal's text is collected into the
- pool and its slot index left in rdStrX; a single-character literal stays a
- TScalar holding its character code, which is what "c := 'a'" wants. *)
- CONST q = AposC ;
- BEGIN
- isStr := FALSE ;
- cls := TScalar ;
- v := 0 ;
- Skip () ;
- IF CurCh () = '$' THEN
- RdIntConst (v) ;
- cls := TScalar
- ELSIF Digit (CurCh ()) THEN
- RdIntConst (v) ;
- IF (CurCh () = '.') AND (Digit (PeekAhead (1))) THEN
- cls := TReal ;
- DropCh (GetCh ())
- END ;
- IF (CurCh () = 'E') OR (CurCh () = 'e') THEN
- cls := TReal ;
- DropCh (GetCh ())
- END ;
- IF cls = TReal THEN
- WHILE AlphaNum (CurCh ()) OR (CurCh () = '.') OR (CurCh () = '-')
- OR (CurCh () = '+') DO
- DropCh (GetCh ())
- END
- END
- ELSIF ORD (CurCh ()) = q THEN
- DropCh (GetCh ()) ;
- IF ORD (CurCh ()) = q THEN
- (* '' - the empty string. It used to be reported as the scalar 39,
- so writeln('') printed a single quote mark. It is a string of
- length zero, and an inline zero-length literal is exactly what the
- runtime's JCXZ path is for. *)
- DropCh (GetCh ()) ;
- isStr := TRUE ;
- cls := TString ;
- v := 0 ;
- rdStrX := StrNew (0, FALSE)
- ELSIF (CurCh () = 0C) OR ((ORD (CurCh ()) = 0DH)) OR ((ORD (CurCh ()) = 0AH)) THEN
- Err (EUnknown)
- ELSE
- v := VAL (LONGINT, ORD (GetCh ())) ;
- cls := TScalar ;
- IF ORD (CurCh ()) = q THEN
- DropCh (GetCh ())
- ELSE
- isStr := TRUE ;
- cls := TString ;
- (* Scan to the closing quote. NB: the loop condition must test
- the CURRENT character and only the current character. The
- obvious-looking "while CurCh # quote" with a
- "if PeekAhead(1) = quote then consume two" body is wrong:
- consuming the quote moves the cursor past it, so the next
- condition test sees the character AFTER the literal, is
- satisfied, and the scan runs on to end-of-buffer - which
- silently eats the rest of the program and makes every later
- error point at end-of-file. Stop on the quote itself, and
- treat a doubled quote as one embedded quote character.
- The first character is already gone - it was read into v above -
- so the pool slot is pre-seeded with it. *)
- rdStrX := StrNew (VAL (CARDINAL, v), TRUE) ;
- LOOP
- IF ORD (CurCh ()) = q THEN
- IF ORD (PeekAhead (1)) = q THEN
- StrPut (rdStrX) ; (* '' inside *)
- DropCh (GetCh ()) ; DropCh (GetCh ())
- ELSE
- EXIT (* closing quote *)
- END
- ELSIF (CurCh () = 0C) OR (ORD (CurCh ()) = 0DH) THEN
- Err (EUnknown) ; (* unterminated *)
- EXIT
- ELSE
- StrPut (rdStrX) ;
- DropCh (GetCh ())
- END
- END ;
- IF ORD (CurCh ()) = q THEN
- DropCh (GetCh ())
- END
- END
- END
- ELSE
- Err (EUnknown)
- END
- END RdConst ;
- (* ---------------------------------------------------------------- *)
- (* forward declarations *)
- (* ---------------------------------------------------------------- *)
- PROCEDURE ParseExpr (VAR r : ERes) ; FORWARD ;
- PROCEDURE ParseAdd (VAR r : ERes) ; FORWARD ;
- PROCEDURE ParseMul (VAR r : ERes) ; FORWARD ;
- PROCEDURE ParseNeg (VAR r : ERes) ; FORWARD ;
- PROCEDURE ParseAtom (VAR r : ERes) ; FORWARD ;
- PROCEDURE ParseType (VAR cls, size, elem : CARDINAL) ; FORWARD ;
- PROCEDURE Statmnt () ; FORWARD ;
- PROCEDURE ParseCall (idx : CARDINAL) ; FORWARD ;
- PROCEDURE ParseCallArgs (idx : CARDINAL) ; FORWARD ;
- PROCEDURE EmCallMost (idx, nk : CARDINAL) ; FORWARD ;
- (* ---------------------------------------------------------------- *)
- (* expressions (TPSRC9) *)
- (* ---------------------------------------------------------------- *)
- PROCEDURE LoadAtom (VAR r : ERes) ;
- (* load value of r into AX (folding constants).
- A string literal is refused here, and this is the single place that has to:
- assignment, IF, WHILE, FOR, REPEAT, CASE, array subscripts and every
- operator all reach their operand through LoadAtom, and none of them can use
- a counted string where a 16-bit word is expected. Reporting it here rather
- than in the parser means writeln('hi') still works - IoCall handles a
- literal before it ever calls LoadAtom. *)
- BEGIN
- IF r.cls = TString THEN
- Err (ENoLib) ; (* string value used as a number *)
- r.kind := 2 ;
- RETURN
- END ;
- IF r.kind = 0 THEN
- EmMovAxi (W16 (r.imm)) ;
- r.kind := 2
- ELSIF r.kind = 1 THEN
- IF symtab [r.idx].size > 2 THEN
- Err (ENoLib)
- ELSE
- EmLoadVar (symtab [r.idx].local,
- (symtab [r.idx].off + r.boff) MOD 10000H,
- symtab [r.idx].size) ;
- IF symtab [r.idx].size = 1 THEN
- EmMovAh0 ()
- END ;
- r.kind := 2
- END
- END
- END LoadAtom ;
- PROCEDURE ParseSub (VAR r : ERes) ;
- (* consume '[' constExpr ']' while present, folding the index into the
- base offset (constant indexing only) *)
- VAR t : ERes ;
- BEGIN
- LOOP
- Skip () ;
- IF CurCh () # '[' THEN
- RETURN
- END ;
- DropCh (GetCh ()) ;
- ParseExpr (t) ;
- IF OK () THEN
- IF t.kind # 0 THEN
- Err (ENoLib) ;
- RETURN
- END ;
- IF symtab [r.idx].cls = TArray THEN
- r.boff := W16 (VAL (LONGINT, r.boff)
- + t.imm * VAL (LONGINT, symtab [r.idx].elem))
- ELSE
- Err (ESimpType) ;
- RETURN
- END
- END ;
- ExpectDelim (']', ENoSemi)
- END
- END ParseSub ;
- PROCEDURE ParseVar (VAR r : ERes) ;
- (* name [ '[' constExpr ']' ]* ; a function name maps to its result var *)
- VAR idx : CARDINAL ;
- BEGIN
- IF NOT Search (wrd, idx) THEN
- Err (EUnknown) ;
- RETURN
- END ;
- r.idx := idx ;
- r.kind := 1 ;
- r.boff := 0 ;
- r.cls := symtab [idx].cls ;
- IF symtab [idx].tag = KFunc THEN
- idx := symtab [idx].resvar ;
- r.idx := idx ;
- r.cls := symtab [idx].cls
- ELSIF (symtab [idx].tag # KVar) AND (symtab [idx].tag # KType) THEN
- Err (EUnknown) ;
- RETURN
- END ;
- ParseSub (r)
- END ParseVar ;
- PROCEDURE ConstAdd (a, b : LONGINT) : LONGINT ;
- BEGIN
- RETURN VAL (LONGINT, W16 (a + b))
- END ConstAdd ;
- PROCEDURE ConstSub (a, b : LONGINT) : LONGINT ;
- BEGIN
- RETURN VAL (LONGINT, W16 (a - b))
- END ConstSub ;
- PROCEDURE ConstMul (a, b : LONGINT) : LONGINT ;
- BEGIN
- RETURN VAL (LONGINT, W16 (a * b))
- END ConstMul ;
- PROCEDURE EmMoveAxDx () ;
- BEGIN
- Ebyte (92H)
- END EmMoveAxDx ;
- PROCEDURE BinOpEmit (op : CARDINAL ; left, right : ERes ; VAR res : ERes) ;
- (* binary operation at one precedence level; folds constant operands *)
- VAR f : LONGINT ;
- okc : BOOLEAN ;
- BEGIN
- IF op = TkAnd THEN
- IF (left.kind = 0) AND (right.kind = 0) THEN
- res.kind := 0 ;
- res.imm := BitAnd (left.imm, right.imm) ;
- res.cls := TBool ;
- RETURN
- END ;
- LoadAtom (left) ; EmPushAx () ;
- LoadAtom (right) ; EmPopCx () ;
- EmAndAxCx () ;
- res.kind := 2 ; res.cls := TBool ;
- RETURN
- END ;
- IF op = TkOr THEN
- IF (left.kind = 0) AND (right.kind = 0) THEN
- res.kind := 0 ;
- res.imm := BitOr (left.imm, right.imm) ;
- res.cls := TBool ;
- RETURN
- END ;
- LoadAtom (left) ; EmPushAx () ;
- LoadAtom (right) ; EmPopCx () ;
- EmOrAxCx () ;
- res.kind := 2 ; res.cls := TBool ;
- RETURN
- END ;
- IF (left.kind = 0) AND (right.kind = 0) THEN
- okc := FALSE ;
- CASE op OF
- 1 : f := ConstAdd (left.imm, right.imm) ; okc := TRUE ;
- | 2 : f := ConstSub (left.imm, right.imm) ; okc := TRUE ;
- | 3 : f := ConstMul (left.imm, right.imm) ; okc := TRUE ;
- | TkDiv : okc := (right.imm # 0) AND (right.imm > 0)
- AND (left.imm >= 0) ;
- IF okc THEN f := left.imm DIV right.imm END ;
- | TkMod : okc := (right.imm # 0) AND (right.imm > 0)
- AND (left.imm >= 0) ;
- IF okc THEN f := left.imm MOD right.imm END ;
- ELSE
- okc := FALSE
- END ;
- IF okc THEN
- res.kind := 0 ;
- res.imm := VAL (LONGINT, W16 (f)) ;
- res.cls := left.cls ;
- RETURN
- ELSIF op = TkDiv THEN
- Err (EConstRange) ;
- RETURN
- END
- END ;
- LoadAtom (left) ; EmPushAx () ;
- LoadAtom (right) ; EmPopCx () ;
- EmXchgAxCx () ;
- CASE op OF
- 1 : EmAddAxCx ;
- | 2 : EmSubAxCx ;
- | 3 : EmMulAxCx ;
- | TkDiv : EmIDivAxCx ;
- | TkMod : EmIDivAxCx ; EmMoveAxDx ;
- ELSE
- Err (ETypeErr)
- END ;
- res.kind := 2 ;
- res.cls := left.cls
- END BinOpEmit ;
- PROCEDURE ConstCmp (op : CARDINAL ; a, b : LONGINT ; VAR f : LONGINT)
- : BOOLEAN ;
- VAR a16, b16 : CARDINAL ;
- BEGIN
- a16 := W16 (a) ;
- b16 := W16 (b) ;
- f := 0 ;
- CASE op OF
- 1 : f := VAL (LONGINT, ORD (a16 = b16)) ;
- | 2 : f := VAL (LONGINT, ORD (a16 # b16)) ;
- | 3 : f := VAL (LONGINT, ORD (a16 < b16)) ;
- | 4 : f := VAL (LONGINT, ORD (a16 >= b16)) ;
- | 5 : f := VAL (LONGINT, ORD (a16 > b16)) ;
- | 6 : f := VAL (LONGINT, ORD (a16 <= b16)) ;
- ELSE
- RETURN FALSE
- END ;
- RETURN TRUE
- END ConstCmp ;
- PROCEDURE ParseCmp (VAR r : ERes) ;
- (* "=" | "<>" | "<" | "<=" | ">" | ">=" *)
- VAR op : CARDINAL ;
- left, right : ERes ;
- f : LONGINT ;
- BEGIN
- ParseAdd (r) ;
- LOOP
- op := 0 ;
- Skip () ;
- IF CurCh () = '=' THEN
- op := 1 ; DropCh (GetCh ())
- ELSIF (CurCh () = '<') AND (PeekAhead (1) = '>') THEN
- op := 2 ; DropCh (GetCh ()) ; DropCh (GetCh ())
- ELSIF CurCh () = '<' THEN
- IF PeekAhead (1) = '=' THEN
- op := 6 ; DropCh (GetCh ()) ; DropCh (GetCh ())
- ELSE
- op := 3 ; DropCh (GetCh ())
- END
- ELSIF CurCh () = '>' THEN
- IF PeekAhead (1) = '=' THEN
- op := 5 ; DropCh (GetCh ()) ; DropCh (GetCh ())
- ELSE
- op := 4 ; DropCh (GetCh ())
- END
- END ;
- IF op = 0 THEN
- RETURN
- END ;
- left := r ;
- ParseAdd (right) ;
- IF (left.kind = 0) AND (right.kind = 0) THEN
- IF ConstCmp (op, left.imm, right.imm, f) THEN
- r.kind := 0 ;
- r.imm := f ;
- r.cls := TBool
- ELSE
- r.kind := 0 ;
- r.imm := 0 ;
- r.cls := TBool
- END
- ELSE
- LoadAtom (left) ; EmPushAx () ;
- LoadAtom (right) ; EmPopCx () ;
- EmXchgAxCx () ;
- EmCmpAxCx () ;
- CASE op OF
- 1 : EmSetcc (94H) ;
- | 2 : EmSetcc (95H) ;
- | 3 : EmSetcc (9CH) ;
- | 4 : EmSetcc (9DH) ;
- | 5 : EmSetcc (9FH) ;
- | 6 : EmSetcc (9EH)
- END ;
- r.kind := 2 ;
- r.cls := TBool
- END
- END
- END ParseCmp ;
- PROCEDURE ParseAdd (VAR r : ERes) ;
- VAR op : CARDINAL ;
- left, right : ERes ;
- BEGIN
- ParseMul (r) ;
- LOOP
- op := 0 ;
- Skip () ;
- IF CurCh () = '+' THEN
- op := 1 ; DropCh (GetCh ())
- ELSIF CurCh () = '-' THEN
- op := 2 ; DropCh (GetCh ())
- ELSIF KwAhead ("OR") THEN
- GetWord () ;
- op := TkOr
- ELSE
- RETURN
- END ;
- left := r ;
- ParseMul (right) ;
- BinOpEmit (op, left, right, r)
- END
- END ParseAdd ;
- PROCEDURE ParseMul (VAR r : ERes) ;
- VAR op : CARDINAL ;
- left, right : ERes ;
- BEGIN
- ParseNeg (r) ;
- LOOP
- op := 0 ;
- Skip () ;
- IF CurCh () = '*' THEN
- op := 1 ; DropCh (GetCh ())
- ELSIF CurCh () = '/' THEN
- op := 2 ; DropCh (GetCh ()) ; Err (ENoLib)
- ELSIF KwAhead ("DIV") THEN
- GetWord () ; op := TkDiv
- ELSIF KwAhead ("MOD") THEN
- GetWord () ; op := TkMod
- ELSIF KwAhead ("AND") THEN
- GetWord () ; op := TkAnd
- ELSE
- RETURN
- END ;
- IF op = 2 THEN
- RETURN
- END ;
- left := r ;
- ParseNeg (right) ;
- BinOpEmit (op, left, right, r)
- END
- END ParseMul ;
- PROCEDURE ParseNeg (VAR r : ERes) ;
- BEGIN
- Skip () ;
- IF CurCh () = '+' THEN
- DropCh (GetCh ()) ;
- ParseNeg (r) ;
- RETURN
- ELSIF CurCh () = '-' THEN
- DropCh (GetCh ()) ;
- ParseNeg (r) ;
- IF r.kind = 0 THEN
- r.imm := VAL (LONGINT, W16 (0 - r.imm))
- ELSE
- LoadAtom (r) ;
- EmNegAx () ;
- r.kind := 2
- END ;
- RETURN
- ELSIF KwAhead ("NOT") THEN
- GetWord () ;
- ParseNeg (r) ;
- IF r.kind = 0 THEN
- r.imm := BitNot (r.imm)
- ELSE
- LoadAtom (r) ;
- EmNotAx () ;
- r.kind := 2
- END ;
- RETURN
- END ;
- ParseAtom (r)
- END ParseNeg ;
- PROCEDURE ParseAtom (VAR r : ERes) ;
- (* const | variable | func(params) | '(' expr ')' *)
- VAR idx : CARDINAL ;
- strf : BOOLEAN ;
- quoted : BOOLEAN ;
- BEGIN
- r.chr := FALSE ; (* default: not a quoted char literal *)
- Skip () ;
- IF CurCh () = '(' THEN
- DropCh (GetCh ()) ;
- ParseExpr (r) ;
- ExpectDelim (')', ENoSemi) ;
- RETURN
- END ;
- IF (CurCh () = '$') OR (Digit (CurCh ())) OR (ORD (CurCh ()) = AposC) THEN
- (* remember that this was a quoted literal BEFORE RdConst consumes it:
- RdConst reports a 1-character literal as TScalar (its char code),
- which is right for "c := 'a'" but would make writeln('a') print 97.
- Mark it so the writer picks the char entry, not the integer one. *)
- quoted := (ORD (CurCh ()) = AposC) ;
- RdConst (r.imm, r.cls, strf) ;
- r.chr := quoted AND (r.cls = TScalar) AND NOT strf ;
- IF r.cls = TReal THEN
- Err (ENoLib) ;
- r.kind := 2 ;
- RETURN
- END ;
- IF strf THEN
- (* A string literal is legal here as a *value* - it is not rejected
- at this point, because writeln('hi') needs it and IoCall is the
- only place that knows how to emit one. Everywhere else the
- literal has to end up as a machine word, and that is caught by
- LoadAtom, which refuses a TString. *)
- r.strx := rdStrX ;
- r.kind := 3 ;
- RETURN
- END ;
- r.kind := 0 ;
- RETURN
- END ;
- IF NOT Alpha (CurCh ()) THEN
- Err (EUnknown) ;
- RETURN
- END ;
- GetWord () ;
- IF NOT Search (wrd, idx) THEN
- Err (EUnknown) ;
- RETURN
- END ;
- IF symtab [idx].tag = KConst THEN
- r.kind := 0 ;
- r.imm := symtab [idx].lval ;
- r.cls := symtab [idx].cls ;
- RETURN
- ELSIF symtab [idx].tag = KFunc THEN
- ParseCall (idx) ;
- r.kind := 2 ;
- r.cls := symtab [idx].cls ;
- RETURN
- ELSIF (symtab [idx].tag = KVar) OR (symtab [idx].tag = KType) THEN
- ParseVar (r) ;
- r.boff := 0 ;
- RETURN
- ELSE
- Err (EUnknown)
- END
- END ParseAtom ;
- PROCEDURE AddPend (kind, who, place : CARDINAL) ;
- BEGIN
- IF nPend < MaxPend THEN
- pend [nPend].kind := kind ;
- pend [nPend].who := who ;
- pend [nPend].place := place ;
- INC (nPend)
- ELSE
- Err (ECompOvf)
- END
- END AddPend ;
- PROCEDURE EmCallMost (idx, nk : CARDINAL) ;
- (* emit the call to sym 'idx' and clean up nk value arguments *)
- VAR p : CARDINAL ;
- BEGIN
- IF symtab [idx].defnd THEN
- DropC (EmCall (symtab [idx].goPos))
- ELSE
- p := EmCall (0) ;
- AddPend (1, idx, p)
- END ;
- IF nk > 0 THEN
- EmAddSp (2 * nk)
- END
- END EmCallMost ;
- PROCEDURE ParseCallArgs (idx : CARDINAL) ;
- (* '(' already consumed: read args ')' then call. Arguments are pushed
- right-to-left so the first-declared parameter lands at BP+4. *)
- VAR args : ARRAY [0..15] OF ERes ;
- nArgs, i : CARDINAL ;
- BEGIN
- nArgs := 0 ;
- IF CurCh () = ')' THEN
- DropCh (GetCh ())
- ELSE
- LOOP
- IF nArgs >= 16 THEN
- Err (ECompOvf) ;
- EXIT
- END ;
- ParseExpr (args [nArgs]) ;
- INC (nArgs) ;
- IF NOT MatchDelim (',') THEN
- EXIT
- END
- END ;
- ExpectDelim (')', ENoSemi)
- END ;
- i := nArgs ;
- WHILE i > 0 DO
- DEC (i) ;
- LoadAtom (args [i]) ;
- EmPushAx ()
- END ;
- EmCallMost (idx, nArgs)
- END ParseCallArgs ;
- PROCEDURE ParseCall (idx : CARDINAL) ;
- (* procedure/function call; '(' optional *)
- VAR args : ARRAY [0..15] OF ERes ;
- nArgs, i : CARDINAL ;
- BEGIN
- nArgs := 0 ;
- IF MatchDelim ('(') THEN
- IF CurCh () # ')' THEN
- LOOP
- IF nArgs >= 16 THEN
- Err (ECompOvf) ;
- EXIT
- END ;
- ParseExpr (args [nArgs]) ;
- INC (nArgs) ;
- IF NOT MatchDelim (',') THEN
- EXIT
- END
- END ;
- ExpectDelim (')', ENoSemi)
- ELSE
- DropCh (GetCh ())
- END
- END ;
- i := nArgs ;
- WHILE i > 0 DO
- DEC (i) ;
- LoadAtom (args [i]) ;
- EmPushAx ()
- END ;
- EmCallMost (idx, nArgs)
- END ParseCall ;
- PROCEDURE ParseExpr (VAR r : ERes) ;
- BEGIN
- ParseCmp (r)
- END ParseExpr ;
- (* ---------------------------------------------------------------- *)
- (* statements (TPSRC8) *)
- (* ---------------------------------------------------------------- *)
- PROCEDURE ParseLabelStmt () ;
- (* numeric label definition 'n :' *)
- VAR n : CARDINAL ;
- nm : ARRAY [0..9] OF CHAR ;
- idx : CARDINAL ;
- i : CARDINAL ;
- BEGIN
- n := 0 ;
- WHILE Digit (CurCh ()) DO
- n := n * 10 + (ORD (CurCh ()) - ORD ('0')) ;
- DropCh (GetCh ())
- END ;
- ExpectDelim (':', ENoSemi) ;
- NumToName (n, nm) ;
- IF Search (nm, idx) THEN
- IF symtab [idx].tag = KLabel THEN
- symtab [idx].defnd := TRUE ;
- symtab [idx].goPos := pc ;
- i := 0 ;
- WHILE i < nPend DO
- IF (pend [i].kind = 0) AND (pend [i].who = idx) THEN
- SetPatTgt (pend [i].place, pc) ;
- pend [i].kind := 99
- END ;
- INC (i)
- END
- ELSE
- Err (EUnknown)
- END
- ELSE
- idx := NewSym (nm, KLabel, TScalar, 0, 0, 0, 0, FALSE) ;
- symtab [idx].defnd := TRUE ;
- symtab [idx].goPos := pc
- END
- END ParseLabelStmt ;
- PROCEDURE Assignment (r : ERes) ;
- (* ':=' already consumed by the caller; store expression into r *)
- VAR src : ERes ;
- BEGIN
- IF r.kind # 1 THEN
- Err (EUnknown) ;
- RETURN
- END ;
- IF symtab [r.idx].size > 2 THEN
- Err (ENoLib) ;
- RETURN
- END ;
- ParseExpr (src) ;
- LoadAtom (src) ;
- EmStoreVar (symtab [r.idx].local,
- (symtab [r.idx].off + r.boff) MOD 10000H,
- symtab [r.idx].size)
- END Assignment ;
- PROCEDURE Compound () ;
- (* BEGIN statement ';' ... END; END is consumed here *)
- VAR tok : CARDINAL ;
- BEGIN
- LOOP
- PeekKw (tok) ;
- IF tok = TkEnd THEN
- DropB (MatchKey (tok)) ;
- RETURN
- END ;
- Statmnt () ;
- IF NOT OK () THEN
- RETURN
- END ;
- IF NOT MatchDelim (';') THEN
- PeekKw (tok) ;
- IF tok = TkEnd THEN
- DropB (MatchKey (tok)) ;
- RETURN
- END ;
- Err (ENoSemi) ;
- RETURN
- END
- END
- END Compound ;
- PROCEDURE IoCall (idx : CARDINAL) ;
- (* WRITE / WRITELN / READ / READLN / HALT.
- TP3 (TPSRC8 pwriteln, pwrloop, prdtyped) does not pass a descriptor to
- the runtime: it looks at each argument's class and emits a *different*
- call per type, so the formatting is fixed at compile time. Mirrored
- here - one call per argument, then a final call for the line break.
- WRITE/WRITELN push the value; READ/READLN push the address, so the
- runtime can store. As everywhere else in this compiler the caller
- cleans the argument off the stack. *)
- VAR args : ARRAY [0..15] OF ERes ;
- nArgs, i, ent, which, acls : CARDINAL ;
- reading : BOOLEAN ;
- pushed : BOOLEAN ;
- n : CARDINAL ;
- dummy : ERes ;
- BEGIN
- which := symtab [idx].cls ; (* BI_* *)
- IF which = BI_Halt THEN
- IF MatchDelim ('(') THEN (* halt(0) - code ignored *)
- ParseExpr (dummy) ;
- ExpectDelim (')', ENoSemi)
- END ;
- DropC (EmCall (TU_Halt)) ;
- RETURN
- END ;
- reading := (which = BI_Read) OR (which = BI_ReadLn) ;
- nArgs := 0 ;
- IF MatchDelim ('(') THEN
- IF CurCh () # ')' THEN
- LOOP
- IF nArgs >= 16 THEN
- Err (ECompOvf) ;
- EXIT
- END ;
- ParseExpr (args [nArgs]) ;
- INC (nArgs) ;
- IF NOT MatchDelim (',') THEN
- EXIT
- END
- END ;
- IF NOT MatchDelim (')') THEN
- Err (ENoSemi) ;
- RETURN
- END
- ELSE
- DropCh (GetCh ())
- END
- END ;
- IF reading AND (nArgs = 0) THEN
- (* readln with no variable: just skip to the next line *)
- DropC (EmCall (TU_RdLn)) ;
- RETURN
- END ;
- (* NB: guard the loop bound - with nArgs = 0, "nArgs - 1" would wrap round
- to 65535 in CARDINAL and spin 65536 times. *)
- IF nArgs > 0 THEN
- FOR i := 0 TO nArgs - 1 DO
- pushed := TRUE ; (* default: value is on the stack -> call + pop *)
- IF reading THEN
- IF args [i].kind # 1 THEN
- Err (ETypeErr) ; (* READ needs a variable *)
- RETURN
- END ;
- acls := symtab [args [i].idx].cls ;
- IF acls = TString THEN
- Err (ENoLib) ; (* string runtime pending *)
- RETURN
- END ;
- EmPushVarAddr (symtab [args [i].idx].local, symtab [args [i].idx].off) ;
- IF acls = TReal THEN
- ent := TU_RdInt (* real reads: not yet *)
- ELSIF acls = TBool THEN
- ent := TU_RdBool
- ELSIF acls = TScalar THEN
- ent := TU_RdInt
- ELSE
- ent := TU_RdChar
- END
- ELSE
- acls := args [i].cls ;
- IF (acls = TString) AND (args [i].kind = 3) THEN
- (* An inline string literal. TP3 TPSRC8 pwrinlin special-cases a
- literal that is followed directly by ',' or ')' - i.e. an
- argument, not an expression - and emits
- CALL wrtinl <length byte> <characters...>
- with no stack argument at all; wrtinl reads the length from the
- return address and returns to just past the last character.
- Mirrored exactly, so the literal costs only its own characters
- in the code stream and nothing in the data segment. *)
- IF strLen [args [i].strx] > 255 THEN
- (* The length is one byte, so a literal of 256 characters or
- more would wrap: 300 characters emitted behind a length of
- 44, and the runtime would print 44 of them and silently drop
- the rest. TP3 strings are at most 255 characters, so refuse
- rather than truncate. *)
- Err (EConstRange) ;
- RETURN
- END ;
- DropC (EmCall (TU_WrInl)) ;
- Ebyte (VAL (BYTE, strLen [args [i].strx])) ;
- n := 0 ;
- WHILE n < strLen [args [i].strx] DO
- Ebyte (VAL (BYTE, ORD (strPool [strOff [args [i].strx] + n]))) ;
- INC (n)
- END ;
- pushed := FALSE (* nothing was pushed for this one *)
- ELSE
- IF acls = TString THEN
- (* A string *variable*. Not emitted rather than emitted wrongly:
- EmPushVarAddr's local form is still wrong (see the note on
- that procedure), and a wrong address here would print
- garbage instead of failing. *)
- Err (ENoLib) ;
- RETURN
- END ;
- LoadAtom (args [i]) ;
- EmPushAx () ;
- IF args [i].chr THEN
- ent := TU_WrChar (* 'a' - one char, not 97 *)
- ELSIF acls = TReal THEN
- ent := TU_WrReal
- ELSIF acls = TBool THEN
- ent := TU_WrBool
- ELSIF acls = TScalar THEN
- ent := TU_WrInt
- ELSE
- ent := TU_WrChar
- END
- END
- END ;
- IF pushed THEN
- DropC (EmCall (ent)) ;
- EmAddSp (2) (* one 16-bit argument *)
- END
- END
- END ;
- IF which = BI_WriteLn THEN
- DropC (EmCall (TU_WrLn))
- ELSIF which = BI_ReadLn THEN
- DropC (EmCall (TU_RdLn))
- END
- END IoCall ;
- PROCEDURE Statmnt () ;
- VAR tok : CARDINAL ;
- idx, i2 : CARDINAL ;
- t, src : ERes ;
- L1, zj, zj2, exj : CARDINAL ;
- lo, hi, v : LONGINT ;
- clso : CARDINAL ;
- i : CARDINAL ;
- nm : ARRAY [0..MaxName] OF CHAR ;
- strf : BOOLEAN ;
- dow : BOOLEAN ;
- BEGIN
- Skip () ;
- IF Digit (CurCh ()) THEN
- ParseLabelStmt () ; (* consumed 'n' ':' *)
- Statmnt () ; (* 'n : statement' - the statement follows
- the label directly, with no ';' between *)
- RETURN
- END ;
- IF NOT Alpha (CurCh ()) THEN
- ExpectDelim (';', ENoSemi) ;
- RETURN
- END ;
- DropB (MatchKey (tok)) ;
- IF tok = TkBegin THEN
- Compound ()
- ELSIF tok = TkIf THEN
- ParseExpr (t) ;
- LoadAtom (t) ;
- EmCmpAxi (0) ;
- zj := EmJcc (84H, 0) ; (* JZ -> else/end *)
- IF NOT (MatchKey (tok) AND (tok = TkThen)) THEN
- Err (ENoSemi)
- END ;
- Statmnt () ;
- IF MatchKey (tok) AND (tok = TkElse) THEN
- exj := EmJmpNear (0) ;
- SetPatTgt (zj, pc) ;
- Statmnt () ;
- SetPatTgt (exj, pc)
- ELSE
- SetPatTgt (zj, pc)
- END
- ELSIF tok = TkWhile THEN
- L1 := pc ;
- ParseExpr (t) ;
- LoadAtom (t) ;
- EmCmpAxi (0) ;
- zj := EmJcc (84H, 0) ; (* JZ -> end *)
- IF NOT (MatchKey (tok) AND (tok = TkDo)) THEN
- Err (ENoSemi)
- END ;
- brkSave [brkN] := exitCnt ;
- loopTy [brkN] := 1 ;
- INC (brkN) ;
- Statmnt () ;
- DEC (brkN) ;
- i := brkSave [brkN] ;
- WHILE i < exitCnt DO
- SetPatTgt (exitPatch [i], pc) ;
- INC (i)
- END ;
- exitCnt := brkSave [brkN] ;
- DropC (EmJmpNear (L1)) ;
- SetPatTgt (zj, pc)
- ELSIF tok = TkRepeat THEN
- L1 := pc ;
- brkSave [brkN] := exitCnt ;
- loopTy [brkN] := 1 ;
- INC (brkN) ;
- Statmnt () ;
- IF NOT (MatchKey (tok) AND (tok = TkUntil)) THEN
- Err (ENoSemi)
- END ;
- ParseExpr (t) ;
- LoadAtom (t) ;
- EmCmpAxi (0) ;
- zj := EmJcc (85H, L1) ; (* JNZ -> body again *)
- DEC (brkN) ;
- i := brkSave [brkN] ;
- WHILE i < exitCnt DO
- SetPatTgt (exitPatch [i], pc) ;
- INC (i)
- END ;
- exitCnt := brkSave [brkN]
- ELSIF tok = TkFor THEN
- (* control variable *)
- Skip () ; (* after the FOR keyword: skip blanks *)
- IF NOT Alpha (CurCh ()) THEN
- Err (EUnknown) ;
- RETURN
- END ;
- GetWord () ;
- IF NOT Search (wrd, idx) THEN
- Err (EUnknown) ;
- RETURN
- END ;
- IF symtab [idx].size > 2 THEN
- Err (ENoLib) ;
- RETURN
- END ;
- IF NOT MatchAssign () THEN
- Err (ENoSemi)
- END ;
- ParseExpr (src) ;
- LoadAtom (src) ;
- EmStoreVar (symtab [idx].local, symtab [idx].off,
- symtab [idx].size) ;
- IF NOT MatchKey (tok) THEN
- Err (ESimpType) ;
- RETURN
- END ;
- IF (tok = TkTo) OR (tok = TkDownto) THEN
- dow := (tok = TkDownto)
- ELSE
- Err (ESimpType) ;
- RETURN
- END ;
- ParseExpr (t) ;
- LoadAtom (t) ;
- EmPushAx () ; (* loop bound on the stack *)
- IF NOT (MatchKey (tok) AND (tok = TkDo)) THEN
- Err (ENoSemi)
- END ;
- brkSave [brkN] := exitCnt ;
- loopTy [brkN] := 2 ;
- INC (brkN) ;
- L1 := pc ; (* Ltest *)
- Statmnt () ;
- DEC (brkN) ;
- i := brkSave [brkN] ;
- WHILE i < exitCnt DO
- SetPatTgt (exitPatch [i], pc) ;
- INC (i)
- END ;
- exitCnt := brkSave [brkN] ;
- (* test then step: ax = var ; cx = bound (from [sp]) *)
- EmMovCxSp () ;
- EmLoadVar (symtab [idx].local, symtab [idx].off,
- symtab [idx].size) ;
- EmCmpAxCx () ;
- IF dow THEN
- zj := EmJcc (8CH, 0) (* JL -> done *)
- ELSE
- zj := EmJcc (8FH, 0) (* JG -> done *)
- END ;
- EmLoadVar (symtab [idx].local, symtab [idx].off,
- symtab [idx].size) ;
- IF dow THEN
- EmDecAx ()
- ELSE
- EmIncAx ()
- END ;
- EmStoreVar (symtab [idx].local, symtab [idx].off,
- symtab [idx].size) ;
- DropC (EmJmpNear (L1)) ;
- SetPatTgt (zj, pc) ; (* done: drop bound, continue *)
- EmAddSp (2)
- ELSIF tok = TkCase THEN
- ParseExpr (t) ;
- LoadAtom (t) ;
- EmPushAx () ; (* selector on the stack *)
- IF NOT (MatchKey (tok) AND (tok = TkOf)) THEN
- Err (ENoSemi)
- END ;
- caseN := 0 ;
- LOOP
- Skip () ;
- IF MatchDelim (';') THEN
- Skip ()
- END ;
- PeekKw (tok) ;
- IF (tok = TkEnd) OR (tok = TkElse) THEN
- EXIT
- END ;
- (* case label : constant identifier or literal *)
- IF Alpha (CurCh ()) THEN
- GetWord () ;
- IF Search (wrd, i2) AND (symtab [i2].tag = KConst) THEN
- lo := symtab [i2].lval
- ELSE
- Err (EUnknown) ;
- EXIT
- END
- ELSE
- RdConst (lo, clso, strf)
- END ;
- IF MatchRange () THEN
- RdConst (hi, clso, strf)
- ELSE
- hi := lo
- END ;
- ExpectDelim (':', ENoSemi) ;
- EmMovAxSp () ;
- EmCmpAxi (W16 (lo)) ;
- zj := EmJcc (85H, 0) ; (* JNZ -> next *)
- IF hi # lo THEN
- EmCmpAxi (W16 (hi)) ;
- zj2 := EmJcc (85H, 0)
- ELSE
- zj2 := 0
- END ;
- Statmnt () ;
- IF caseN >= 64 THEN
- Err (ECompOvf) ;
- EXIT
- END ;
- caseJmp [caseN] := EmJmpNear (0) ;
- INC (caseN) ;
- SetPatTgt (zj, pc) ;
- IF zj2 # 0 THEN
- SetPatTgt (zj2, pc)
- END
- END ;
- IF tok = TkElse THEN
- DropB (MatchKey (tok)) ;
- Statmnt () ;
- IF NOT (MatchKey (tok) AND (tok = TkEnd)) THEN
- Err (ENoSemi)
- END
- ELSE
- DropB (MatchKey (tok))
- END ;
- EmAddSp (2) ;
- FOR i := 0 TO caseN - 1 DO
- SetPatTgt (caseJmp [i], pc)
- END
- ELSIF tok = TkGoto THEN
- v := 0 ;
- Skip () ; (* after the GOTO keyword: skip blanks *)
- IF Digit (CurCh ()) THEN
- RdIntConst (v) ;
- NumToName (W16 (v), nm) ;
- IF Search (nm, idx) THEN
- IF symtab [idx].tag = KLabel THEN
- IF symtab [idx].defnd THEN
- DropC (EmJmpNear (symtab [idx].goPos))
- ELSE
- zj := EmJmpNear (0) ;
- AddPend (0, idx, zj)
- END
- ELSE
- Err (EUnknown)
- END
- ELSE
- idx := NewSym (nm, KLabel, TScalar, 0, 0, 0, 0, FALSE) ;
- zj := EmJmpNear (0) ;
- AddPend (0, idx, zj)
- END
- ELSE
- Err (EUnknown)
- END
- ELSIF tok = TkExit THEN
- IF brkN = 0 THEN
- Err (EUnknown)
- ELSE
- IF loopTy [brkN - 1] = 2 THEN
- EmAddSp (2) (* drop FOR bound *)
- END ;
- zj := EmJmpNear (0) ;
- IF exitCnt < 64 THEN
- exitPatch [exitCnt] := zj ;
- INC (exitCnt)
- END
- END
- ELSIF tok = TkWith THEN
- Err (ENoLib)
- ELSE
- (* identifier statement: assignment or call *)
- IF NOT Search (wrd, idx) THEN
- Err (EUnknown) ;
- RETURN
- END ;
- IF symtab [idx].tag = KBuiltin THEN
- IoCall (idx) ;
- RETURN
- END ;
- IF symtab [idx].tag = KProc THEN
- IF MatchDelim ('(') THEN
- ParseCallArgs (idx)
- ELSE
- ParseCall (idx)
- END ;
- RETURN
- END ;
- IF (symtab [idx].tag = KVar) OR (symtab [idx].tag = KFunc) THEN
- t.idx := idx ;
- t.kind := 1 ;
- t.boff := 0 ;
- t.cls := symtab [idx].cls ;
- IF symtab [idx].tag = KFunc THEN
- t.idx := symtab [idx].resvar ;
- t.cls := symtab [t.idx].cls
- END ;
- ParseSub (t) ;
- IF MatchAssign () THEN
- Assignment (t) ;
- RETURN
- END ;
- Err (ENoSemi) ;
- RETURN
- END ;
- Err (ENoSemi)
- END
- END Statmnt ;
- (* ---------------------------------------------------------------- *)
- (* types and declarations (TPSRC7) *)
- (* ---------------------------------------------------------------- *)
- PROCEDURE ParseType (VAR cls, size, elem : CARDINAL) ;
- VAR tok : CARDINAL ;
- idx : CARDINAL ;
- lo, hi : LONGINT ;
- s2, e2 : CARDINAL ;
- subcls : CARDINAL ;
- strf : BOOLEAN ;
- consumed : BOOLEAN ;
- BEGIN
- cls := TNone ; size := 0 ; elem := 0 ;
- consumed := FALSE ;
- Skip () ; (* after ':' / '=' : skip blanks *)
- IF Alpha (CurCh ()) THEN
- DropB (MatchKey (tok)) ;
- consumed := TRUE
- ELSE
- tok := TkNone
- END ;
- IF tok = TkArray THEN
- ExpectDelim ('[', ENoSemi) ;
- RdConst (lo, subcls, strf) ;
- IF NOT MatchRange () THEN
- Err (ESimpType)
- END ;
- RdConst (hi, subcls, strf) ;
- ExpectDelim (']', ENoSemi) ;
- IF NOT (MatchKey (tok) AND (tok = TkOf)) THEN
- Err (ENoSemi)
- END ;
- ParseType (cls, s2, e2) ;
- cls := TArray ;
- elem := s2 ;
- size := s2 * (W16 (VAL (LONGINT, W16 (hi))
- - VAL (LONGINT, W16 (lo)) + 1))
- ELSIF tok = TkString THEN
- cls := TString ;
- size := 256 ;
- elem := 1 ;
- IF MatchDelim ('[') THEN
- RdConst (hi, subcls, strf) ;
- ExpectDelim (']', ENoSemi) ;
- size := W16 (hi) + 1
- END
- ELSIF tok = TkSet THEN
- Err (ENoLib) ;
- IF MatchKey (tok) AND (tok = TkOf) THEN
- ParseType (cls, s2, e2)
- END
- ELSIF tok = TkRecord THEN
- Err (ENoLib) ;
- LOOP
- PeekKw (tok) ;
- IF tok = TkEnd THEN
- DropB (MatchKey (tok)) ;
- EXIT
- END ;
- IF CurCh () = 0C THEN
- EXIT
- END ;
- Skip () ;
- IF Alpha (CurCh ()) THEN
- DropCh (GetCh ())
- ELSE
- DropCh (GetCh ())
- END
- END
- ELSIF (tok = TkFile) OR (tok = TkText) THEN
- cls := TFile ;
- size := 0 ;
- elem := 0 ;
- Err (ENoLib)
- ELSE
- IF consumed THEN
- IF Search (wrd, idx) AND (symtab [idx].tag = KType) THEN
- cls := symtab [idx].cls ;
- size := symtab [idx].size ;
- elem := symtab [idx].size
- ELSE
- Err (EUnknown)
- END
- ELSE
- (* subrange lo .. hi *)
- RdConst (lo, subcls, strf) ;
- IF NOT MatchRange () THEN
- Err (ESimpType) ;
- RETURN
- END ;
- RdConst (hi, subcls, strf) ;
- cls := TScalar ;
- size := 2 ;
- elem := 2
- END
- END
- END ParseType ;
- PROCEDURE DefVar () ;
- (* 'name' (',' name)* ':' type [ABSOLUTE addr] ; group repeated until a
- declaration keyword appears *)
- VAR nm : ARRAY [0..MaxName] OF CHAR ;
- cls, size, elem : CARDINAL ;
- tok : CARDINAL ;
- v : LONGINT ;
- off : CARDINAL ;
- BEGIN
- LOOP
- Skip () ; (* after the VAR keyword: skip blanks *)
- IF NOT Alpha (CurCh ()) THEN
- Err (EUnknown) ;
- RETURN
- END ;
- LOOP
- GetWord () ;
- SaveWord (nm) ;
- DupTest (nm) ;
- IF NOT MatchDelim (':') THEN
- Err (ENoSemi)
- END ;
- ParseType (cls, size, elem) ;
- off := 0 ;
- IF lexnest = 0 THEN
- IF size > 2 THEN
- Err (ENoLib) ;
- RETURN
- END ;
- PeekKw (tok) ;
- IF tok = TkAbsolute THEN
- DropB (MatchKey (tok)) ;
- IF (CurCh () = '$') OR (Digit (CurCh ())) THEN
- RdIntConst (v) ;
- off := W16 (v)
- ELSE
- Err (EUnknown)
- END
- ELSE
- off := dc ;
- dc := dc + size
- END ;
- DropC (NewSym (nm, KVar, cls, size, elem, off, 0, FALSE)) ;
- varspc := varspc + size
- ELSE
- IF size > 2 THEN
- Err (ENoLib) ;
- RETURN
- END ;
- DropC (NewSym (nm, KVar, cls, size, elem, locFree, 0, TRUE)) ;
- locFree := (locFree - size) MOD 10000H ;
- locBytes := locBytes + size
- END ;
- IF NOT MatchDelim (',') THEN
- EXIT
- END
- END ;
- IF NOT MatchDelim (';') THEN
- Err (ENoSemi) ;
- RETURN
- END ;
- PeekKw (tok) ;
- IF tok # TkNone THEN
- RETURN
- END
- END
- END DefVar ;
- PROCEDURE DefConst () ;
- VAR nm : ARRAY [0..MaxName] OF CHAR ;
- v : LONGINT ;
- cls : CARDINAL ;
- isStr : BOOLEAN ;
- tok : CARDINAL ;
- BEGIN
- LOOP
- PeekKw (tok) ;
- IF tok # TkNone THEN
- RETURN
- END ;
- Skip () ; (* after the CONST keyword: skip blanks *)
- IF NOT Alpha (CurCh ()) THEN
- Err (EUnknown) ;
- RETURN
- END ;
- GetWord () ;
- SaveWord (nm) ;
- DupTest (nm) ;
- ExpectDelim ('=', ENoSemi) ;
- RdConst (v, cls, isStr) ;
- IF isStr THEN
- Err (ENoLib)
- END ;
- DropC (NewSym (nm, KConst, cls, 0, 0, 0, v, FALSE)) ;
- IF NOT MatchDelim (';') THEN
- Err (ENoSemi) ;
- RETURN
- END
- END
- END DefConst ;
- PROCEDURE DefLabelPart () ;
- VAR nm : ARRAY [0..9] OF CHAR ;
- n : CARDINAL ;
- BEGIN
- LOOP
- Skip () ;
- IF NOT Digit (CurCh ()) THEN
- Err (EUnknown) ;
- RETURN
- END ;
- n := 0 ;
- WHILE Digit (CurCh ()) DO
- n := n * 10 + (ORD (CurCh ()) - ORD ('0')) ;
- DropCh (GetCh ())
- END ;
- NumToName (n, nm) ;
- IF NOT Search (nm, n) THEN
- DropC (NewSym (nm, KLabel, TScalar, 0, 0, 0, 0, FALSE))
- END ;
- IF NOT MatchDelim (',') THEN
- IF MatchDelim (';') THEN
- RETURN
- END ;
- Err (ENoSemi) ;
- RETURN
- END
- END
- END DefLabelPart ;
- PROCEDURE IfMatchSemi () ;
- BEGIN
- IF NOT MatchDelim (';') THEN
- Err (ENoSemi)
- END
- END IfMatchSemi ;
- PROCEDURE SymEpi () ;
- (* function result: AX := result var *)
- BEGIN
- IF curIsFunc THEN
- IF OK () THEN
- EmLoadVar (TRUE, symtab [resultVar].off, symtab [resultVar].size)
- END
- END
- END SymEpi ;
- PROCEDURE ProcFunc () ;
- (* PROCEDURE name (params) ; body | FUNCTION name (params) : type ; body *)
- VAR nm : ARRAY [0..MaxName] OF CHAR ;
- idx, old : CARDINAL ;
- tok, tok2 : CARDINAL ;
- parmNm : ARRAY [0..MaxName] OF CHAR ;
- cls, size, elem : CARDINAL ;
- isFunc : BOOLEAN ;
- saveNest, saveLoc, saveRes, saveF : CARDINAL ;
- saveLB, savePO : CARDINAL ;
- i : CARDINAL ;
- BEGIN
- isFunc := curIsFunc ;
- Skip () ; (* after the PROCEDURE/FUNCTION keyword *)
- IF NOT Alpha (CurCh ()) THEN
- Err (EUnknown) ;
- RETURN
- END ;
- GetWord () ;
- SaveWord (nm) ;
- IF Search (nm, idx) AND (symtab [idx].tag = KProc)
- AND (symtab [idx].fwd) THEN
- old := idx
- ELSIF Search (nm, idx) THEN
- Err (EUnknown) ;
- RETURN
- ELSE
- old := NewSym (nm, KProc, TNone, 0, 0, 0, 0, FALSE)
- END ;
- IF isFunc THEN
- symtab [old].tag := KFunc
- END ;
- saveNest := lexnest ;
- saveLoc := locFree ;
- saveRes := resultVar ;
- saveF := VAL (CARDINAL, ORD (curIsFunc)) ;
- saveLB := locBytes ;
- savePO := parmOff ;
- INC (lexnest) ;
- locFree := 0FFFEH ;
- locBytes := 0 ;
- parmOff := 4 ;
- IF MatchDelim ('(') THEN
- IF CurCh () # ')' THEN
- LOOP
- (* PeekKw, not MatchKey: MatchKey CONSUMES the word it reads, so
- using it to test for VAR would eat the parameter's name. *)
- PeekKw (tok) ;
- IF tok = TkVar THEN
- (* VAR parameter recorded as value in this milestone *)
- DropB (MatchKey (tok))
- END ;
- Skip () ; (* blanks before the parameter name *)
- IF NOT Alpha (CurCh ()) THEN
- Err (EUnknown) ;
- RETURN
- END ;
- GetWord () ;
- SaveWord (parmNm) ;
- DupTest (parmNm) ;
- ExpectDelim (':', ENoSemi) ; (* formal is 'name : type' *)
- ParseType (cls, size, elem) ;
- IF size > 2 THEN
- Err (ENoLib) ;
- RETURN
- END ;
- DropC (NewSym (parmNm, KVar, cls, size, elem, parmOff, 0, TRUE)) ;
- parmOff := parmOff + 2 ;
- IF NOT MatchDelim (',') THEN
- IF MatchDelim (')') THEN
- EXIT
- END ;
- Err (ENoSemi) ;
- EXIT
- END
- END
- ELSE
- DropCh (GetCh ())
- END
- END ;
- IF isFunc THEN
- IF MatchDelim (':') THEN
- ParseType (cls, size, elem)
- ELSE
- cls := TScalar ;
- size := 2 ;
- elem := 2
- END ;
- IF size > 2 THEN
- Err (ENoLib) ;
- RETURN
- END ;
- symtab [old].cls := cls ;
- symtab [old].size := size ;
- resultVar := NewSym (nm, KVar, cls, size, elem, locFree, 0, TRUE) ;
- symtab [old].resvar := resultVar ;
- locFree := (locFree - size) MOD 10000H ;
- locBytes := locBytes + size
- END ;
- IfMatchSemi () ;
- PeekKw (tok2) ;
- IF (tok2 = TkForward) OR (tok2 = TkExternal) THEN
- DropB (MatchKey (tok2)) ;
- symtab [old].fwd := TRUE ;
- symtab [old].defnd := (tok2 = TkExternal) ;
- IfMatchSemi () ;
- lexnest := saveNest ;
- locFree := saveLoc ;
- resultVar := saveRes ;
- curIsFunc := (saveF # 0) ;
- locBytes := saveLB ;
- parmOff := savePO ;
- RETURN
- END ;
- (* body *)
- symtab [old].goPos := pc ;
- symtab [old].defnd := TRUE ;
- EmPushBp () ;
- EmMovBpSp () ;
- DefPart () ; (* nested declarations; stops at BEGIN *)
- IF locBytes > 0 THEN
- EmSubSp (locBytes)
- END ;
- DropC (EmCall (TU_StackChk)) ;
- Statmnt () ; (* body *)
- IF OK () THEN
- SymEpi () ;
- EmLeave () ;
- EmRet ()
- END ;
- (* patch pending forward calls to this proc *)
- i := 0 ;
- WHILE i < nPend DO
- IF (pend [i].kind = 1) AND (pend [i].who = old) THEN
- SetPatTgt (pend [i].place, symtab [old].goPos) ;
- pend [i].kind := 99
- END ;
- INC (i)
- END ;
- lexnest := saveNest ;
- locFree := saveLoc ;
- resultVar := saveRes ;
- curIsFunc := (saveF # 0) ;
- locBytes := saveLB ;
- parmOff := savePO
- END ProcFunc ;
- PROCEDURE DefType () ;
- (* 'name' '=' typeDef ; ... until a declaration keyword appears *)
- VAR nm : ARRAY [0..MaxName] OF CHAR ;
- cls, size, elem : CARDINAL ;
- tok : CARDINAL ;
- BEGIN
- LOOP
- PeekKw (tok) ;
- IF tok # TkNone THEN
- RETURN
- END ;
- Skip () ; (* after the TYPE keyword: skip blanks *)
- IF NOT Alpha (CurCh ()) THEN
- Err (EUnknown) ;
- RETURN
- END ;
- GetWord () ;
- SaveWord (nm) ;
- DupTest (nm) ;
- ExpectDelim ('=', ENoSemi) ;
- ParseType (cls, size, elem) ;
- IF NOT OK () THEN
- RETURN
- END ;
- DropC (NewSym (nm, KType, cls, size, elem, 0, 0, FALSE)) ;
- IF NOT MatchDelim (';') THEN
- Err (ENoSemi) ;
- RETURN
- END
- END
- END DefType ;
- PROCEDURE DefPart () ;
- (* LABEL / CONST / TYPE / VAR / OVERLAY / PROC / FUNCTION / BEGIN *)
- VAR tok : CARDINAL ;
- BEGIN
- LOOP
- IF MatchDelim (';') THEN
- (* separator between declarations *)
- ELSE
- PeekKw (tok) ;
- IF tok = TkBegin THEN
- RETURN
- END ;
- IF NOT MatchKey (tok) THEN
- Err (EUnknown) ;
- RETURN
- END ;
- CASE tok OF
- TkLabel : DefLabelPart () ;
- | TkConst : DefConst () ;
- | TkType : DefType () ;
- | TkVar : DefVar () ;
- | TkOverlay :
- LOOP
- Skip () ;
- IF CurCh () = ';' THEN
- DropCh (GetCh ()) ;
- EXIT
- END ;
- IF CurCh () = 0C THEN
- Err (ENoSemi) ;
- EXIT
- END ;
- DropCh (GetCh ())
- END ;
- | TkProcedure : curIsFunc := FALSE ; ProcFunc () ;
- | TkFunction : curIsFunc := TRUE ; ProcFunc () ;
- ELSE
- Err (EUnknown) ;
- RETURN
- END ;
- IF NOT OK () THEN
- RETURN
- END
- END
- END
- END DefPart ;
- (* ---------------------------------------------------------------- *)
- (* driver (TPSRC7 compile) *)
- (* ---------------------------------------------------------------- *)
- PROCEDURE DefBuiltins () ;
- (* The standard procedures. Without these, WRITELN is absent from the
- symbol table, Statmnt's identifier branch fails its Search and every
- program that prints anything dies with EUnknown (41) on the '(' after the
- call name - the single remaining cause of failure in the fixture matrix.
- Tagged KBuiltin (not KProc) because these are not called generically:
- WRITE/WRITELN/READ/READLN need TP3's per-argument type dispatch
- (IoCall), and HALT takes no argument at all. defnd is TRUE because the
- entry point is known - there is no forward reference to patch. *)
- VAR i : CARDINAL ;
- BEGIN
- i := NewSym ("WRITE" , KBuiltin, BI_Write , 0, 0, 0, 0, FALSE) ;
- symtab [i].defnd := TRUE ;
- symtab [i].goPos := TU_WrInt ;
- i := NewSym ("WRITELN", KBuiltin, BI_WriteLn, 0, 0, 0, 0, FALSE) ;
- symtab [i].defnd := TRUE ;
- symtab [i].goPos := TU_WrInt ;
- i := NewSym ("READ" , KBuiltin, BI_Read , 0, 0, 0, 0, FALSE) ;
- symtab [i].defnd := TRUE ;
- symtab [i].goPos := TU_RdInt ;
- i := NewSym ("READLN" , KBuiltin, BI_ReadLn , 0, 0, 0, 0, FALSE) ;
- symtab [i].defnd := TRUE ;
- symtab [i].goPos := TU_RdInt ;
- i := NewSym ("HALT" , KBuiltin, BI_Halt , 0, 0, 0, 0, FALSE) ;
- symtab [i].defnd := TRUE ;
- symtab [i].goPos := TU_Halt
- END DefBuiltins ;
- PROCEDURE Inittur () ;
- (* reset compiler state and define the standard types *)
- BEGIN
- abortFac := FALSE ;
- errNum := 0 ;
- txerrPos := 0 ;
- srcPos := 0 ;
- srcLen := Length () ;
- pc := 0 ;
- dc := 100H ;
- strTop := 0 ;
- strCnt := 0 ;
- rdStrX := 0 ;
- varspc := 0 ;
- symTop := 0 ;
- nPatch := 0 ;
- nPend := 0 ;
- exitCnt := 0 ;
- brkN := 0 ;
- caseN := 0 ;
- lexnest := 0 ;
- curIsFunc := FALSE ;
- resultVar := 0 ;
- locFree := 0FFFEH ;
- locBytes := 0 ;
- parmOff := 4 ;
- dirs.rng := TRUE ;
- dirs.chk := TRUE ;
- InitKeys () ;
- DropC (NewSym ("INTEGER", KType, TScalar, 2, 2, 0, 0, FALSE)) ;
- DropC (NewSym ("BYTE" , KType, TScalar, 1, 1, 0, 0, FALSE)) ;
- DropC (NewSym ("CHAR" , KType, TScalar, 1, 1, 0, 0, FALSE)) ;
- DropC (NewSym ("BOOLEAN", KType, TBool , 1, 1, 0, 0, FALSE)) ;
- DropC (NewSym ("REAL" , KType, TReal , 6, 6, 0, 0, FALSE)) ;
- DropC (NewSym ("STRING" , KType, TString, 256, 1, 0, 0, FALSE)) ;
- DropC (NewSym ("TRUE" , KConst, TBool, 1, 1, 0, 1, FALSE)) ;
- DropC (NewSym ("FALSE" , KConst, TBool, 1, 1, 0, 0, FALSE)) ;
- DefBuiltins () ;
- tmpA := NewSym ("@@T1", KVar, TScalar, 2, 2, dc, 0, FALSE) ;
- dc := dc + 2 ;
- tmpB := NewSym ("@@T2", KVar, TScalar, 2, 2, dc, 0, FALSE) ;
- dc := dc + 2
- END Inittur ;
- PROCEDURE HeadWord (VAR slot : CARDINAL) ;
- BEGIN
- slot := pc ;
- Eword (0)
- END HeadWord ;
- PROCEDURE Compile (VAR errNo, errPos : CARDINAL) : BOOLEAN ;
- VAR tok : CARDINAL ;
- BEGIN
- Inittur () ;
- IF OK () THEN
- (* program prologue: header words, CALL initmem, MOV BP,SP *)
- HeadWord (hdrFlag) ;
- HeadWord (hdrCS) ;
- HeadWord (hdrDS) ;
- HeadWord (hdrHeap) ;
- HeadWord (hdrMax) ;
- Eword (16) ; (* max open files *)
- Eword (0) ; (* input buffer word *)
- Eword (0) ; (* output buffer word *)
- DropC (EmCall (TU_InitMem)) ;
- EmMovBpSp () ;
- IF MatchKey (tok) AND (tok = TkProgram) THEN
- (* MatchKey stops right after "PROGRAM", so the optional program
- name normally follows blanks. Skip them before testing for the
- name: otherwise Alpha sees the blank, the name is never consumed
- and IfMatchSemi reports ENoSemi at the name. *)
- Skip () ;
- IF Alpha (CurCh ()) THEN
- GetWord ()
- END ;
- IF MatchDelim ('(') THEN
- WHILE NOT MatchDelim (')') DO
- IF Alpha (CurCh ()) THEN
- GetWord ()
- END ;
- IF CurCh () = ',' THEN
- DropCh (GetCh ())
- END
- END
- END ;
- IfMatchSemi ()
- END ;
- IF OK () THEN
- DefPart () ;
- IF OK () THEN
- IF MatchKey (tok) AND (tok = TkBegin) THEN
- Compound () ;
- IF OK () THEN
- EmXorAxAx () ;
- DropC (EmCall (TU_ProgEnd)) ;
- ResolvePatches () ;
- codeSz := pc ;
- dataSz := dc ;
- cbuf [hdrCS] := VAL (BYTE, (codeSz DIV 16) MOD 100H) ;
- cbuf [hdrCS + 1] := VAL (BYTE, ((codeSz DIV 16) DIV 100H) MOD 100H) ;
- cbuf [hdrDS] := VAL (BYTE, (dataSz DIV 16) MOD 100H) ;
- cbuf [hdrDS + 1] := VAL (BYTE, ((dataSz DIV 16) DIV 100H) MOD 100H) ;
- cbuf [hdrFlag] := 1 ;
- cbuf [hdrFlag + 1] := 0 ;
- cbuf [hdrHeap] := 0 ;
- cbuf [hdrHeap + 1] := 0 ;
- cbuf [hdrMax] := 0 ;
- cbuf [hdrMax + 1] := 0
- END
- ELSE
- Err (EUnknown)
- END
- END
- END
- END ;
- IF NOT MatchDelim ('.') THEN
- Err (EPointExp)
- END ;
- IF abortFac THEN
- errNo := errNum ; (* was "errNo := errNo": a self-assignment,
- because the formal shadowed the module
- variable, so the error code always
- reached the caller as 0 *)
- errPos := txerrPos ;
- RETURN FALSE
- END ;
- errNo := 0 ;
- errPos := 0 ;
- RETURN TRUE
- END Compile ;
- PROCEDURE CodeBytes () : CARDINAL ;
- BEGIN
- RETURN codeSz
- END CodeBytes ;
- PROCEDURE DataBytes () : CARDINAL ;
- BEGIN
- RETURN dataSz
- END DataBytes ;
- PROCEDURE CodeByteAt (i : CARDINAL) : BYTE ;
- (* i-th byte of the emitted image, for test harnesses that need to check
- the generated 8086 code rather than just its size. Returns 0 past the
- end of the image. *)
- BEGIN
- IF i >= codeSz THEN
- RETURN 0
- END ;
- RETURN cbuf [i]
- END CodeByteAt ;
- END Compiler.
|