| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821 | {    Copyright (c) 1998-2002 by Florian Klaempfl    This unit implements the scanner part and handling of the switches    This program is free software; you can redistribute it and/or modify    it under the terms of the GNU General Public License as published by    the Free Software Foundation; either version 2 of the License, or    (at your option) any later version.    This program is distributed in the hope that it will be useful,    but WITHOUT ANY WARRANTY; without even the implied warranty of    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    GNU General Public License for more details.    You should have received a copy of the GNU General Public License    along with this program; if not, write to the Free Software    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ****************************************************************************}unit scanner;{$i fpcdefs.inc}interface    uses       cclasses,       globtype,globals,version,tokens,       verbose,comphook,       finput,       widestr;    const       max_include_nesting=32;       max_macro_nesting=16;       preprocbufsize=32*1024;    type       tcommentstyle = (comment_none,comment_tp,comment_oldtp,comment_delphi,comment_c);       tscannerfile = class;       preproctyp = (pp_ifdef,pp_ifndef,pp_if,pp_ifopt,pp_else,pp_elseif);       tpreprocstack = class          typ     : preproctyp;          accept  : boolean;          next    : tpreprocstack;          name    : stringid;          line_nb : longint;          owner   : tscannerfile;          constructor Create(atyp:preproctyp;a:boolean;n:tpreprocstack);       end;       tdirectiveproc=procedure;       tdirectiveitem = class(TNamedIndexItem)       public          is_conditional : boolean;          proc : tdirectiveproc;          constructor Create(const n:string;p:tdirectiveproc);          constructor CreateCond(const n:string;p:tdirectiveproc);       end;       tcompile_time_predicate = function(var valuedescr: String) : Boolean;       tscannerfile = class       public          inputfile    : tinputfile;  { current inputfile list }          inputfilecount : longint;          inputbuffer,                { input buffer }          inputpointer : pchar;          inputstart   : longint;          line_no,                    { line }          lastlinepos  : longint;          lasttokenpos : longint;     { token }          lasttoken,          nexttoken    : ttoken;          comment_level,          yylexcount     : longint;          lastasmgetchar : char;          ignoredirectives : tstringlist; { ignore directives, used to give warnings only once }          preprocstack   : tpreprocstack;          in_asm_string  : boolean;          preproc_pattern : string;          preproc_token   : ttoken;          constructor Create(const fn:string);          destructor Destroy;override;        { File buffer things }          function  openinputfile:boolean;          procedure closeinputfile;          function  tempopeninputfile:boolean;          procedure tempcloseinputfile;          procedure saveinputfile;          procedure restoreinputfile;          procedure firstfile;          procedure nextfile;          procedure addfile(hp:tinputfile);          procedure reload;          procedure insertmacro(const macname:string;p:pchar;len,line,fileindex:longint);        { Scanner things }          procedure gettokenpos;          procedure inc_comment_level;          procedure dec_comment_level;          procedure illegal_char(c:char);          procedure end_of_file;          procedure checkpreprocstack;          procedure poppreprocstack;          procedure ifpreprocstack(atyp : preproctyp;compile_time_predicate:tcompile_time_predicate;messid:longint);          procedure elseifpreprocstack(compile_time_predicate:tcompile_time_predicate);          procedure elsepreprocstack;          procedure handleconditional(p:tdirectiveitem);          procedure handledirectives;          procedure linebreak;          procedure readchar;          procedure readstring;          procedure readnumber;          function  readid:string;          function  readval:longint;          function  readval_asstring:string;          function  readcomment:string;          function  readquotedstring:string;          function  readstate:char;          function  readstatedefault:char;          procedure skipspace;          procedure skipuntildirective;          procedure skipcomment;          procedure skipdelphicomment;          procedure skipoldtpcomment;          procedure readtoken;          function  readpreproc:ttoken;          function  asmgetcharstart : char;          function  asmgetchar:char;       end;{$ifdef PREPROCWRITE}       tpreprocfile=class         f   : text;         buf : pointer;         spacefound,         eolfound : boolean;         constructor create(const fn:string);         destructor  destroy;         procedure Add(const s:string);         procedure AddSpace;       end;{$endif PREPROCWRITE}    var        { read strings }        c              : char;        orgpattern,        pattern        : string;        patternw       : pcompilerwidestring;        { token }        token,                        { current token being parsed }        idtoken    : ttoken;          { holds the token if the pattern is a known word }        current_scanner : tscannerfile;  { current scanner in use }        aktcommentstyle : tcommentstyle; { needed to use read_comment from directives }{$ifdef PREPROCWRITE}        preprocfile     : tpreprocfile;  { used with only preprocessing }{$endif PREPROCWRITE}    type        tdirectivemode = (directive_all, directive_turbo, directive_mac);    procedure AddDirective(const s:string; dm: tdirectivemode; p:tdirectiveproc);    procedure AddConditional(const s:string; dm: tdirectivemode; p:tdirectiveproc);    procedure InitScanner;    procedure DoneScanner;    {To be called when the language mode is finally determined}    procedure ConsolidateMode;    Function SetCompileMode(const s:string; changeInit: boolean):boolean;implementation    uses      dos,      cutils,      systems,      switches,      symbase,symtable,symtype,symsym,symconst,symdef,defutil,      fmodule;    var      { dictionaries with the supported directives }      turbo_scannerdirectives : tdictionary;     { for other modes }      mac_scannerdirectives : tdictionary;       { for mode mac }{*****************************************************************************                              Helper routines*****************************************************************************}    const      { use any special name that is an invalid file name to avoid problems }      preprocstring : array [preproctyp] of string[7]        = ('$IFDEF','$IFNDEF','$IF','$IFOPT','$ELSE','$ELSEIF');    function is_keyword(const s:string):boolean;      var        low,high,mid : longint;      begin        if not (length(s) in [tokenlenmin..tokenlenmax]) or           not (s[1] in ['a'..'z','A'..'Z']) then         begin           is_keyword:=false;           exit;         end;        low:=ord(tokenidx^[length(s),s[1]].first);        high:=ord(tokenidx^[length(s),s[1]].last);        while low<high do         begin           mid:=(high+low+1) shr 1;           if pattern<tokeninfo^[ttoken(mid)].str then            high:=mid-1           else            low:=mid;         end;        is_keyword:=(pattern=tokeninfo^[ttoken(high)].str) and                    (tokeninfo^[ttoken(high)].keyword in aktmodeswitches);      end;    {To be called when the language mode is finally determined}    procedure ConsolidateMode;    begin      if m_mac in aktmodeswitches then        if current_module.is_unit and not assigned(current_module.globalmacrosymtable) then          begin            current_module.globalmacrosymtable:= tmacrosymtable.create(true);            current_module.globalmacrosymtable.next:= current_module.localmacrosymtable;            macrosymtablestack:=current_module.globalmacrosymtable;          end;    end;      Function SetCompileMode(const s:string; changeInit: boolean):boolean;      var        b : boolean;        oldaktmodeswitches : tmodeswitches;      begin        oldaktmodeswitches:=aktmodeswitches;        b:=true;        if s='DEFAULT' then          aktmodeswitches:=initmodeswitches        else         if s='DELPHI' then          aktmodeswitches:=delphimodeswitches        else         if s='TP' then          aktmodeswitches:=tpmodeswitches        else         if s='FPC' then          aktmodeswitches:=fpcmodeswitches        else         if s='OBJFPC' then          aktmodeswitches:=objfpcmodeswitches        else         if s='GPC' then          aktmodeswitches:=gpcmodeswitches        else         if s='MACPAS' then          aktmodeswitches:=macmodeswitches        else         b:=false;        if b and changeInit then          initmodeswitches := aktmodeswitches;        if b then         begin           { resolve all postponed switch changes }           if localswitcheschanged then             begin               aktlocalswitches:=nextaktlocalswitches;               localswitcheschanged:=false;             end;           { turn ansistrings on by default ? }           if (m_delphi in aktmodeswitches) then            begin              include(aktlocalswitches,cs_ansistrings);              if changeinit then               include(initlocalswitches,cs_ansistrings);            end           else            begin              exclude(aktlocalswitches,cs_ansistrings);              if changeinit then               exclude(initlocalswitches,cs_ansistrings);            end;           { support goto/label by default in delphi/tp7/mac modes }           if ([m_delphi,m_tp7,m_mac] * aktmodeswitches <> []) then             begin               include(aktmoduleswitches,cs_support_goto);               if changeinit then                 include(initmoduleswitches,cs_support_goto);             end;           { Default enum packing for delphi/tp7 }           if (m_tp7 in aktmodeswitches) or              (m_delphi in aktmodeswitches) then             aktpackenum:=1           else if (m_mac in aktmodeswitches) then             { compatible with Metrowerks Pascal }             aktpackenum:=2           else             aktpackenum:=4;           if changeinit then             initpackenum:=aktpackenum;{$ifdef i386}           { Default to intel assembler for delphi/tp7 on i386 }           if (m_delphi in aktmodeswitches) or              (m_tp7 in aktmodeswitches) then             aktasmmode:=asmmode_i386_intel;           if changeinit then             initasmmode:=aktasmmode;{$endif i386}            { Undefine old symbol }            if (m_delphi in oldaktmodeswitches) then              undef_system_macro('FPC_DELPHI')            else if (m_tp7 in oldaktmodeswitches) then              undef_system_macro('FPC_TP')            else if (m_objfpc in oldaktmodeswitches) then              undef_system_macro('FPC_OBJFPC')            else if (m_gpc in oldaktmodeswitches) then              undef_system_macro('FPC_GPC')            else if (m_mac in oldaktmodeswitches) then              undef_system_macro('FPC_MACPAS');            { define new symbol in delphi,objfpc,tp,gpc,macpas mode }            if (m_delphi in aktmodeswitches) then              def_system_macro('FPC_DELPHI')            else if (m_tp7 in aktmodeswitches) then              def_system_macro('FPC_TP')            else if (m_objfpc in aktmodeswitches) then              def_system_macro('FPC_OBJFPC')            else if (m_gpc in aktmodeswitches) then              def_system_macro('FPC_GPC')            else if (m_mac in aktmodeswitches) then              def_system_macro('FPC_MACPAS');         end;        SetCompileMode:=b;      end;{*****************************************************************************                           Conditional Directives*****************************************************************************}    procedure dir_else;      begin        current_scanner.elsepreprocstack;      end;    procedure dir_endif;      begin        current_scanner.poppreprocstack;      end;    function isdef(var valuedescr: String): Boolean;      var        hs    : string;        mac   : tmacro;      begin        current_scanner.skipspace;        hs:=current_scanner.readid;        valuedescr:= hs;        if hs='' then          Message(scan_e_error_in_preproc_expr);        mac:=tmacro(search_macro(hs));        if assigned(mac) then          mac.is_used:=true;        isdef:= assigned(mac) and mac.defined;      end;    procedure dir_ifdef;      begin        current_scanner.ifpreprocstack(pp_ifdef,@isdef,scan_c_ifdef_found);      end;    function isnotdef(var valuedescr: String): Boolean;      var        hs    : string;        mac   : tmacro;      begin        current_scanner.skipspace;        hs:=current_scanner.readid;        valuedescr:= hs;        if hs='' then          Message(scan_e_error_in_preproc_expr);        mac:=tmacro(search_macro(hs));        if assigned(mac) then          mac.is_used:=true;        isnotdef:= not (assigned(mac) and mac.defined);      end;    procedure dir_ifndef;      begin        current_scanner.ifpreprocstack(pp_ifndef,@isnotdef,scan_c_ifndef_found);      end;    function opt_check(var valuedescr: String): Boolean;      var        hs    : string;        state : char;      begin        opt_check:= false;        current_scanner.skipspace;        hs:=current_scanner.readid;        valuedescr:= hs;        if (length(hs)>1) then          Message1(scan_w_illegal_switch,hs)        else          begin            state:=current_scanner.ReadState;            if state in ['-','+'] then              opt_check:=CheckSwitch(hs[1],state)            else              Message(scan_e_error_in_preproc_expr);          end;      end;    procedure dir_ifopt;      begin        current_scanner.ifpreprocstack(pp_ifopt,@opt_check,scan_c_ifopt_found);      end;    procedure dir_libprefix;      var        s : string;      begin        current_scanner.skipspace;        if c <> '''' then          Message2(scan_f_syn_expected, '''', c);        s := current_scanner.readquotedstring;        stringdispose(outputprefix);        outputprefix := stringdup(s);        with current_module do         setfilename(paramfn^, paramallowoutput);      end;    procedure dir_libsuffix;      var        s : string;      begin        current_scanner.skipspace;        if c <> '''' then          Message2(scan_f_syn_expected, '''', c);        s := current_scanner.readquotedstring;        stringdispose(outputsuffix);        outputsuffix := stringdup(s);        with current_module do          setfilename(paramfn^, paramallowoutput);      end;    procedure dir_extension;      var        s : string;      begin        current_scanner.skipspace;        if c <> '''' then          Message2(scan_f_syn_expected, '''', c);        s := current_scanner.readquotedstring;        outputextension := '.'+s;        with current_module do          setfilename(paramfn^, paramallowoutput);      end;{Compile time expression type check----------------------------------Each subexpression returns its type to the caller, which then cando type check.  Since data types of compile time expressions isnot well defined, the type system does a best effort. The drawback isthat some errors might not be detected.Instead of returning a particular data type, a set of possible data typesare returned. This way ambigouos types can be handled.  For instance avalue of 1 can be both a boolean and and integer.Booleans--------The following forms of boolean values are supported:* C coded, that is 0 is false, non-zero is true.* TRUE/FALSE for mac style compile time variablesThus boolean mac compile time variables are always stored as TRUE/FALSE.When a compile time expression is evaluated, they are then translatedto C coded booleans (0/1), to simplify for the expression evaluator.Note that this scheme then also of support mac compile time variables whichare 0/1 but with a boolean meaning.The TRUE/FALSE format is new from 22 august 2005, but the above schememeans that units which is not recompiled, and thus storescompile time variables as the old format (0/1), continue to work.Short circuit evaluation------------------------For this to work, the part of a compile time expression which is shortcircuited, should not be evaluated, while it still should be parsed.Therefor there is a parameter eval, telling whether evaluation is needed.In case not, the value returned can be arbitrary.}    type      {Compile time expression types}      TCTEType = (ctetBoolean, ctetInteger, ctetString, ctetSet);      TCTETypeSet = set of TCTEType;    const      cteTypeNames : array[TCTEType] of string[10] = (        'BOOLEAN','INTEGER','STRING','SET');      {Subset of types which can be elements in sets.}      setElementTypes = [ctetBoolean, ctetInteger, ctetString];    function GetCTETypeName(t: TCTETypeSet): String;      var        i: TCTEType;      begin        result:= '';        for i:= Low(TCTEType) to High(TCTEType) do          if i in t then            if result = '' then              result:= cteTypeNames[i]            else              result:= result + ' or ' + cteTypeNames[i];      end;    procedure CTEError(actType, desiredExprType: TCTETypeSet; place: String);    begin      Message3(scan_e_compile_time_typeerror,               GetCTETypeName(desiredExprType),               GetCTETypeName(actType),               place              );    end;    function parse_compiler_expr(var compileExprType: TCTETypeSet):string;        function read_expr(var exprType: TCTETypeSet; eval : Boolean) : string; forward;        procedure preproc_consume(t : ttoken);        begin          if t<>current_scanner.preproc_token then            Message(scan_e_preproc_syntax_error);          current_scanner.preproc_token:=current_scanner.readpreproc;        end;        function preproc_substitutedtoken(var macroType: TCTETypeSet; eval : Boolean): string;                                { Currently this parses identifiers as well as numbers.          The result from this procedure can either be that the token          itself is a value, or that it is a compile time variable/macro,          which then is substituted for another value (for macros          recursivelly substituted).}        var          hs: string;          mac : tmacro;          macrocount,          len : integer;          numres : longint;          w: word;        begin          result := current_scanner.preproc_pattern;          if not eval then            exit;          mac:= nil;          { Substitue macros and compiler variables with their content/value.            For real macros also do recursive substitution. }          macrocount:=0;          repeat            mac:=tmacro(search_macro(result));            inc(macrocount);            if macrocount>max_macro_nesting then              begin                Message(scan_w_macro_too_deep);                break;              end;            if assigned(mac) and mac.defined then              if assigned(mac.buftext) then                begin                  if mac.buflen>255 then                    begin                      len:=255;                      Message(scan_w_macro_cut_after_255_chars);                    end                  else                    len:=mac.buflen;                  hs[0]:=char(len);                  move(mac.buftext^,hs[1],len);                  result:=upcase(hs);                  mac.is_used:=true;                end              else                begin                  Message1(scan_e_error_macro_lacks_value, result);                  break;                end            else              begin                  break;              end;            if mac.is_compiler_var then              break;          until false;          { At this point, result do contain the value. Do some decoding and            determine the type.}          val(result,numres,w);          if (w=0) then {It is an integer}            begin              if (numres = 0) or (numres = 1) then                macroType := [ctetInteger, ctetBoolean]              else                macroType := [ctetInteger];            end          else if assigned(mac) and (m_mac in aktmodeswitches) and (result='FALSE') then            begin              result:= '0';              macroType:= [ctetBoolean];            end          else if assigned(mac) and (m_mac in aktmodeswitches) and (result='TRUE') then            begin              result:= '1';              macroType:= [ctetBoolean];            end          else if (m_mac in aktmodeswitches) and                  (not assigned(mac) or not mac.defined) and                  (macrocount = 1) then            begin              {Errors in mode mac is issued here. For non macpas modes there is               more liberty, but the error will eventually be caught at a later stage.}              Message1(scan_e_error_macro_undefined, result);              macroType:= [ctetString]; {Just to have something}            end          else            macroType:= [ctetString];        end;        function read_factor(var factorType: TCTETypeSet; eval : Boolean) : string;        var           hs : string;           mac: tmacro;           srsym : tsym;           srsymtable : tsymtable;           l : longint;           w : integer;           hasKlammer: Boolean;           setElemType : TCTETypeSet;        begin           if current_scanner.preproc_token=_ID then             begin                if current_scanner.preproc_pattern='DEFINED' then                  begin                    factorType:= [ctetBoolean];                    preproc_consume(_ID);                    current_scanner.skipspace;                    if current_scanner.preproc_token =_LKLAMMER then                      begin                        preproc_consume(_LKLAMMER);                        current_scanner.skipspace;                        hasKlammer:= true;                      end                    else if (m_mac in aktmodeswitches) then                      hasKlammer:= false                    else                      Message(scan_e_error_in_preproc_expr);                    if current_scanner.preproc_token =_ID then                      begin                        hs := current_scanner.preproc_pattern;                        mac := tmacro(search_macro(hs));                        if assigned(mac) and mac.defined then                          begin                            hs := '1';                            mac.is_used:=true;                          end                        else                          hs := '0';                        read_factor := hs;                        preproc_consume(_ID);                        current_scanner.skipspace;                      end                    else                      Message(scan_e_error_in_preproc_expr);                    if hasKlammer then                      if current_scanner.preproc_token =_RKLAMMER then                        preproc_consume(_RKLAMMER)                      else                        Message(scan_e_error_in_preproc_expr);                  end                else                if (m_mac in aktmodeswitches) and (current_scanner.preproc_pattern='UNDEFINED') then                  begin                    factorType:= [ctetBoolean];                    preproc_consume(_ID);                    current_scanner.skipspace;                    if current_scanner.preproc_token =_ID then                      begin                        hs := current_scanner.preproc_pattern;                        mac := tmacro(search_macro(hs));                        if assigned(mac) then                          begin                            hs := '0';                            mac.is_used:=true;                          end                        else                          hs := '1';                        read_factor := hs;                        preproc_consume(_ID);                        current_scanner.skipspace;                      end                    else                      Message(scan_e_error_in_preproc_expr);                  end                else                if (m_mac in aktmodeswitches) and (current_scanner.preproc_pattern='OPTION') then                  begin                    factorType:= [ctetBoolean];                    preproc_consume(_ID);                    current_scanner.skipspace;                    if current_scanner.preproc_token =_LKLAMMER then                      begin                        preproc_consume(_LKLAMMER);                        current_scanner.skipspace;                      end                    else                      Message(scan_e_error_in_preproc_expr);                    if not (current_scanner.preproc_token = _ID) then                      Message(scan_e_error_in_preproc_expr);                    hs:=current_scanner.preproc_pattern;                    if (length(hs) > 1) then                      {This is allowed in Metrowerks Pascal}                      Message(scan_e_error_in_preproc_expr)                    else                      begin                        if CheckSwitch(hs[1],'+') then                          read_factor := '1'                        else                          read_factor := '0';                      end;                    preproc_consume(_ID);                    current_scanner.skipspace;                    if current_scanner.preproc_token =_RKLAMMER then                      preproc_consume(_RKLAMMER)                    else                      Message(scan_e_error_in_preproc_expr);                  end                else                if current_scanner.preproc_pattern='SIZEOF' then                  begin                    factorType:= [ctetInteger];                    preproc_consume(_ID);                    current_scanner.skipspace;                    if current_scanner.preproc_token =_LKLAMMER then                      begin                        preproc_consume(_LKLAMMER);                        current_scanner.skipspace;                      end                    else                      Message(scan_e_preproc_syntax_error);                    if eval then                      if searchsym(current_scanner.preproc_pattern,srsym,srsymtable) then                        begin                          l:=0;                          case srsym.typ of                            globalvarsym,                            localvarsym,                            paravarsym :                              l:=tabstractvarsym(srsym).getsize;                            typedconstsym :                              l:=ttypedconstsym(srsym).getsize;                            typesym:                              l:=ttypesym(srsym).restype.def.size;                            else                              Message(scan_e_error_in_preproc_expr);                          end;                          str(l,read_factor);                        end                      else                        Message1(sym_e_id_not_found,current_scanner.preproc_pattern);                    preproc_consume(_ID);                    current_scanner.skipspace;                    if current_scanner.preproc_token =_RKLAMMER then                      preproc_consume(_RKLAMMER)                    else                      Message(scan_e_preproc_syntax_error);                  end                else                if current_scanner.preproc_pattern='DECLARED' then                  begin                    factorType:= [ctetBoolean];                    preproc_consume(_ID);                    current_scanner.skipspace;                    if current_scanner.preproc_token =_LKLAMMER then                      begin                        preproc_consume(_LKLAMMER);                        current_scanner.skipspace;                      end                    else                      Message(scan_e_error_in_preproc_expr);                    if current_scanner.preproc_token =_ID then                      begin                        hs := upper(current_scanner.preproc_pattern);                        if searchsym(hs,srsym,srsymtable) then                          hs := '1'                        else                          hs := '0';                        read_factor := hs;                        preproc_consume(_ID);                        current_scanner.skipspace;                      end                    else                      Message(scan_e_error_in_preproc_expr);                    if current_scanner.preproc_token =_RKLAMMER then                      preproc_consume(_RKLAMMER)                    else                      Message(scan_e_error_in_preproc_expr);                  end                else                if current_scanner.preproc_pattern='NOT' then                  begin                    factorType:= [ctetBoolean];                    preproc_consume(_ID);                    hs:=read_factor(factorType, eval);                    if eval then                      begin                        if not (ctetBoolean in factorType) then                          CTEError(factorType, [ctetBoolean], 'NOT');                        val(hs,l,w);                        if l<>0 then                          read_factor:='0'                        else                          read_factor:='1';                      end                    else                      read_factor:='0'; {Just to have something}                  end                else                if (m_mac in aktmodeswitches) and (current_scanner.preproc_pattern='TRUE') then                  begin                    factorType:= [ctetBoolean];                    preproc_consume(_ID);                    read_factor:='1';                  end                else                if (m_mac in aktmodeswitches) and (current_scanner.preproc_pattern='FALSE') then                  begin                    factorType:= [ctetBoolean];                    preproc_consume(_ID);                    read_factor:='0';                  end                else                  begin                    hs:=preproc_substitutedtoken(factorType, eval);                    { Default is to return the original symbol }                    read_factor:=hs;                    if eval and (m_delphi in aktmodeswitches) and (ctetString in factorType) then                      if searchsym(current_scanner.preproc_pattern,srsym,srsymtable) then                        begin                          case srsym.typ of                            constsym :                              begin                                with tconstsym(srsym) do                                  begin                                    case consttyp of                                      constord :                                        begin                                          case consttype.def.deftype of                                            orddef:                                              begin                                                if is_integer(consttype.def) then                                                  begin                                                    read_factor:=tostr(value.valueord);                                                    factorType:= [ctetInteger];                                                  end                                                else if is_boolean(consttype.def) then                                                  begin                                                    read_factor:=tostr(value.valueord);                                                    factorType:= [ctetBoolean];                                                  end                                                else if is_char(consttype.def) then                                                  begin                                                    read_factor:=chr(value.valueord);                                                    factorType:= [ctetString];                                                  end                                              end;                                            enumdef:                                              begin                                                read_factor:=tostr(value.valueord);                                                factorType:= [ctetInteger];                                              end;                                          end;                                        end;                                      conststring :                                        begin                                          read_factor := upper(pchar(value.valueptr));                                          factorType:= [ctetString];                                        end;                                      constset :                                        begin                                          hs:=',';                                          for l:=0 to 255 do                                            if l in pconstset(tconstsym(srsym).value.valueptr)^ then                                              hs:=hs+tostr(l)+',';                                          read_factor := hs;                                          factorType:= [ctetSet];                                        end;                                    end;                                  end;                              end;                            enumsym :                              begin                                read_factor:=tostr(tenumsym(srsym).value);                                factorType:= [ctetInteger];                              end;                          end;                        end;                    preproc_consume(_ID);                    current_scanner.skipspace;                  end             end           else if current_scanner.preproc_token =_LKLAMMER then             begin                preproc_consume(_LKLAMMER);                read_factor:=read_expr(factorType, eval);                preproc_consume(_RKLAMMER);             end           else if current_scanner.preproc_token = _LECKKLAMMER then             begin               preproc_consume(_LECKKLAMMER);               read_factor := ',';               while current_scanner.preproc_token = _ID do               begin                 read_factor := read_factor+read_factor(setElemType, eval)+',';                 if current_scanner.preproc_token = _COMMA then                   preproc_consume(_COMMA);               end;               // TODO Add check of setElemType               preproc_consume(_RECKKLAMMER);               factorType:= [ctetSet];             end           else             Message(scan_e_error_in_preproc_expr);        end;        function read_term(var termType: TCTETypeSet; eval : Boolean) : string;        var           hs1,hs2 : string;           l1,l2 : longint;           w : integer;           termType2: TCTETypeSet;        begin          hs1:=read_factor(termType, eval);          repeat            if (current_scanner.preproc_token<>_ID) then              break;            if current_scanner.preproc_pattern<>'AND' then              break;            val(hs1,l1,w);            if l1=0 then              eval:= false; {Short circuit evaluation of OR}            if eval then               begin                {Check if first expr is boolean. Must be done here, after we know                 it is an AND expression.}                if not (ctetBoolean in termType) then                  CTEError(termType, [ctetBoolean], 'AND');                termType:= [ctetBoolean];              end;            preproc_consume(_ID);            hs2:=read_factor(termType2, eval);            if eval then              begin                if not (ctetBoolean in termType2) then                  CTEError(termType2, [ctetBoolean], 'AND');                val(hs2,l2,w);                if (l1<>0) and (l2<>0) then                  hs1:='1'                else                  hs1:='0';              end;           until false;           read_term:=hs1;        end;        function read_simple_expr(var simpleExprType: TCTETypeSet; eval : Boolean) : string;        var           hs1,hs2 : string;           l1,l2 : longint;           w : integer;           simpleExprType2: TCTETypeSet;        begin          hs1:=read_term(simpleExprType, eval);          repeat            if (current_scanner.preproc_token<>_ID) then              break;            if current_scanner.preproc_pattern<>'OR' then              break;            val(hs1,l1,w);            if l1<>0 then              eval:= false; {Short circuit evaluation of OR}            if eval then              begin                {Check if first expr is boolean. Must be done here, after we know                 it is an OR expression.}                if not (ctetBoolean in simpleExprType) then                  CTEError(simpleExprType, [ctetBoolean], 'OR');                simpleExprType:= [ctetBoolean];              end;            preproc_consume(_ID);            hs2:=read_term(simpleExprType2, eval);            if eval then              begin                if not (ctetBoolean in simpleExprType2) then                  CTEError(simpleExprType2, [ctetBoolean], 'OR');                val(hs2,l2,w);                if (l1<>0) or (l2<>0) then                  hs1:='1'                else                  hs1:='0';              end;          until false;          read_simple_expr:=hs1;        end;        function read_expr(var exprType: TCTETypeSet; eval : Boolean) : string;        var           hs1,hs2 : string;           b : boolean;           op : ttoken;           w : integer;           l1,l2 : longint;           exprType2: TCTETypeSet;        begin           hs1:=read_simple_expr(exprType, eval);           op:=current_scanner.preproc_token;           if (op = _ID) and (current_scanner.preproc_pattern = 'IN') then             op := _IN;           if not (op in [_IN,_EQUAL,_UNEQUAL,_LT,_GT,_LTE,_GTE]) then             begin                read_expr:=hs1;                exit;             end;           if (op = _IN) then             preproc_consume(_ID)           else             preproc_consume(op);           hs2:=read_simple_expr(exprType2, eval);           if eval then             begin               if op = _IN then                 begin                   if exprType2 <> [ctetSet] then                     CTEError(exprType2, [ctetSet], 'IN');                   if exprType = [ctetSet] then                     CTEError(exprType, setElementTypes, 'IN');                      if is_number(hs1) and is_number(hs2) then                    Message(scan_e_preproc_syntax_error)                  else if hs2[1] = ',' then                    b:=pos(','+hs1+',', hs2) > 0   { TODO For integer sets, perhaps check for numeric equivalence so that 0 = 00 }                  else                    Message(scan_e_preproc_syntax_error);                 end               else                 begin                   if (exprType * exprType2) = [] then                     CTEError(exprType2, exprType, tokeninfo^[op].str);                       if is_number(hs1) and is_number(hs2) then                     begin                       val(hs1,l1,w);                       val(hs2,l2,w);                       case op of                         _EQUAL :                           b:=l1=l2;                         _UNEQUAL :                           b:=l1<>l2;                         _LT :                           b:=l1<l2;                         _GT :                           b:=l1>l2;                         _GTE :                           b:=l1>=l2;                         _LTE :                           b:=l1<=l2;                       end;                     end                   else                     begin                       case op of                         _EQUAL :                           b:=hs1=hs2;                         _UNEQUAL :                           b:=hs1<>hs2;                         _LT :                           b:=hs1<hs2;                         _GT :                            b:=hs1>hs2;                         _GTE :                            b:=hs1>=hs2;                         _LTE :                           b:=hs1<=hs2;                       end;                     end;                 end;              end           else             b:= false; {Just to have something}           if b then             read_expr:='1'           else             read_expr:='0';           exprType:= [ctetBoolean];        end;     begin        current_scanner.skipspace;        { start preproc expression scanner }        current_scanner.preproc_token:=current_scanner.readpreproc;        parse_compiler_expr:=read_expr(compileExprType, true);     end;    function boolean_compile_time_expr(var valuedescr: String): Boolean;      var        hs : string;        exprType: TCTETypeSet;      begin        hs:=parse_compiler_expr(exprType);        if (exprType * [ctetBoolean]) = [] then          CTEError(exprType, [ctetBoolean], 'IF or ELSEIF');        boolean_compile_time_expr:= hs <> '0';        valuedescr:= hs;      end;    procedure dir_if;      begin        current_scanner.ifpreprocstack(pp_if,@boolean_compile_time_expr, scan_c_if_found);      end;    procedure dir_elseif;      begin        current_scanner.elseifpreprocstack(@boolean_compile_time_expr);      end;    procedure dir_define_impl(macstyle: boolean);      var        hs  : string;        bracketcount : longint;        mac : tmacro;        macropos : longint;        macrobuffer : pmacrobuffer;      begin        current_scanner.skipspace;        hs:=current_scanner.readid;        mac:=tmacro(search_macro(hs));        if not assigned(mac) or (mac.owner <> macrosymtablestack) then          begin            mac:=tmacro.create(hs);            mac.defined:=true;            Message1(parser_c_macro_defined,mac.name);            macrosymtablestack.insert(mac);          end        else          begin            Message1(parser_c_macro_defined,mac.name);            mac.defined:=true;            mac.is_compiler_var:=false;          { delete old definition }            if assigned(mac.buftext) then             begin               freemem(mac.buftext,mac.buflen);               mac.buftext:=nil;             end;          end;        mac.is_used:=true;        if (cs_support_macro in aktmoduleswitches) then          begin             { !!!!!! handle macro params, need we this? }             current_scanner.skipspace;             if not macstyle then               begin                 { may be a macro? }                 if c <> ':' then                   exit;                 current_scanner.readchar;                 if c <> '=' then                   exit;                 current_scanner.readchar;                 current_scanner.skipspace;               end;             { key words are never substituted }             if is_keyword(hs) then               Message(scan_e_keyword_cant_be_a_macro);             new(macrobuffer);             macropos:=0;             { parse macro, brackets are counted so it's possible               to have a $ifdef etc. in the macro }             bracketcount:=0;             repeat               case c of                 '}' :                   if (bracketcount=0) then                    break                   else                    dec(bracketcount);                 '{' :                   inc(bracketcount);                 #10,#13 :                   current_scanner.linebreak;                 #26 :                   current_scanner.end_of_file;               end;               macrobuffer^[macropos]:=c;               inc(macropos);               if macropos>=maxmacrolen then                 Message(scan_f_macro_buffer_overflow);               current_scanner.readchar;             until false;             { free buffer of macro ?}             if assigned(mac.buftext) then               freemem(mac.buftext,mac.buflen);             { get new mem }             getmem(mac.buftext,macropos);             mac.buflen:=macropos;             { copy the text }             move(macrobuffer^,mac.buftext^,macropos);             dispose(macrobuffer);          end        else          begin           { check if there is an assignment, then we need to give a             warning }             current_scanner.skipspace;             if c=':' then              begin                current_scanner.readchar;                if c='=' then                  Message(scan_w_macro_support_turned_off);              end;          end;      end;    procedure dir_define;      begin        dir_define_impl(false);      end;    procedure dir_definec;      begin        dir_define_impl(true);      end;    procedure dir_setc;      var        hs  : string;        mac : tmacro;        exprType: TCTETypeSet;        l : longint;        w : integer;      begin        current_scanner.skipspace;        hs:=current_scanner.readid;        mac:=tmacro(search_macro(hs));        if not assigned(mac) or (mac.owner <> macrosymtablestack) then          begin            mac:=tmacro.create(hs);            mac.defined:=true;            mac.is_compiler_var:=true;            Message1(parser_c_macro_defined,mac.name);            macrosymtablestack.insert(mac);          end        else          begin            mac.defined:=true;            mac.is_compiler_var:=true;          { delete old definition }            if assigned(mac.buftext) then             begin               freemem(mac.buftext,mac.buflen);               mac.buftext:=nil;             end;          end;        mac.is_used:=true;        { key words are never substituted }           if is_keyword(hs) then            Message(scan_e_keyword_cant_be_a_macro);         { !!!!!! handle macro params, need we this? }           current_scanner.skipspace;         { may be a macro? }        { assignment can be both := and = }        if c=':' then          current_scanner.readchar;        if c='=' then          begin             current_scanner.readchar;             hs:= parse_compiler_expr(exprType);             if (exprType * [ctetBoolean, ctetInteger]) = [] then               CTEError(exprType, [ctetBoolean, ctetInteger], 'SETC');             if length(hs) <> 0 then               begin                 {If we are absolutely shure it is boolean, translate                  to TRUE/FALSE to increase possibility to do future type check}                 if exprType = [ctetBoolean] then                   begin                     val(hs,l,w);                     if l<>0 then                       hs:='TRUE'                     else                       hs:='FALSE';                   end;                 Message2(parser_c_macro_set_to,mac.name,hs);                 { free buffer of macro ?}                 if assigned(mac.buftext) then                   freemem(mac.buftext,mac.buflen);                 { get new mem }                 getmem(mac.buftext,length(hs));                 mac.buflen:=length(hs);                 { copy the text }                 move(hs[1],mac.buftext^,mac.buflen);               end             else               Message(scan_e_preproc_syntax_error);          end        else          Message(scan_e_preproc_syntax_error);      end;    procedure dir_undef;      var        hs  : string;        mac : tmacro;      begin        current_scanner.skipspace;        hs:=current_scanner.readid;        mac:=tmacro(search_macro(hs));        if not assigned(mac) or (mac.owner <> macrosymtablestack) then          begin             mac:=tmacro.create(hs);             Message1(parser_c_macro_undefined,mac.name);             mac.defined:=false;             macrosymtablestack.insert(mac);          end        else          begin             Message1(parser_c_macro_undefined,mac.name);             mac.defined:=false;             mac.is_compiler_var:=false;             { delete old definition }             if assigned(mac.buftext) then               begin                  freemem(mac.buftext,mac.buflen);                  mac.buftext:=nil;               end;          end;        mac.is_used:=true;      end;    procedure dir_include;        function findincludefile(const path,name,ext:string;var foundfile:string):boolean;        var          found  : boolean;          hpath  : string;        begin         (* look for the include file           If path was specified as part of {$I } then            1. specified path (expanded with path of inputfile if relative)           else            1. path of current inputfile,current dir            2. local includepath            3. global includepath *)           found:=false;           foundfile:='';           hpath:='';           if path<>'' then             begin               if not path_absolute(path) then                 hpath:=current_scanner.inputfile.path^+path               else                 hpath:=path;               found:=FindFile(name+ext, hpath,foundfile);             end           else             begin               hpath:=current_scanner.inputfile.path^+';'+CurDirRelPath(source_info);               found:=FindFile(name+ext, hpath,foundfile);               if not found then                 found:=current_module.localincludesearchpath.FindFile(name+ext,foundfile);               if not found  then                 found:=includesearchpath.FindFile(name+ext,foundfile);             end;           findincludefile:=found;        end;      var        args,        foundfile,        hs    : string;        path  : dirstr;        name  : namestr;        ext   : extstr;        hp    : tinputfile;        found : boolean;      begin        current_scanner.skipspace;        args:=current_scanner.readcomment;        hs:=GetToken(args,' ');        if hs='' then         exit;        if (hs[1]='%') then         begin         { case insensitive }           hs:=upper(hs);         { remove %'s }           Delete(hs,1,1);           if hs[length(hs)]='%' then            Delete(hs,length(hs),1);         { save old }           path:=hs;         { first check for internal macros }           if hs='TIME' then            hs:=gettimestr           else            if hs='DATE' then             hs:=getdatestr           else            if hs='FILE' then             hs:=current_module.sourcefiles.get_file_name(aktfilepos.fileindex)           else            if hs='LINE' then             hs:=tostr(aktfilepos.line)           else            if hs='FPCVERSION' then             hs:=version_string           else            if hs='FPCDATE' then             hs:=date_string           else            if hs='FPCTARGET' then             hs:=target_cpu_string           else            if hs='FPCTARGETCPU' then             hs:=target_cpu_string           else            if hs='FPCTARGETOS' then             hs:=target_info.shortname           else             hs:=getenv(hs);           if hs='' then            Message1(scan_w_include_env_not_found,path);           { make it a stringconst }           hs:=''''+hs+'''';           current_scanner.insertmacro(path,@hs[1],length(hs),            current_scanner.line_no,current_scanner.inputfile.ref_index);         end        else         begin           hs:=FixFileName(hs);           fsplit(hs,path,name,ext);           { try to find the file }           found:=findincludefile(path,name,ext,foundfile);           if (ext='') then            begin              { try default extensions .inc , .pp and .pas }              if (not found) then               found:=findincludefile(path,name,'.inc',foundfile);              if (not found) then               found:=findincludefile(path,name,sourceext,foundfile);              if (not found) then               found:=findincludefile(path,name,pasext,foundfile);            end;           if current_scanner.inputfilecount<max_include_nesting then             begin               inc(current_scanner.inputfilecount);               { we need to reread the current char }               dec(current_scanner.inputpointer);               { shutdown current file }               current_scanner.tempcloseinputfile;               { load new file }               hp:=do_openinputfile(foundfile);               current_scanner.addfile(hp);               current_module.sourcefiles.register_file(hp);               if (not found) then                Message1(scan_f_cannot_open_includefile,hs);              if (not current_scanner.openinputfile) then                Message1(scan_f_cannot_open_includefile,hs);               Message1(scan_t_start_include_file,current_scanner.inputfile.path^+current_scanner.inputfile.name^);               current_scanner.reload;             end           else             Message(scan_f_include_deep_ten);         end;      end;{*****************************************************************************                            Preprocessor writting*****************************************************************************}{$ifdef PREPROCWRITE}    constructor tpreprocfile.create(const fn:string);      begin      { open outputfile }        assign(f,fn);        {$I-}         rewrite(f);        {$I+}        if ioresult<>0 then         Comment(V_Fatal,'can''t create file '+fn);        getmem(buf,preprocbufsize);        settextbuf(f,buf^,preprocbufsize);      { reset }        eolfound:=false;        spacefound:=false;      end;    destructor tpreprocfile.destroy;      begin        close(f);        freemem(buf,preprocbufsize);      end;    procedure tpreprocfile.add(const s:string);      begin        write(f,s);      end;    procedure tpreprocfile.addspace;      begin        if eolfound then         begin           writeln(f,'');           eolfound:=false;           spacefound:=false;         end        else         if spacefound then          begin            write(f,' ');            spacefound:=false;          end;      end;{$endif PREPROCWRITE}{*****************************************************************************                              TPreProcStack*****************************************************************************}    constructor tpreprocstack.create(atyp : preproctyp;a:boolean;n:tpreprocstack);      begin        accept:=a;        typ:=atyp;        next:=n;      end;{*****************************************************************************                              TDirectiveItem*****************************************************************************}    constructor TDirectiveItem.Create(const n:string;p:tdirectiveproc);      begin        inherited CreateName(n);        is_conditional:=false;        proc:=p;      end;    constructor TDirectiveItem.CreateCond(const n:string;p:tdirectiveproc);      begin        inherited CreateName(n);        is_conditional:=true;        proc:=p;      end;{****************************************************************************                                TSCANNERFILE ****************************************************************************}    constructor tscannerfile.create(const fn:string);      begin        inputfile:=do_openinputfile(fn);        if assigned(current_module) then          current_module.sourcefiles.register_file(inputfile);      { reset localinput }        inputbuffer:=nil;        inputpointer:=nil;        inputstart:=0;      { reset scanner }        preprocstack:=nil;        comment_level:=0;        yylexcount:=0;        block_type:=bt_general;        line_no:=0;        lastlinepos:=0;        lasttokenpos:=0;        lasttoken:=NOTOKEN;        nexttoken:=NOTOKEN;        lastasmgetchar:=#0;        ignoredirectives:=TStringList.Create;        in_asm_string:=false;      end;    procedure tscannerfile.firstfile;      begin      { load block }        if not openinputfile then          Message1(scan_f_cannot_open_input,inputfile.name^);        reload;      end;    destructor tscannerfile.destroy;      begin        if assigned(current_module) and           (current_module.state=ms_compiled) and           (status.errorcount=0) then          checkpreprocstack        else          begin            while assigned(preprocstack) do             poppreprocstack;          end;        if not inputfile.closed then          closeinputfile;        ignoredirectives.free;      end;    function tscannerfile.openinputfile:boolean;      begin        openinputfile:=inputfile.open;      { load buffer }        inputbuffer:=inputfile.buf;        inputpointer:=inputfile.buf;        inputstart:=inputfile.bufstart;      { line }        line_no:=0;        lastlinepos:=0;        lasttokenpos:=0;      end;    procedure tscannerfile.closeinputfile;      begin        inputfile.close;      { reset buffer }        inputbuffer:=nil;        inputpointer:=nil;        inputstart:=0;      { reset line }        line_no:=0;        lastlinepos:=0;        lasttokenpos:=0;      end;    function tscannerfile.tempopeninputfile:boolean;      begin        if inputfile.is_macro then          exit;        tempopeninputfile:=inputfile.tempopen;      { reload buffer }        inputbuffer:=inputfile.buf;        inputpointer:=inputfile.buf;        inputstart:=inputfile.bufstart;      end;    procedure tscannerfile.tempcloseinputfile;      begin        if inputfile.closed or inputfile.is_macro then         exit;        inputfile.setpos(inputstart+(inputpointer-inputbuffer));        inputfile.tempclose;      { reset buffer }        inputbuffer:=nil;        inputpointer:=nil;        inputstart:=0;      end;    procedure tscannerfile.saveinputfile;      begin        inputfile.saveinputpointer:=inputpointer;        inputfile.savelastlinepos:=lastlinepos;        inputfile.saveline_no:=line_no;      end;    procedure tscannerfile.restoreinputfile;      begin        inputpointer:=inputfile.saveinputpointer;        lastlinepos:=inputfile.savelastlinepos;        line_no:=inputfile.saveline_no;        if not inputfile.is_macro then          parser_current_file:=inputfile.name^;      end;    procedure tscannerfile.nextfile;      var        to_dispose : tinputfile;      begin        if assigned(inputfile.next) then         begin           if inputfile.is_macro then             to_dispose:=inputfile           else             begin               to_dispose:=nil;               dec(inputfilecount);             end;           { we can allways close the file, no ? }           inputfile.close;           inputfile:=inputfile.next;           if assigned(to_dispose) then             to_dispose.free;           restoreinputfile;         end;      end;    procedure tscannerfile.addfile(hp:tinputfile);      begin        saveinputfile;        { add to list }        hp.next:=inputfile;        inputfile:=hp;        { load new inputfile }        restoreinputfile;      end;    procedure tscannerfile.reload;      begin        with inputfile do         begin           { when nothing more to read then leave immediatly, so we             don't change the aktfilepos and leave it point to the last             char }           if (c=#26) and (not assigned(next)) then            exit;           repeat           { still more to read?, then change the #0 to a space so its seen             as a seperator, this can't be used for macro's which can change             the place of the #0 in the buffer with tempopen }             if (c=#0) and (bufsize>0) and                not(inputfile.is_macro) and                (inputpointer-inputbuffer<bufsize) then              begin                c:=' ';                inc(inputpointer);                exit;              end;           { can we read more from this file ? }             if (c<>#26) and (not endoffile) then              begin                readbuf;                inputpointer:=buf;                inputbuffer:=buf;                inputstart:=bufstart;              { first line? }                if line_no=0 then                 begin                   c:=inputpointer^;                   { eat utf-8 signature? }                   if (ord(inputpointer^)=$ef) and                     (ord((inputpointer+1)^)=$bb) and                     (ord((inputpointer+2)^)=$bf) then                     begin                       inc(inputpointer,3);                       message(scan_c_switching_to_utf8);                       aktsourcecodepage:='utf8';                     end;                   line_no:=1;                   if cs_asm_source in aktglobalswitches then                     inputfile.setline(line_no,bufstart);                 end;              end             else              begin              { load eof position in tokenpos/aktfilepos }                gettokenpos;              { close file }                closeinputfile;              { no next module, than EOF }                if not assigned(inputfile.next) then                 begin                   c:=#26;                   exit;                 end;              { load next file and reopen it }                nextfile;                tempopeninputfile;              { status }                Message1(scan_t_back_in,inputfile.name^);              end;           { load next char }             c:=inputpointer^;             inc(inputpointer);           until c<>#0; { if also end, then reload again }         end;      end;    procedure tscannerfile.insertmacro(const macname:string;p:pchar;len,line,fileindex:longint);      var        hp : tinputfile;      begin        { save old postion }        dec(inputpointer);        tempcloseinputfile;      { create macro 'file' }        { use special name to dispose after !! }        hp:=do_openinputfile('_Macro_.'+macname);        addfile(hp);        with inputfile do         begin           setmacro(p,len);         { local buffer }           inputbuffer:=buf;           inputpointer:=buf;           inputstart:=bufstart;           ref_index:=fileindex;         end;      { reset line }        line_no:=line;        lastlinepos:=0;        lasttokenpos:=0;      { load new c }        c:=inputpointer^;        inc(inputpointer);      end;    procedure tscannerfile.gettokenpos;    { load the values of tokenpos and lasttokenpos }      begin        lasttokenpos:=inputstart+(inputpointer-inputbuffer);        akttokenpos.line:=line_no;        akttokenpos.column:=lasttokenpos-lastlinepos;        akttokenpos.fileindex:=inputfile.ref_index;        aktfilepos:=akttokenpos;      end;    procedure tscannerfile.inc_comment_level;      var         oldaktfilepos : tfileposinfo;      begin         if (m_nested_comment in aktmodeswitches) then           inc(comment_level)         else           comment_level:=1;         if (comment_level>1) then          begin             oldaktfilepos:=aktfilepos;             gettokenpos; { update for warning }             Message1(scan_w_comment_level,tostr(comment_level));             aktfilepos:=oldaktfilepos;          end;      end;    procedure tscannerfile.dec_comment_level;      begin         if (m_nested_comment in aktmodeswitches) then           dec(comment_level)         else           comment_level:=0;      end;    procedure tscannerfile.linebreak;      var         cur : char;         oldtokenpos,         oldaktfilepos : tfileposinfo;      begin        with inputfile do         begin           if (byte(inputpointer^)=0) and not(endoffile) then             begin               cur:=c;               reload;               if byte(cur)+byte(c)<>23 then                 dec(inputpointer);             end           else             begin               { Support all combination of #10 and #13 as line break }               if (byte(inputpointer^)+byte(c)=23) then                 inc(inputpointer);             end;           { Always return #10 as line break }           c:=#10;           { increase line counters }           lastlinepos:=bufstart+(inputpointer-inputbuffer);           inc(line_no);           { update linebuffer }           if cs_asm_source in aktglobalswitches then             inputfile.setline(line_no,lastlinepos);           { update for status and call the show status routine,             but don't touch aktfilepos ! }           oldaktfilepos:=aktfilepos;           oldtokenpos:=akttokenpos;           gettokenpos; { update for v_status }           inc(status.compiledlines);           ShowStatus;           aktfilepos:=oldaktfilepos;           akttokenpos:=oldtokenpos;         end;      end;    procedure tscannerfile.illegal_char(c:char);      var        s : string;      begin        if c in [#32..#255] then          s:=''''+c+''''        else          s:='#'+tostr(ord(c));        Message2(scan_f_illegal_char,s,'$'+hexstr(ord(c),2));      end;    procedure tscannerfile.end_of_file;      begin        checkpreprocstack;        Message(scan_f_end_of_file);      end;  {-------------------------------------------           IF Conditional Handling  -------------------------------------------}    procedure tscannerfile.checkpreprocstack;      begin      { check for missing ifdefs }        while assigned(preprocstack) do         begin           Message4(scan_e_endif_expected,preprocstring[preprocstack.typ],preprocstack.name,             preprocstack.owner.inputfile.name^,tostr(preprocstack.line_nb));           poppreprocstack;         end;      end;    procedure tscannerfile.poppreprocstack;      var        hp : tpreprocstack;      begin        if assigned(preprocstack) then         begin           Message1(scan_c_endif_found,preprocstack.name);           hp:=preprocstack.next;           preprocstack.free;           preprocstack:=hp;         end        else         Message(scan_e_endif_without_if);      end;    procedure tscannerfile.ifpreprocstack(atyp : preproctyp;compile_time_predicate:tcompile_time_predicate;messid:longint);      var        condition: Boolean;        valuedescr: String;      begin        if (preprocstack=nil) or preprocstack.accept then          condition:= compile_time_predicate(valuedescr)        else          begin            condition:= false;            valuedescr:= '';          end;        preprocstack:=tpreprocstack.create(atyp, condition, preprocstack);        preprocstack.name:=valuedescr;        preprocstack.line_nb:=line_no;        preprocstack.owner:=self;        if preprocstack.accept then          Message2(messid,preprocstack.name,'accepted')        else          Message2(messid,preprocstack.name,'rejected');      end;    procedure tscannerfile.elsepreprocstack;      begin        if assigned(preprocstack) and           (preprocstack.typ<>pp_else) then         begin           if (preprocstack.typ=pp_elseif) then             preprocstack.accept:=false           else             if (not(assigned(preprocstack.next)) or (preprocstack.next.accept)) then               preprocstack.accept:=not preprocstack.accept;           preprocstack.typ:=pp_else;           preprocstack.line_nb:=line_no;           if preprocstack.accept then            Message2(scan_c_else_found,preprocstack.name,'accepted')           else            Message2(scan_c_else_found,preprocstack.name,'rejected');         end        else         Message(scan_e_endif_without_if);      end;    procedure tscannerfile.elseifpreprocstack(compile_time_predicate:tcompile_time_predicate);      var        valuedescr: String;      begin        if assigned(preprocstack) and           (preprocstack.typ in [pp_if,pp_elseif]) then         begin           { when the branch is accepted we use pp_elseif so we know that             all the next branches need to be rejected. when this branch is still             not accepted then leave it at pp_if }           if (preprocstack.typ=pp_elseif) then             preprocstack.accept:=false           else if (preprocstack.typ=pp_if) and preprocstack.accept then               begin                 preprocstack.accept:=false;                 preprocstack.typ:=pp_elseif;               end           else if (not(assigned(preprocstack.next)) or (preprocstack.next.accept))                    and compile_time_predicate(valuedescr) then               begin                 preprocstack.name:=valuedescr;                 preprocstack.accept:=true;                 preprocstack.typ:=pp_elseif;               end;           preprocstack.line_nb:=line_no;           if preprocstack.accept then             Message2(scan_c_else_found,preprocstack.name,'accepted')           else             Message2(scan_c_else_found,preprocstack.name,'rejected');         end        else         Message(scan_e_endif_without_if);      end;    procedure tscannerfile.handleconditional(p:tdirectiveitem);      var        oldaktfilepos : tfileposinfo;      begin        oldaktfilepos:=aktfilepos;        repeat          current_scanner.gettokenpos;          p.proc();          { accept the text ? }          if (current_scanner.preprocstack=nil) or current_scanner.preprocstack.accept then           break          else           begin             current_scanner.gettokenpos;             Message(scan_c_skipping_until);             repeat               current_scanner.skipuntildirective;               if not (m_mac in aktmodeswitches) then                 p:=tdirectiveitem(turbo_scannerdirectives.search(current_scanner.readid))               else                 p:=tdirectiveitem(mac_scannerdirectives.search(current_scanner.readid));             until assigned(p) and (p.is_conditional);             current_scanner.gettokenpos;             Message1(scan_d_handling_switch,'$'+p.name);           end;        until false;        aktfilepos:=oldaktfilepos;      end;    procedure tscannerfile.handledirectives;      var         t  : tdirectiveitem;         hs : string;      begin         gettokenpos;         readchar; {Remove the $}         hs:=readid;{$ifdef PREPROCWRITE}         if parapreprocess then          begin            t:=Get_Directive(hs);            if not(is_conditional(t) or (t=_DIR_DEFINE) or (t=_DIR_UNDEF)) then             begin               preprocfile^.AddSpace;               preprocfile^.Add('{$'+hs+current_scanner.readcomment+'}');               exit;             end;          end;{$endif PREPROCWRITE}         { skip this directive? }         if (ignoredirectives.find(hs)<>nil) then          begin            if (comment_level>0) then             readcomment;            { we've read the whole comment }            aktcommentstyle:=comment_none;            exit;          end;         if hs='' then          begin            Message1(scan_w_illegal_switch,'$'+hs);          end;      { Check for compiler switches }         while (length(hs)=1) and (c in ['-','+']) do          begin            HandleSwitch(hs[1],c);            current_scanner.readchar; {Remove + or -}            if c=',' then             begin               current_scanner.readchar;   {Remove , }             { read next switch, support $v+,$+}               hs:=current_scanner.readid;               if (hs='') then                begin                  if (c='$') and (m_fpc in aktmodeswitches) then                   begin                     current_scanner.readchar;  { skip $ }                     hs:=current_scanner.readid;                   end;                  if (hs='') then                   Message1(scan_w_illegal_directive,'$'+c);                end               else                Message1(scan_d_handling_switch,'$'+hs);             end            else             hs:='';          end;      { directives may follow switches after a , }         if hs<>'' then          begin            if not (m_mac in aktmodeswitches) then              t:=tdirectiveitem(turbo_scannerdirectives.search(hs))            else              t:=tdirectiveitem(mac_scannerdirectives.search(hs));            if assigned(t) then             begin               if t.is_conditional then                handleconditional(t)               else                begin                  Message1(scan_d_handling_switch,'$'+hs);                  t.proc();                end;             end            else             begin               current_scanner.ignoredirectives.insert(hs);               Message1(scan_w_illegal_directive,'$'+hs);             end;          { conditionals already read the comment }            if (current_scanner.comment_level>0) then             current_scanner.readcomment;            { we've read the whole comment }            aktcommentstyle:=comment_none;          end;      end;    procedure tscannerfile.readchar;      begin        c:=inputpointer^;        if c=#0 then          reload        else          inc(inputpointer);      end;    procedure tscannerfile.readstring;      var        i : longint;        err : boolean;      begin        err:=false;        i:=0;        repeat          case c of            '_',            '0'..'9',            'A'..'Z' :              begin                if i<255 then                 begin                   inc(i);                   orgpattern[i]:=c;                   pattern[i]:=c;                 end                else                 begin                   if not err then                     begin                       Message(scan_e_string_exceeds_255_chars);                       err:=true;                     end;                 end;                c:=inputpointer^;                inc(inputpointer);              end;            'a'..'z' :              begin                if i<255 then                 begin                   inc(i);                   orgpattern[i]:=c;                   pattern[i]:=chr(ord(c)-32)                 end                else                 begin                   if not err then                     begin                       Message(scan_e_string_exceeds_255_chars);                       err:=true;                     end;                 end;                c:=inputpointer^;                inc(inputpointer);              end;            #0 :              reload;            else              break;          end;        until false;        orgpattern[0]:=chr(i);        pattern[0]:=chr(i);      end;    procedure tscannerfile.readnumber;      var        base,        i  : longint;      begin        case c of          '%' :            begin              readchar;              base:=2;              pattern[1]:='%';              i:=1;            end;          '&' :            begin              readchar;              base:=8;              pattern[1]:='&';              i:=1;            end;          '$' :            begin              readchar;              base:=16;              pattern[1]:='$';              i:=1;            end;          else            begin              base:=10;              i:=0;            end;        end;        while ((base>=10) and (c in ['0'..'9'])) or              ((base=16) and (c in ['A'..'F','a'..'f'])) or              ((base=8) and (c in ['0'..'7'])) or              ((base=2) and (c in ['0'..'1'])) do         begin           if i<255 then            begin              inc(i);              pattern[i]:=c;            end;           readchar;         end;        pattern[0]:=chr(i);      end;    function tscannerfile.readid:string;      begin        readstring;        readid:=pattern;      end;    function tscannerfile.readval:longint;      var        l : longint;        w : integer;      begin        readnumber;        val(pattern,l,w);        readval:=l;      end;    function tscannerfile.readval_asstring:string;      begin        readnumber;        readval_asstring:=pattern;      end;    function tscannerfile.readcomment:string;      var        i : longint;      begin        i:=0;        repeat          case c of            '{' :              begin                if aktcommentstyle=comment_tp then                  inc_comment_level;              end;            '}' :              begin                if aktcommentstyle=comment_tp then                  begin                    readchar;                    dec_comment_level;                    if comment_level=0 then                      break                    else                      continue;                  end;              end;            '*' :              begin                if aktcommentstyle=comment_oldtp then                  begin                    readchar;                    if c=')' then                      begin                        readchar;                        dec_comment_level;                        break;                      end                    else                    { Add both characters !!}                      if (i<255) then                        begin                          inc(i);                          readcomment[i]:='*';                          if (i<255) then                            begin                              inc(i);                              readcomment[i]:='*';                            end;                        end;                  end                else                { Not old TP comment, so add...}                  begin                    if (i<255) then                      begin                        inc(i);                        readcomment[i]:='*';                      end;                  end;              end;            #10,#13 :              linebreak;            #26 :              end_of_file;            else              begin                if (i<255) then                  begin                    inc(i);                    readcomment[i]:=c;                  end;              end;          end;          readchar;        until false;        readcomment[0]:=chr(i);      end;    function tscannerfile.readquotedstring:string;      var        i : longint;        msgwritten : boolean;      begin        i:=0;        msgwritten:=false;        if (c='''') then          begin            repeat              readchar;              case c of                #26 :                  end_of_file;                #10,#13 :                  Message(scan_f_string_exceeds_line);                '''' :                  begin                    readchar;                    if c<>'''' then                     break;                  end;              end;              if i<255 then                begin                  inc(i);                  result[i]:=c;                end              else                begin                  if not msgwritten then                    begin                      Message(scan_e_string_exceeds_255_chars);                      msgwritten:=true;                    end;                 end;            until false;          end;        result[0]:=chr(i);      end;    function tscannerfile.readstate:char;      var        state : char;      begin        state:=' ';        if c=' ' then         begin           current_scanner.skipspace;           current_scanner.readid;           if pattern='ON' then            state:='+'           else            if pattern='OFF' then             state:='-';         end        else         state:=c;        if not (state in ['+','-']) then         Message(scan_e_wrong_switch_toggle);        readstate:=state;      end;    function tscannerfile.readstatedefault:char;      var        state : char;      begin        state:=' ';        if c=' ' then         begin           current_scanner.skipspace;           current_scanner.readid;           if pattern='ON' then            state:='+'           else            if pattern='OFF' then             state:='-'            else             if pattern='DEFAULT' then              state:='*';         end        else         state:=c;        if not (state in ['+','-','*']) then         Message(scan_e_wrong_switch_toggle_default);        readstatedefault:=state;      end;    procedure tscannerfile.skipspace;      begin        repeat          case c of            #26 :              begin                reload;                if (c=#26) and not assigned(inputfile.next) then                  break;                continue;              end;            #10,            #13 :              linebreak;            #9,#11,#12,' ' :              ;            else              break;          end;          readchar;        until false;      end;    procedure tscannerfile.skipuntildirective;      var        found : longint;        next_char_loaded : boolean;      begin         found:=0;         next_char_loaded:=false;         repeat           case c of             #10,             #13 :               linebreak;             #26 :               begin                 reload;                 if (c=#26) and not assigned(inputfile.next) then                   end_of_file;                 continue;               end;             '{' :               begin                 if (aktcommentstyle in [comment_tp,comment_none]) then                   begin                     aktcommentstyle:=comment_tp;                     if (comment_level=0) then                       found:=1;                     inc_comment_level;                   end;               end;             '*' :               begin                 if (aktcommentstyle=comment_oldtp) then                   begin                     readchar;                     if c=')' then                       begin                         dec_comment_level;                         found:=0;                         aktcommentstyle:=comment_none;                       end                     else                       next_char_loaded:=true;                   end                 else                   found := 0;               end;             '}' :               begin                 if (aktcommentstyle=comment_tp) then                   begin                     dec_comment_level;                     if (comment_level=0) then                       aktcommentstyle:=comment_none;                     found:=0;                   end;               end;             '$' :               begin                 if found=1 then                  found:=2;               end;             '''' :               if (aktcommentstyle=comment_none) then                begin                  repeat                    readchar;                    case c of                      #26 :                        end_of_file;                      #10,#13 :                        break;                      '''' :                        begin                          readchar;                          if c<>'''' then                           begin                             next_char_loaded:=true;                             break;                           end;                        end;                    end;                  until false;                end;             '(' :               begin                 if (aktcommentstyle=comment_none) then                  begin                    readchar;                    if c='*' then                     begin                       readchar;                       if c='$' then                        begin                          found:=2;                          inc_comment_level;                          aktcommentstyle:=comment_oldtp;                        end                       else                        begin                          skipoldtpcomment;                          next_char_loaded:=true;                        end;                     end                    else                     next_char_loaded:=true;                  end                 else                  found:=0;               end;             '/' :               begin                 if (aktcommentstyle=comment_none) then                  begin                    readchar;                    if c='/' then                     skipdelphicomment;                    next_char_loaded:=true;                  end                 else                  found:=0;               end;             else               found:=0;           end;           if next_char_loaded then             next_char_loaded:=false           else             readchar;         until (found=2);      end;{****************************************************************************                             Comment Handling****************************************************************************}    procedure tscannerfile.skipcomment;      begin        aktcommentstyle:=comment_tp;        readchar;        inc_comment_level;      { handle compiler switches }        if (c='$') then         handledirectives;      { handle_switches can dec comment_level,  }        while (comment_level>0) do         begin           case c of            '{' :              inc_comment_level;            '}' :              dec_comment_level;            #10,#13 :              linebreak;            #26 :              begin                reload;                if (c=#26) and not assigned(inputfile.next) then                  end_of_file;                continue;              end;           end;           readchar;         end;        aktcommentstyle:=comment_none;      end;    procedure tscannerfile.skipdelphicomment;      begin        aktcommentstyle:=comment_delphi;        inc_comment_level;        readchar;        { this is not supported }        if c='$' then          Message(scan_w_wrong_styled_switch);        { skip comment }        while not (c in [#10,#13,#26]) do          readchar;        dec_comment_level;        aktcommentstyle:=comment_none;      end;    procedure tscannerfile.skipoldtpcomment;      var        found : longint;      begin        aktcommentstyle:=comment_oldtp;        inc_comment_level;        { only load a char if last already processed,          was cause of bug1634 PM }        if c=#0 then          readchar;      { this is now supported }        if (c='$') then         handledirectives;      { skip comment }        while (comment_level>0) do         begin           found:=0;           repeat             case c of               #26 :                 begin                   reload;                   if (c=#26) and not assigned(inputfile.next) then                     end_of_file;                   continue;                 end;               #10,#13 :                 linebreak;               '*' :                 begin                   if found=3 then                    found:=4                   else                    found:=1;                 end;               ')' :                 begin                   if found in [1,4] then                    begin                      dec_comment_level;                      if comment_level=0 then                       found:=2                      else                       found:=0;                    end;                 end;               '(' :                 begin                   if found=4 then                    inc_comment_level;                   found:=3;                 end;               else                 begin                   if found=4 then                    inc_comment_level;                   found:=0;                 end;             end;             readchar;           until (found=2);         end;        aktcommentstyle:=comment_none;      end;{****************************************************************************                               Token Scanner****************************************************************************}    procedure tscannerfile.readtoken;      var        code    : integer;        len,        low,high,mid : longint;        w : word;        m       : longint;        mac     : tmacro;        asciinr : string[6];        msgwritten,        iswidestring : boolean;      label         exit_label;      begin        if localswitcheschanged then          begin            aktlocalswitches:=nextaktlocalswitches;            localswitcheschanged:=false;          end;      { was there already a token read, then return that token }        if nexttoken<>NOTOKEN then         begin           token:=nexttoken;           nexttoken:=NOTOKEN;           goto exit_label;         end;      { Skip all spaces and comments }        repeat          case c of            '{' :              skipcomment;            #26 :              begin                reload;                if (c=#26) and not assigned(inputfile.next) then                  break;              end;            ' ',#9..#13 :              begin{$ifdef PREPROCWRITE}                if parapreprocess then                 begin                   if c=#10 then                    preprocfile.eolfound:=true                   else                    preprocfile.spacefound:=true;                 end;{$endif PREPROCWRITE}                skipspace;              end            else              break;          end;        until false;      { Save current token position, for EOF its already loaded }        if c<>#26 then          gettokenpos;      { Check first for a identifier/keyword, this is 20+% faster (PFV) }        if c in ['A'..'Z','a'..'z','_'] then         begin           readstring;           token:=_ID;           idtoken:=_ID;         { keyword or any other known token,           pattern is always uppercased }           if (pattern[1]<>'_') and (length(pattern) in [tokenlenmin..tokenlenmax]) then            begin              low:=ord(tokenidx^[length(pattern),pattern[1]].first);              high:=ord(tokenidx^[length(pattern),pattern[1]].last);              while low<high do               begin                 mid:=(high+low+1) shr 1;                 if pattern<tokeninfo^[ttoken(mid)].str then                  high:=mid-1                 else                  low:=mid;               end;              with tokeninfo^[ttoken(high)] do                if pattern=str then                  begin                    if keyword in aktmodeswitches then                      if op=NOTOKEN then                        token:=ttoken(high)                      else                        token:=op;                    idtoken:=ttoken(high);                  end;            end;         { Only process identifiers and not keywords }           if token=_ID then            begin            { this takes some time ... }              if (cs_support_macro in aktmoduleswitches) then               begin                 mac:=tmacro(search_macro(pattern));                 if assigned(mac) and (not mac.is_compiler_var) and (assigned(mac.buftext)) then                  begin                    if yylexcount<max_macro_nesting then                     begin                       mac.is_used:=true;                       inc(yylexcount);                       insertmacro(pattern,mac.buftext,mac.buflen,                         mac.fileinfo.line,mac.fileinfo.fileindex);                     { handle empty macros }                       if c=#0 then                         reload;                       readtoken;                       { that's all folks }                       dec(yylexcount);                       exit;                     end                    else                     Message(scan_w_macro_too_deep);                  end;               end;            end;         { return token }           goto exit_label;         end        else         begin           idtoken:=_NOID;           case c of             '$' :               begin                 readnumber;                 token:=_INTCONST;                 goto exit_label;               end;             '%' :               begin                 if not(m_fpc in aktmodeswitches) then                  Illegal_Char(c)                 else                  begin                    readnumber;                    token:=_INTCONST;                    goto exit_label;                  end;               end;             '&' :               begin                 if m_fpc in aktmodeswitches then                  begin                    readnumber;                    token:=_INTCONST;                    goto exit_label;                  end                 else if m_mac in aktmodeswitches then                  begin                    readchar;                    token:=_AMPERSAND;                    goto exit_label;                  end                 else                  Illegal_Char(c);               end;             '0'..'9' :               begin                 readnumber;                 if (c in ['.','e','E']) then                  begin                  { first check for a . }                    if c='.' then                     begin                       readchar;                       { is it a .. from a range? }                       case c of                         '.' :                           begin                             readchar;                             token:=_INTCONST;                             nexttoken:=_POINTPOINT;                             goto exit_label;                           end;                         ')' :                           begin                             readchar;                             token:=_INTCONST;                             nexttoken:=_RECKKLAMMER;                             goto exit_label;                           end;                       end;                       { insert the number after the . }                       pattern:=pattern+'.';                       while c in ['0'..'9'] do                        begin                          pattern:=pattern+c;                          readchar;                        end;                      end;                  { E can also follow after a point is scanned }                    if c in ['e','E'] then                     begin                       pattern:=pattern+'E';                       readchar;                       if c in ['-','+'] then                        begin                          pattern:=pattern+c;                          readchar;                        end;                       if not(c in ['0'..'9']) then                        Illegal_Char(c);                       while c in ['0'..'9'] do                        begin                          pattern:=pattern+c;                          readchar;                        end;                     end;                    token:=_REALNUMBER;                    goto exit_label;                  end;                 token:=_INTCONST;                 goto exit_label;               end;             ';' :               begin                 readchar;                 token:=_SEMICOLON;                 goto exit_label;               end;             '[' :               begin                 readchar;                 token:=_LECKKLAMMER;                 goto exit_label;               end;             ']' :               begin                 readchar;                 token:=_RECKKLAMMER;                 goto exit_label;               end;             '(' :               begin                 readchar;                 case c of                   '*' :                     begin                       c:=#0;{Signal skipoldtpcomment to reload a char }                       skipoldtpcomment;                       readtoken;                       exit;                     end;                   '.' :                     begin                       readchar;                       token:=_LECKKLAMMER;                       goto exit_label;                     end;                 end;                 token:=_LKLAMMER;                 goto exit_label;               end;             ')' :               begin                 readchar;                 token:=_RKLAMMER;                 goto exit_label;               end;             '+' :               begin                 readchar;                 if (c='=') and (cs_support_c_operators in aktmoduleswitches) then                  begin                    readchar;                    token:=_PLUSASN;                    goto exit_label;                  end;                 token:=_PLUS;                 goto exit_label;               end;             '-' :               begin                 readchar;                 if (c='=') and (cs_support_c_operators in aktmoduleswitches) then                  begin                    readchar;                    token:=_MINUSASN;                    goto exit_label;                  end;                 token:=_MINUS;                 goto exit_label;               end;             ':' :               begin                 readchar;                 if c='=' then                  begin                    readchar;                    token:=_ASSIGNMENT;                    goto exit_label;                  end;                 token:=_COLON;                 goto exit_label;               end;             '*' :               begin                 readchar;                 if (c='=') and (cs_support_c_operators in aktmoduleswitches) then                  begin                    readchar;                    token:=_STARASN;                  end                 else                  if c='*' then                   begin                     readchar;                     token:=_STARSTAR;                   end                 else                  token:=_STAR;                 goto exit_label;               end;             '/' :               begin                 readchar;                 case c of                   '=' :                     begin                       if (cs_support_c_operators in aktmoduleswitches) then                        begin                          readchar;                          token:=_SLASHASN;                          goto exit_label;                        end;                     end;                   '/' :                     begin                       skipdelphicomment;                       readtoken;                       exit;                     end;                 end;                 token:=_SLASH;                 goto exit_label;               end;             '|' :               if m_mac in aktmodeswitches then                begin                  readchar;                  token:=_PIPE;                  goto exit_label;                end               else                Illegal_Char(c);             '=' :               begin                 readchar;                 token:=_EQUAL;                 goto exit_label;               end;             '.' :               begin                 readchar;                 case c of                   '.' :                     begin                       readchar;                       case c of                         '.' :                         begin                           readchar;                           token:=_POINTPOINTPOINT;                           goto exit_label;                         end;                       else                         begin                           token:=_POINTPOINT;                           goto exit_label;                         end;                       end;                     end;                   ')' :                     begin                       readchar;                       token:=_RECKKLAMMER;                       goto exit_label;                     end;                 end;                 token:=_POINT;                 goto exit_label;               end;             '@' :               begin                 readchar;                 token:=_KLAMMERAFFE;                 goto exit_label;               end;             ',' :               begin                 readchar;                 token:=_COMMA;                 goto exit_label;               end;             '''','#','^' :               begin                 len:=0;                 msgwritten:=false;                 pattern:='';                 iswidestring:=false;                 if c='^' then                  begin                    readchar;                    c:=upcase(c);                    if (block_type=bt_type) or                       (lasttoken=_ID) or (lasttoken=_NIL) or                       (lasttoken=_RKLAMMER) or (lasttoken=_RECKKLAMMER) or (lasttoken=_CARET) then                     begin                       token:=_CARET;                       goto exit_label;                     end                    else                     begin                       inc(len);                       if c<#64 then                        pattern[len]:=chr(ord(c)+64)                       else                        pattern[len]:=chr(ord(c)-64);                       readchar;                     end;                  end;                 repeat                   case c of                     '#' :                       begin                         readchar; { read # }                         if c='$' then                           begin                              readchar; { read leading $ }                              asciinr:='$';                              while (upcase(c) in ['A'..'F','0'..'9']) and (length(asciinr)<6) do                               begin                                 asciinr:=asciinr+c;                                 readchar;                               end;                           end                         else                           begin                              asciinr:='';                              while (c in ['0'..'9']) and (length(asciinr)<6) do                               begin                                 asciinr:=asciinr+c;                                 readchar;                               end;                           end;                         val(asciinr,m,code);                         if (asciinr='') or (code<>0) then                           Message(scan_e_illegal_char_const)                         else if (m<0) or (m>255) or (length(asciinr)>3) then                           begin                              if (m>=0) and (m<=65535) then                                begin                                  if not iswidestring then                                   begin                                     ascii2unicode(@pattern[1],len,patternw);                                     iswidestring:=true;                                     len:=0;                                   end;                                  concatwidestringchar(patternw,tcompilerwidechar(m));                                end                              else                                Message(scan_e_illegal_char_const)                           end                         else if iswidestring then                           concatwidestringchar(patternw,asciichar2unicode(char(m)))                         else                           begin                             if len<255 then                              begin                                inc(len);                                pattern[len]:=chr(m);                              end                             else                              begin                                if not msgwritten then                                 begin                                   Message(scan_e_string_exceeds_255_chars);                                   msgwritten:=true;                                 end;                              end;                           end;                       end;                     '''' :                       begin                         repeat                           readchar;                           case c of                             #26 :                               end_of_file;                             #10,#13 :                               Message(scan_f_string_exceeds_line);                             '''' :                               begin                                 readchar;                                 if c<>'''' then                                  break;                               end;                           end;                           { interpret as utf-8 string? }                           if (ord(c)>=$80) and (aktsourcecodepage='utf8') then                             begin                               { convert existing string to an utf-8 string }                               if not iswidestring then                                 begin                                   ascii2unicode(@pattern[1],len,patternw);                                   iswidestring:=true;                                   len:=0;                                 end;                               { four or more chars aren't handled }                               if (ord(c) and $f0)=$f0 then                                 message(scan_e_utf8_bigger_than_65535)                               { three chars }                               else if (ord(c) and $e0)=$e0 then                                 begin                                   w:=ord(c) and $f;                                   readchar;                                   if (ord(c) and $c0)<>$80 then                                     message(scan_e_utf8_malformed);                                   w:=(w shl 6) or (ord(c) and $3f);                                   readchar;                                   if (ord(c) and $c0)<>$80 then                                     message(scan_e_utf8_malformed);                                   w:=(w shl 6) or (ord(c) and $3f);                                   concatwidestringchar(patternw,w);                                 end                               { two chars }                               else if (ord(c) and $c0)<>0 then                                 begin                                   w:=ord(c) and $1f;                                   readchar;                                   if (ord(c) and $c0)<>$80 then                                     message(scan_e_utf8_malformed);                                   w:=(w shl 6) or (ord(c) and $3f);                                   concatwidestringchar(patternw,w);                                 end                               { illegal }                               else if (ord(c) and $80)<>0 then                                 message(scan_e_utf8_malformed)                               else                                 concatwidestringchar(patternw,tcompilerwidechar(c))                             end                           else if iswidestring then                             begin                               if aktsourcecodepage='utf8' then                                 concatwidestringchar(patternw,ord(c))                               else                                 concatwidestringchar(patternw,asciichar2unicode(c))                             end                           else                             begin                               if len<255 then                                begin                                  inc(len);                                  pattern[len]:=c;                                end                               else                                begin                                  if not msgwritten then                                   begin                                     Message(scan_e_string_exceeds_255_chars);                                     msgwritten:=true;                                   end;                                end;                             end;                         until false;                       end;                     '^' :                       begin                         readchar;                         c:=upcase(c);                         if c<#64 then                          c:=chr(ord(c)+64)                         else                          c:=chr(ord(c)-64);                         if iswidestring then                           concatwidestringchar(patternw,asciichar2unicode(c))                         else                           begin                             if len<255 then                              begin                                inc(len);                                pattern[len]:=c;                              end                             else                              begin                                if not msgwritten then                                 begin                                   Message(scan_e_string_exceeds_255_chars);                                   msgwritten:=true;                                 end;                              end;                           end;                         readchar;                       end;                     else                      break;                   end;                 until false;                 { strings with length 1 become const chars }                 if iswidestring then                   begin                      if patternw^.len=1 then                       token:=_CWCHAR                      else                       token:=_CWSTRING;                   end                 else                   begin                      pattern[0]:=chr(len);                      if len=1 then                       token:=_CCHAR                      else                       token:=_CSTRING;                   end;                 goto exit_label;               end;             '>' :               begin                 readchar;                 case c of                   '=' :                     begin                       readchar;                       token:=_GTE;                       goto exit_label;                     end;                   '>' :                     begin                       readchar;                       token:=_OP_SHR;                       goto exit_label;                     end;                   '<' :                     begin { >< is for a symetric diff for sets }                       readchar;                       token:=_SYMDIF;                       goto exit_label;                     end;                 end;                 token:=_GT;                 goto exit_label;               end;             '<' :               begin                 readchar;                 case c of                   '>' :                     begin                       readchar;                       token:=_UNEQUAL;                       goto exit_label;                     end;                   '=' :                     begin                       readchar;                       token:=_LTE;                       goto exit_label;                     end;                   '<' :                     begin                       readchar;                       token:=_OP_SHL;                       goto exit_label;                     end;                 end;                 token:=_LT;                 goto exit_label;               end;             #26 :               begin                 token:=_EOF;                 checkpreprocstack;                 goto exit_label;               end;             else               Illegal_Char(c);           end;        end;exit_label:        lasttoken:=token;      end;    function tscannerfile.readpreproc:ttoken;      begin         skipspace;         case c of           '_',           'A'..'Z',           'a'..'z' :             begin               current_scanner.preproc_pattern:=readid;               readpreproc:=_ID;             end;           '0'..'9' :             begin               current_scanner.preproc_pattern:=readval_asstring;               { realnumber? }               if c='.' then                 begin                   readchar;                   while c in ['0'..'9'] do                     begin                       current_scanner.preproc_pattern:=current_scanner.preproc_pattern+c;                       readchar;                     end;                 end;               readpreproc:=_ID;             end;           '$','%','&' :             begin               current_scanner.preproc_pattern:=readval_asstring;               readpreproc:=_ID;             end;           ',' :             begin               readchar;               readpreproc:=_COMMA;             end;           '}' :             begin               readpreproc:=_END;             end;           '(' :             begin               readchar;               readpreproc:=_LKLAMMER;             end;           ')' :             begin               readchar;               readpreproc:=_RKLAMMER;             end;           '[' :             begin               readchar;               readpreproc:=_LECKKLAMMER;             end;           ']' :             begin               readchar;               readpreproc:=_RECKKLAMMER;             end;           '+' :             begin               readchar;               readpreproc:=_PLUS;             end;           '-' :             begin               readchar;               readpreproc:=_MINUS;             end;           '*' :             begin               readchar;               readpreproc:=_STAR;             end;           '/' :             begin               readchar;               readpreproc:=_SLASH;             end;           '=' :             begin               readchar;               readpreproc:=_EQUAL;             end;           '>' :             begin               readchar;               if c='=' then                 begin                   readchar;                   readpreproc:=_GTE;                 end               else                 readpreproc:=_GT;             end;           '<' :             begin               readchar;               case c of                 '>' :                   begin                     readchar;                     readpreproc:=_UNEQUAL;                   end;                 '=' :                   begin                     readchar;                     readpreproc:=_LTE;                   end;                 else                   readpreproc:=_LT;               end;             end;           #26 :             begin               readpreproc:=_EOF;               checkpreprocstack;             end;           else             Illegal_Char(c);         end;      end;    function tscannerfile.asmgetcharstart : char;      begin        { return first the character already          available in c }        lastasmgetchar:=c;        result:=asmgetchar;      end;    function tscannerfile.asmgetchar : char;      begin         if lastasmgetchar<>#0 then          begin            c:=lastasmgetchar;            lastasmgetchar:=#0;          end         else          readchar;         if in_asm_string then           begin             asmgetchar:=c;             exit;           end;         repeat           case c of{$ifndef arm}             // the { ... } is used in ARM assembler to define register sets,  so we can't used             // it as comment, either (* ... *), /* ... */ or // ... should be used instead             '{' :               skipcomment;{$endif arm}             #10,#13 :               begin                 linebreak;                 asmgetchar:=c;                 exit;               end;             #26 :               begin                 reload;                 if (c=#26) and not assigned(inputfile.next) then                   end_of_file;                 continue;               end;             '/' :               begin                  readchar;                  if c='/' then                   skipdelphicomment                  else                   begin                     asmgetchar:='/';                     lastasmgetchar:=c;                     exit;                   end;               end;             '(' :               begin                  readchar;                  if c='*' then                   begin                     c:=#0;{Signal skipoldtpcomment to reload a char }                     skipoldtpcomment;                   end                  else                   begin                     asmgetchar:='(';                     lastasmgetchar:=c;                     exit;                   end;               end;             else               begin                 asmgetchar:=c;                 exit;               end;           end;         until false;      end;{*****************************************************************************                                   Helpers*****************************************************************************}    procedure AddDirective(const s:string; dm: tdirectivemode; p:tdirectiveproc);      begin        if dm in [directive_all, directive_turbo] then          turbo_scannerdirectives.insert(tdirectiveitem.create(s,p));        if dm in [directive_all, directive_mac] then          mac_scannerdirectives.insert(tdirectiveitem.create(s,p));      end;    procedure AddConditional(const s:string; dm: tdirectivemode; p:tdirectiveproc);      begin        if dm in [directive_all, directive_turbo] then          turbo_scannerdirectives.insert(tdirectiveitem.createcond(s,p));        if dm in [directive_all, directive_mac] then          mac_scannerdirectives.insert(tdirectiveitem.createcond(s,p));      end;{*****************************************************************************                                Initialization*****************************************************************************}    procedure InitScanner;      begin        InitWideString(patternw);        turbo_scannerdirectives:=TDictionary.Create;        mac_scannerdirectives:=TDictionary.Create;        { Common directives and conditionals }        AddDirective('I',directive_all, @dir_include);        AddDirective('DEFINE',directive_all, @dir_define);        AddDirective('UNDEF',directive_all, @dir_undef);        AddConditional('IF',directive_all, @dir_if);        AddConditional('IFDEF',directive_all, @dir_ifdef);        AddConditional('IFNDEF',directive_all, @dir_ifndef);        AddConditional('ELSE',directive_all, @dir_else);        AddConditional('ELSEIF',directive_all, @dir_elseif);        AddConditional('ENDIF',directive_all, @dir_endif);        { Directives and conditionals for all modes except mode macpas}        AddDirective('INCLUDE',directive_turbo, @dir_include);        AddDirective('LIBPREFIX',directive_turbo, @dir_libprefix);        AddDirective('LIBSUFFIX',directive_turbo, @dir_libsuffix);        AddDirective('EXTENSION',directive_turbo, @dir_extension);        AddConditional('IFEND',directive_turbo, @dir_endif);        AddConditional('IFOPT',directive_turbo, @dir_ifopt);        { Directives and conditionals for mode macpas: }        AddDirective('SETC',directive_mac, @dir_setc);        AddDirective('DEFINEC',directive_mac, @dir_definec);        AddDirective('UNDEFC',directive_mac, @dir_undef);        AddConditional('IFC',directive_mac, @dir_if);        AddConditional('ELSEC',directive_mac, @dir_else);        AddConditional('ELIFC',directive_mac, @dir_elseif);        AddConditional('ENDC',directive_mac, @dir_endif);      end;    procedure DoneScanner;      begin        turbo_scannerdirectives.Free;        mac_scannerdirectives.Free;        DoneWideString(patternw);      end;end.
 |