102030405060708090100110120130140150160170180190200210220230240250260270280290300310320330340350360370380390400410420430440450460470480490500510520530540550560570580590600610620630640650660670680690700710720730740750760770780790800810820830840850860870880890900910920930940950960970980990100010101020103010401050106010701080109011001110112011301140115011601170118011901200121012201230124012501260127012801290130013101320133013401350136013701380139014001410142014301440145014601470148014901500151015201530154015501560157015801590160016101620163016401650166016701680169017001710172017301740175017601770178017901800181018201830184018501860187018801890190019101920193019401950196019701980199020002010202020302040205020602070208020902100211021202130214021502160217021802190220022102220223022402250226022702280229023002310232023302340235023602370238023902400241024202430244024502460247024802490250025102520253025402550256025702580259026002610262026302640265026602670268026902700271027202730274027502760277027802790280028102820283028402850286028702880289029002910292029302940295029602970298029903000301030203030304030503060307030803090310031103120313031403150316031703180319032003210322032303240325032603270328032903300331033203330334033503360337033803390340034103420343034403450346034703480349035003510352035303540355035603570358035903600361036203630364036503660367036803690370037103720373037403750376037703780379038003810382038303840385038603870388038903900391039203930394039503960397039803990400040104020403040404050406040704080409041004110412041304140415041604170418041904200421042204230424042504260427042804290430043104320433043404350436043704380439044004410442044304440445044604470448044904500451045204530454045504560457045804590460046104620463046404650466046704680469047004710472047304740475047604770478047904800481048204830484048504860487048804890490049104920493049404950496049704980499050005010502050305040505050605070508050905100511051205130514051505160517051805190520052105220523052405250526052705280529053005310532053305340535053605370538053905400541054205430544054505460547054805490550055105520553055405550556055705580559056005610562056305640565056605670568056905700571057205730574057505760577057805790580058105820583058405850586058705880589059005910592059305940595059605970598059906000601060206030604060506060607060806090610061106120613061406150616061706180619062006210622062306240625062606270628062906300631063206330634063506360637063806390640064106420643064406450646064706480649065006510652065306540655065606570658065906600661066206630664066506660667066806690670067106720673067406750676067706780679068006810682068306840685068606870688068906900691069206930694069506960697069806990700070107020703070407050706070707080709071007110712071307140715071607170718071907200721072207230724072507260727072807290730073107320733073407350736073707380739074007410742074307440745074607470748074907500751075207530754075507560757075807590760076107620763076407650766076707680769077007710772077307740775077607770778077907800781078207830784078507860787078807890790079107920793079407950796079707980799080008010802080308040805080608070808080908100811081208130814081508160817081808190820082108220823082408250826082708280829083008310832083308340835083608370838083908400841084208430844084508460847084808490850085108520853085408550856085708580859086008610862086308640865086608670868086908700871087208730874087508760877087808790880088108820883088408850886088708880889089008910892089308940895089608970898089909000901090209030904090509060907090809090910091109120913091409150916091709180919092009210922092309240925092609270928092909300931093209330934093509360937093809390940094109420943094409450946094709480949095009510952095309540955095609570958095909600961096209630964096509660967096809690970097109720973097409750976097709780979098009810982098309840985098609870988098909900991099209930994099509960997099809990100001001010020100301004010050100601007010080100901010010110101201013010140101501016010170101801019010200102101022010230102401025010260102701028010290103001031010320103301034010350103601037010380103901040010410104201043010440104501046010470104801049010500105101052010530105401055010560105701058010590106001061010620106301064010650106601067010680106901070010710107201073010740107501076010770107801079010800108101082010830108401085010860108701088010890109001091010920109301094010950109601097010980109901100011010110201103011040110501106011070110801109011100111101112011130111401115011160111701118011190112001121011220112301124011250112601127011280112901130011310113201133011340113501136011370113801139011400114101142011430114401145011460114701148011490115001151011520115301154011550115601157011580115901160011610116201163011640116501166011670116801169011700117101172011730117401175011760117701178011790118001181011820118301184011850118601187011880118901190011910119201193011940119501196011970119801199012000120101202012030120401205012060120701208012090121001211012120121301214012150121601217012180121901220012210122201223012240122501226012270122801229012300123101232012330123401235012360123701238012390124001241012420124301244012450124601247012480124901250012510125201253012540125501256012570125801259012600126101262012630126401265012660126701268012690127001271012720127301274012750127601277012780127901280012810128201283012840128501286012870128801289012900129101292012930129401295012960129701298012990130001301013020130301304013050130601307013080130901310013110131201313013140131501316013170131801319013200132101322013230132401325013260132701328013290133001331013320133301334013350133601337013380133901340013410134201343013440134501346013470134801349013500135101352013530135401355013560135701358013590136001361013620136301364013650136601367013680136901370013710137201373013740137501376013770137801379013800138101382013830138401385013860138701388013890139001391013920139301394013950139601397013980139901400014010140201403014040140501406014070140801409014100141101412014130141401415014160141701418014190142001421014220142301424014250142601427014280142901430014310143201433014340143501436014370143801439014400144101442014430144401445014460144701448014490145001451014520145301454014550145601457014580145901460014610146201463014640146501466014670146801469014700147101472014730147401475014760147701478014790148001481014820148301484014850148601487014880148901490014910149201493014940149501496014970149801499015000150101502015030150401505015060150701508015090151001511015120151301514015150151601517015180151901520015210152201523015240152501526015270152801529015300153101532015330153401535015360153701538015390154001541015420154301544015450154601547015480154901550015510155201553015540155501556015570155801559015600156101562015630156401565015660156701568015690157001571015720157301574015750157601577015780157901580015810158201583015840158501586015870158801589015900159101592015930159401595015960159701598015990160001601016020160301604016050160601607016080160901610016110161201613016140161501616016170161801619016200162101622016230162401625016260162701628016290163001631016320163301634016350163601637016380163901640016410164201643016440164501646016470164801649016500165101652016530165401655016560165701658016590166001661016620166301664016650166601667016680166901670016710167201673016740167501676016770167801679016800168101682016830168401685016860168701688016890169001691016920169301694016950169601697016980169901700017010170201703017040170501706017070170801709017100171101712017130171401715017160171701718017190172001721017220172301724017250172601727017280172901730017310173201733017340173501736017370173801739017400174101742017430174401745017460174701748017490175001751017520175301754017550175601757017580175901760017610176201763017640176501766017670176801769017700177101772017730177401775017760177701778017790178001781017820178301784017850178601787017880178901790017910179201793017940179501796017970179801799018000180101802018030180401805018060180701808018090181001811018120181301814018150181601817018180181901820018210182201823018240182501826018270182801829018300183101832018330183401835018360183701838018390184001841018420184301844018450184601847018480184901850018510185201853018540185501856018570185801859018600186101862018630186401865018660186701868018690187001871018720187301874018750187601877018780187901880018810188201883018840188501886018870188801889018900189101892018930189401895018960189701898018990190001901019020190301904019050190601907019080190901910019110191201913019140191501916019170191801919019200192101922019230192401925019260192701928019290193001931019320193301934019350193601937019380193901940019410194201943019440194501946019470194801949019500195101952019530195401955019560195701958019590196001961019620196301964019650196601967019680196901970019710197201973019740197501976019770197801979019800198101982019830198401985019860198701988019890199001991019920199301994019950199601997019980199902000020010200202003020040200502006020070200802009020100201102012020130201402015020160201702018020190202002021020220202302024020250202602027020280202902030020310203202033020340203502036020370203802039020400204102042020430204402045020460204702048020490205002051020520205302054020550205602057020580205902060020610206202063020640206502066020670206802069020700207102072020730207402075020760207702078020790208002081020820208302084020850208602087020880208902090020910209202093020940209502096020970209802099021000210102102021030210402105021060210702108021090211002111021120211302114021150211602117021180211902120021210212202123021240212502126021270212802129021300213102132021330213402135021360213702138021390214002141021420214302144021450214602147021480214902150021510215202153021540215502156021570215802159021600216102162021630216402165021660216702168021690217002171021720217302174021750217602177021780217902180021810218202183021840218502186021870218802189021900219102192021930219402195021960219702198021990220002201022020220302204022050220602207022080220902210022110221202213022140221502216022170221802219022200222102222022230222402225022260222702228022290223002231022320223302234022350223602237022380223902240022410224202243022440224502246022470224802249022500225102252022530225402255022560225702258022590226002261022620226302264022650226602267022680226902270022710227202273022740227502276022770227802279022800228102282022830228402285022860228702288022890229002291022920229302294022950229602297022980229902300023010230202303023040230502306023070230802309023100231102312023130231402315023160231702318023190232002321023220232302324023250232602327023280232902330023310233202333023340233502336023370233802339023400234102342023430234402345023460234702348023490235002351023520235302354023550235602357023580235902360023610236202363023640236502366023670236802369023700237102372023730237402375023760237702378023790238002381023820238302384023850238602387023880238902390023910239202393023940239502396023970239802399024000240102402024030240402405024060240702408024090241002411024120241302414024150241602417024180241902420024210242202423024240242502426024270242802429024300243102432024330243402435024360243702438024390244002441024420244302444024450244602447024480244902450024510245202453024540245502456024570245802459024600246102462024630246402465024660246702468024690247002471024720247302474024750247602477024780247902480024810248202483024840248502486024870248802489024900249102492024930249402495024960249702498024990250002501025020250302504025050250602507025080250902510025110251202513025140251502516025170251802519025200252102522025230252402525025260252702528025290253002531025320253302534025350253602537025380253902540025410254202543025440254502546025470254802549025500255102552025530255402555025560255702558025590256002561025620256302564025650256602567025680256902570025710257202573025740257502576025770257802579025800258102582025830258402585025860258702588025890259002591025920259302594025950259602597025980259902600026010260202603026040260502606026070260802609026100261102612026130261402615026160261702618026190262002621026220262302624026250262602627026280262902630026310263202633026340263502636026370263802639026400264102642026430264402645026460264702648026490265002651026520265302654026550265602657026580265902660026610266202663026640266502666026670266802669026700267102672026730267402675026760267702678026790268002681026820268302684026850268602687026880268902690026910269202693026940269502696026970269802699027000270102702027030270402705027060270702708027090271002711027120271302714027150271602717027180271902720027210272202723027240272502726027270272802729027300273102732027330273402735027360273702738027390274002741027420274302744027450274602747027480274902750027510275202753027540275502756027570275802759027600276102762027630276402765027660276702768027690277002771027720277302774027750277602777027780277902780027810278202783027840278502786027870278802789027900279102792027930279402795027960279702798027990280002801028020280302804028050280602807028080280902810028110281202813028140281502816028170281802819028200282102822028230282402825028260282702828028290283002831028320283302834028350283602837028380283902840028410284202843028440284502846028470284802849028500285102852028530285402855028560285702858028590286002861028620286302864028650286602867028680286902870028710287202873028740287502876028770287802879028800288102882028830288402885028860288702888028890289002891028920289302894028950289602897028980289902900029010290202903029040290502906029070290802909029100291102912029130291402915029160291702918029190292002921029220292302924029250292602927029280292902930029310293202933029340293502936029370293802939029400294102942029430294402945029460294702948029490295002951029520295302954029550295602957029580295902960029610296202963029640296502966029670296802969029700297102972029730297402975029760297702978029790298002981029820298302984029850298602987029880298902990029910299202993029940299502996029970299802999030000300103002030030300403005030060300703008030090301003011030120301303014030150301603017030180301903020030210302203023030240302503026030270302803029030300303103032030330303403035030360303703038030390304003041030420304303044030450304603047030480304903050030510305203053030540305503056030570305803059030600306103062030630306403065030660306703068030690307003071030720307303074030750307603077030780307903080030810308203083030840308503086030870308803089030900309103092030930309403095030960309703098030990310003101031020310303104031050310603107031080310903110031110311203113031140311503116031170311803119031200312103122031230312403125031260312703128031290313003131031320313303134031350313603137031380313903140031410314203143031440314503146031470314803149031500315103152031530315403155031560315703158031590316003161031620316303164031650316603167031680316903170031710317203173031740317503176031770317803179031800318103182031830318403185031860318703188031890319003191031920319303194031950319603197031980319903200032010320203203032040320503206032070320803209032100321103212032130321403215032160321703218032190322003221032220322303224032250322603227032280322903230032310323203233032340323503236032370323803239032400324103242032430324403245032460324703248032490325003251032520325303254032550325603257032580325903260032610326203263032640326503266032670326803269032700327103272032730327403275032760327703278032790328003281032820328303284032850328603287032880328903290032910329203293032940329503296032970329803299033000330103302033030330403305033060330703308033090331003311033120331303314033150331603317033180331903320033210332203323033240332503326033270332803329033300333103332033330333403335033360333703338033390334003341033420334303344033450334603347033480334903350033510335203353033540335503356033570335803359033600336103362033630336403365033660336703368033690337003371033720337303374033750337603377033780337903380033810338203383033840338503386033870338803389033900339103392033930339403395033960339703398033990340003401034020340303404034050340603407034080340903410034110341203413034140341503416034170341803419034200342103422034230342403425034260342703428034290343003431034320343303434034350343603437034380343903440034410344203443034440344503446034470344803449034500345103452034530345403455034560345703458034590346003461034620346303464034650346603467034680346903470034710347203473034740347503476034770347803479034800348103482034830348403485034860348703488034890349003491034920349303494034950349603497034980349903500035010350203503035040350503506035070350803509035100351103512035130351403515035160351703518035190352003521035220352303524035250352603527035280352903530035310353203533035340353503536035370353803539035400354103542035430354403545035460354703548035490355003551035520355303554035550355603557035580355903560035610356203563035640356503566035670356803569035700357103572035730357403575035760357703578035790358003581035820358303584035850358603587035880358903590035910359203593035940359503596035970359803599036000360103602036030360403605036060360703608036090361003611036120361303614036150361603617036180361903620036210362203623036240362503626036270362803629036300363103632036330363403635036360363703638036390364003641036420364303644036450364603647036480364903650036510365203653036540365503656036570365803659036600366103662036630366403665036660366703668036690367003671036720367303674036750367603677036780367903680036810368203683036840368503686036870368803689036900369103692036930369403695036960369703698036990370003701037020370303704037050370603707037080370903710037110371203713037140371503716037170371803719037200372103722037230372403725037260372703728037290373003731037320373303734037350373603737037380373903740037410374203743037440374503746037470374803749037500375103752037530375403755037560375703758037590376003761037620376303764037650376603767037680376903770037710377203773037740377503776037770377803779037800378103782037830378403785037860378703788037890379003791037920379303794037950379603797037980379903800038010380203803038040380503806038070380803809038100381103812038130381403815038160381703818038190382003821038220382303824038250382603827038280382903830038310383203833038340383503836038370383803839038400384103842038430384403845038460384703848038490385003851038520385303854038550385603857038580385903860038610386203863038640386503866038670386803869038700387103872038730387403875038760387703878038790388003881038820388303884038850388603887038880388903890038910389203893038940389503896038970389803899039000390103902039030390403905039060390703908039090391003911039120391303914039150391603917039180391903920039210392203923039240392503926039270392803929039300393103932039330393403935039360393703938039390394003941039420394303944039450394603947039480394903950039510395203953039540395503956039570395803959039600396103962039630396403965039660396703968039690397003971039720397303974039750397603977039780397903980039810398203983039840398503986039870398803989039900399103992039930399403995039960399703998039990400004001040020400304004040050400604007040080400904010040110401204013040140401504016040170401804019040200402104022040230402404025040260402704028040290403004031040320403304034040350403604037040380403904040040410404204043040440404504046040470404804049040500405104052040530405404055040560405704058040590406004061040620406304064040650406604067040680406904070040710407204073040740407504076040770407804079040800408104082040830408404085040860408704088040890409004091040920409304094040950409604097040980409904100041010410204103041040410504106041070410804109041100411104112041130411404115041160411704118041190412004121041220412304124041250412604127041280412904130041310413204133041340413504136041370413804139041400414104142041430414404145041460414704148041490415004151041520415304154041550415604157041580415904160041610416204163041640416504166041670416804169041700417104172041730417404175041760417704178041790418004181041820418304184041850418604187041880418904190041910419204193041940419504196041970419804199042000420104202042030420404205042060420704208042090421004211042120421304214042150421604217042180421904220042210422204223042240422504226042270422804229042300423104232042330423404235042360423704238042390424004241042420424304244042450424604247042480424904250042510425204253042540425504256042570425804259042600426104262042630426404265042660426704268042690427004271042720427304274042750427604277042780427904280042810428204283042840428504286042870428804289042900429104292042930429404295042960429704298042990430004301043020430304304043050430604307043080430904310043110431204313043140431504316043170431804319043200432104322043230432404325043260432704328043290433004331043320433304334043350433604337043380433904340043410434204343043440434504346043470434804349043500435104352043530435404355043560435704358043590436004361043620436304364043650436604367043680436904370043710437204373043740437504376043770437804379043800438104382043830438404385043860438704388043890439004391043920439304394043950439604397043980439904400044010440204403044040440504406044070440804409044100441104412044130441404415044160441704418044190442004421044220442304424044250442604427044280442904430044310443204433044340443504436044370443804439044400444104442044430444404445044460444704448044490445004451044520445304454044550445604457044580445904460044610446204463044640446504466044670446804469044700447104472044730447404475044760447704478044790448004481044820448304484044850448604487044880448904490044910449204493044940449504496044970449804499045000450104502045030450404505045060450704508045090451004511045120451304514045150451604517045180451904520045210452204523045240452504526045270452804529045300453104532045330453404535045360453704538045390454004541045420454304544045450454604547045480454904550045510455204553045540455504556045570455804559045600456104562045630456404565045660456704568045690457004571045720457304574045750457604577045780457904580045810458204583045840458504586045870458804589045900459104592045930459404595045960459704598045990460004601046020460304604046050460604607046080460904610046110461204613046140461504616046170461804619046200462104622046230462404625046260462704628046290463004631046320463304634046350463604637046380463904640046410464204643046440464504646046470464804649046500465104652046530465404655046560465704658046590466004661046620466304664046650466604667046680466904670046710467204673046740467504676046770467804679046800468104682046830468404685046860468704688046890469004691046920469304694046950469604697046980469904700047010470204703047040470504706047070470804709047100471104712047130471404715047160471704718047190472004721047220472304724047250472604727047280472904730047310473204733047340473504736047370473804739047400474104742047430474404745047460474704748047490475004751047520475304754047550475604757047580475904760047610476204763047640476504766047670476804769047700477104772047730477404775047760477704778047790478004781047820478304784047850478604787047880478904790047910479204793047940479504796047970479804799048000480104802048030480404805048060480704808048090481004811048120481304814048150481604817048180481904820048210482204823048240482504826048270482804829048300483104832048330483404835048360483704838048390484004841048420484304844048450484604847048480484904850048510485204853048540485504856048570485804859048600486104862048630486404865048660486704868048690487004871048720487304874048750487604877048780487904880048810488204883048840488504886048870488804889048900489104892048930489404895048960489704898048990490004901049020490304904049050490604907049080490904910049110491204913049140491504916049170491804919049200492104922049230492404925049260492704928049290493004931049320493304934049350493604937049380493904940049410494204943049440494504946049470494804949049500495104952049530495404955049560495704958049590496004961049620496304964049650496604967049680496904970049710497204973049740497504976049770497804979049800498104982049830498404985049860498704988049890499004991049920499304994049950499604997049980499905000050010500205003050040500505006050070500805009050100501105012050130501405015050160501705018050190502005021050220502305024050250502605027050280502905030050310503205033050340503505036050370503805039050400504105042050430504405045050460504705048050490505005051050520505305054050550505605057050580505905060050610506205063050640506505066050670506805069050700507105072050730507405075050760507705078050790508005081050820508305084050850508605087050880508905090050910509205093050940509505096050970509805099051000510105102051030510405105051060510705108051090511005111051120511305114051150511605117051180511905120051210512205123051240512505126051270512805129051300513105132051330513405135051360513705138051390514005141051420514305144051450514605147051480514905150051510515205153051540515505156051570515805159051600516105162051630516405165051660516705168051690517005171051720517305174051750517605177051780517905180051810518205183051840518505186051870518805189051900519105192051930519405195051960519705198051990520005201052020520305204052050520605207052080520905210052110521205213052140521505216052170521805219052200522105222052230522405225052260522705228052290523005231052320523305234052350523605237052380523905240052410524205243052440524505246052470524805249052500525105252052530525405255052560525705258052590526005261052620526305264052650526605267052680526905270052710527205273052740527505276052770527805279052800528105282052830528405285052860528705288052890529005291052920529305294052950529605297052980529905300053010530205303053040530505306053070530805309053100531105312053130531405315053160531705318053190532005321053220532305324053250532605327053280532905330053310533205333053340533505336053370533805339053400534105342053430534405345053460534705348053490535005351053520535305354053550535605357053580535905360053610536205363053640536505366053670536805369053700537105372053730537405375053760537705378053790538005381053820538305384053850538605387053880538905390053910539205393053940539505396053970539805399054000540105402054030540405405054060540705408054090541005411054120541305414054150541605417054180541905420054210542205423054240542505426054270542805429054300543105432054330543405435054360543705438054390544005441054420544305444054450544605447054480544905450054510545205453054540545505456054570545805459054600546105462054630546405465054660546705468054690547005471054720547305474054750547605477054780547905480054810548205483054840548505486054870548805489054900549105492054930549405495054960549705498054990550005501055020550305504055050550605507055080550905510055110551205513055140551505516055170551805519055200552105522055230552405525055260552705528055290553005531055320553305534055350553605537055380553905540055410554205543055440554505546055470554805549055500555105552055530555405555055560555705558055590556005561055620556305564055650556605567055680556905570055710557205573055740557505576055770557805579055800558105582055830558405585055860558705588055890559005591055920559305594055950559605597055980559905600056010560205603056040560505606056070560805609056100561105612056130561405615056160561705618056190562005621056220562305624056250562605627056280562905630056310563205633056340563505636056370563805639056400564105642056430564405645056460564705648056490565005651056520565305654056550565605657056580565905660056610566205663056640566505666056670566805669056700567105672056730567405675056760567705678056790568005681056820568305684056850568605687056880568905690056910569205693056940569505696056970569805699057000570105702057030570405705057060570705708057090571005711057120571305714057150571605717057180571905720057210572205723057240572505726057270572805729057300573105732057330573405735057360573705738057390574005741057420574305744057450574605747057480574905750057510575205753057540575505756057570575805759057600576105762057630576405765057660576705768057690577005771057720577305774057750577605777057780577905780057810578205783057840578505786057870578805789057900579105792057930579405795057960579705798057990580005801058020580305804058050580605807058080580905810058110581205813058140581505816058170581805819058200582105822058230582405825058260582705828058290583005831058320583305834058350583605837058380583905840058410584205843058440584505846058470584805849058500585105852058530585405855058560585705858058590586005861058620586305864058650586605867058680586905870058710587205873058740587505876058770587805879058800588105882058830588405885058860588705888058890589005891058920589305894058950589605897058980589905900059010590205903059040590505906059070590805909059100591105912059130591405915059160591705918059190592005921059220592305924059250592605927059280592905930059310593205933059340593505936059370593805939059400594105942059430594405945059460594705948059490595005951059520595305954059550595605957059580595905960059610596205963059640596505966059670596805969059700597105972059730597405975059760597705978059790598005981059820598305984059850598605987059880598905990059910599205993059940599505996059970599805999060000600106002060030600406005060060600706008060090601006011060120601306014060150601606017060180601906020060210602206023060240602506026060270602806029060300603106032060330603406035060360603706038060390604006041060420604306044060450604606047060480604906050060510605206053060540605506056060570605806059060600606106062060630606406065060660606706068060690607006071060720607306074060750607606077060780607906080060810608206083060840608506086060870608806089060900609106092060930609406095060960609706098060990610006101061020610306104061050610606107061080610906110061110611206113061140611506116061170611806119061200612106122061230612406125061260612706128061290613006131061320613306134061350613606137061380613906140061410614206143061440614506146061470614806149061500615106152061530615406155061560615706158061590616006161061620616306164061650616606167061680616906170061710617206173061740617506176061770617806179061800618106182061830618406185061860618706188061890619006191061920619306194061950619606197061980619906200062010620206203062040620506206062070620806209062100621106212062130621406215062160621706218062190622006221062220622306224062250622606227062280622906230062310623206233062340623506236062370623806239062400624106242062430624406245062460624706248062490625006251062520625306254062550625606257062580625906260062610626206263062640626506266062670626806269062700627106272062730627406275062760627706278062790628006281062820628306284062850628606287062880628906290062910629206293062940629506296062970629806299063000630106302063030630406305063060630706308063090631006311063120631306314063150631606317063180631906320063210632206323063240632506326063270632806329063300633106332063330633406335063360633706338063390634006341063420634306344063450634606347063480634906350063510635206353063540635506356063570635806359063600636106362063630636406365063660636706368063690637006371063720637306374063750637606377063780637906380063810638206383063840638506386063870638806389063900639106392063930639406395063960639706398063990640006401064020640306404064050640606407064080640906410064110641206413064140641506416064170641806419064200642106422064230642406425064260642706428064290643006431064320643306434064350643606437064380643906440064410644206443064440644506446064470644806449064500645106452064530645406455064560645706458064590646006461064620646306464064650646606467064680646906470064710647206473064740647506476064770647806479064800648106482064830648406485064860648706488064890649006491064920649306494064950649606497064980649906500065010650206503065040650506506065070650806509065100651106512065130651406515065160651706518065190652006521065220652306524065250652606527065280652906530065310653206533065340653506536065370653806539065400654106542065430654406545065460654706548065490655006551065520655306554065550655606557065580655906560065610656206563065640656506566065670656806569065700657106572065730657406575065760657706578065790658006581065820658306584065850658606587065880658906590065910659206593065940659506596065970659806599066000660106602066030660406605066060660706608066090661006611066120661306614066150661606617066180661906620066210662206623066240662506626066270662806629066300663106632066330663406635066360663706638066390664006641066420664306644066450664606647066480664906650066510665206653066540665506656066570665806659066600666106662066630666406665066660666706668066690667006671066720667306674066750667606677066780667906680066810668206683066840668506686066870668806689066900669106692066930669406695066960669706698066990670006701067020670306704067050670606707067080670906710067110671206713067140671506716067170671806719067200672106722067230672406725067260672706728067290673006731067320673306734067350673606737067380673906740067410674206743067440674506746067470674806749067500675106752067530675406755067560675706758067590676006761067620676306764067650676606767067680676906770067710677206773067740677506776067770677806779067800678106782067830678406785067860678706788067890679006791067920679306794067950679606797067980679906800068010680206803068040680506806068070680806809068100681106812068130681406815068160681706818068190682006821068220682306824068250682606827068280682906830068310683206833068340683506836068370683806839068400684106842068430684406845068460684706848068490685006851068520685306854068550685606857068580685906860068610686206863068640686506866068670686806869068700687106872068730687406875068760687706878068790688006881068820688306884068850688606887068880688906890068910689206893068940689506896068970689806899069000690106902069030690406905069060690706908069090691006911069120691306914069150691606917069180691906920069210692206923069240692506926069270692806929069300693106932069330693406935069360693706938069390694006941069420694306944069450694606947069480694906950069510695206953069540695506956069570695806959069600696106962069630696406965069660696706968069690697006971069720697306974069750697606977069780697906980069810698206983069840698506986069870698806989069900699106992069930699406995069960699706998069990700007001070020700307004070050700607007070080700907010070110701207013070140701507016070170701807019070200702107022070230702407025070260702707028070290703007031070320703307034070350703607037070380703907040070410704207043070440704507046070470704807049070500705107052070530705407055070560705707058070590706007061070620706307064070650706607067070680706907070070710707207073070740707507076070770707807079070800708107082070830708407085070860708707088070890709007091070920709307094070950709607097070980709907100071010710207103071040710507106071070710807109071100711107112071130711407115071160711707118071190712007121071220712307124071250712607127071280712907130071310713207133071340713507136071370713807139071400714107142071430714407145071460714707148071490715007151071520715307154071550715607157071580715907160071610716207163071640716507166071670716807169071700717107172071730717407175071760717707178071790718007181071820718307184071850718607187071880718907190071910719207193071940719507196071970719807199072000720107202072030720407205072060720707208072090721007211072120721307214072150721607217072180721907220072210722207223072240722507226072270722807229072300723107232072330723407235072360723707238072390724007241072420724307244072450724607247072480724907250072510725207253072540725507256072570725807259072600726107262072630726407265072660726707268072690727007271072720727307274072750727607277072780727907280072810728207283072840728507286072870728807289072900729107292072930729407295072960729707298072990730007301073020730307304073050730607307073080730907310073110731207313073140731507316073170731807319073200732107322073230732407325073260732707328073290733007331073320733307334073350733607337073380733907340073410734207343073440734507346073470734807349073500735107352073530735407355073560735707358073590736007361073620736307364073650736607367073680736907370073710737207373073740737507376073770737807379073800738107382073830738407385073860738707388073890739007391073920739307394073950739607397073980739907400074010740207403074040740507406074070740807409074100741107412074130741407415074160741707418074190742007421074220742307424074250742607427074280742907430074310743207433074340743507436074370743807439074400744107442074430744407445074460744707448074490745007451074520745307454074550745607457074580745907460074610746207463074640746507466074670746807469074700747107472074730747407475074760747707478074790748007481074820748307484074850748607487074880748907490074910749207493074940749507496074970749807499075000750107502075030750407505075060750707508075090751007511075120751307514075150751607517075180751907520075210752207523075240752507526075270752807529075300753107532075330753407535075360753707538075390754007541075420754307544075450754607547075480754907550075510755207553075540755507556075570755807559075600756107562075630756407565075660756707568075690757007571075720757307574075750757607577075780757907580075810758207583075840758507586075870758807589075900759107592075930759407595075960759707598075990760007601076020760307604076050760607607076080760907610076110761207613076140761507616076170761807619076200762107622076230762407625076260762707628076290763007631076320763307634076350763607637076380763907640076410764207643076440764507646076470764807649076500765107652076530765407655076560765707658076590766007661076620766307664076650766607667076680766907670076710767207673076740767507676076770767807679076800768107682076830768407685076860768707688076890769007691076920769307694076950769607697076980769907700077010770207703077040770507706077070770807709077100771107712077130771407715077160771707718077190772007721077220772307724077250772607727077280772907730077310773207733077340773507736077370773807739077400774107742077430774407745077460774707748077490775007751077520775307754077550775607757077580775907760077610776207763077640776507766077670776807769077700777107772077730777407775077760777707778077790778007781077820778307784077850778607787077880778907790077910779207793077940779507796077970779807799078000780107802078030780407805078060780707808078090781007811078120781307814078150781607817078180781907820078210782207823078240782507826078270782807829078300783107832078330783407835078360783707838078390784007841078420784307844078450784607847078480784907850078510785207853078540785507856078570785807859078600786107862078630786407865078660786707868078690787007871078720787307874078750787607877078780787907880078810788207883078840788507886078870788807889078900789107892078930789407895078960789707898078990790007901079020790307904079050790607907079080790907910079110791207913079140791507916079170791807919079200792107922079230792407925079260792707928079290793007931079320793307934079350793607937079380793907940079410794207943079440794507946079470794807949079500795107952079530795407955079560795707958079590796007961079620796307964079650796607967079680796907970079710797207973079740797507976079770797807979079800798107982079830798407985079860798707988079890799007991079920799307994079950799607997079980799908000080010800208003080040800508006080070800808009080100801108012080130801408015080160801708018080190802008021080220802308024080250802608027080280802908030080310803208033080340803508036080370803808039080400804108042080430804408045080460804708048080490805008051080520805308054080550805608057080580805908060080610806208063080640806508066080670806808069080700807108072080730807408075080760807708078080790808008081080820808308084080850808608087080880808908090080910809208093080940809508096080970809808099081000810108102081030810408105081060810708108081090811008111081120811308114081150811608117081180811908120081210812208123081240812508126081270812808129081300813108132081330813408135081360813708138081390814008141081420814308144081450814608147081480814908150081510815208153081540815508156081570815808159081600816108162081630816408165081660816708168081690817008171081720817308174081750817608177081780817908180081810818208183081840818508186081870818808189081900819108192081930819408195081960819708198081990820008201082020820308204082050820608207082080820908210082110821208213082140821508216082170821808219082200822108222082230822408225082260822708228082290823008231082320823308234082350823608237082380823908240082410824208243082440824508246082470824808249082500825108252082530825408255082560825708258082590826008261082620826308264082650826608267082680826908270082710827208273082740827508276082770827808279082800828108282082830828408285082860828708288082890829008291082920829308294082950829608297082980829908300083010830208303083040830508306083070830808309083100831108312083130831408315083160831708318083190832008321083220832308324083250832608327083280832908330083310833208333083340833508336083370833808339083400834108342083430834408345083460834708348083490835008351083520835308354083550835608357083580835908360083610836208363083640836508366083670836808369083700837108372083730837408375083760837708378083790838008381083820838308384083850838608387083880838908390083910839208393083940839508396083970839808399084000840108402084030840408405084060840708408084090841008411084120841308414084150841608417084180841908420084210842208423084240842508426084270842808429084300843108432084330843408435084360843708438084390844008441084420844308444084450844608447084480844908450084510845208453084540845508456084570845808459084600846108462084630846408465084660846708468084690847008471084720847308474084750847608477084780847908480084810848208483084840848508486084870848808489084900849108492084930849408495084960849708498084990850008501085020850308504085050850608507085080850908510085110851208513085140851508516085170851808519085200852108522085230852408525085260852708528085290853008531085320853308534085350853608537085380853908540085410854208543085440854508546085470854808549085500855108552085530855408555085560855708558085590856008561085620856308564085650856608567085680856908570085710857208573085740857508576085770857808579085800858108582085830858408585085860858708588085890859008591085920859308594085950859608597085980859908600086010860208603086040860508606086070860808609086100861108612086130861408615086160861708618086190862008621086220862308624086250862608627086280862908630086310863208633086340863508636086370863808639086400864108642086430864408645086460864708648086490865008651086520865308654086550865608657086580865908660086610866208663086640866508666086670866808669086700867108672086730867408675086760867708678086790868008681086820868308684086850868608687086880868908690086910869208693086940869508696086970869808699087000870108702087030870408705087060870708708087090871008711087120871308714087150871608717087180871908720087210872208723087240872508726087270872808729087300873108732087330873408735087360873708738087390874008741087420874308744087450874608747087480874908750087510875208753087540875508756087570875808759087600876108762087630876408765087660876708768087690877008771087720877308774087750877608777087780877908780087810878208783087840878508786087870878808789087900879108792087930879408795087960879708798087990880008801088020880308804088050880608807088080880908810088110881208813088140881508816088170881808819088200882108822088230882408825088260882708828088290883008831088320883308834088350883608837088380883908840088410884208843088440884508846088470884808849088500885108852088530885408855088560885708858088590886008861088620886308864088650886608867088680886908870088710887208873088740887508876088770887808879088800888108882088830888408885088860888708888088890889008891088920889308894088950889608897088980889908900089010890208903089040890508906089070890808909089100891108912089130891408915089160891708918089190892008921089220892308924089250892608927089280892908930089310893208933089340893508936089370893808939089400894108942089430894408945089460894708948089490895008951089520895308954089550895608957089580895908960089610896208963089640896508966089670896808969089700897108972089730897408975089760897708978089790898008981089820898308984089850898608987089880898908990089910899208993089940899508996089970899808999090000900109002090030900409005090060900709008090090901009011090120901309014090150901609017090180901909020090210902209023090240902509026090270902809029090300903109032090330903409035090360903709038090390904009041090420904309044090450904609047090480904909050090510905209053090540905509056090570905809059090600906109062090630906409065090660906709068090690907009071090720907309074090750907609077090780907909080090810908209083090840 // Written in the D programming language module dparse.parser; import dparse.lexer; import dparse.ast; import dparse.rollback_allocator; import dparse.stack_buffer; import std.experimental.allocator.mallocator; import std.experimental.allocator; import std.conv; import std.algorithm; import std.array; import std.string : format; // Uncomment this if you want ALL THE OUTPUT // Caution: generates 180 megabytes of logging for std.datetime //version = dparse_verbose; /** * Prototype for a custom parser message function or delegate. * Parameters passed are a file name, a line, a column, a message and a `bool` * that indicates if the message is a warning (`false`) or a if it's an error (`true`). */ alias MessageFunction = void function(string fileName , size_t line, size_t column, string message, bool isError); /// ditto alias MessageDelegate = void delegate(string, size_t, size_t, string, bool); /** * Parser configuration struct */ struct ParserConfig { /// The tokens parsed by dparse.lexer. const(Token)[] tokens; /// The name of the file being parsed string fileName; /// A pointer to a rollback allocator. RollbackAllocator* allocator; /// An optional function used to handle warnings and errors. MessageFunction messageFunction; /// An optional delegate used to handle warnings and errors. /// Set either this one or messageFunction, not both. MessageDelegate messageDelegate; /// An optional pointer to a variable receiving the error count. uint* errorCount; /// An optional pointer to a variable receiving the warning count. uint* warningCount; } /** * Params: * parserConfig = a parser configuration. * Returns: * The parsed module. */ Module parseModule()(auto ref ParserConfig parserConfig) { auto parser = new Parser(); with (parserConfig) { parser.fileName = fileName; parser.tokens = tokens; parser.messageFunction = messageFunction; parser.messageDelegate = messageDelegate; parser.allocator = allocator; } Module mod = parser.parseModule(); with (parserConfig) { if (warningCount !is null) *warningCount = parser.warningCount; if (errorCount !is null) *errorCount = parser.errorCount; } return mod; } /** * Params: * tokens = The tokens parsed by dparse.lexer. * fileName = The name of the file being parsed. * allocator = A pointer to a rollback allocator. * messageFuncOrDg = Either a function or a delegate that receives the parser messages. * errorCount = An optional pointer to a variable receiving the error count. * warningCount = An optional pointer to a variable receiving the warning count. * Returns: * The parsed module. */ Module parseModule(F)(const(Token)[] tokens, string fileName, RollbackAllocator* allocator, F messageFuncOrDg = null, uint* errorCount = null, uint* warningCount = null) { static if (is(F)) { static if (is(F : MessageFunction)) return ParserConfig(tokens, fileName, allocator, messageFuncOrDg, null, errorCount, warningCount).parseModule(); else static if (is(F : MessageDelegate)) return ParserConfig(tokens, fileName, allocator, null, messageFuncOrDg, errorCount, warningCount).parseModule(); else static assert(0, "F must be a MessageFunction or a MessageDelegate"); } else { return ParserConfig(tokens, fileName, allocator, null, null, null, null).parseModule(); } } /** * D Parser. * * It is sometimes useful to sub-class Parser to skip over things that are not * interesting. For example, DCD skips over function bodies when caching symbols * from imported files. */ class Parser { /** * Parses an AddExpression. * * $(GRAMMAR $(RULEDEF addExpression): * $(RULE mulExpression) * | $(RULE addExpression) $(LPAREN)$(LITERAL '+') | $(LITERAL'-') | $(LITERAL'~')$(RPAREN) $(RULE mulExpression) * ;) */ ExpressionNode parseAddExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AddExpression, MulExpression, tok!"+", tok!"-", tok!"~")(); } /** * Parses an AliasDeclaration. * * $(GRAMMAR $(RULEDEF aliasDeclaration): * $(LITERAL 'alias') $(RULE aliasInitializer) $(LPAREN)$(LITERAL ',') $(RULE aliasInitializer)$(RPAREN)* $(LITERAL ';') * | $(LITERAL 'alias') $(RULE storageClass)* $(RULE type) $(RULE declaratorIdentifierList) $(LITERAL ';') * | $(LITERAL 'alias') $(RULE storageClass)* $(RULE type) $(RULE identifier) $(LITERAL '(') $(RULE parameters) $(LITERAL ')') $(memberFunctionAttribute)* $(LITERAL ';') * ;) */ AliasDeclaration parseAliasDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AliasDeclaration; mixin(tokenCheck!"alias"); node.comment = comment; comment = null; if (startsWith(tok!"identifier", tok!"=") || startsWith(tok!"identifier", tok!"(")) { StackBuffer initializers; do { if (!initializers.put(parseAliasInitializer())) return null; if (currentIs(tok!",")) advance(); else break; } while (moreTokens()); ownArray(node.initializers, initializers); } else { StackBuffer storageClasses; while (moreTokens() && isStorageClass()) if (!storageClasses.put(parseStorageClass())) return null; ownArray(node.storageClasses, storageClasses); mixin (parseNodeQ!(`node.type`, `Type`)); mixin (parseNodeQ!(`node.declaratorIdentifierList`, `DeclaratorIdentifierList`)); if (currentIs(tok!"(")) { mixin(parseNodeQ!(`node.parameters`, `Parameters`)); StackBuffer memberFunctionAttributes; while (moreTokens() && currentIsMemberFunctionAttribute()) if (!memberFunctionAttributes.put(parseMemberFunctionAttribute())) return null; ownArray(node.memberFunctionAttributes, memberFunctionAttributes); } } return attachCommentFromSemicolon(node, startIndex); } /** * Parses an AliasAssign. * * $(GRAMMAR $(RULEDEF aliasAssign): * $(LITERAL Identifier) $(LITERAL '=') $(RULE type) */ AliasAssign parseAliasAssign() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AliasAssign; node.comment = comment; comment = null; mixin(tokenCheck!(`node.identifier`, "identifier")); mixin(tokenCheck!"="); mixin(parseNodeQ!(`node.type`, `Type`)); return attachCommentFromSemicolon(node, startIndex); } /** * Parses an AliasInitializer. * * $(GRAMMAR $(RULEDEF aliasInitializer): * $(LITERAL Identifier) $(RULE templateParameters)? $(LITERAL '=') $(RULE storageClass)* $(RULE type) * | $(LITERAL Identifier) $(RULE templateParameters)? $(LITERAL '=') $(RULE storageClass)* $(RULE type) $(RULE parameters) $(RULE memberFunctionAttribute)* * | $(LITERAL Identifier) $(RULE templateParameters)? $(LITERAL '=') $(RULE functionLiteralExpression) * ;) */ AliasInitializer parseAliasInitializer() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AliasInitializer; mixin (tokenCheck!(`node.name`, "identifier")); if (currentIs(tok!"(")) mixin (parseNodeQ!(`node.templateParameters`, `TemplateParameters`)); mixin(tokenCheck!"="); bool isFunction() { if (currentIsOneOf(tok!"function", tok!"delegate", tok!"{")) return true; if (startsWith(tok!"identifier", tok!"=>")) return true; const b = setBookmark(); scope(exit) goToBookmark(b); if (currentIs(tok!"(") || currentIs(tok!"ref") && peekIs(tok!"(")) { if (currentIs(tok!"ref")) advance(); const t = peekPastParens(); if (t !is null) { if (t.type == tok!"=>" || t.type == tok!"{" || isMemberFunctionAttribute(t.type)) return true; } } return false; } if (isFunction) mixin (parseNodeQ!(`node.functionLiteralExpression`, `FunctionLiteralExpression`)); else { StackBuffer storageClasses; while (moreTokens() && isStorageClass()) if (!storageClasses.put(parseStorageClass())) return null; ownArray(node.storageClasses, storageClasses); mixin (parseNodeQ!(`node.type`, `Type`)); if (currentIs(tok!"(")) { mixin (parseNodeQ!(`node.parameters`, `Parameters`)); StackBuffer memberFunctionAttributes; while (moreTokens() && currentIsMemberFunctionAttribute()) if (!memberFunctionAttributes.put(parseMemberFunctionAttribute())) return null; ownArray(node.memberFunctionAttributes, memberFunctionAttributes); } } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AliasThisDeclaration. * * $(GRAMMAR $(RULEDEF aliasThisDeclaration): * $(LITERAL 'alias') $(LITERAL Identifier) $(LITERAL 'this') $(LITERAL ';') * ;) */ AliasThisDeclaration parseAliasThisDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AliasThisDeclaration; mixin(tokenCheck!"alias"); mixin(tokenCheck!(`node.identifier`, "identifier")); mixin(tokenCheck!"this"); return attachCommentFromSemicolon(node, startIndex); } /** * Parses an AlignAttribute. * * $(GRAMMAR $(RULEDEF alignAttribute): * $(LITERAL 'align') ($(LITERAL '$(LPAREN)') $(RULE assignExpression) $(LITERAL '$(RPAREN)'))? * ;) */ AlignAttribute parseAlignAttribute() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AlignAttribute; expect(tok!"align"); if (currentIs(tok!"(")) { mixin(tokenCheck!"("); mixin(parseNodeQ!("node.assignExpression", "AssignExpression")); mixin(tokenCheck!")"); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AndAndExpression. * * $(GRAMMAR $(RULEDEF andAndExpression): * $(RULE orExpression) * | $(RULE andAndExpression) $(LITERAL '&&') $(RULE orExpression) * ;) */ ExpressionNode parseAndAndExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AndAndExpression, OrExpression, tok!"&&")(); } /** * Parses an AndExpression. * * $(GRAMMAR $(RULEDEF andExpression): * $(RULE cmpExpression) * | $(RULE andExpression) $(LITERAL '&') $(RULE cmpExpression) * ;) */ ExpressionNode parseAndExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AndExpression, CmpExpression, tok!"&")(); } /** * Parses an ArgumentList. * * $(GRAMMAR $(RULEDEF argumentList): * $(RULE assignExpression) ($(LITERAL ',') $(RULE assignExpression)?)* * ;) */ ArgumentList parseArgumentList() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; if (!moreTokens) { error("argument list expected instead of EOF"); return null; } size_t startLocation = current().index; auto node = parseCommaSeparatedRule!(ArgumentList, AssignExpression)(true); mixin (nullCheck!`node`); node.startLocation = startLocation; if (moreTokens) node.endLocation = current().index; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses Arguments. * * $(GRAMMAR $(RULEDEF arguments): * $(LITERAL '$(LPAREN)') $(RULE argumentList)? $(LITERAL '$(RPAREN)') * ;) */ Arguments parseArguments() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Arguments; mixin(tokenCheck!"("); if (!currentIs(tok!")")) mixin (parseNodeQ!(`node.argumentList`, `ArgumentList`)); mixin(tokenCheck!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an ArrayInitializer. * * $(GRAMMAR $(RULEDEF arrayInitializer): * $(LITERAL '[') $(LITERAL ']') * | $(LITERAL '[') $(RULE arrayMemberInitialization) ($(LITERAL ',') $(RULE arrayMemberInitialization)?)* $(LITERAL ']') * ;) */ ArrayInitializer parseArrayInitializer() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ArrayInitializer; const open = expect(tok!"["); mixin (nullCheck!`open`); node.startLocation = open.index; StackBuffer arrayMemberInitializations; while (moreTokens()) { if (currentIs(tok!"]")) break; if (!arrayMemberInitializations.put(parseArrayMemberInitialization())) return null; if (currentIs(tok!",")) advance(); else break; } ownArray(node.arrayMemberInitializations, arrayMemberInitializations); const close = expect(tok!"]"); mixin (nullCheck!`close`); node.endLocation = close.index; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an ArrayLiteral. * * $(GRAMMAR $(RULEDEF arrayLiteral): * $(LITERAL '[') $(RULE argumentList)? $(LITERAL ']') * ;) */ ArrayLiteral parseArrayLiteral() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ArrayLiteral; mixin(tokenCheck!"["); if (!currentIs(tok!"]")) mixin (parseNodeQ!(`node.argumentList`, `ArgumentList`)); mixin(tokenCheck!"]"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an ArrayMemberInitialization. * * $(GRAMMAR $(RULEDEF arrayMemberInitialization): * ($(RULE assignExpression) $(LITERAL ':'))? $(RULE nonVoidInitializer) * ;) */ ArrayMemberInitialization parseArrayMemberInitialization() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ArrayMemberInitialization; switch (current.type) { case tok!"[": immutable b = setBookmark(); skipBrackets(); if (currentIs(tok!":")) { goToBookmark(b); mixin (parseNodeQ!(`node.assignExpression`, `AssignExpression`)); advance(); // : mixin (parseNodeQ!(`node.nonVoidInitializer`, `NonVoidInitializer`)); break; } else { goToBookmark(b); goto case; } case tok!"{": mixin (parseNodeQ!(`node.nonVoidInitializer`, `NonVoidInitializer`)); break; default: auto assignExpression = parseAssignExpression(); mixin (nullCheck!`assignExpression`); if (currentIs(tok!":")) { node.assignExpression = assignExpression; advance(); mixin(parseNodeQ!(`node.nonVoidInitializer`, `NonVoidInitializer`)); } else { node.nonVoidInitializer = allocator.make!NonVoidInitializer; node.nonVoidInitializer.assignExpression = assignExpression; } } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AsmAddExp * * $(GRAMMAR $(RULEDEF asmAddExp): * $(RULE asmMulExp) * | $(RULE asmAddExp) ($(LITERAL '+') | $(LITERAL '-')) $(RULE asmMulExp) * ;) */ ExpressionNode parseAsmAddExp() { mixin (traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmAddExp, AsmMulExp, tok!"+", tok!"-")(); } /** * Parses an AsmAndExp * * $(GRAMMAR $(RULEDEF asmAndExp): * $(RULE asmEqualExp) * | $(RULE asmAndExp) $(LITERAL '&') $(RULE asmEqualExp) * ;) */ ExpressionNode parseAsmAndExp() { mixin (traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmAndExp, AsmEqualExp, tok!"&"); } /** * Parses an AsmBrExp * * $(GRAMMAR $(RULEDEF asmBrExp): * $(RULE asmUnaExp) * | $(RULE asmBrExp)? $(LITERAL '[') $(RULE asmExp) $(LITERAL ']') * ;) */ AsmBrExp parseAsmBrExp() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; if (!moreTokens) { error("Found end-of-file when expecting an AsmBrExp", false); return null; } AsmBrExp node = allocator.make!AsmBrExp(); size_t line = current.line; size_t column = current.column; if (currentIs(tok!"[")) { advance(); // [ mixin (parseNodeQ!(`node.asmExp`, `AsmExp`)); mixin(tokenCheck!"]"); if (currentIs(tok!"[")) goto brLoop; } else { mixin(parseNodeQ!(`node.asmUnaExp`, `AsmUnaExp`)); brLoop: while (currentIs(tok!"[")) { AsmBrExp br = allocator.make!AsmBrExp(); // huehuehuehue br.asmBrExp = node; br.line = current().line; br.column = current().column; node = br; node.line = line; node.column = column; advance(); // [ mixin(parseNodeQ!(`node.asmExp`, `AsmExp`)); mixin(tokenCheck!"]"); } } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AsmEqualExp * * $(GRAMMAR $(RULEDEF asmEqualExp): * $(RULE asmRelExp) * | $(RULE asmEqualExp) ('==' | '!=') $(RULE asmRelExp) * ;) */ ExpressionNode parseAsmEqualExp() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmEqualExp, AsmRelExp, tok!"==", tok!"!=")(); } /** * Parses an AsmExp * * $(GRAMMAR $(RULEDEF asmExp): * $(RULE asmLogOrExp) ($(LITERAL '?') $(RULE asmExp) $(LITERAL ':') $(RULE asmExp))? * ;) */ ExpressionNode parseAsmExp() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; AsmExp node = allocator.make!AsmExp; mixin(parseNodeQ!(`node.left`, `AsmLogOrExp`)); if (currentIs(tok!"?")) { advance(); mixin(parseNodeQ!(`node.middle`, `AsmExp`)); mixin(tokenCheck!":"); mixin(parseNodeQ!(`node.right`, `AsmExp`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AsmInstruction * * $(GRAMMAR $(RULEDEF asmInstruction): * $(LITERAL Identifier) * | $(LITERAL 'align') $(LITERAL IntegerLiteral) * | $(LITERAL 'align') $(LITERAL Identifier) * | $(LITERAL Identifier) $(LITERAL ':') $(RULE asmInstruction) * | $(LITERAL Identifier) $(RULE operands) * | $(LITERAL 'in') $(RULE operands) * | $(LITERAL 'out') $(RULE operands) * | $(LITERAL 'int') $(RULE operands) * | $(LITERAL ';') * ;) */ AsmInstruction parseAsmInstruction(ref bool maybeGccASm) { mixin (traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; AsmInstruction node = allocator.make!AsmInstruction; if (currentIs(tok!";")) { warn("Empty asm instruction"); node.tokens = tokens[startIndex .. index]; return node; } if (currentIs(tok!"align")) { advance(); // align node.hasAlign = true; if (currentIsOneOf(tok!"intLiteral", tok!"identifier")) { node.identifierOrIntegerOrOpcode = advance(); if (!currentIs(tok!";")) { error("`;` expected."); if (moreTokens()) advance(); return null; } } else { error("Identifier or integer literal expected."); return null; } } else if (currentIsOneOf(tok!"identifier", tok!"in", tok!"out", tok!"int")) { node.identifierOrIntegerOrOpcode = advance(); if (node.identifierOrIntegerOrOpcode == tok!"identifier" && currentIs(tok!":")) { advance(); // : node.isLabel = true; if (currentIs(tok!";")) { node.tokens = tokens[startIndex .. index]; return node; } node.asmInstruction = parseAsmInstruction(maybeGccASm); if (node.asmInstruction is null) return null; } else if (!currentIs(tok!";")) mixin(parseNodeQ!(`node.operands`, `Operands`)); } else { maybeGccASm = true; return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AsmLogAndExp * * $(GRAMMAR $(RULEDEF asmLogAndExp): * $(RULE asmOrExp) * $(RULE asmLogAndExp) $(LITERAL '&&') $(RULE asmOrExp) * ;) */ ExpressionNode parseAsmLogAndExp() { mixin (traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmLogAndExp, AsmOrExp, tok!"&&"); } /** * Parses an AsmLogOrExp * * $(GRAMMAR $(RULEDEF asmLogOrExp): * $(RULE asmLogAndExp) * | $(RULE asmLogOrExp) '||' $(RULE asmLogAndExp) * ;) */ ExpressionNode parseAsmLogOrExp() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmLogOrExp, AsmLogAndExp, tok!"||")(); } /** * Parses an AsmMulExp * * $(GRAMMAR $(RULEDEF asmMulExp): * $(RULE asmBrExp) * | $(RULE asmMulExp) ($(LITERAL '*') | $(LITERAL '/') | $(LITERAL '%')) $(RULE asmBrExp) * ;) */ ExpressionNode parseAsmMulExp() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmMulExp, AsmBrExp, tok!"*", tok!"/", tok!"%")(); } /** * Parses an AsmOrExp * * $(GRAMMAR $(RULEDEF asmOrExp): * $(RULE asmXorExp) * | $(RULE asmOrExp) $(LITERAL '|') $(RULE asmXorExp) * ;) */ ExpressionNode parseAsmOrExp() { mixin (traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmOrExp, AsmXorExp, tok!"|")(); } /** * Parses an AsmPrimaryExp * * $(GRAMMAR $(RULEDEF asmPrimaryExp): * $(LITERAL IntegerLiteral) * | $(LITERAL FloatLiteral) * | $(LITERAL StringLiteral) * | $(RULE register) * | $(RULE register : AsmExp) * | $(RULE identifierChain) * | $(LITERAL '$') * | $(LITERAL 'this') * | $(LITERAL '__LOCAL_SIZE') * ;) */ AsmPrimaryExp parseAsmPrimaryExp() { import std.range : assumeSorted; mixin (traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; AsmPrimaryExp node = allocator.make!AsmPrimaryExp(); switch (current().type) { foreach (NL; NumberLiterals) {case NL:} case tok!"stringLiteral": case tok!"$": case tok!"this": node.token = advance(); break; case tok!"identifier": if (assumeSorted(REGISTER_NAMES).equalRange(current().text).length > 0) { trace("Found register"); mixin (nullCheck!`(node.register = parseRegister())`); if (currentIs(tok!":")) { advance(); mixin(parseNodeQ!(`node.segmentOverrideSuffix`, `AsmExp`)); } } else mixin(parseNodeQ!(`node.identifierChain`, `IdentifierChain`)); break; default: error("Float literal, integer literal, `$`, `this` or identifier expected."); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AsmRelExp * * $(GRAMMAR $(RULEDEF asmRelExp): * $(RULE asmShiftExp) * | $(RULE asmRelExp) (($(LITERAL '<') | $(LITERAL '<=') | $(LITERAL '>') | $(LITERAL '>=')) $(RULE asmShiftExp))? * ;) */ ExpressionNode parseAsmRelExp() { mixin (traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmRelExp, AsmShiftExp, tok!"<", tok!"<=", tok!">", tok!">=")(); } /** * Parses an AsmShiftExp * * $(GRAMMAR $(RULEDEF asmShiftExp): * $(RULE asmAddExp) * $(RULE asmShiftExp) ($(LITERAL '<<') | $(LITERAL '>>') | $(LITERAL '>>>')) $(RULE asmAddExp) * ;) */ ExpressionNode parseAsmShiftExp() { mixin (traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmShiftExp, AsmAddExp, tok!"<<", tok!">>", tok!">>>"); } /** * Parses an AsmStatement * * $(GRAMMAR $(RULEDEF asmStatement): * $(LITERAL 'asm') $(RULE functionAttributes)? $(LITERAL '{') ( $(RULE asmInstruction)+ | $(RULE gccAsmInstruction)+ ) $(LITERAL '}') * ;) */ AsmStatement parseAsmStatement() { mixin (traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; AsmStatement node = allocator.make!AsmStatement; advance(); // asm StackBuffer functionAttributes; while (isAttribute()) { if (!functionAttributes.put(parseFunctionAttribute())) { error("Function attribute or `{` expected"); return null; } } ownArray(node.functionAttributes, functionAttributes); expect(tok!"{"); // DMD-style and GCC-style assembly might look identical in the beginning. // Try DMD style first and restart with GCC if it fails because of GCC elements bool maybeGccStyle; const instrStart = allocator.setCheckpoint(); const instrStartIdx = index; StackBuffer instructions; while (moreTokens() && !currentIs(tok!"}")) { auto c = allocator.setCheckpoint(); if (!instructions.put(parseAsmInstruction(maybeGccStyle))) { if (maybeGccStyle) break; allocator.rollback(c); } else expect(tok!";"); } if (!maybeGccStyle) { ownArray(node.asmInstructions, instructions); } else { // Revert to the beginning of the first instruction destroy(instructions); allocator.rollback(instrStart); index = instrStartIdx; while (moreTokens() && !currentIs(tok!"}")) { auto c = allocator.setCheckpoint(); if (!instructions.put(parseGccAsmInstruction())) allocator.rollback(c); else expect(tok!";"); } ownArray(node.gccAsmInstructions, instructions); } expect(tok!"}"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AsmTypePrefix * * Note that in the following grammar definition the first identifier must * be "near", "far", "word", "dword", or "qword". The second identifier must * be "ptr". * * $(GRAMMAR $(RULEDEF asmTypePrefix): * $(LITERAL Identifier) $(LITERAL Identifier)? * | $(LITERAL 'byte') $(LITERAL Identifier)? * | $(LITERAL 'short') $(LITERAL Identifier)? * | $(LITERAL 'int') $(LITERAL Identifier)? * | $(LITERAL 'float') $(LITERAL Identifier)? * | $(LITERAL 'double') $(LITERAL Identifier)? * | $(LITERAL 'real') $(LITERAL Identifier)? * ;) */ AsmTypePrefix parseAsmTypePrefix() { mixin (traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; switch (current().type) { case tok!"identifier": case tok!"byte": case tok!"short": case tok!"int": case tok!"float": case tok!"double": case tok!"real": AsmTypePrefix node = allocator.make!AsmTypePrefix(); node.left = advance(); if (node.left.type == tok!"identifier") switch (node.left.text) { case "near": case "far": case "word": case "dword": case "qword": break; default: error("ASM type node expected"); return null; } if (currentIs(tok!"identifier") && current().text == "ptr") node.right = advance(); node.tokens = tokens[startIndex .. index]; return node; default: error("Expected an identifier, `byte`, `short`, `int`, `float`, `double`, or `real`"); return null; } } /** * Parses an AsmUnaExp * * $(GRAMMAR $(RULEDEF asmUnaExp): * $(RULE asmTypePrefix) $(RULE asmExp) * | $(LITERAL Identifier) $(RULE asmExp) * | $(LITERAL '+') $(RULE asmUnaExp) * | $(LITERAL '-') $(RULE asmUnaExp) * | $(LITERAL '!') $(RULE asmUnaExp) * | $(LITERAL '~') $(RULE asmUnaExp) * | $(RULE asmPrimaryExp) * ;) */ AsmUnaExp parseAsmUnaExp() { mixin (traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; AsmUnaExp node = allocator.make!AsmUnaExp(); switch (current().type) { case tok!"+": case tok!"-": case tok!"!": case tok!"~": node.prefix = advance(); mixin(parseNodeQ!(`node.asmUnaExp`, `AsmUnaExp`)); break; case tok!"byte": case tok!"short": case tok!"int": case tok!"float": case tok!"double": case tok!"real": typePrefix: mixin(parseNodeQ!(`node.asmTypePrefix`, `AsmTypePrefix`)); mixin(parseNodeQ!(`node.asmExp`, `AsmExp`)); break; case tok!"identifier": switch (current().text) { case "offsetof": case "seg": node.prefix = advance(); mixin(parseNodeQ!(`node.asmExp`, `AsmExp`)); break; case "near": case "far": case "word": case "dword": case "qword": goto typePrefix; default: goto outerDefault; } break; outerDefault: default: mixin(parseNodeQ!(`node.asmPrimaryExp`, `AsmPrimaryExp`)); break; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AsmXorExp * * $(GRAMMAR $(RULEDEF asmXorExp): * $(RULE asmAndExp) * | $(RULE asmXorExp) $(LITERAL '^') $(RULE asmAndExp) * ;) */ ExpressionNode parseAsmXorExp() { mixin (traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmXorExp, AsmAndExp, tok!"^")(); } /** * Parses an AssertArguments * * $(GRAMMAR $(RULEDEF assertArguments): * $(RULE assignExpression) ($(LITERAL ',') $(RULE assignExpression))? $(LITERAL ',')? * ;) */ AssertArguments parseAssertArguments() { auto startIndex = index; auto node = allocator.make!AssertArguments; mixin(parseNodeQ!(`node.assertion`, `AssignExpression`)); if (currentIs(tok!",")) advance(); if (currentIs(tok!")")) return node; mixin(parseNodeQ!(`node.message`, `AssignExpression`)); if (currentIs(tok!",")) advance(); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AssertExpression * * $(GRAMMAR $(RULEDEF assertExpression): * $(LITERAL 'assert') $(LITERAL '$(LPAREN)') $(RULE assertArguments) $(LITERAL '$(RPAREN)') * ;) */ AssertExpression parseAssertExpression() { auto startIndex = index; mixin(traceEnterAndExit!(__FUNCTION__)); auto node = allocator.make!AssertExpression; node.line = current.line; node.column = current.column; advance(); // "assert" mixin(tokenCheck!"("); mixin(parseNodeQ!(`node.assertArguments`, `AssertArguments`)); mixin(tokenCheck!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AssignExpression * * $(GRAMMAR $(RULEDEF assignExpression): * $(RULE ternaryExpression) ($(RULE assignOperator) $(RULE assignExpression))? * ; *$(RULEDEF assignOperator): * $(LITERAL '=') * | $(LITERAL '>>>=') * | $(LITERAL '>>=') * | $(LITERAL '<<=') * | $(LITERAL '+=') * | $(LITERAL '-=') * | $(LITERAL '*=') * | $(LITERAL '%=') * | $(LITERAL '&=') * | $(LITERAL '/=') * | $(LITERAL '|=') * | $(LITERAL '^^=') * | $(LITERAL '^=') * | $(LITERAL '~=') * ;) */ ExpressionNode parseAssignExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; if (!moreTokens) { error("Assign expression expected instead of EOF"); return null; } auto ternary = parseTernaryExpression(); if (ternary is null) return null; if (currentIsOneOf(tok!"=", tok!">>>=", tok!">>=", tok!"<<=", tok!"+=", tok!"-=", tok!"*=", tok!"%=", tok!"&=", tok!"/=", tok!"|=", tok!"^^=", tok!"^=", tok!"~=")) { auto node = allocator.make!AssignExpression; node.line = current().line; node.column = current().column; node.ternaryExpression = ternary; node.operator = advance().type; mixin(parseNodeQ!(`node.expression`, `AssignExpression`)); node.tokens = tokens[startIndex .. index]; return node; } return ternary; } /** * Parses an AssocArrayLiteral * * $(GRAMMAR $(RULEDEF assocArrayLiteral): * $(LITERAL '[') $(RULE keyValuePairs) $(LITERAL ']') * ;) */ AssocArrayLiteral parseAssocArrayLiteral() { mixin(traceEnterAndExit!(__FUNCTION__)); mixin (simpleParse!(AssocArrayLiteral, tok!"[", "keyValuePairs|parseKeyValuePairs", tok!"]")); } /** * Parses an AtAttribute * * $(GRAMMAR $(RULEDEF atAttribute): * $(LITERAL '@') $(LITERAL Identifier) * | $(LITERAL '@') $(LITERAL Identifier) $(LITERAL '$(LPAREN)') $(RULE argumentList)? $(LITERAL '$(RPAREN)') * | $(LITERAL '@') $(LITERAL '$(LPAREN)') $(RULE argumentList) $(LITERAL '$(RPAREN)') * | $(LITERAL '@') $(RULE templateInstance) * ;) */ AtAttribute parseAtAttribute() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AtAttribute; const start = expect(tok!"@"); mixin (nullCheck!`start`); if (!moreTokens) { error("`(`, or identifier expected"); return null; } node.startLocation = start.index; switch (current.type) { case tok!"identifier": if (peekIs(tok!"!")) mixin(parseNodeQ!(`node.templateInstance`, `TemplateInstance`)); else node.identifier = advance(); if (currentIs(tok!"(")) { advance(); // ( node.useParen = true; if (!currentIs(tok!")")) mixin(parseNodeQ!(`node.argumentList`, `ArgumentList`)); expect(tok!")"); } break; case tok!"(": advance(); node.useParen = true; mixin(parseNodeQ!(`node.argumentList`, `ArgumentList`)); expect(tok!")"); break; default: error("`(`, or identifier expected"); return null; } if (moreTokens) node.endLocation = current().index; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an Attribute * * $(GRAMMAR $(RULEDEF attribute): * $(RULE pragmaExpression) * | $(RULE alignAttribute) * | $(RULE deprecated) * | $(RULE atAttribute) * | $(RULE linkageAttribute) * | $(LITERAL 'export') * | $(LITERAL 'package') ($(LITERAL "(") $(RULE identifierChain) $(LITERAL ")"))? * | $(LITERAL 'private') * | $(LITERAL 'protected') * | $(LITERAL 'public') * | $(LITERAL 'static') * | $(LITERAL 'extern') * | $(LITERAL 'abstract') * | $(LITERAL 'final') * | $(LITERAL 'override') * | $(LITERAL 'synchronized') * | $(LITERAL 'auto') * | $(LITERAL 'scope') * | $(LITERAL 'const') * | $(LITERAL 'immutable') * | $(LITERAL 'inout') * | $(LITERAL 'shared') * | $(LITERAL '__gshared') * | $(LITERAL 'nothrow') * | $(LITERAL 'pure') * | $(LITERAL 'ref') * | $(LITERAL 'throw') * ;) */ Attribute parseAttribute() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Attribute; switch (current.type) { case tok!"pragma": mixin(parseNodeQ!(`node.pragmaExpression`, `PragmaExpression`)); break; case tok!"deprecated": mixin(parseNodeQ!(`node.deprecated_`, `Deprecated`)); break; case tok!"align": mixin(parseNodeQ!(`node.alignAttribute`, `AlignAttribute`)); break; case tok!"@": mixin(parseNodeQ!(`node.atAttribute`, `AtAttribute`)); break; case tok!"package": node.attribute = advance(); if (currentIs(tok!"(")) { expect(tok!"("); mixin(parseNodeQ!(`node.identifierChain`, `IdentifierChain`)); expect(tok!")"); } break; case tok!"extern": if (peekIs(tok!"(")) { mixin(parseNodeQ!(`node.linkageAttribute`, `LinkageAttribute`)); break; } else goto case; case tok!"private": case tok!"protected": case tok!"public": case tok!"export": case tok!"static": case tok!"abstract": case tok!"final": case tok!"override": case tok!"synchronized": case tok!"auto": case tok!"scope": case tok!"const": case tok!"immutable": case tok!"inout": case tok!"shared": case tok!"__gshared": case tok!"nothrow": case tok!"pure": case tok!"ref": case tok!"throw": node.attribute = advance(); break; default: return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AttributeDeclaration * * $(GRAMMAR $(RULEDEF attributeDeclaration): * $(RULE _attribute) $(LITERAL ':') * ;) */ AttributeDeclaration parseAttributeDeclaration(Attribute attribute = null) { auto startIndex = index; auto node = allocator.make!AttributeDeclaration; node.line = current.line; node.attribute = attribute is null ? parseAttribute() : attribute; expect(tok!":"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AutoDeclaration * * $(GRAMMAR $(RULEDEF autoDeclaration): * $(RULE storageClass)+ $(RULE autoDeclarationPart) ($(LITERAL ',') $(RULE autoDeclarationPart))* $(LITERAL ';') * ;) */ AutoDeclaration parseAutoDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AutoDeclaration; node.comment = comment; comment = null; StackBuffer storageClasses; while (isStorageClass()) if (!storageClasses.put(parseStorageClass())) return null; ownArray(node.storageClasses, storageClasses); StackBuffer parts; do { if (!parts.put(parseAutoDeclarationPart())) return null; if (currentIs(tok!",")) advance(); else break; } while (moreTokens()); ownArray(node.parts, parts); return attachCommentFromSemicolon(node, startIndex); } /** * Parses an AutoDeclarationPart * * $(GRAMMAR $(RULEDEF autoDeclarationPart): * $(LITERAL Identifier) $(RULE templateParameters)? $(LITERAL '=') $(RULE initializer) * ;) */ AutoDeclarationPart parseAutoDeclarationPart() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto part = allocator.make!AutoDeclarationPart; auto i = expect(tok!"identifier"); if (i is null) return null; part.identifier = *i; if (currentIs(tok!"(")) mixin(parseNodeQ!("part.templateParameters", "TemplateParameters")); mixin(tokenCheck!"="); mixin(parseNodeQ!("part.initializer", "Initializer")); part.tokens = tokens[startIndex .. index]; return part; } /** * Parses a BlockStatement * * $(GRAMMAR $(RULEDEF blockStatement): * $(LITERAL '{') $(RULE declarationsAndStatements)? $(LITERAL '}') * ;) */ BlockStatement parseBlockStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!BlockStatement; const openBrace = expect(tok!"{"); mixin (nullCheck!`openBrace`); node.startLocation = openBrace.index; if (!currentIs(tok!"}")) { mixin(parseNodeQ!(`node.declarationsAndStatements`, `DeclarationsAndStatements`)); } const closeBrace = expect(tok!"}"); if (closeBrace !is null) node.endLocation = closeBrace.index; else { trace("Could not find end of block statement."); node.endLocation = size_t.max; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a BreakStatement * * $(GRAMMAR $(RULEDEF breakStatement): * $(LITERAL 'break') $(LITERAL Identifier)? $(LITERAL ';') * ;) */ BreakStatement parseBreakStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; expect(tok!"break"); if (!moreTokens) return null; auto node = allocator.make!BreakStatement; switch (current.type) { case tok!"identifier": node.label = advance(); mixin(tokenCheck!";"); break; case tok!";": advance(); break; default: error("Identifier or semicolon expected following `break`"); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a BaseClass * * $(GRAMMAR $(RULEDEF baseClass): * $(RULE type2) * ;) */ BaseClass parseBaseClass() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!BaseClass; if (!moreTokens) return null; if (current.type.isProtection()) { warn("Use of base class protection is deprecated."); advance(); } if ((node.type2 = parseType2()) is null) { return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a BaseClassList * * $(GRAMMAR $(RULEDEF baseClassList): * $(RULE baseClass) ($(LITERAL ',') $(RULE baseClass))* * ;) */ BaseClassList parseBaseClassList() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseCommaSeparatedRule!(BaseClassList, BaseClass)(); } /** * Parses an BuiltinType * * $(GRAMMAR $(RULEDEF builtinType): * $(LITERAL 'bool') * | $(LITERAL 'byte') * | $(LITERAL 'ubyte') * | $(LITERAL 'short') * | $(LITERAL 'ushort') * | $(LITERAL 'int') * | $(LITERAL 'uint') * | $(LITERAL 'long') * | $(LITERAL 'ulong') * | $(LITERAL 'char') * | $(LITERAL 'wchar') * | $(LITERAL 'dchar') * | $(LITERAL 'float') * | $(LITERAL 'double') * | $(LITERAL 'real') * | $(LITERAL 'ifloat') * | $(LITERAL 'idouble') * | $(LITERAL 'ireal') * | $(LITERAL 'cfloat') * | $(LITERAL 'cdouble') * | $(LITERAL 'creal') * | $(LITERAL 'void') * ;) */ IdType parseBuiltinType() { mixin(traceEnterAndExit!(__FUNCTION__)); return advance().type; } /** * Parses a CaseRangeStatement * * $(GRAMMAR $(RULEDEF caseRangeStatement): * $(LITERAL 'case') $(RULE assignExpression) $(LITERAL ':') $(LITERAL '...') $(LITERAL 'case') $(RULE assignExpression) $(LITERAL ':') $(RULE declarationsAndStatements) * ;) */ CaseRangeStatement parseCaseRangeStatement(ExpressionNode low) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!CaseRangeStatement; assert (low !is null); node.low = low; mixin(tokenCheck!":"); mixin(tokenCheck!".."); expect(tok!"case"); mixin(parseNodeQ!(`node.high`, `AssignExpression`)); const colon = expect(tok!":"); if (colon is null) return null; node.colonLocation = colon.index; mixin(parseNodeQ!(`node.declarationsAndStatements`, `DeclarationsAndStatements`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an CaseStatement * * $(GRAMMAR $(RULEDEF caseStatement): * $(LITERAL 'case') $(RULE _argumentList) $(LITERAL ':') $(RULE declarationsAndStatements) * ;) */ CaseStatement parseCaseStatement(ArgumentList argumentList = null) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!CaseStatement; node.argumentList = argumentList; const colon = expect(tok!":"); if (colon is null) return null; node.colonLocation = colon.index; mixin (nullCheck!`node.declarationsAndStatements = parseDeclarationsAndStatements(false)`); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a CastExpression * * $(GRAMMAR $(RULEDEF castExpression): * $(LITERAL 'cast') $(LITERAL '$(LPAREN)') ($(RULE type) | $(RULE castQualifier))? $(LITERAL '$(RPAREN)') $(RULE unaryExpression) * ;) */ CastExpression parseCastExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!CastExpression; expect(tok!"cast"); mixin(tokenCheck!"("); if (!currentIs(tok!")")) { if (isCastQualifier()) mixin(parseNodeQ!(`node.castQualifier`, `CastQualifier`)); else mixin(parseNodeQ!(`node.type`, `Type`)); } mixin(tokenCheck!")"); mixin(parseNodeQ!(`node.unaryExpression`, `UnaryExpression`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a CastQualifier * * $(GRAMMAR $(RULEDEF castQualifier): * $(LITERAL 'const') * | $(LITERAL 'const') $(LITERAL 'shared') * | $(LITERAL 'immutable') * | $(LITERAL 'inout') * | $(LITERAL 'inout') $(LITERAL 'shared') * | $(LITERAL 'shared') * | $(LITERAL 'shared') $(LITERAL 'const') * | $(LITERAL 'shared') $(LITERAL 'inout') * ;) */ CastQualifier parseCastQualifier() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!CastQualifier; if (!moreTokens) return null; switch (current.type) { case tok!"inout": case tok!"const": node.first = advance(); if (currentIs(tok!"shared")) node.second = advance(); break; case tok!"shared": node.first = advance(); if (currentIsOneOf(tok!"const", tok!"inout")) node.second = advance(); break; case tok!"immutable": node.first = advance(); break; default: error("`const`, `immutable`, `inout`, or `shared` expected"); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Catch * * $(GRAMMAR $(RULEDEF catch): * $(LITERAL 'catch') $(LITERAL '$(LPAREN)') $(RULE type) $(LITERAL Identifier)? $(LITERAL '$(RPAREN)') $(RULE declarationOrStatement) * ;) */ Catch parseCatch() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Catch; expect(tok!"catch"); mixin(tokenCheck!"("); mixin(parseNodeQ!(`node.type`, `Type`)); if (currentIs(tok!"identifier")) node.identifier = advance(); mixin(tokenCheck!")"); mixin(parseNodeQ!(`node.declarationOrStatement`, `DeclarationOrStatement`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Catches * * $(GRAMMAR $(RULEDEF catches): * $(RULE catch)+ * | $(RULE catch)* $(RULE lastCatch) * ;) */ Catches parseCatches() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Catches; StackBuffer catches; while (moreTokens()) { if (!currentIs(tok!"catch")) break; if (peekIs(tok!"(")) { if (!catches.put(parseCatch())) return null; } else { mixin(parseNodeQ!(`node.lastCatch`, `LastCatch`)); break; } } ownArray(node.catches, catches); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a ClassDeclaration * * $(GRAMMAR $(RULEDEF classDeclaration): * $(LITERAL 'class') $(LITERAL Identifier) $(LITERAL ';') * | $(LITERAL 'class') $(LITERAL Identifier) ($(LITERAL ':') $(RULE baseClassList))? $(RULE structBody) * | $(LITERAL 'class') $(LITERAL Identifier) $(RULE templateParameters) $(RULE constraint)? ($(RULE structBody) | $(LITERAL ';')) * | $(LITERAL 'class') $(LITERAL Identifier) $(RULE templateParameters) $(RULE constraint)? ($(LITERAL ':') $(RULE baseClassList))? $(RULE structBody) * | $(LITERAL 'class') $(LITERAL Identifier) $(RULE templateParameters) ($(LITERAL ':') $(RULE baseClassList))? $(RULE constraint)? $(RULE structBody) * ;) */ ClassDeclaration parseClassDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ClassDeclaration; expect(tok!"class"); return parseInterfaceOrClass(node, startIndex); } /** * Parses a CmpExpression * * $(GRAMMAR $(RULEDEF cmpExpression): * $(RULE shiftExpression) * | $(RULE equalExpression) * | $(RULE identityExpression) * | $(RULE relExpression) * | $(RULE inExpression) * ;) */ ExpressionNode parseCmpExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto shift = parseShiftExpression(); if (shift is null) return null; if (!moreTokens()) return shift; switch (current.type) { case tok!"is": auto node = allocator.make!CmpExpression; mixin (nullCheck!`node.identityExpression = parseIdentityExpression(shift)`); node.tokens = tokens[startIndex .. index]; return node; case tok!"in": auto node = allocator.make!CmpExpression; mixin (nullCheck!`node.inExpression = parseInExpression(shift)`); node.tokens = tokens[startIndex .. index]; return node; case tok!"!": auto node = allocator.make!CmpExpression; if (peekIs(tok!"is")) mixin (nullCheck!`node.identityExpression = parseIdentityExpression(shift)`); else if (peekIs(tok!"in")) mixin (nullCheck!`node.inExpression = parseInExpression(shift)`); node.tokens = tokens[startIndex .. index]; return node; case tok!"<": case tok!"<=": case tok!">": case tok!">=": case tok!"!<>=": case tok!"!<>": case tok!"<>": case tok!"<>=": case tok!"!>": case tok!"!>=": case tok!"!<": case tok!"!<=": auto node = allocator.make!CmpExpression; mixin (nullCheck!`node.relExpression = parseRelExpression(shift)`); node.tokens = tokens[startIndex .. index]; return node; case tok!"==": case tok!"!=": auto node = allocator.make!CmpExpression; mixin (nullCheck!`node.equalExpression = parseEqualExpression(shift)`); node.tokens = tokens[startIndex .. index]; return node; default: return shift; } } /** * Parses a CompileCondition * * $(GRAMMAR $(RULEDEF compileCondition): * $(RULE versionCondition) * | $(RULE debugCondition) * | $(RULE staticIfCondition) * ;) */ CompileCondition parseCompileCondition() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!CompileCondition; if (!moreTokens) return null; switch (current.type) { case tok!"version": mixin(parseNodeQ!(`node.versionCondition`, `VersionCondition`)); break; case tok!"debug": mixin(parseNodeQ!(`node.debugCondition`, `DebugCondition`)); break; case tok!"static": mixin(parseNodeQ!(`node.staticIfCondition`, `StaticIfCondition`)); break; default: error("`version`, `debug`, or `static` expected"); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a ConditionalDeclaration * * $(GRAMMAR $(RULEDEF conditionalDeclaration): * $(RULE compileCondition) $(RULE declaration) * | $(RULE compileCondition) $(LITERAL '{') $(RULE declaration)* $(LITERAL '}') * | $(RULE compileCondition) $(LITERAL ':') $(RULE declaration)+ * | $(RULE compileCondition) $(RULE declaration) $(LITERAL 'else') $(LITERAL ':') $(RULE declaration)* * | $(RULE compileCondition) $(RULE declaration) $(LITERAL 'else') $(RULE declaration) * | $(RULE compileCondition) $(RULE declaration) $(LITERAL 'else') $(LITERAL '{') $(RULE declaration)* $(LITERAL '}') * | $(RULE compileCondition) $(LITERAL '{') $(RULE declaration)* $(LITERAL '}') $(LITERAL 'else') $(RULE declaration) * | $(RULE compileCondition) $(LITERAL '{') $(RULE declaration)* $(LITERAL '}') $(LITERAL 'else') $(LITERAL '{') $(RULE declaration)* $(LITERAL '}') * | $(RULE compileCondition) $(LITERAL '{') $(RULE declaration)* $(LITERAL '}') $(LITERAL 'else') $(LITERAL ':') $(RULE declaration)* * | $(RULE compileCondition) $(LITERAL ':') $(RULE declaration)+ $(LITERAL 'else') $(RULE declaration) * | $(RULE compileCondition) $(LITERAL ':') $(RULE declaration)+ $(LITERAL 'else') $(LITERAL '{') $(RULE declaration)* $(LITERAL '}') * | $(RULE compileCondition) $(LITERAL ':') $(RULE declaration)+ $(LITERAL 'else') $(LITERAL ':') $(RULE declaration)* * ;) */ ConditionalDeclaration parseConditionalDeclaration(bool strict, bool inTemplateDeclaration = false) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ConditionalDeclaration; mixin(parseNodeQ!(`node.compileCondition`, `CompileCondition`)); StackBuffer trueDeclarations; if (currentIs(tok!":") || currentIs(tok!"{")) { immutable bool brace = currentIs(tok!"{"); node.trueStyle = brace ? DeclarationListStyle.block : DeclarationListStyle.colon; advance(); while (moreTokens() && !currentIs(tok!"}") && !currentIs(tok!"else")) { immutable c = allocator.setCheckpoint(); if (!trueDeclarations.put(parseDeclaration(strict, true, inTemplateDeclaration))) { allocator.rollback(c); return null; } } if (brace) mixin(tokenCheck!"}"); } else { if (!trueDeclarations.put(parseDeclaration(strict, true, inTemplateDeclaration))) return null; node.trueStyle = DeclarationListStyle.single; } ownArray(node.trueDeclarations, trueDeclarations); if (currentIs(tok!"else")) { node.hasElse = true; advance(); } else { node.tokens = tokens[startIndex .. index]; return node; } StackBuffer falseDeclarations; if (currentIs(tok!":") || currentIs(tok!"{")) { immutable bool brace = currentIs(tok!"{"); node.falseStyle = brace ? DeclarationListStyle.block : DeclarationListStyle.colon; advance(); while (moreTokens() && !currentIs(tok!"}")) if (!falseDeclarations.put(parseDeclaration(strict, true, inTemplateDeclaration))) return null; if (brace) mixin(tokenCheck!"}"); } else { if (!falseDeclarations.put(parseDeclaration(strict, true, inTemplateDeclaration))) return null; node.falseStyle = DeclarationListStyle.single; } ownArray(node.falseDeclarations, falseDeclarations); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a ConditionalStatement * * $(GRAMMAR $(RULEDEF conditionalStatement): * $(RULE compileCondition) $(RULE declarationOrStatement) ($(LITERAL 'else') $(RULE declarationOrStatement))? * ;) */ ConditionalStatement parseConditionalStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ConditionalStatement; mixin(parseNodeQ!(`node.compileCondition`, `CompileCondition`)); mixin(parseNodeQ!(`node.trueStatement`, `DeclarationOrStatement`)); if (currentIs(tok!"else")) { advance(); mixin(parseNodeQ!(`node.falseStatement`, `DeclarationOrStatement`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Constraint * * $(GRAMMAR $(RULEDEF constraint): * $(LITERAL 'if') $(LITERAL '$(LPAREN)') $(RULE expression) $(LITERAL '$(RPAREN)') * ;) */ Constraint parseConstraint() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Constraint; auto ifToken = expect(tok!"if"); mixin (nullCheck!`ifToken`); node.location = ifToken.index; mixin(tokenCheck!"("); mixin(parseNodeQ!(`node.expression`, `Expression`)); mixin(tokenCheck!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Constructor * * $(GRAMMAR $(RULEDEF constructor): * $(LITERAL 'this') $(RULE templateParameters)? $(RULE parameters) $(RULE memberFunctionAttribute)* $(RULE constraint)? ($(RULE functionBody) | $(LITERAL ';')) * ;) */ Constructor parseConstructor() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; Constructor node = allocator.make!Constructor; node.comment = comment; comment = null; const t = expect(tok!"this"); mixin (nullCheck!`t`); node.location = t.index; node.line = t.line; node.column = t.column; const p = peekPastParens(); bool isTemplate = false; if (p !is null && p.type == tok!"(") { isTemplate = true; mixin(parseNodeQ!(`node.templateParameters`, `TemplateParameters`)); } mixin(parseNodeQ!(`node.parameters`, `Parameters`)); StackBuffer memberFunctionAttributes; while (moreTokens() && currentIsMemberFunctionAttribute()) if (!memberFunctionAttributes.put(parseMemberFunctionAttribute())) return null; ownArray(node.memberFunctionAttributes, memberFunctionAttributes); if (isTemplate && currentIs(tok!"if")) mixin(parseNodeQ!(`node.constraint`, `Constraint`)); if (currentIs(tok!";")) advance(); else mixin(parseNodeQ!(`node.functionBody`, `FunctionBody`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an ContinueStatement * * $(GRAMMAR $(RULEDEF continueStatement): * $(LITERAL 'continue') $(LITERAL Identifier)? $(LITERAL ';') * ;) */ ContinueStatement parseContinueStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; mixin(tokenCheck!"continue"); if (!moreTokens) return null; auto node = allocator.make!ContinueStatement; switch (current.type) { case tok!"identifier": node.label = advance(); mixin(tokenCheck!";"); break; case tok!";": advance(); break; default: error("Identifier or semicolon expected following `continue`"); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a DebugCondition * * $(GRAMMAR $(RULEDEF debugCondition): * $(LITERAL 'debug') ($(LITERAL '$(LPAREN)') ($(LITERAL IntegerLiteral) | $(LITERAL Identifier)) $(LITERAL '$(RPAREN)'))? * ;) */ DebugCondition parseDebugCondition() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!DebugCondition; const d = expect(tok!"debug"); mixin (nullCheck!`d`); node.debugIndex = d.index; if (currentIs(tok!"(")) { advance(); if (currentIsOneOf(tok!"intLiteral", tok!"identifier")) node.identifierOrInteger = advance(); else { error(`Integer literal or identifier expected`); return null; } mixin(tokenCheck!")"); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a DebugSpecification * * $(GRAMMAR $(RULEDEF debugSpecification): * $(LITERAL 'debug') $(LITERAL '=') ($(LITERAL Identifier) | $(LITERAL IntegerLiteral)) $(LITERAL ';') * ;) */ DebugSpecification parseDebugSpecification() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!DebugSpecification; mixin(tokenCheck!"debug"); mixin(tokenCheck!"="); if (currentIsOneOf(tok!"identifier", tok!"intLiteral")) node.identifierOrInteger = advance(); else { error("Integer literal or identifier expected"); return null; } mixin(tokenCheck!";"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Declaration * * Params: * strict = if true, do not return partial AST nodes on errors. * mustBeDeclaration = do not parse as a declaration if it could be parsed as a function call * inTemplateDeclaration = if this function is called from a templated context * * $(GRAMMAR $(RULEDEF declaration): * $(RULE attribute)* $(RULE declaration2) * | $(RULE attribute)+ $(LITERAL '{') $(RULE declaration)* $(LITERAL '}') * ; * $(RULEDEF declaration2): * $(RULE aliasDeclaration) * | $(RULR aliasAssign) * | $(RULE aliasThisDeclaration) * | $(RULE anonymousEnumDeclaration) * | $(RULE attributeDeclaration) * | $(RULE classDeclaration) * | $(RULE conditionalDeclaration) * | $(RULE constructor) * | $(RULE debugSpecification) * | $(RULE destructor) * | $(RULE enumDeclaration) * | $(RULE eponymousTemplateDeclaration) * | $(RULE functionDeclaration) * | $(RULE importDeclaration) * | $(RULE interfaceDeclaration) * | $(RULE invariant) * | $(RULE mixinDeclaration) * | $(RULE mixinTemplateDeclaration) * | $(RULE pragmaDeclaration) * | $(RULE sharedStaticConstructor) * | $(RULE sharedStaticDestructor) * | $(RULE staticAssertDeclaration) * | $(RULE staticConstructor) * | $(RULE staticDestructor) * | $(RULE structDeclaration) * | $(RULE templateDeclaration) * | $(RULE unionDeclaration) * | $(RULE unittest) * | $(RULE variableDeclaration) * | $(RULE versionSpecification) * ;) */ Declaration parseDeclaration(bool strict = false, bool mustBeDeclaration = false, bool inTemplateDeclaration = false) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Declaration; if (!moreTokens) { error("declaration expected instead of EOF"); return null; } if (current.comment !is null) comment = current.comment; size_t autoStorageClassStart = size_t.max; DecType isAuto; StackBuffer attributes; do { isAuto = isAutoDeclaration(autoStorageClassStart); if (isAuto != DecType.other && index == autoStorageClassStart) break; if (!isAttribute()) break; immutable c = allocator.setCheckpoint(); auto attr = parseAttribute(); if (attr is null) { allocator.rollback(c); break; } if (currentIs(tok!":")) { node.attributeDeclaration = parseAttributeDeclaration(attr); mixin(nullCheck!`node.attributeDeclaration`); ownArray(node.attributes, attributes); node.tokens = tokens[startIndex .. index]; return node; } else attributes.put(attr); } while (moreTokens()); ownArray(node.attributes, attributes); if (!moreTokens) { error("declaration expected instead of EOF"); return null; } if (!currentIs(tok!"enum")) // #165: handle enums separatly b/c of EponymousTemplateDeclaration { if (isAuto == DecType.autoVar) { mixin(nullCheck!`node.variableDeclaration = parseVariableDeclaration(null, true)`); node.tokens = tokens[startIndex .. index]; return node; } else if (isAuto == DecType.autoFun) { mixin(nullCheck!`node.functionDeclaration = parseFunctionDeclaration(null, true)`); node.tokens = tokens[startIndex .. index]; return node; } } switch (current.type) { case tok!"asm": case tok!"break": case tok!"case": case tok!"continue": case tok!"default": case tok!"do": case tok!"for": case tok!"foreach": case tok!"foreach_reverse": case tok!"goto": case tok!"if": case tok!"return": case tok!"switch": case tok!"throw": case tok!"try": case tok!"while": case tok!"assert": goto default; case tok!";": // http://d.puremagic.com/issues/show_bug.cgi?id=4559 warn("Empty declaration"); advance(); break; case tok!"{": if (node.attributes.empty) { error("declaration expected instead of `{`"); return null; } advance(); StackBuffer declarations; while (moreTokens() && !currentIs(tok!"}")) { auto c = allocator.setCheckpoint(); if (!declarations.put(parseDeclaration(strict, false, inTemplateDeclaration))) { allocator.rollback(c); return null; } } ownArray(node.declarations, declarations); mixin(tokenCheck!"}"); break; case tok!"alias": if (startsWith(tok!"alias", tok!"identifier", tok!"this")) mixin(parseNodeQ!(`node.aliasThisDeclaration`, `AliasThisDeclaration`)); else mixin(parseNodeQ!(`node.aliasDeclaration`, `AliasDeclaration`)); break; case tok!"class": mixin(parseNodeQ!(`node.classDeclaration`, `ClassDeclaration`)); break; case tok!"this": if (!mustBeDeclaration && peekIs(tok!"(")) { // Do not parse as a declaration if we could parse as a function call. ++index; const past = peekPastParens(); --index; if (past !is null && past.type == tok!";") return null; } if (startsWith(tok!"this", tok!"(", tok!"this", tok!")")) mixin(parseNodeQ!(`node.postblit`, `Postblit`)); else mixin(parseNodeQ!(`node.constructor`, `Constructor`)); break; case tok!"~": mixin(parseNodeQ!(`node.destructor`, `Destructor`)); break; case tok!"enum": immutable b = setBookmark(); advance(); // enum if (currentIsOneOf(tok!":", tok!"{")) { goToBookmark(b); mixin(parseNodeQ!(`node.anonymousEnumDeclaration`, `AnonymousEnumDeclaration`)); } else if (currentIs(tok!"identifier")) { advance(); if (currentIs(tok!"(")) { skipParens(); // () if (currentIs(tok!"(")) skipParens(); if (!currentIs(tok!"=")) { goToBookmark(b); node.functionDeclaration = parseFunctionDeclaration(null, true, node.attributes); mixin (nullCheck!`node.functionDeclaration`); } else { goToBookmark(b); mixin(parseNodeQ!(`node.eponymousTemplateDeclaration`, `EponymousTemplateDeclaration`)); } } else if (currentIsOneOf(tok!":", tok!"{", tok!";")) { goToBookmark(b); mixin(parseNodeQ!(`node.enumDeclaration`, `EnumDeclaration`)); } else { immutable bool eq = currentIs(tok!"="); goToBookmark(b); mixin (nullCheck!`node.variableDeclaration = parseVariableDeclaration(null, eq)`); } } else { immutable bool s = isStorageClass(); goToBookmark(b); mixin (nullCheck!`node.variableDeclaration = parseVariableDeclaration(null, s)`); } break; case tok!"import": mixin(parseNodeQ!(`node.importDeclaration`, `ImportDeclaration`)); break; case tok!"interface": mixin(parseNodeQ!(`node.interfaceDeclaration`, `InterfaceDeclaration`)); break; case tok!"mixin": if (peekIs(tok!"template")) mixin(parseNodeQ!(`node.mixinTemplateDeclaration`, `MixinTemplateDeclaration`)); else { immutable b = setBookmark(); advance(); if (currentIs(tok!"(")) { const t = peekPastParens(); if (t !is null && t.type == tok!";") { goToBookmark(b); mixin(parseNodeQ!(`node.mixinDeclaration`, `MixinDeclaration`)); } else { goToBookmark(b); error("Declaration expected"); return null; } } else { goToBookmark(b); mixin(parseNodeQ!(`node.mixinDeclaration`, `MixinDeclaration`)); } } break; case tok!"pragma": mixin(parseNodeQ!(`node.pragmaDeclaration`, `PragmaDeclaration`)); break; case tok!"shared": if (startsWith(tok!"shared", tok!"static", tok!"this")) mixin(parseNodeQ!(`node.sharedStaticConstructor`, `SharedStaticConstructor`)); else if (startsWith(tok!"shared", tok!"static", tok!"~")) mixin(parseNodeQ!(`node.sharedStaticDestructor`, `SharedStaticDestructor`)); else goto type; break; case tok!"static": if (peekIs(tok!"this")) mixin(parseNodeQ!(`node.staticConstructor`, `StaticConstructor`)); else if (peekIs(tok!"~")) mixin(parseNodeQ!(`node.staticDestructor`, `StaticDestructor`)); else if (peekIs(tok!"if")) mixin (nullCheck!`node.conditionalDeclaration = parseConditionalDeclaration(strict, inTemplateDeclaration)`); else if (peekIs(tok!"assert")) mixin(parseNodeQ!(`node.staticAssertDeclaration`, `StaticAssertDeclaration`)); else if (peekIs(tok!"foreach") || peekIs(tok!"foreach_reverse")) mixin(nullCheck!(`node.staticForeachDeclaration = parseStaticForeachDeclaration(inTemplateDeclaration)`)); else goto type; break; case tok!"struct": mixin(parseNodeQ!(`node.structDeclaration`, `StructDeclaration`)); break; case tok!"template": mixin(parseNodeQ!(`node.templateDeclaration`, `TemplateDeclaration`)); break; case tok!"union": mixin(parseNodeQ!(`node.unionDeclaration`, `UnionDeclaration`)); break; case tok!"invariant": mixin(parseNodeQ!(`node.invariant_`, `Invariant`)); break; case tok!"unittest": mixin(parseNodeQ!(`node.unittest_`, `Unittest`)); break; case tok!"identifier": if (inTemplateDeclaration && peekIs(tok!"=")) { mixin(parseNodeQ!(`node.aliasAssign`, `AliasAssign`)); break; } else goto type; case tok!".": case tok!"const": case tok!"immutable": case tok!"inout": case tok!"scope": case tok!"typeof": case tok!"__vector": case tok!"__traits": foreach (B; BasicTypes) { case B: } type: Type t = parseType(); if (t is null || !currentIs(tok!"identifier")) { if (t) error("no identifier for declarator"); return null; } const b2 = setBookmark(); auto savedComment = comment; node.variableDeclaration = parseVariableDeclaration(t, false); if (node.variableDeclaration is null) { goToBookmark(b2); if (savedComment && comment is null) comment = savedComment; node.functionDeclaration = parseFunctionDeclaration(t, false); } else abandonBookmark(b2); if (!node.variableDeclaration && !node.functionDeclaration) { error("invalid variable declaration or function declaration", false); return null; } break; case tok!"version": if (peekIs(tok!"(")) mixin (nullCheck!`node.conditionalDeclaration = parseConditionalDeclaration(strict, inTemplateDeclaration)`); else if (peekIs(tok!"=")) mixin(parseNodeQ!(`node.versionSpecification`, `VersionSpecification`)); else { error("`=` or `(` expected following `version`"); return null; } break; case tok!"debug": if (peekIs(tok!"=")) mixin(parseNodeQ!(`node.debugSpecification`, `DebugSpecification`)); else mixin (nullCheck!`node.conditionalDeclaration = parseConditionalDeclaration(strict, inTemplateDeclaration)`); break; default: error("Declaration expected"); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses DeclarationsAndStatements * * $(GRAMMAR $(RULEDEF declarationsAndStatements): * $(RULE declarationOrStatement)+ * ;) */ DeclarationsAndStatements parseDeclarationsAndStatements(bool includeCases = true) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!DeclarationsAndStatements; StackBuffer declarationsAndStatements; while (!currentIsOneOf(tok!"}", tok!"else") && moreTokens() && suppressedErrorCount <= MAX_ERRORS) { if (currentIs(tok!"case") && !includeCases) break; if (currentIs(tok!"while")) { immutable b = setBookmark(); scope (exit) goToBookmark(b); advance(); if (currentIs(tok!"(")) { const p = peekPastParens(); if (p !is null && *p == tok!";") break; } } immutable c = allocator.setCheckpoint(); if (!declarationsAndStatements.put(parseDeclarationOrStatement())) { allocator.rollback(c); // detect the pattern ".}" for DCD. This is what happens when // located at the end of a well delimited body/scope and requesting // completion. This is also a case where it's sure sure that // there's no ambiguity, even if it happens during a lookup: // it's not a decl, it's not a statement, it's an error. if (currentIs(tok!"}") && index > 0 && previous == tok!".") break; if (!suppressMessages.empty) return null; // better for DCD, if the end of the block is reached then // go back, allowing the following declarations to be in // the right scope, instead of the block we were in. if (index > 0 && previous == tok!"}") { index -= 1; break; } } } ownArray(node.declarationsAndStatements, declarationsAndStatements); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a DeclarationOrStatement * * $(GRAMMAR $(RULEDEF declarationOrStatement): * $(RULE declaration) * | $(RULE statement) * ;) */ DeclarationOrStatement parseDeclarationOrStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!DeclarationOrStatement; if (moreTokens) node.startLocation = current.index; // "Any ambiguities in the grammar between Statements and // Declarations are resolved by the declarations taking precedence." immutable b = setBookmark(); immutable c = allocator.setCheckpoint(); auto d = parseDeclaration(true, false); if (d is null) { allocator.rollback(c); goToBookmark(b); mixin(parseNodeQ!(`node.statement`, `Statement`)); } else { // TODO: Make this more efficient. Right now we parse the declaration // twice, once with errors and warnings ignored, and once with them // printed. Maybe store messages to then be abandoned or written later? allocator.rollback(c); goToBookmark(b); node.declaration = parseDeclaration(true, true); } if (moreTokens) node.endLocation = current.index; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Declarator * * $(GRAMMAR $(RULEDEF declarator): * $(LITERAL Identifier) * | $(LITERAL Identifier) $(LITERAL '=') $(RULE initializer) * | $(LITERAL Identifier) $(RULE templateParameters) $(LITERAL '=') $(RULE initializer) * ;) */ Declarator parseDeclarator() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; Declarator node = allocator.make!Declarator; const id = expect(tok!"identifier"); mixin (nullCheck!`id`); node.name = *id; if (currentIs(tok!"[")) // dmd doesn't accept pointer after identifier { warn("C-style array declaration."); StackBuffer typeSuffixes; while (moreTokens() && currentIs(tok!"[")) if (!typeSuffixes.put(parseTypeSuffix())) return null; ownArray(node.cstyle, typeSuffixes); } if (currentIs(tok!"(")) { mixin (nullCheck!`(node.templateParameters = parseTemplateParameters())`); mixin(tokenCheck!"="); mixin (nullCheck!`(node.initializer = parseInitializer())`); } else if (currentIs(tok!"=")) { advance(); mixin(parseNodeQ!(`node.initializer`, `Initializer`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a DeclaratorIdentifierList * * $(GRAMMAR $(RULEDEF declaratorIdentifierList): * $(LITERAL Identifier) ($(LITERAL ',') $(LITERAL Identifier))* * ;) */ DeclaratorIdentifierList parseDeclaratorIdentifierList() { auto node = allocator.make!DeclaratorIdentifierList; auto startIndex = index; StackBuffer identifiers; while (moreTokens()) { const ident = expect(tok!"identifier"); mixin(nullCheck!`ident`); identifiers.put(*ident); if (currentIs(tok!",")) { advance(); continue; } else break; } ownArray(node.identifiers, identifiers); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a DefaultStatement * * $(GRAMMAR $(RULEDEF defaultStatement): * $(LITERAL 'default') $(LITERAL ':') $(RULE declarationsAndStatements) * ;) */ DefaultStatement parseDefaultStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!DefaultStatement; mixin(tokenCheck!"default"); const colon = expect(tok!":"); if (colon is null) return null; node.colonLocation = colon.index; mixin(parseNodeQ!(`node.declarationsAndStatements`, `DeclarationsAndStatements`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a DeleteExpression * * $(GRAMMAR $(RULEDEF deleteExpression): * $(LITERAL 'delete') $(RULE unaryExpression) * ;) */ DeleteExpression parseDeleteExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!DeleteExpression; node.line = current.line; node.column = current.column; mixin(tokenCheck!"delete"); mixin(parseNodeQ!(`node.unaryExpression`, `UnaryExpression`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Deprecated attribute * * $(GRAMMAR $(RULEDEF deprecated): * $(LITERAL 'deprecated') ($(LITERAL '$(LPAREN)') $(LITERAL StringLiteral)+ $(LITERAL '$(RPAREN)'))? * ;) */ Deprecated parseDeprecated() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Deprecated; mixin(tokenCheck!"deprecated"); if (currentIs(tok!"(")) { advance(); mixin (parseNodeQ!(`node.assignExpression`, `AssignExpression`)); mixin (tokenCheck!")"); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Destructor * * $(GRAMMAR $(RULEDEF destructor): * $(LITERAL '~') $(LITERAL 'this') $(LITERAL '$(LPAREN)') $(LITERAL '$(RPAREN)') $(RULE memberFunctionAttribute)* ($(RULE functionBody) | $(LITERAL ';')) * ;) */ Destructor parseDestructor() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Destructor; node.comment = comment; comment = null; mixin(tokenCheck!"~"); if (!moreTokens) { error("`this` expected"); return null; } node.index = current.index; node.line = current.line; node.column = current.column; mixin(tokenCheck!"this"); mixin(tokenCheck!"("); mixin(tokenCheck!")"); if (currentIs(tok!";")) advance(); else { StackBuffer memberFunctionAttributes; while (moreTokens() && currentIsMemberFunctionAttribute()) if (!memberFunctionAttributes.put(parseMemberFunctionAttribute())) return null; ownArray(node.memberFunctionAttributes, memberFunctionAttributes); mixin(parseNodeQ!(`node.functionBody`, `FunctionBody`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a DoStatement * * $(GRAMMAR $(RULEDEF doStatement): * $(LITERAL 'do') $(RULE statementNoCaseNoDefault) $(LITERAL 'while') $(LITERAL '$(LPAREN)') $(RULE expression) $(LITERAL '$(RPAREN)') $(LITERAL ';') * ;) */ DoStatement parseDoStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; mixin(tokenCheck!"do"); if (!moreTokens) return null; auto node = allocator.make!DoStatement; mixin(parseNodeQ!(`node.statementNoCaseNoDefault`, `StatementNoCaseNoDefault`)); mixin(tokenCheck!"while"); mixin(tokenCheck!"("); mixin(parseNodeQ!(`node.expression`, `Expression`)); mixin(tokenCheck!")"); mixin(tokenCheck!";"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an EnumBody * * $(GRAMMAR $(RULEDEF enumBody): * $(LITERAL '{') $(RULE enumMember) ($(LITERAL ',') $(RULE enumMember)?)* $(LITERAL '}') * ;) */ EnumBody parseEnumBody() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; EnumBody node = allocator.make!EnumBody; const open = expect(tok!"{"); mixin (nullCheck!`open`); node.startLocation = open.index; StackBuffer enumMembers; EnumMember last; while (moreTokens()) { if (currentIsOneOf(tok!"identifier", tok!"@", tok!"deprecated")) { auto c = allocator.setCheckpoint(); auto e = parseEnumMember(); if (!enumMembers.put(e)) allocator.rollback(c); else last = e; if (currentIs(tok!",")) { if (last !is null && last.comment is null) last.comment = current.trailingComment; advance(); if (!currentIs(tok!"}")) continue; } if (currentIs(tok!"}")) { if (last !is null && last.comment is null) last.comment = tokens[index - 1].trailingComment; break; } else { error("`,` or `}` expected"); if (currentIs(tok!"}")) break; } } else error("Enum member expected"); } ownArray(node.enumMembers, enumMembers); const close = expect (tok!"}"); if (close !is null) node.endLocation = close.index; node.tokens = tokens[startIndex .. index]; return node; } /** * $(GRAMMAR $(RULEDEF anonymousEnumMember): * $(RULE type) $(LITERAL identifier) $(LITERAL '=') $(RULE assignExpression) * | $(LITERAL identifier) $(LITERAL '=') $(RULE assignExpression) * | $(LITERAL identifier) * ;) */ AnonymousEnumMember parseAnonymousEnumMember(bool typeAllowed) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AnonymousEnumMember; if (currentIs(tok!"identifier") && peekIsOneOf(tok!",", tok!"=", tok!"}")) { node.comment = current.comment; mixin(tokenCheck!(`node.name`, `identifier`)); if (currentIs(tok!"=")) { advance(); // = goto assign; } } else if (typeAllowed) { node.comment = current.comment; mixin(parseNodeQ!(`node.type`, `Type`)); mixin(tokenCheck!(`node.name`, `identifier`)); mixin(tokenCheck!"="); assign: mixin(parseNodeQ!(`node.assignExpression`, `AssignExpression`)); } else { error("Cannot specify anonymous enum member type if anonymous enum has a base type."); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * $(GRAMMAR $(RULEDEF anonymousEnumDeclaration): * $(LITERAL 'enum') ($(LITERAL ':') $(RULE type))? $(LITERAL '{') $(RULE anonymousEnumMember)+ $(LITERAL '}') * ;) */ AnonymousEnumDeclaration parseAnonymousEnumDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AnonymousEnumDeclaration; mixin(tokenCheck!"enum"); immutable bool hasBaseType = currentIs(tok!":"); if (hasBaseType) { advance(); mixin(parseNodeQ!(`node.baseType`, `Type`)); } mixin(tokenCheck!"{"); StackBuffer members; AnonymousEnumMember last; while (moreTokens()) { if (currentIs(tok!",")) { if (last !is null && last.comment is null) last.comment = current.trailingComment; advance(); continue; } else if (currentIs(tok!"}")) { if (last !is null && last.comment is null) last.comment = tokens[index - 1].trailingComment; break; } else { immutable c = allocator.setCheckpoint(); auto e = parseAnonymousEnumMember(!hasBaseType); if (!members.put(e)) allocator.rollback(c); else last = e; } } ownArray(node.members, members); mixin(tokenCheck!"}"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an EnumDeclaration * * $(GRAMMAR $(RULEDEF enumDeclaration): * $(LITERAL 'enum') $(LITERAL Identifier) ($(LITERAL ':') $(RULE type))? $(LITERAL ';') * | $(LITERAL 'enum') $(LITERAL Identifier) ($(LITERAL ':') $(RULE type))? $(RULE enumBody) * ;) */ EnumDeclaration parseEnumDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!EnumDeclaration; mixin(tokenCheck!"enum"); mixin (tokenCheck!(`node.name`, `identifier`)); node.comment = comment; comment = null; if (currentIs(tok!":")) { advance(); // skip ':' mixin(parseNodeQ!(`node.type`, `Type`)); } if (currentIs(tok!";")) { advance(); node.tokens = tokens[startIndex .. index]; return node; } mixin(parseNodeQ!(`node.enumBody`, `EnumBody`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an EnumMemberAttribute * * $(GRAMMAR $(RULEDEF enumMemberAttribute): * $(RULE atAttribute) * | $(RULE deprecated) * ;) */ EnumMemberAttribute parseEnumMemberAttribute() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; EnumMemberAttribute node; if (currentIs(tok!"@")) { node = allocator.make!EnumMemberAttribute; mixin(parseNodeQ!(`node.atAttribute`, `AtAttribute`)); } else if (currentIs(tok!"deprecated")) { node = allocator.make!EnumMemberAttribute; mixin(parseNodeQ!(`node.deprecated_`, `Deprecated`)); } if (node) node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an EnumMember * * $(GRAMMAR $(RULEDEF enumMember): * ($(RULE enumMemberAttribute))* $(LITERAL Identifier) ($(LITERAL '=') $(RULE assignExpression))? * ;) */ EnumMember parseEnumMember() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; EnumMember node = allocator.make!EnumMember; node.comment = current.comment; StackBuffer emas; while (moreTokens()) { if (!emas.put(parseEnumMemberAttribute())) break; } ownArray(node.enumMemberAttributes, emas); mixin (tokenCheck!(`node.name`, `identifier`)); if (currentIs(tok!"=")) { advance(); mixin(parseNodeQ!(`node.assignExpression`, `AssignExpression`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an EponymousTemplateDeclaration * * $(GRAMMAR $(RULEDEF eponymousTemplateDeclaration): * $(LITERAL 'enum') $(LITERAL Identifier) $(RULE templateParameters) $(LITERAL '=') $(RULE assignExpression) $(LITERAL ';') * ;) */ EponymousTemplateDeclaration parseEponymousTemplateDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!EponymousTemplateDeclaration; node.comment = current.comment; advance(); // enum const ident = expect(tok!"identifier"); mixin (nullCheck!`ident`); node.name = *ident; mixin(parseNodeQ!(`node.templateParameters`, `TemplateParameters`)); expect(tok!"="); node.assignExpression = parseAssignExpression(); if (node.assignExpression is null) mixin(parseNodeQ!(`node.type`, `Type`)); expect(tok!";"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an EqualExpression * * $(GRAMMAR $(RULEDEF equalExpression): * $(RULE shiftExpression) ($(LITERAL '==') | $(LITERAL '!=')) $(RULE shiftExpression) * ;) */ EqualExpression parseEqualExpression(ExpressionNode shift = null) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!EqualExpression; node.left = shift is null ? parseShiftExpression() : shift; mixin (nullCheck!`node.left`); if (currentIsOneOf(tok!"==", tok!"!=")) node.operator = advance().type; mixin(parseNodeQ!(`node.right`, `ShiftExpression`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an Expression * * $(GRAMMAR $(RULEDEF expression): * $(RULE assignExpression) ($(LITERAL ',') $(RULE assignExpression))* * ;) */ Expression parseExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); if (suppressedErrorCount > MAX_ERRORS) return null; if (!moreTokens()) { error("Expected expression instead of EOF"); return null; } return parseCommaSeparatedRule!(Expression, AssignExpression, true)(); } /** * Parses an ExpressionStatement * * $(GRAMMAR $(RULEDEF expressionStatement): * $(RULE _expression) $(LITERAL ';') * ;) */ ExpressionStatement parseExpressionStatement(Expression expression = null) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ExpressionStatement; node.expression = expression is null ? parseExpression() : expression; if (node.expression is null || expect(tok!";") is null) return null; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a FinalSwitchStatement * * $(GRAMMAR $(RULEDEF finalSwitchStatement): * $(LITERAL 'final') $(RULE switchStatement) * ;) */ FinalSwitchStatement parseFinalSwitchStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); mixin (simpleParse!(FinalSwitchStatement, tok!"final", "switchStatement|parseSwitchStatement")); } /** * Parses a Finally * * $(GRAMMAR $(RULEDEF finally): * $(LITERAL 'finally') $(RULE declarationOrStatement) * ;) */ Finally parseFinally() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Finally; mixin(tokenCheck!"finally"); mixin(parseNodeQ!(`node.declarationOrStatement`, `DeclarationOrStatement`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a ForStatement * * $(GRAMMAR $(RULEDEF forStatement): * $(LITERAL 'for') $(LITERAL '$(LPAREN)') ($(RULE declaration) | $(RULE statementNoCaseNoDefault)) $(RULE expression)? $(LITERAL ';') $(RULE expression)? $(LITERAL '$(RPAREN)') $(RULE declarationOrStatement) * ;) */ ForStatement parseForStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ForStatement; mixin(tokenCheck!"for"); if (moreTokens) node.startIndex = current().index; mixin(tokenCheck!"("); if (currentIs(tok!";")) advance(); else mixin(parseNodeQ!(`node.initialization`, `DeclarationOrStatement`)); if (currentIs(tok!";")) advance(); else { mixin(parseNodeQ!(`node.test`, `Expression`)); expect(tok!";"); } if (!currentIs(tok!")")) mixin(parseNodeQ!(`node.increment`, `Expression`)); mixin(tokenCheck!")"); // Intentionally return an incomplete parse tree so that DCD will work // more correctly. if (currentIs(tok!"}")) { error("Statement expected", false); node.tokens = tokens[startIndex .. index]; return node; } mixin(parseNodeQ!(`node.declarationOrStatement`, `DeclarationOrStatement`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a StaticForeachDeclaration * * $(GRAMMAR $(RULEDEF staticForeachDeclaration): * $(LITERAL 'static') ($(LITERAL 'foreach') | $(LITERAL 'foreach_reverse')) $(LITERAL '$(LPAREN)') $(RULE foreachTypeList) $(LITERAL ';') $(RULE expression) $(LITERAL '$(RPAREN)') ($(RULE declaration) | $(LITERAL '{') $(RULE declaration)* $(LITERAL '}')) * | $(LITERAL 'static') ($(LITERAL 'foreach') | $(LITERAL 'foreach_reverse')) $(LITERAL '$(LPAREN)') $(RULE foreachType) $(LITERAL ';') $(RULE expression) $(LITERAL '..') $(RULE expression) $(LITERAL '$(RPAREN)') ($(RULE declaration) | $(LITERAL '{') $(RULE declaration)* $(LITERAL '}')) * ;) */ StaticForeachDeclaration parseStaticForeachDeclaration(bool inTemplateDeclaration = false) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; mixin(tokenCheck!"static"); auto decl = parseForeach!true(inTemplateDeclaration); if (decl) decl.tokens = tokens[startIndex .. index]; return decl; } /** * Parses a StaticForeachStatement * * $(GRAMMAR $(RULEDEF staticForeachStatement): * $(LITERAL 'static') $(RULE foreachStatement) * ;) */ StaticForeachStatement parseStaticForeachStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); mixin(simpleParse!(StaticForeachStatement, tok!"static", "foreachStatement|parseForeachStatement")); } /** * Parses a ForeachStatement * * $(GRAMMAR $(RULEDEF foreachStatement): * ($(LITERAL 'foreach') | $(LITERAL 'foreach_reverse')) $(LITERAL '$(LPAREN)') $(RULE foreachTypeList) $(LITERAL ';') $(RULE expression) $(LITERAL '$(RPAREN)') $(RULE declarationOrStatement) * | ($(LITERAL 'foreach') | $(LITERAL 'foreach_reverse')) $(LITERAL '$(LPAREN)') $(RULE foreachType) $(LITERAL ';') $(RULE expression) $(LITERAL '..') $(RULE expression) $(LITERAL '$(RPAREN)') $(RULE declarationOrStatement) * ;) */ ForeachStatement parseForeachStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseForeach!false(); } Foreach!declOnly parseForeach(bool declOnly = false)(bool inTemplateDeclaration = false) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; Foreach!declOnly node = allocator.make!(Foreach!declOnly); if (currentIsOneOf(tok!"foreach", tok!"foreach_reverse")) node.type = advance().type; else { error("`foreach` or `foreach_reverse` expected"); return null; } if (moreTokens) node.startIndex = current().index; mixin(tokenCheck!"("); ForeachTypeList feType = parseForeachTypeList(); mixin (nullCheck!`feType`); immutable bool canBeRange = feType.items.length == 1; mixin(tokenCheck!";"); mixin(parseNodeQ!(`node.low`, `Expression`)); mixin (nullCheck!`node.low`); if (currentIs(tok!"..")) { if (!canBeRange) { error(`Cannot have more than one foreach variable for a foreach range statement`); return null; } advance(); mixin(parseNodeQ!(`node.high`, `Expression`)); node.foreachType = feType.items[0]; mixin (nullCheck!`node.high`); } else { node.foreachTypeList = feType; } mixin(tokenCheck!")"); if (currentIs(tok!"}")) { error("Statement expected", false); node.tokens = tokens[startIndex .. index]; return node; // this line makes DCD better } static if (declOnly) { node.style = currentIs(tok!"{") ? DeclarationListStyle.block : DeclarationListStyle.single; StackBuffer declarations; if (currentIs(tok!"{")) { advance(); while (moreTokens() && !currentIs(tok!"}")) { immutable b = setBookmark(); immutable c = allocator.setCheckpoint(); if (declarations.put(parseDeclaration(true, true, inTemplateDeclaration))) abandonBookmark(b); else { goToBookmark(b); allocator.rollback(c); return null; } } mixin(tokenCheck!"}"); } else if (!declarations.put(parseDeclaration(true, true, inTemplateDeclaration))) return null; ownArray(node.declarations, declarations); } else mixin(parseNodeQ!(`node.declarationOrStatement`, `DeclarationOrStatement`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a ForeachType * * $(GRAMMAR $(RULEDEF foreachType): * ($(LITERAL 'ref') | $(LITERAL 'alias') | $(LITERAL 'enum') | $(RULE typeConstructor))* $(RULE type)? $(LITERAL Identifier) * ;) */ ForeachType parseForeachType() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ForeachType; while (moreTokens()) { IdType typeConstructor; if (currentIs(tok!"ref")) { node.isRef = true; advance(); } else if (currentIs(tok!"alias")) { node.isAlias = true; advance(); } else if (currentIs(tok!"enum")) { node.isEnum = true; advance(); } else if (tok!"" != (typeConstructor = parseTypeConstructor(false))) { trace("\033[01;36mType constructor"); node.typeConstructors ~= typeConstructor; } else break; } if (currentIs(tok!"identifier") && peekIsOneOf(tok!",", tok!";")) { node.identifier = advance(); node.tokens = tokens[startIndex .. index]; return node; } mixin(parseNodeQ!(`node.type`, `Type`)); const ident = expect(tok!"identifier"); mixin(nullCheck!`ident`); node.identifier = *ident; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a ForeachTypeList * * $(GRAMMAR $(RULEDEF foreachTypeList): * $(RULE foreachType) ($(LITERAL ',') $(RULE foreachType))* * ;) */ ForeachTypeList parseForeachTypeList() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseCommaSeparatedRule!(ForeachTypeList, ForeachType)(); } /** * Parses a FunctionAttribute * * $(GRAMMAR $(RULEDEF functionAttribute): * $(RULE atAttribute) * | $(LITERAL 'pure') * | $(LITERAL 'nothrow') * ;) */ FunctionAttribute parseFunctionAttribute(bool validate = true) { auto startIndex = index; auto node = allocator.make!FunctionAttribute; switch (current.type) { case tok!"@": mixin(parseNodeQ!(`node.atAttribute`, `AtAttribute`)); break; case tok!"pure": case tok!"nothrow": node.token = advance(); break; default: if (validate) error("`@`attribute, `pure`, or `nothrow` expected"); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a FunctionBody. * Note that any change of this function must also be applied in dsymbol SimpleParser, which can be found * $(LINK2 https://github.com/dlang-community/dsymbol/blob/master/src/dsymbol/conversion/package.d, here). * * $(GRAMMAR $(RULEDEF functionBody): * $(RULE specifiedFunctionBody) * | $(RULE missingFunctionBody) * ;) */ FunctionBody parseFunctionBody() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!FunctionBody; immutable b = setBookmark(); immutable c = allocator.setCheckpoint(); auto missingFunctionBody = parseMissingFunctionBody(); if (missingFunctionBody !is null) { abandonBookmark(b); node.missingFunctionBody = missingFunctionBody; } else { allocator.rollback(c); goToBookmark(b, false); auto shortenedFunctionBody = parseShortenedFunctionBody(); if (shortenedFunctionBody !is null) { abandonBookmark(b); node.shortenedFunctionBody = shortenedFunctionBody; } else { allocator.rollback(c); goToBookmark(b); mixin(parseNodeQ!(`node.specifiedFunctionBody`, `SpecifiedFunctionBody`)); } } node.endLocation = previous.index; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a FunctionCallExpression * * $(GRAMMAR $(RULEDEF functionCallExpression): * $(RULE symbol) $(RULE arguments) * | $(RULE unaryExpression) $(RULE arguments) * | $(RULE type) $(RULE arguments) * ;) */ FunctionCallExpression parseFunctionCallExpression(UnaryExpression unary = null) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!FunctionCallExpression; switch (current.type) { case tok!"const": case tok!"immutable": case tok!"inout": case tok!"shared": case tok!"scope": case tok!"pure": case tok!"nothrow": mixin(parseNodeQ!(`node.type`, `Type`)); mixin(parseNodeQ!(`node.arguments`, `Arguments`)); break; default: if (unary !is null) node.unaryExpression = unary; else mixin(parseNodeQ!(`node.unaryExpression`, `UnaryExpression`)); if (currentIs(tok!"!")) mixin(parseNodeQ!(`node.templateArguments`, `TemplateArguments`)); if (unary !is null) mixin(parseNodeQ!(`node.arguments`, `Arguments`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a FunctionContract * * $(GRAMMAR $(RULEDEF functionContract): * $(RULE inOutContractExpression) * | $(RULE inOutStatement) * ;) */ FunctionContract parseFunctionContract(bool allowStatement = true) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!FunctionContract; if (allowStatement && (peekIs(tok!"{") || (currentIs(tok!"out") && peekAre(tok!"(", tok!"identifier", tok!")")))) mixin(parseNodeQ!(`node.inOutStatement`, `InOutStatement`)); else if (peekIs(tok!"(")) mixin(parseNodeQ!(`node.inOutContractExpression`, `InOutContractExpression`)); else { error(allowStatement ? "`{` or `(` expected" : "`(` expected"); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a FunctionDeclaration * * $(GRAMMAR $(RULEDEF functionDeclaration): * ($(RULE storageClass)+ | $(RULE _type)) $(LITERAL Identifier) $(RULE parameters) $(RULE memberFunctionAttribute)* ($(RULE functionBody) | $(LITERAL ';')) * | ($(RULE storageClass)+ | $(RULE _type)) $(LITERAL Identifier) $(RULE templateParameters) $(RULE parameters) $(RULE memberFunctionAttribute)* $(RULE constraint)? ($(RULE functionBody) | $(LITERAL ';')) * ;) */ FunctionDeclaration parseFunctionDeclaration(Type type = null, bool isAuto = false, Attribute[] attributes = null) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!FunctionDeclaration; node.comment = comment; comment = null; StackBuffer memberFunctionAttributes; node.attributes = attributes; if (isAuto) { StackBuffer storageClasses; while (isStorageClass()) if (!storageClasses.put(parseStorageClass())) return null; ownArray(node.storageClasses, storageClasses); foreach (a; node.attributes) { if (a.attribute == tok!"auto") node.hasAuto = true; else if (a.attribute == tok!"ref") node.hasRef = true; else continue; } } else { while (moreTokens() && currentIsMemberFunctionAttribute()) if (!memberFunctionAttributes.put(parseMemberFunctionAttribute())) return null; if (type is null) mixin(parseNodeQ!(`node.returnType`, `Type`)); else node.returnType = type; } mixin(tokenCheck!(`node.name`, "identifier")); if (!currentIs(tok!"(")) { error("`(` expected"); return null; } const p = peekPastParens(); immutable bool isTemplate = p !is null && p.type == tok!"("; if (isTemplate) mixin(parseNodeQ!(`node.templateParameters`, `TemplateParameters`)); mixin(parseNodeQ!(`node.parameters`, `Parameters`)); while (moreTokens() && currentIsMemberFunctionAttribute()) if (!memberFunctionAttributes.put(parseMemberFunctionAttribute())) return null; if (isTemplate && currentIs(tok!"if")) mixin(parseNodeQ!(`node.constraint`, `Constraint`)); mixin(parseNodeQ!(`node.functionBody`, `FunctionBody`)); if (node.functionBody && node.functionBody.specifiedFunctionBody && node.functionBody.specifiedFunctionBody.blockStatement && node.functionBody.specifiedFunctionBody.blockStatement.tokens.length) attachComment(node, node.functionBody.specifiedFunctionBody.blockStatement.tokens[$ - 1].trailingComment); ownArray(node.memberFunctionAttributes, memberFunctionAttributes); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a FunctionLiteralExpression * * $(GRAMMAR $(RULEDEF functionLiteralExpression): * | $(LITERAL 'delegate') $(LITERAL 'ref')? $(RULE type)? ($(RULE parameters) $(RULE functionAttribute)*)? $(RULE specifiedFunctionBody) * | $(LITERAL 'function') $(LITERAL 'ref')? $(RULE type)? ($(RULE parameters) $(RULE functionAttribute)*)? $(RULE specifiedFunctionBody) * | $(LITERAL 'ref')? $(RULE parameters) $(RULE functionAttribute)* $(RULE specifiedFunctionBody) * | $(RULE specifiedFunctionBody) * | $(LITERAL Identifier) $(LITERAL '=>') $(RULE assignExpression) * | $(LITERAL 'function') $(LITERAL 'ref')? $(RULE type)? $(RULE parameters) $(RULE functionAttribute)* $(LITERAL '=>') $(RULE assignExpression) * | $(LITERAL 'delegate') $(LITERAL 'ref')? $(RULE type)? $(RULE parameters) $(RULE functionAttribute)* $(LITERAL '=>') $(RULE assignExpression) * | $(LITERAL 'ref')? $(RULE parameters) $(RULE functionAttribute)* $(LITERAL '=>') $(RULE assignExpression) * ;) */ FunctionLiteralExpression parseFunctionLiteralExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!FunctionLiteralExpression; node.line = current.line; node.column = current.column; if (currentIsOneOf(tok!"function", tok!"delegate")) { node.functionOrDelegate = advance().type; if (currentIs(tok!"ref")) { advance(); node.isReturnRef = true; } if (!currentIsOneOf(tok!"(", tok!"in", tok!"do", tok!"out", tok!"{", tok!"=>") && current.text != "body") mixin(parseNodeQ!(`node.returnType`, `Type`)); } if (startsWith(tok!"identifier", tok!"=>")) { node.identifier = advance(); advance(); // => mixin(parseNodeQ!(`node.assignExpression`, `AssignExpression`)); node.tokens = tokens[startIndex .. index]; return node; } else if (currentIs(tok!"(") || currentIs(tok!"ref") && peekIs(tok!"(")) { if (currentIs(tok!"ref")) { advance(); node.isReturnRef = true; } mixin(parseNodeQ!(`node.parameters`, `Parameters`)); StackBuffer memberFunctionAttributes; while (currentIsMemberFunctionAttribute()) { auto c = allocator.setCheckpoint(); if (!memberFunctionAttributes.put(parseMemberFunctionAttribute())) { allocator.rollback(c); break; } } ownArray(node.memberFunctionAttributes, memberFunctionAttributes); } if (currentIs(tok!"=>")) { advance(); mixin(parseNodeQ!(`node.assignExpression`, `AssignExpression`)); } else mixin(parseNodeQ!(`node.specifiedFunctionBody`, `SpecifiedFunctionBody`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AsmInstruction using GCC Assembler * * $(GRAMMAR $(RULEDEF gccAsmInstruction): * | $(RULE expression) $(LITERAL ':') $(RULE gccAsmOperandList)? ($(LITERAL ':') $(RULE gccAsmOperandList)? ($(LITERAL ':') $(RULE stringLiteralList))? )? $(LITERAL ';') * | $(RULE expression) $(LITERAL ':') $(LITERAL ':') $(RULE gccAsmOperandList)? $(LITERAL ':') $(RULE stringLiteralList) $(LITERAL ';') $(LITERAL ':') $(RULE declaratorIdentifierList) $(LITERAL ';') * ;) */ /* * References: * - [1] https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html * - [2] https://wiki.dlang.org/Using_GDC * - [3] https://github.com/dlang/dmd/blob/master/src/dmd/iasmgcc.d * * Separated into a different method because one cannot interleave DMD & GCC asm * <asm-qualifiers> (volatile, inline, goto) not supperted (yet?) */ GccAsmInstruction parseGccAsmInstruction() { mixin(traceEnterAndExit!(__FUNCTION__)); const startIndex = index; auto node = allocator.make!GccAsmInstruction(); // Allow empty asm instructions if (currentIs(tok!";")) { warn("Empty asm instruction"); node.tokens = tokens[startIndex .. index]; return node; } mixin(parseNodeQ!("node.assemblerTemplate", "Expression")); // GDC allows e.g. asm { mixin(<some asm instruction>); } if (!currentIs(tok!";")) { mixin(tokenCheck!":"); if (!currentIsOneOf(tok!":", tok!";")) mixin(parseNodeQ!(`node.outputOperands`, `GccAsmOperandList`)); if (skip(tok!":")) { if (!currentIsOneOf(tok!":", tok!";")) mixin(parseNodeQ!(`node.inputOperands`, `GccAsmOperandList`)); if (skip(tok!":")) { if (!currentIs(tok!":")) mixin(parseNodeQ!("node.registers", "StringLiteralList")); if (skip(tok!":")) { size_t cp; if (node.outputOperands) { error("goto-labels only allowed without output operands!", false); cp = allocator.setCheckpoint(); } // Parse even with the error above for better error reporting mixin(parseNodeQ!("node.gotos", "DeclaratorIdentifierList")); if (cp) { allocator.rollback(cp); return null; } } } } } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a GccAsmOperandList * * $(GRAMMAR $(RULEDEF gccAsmOperandList): * $(RULE gccAsmOperand) ($(LITERAL ',') $(RULE gccAsmOperand))* * ;) */ GccAsmOperandList parseGccAsmOperandList() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseCommaSeparatedRule!(GccAsmOperandList, GccAsmOperand)(); } /** * Parses a GccAsmOperand * * $(GRAMMAR $(RULEDEF gccAsmOperand): * ($(LITERAL '[') $(RULE identifier) $(LITERAL ']'))? $(RULE stringLiteral) $(LITERAL '$(LPAREN)') $(RULE assignExpression) $(LITERAL '$(RPAREN)') * ;) */ GccAsmOperand parseGccAsmOperand() { mixin(traceEnterAndExit!(__FUNCTION__)); const startIndex = index; auto node = allocator.make!GccAsmOperand(); if (currentIs(tok!"[")) { advance(); if (auto t = expect(tok!"identifier")) node.symbolicName = *t; mixin(tokenCheck!"]"); } mixin(tokenCheck!("node.constraint", "stringLiteral")); // GCC actually requires braces but GDC didn't for quite some time, // see https://github.com/dlang/dmd/pull/10820 const hasParens = skip(tok!"("); if (!hasParens) warn("Omitting parenthesis around operands is deprecated!"); mixin(parseNodeQ!("node.expression", "AssignExpression")); if (hasParens) expect(tok!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a GotoStatement * * $(GRAMMAR $(RULEDEF gotoStatement): * $(LITERAL 'goto') ($(LITERAL Identifier) | $(LITERAL 'default') | $(LITERAL 'case') $(RULE expression)?) $(LITERAL ';') * ;) */ GotoStatement parseGotoStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!GotoStatement; mixin(tokenCheck!"goto"); if (!moreTokens) return null; switch (current.type) { case tok!"identifier": case tok!"default": node.label = advance(); break; case tok!"case": node.label = advance(); if (!currentIs(tok!";")) mixin(parseNodeQ!(`node.expression`, `Expression`)); break; default: error("Identifier, `default`, or `case` expected"); return null; } mixin(tokenCheck!";"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an IdentifierChain * * $(GRAMMAR $(RULEDEF identifierChain): * $(LITERAL Identifier) ($(LITERAL '.') $(LITERAL Identifier))* * ;) */ IdentifierChain parseIdentifierChain() { auto startIndex = index; auto node = allocator.make!IdentifierChain; StackBuffer identifiers; while (moreTokens()) { const ident = expect(tok!"identifier"); mixin(nullCheck!`ident`); identifiers.put(*ident); if (currentIs(tok!".")) { advance(); continue; } else break; } ownArray(node.identifiers, identifiers); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a TypeIdentifierPart. * * $(GRAMMAR $(RULEDEF typeIdentifierPart): * $(RULE identifierOrTemplateInstance) * | $(RULE identifierOrTemplateInstance) $(LITERAL '.') $(RULE typeIdentifierPart) * | $(RULE identifierOrTemplateInstance) $(LITERAL '[') $(RULE assignExpression) $(LITERAL ']') * | $(RULE identifierOrTemplateInstance) $(LITERAL '[') $(RULE assignExpression) $(LITERAL ']') $(LITERAL '.') $(RULE typeIdentifierPart) * ;) */ TypeIdentifierPart parseTypeIdentifierPart() { auto startIndex = index; TypeIdentifierPart node = allocator.make!TypeIdentifierPart; if (currentIs(tok!".")) { node.dot = true; advance(); } mixin(parseNodeQ!(`node.identifierOrTemplateInstance`, `IdentifierOrTemplateInstance`)); if (currentIs(tok!"[")) { // dyn arrays -> type suffixes if (peekIs(tok!"]")) { node.tokens = tokens[startIndex .. index - 1]; return node; } const b = setBookmark(); advance(); const cp = allocator.setCheckpoint; node.indexer = parseAssignExpression(); // here we can have a type (AA key) if (node.indexer is null) { goToBookmark(b); return node; } // indexer followed by ".." -> sliceExp -> type suffix else if (currentIs(tok!"..")) { allocator.rollback(cp); node.indexer = null; goToBookmark(b); return node; } // otherwise either the index of a type list or a dim abandonBookmark(b); expect(tok!"]"); if (!currentIs(tok!".")) { node.tokens = tokens[startIndex .. index]; return node; } } if (currentIs(tok!".")) { advance(); mixin(parseNodeQ!(`node.typeIdentifierPart`, `TypeIdentifierPart`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an IdentifierOrTemplateChain * * $(GRAMMAR $(RULEDEF identifierOrTemplateChain): * $(RULE identifierOrTemplateInstance) ($(LITERAL '.') $(RULE identifierOrTemplateInstance))* * ;) */ IdentifierOrTemplateChain parseIdentifierOrTemplateChain() { auto startIndex = index; auto node = allocator.make!IdentifierOrTemplateChain; StackBuffer identifiersOrTemplateInstances; while (moreTokens()) { auto c = allocator.setCheckpoint(); if (!identifiersOrTemplateInstances.put(parseIdentifierOrTemplateInstance())) { allocator.rollback(c); if (identifiersOrTemplateInstances.length == 0) return null; else break; } if (!currentIs(tok!".")) break; else advance(); } ownArray(node.identifiersOrTemplateInstances, identifiersOrTemplateInstances); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an IdentifierOrTemplateInstance * * $(GRAMMAR $(RULEDEF identifierOrTemplateInstance): * $(LITERAL Identifier) * | $(RULE templateInstance) * ;) */ IdentifierOrTemplateInstance parseIdentifierOrTemplateInstance() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!IdentifierOrTemplateInstance; if (peekIs(tok!"!") && !startsWith(tok!"identifier", tok!"!", tok!"is") && !startsWith(tok!"identifier", tok!"!", tok!"in")) { mixin(parseNodeQ!(`node.templateInstance`, `TemplateInstance`)); } else { const ident = expect(tok!"identifier"); mixin(nullCheck!`ident`); node.identifier = *ident; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an IdentityExpression * * $(GRAMMAR $(RULEDEF identityExpression): * $(RULE shiftExpression) ($(LITERAL 'is') | ($(LITERAL '!') $(LITERAL 'is'))) $(RULE shiftExpression) * ;) */ ExpressionNode parseIdentityExpression(ExpressionNode shift = null) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!IdentityExpression; mixin(nullCheck!`node.left = shift is null ? parseShiftExpression() : shift`); if (currentIs(tok!"!")) { advance(); node.negated = true; } mixin(tokenCheck!"is"); mixin(parseNodeQ!(`node.right`, `ShiftExpression`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an IfStatement * * $(GRAMMAR $(RULEDEF ifStatement): * $(LITERAL 'if') $(LITERAL '$(LPAREN)') $(RULE ifCondition) $(LITERAL '$(RPAREN)') $(RULE declarationOrStatement) ($(LITERAL 'else') $(RULE declarationOrStatement))? *$(RULEDEF ifCondition): * $(LITERAL 'auto') $(LITERAL Identifier) $(LITERAL '=') $(RULE expression) * | $(RULE typeConstructors) $(LITERAL Identifier) $(LITERAL '=') $(RULE expression) * | $(RULE typeConstructors)? $(RULE type) $(LITERAL Identifier) $(LITERAL '=') $(RULE expression) * | $(RULE expression) * ;) */ IfStatement parseIfStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; IfStatement node = allocator.make!IfStatement; node.line = current().line; node.column = current().column; mixin(tokenCheck!"if"); if (moreTokens) node.startIndex = current().index; mixin(tokenCheck!"("); const b = setBookmark(); // ex. case: // `if (auto identifier = exp)` if (currentIs(tok!"auto") && peekIs(tok!"identifier")) { abandonBookmark(b); advance(); node.identifier = advance(); mixin(tokenCheck!"="); mixin(parseNodeQ!(`node.expression`, `Expression`)); } // `if (const shared ...` if (!node.expression && moreTokens && isTypeCtor(current.type)) { while (moreTokens) { // type ctor followed by open param is part of the type if (!isTypeCtor(current.type) || peekIs(tok!"(")) break; node.typeCtors ~= advance().type; } } // ex. case: // if (const shared stuff = exp) if (!node.expression && node.typeCtors.length && currentIs(tok!"identifier") && peekIs(tok!"=")) { abandonBookmark(b); node.identifier = advance(); advance(); mixin(parseNodeQ!(`node.expression`, `Expression`)); } if (!node.expression) { const c = allocator.setCheckpoint(); // ex. cases: // if (Type stuff = exp) // if (const shared Type stuff = exp) // if (const shared const(Type) stuff = exp) if (Type tp = parseType()) { if (currentIs(tok!"identifier") && peekIs(tok!"=")) { abandonBookmark(b); node.type = tp; node.identifier = advance(); advance(); mixin(parseNodeQ!(`node.expression`, `Expression`)); } // will try an expr since Type and Expression are ambiguous else allocator.rollback(c); } } // Relational expressions, unaries and such as condition if (!node.expression) { goToBookmark(b); mixin(parseNodeQ!(`node.expression`, `Expression`)); } if (!node.expression) { error("expression or declaration expected"); } mixin(tokenCheck!")"); if (currentIs(tok!"}")) { error("Statement expected", false); node.tokens = tokens[startIndex .. index]; return node; // this line makes DCD better } mixin(parseNodeQ!(`node.thenStatement`, `DeclarationOrStatement`)); if (currentIs(tok!"else")) { advance(); mixin(parseNodeQ!(`node.elseStatement`, `DeclarationOrStatement`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an ImportBind * * $(GRAMMAR $(RULEDEF importBind): * $(LITERAL Identifier) ($(LITERAL '=') $(LITERAL Identifier))? * ;) */ ImportBind parseImportBind() { auto startIndex = index; auto node = allocator.make!ImportBind; const ident = expect(tok!"identifier"); mixin(nullCheck!`ident`); node.left = *ident; if (currentIs(tok!"=")) { advance(); const id = expect(tok!"identifier"); mixin(nullCheck!`id`); node.right = *id; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses ImportBindings * * $(GRAMMAR $(RULEDEF importBindings): * $(RULE _singleImport) $(LITERAL ':') $(RULE importBind) ($(LITERAL ',') $(RULE importBind))* * ;) */ ImportBindings parseImportBindings(SingleImport singleImport) { auto startIndex = index; auto node = allocator.make!ImportBindings; mixin(nullCheck!`node.singleImport = singleImport is null ? parseSingleImport() : singleImport`); mixin(tokenCheck!":"); StackBuffer importBinds; while (moreTokens()) { immutable c = allocator.setCheckpoint(); if (importBinds.put(parseImportBind())) { if (currentIs(tok!",")) advance(); else break; } else { allocator.rollback(c); break; } } ownArray(node.importBinds, importBinds); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an ImportDeclaration * * $(GRAMMAR $(RULEDEF importDeclaration): * $(LITERAL 'import') $(RULE singleImport) ($(LITERAL ',') $(RULE singleImport))* ($(LITERAL ',') $(RULE importBindings))? $(LITERAL ';') * | $(LITERAL 'import') $(RULE importBindings) $(LITERAL ';') * ;) */ ImportDeclaration parseImportDeclaration() { auto startIndex = index; auto node = allocator.make!ImportDeclaration; node.startIndex = current().index; mixin(tokenCheck!"import"); SingleImport si = parseSingleImport(); if (si is null) return null; if (currentIs(tok!":")) node.importBindings = parseImportBindings(si); else { StackBuffer singleImports; singleImports.put(si); if (currentIs(tok!",")) { advance(); while (moreTokens()) { auto single = parseSingleImport(); mixin(nullCheck!`single`); if (currentIs(tok!":")) { node.importBindings = parseImportBindings(single); break; } else { singleImports.put(single); if (currentIs(tok!",")) advance(); else break; } } } ownArray(node.singleImports, singleImports); } node.endIndex = (moreTokens() ? current() : previous()).index + 1; mixin(tokenCheck!";"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an ImportExpression * * $(GRAMMAR $(RULEDEF importExpression): * $(LITERAL 'import') $(LITERAL '$(LPAREN)') $(RULE assignExpression) $(LITERAL '$(RPAREN)') * ;) */ ImportExpression parseImportExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); mixin(simpleParse!(ImportExpression, tok!"import", tok!"(", "assignExpression|parseAssignExpression", tok!")")); } /** * Parses an Index * * $(GRAMMAR $(RULEDEF index): * $(RULE assignExpression) ($(LITERAL '..') $(RULE assignExpression))? * ; * ) */ Index parseIndex() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Index(); mixin(parseNodeQ!(`node.low`, `AssignExpression`)); if (currentIs(tok!"..")) { advance(); mixin(parseNodeQ!(`node.high`, `AssignExpression`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an IndexExpression * * $(GRAMMAR $(RULEDEF indexExpression): * $(RULE _unaryExpression) $(LITERAL '[') $(LITERAL ']') * | $(RULE _unaryExpression) $(LITERAL '[') $(RULE index) ($(LITERAL ',') $(RULE index))* $(LITERAL ']') * ; * ) */ IndexExpression parseIndexExpression(UnaryExpression unaryExpression = null) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!IndexExpression; mixin(nullCheck!`node.unaryExpression = unaryExpression is null ? parseUnaryExpression() : unaryExpression`); mixin(tokenCheck!"["); StackBuffer indexes; while (true) { if (currentIs(tok!"]")) break; if (!(indexes.put(parseIndex()))) return null; if (!moreTokens()) { error("Expected ',' or ']' instead of EOF"); return null; } if (currentIs(tok!",")) advance(); else break; } ownArray(node.indexes, indexes); mixin(tokenCheck!"]"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an InContractExpression * * $(GRAMMAR $(RULEDEF inContractExpression): * $(LITERAL 'in') $(LITERAL '$(LPAREN)') $(RULE assertArguments) $(LITERAL '$(RPAREN)') * ;) */ InContractExpression parseInContractExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!InContractExpression; const i = expect(tok!"in"); mixin(nullCheck!`i`); node.inTokenLocation = i.index; mixin(tokenCheck!"("); mixin(parseNodeQ!(`node.assertArguments`, `AssertArguments`)); mixin(tokenCheck!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an InExpression * * $(GRAMMAR $(RULEDEF inExpression): * $(RULE shiftExpression) ($(LITERAL 'in') | ($(LITERAL '!') $(LITERAL 'in'))) $(RULE shiftExpression) * ;) */ ExpressionNode parseInExpression(ExpressionNode shift = null) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!InExpression; mixin(nullCheck!`node.left = shift is null ? parseShiftExpression() : shift`); if (currentIs(tok!"!")) { node.negated = true; advance(); } mixin(tokenCheck!"in"); mixin(parseNodeQ!(`node.right`, `ShiftExpression`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an InOutContractExpression * * $(GRAMMAR $(RULEDEF inOutContractExpression): * $(RULE inContractExpression) * | $(RULE outContractExpression) * ;) */ InOutContractExpression parseInOutContractExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!InOutContractExpression; if (currentIs(tok!"in")) mixin(parseNodeQ!(`node.inContractExpression`, `InContractExpression`)); else if (currentIs(tok!"out")) mixin(parseNodeQ!(`node.outContractExpression`, `OutContractExpression`)); else return null; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an InOutStatement * * $(GRAMMAR $(RULEDEF inOutStatement): * $(RULE inStatement) * | $(RULE outStatement) * ;) */ InOutStatement parseInOutStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!InOutStatement; if (currentIs(tok!"in")) mixin(parseNodeQ!(`node.inStatement`, `InStatement`)); else if (currentIs(tok!"out")) mixin(parseNodeQ!(`node.outStatement`, `OutStatement`)); else return null; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an InStatement * * $(GRAMMAR $(RULEDEF inStatement): * $(LITERAL 'in') $(RULE blockStatement) * ;) */ InStatement parseInStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!InStatement; const i = expect(tok!"in"); mixin(nullCheck!`i`); node.inTokenLocation = i.index; mixin(parseNodeQ!(`node.blockStatement`, `BlockStatement`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an Initializer * * $(GRAMMAR $(RULEDEF initializer): * $(LITERAL 'void') * | $(RULE nonVoidInitializer) * ;) */ Initializer parseInitializer() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Initializer; if (currentIs(tok!"void") && peekIsOneOf(tok!",", tok!";")) advance(); else mixin(parseNodeQ!(`node.nonVoidInitializer`, `NonVoidInitializer`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an InterfaceDeclaration * * $(GRAMMAR $(RULEDEF interfaceDeclaration): * $(LITERAL 'interface') $(LITERAL Identifier) $(LITERAL ';') * | $(LITERAL 'interface') $(LITERAL Identifier) ($(LITERAL ':') $(RULE baseClassList))? $(RULE structBody) * | $(LITERAL 'interface') $(LITERAL Identifier) $(RULE templateParameters) $(RULE constraint)? ($(LITERAL ':') $(RULE baseClassList))? $(RULE structBody) * | $(LITERAL 'interface') $(LITERAL Identifier) $(RULE templateParameters) ($(LITERAL ':') $(RULE baseClassList))? $(RULE constraint)? $(RULE structBody) * ;) */ InterfaceDeclaration parseInterfaceDeclaration() { auto startIndex = index; auto node = allocator.make!InterfaceDeclaration; expect(tok!"interface"); return parseInterfaceOrClass(node, startIndex); } /** * Parses an Invariant * * $(GRAMMAR $(RULEDEF invariant): * $(LITERAL 'invariant') ($(LITERAL '$(LPAREN)') $(LITERAL '$(LPAREN)'))? $(RULE blockStatement) * | $(LITERAL 'invariant') $(LITERAL '$(LPAREN)') $(RULE assertArguments) $(LITERAL '$(RPAREN)') $(LITERAL ';') * ;) */ Invariant parseInvariant() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Invariant; node.index = current.index; node.line = current.line; mixin(tokenCheck!"invariant"); bool mustHaveBlock; if (currentIs(tok!"(") && peekIs(tok!")")) { mustHaveBlock = true; node.useParen = true; advance(); advance(); } if (currentIs(tok!"{")) { if (currentIs(tok!"(")) { advance(); mixin(tokenCheck!")"); } mixin(parseNodeQ!(`node.blockStatement`, `BlockStatement`)); } else if (!mustHaveBlock && currentIs(tok!"(")) { advance(); node.useParen = true; mixin(parseNodeQ!(`node.assertArguments`, `AssertArguments`)); mixin(tokenCheck!")"); mixin(tokenCheck!";"); } else return null; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an IsExpression * * $(GRAMMAR $(RULEDEF isExpression): * $(LITERAL'is') $(LITERAL '$(LPAREN)') $(RULE type) $(LITERAL identifier)? $(LITERAL '$(RPAREN)') * | $(LITERAL'is') $(LITERAL '$(LPAREN)') $(RULE type) $(LITERAL identifier)? $(LITERAL ':') $(RULE typeSpecialization) $(LITERAL '$(RPAREN)') * | $(LITERAL'is') $(LITERAL '$(LPAREN)') $(RULE type) $(LITERAL identifier)? $(LITERAL '=') $(RULE typeSpecialization) $(LITERAL '$(RPAREN)') * | $(LITERAL'is') $(LITERAL '$(LPAREN)') $(RULE type) $(LITERAL identifier)? $(LITERAL ':') $(RULE typeSpecialization) $(LITERAL ',') $(RULE templateParameterList) $(LITERAL '$(RPAREN)') * | $(LITERAL'is') $(LITERAL '$(LPAREN)') $(RULE type) $(LITERAL identifier)? $(LITERAL '=') $(RULE typeSpecialization) $(LITERAL ',') $(RULE templateParameterList) $(LITERAL '$(RPAREN)') * ;) */ IsExpression parseIsExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!IsExpression; mixin(tokenCheck!"is"); mixin(tokenCheck!"("); mixin(parseNodeQ!(`node.type`, `Type`)); if (currentIs(tok!"identifier")) node.identifier = advance(); if (currentIsOneOf(tok!"==", tok!":")) { node.equalsOrColon = advance().type; mixin(parseNodeQ!(`node.typeSpecialization`, `TypeSpecialization`)); if (currentIs(tok!",")) { advance(); mixin(parseNodeQ!(`node.templateParameterList`, `TemplateParameterList`)); } } mixin(tokenCheck!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a KeyValuePair * * $(GRAMMAR $(RULEDEF keyValuePair): * $(RULE assignExpression) $(LITERAL ':') $(RULE assignExpression) * ;) */ KeyValuePair parseKeyValuePair() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!KeyValuePair; mixin(parseNodeQ!(`node.key`, `AssignExpression`)); mixin(tokenCheck!":"); mixin(parseNodeQ!(`node.value`, `AssignExpression`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses KeyValuePairs * * $(GRAMMAR $(RULEDEF keyValuePairs): * $(RULE keyValuePair) ($(LITERAL ',') $(RULE keyValuePair))* $(LITERAL ',')? * ;) */ KeyValuePairs parseKeyValuePairs() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!KeyValuePairs; StackBuffer keyValuePairs; while (moreTokens()) { if (!keyValuePairs.put(parseKeyValuePair())) return null; if (currentIs(tok!",")) { advance(); if (currentIs(tok!"]")) break; } else break; } ownArray(node.keyValuePairs, keyValuePairs); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a LabeledStatement * * $(GRAMMAR $(RULEDEF labeledStatement): * $(LITERAL Identifier) $(LITERAL ':') $(RULE declarationOrStatement)? * ;) */ LabeledStatement parseLabeledStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!LabeledStatement; const ident = expect(tok!"identifier"); mixin (nullCheck!`ident`); node.identifier = *ident; expect(tok!":"); if (!currentIs(tok!"}")) mixin(parseNodeQ!(`node.declarationOrStatement`, `DeclarationOrStatement`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a LastCatch * * $(GRAMMAR $(RULEDEF lastCatch): * $(LITERAL 'catch') $(RULE statementNoCaseNoDefault) * ;) */ LastCatch parseLastCatch() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!LastCatch; const t = expect(tok!"catch"); mixin (nullCheck!`t`); node.line = t.line; no
// Written in the D programming language module dparse.parser; import dparse.lexer; import dparse.ast; import dparse.rollback_allocator; import dparse.stack_buffer; import std.experimental.allocator.mallocator; import std.experimental.allocator; import std.conv; import std.algorithm; import std.array; import std.string : format; // Uncomment this if you want ALL THE OUTPUT // Caution: generates 180 megabytes of logging for std.datetime //version = dparse_verbose; /** * Prototype for a custom parser message function or delegate. * Parameters passed are a file name, a line, a column, a message and a `bool` * that indicates if the message is a warning (`false`) or a if it's an error (`true`). */ alias MessageFunction = void function(string fileName , size_t line, size_t column, string message, bool isError); /// ditto alias MessageDelegate = void delegate(string, size_t, size_t, string, bool); /** * Parser configuration struct */ struct ParserConfig { /// The tokens parsed by dparse.lexer. const(Token)[] tokens; /// The name of the file being parsed string fileName; /// A pointer to a rollback allocator. RollbackAllocator* allocator; /// An optional function used to handle warnings and errors. MessageFunction messageFunction; /// An optional delegate used to handle warnings and errors. /// Set either this one or messageFunction, not both. MessageDelegate messageDelegate; /// An optional pointer to a variable receiving the error count. uint* errorCount; /// An optional pointer to a variable receiving the warning count. uint* warningCount; } /** * Params: * parserConfig = a parser configuration. * Returns: * The parsed module. */ Module parseModule()(auto ref ParserConfig parserConfig) { auto parser = new Parser(); with (parserConfig) { parser.fileName = fileName; parser.tokens = tokens; parser.messageFunction = messageFunction; parser.messageDelegate = messageDelegate; parser.allocator = allocator; } Module mod = parser.parseModule(); with (parserConfig) { if (warningCount !is null) *warningCount = parser.warningCount; if (errorCount !is null) *errorCount = parser.errorCount; } return mod; } /** * Params: * tokens = The tokens parsed by dparse.lexer. * fileName = The name of the file being parsed. * allocator = A pointer to a rollback allocator. * messageFuncOrDg = Either a function or a delegate that receives the parser messages. * errorCount = An optional pointer to a variable receiving the error count. * warningCount = An optional pointer to a variable receiving the warning count. * Returns: * The parsed module. */ Module parseModule(F)(const(Token)[] tokens, string fileName, RollbackAllocator* allocator, F messageFuncOrDg = null, uint* errorCount = null, uint* warningCount = null) { static if (is(F)) { static if (is(F : MessageFunction)) return ParserConfig(tokens, fileName, allocator, messageFuncOrDg, null, errorCount, warningCount).parseModule(); else static if (is(F : MessageDelegate)) return ParserConfig(tokens, fileName, allocator, null, messageFuncOrDg, errorCount, warningCount).parseModule(); else static assert(0, "F must be a MessageFunction or a MessageDelegate"); } else { return ParserConfig(tokens, fileName, allocator, null, null, null, null).parseModule(); } } /** * D Parser. * * It is sometimes useful to sub-class Parser to skip over things that are not * interesting. For example, DCD skips over function bodies when caching symbols * from imported files. */ class Parser { /** * Parses an AddExpression. * * $(GRAMMAR $(RULEDEF addExpression): * $(RULE mulExpression) * | $(RULE addExpression) $(LPAREN)$(LITERAL '+') | $(LITERAL'-') | $(LITERAL'~')$(RPAREN) $(RULE mulExpression) * ;) */ ExpressionNode parseAddExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AddExpression, MulExpression, tok!"+", tok!"-", tok!"~")(); } /** * Parses an AliasDeclaration. * * $(GRAMMAR $(RULEDEF aliasDeclaration): * $(LITERAL 'alias') $(RULE aliasInitializer) $(LPAREN)$(LITERAL ',') $(RULE aliasInitializer)$(RPAREN)* $(LITERAL ';') * | $(LITERAL 'alias') $(RULE storageClass)* $(RULE type) $(RULE declaratorIdentifierList) $(LITERAL ';') * | $(LITERAL 'alias') $(RULE storageClass)* $(RULE type) $(RULE identifier) $(LITERAL '(') $(RULE parameters) $(LITERAL ')') $(memberFunctionAttribute)* $(LITERAL ';') * ;) */ AliasDeclaration parseAliasDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AliasDeclaration; mixin(tokenCheck!"alias"); node.comment = comment; comment = null; if (startsWith(tok!"identifier", tok!"=") || startsWith(tok!"identifier", tok!"(")) { StackBuffer initializers; do { if (!initializers.put(parseAliasInitializer())) return null; if (currentIs(tok!",")) advance(); else break; } while (moreTokens()); ownArray(node.initializers, initializers); } else { StackBuffer storageClasses; while (moreTokens() && isStorageClass()) if (!storageClasses.put(parseStorageClass())) return null; ownArray(node.storageClasses, storageClasses); mixin (parseNodeQ!(`node.type`, `Type`)); mixin (parseNodeQ!(`node.declaratorIdentifierList`, `DeclaratorIdentifierList`)); if (currentIs(tok!"(")) { mixin(parseNodeQ!(`node.parameters`, `Parameters`)); StackBuffer memberFunctionAttributes; while (moreTokens() && currentIsMemberFunctionAttribute()) if (!memberFunctionAttributes.put(parseMemberFunctionAttribute())) return null; ownArray(node.memberFunctionAttributes, memberFunctionAttributes); } } return attachCommentFromSemicolon(node, startIndex); } /** * Parses an AliasAssign. * * $(GRAMMAR $(RULEDEF aliasAssign): * $(LITERAL Identifier) $(LITERAL '=') $(RULE type) */ AliasAssign parseAliasAssign() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AliasAssign; node.comment = comment; comment = null; mixin(tokenCheck!(`node.identifier`, "identifier")); mixin(tokenCheck!"="); mixin(parseNodeQ!(`node.type`, `Type`)); return attachCommentFromSemicolon(node, startIndex); } /** * Parses an AliasInitializer. * * $(GRAMMAR $(RULEDEF aliasInitializer): * $(LITERAL Identifier) $(RULE templateParameters)? $(LITERAL '=') $(RULE storageClass)* $(RULE type) * | $(LITERAL Identifier) $(RULE templateParameters)? $(LITERAL '=') $(RULE storageClass)* $(RULE type) $(RULE parameters) $(RULE memberFunctionAttribute)* * | $(LITERAL Identifier) $(RULE templateParameters)? $(LITERAL '=') $(RULE functionLiteralExpression) * ;) */ AliasInitializer parseAliasInitializer() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AliasInitializer; mixin (tokenCheck!(`node.name`, "identifier")); if (currentIs(tok!"(")) mixin (parseNodeQ!(`node.templateParameters`, `TemplateParameters`)); mixin(tokenCheck!"="); bool isFunction() { if (currentIsOneOf(tok!"function", tok!"delegate", tok!"{")) return true; if (startsWith(tok!"identifier", tok!"=>")) return true; const b = setBookmark(); scope(exit) goToBookmark(b); if (currentIs(tok!"(") || currentIs(tok!"ref") && peekIs(tok!"(")) { if (currentIs(tok!"ref")) advance(); const t = peekPastParens(); if (t !is null) { if (t.type == tok!"=>" || t.type == tok!"{" || isMemberFunctionAttribute(t.type)) return true; } } return false; } if (isFunction) mixin (parseNodeQ!(`node.functionLiteralExpression`, `FunctionLiteralExpression`)); else { StackBuffer storageClasses; while (moreTokens() && isStorageClass()) if (!storageClasses.put(parseStorageClass())) return null; ownArray(node.storageClasses, storageClasses); mixin (parseNodeQ!(`node.type`, `Type`)); if (currentIs(tok!"(")) { mixin (parseNodeQ!(`node.parameters`, `Parameters`)); StackBuffer memberFunctionAttributes; while (moreTokens() && currentIsMemberFunctionAttribute()) if (!memberFunctionAttributes.put(parseMemberFunctionAttribute())) return null; ownArray(node.memberFunctionAttributes, memberFunctionAttributes); } } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AliasThisDeclaration. * * $(GRAMMAR $(RULEDEF aliasThisDeclaration): * $(LITERAL 'alias') $(LITERAL Identifier) $(LITERAL 'this') $(LITERAL ';') * ;) */ AliasThisDeclaration parseAliasThisDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AliasThisDeclaration; mixin(tokenCheck!"alias"); mixin(tokenCheck!(`node.identifier`, "identifier")); mixin(tokenCheck!"this"); return attachCommentFromSemicolon(node, startIndex); } /** * Parses an AlignAttribute. * * $(GRAMMAR $(RULEDEF alignAttribute): * $(LITERAL 'align') ($(LITERAL '$(LPAREN)') $(RULE assignExpression) $(LITERAL '$(RPAREN)'))? * ;) */ AlignAttribute parseAlignAttribute() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AlignAttribute; expect(tok!"align"); if (currentIs(tok!"(")) { mixin(tokenCheck!"("); mixin(parseNodeQ!("node.assignExpression", "AssignExpression")); mixin(tokenCheck!")"); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AndAndExpression. * * $(GRAMMAR $(RULEDEF andAndExpression): * $(RULE orExpression) * | $(RULE andAndExpression) $(LITERAL '&&') $(RULE orExpression) * ;) */ ExpressionNode parseAndAndExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AndAndExpression, OrExpression, tok!"&&")(); } /** * Parses an AndExpression. * * $(GRAMMAR $(RULEDEF andExpression): * $(RULE cmpExpression) * | $(RULE andExpression) $(LITERAL '&') $(RULE cmpExpression) * ;) */ ExpressionNode parseAndExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AndExpression, CmpExpression, tok!"&")(); } /** * Parses an ArgumentList. * * $(GRAMMAR $(RULEDEF argumentList): * $(RULE assignExpression) ($(LITERAL ',') $(RULE assignExpression)?)* * ;) */ ArgumentList parseArgumentList() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; if (!moreTokens) { error("argument list expected instead of EOF"); return null; } size_t startLocation = current().index; auto node = parseCommaSeparatedRule!(ArgumentList, AssignExpression)(true); mixin (nullCheck!`node`); node.startLocation = startLocation; if (moreTokens) node.endLocation = current().index; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses Arguments. * * $(GRAMMAR $(RULEDEF arguments): * $(LITERAL '$(LPAREN)') $(RULE argumentList)? $(LITERAL '$(RPAREN)') * ;) */ Arguments parseArguments() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Arguments; mixin(tokenCheck!"("); if (!currentIs(tok!")")) mixin (parseNodeQ!(`node.argumentList`, `ArgumentList`)); mixin(tokenCheck!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an ArrayInitializer. * * $(GRAMMAR $(RULEDEF arrayInitializer): * $(LITERAL '[') $(LITERAL ']') * | $(LITERAL '[') $(RULE arrayMemberInitialization) ($(LITERAL ',') $(RULE arrayMemberInitialization)?)* $(LITERAL ']') * ;) */ ArrayInitializer parseArrayInitializer() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ArrayInitializer; const open = expect(tok!"["); mixin (nullCheck!`open`); node.startLocation = open.index; StackBuffer arrayMemberInitializations; while (moreTokens()) { if (currentIs(tok!"]")) break; if (!arrayMemberInitializations.put(parseArrayMemberInitialization())) return null; if (currentIs(tok!",")) advance(); else break; } ownArray(node.arrayMemberInitializations, arrayMemberInitializations); const close = expect(tok!"]"); mixin (nullCheck!`close`); node.endLocation = close.index; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an ArrayLiteral. * * $(GRAMMAR $(RULEDEF arrayLiteral): * $(LITERAL '[') $(RULE argumentList)? $(LITERAL ']') * ;) */ ArrayLiteral parseArrayLiteral() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ArrayLiteral; mixin(tokenCheck!"["); if (!currentIs(tok!"]")) mixin (parseNodeQ!(`node.argumentList`, `ArgumentList`)); mixin(tokenCheck!"]"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an ArrayMemberInitialization. * * $(GRAMMAR $(RULEDEF arrayMemberInitialization): * ($(RULE assignExpression) $(LITERAL ':'))? $(RULE nonVoidInitializer) * ;) */ ArrayMemberInitialization parseArrayMemberInitialization() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ArrayMemberInitialization; switch (current.type) { case tok!"[": immutable b = setBookmark(); skipBrackets(); if (currentIs(tok!":")) { goToBookmark(b); mixin (parseNodeQ!(`node.assignExpression`, `AssignExpression`)); advance(); // : mixin (parseNodeQ!(`node.nonVoidInitializer`, `NonVoidInitializer`)); break; } else { goToBookmark(b); goto case; } case tok!"{": mixin (parseNodeQ!(`node.nonVoidInitializer`, `NonVoidInitializer`)); break; default: auto assignExpression = parseAssignExpression(); mixin (nullCheck!`assignExpression`); if (currentIs(tok!":")) { node.assignExpression = assignExpression; advance(); mixin(parseNodeQ!(`node.nonVoidInitializer`, `NonVoidInitializer`)); } else { node.nonVoidInitializer = allocator.make!NonVoidInitializer; node.nonVoidInitializer.assignExpression = assignExpression; } } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AsmAddExp * * $(GRAMMAR $(RULEDEF asmAddExp): * $(RULE asmMulExp) * | $(RULE asmAddExp) ($(LITERAL '+') | $(LITERAL '-')) $(RULE asmMulExp) * ;) */ ExpressionNode parseAsmAddExp() { mixin (traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmAddExp, AsmMulExp, tok!"+", tok!"-")(); } /** * Parses an AsmAndExp * * $(GRAMMAR $(RULEDEF asmAndExp): * $(RULE asmEqualExp) * | $(RULE asmAndExp) $(LITERAL '&') $(RULE asmEqualExp) * ;) */ ExpressionNode parseAsmAndExp() { mixin (traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmAndExp, AsmEqualExp, tok!"&"); } /** * Parses an AsmBrExp * * $(GRAMMAR $(RULEDEF asmBrExp): * $(RULE asmUnaExp) * | $(RULE asmBrExp)? $(LITERAL '[') $(RULE asmExp) $(LITERAL ']') * ;) */ AsmBrExp parseAsmBrExp() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; if (!moreTokens) { error("Found end-of-file when expecting an AsmBrExp", false); return null; } AsmBrExp node = allocator.make!AsmBrExp(); size_t line = current.line; size_t column = current.column; if (currentIs(tok!"[")) { advance(); // [ mixin (parseNodeQ!(`node.asmExp`, `AsmExp`)); mixin(tokenCheck!"]"); if (currentIs(tok!"[")) goto brLoop; } else { mixin(parseNodeQ!(`node.asmUnaExp`, `AsmUnaExp`)); brLoop: while (currentIs(tok!"[")) { AsmBrExp br = allocator.make!AsmBrExp(); // huehuehuehue br.asmBrExp = node; br.line = current().line; br.column = current().column; node = br; node.line = line; node.column = column; advance(); // [ mixin(parseNodeQ!(`node.asmExp`, `AsmExp`)); mixin(tokenCheck!"]"); } } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AsmEqualExp * * $(GRAMMAR $(RULEDEF asmEqualExp): * $(RULE asmRelExp) * | $(RULE asmEqualExp) ('==' | '!=') $(RULE asmRelExp) * ;) */ ExpressionNode parseAsmEqualExp() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmEqualExp, AsmRelExp, tok!"==", tok!"!=")(); } /** * Parses an AsmExp * * $(GRAMMAR $(RULEDEF asmExp): * $(RULE asmLogOrExp) ($(LITERAL '?') $(RULE asmExp) $(LITERAL ':') $(RULE asmExp))? * ;) */ ExpressionNode parseAsmExp() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; AsmExp node = allocator.make!AsmExp; mixin(parseNodeQ!(`node.left`, `AsmLogOrExp`)); if (currentIs(tok!"?")) { advance(); mixin(parseNodeQ!(`node.middle`, `AsmExp`)); mixin(tokenCheck!":"); mixin(parseNodeQ!(`node.right`, `AsmExp`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AsmInstruction * * $(GRAMMAR $(RULEDEF asmInstruction): * $(LITERAL Identifier) * | $(LITERAL 'align') $(LITERAL IntegerLiteral) * | $(LITERAL 'align') $(LITERAL Identifier) * | $(LITERAL Identifier) $(LITERAL ':') $(RULE asmInstruction) * | $(LITERAL Identifier) $(RULE operands) * | $(LITERAL 'in') $(RULE operands) * | $(LITERAL 'out') $(RULE operands) * | $(LITERAL 'int') $(RULE operands) * | $(LITERAL ';') * ;) */ AsmInstruction parseAsmInstruction(ref bool maybeGccASm) { mixin (traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; AsmInstruction node = allocator.make!AsmInstruction; if (currentIs(tok!";")) { warn("Empty asm instruction"); node.tokens = tokens[startIndex .. index]; return node; } if (currentIs(tok!"align")) { advance(); // align node.hasAlign = true; if (currentIsOneOf(tok!"intLiteral", tok!"identifier")) { node.identifierOrIntegerOrOpcode = advance(); if (!currentIs(tok!";")) { error("`;` expected."); if (moreTokens()) advance(); return null; } } else { error("Identifier or integer literal expected."); return null; } } else if (currentIsOneOf(tok!"identifier", tok!"in", tok!"out", tok!"int")) { node.identifierOrIntegerOrOpcode = advance(); if (node.identifierOrIntegerOrOpcode == tok!"identifier" && currentIs(tok!":")) { advance(); // : node.isLabel = true; if (currentIs(tok!";")) { node.tokens = tokens[startIndex .. index]; return node; } node.asmInstruction = parseAsmInstruction(maybeGccASm); if (node.asmInstruction is null) return null; } else if (!currentIs(tok!";")) mixin(parseNodeQ!(`node.operands`, `Operands`)); } else { maybeGccASm = true; return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AsmLogAndExp * * $(GRAMMAR $(RULEDEF asmLogAndExp): * $(RULE asmOrExp) * $(RULE asmLogAndExp) $(LITERAL '&&') $(RULE asmOrExp) * ;) */ ExpressionNode parseAsmLogAndExp() { mixin (traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmLogAndExp, AsmOrExp, tok!"&&"); } /** * Parses an AsmLogOrExp * * $(GRAMMAR $(RULEDEF asmLogOrExp): * $(RULE asmLogAndExp) * | $(RULE asmLogOrExp) '||' $(RULE asmLogAndExp) * ;) */ ExpressionNode parseAsmLogOrExp() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmLogOrExp, AsmLogAndExp, tok!"||")(); } /** * Parses an AsmMulExp * * $(GRAMMAR $(RULEDEF asmMulExp): * $(RULE asmBrExp) * | $(RULE asmMulExp) ($(LITERAL '*') | $(LITERAL '/') | $(LITERAL '%')) $(RULE asmBrExp) * ;) */ ExpressionNode parseAsmMulExp() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmMulExp, AsmBrExp, tok!"*", tok!"/", tok!"%")(); } /** * Parses an AsmOrExp * * $(GRAMMAR $(RULEDEF asmOrExp): * $(RULE asmXorExp) * | $(RULE asmOrExp) $(LITERAL '|') $(RULE asmXorExp) * ;) */ ExpressionNode parseAsmOrExp() { mixin (traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmOrExp, AsmXorExp, tok!"|")(); } /** * Parses an AsmPrimaryExp * * $(GRAMMAR $(RULEDEF asmPrimaryExp): * $(LITERAL IntegerLiteral) * | $(LITERAL FloatLiteral) * | $(LITERAL StringLiteral) * | $(RULE register) * | $(RULE register : AsmExp) * | $(RULE identifierChain) * | $(LITERAL '$') * | $(LITERAL 'this') * | $(LITERAL '__LOCAL_SIZE') * ;) */ AsmPrimaryExp parseAsmPrimaryExp() { import std.range : assumeSorted; mixin (traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; AsmPrimaryExp node = allocator.make!AsmPrimaryExp(); switch (current().type) { foreach (NL; NumberLiterals) {case NL:} case tok!"stringLiteral": case tok!"$": case tok!"this": node.token = advance(); break; case tok!"identifier": if (assumeSorted(REGISTER_NAMES).equalRange(current().text).length > 0) { trace("Found register"); mixin (nullCheck!`(node.register = parseRegister())`); if (currentIs(tok!":")) { advance(); mixin(parseNodeQ!(`node.segmentOverrideSuffix`, `AsmExp`)); } } else mixin(parseNodeQ!(`node.identifierChain`, `IdentifierChain`)); break; default: error("Float literal, integer literal, `$`, `this` or identifier expected."); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AsmRelExp * * $(GRAMMAR $(RULEDEF asmRelExp): * $(RULE asmShiftExp) * | $(RULE asmRelExp) (($(LITERAL '<') | $(LITERAL '<=') | $(LITERAL '>') | $(LITERAL '>=')) $(RULE asmShiftExp))? * ;) */ ExpressionNode parseAsmRelExp() { mixin (traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmRelExp, AsmShiftExp, tok!"<", tok!"<=", tok!">", tok!">=")(); } /** * Parses an AsmShiftExp * * $(GRAMMAR $(RULEDEF asmShiftExp): * $(RULE asmAddExp) * $(RULE asmShiftExp) ($(LITERAL '<<') | $(LITERAL '>>') | $(LITERAL '>>>')) $(RULE asmAddExp) * ;) */ ExpressionNode parseAsmShiftExp() { mixin (traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmShiftExp, AsmAddExp, tok!"<<", tok!">>", tok!">>>"); } /** * Parses an AsmStatement * * $(GRAMMAR $(RULEDEF asmStatement): * $(LITERAL 'asm') $(RULE functionAttributes)? $(LITERAL '{') ( $(RULE asmInstruction)+ | $(RULE gccAsmInstruction)+ ) $(LITERAL '}') * ;) */ AsmStatement parseAsmStatement() { mixin (traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; AsmStatement node = allocator.make!AsmStatement; advance(); // asm StackBuffer functionAttributes; while (isAttribute()) { if (!functionAttributes.put(parseFunctionAttribute())) { error("Function attribute or `{` expected"); return null; } } ownArray(node.functionAttributes, functionAttributes); expect(tok!"{"); // DMD-style and GCC-style assembly might look identical in the beginning. // Try DMD style first and restart with GCC if it fails because of GCC elements bool maybeGccStyle; const instrStart = allocator.setCheckpoint(); const instrStartIdx = index; StackBuffer instructions; while (moreTokens() && !currentIs(tok!"}")) { auto c = allocator.setCheckpoint(); if (!instructions.put(parseAsmInstruction(maybeGccStyle))) { if (maybeGccStyle) break; allocator.rollback(c); } else expect(tok!";"); } if (!maybeGccStyle) { ownArray(node.asmInstructions, instructions); } else { // Revert to the beginning of the first instruction destroy(instructions); allocator.rollback(instrStart); index = instrStartIdx; while (moreTokens() && !currentIs(tok!"}")) { auto c = allocator.setCheckpoint(); if (!instructions.put(parseGccAsmInstruction())) allocator.rollback(c); else expect(tok!";"); } ownArray(node.gccAsmInstructions, instructions); } expect(tok!"}"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AsmTypePrefix * * Note that in the following grammar definition the first identifier must * be "near", "far", "word", "dword", or "qword". The second identifier must * be "ptr". * * $(GRAMMAR $(RULEDEF asmTypePrefix): * $(LITERAL Identifier) $(LITERAL Identifier)? * | $(LITERAL 'byte') $(LITERAL Identifier)? * | $(LITERAL 'short') $(LITERAL Identifier)? * | $(LITERAL 'int') $(LITERAL Identifier)? * | $(LITERAL 'float') $(LITERAL Identifier)? * | $(LITERAL 'double') $(LITERAL Identifier)? * | $(LITERAL 'real') $(LITERAL Identifier)? * ;) */ AsmTypePrefix parseAsmTypePrefix() { mixin (traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; switch (current().type) { case tok!"identifier": case tok!"byte": case tok!"short": case tok!"int": case tok!"float": case tok!"double": case tok!"real": AsmTypePrefix node = allocator.make!AsmTypePrefix(); node.left = advance(); if (node.left.type == tok!"identifier") switch (node.left.text) { case "near": case "far": case "word": case "dword": case "qword": break; default: error("ASM type node expected"); return null; } if (currentIs(tok!"identifier") && current().text == "ptr") node.right = advance(); node.tokens = tokens[startIndex .. index]; return node; default: error("Expected an identifier, `byte`, `short`, `int`, `float`, `double`, or `real`"); return null; } } /** * Parses an AsmUnaExp * * $(GRAMMAR $(RULEDEF asmUnaExp): * $(RULE asmTypePrefix) $(RULE asmExp) * | $(LITERAL Identifier) $(RULE asmExp) * | $(LITERAL '+') $(RULE asmUnaExp) * | $(LITERAL '-') $(RULE asmUnaExp) * | $(LITERAL '!') $(RULE asmUnaExp) * | $(LITERAL '~') $(RULE asmUnaExp) * | $(RULE asmPrimaryExp) * ;) */ AsmUnaExp parseAsmUnaExp() { mixin (traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; AsmUnaExp node = allocator.make!AsmUnaExp(); switch (current().type) { case tok!"+": case tok!"-": case tok!"!": case tok!"~": node.prefix = advance(); mixin(parseNodeQ!(`node.asmUnaExp`, `AsmUnaExp`)); break; case tok!"byte": case tok!"short": case tok!"int": case tok!"float": case tok!"double": case tok!"real": typePrefix: mixin(parseNodeQ!(`node.asmTypePrefix`, `AsmTypePrefix`)); mixin(parseNodeQ!(`node.asmExp`, `AsmExp`)); break; case tok!"identifier": switch (current().text) { case "offsetof": case "seg": node.prefix = advance(); mixin(parseNodeQ!(`node.asmExp`, `AsmExp`)); break; case "near": case "far": case "word": case "dword": case "qword": goto typePrefix; default: goto outerDefault; } break; outerDefault: default: mixin(parseNodeQ!(`node.asmPrimaryExp`, `AsmPrimaryExp`)); break; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AsmXorExp * * $(GRAMMAR $(RULEDEF asmXorExp): * $(RULE asmAndExp) * | $(RULE asmXorExp) $(LITERAL '^') $(RULE asmAndExp) * ;) */ ExpressionNode parseAsmXorExp() { mixin (traceEnterAndExit!(__FUNCTION__)); return parseLeftAssocBinaryExpression!(AsmXorExp, AsmAndExp, tok!"^")(); } /** * Parses an AssertArguments * * $(GRAMMAR $(RULEDEF assertArguments): * $(RULE assignExpression) ($(LITERAL ',') $(RULE assignExpression))? $(LITERAL ',')? * ;) */ AssertArguments parseAssertArguments() { auto startIndex = index; auto node = allocator.make!AssertArguments; mixin(parseNodeQ!(`node.assertion`, `AssignExpression`)); if (currentIs(tok!",")) advance(); if (currentIs(tok!")")) return node; mixin(parseNodeQ!(`node.message`, `AssignExpression`)); if (currentIs(tok!",")) advance(); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AssertExpression * * $(GRAMMAR $(RULEDEF assertExpression): * $(LITERAL 'assert') $(LITERAL '$(LPAREN)') $(RULE assertArguments) $(LITERAL '$(RPAREN)') * ;) */ AssertExpression parseAssertExpression() { auto startIndex = index; mixin(traceEnterAndExit!(__FUNCTION__)); auto node = allocator.make!AssertExpression; node.line = current.line; node.column = current.column; advance(); // "assert" mixin(tokenCheck!"("); mixin(parseNodeQ!(`node.assertArguments`, `AssertArguments`)); mixin(tokenCheck!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AssignExpression * * $(GRAMMAR $(RULEDEF assignExpression): * $(RULE ternaryExpression) ($(RULE assignOperator) $(RULE assignExpression))? * ; *$(RULEDEF assignOperator): * $(LITERAL '=') * | $(LITERAL '>>>=') * | $(LITERAL '>>=') * | $(LITERAL '<<=') * | $(LITERAL '+=') * | $(LITERAL '-=') * | $(LITERAL '*=') * | $(LITERAL '%=') * | $(LITERAL '&=') * | $(LITERAL '/=') * | $(LITERAL '|=') * | $(LITERAL '^^=') * | $(LITERAL '^=') * | $(LITERAL '~=') * ;) */ ExpressionNode parseAssignExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; if (!moreTokens) { error("Assign expression expected instead of EOF"); return null; } auto ternary = parseTernaryExpression(); if (ternary is null) return null; if (currentIsOneOf(tok!"=", tok!">>>=", tok!">>=", tok!"<<=", tok!"+=", tok!"-=", tok!"*=", tok!"%=", tok!"&=", tok!"/=", tok!"|=", tok!"^^=", tok!"^=", tok!"~=")) { auto node = allocator.make!AssignExpression; node.line = current().line; node.column = current().column; node.ternaryExpression = ternary; node.operator = advance().type; mixin(parseNodeQ!(`node.expression`, `AssignExpression`)); node.tokens = tokens[startIndex .. index]; return node; } return ternary; } /** * Parses an AssocArrayLiteral * * $(GRAMMAR $(RULEDEF assocArrayLiteral): * $(LITERAL '[') $(RULE keyValuePairs) $(LITERAL ']') * ;) */ AssocArrayLiteral parseAssocArrayLiteral() { mixin(traceEnterAndExit!(__FUNCTION__)); mixin (simpleParse!(AssocArrayLiteral, tok!"[", "keyValuePairs|parseKeyValuePairs", tok!"]")); } /** * Parses an AtAttribute * * $(GRAMMAR $(RULEDEF atAttribute): * $(LITERAL '@') $(LITERAL Identifier) * | $(LITERAL '@') $(LITERAL Identifier) $(LITERAL '$(LPAREN)') $(RULE argumentList)? $(LITERAL '$(RPAREN)') * | $(LITERAL '@') $(LITERAL '$(LPAREN)') $(RULE argumentList) $(LITERAL '$(RPAREN)') * | $(LITERAL '@') $(RULE templateInstance) * ;) */ AtAttribute parseAtAttribute() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AtAttribute; const start = expect(tok!"@"); mixin (nullCheck!`start`); if (!moreTokens) { error("`(`, or identifier expected"); return null; } node.startLocation = start.index; switch (current.type) { case tok!"identifier": if (peekIs(tok!"!")) mixin(parseNodeQ!(`node.templateInstance`, `TemplateInstance`)); else node.identifier = advance(); if (currentIs(tok!"(")) { advance(); // ( node.useParen = true; if (!currentIs(tok!")")) mixin(parseNodeQ!(`node.argumentList`, `ArgumentList`)); expect(tok!")"); } break; case tok!"(": advance(); node.useParen = true; mixin(parseNodeQ!(`node.argumentList`, `ArgumentList`)); expect(tok!")"); break; default: error("`(`, or identifier expected"); return null; } if (moreTokens) node.endLocation = current().index; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an Attribute * * $(GRAMMAR $(RULEDEF attribute): * $(RULE pragmaExpression) * | $(RULE alignAttribute) * | $(RULE deprecated) * | $(RULE atAttribute) * | $(RULE linkageAttribute) * | $(LITERAL 'export') * | $(LITERAL 'package') ($(LITERAL "(") $(RULE identifierChain) $(LITERAL ")"))? * | $(LITERAL 'private') * | $(LITERAL 'protected') * | $(LITERAL 'public') * | $(LITERAL 'static') * | $(LITERAL 'extern') * | $(LITERAL 'abstract') * | $(LITERAL 'final') * | $(LITERAL 'override') * | $(LITERAL 'synchronized') * | $(LITERAL 'auto') * | $(LITERAL 'scope') * | $(LITERAL 'const') * | $(LITERAL 'immutable') * | $(LITERAL 'inout') * | $(LITERAL 'shared') * | $(LITERAL '__gshared') * | $(LITERAL 'nothrow') * | $(LITERAL 'pure') * | $(LITERAL 'ref') * | $(LITERAL 'throw') * ;) */ Attribute parseAttribute() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Attribute; switch (current.type) { case tok!"pragma": mixin(parseNodeQ!(`node.pragmaExpression`, `PragmaExpression`)); break; case tok!"deprecated": mixin(parseNodeQ!(`node.deprecated_`, `Deprecated`)); break; case tok!"align": mixin(parseNodeQ!(`node.alignAttribute`, `AlignAttribute`)); break; case tok!"@": mixin(parseNodeQ!(`node.atAttribute`, `AtAttribute`)); break; case tok!"package": node.attribute = advance(); if (currentIs(tok!"(")) { expect(tok!"("); mixin(parseNodeQ!(`node.identifierChain`, `IdentifierChain`)); expect(tok!")"); } break; case tok!"extern": if (peekIs(tok!"(")) { mixin(parseNodeQ!(`node.linkageAttribute`, `LinkageAttribute`)); break; } else goto case; case tok!"private": case tok!"protected": case tok!"public": case tok!"export": case tok!"static": case tok!"abstract": case tok!"final": case tok!"override": case tok!"synchronized": case tok!"auto": case tok!"scope": case tok!"const": case tok!"immutable": case tok!"inout": case tok!"shared": case tok!"__gshared": case tok!"nothrow": case tok!"pure": case tok!"ref": case tok!"throw": node.attribute = advance(); break; default: return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AttributeDeclaration * * $(GRAMMAR $(RULEDEF attributeDeclaration): * $(RULE _attribute) $(LITERAL ':') * ;) */ AttributeDeclaration parseAttributeDeclaration(Attribute attribute = null) { auto startIndex = index; auto node = allocator.make!AttributeDeclaration; node.line = current.line; node.attribute = attribute is null ? parseAttribute() : attribute; expect(tok!":"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AutoDeclaration * * $(GRAMMAR $(RULEDEF autoDeclaration): * $(RULE storageClass)+ $(RULE autoDeclarationPart) ($(LITERAL ',') $(RULE autoDeclarationPart))* $(LITERAL ';') * ;) */ AutoDeclaration parseAutoDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AutoDeclaration; node.comment = comment; comment = null; StackBuffer storageClasses; while (isStorageClass()) if (!storageClasses.put(parseStorageClass())) return null; ownArray(node.storageClasses, storageClasses); StackBuffer parts; do { if (!parts.put(parseAutoDeclarationPart())) return null; if (currentIs(tok!",")) advance(); else break; } while (moreTokens()); ownArray(node.parts, parts); return attachCommentFromSemicolon(node, startIndex); } /** * Parses an AutoDeclarationPart * * $(GRAMMAR $(RULEDEF autoDeclarationPart): * $(LITERAL Identifier) $(RULE templateParameters)? $(LITERAL '=') $(RULE initializer) * ;) */ AutoDeclarationPart parseAutoDeclarationPart() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto part = allocator.make!AutoDeclarationPart; auto i = expect(tok!"identifier"); if (i is null) return null; part.identifier = *i; if (currentIs(tok!"(")) mixin(parseNodeQ!("part.templateParameters", "TemplateParameters")); mixin(tokenCheck!"="); mixin(parseNodeQ!("part.initializer", "Initializer")); part.tokens = tokens[startIndex .. index]; return part; } /** * Parses a BlockStatement * * $(GRAMMAR $(RULEDEF blockStatement): * $(LITERAL '{') $(RULE declarationsAndStatements)? $(LITERAL '}') * ;) */ BlockStatement parseBlockStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!BlockStatement; const openBrace = expect(tok!"{"); mixin (nullCheck!`openBrace`); node.startLocation = openBrace.index; if (!currentIs(tok!"}")) { mixin(parseNodeQ!(`node.declarationsAndStatements`, `DeclarationsAndStatements`)); } const closeBrace = expect(tok!"}"); if (closeBrace !is null) node.endLocation = closeBrace.index; else { trace("Could not find end of block statement."); node.endLocation = size_t.max; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a BreakStatement * * $(GRAMMAR $(RULEDEF breakStatement): * $(LITERAL 'break') $(LITERAL Identifier)? $(LITERAL ';') * ;) */ BreakStatement parseBreakStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; expect(tok!"break"); if (!moreTokens) return null; auto node = allocator.make!BreakStatement; switch (current.type) { case tok!"identifier": node.label = advance(); mixin(tokenCheck!";"); break; case tok!";": advance(); break; default: error("Identifier or semicolon expected following `break`"); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a BaseClass * * $(GRAMMAR $(RULEDEF baseClass): * $(RULE type2) * ;) */ BaseClass parseBaseClass() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!BaseClass; if (!moreTokens) return null; if (current.type.isProtection()) { warn("Use of base class protection is deprecated."); advance(); } if ((node.type2 = parseType2()) is null) { return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a BaseClassList * * $(GRAMMAR $(RULEDEF baseClassList): * $(RULE baseClass) ($(LITERAL ',') $(RULE baseClass))* * ;) */ BaseClassList parseBaseClassList() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseCommaSeparatedRule!(BaseClassList, BaseClass)(); } /** * Parses an BuiltinType * * $(GRAMMAR $(RULEDEF builtinType): * $(LITERAL 'bool') * | $(LITERAL 'byte') * | $(LITERAL 'ubyte') * | $(LITERAL 'short') * | $(LITERAL 'ushort') * | $(LITERAL 'int') * | $(LITERAL 'uint') * | $(LITERAL 'long') * | $(LITERAL 'ulong') * | $(LITERAL 'char') * | $(LITERAL 'wchar') * | $(LITERAL 'dchar') * | $(LITERAL 'float') * | $(LITERAL 'double') * | $(LITERAL 'real') * | $(LITERAL 'ifloat') * | $(LITERAL 'idouble') * | $(LITERAL 'ireal') * | $(LITERAL 'cfloat') * | $(LITERAL 'cdouble') * | $(LITERAL 'creal') * | $(LITERAL 'void') * ;) */ IdType parseBuiltinType() { mixin(traceEnterAndExit!(__FUNCTION__)); return advance().type; } /** * Parses a CaseRangeStatement * * $(GRAMMAR $(RULEDEF caseRangeStatement): * $(LITERAL 'case') $(RULE assignExpression) $(LITERAL ':') $(LITERAL '...') $(LITERAL 'case') $(RULE assignExpression) $(LITERAL ':') $(RULE declarationsAndStatements) * ;) */ CaseRangeStatement parseCaseRangeStatement(ExpressionNode low) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!CaseRangeStatement; assert (low !is null); node.low = low; mixin(tokenCheck!":"); mixin(tokenCheck!".."); expect(tok!"case"); mixin(parseNodeQ!(`node.high`, `AssignExpression`)); const colon = expect(tok!":"); if (colon is null) return null; node.colonLocation = colon.index; mixin(parseNodeQ!(`node.declarationsAndStatements`, `DeclarationsAndStatements`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an CaseStatement * * $(GRAMMAR $(RULEDEF caseStatement): * $(LITERAL 'case') $(RULE _argumentList) $(LITERAL ':') $(RULE declarationsAndStatements) * ;) */ CaseStatement parseCaseStatement(ArgumentList argumentList = null) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!CaseStatement; node.argumentList = argumentList; const colon = expect(tok!":"); if (colon is null) return null; node.colonLocation = colon.index; mixin (nullCheck!`node.declarationsAndStatements = parseDeclarationsAndStatements(false)`); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a CastExpression * * $(GRAMMAR $(RULEDEF castExpression): * $(LITERAL 'cast') $(LITERAL '$(LPAREN)') ($(RULE type) | $(RULE castQualifier))? $(LITERAL '$(RPAREN)') $(RULE unaryExpression) * ;) */ CastExpression parseCastExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!CastExpression; expect(tok!"cast"); mixin(tokenCheck!"("); if (!currentIs(tok!")")) { if (isCastQualifier()) mixin(parseNodeQ!(`node.castQualifier`, `CastQualifier`)); else mixin(parseNodeQ!(`node.type`, `Type`)); } mixin(tokenCheck!")"); mixin(parseNodeQ!(`node.unaryExpression`, `UnaryExpression`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a CastQualifier * * $(GRAMMAR $(RULEDEF castQualifier): * $(LITERAL 'const') * | $(LITERAL 'const') $(LITERAL 'shared') * | $(LITERAL 'immutable') * | $(LITERAL 'inout') * | $(LITERAL 'inout') $(LITERAL 'shared') * | $(LITERAL 'shared') * | $(LITERAL 'shared') $(LITERAL 'const') * | $(LITERAL 'shared') $(LITERAL 'inout') * ;) */ CastQualifier parseCastQualifier() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!CastQualifier; if (!moreTokens) return null; switch (current.type) { case tok!"inout": case tok!"const": node.first = advance(); if (currentIs(tok!"shared")) node.second = advance(); break; case tok!"shared": node.first = advance(); if (currentIsOneOf(tok!"const", tok!"inout")) node.second = advance(); break; case tok!"immutable": node.first = advance(); break; default: error("`const`, `immutable`, `inout`, or `shared` expected"); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Catch * * $(GRAMMAR $(RULEDEF catch): * $(LITERAL 'catch') $(LITERAL '$(LPAREN)') $(RULE type) $(LITERAL Identifier)? $(LITERAL '$(RPAREN)') $(RULE declarationOrStatement) * ;) */ Catch parseCatch() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Catch; expect(tok!"catch"); mixin(tokenCheck!"("); mixin(parseNodeQ!(`node.type`, `Type`)); if (currentIs(tok!"identifier")) node.identifier = advance(); mixin(tokenCheck!")"); mixin(parseNodeQ!(`node.declarationOrStatement`, `DeclarationOrStatement`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Catches * * $(GRAMMAR $(RULEDEF catches): * $(RULE catch)+ * | $(RULE catch)* $(RULE lastCatch) * ;) */ Catches parseCatches() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Catches; StackBuffer catches; while (moreTokens()) { if (!currentIs(tok!"catch")) break; if (peekIs(tok!"(")) { if (!catches.put(parseCatch())) return null; } else { mixin(parseNodeQ!(`node.lastCatch`, `LastCatch`)); break; } } ownArray(node.catches, catches); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a ClassDeclaration * * $(GRAMMAR $(RULEDEF classDeclaration): * $(LITERAL 'class') $(LITERAL Identifier) $(LITERAL ';') * | $(LITERAL 'class') $(LITERAL Identifier) ($(LITERAL ':') $(RULE baseClassList))? $(RULE structBody) * | $(LITERAL 'class') $(LITERAL Identifier) $(RULE templateParameters) $(RULE constraint)? ($(RULE structBody) | $(LITERAL ';')) * | $(LITERAL 'class') $(LITERAL Identifier) $(RULE templateParameters) $(RULE constraint)? ($(LITERAL ':') $(RULE baseClassList))? $(RULE structBody) * | $(LITERAL 'class') $(LITERAL Identifier) $(RULE templateParameters) ($(LITERAL ':') $(RULE baseClassList))? $(RULE constraint)? $(RULE structBody) * ;) */ ClassDeclaration parseClassDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ClassDeclaration; expect(tok!"class"); return parseInterfaceOrClass(node, startIndex); } /** * Parses a CmpExpression * * $(GRAMMAR $(RULEDEF cmpExpression): * $(RULE shiftExpression) * | $(RULE equalExpression) * | $(RULE identityExpression) * | $(RULE relExpression) * | $(RULE inExpression) * ;) */ ExpressionNode parseCmpExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto shift = parseShiftExpression(); if (shift is null) return null; if (!moreTokens()) return shift; switch (current.type) { case tok!"is": auto node = allocator.make!CmpExpression; mixin (nullCheck!`node.identityExpression = parseIdentityExpression(shift)`); node.tokens = tokens[startIndex .. index]; return node; case tok!"in": auto node = allocator.make!CmpExpression; mixin (nullCheck!`node.inExpression = parseInExpression(shift)`); node.tokens = tokens[startIndex .. index]; return node; case tok!"!": auto node = allocator.make!CmpExpression; if (peekIs(tok!"is")) mixin (nullCheck!`node.identityExpression = parseIdentityExpression(shift)`); else if (peekIs(tok!"in")) mixin (nullCheck!`node.inExpression = parseInExpression(shift)`); node.tokens = tokens[startIndex .. index]; return node; case tok!"<": case tok!"<=": case tok!">": case tok!">=": case tok!"!<>=": case tok!"!<>": case tok!"<>": case tok!"<>=": case tok!"!>": case tok!"!>=": case tok!"!<": case tok!"!<=": auto node = allocator.make!CmpExpression; mixin (nullCheck!`node.relExpression = parseRelExpression(shift)`); node.tokens = tokens[startIndex .. index]; return node; case tok!"==": case tok!"!=": auto node = allocator.make!CmpExpression; mixin (nullCheck!`node.equalExpression = parseEqualExpression(shift)`); node.tokens = tokens[startIndex .. index]; return node; default: return shift; } } /** * Parses a CompileCondition * * $(GRAMMAR $(RULEDEF compileCondition): * $(RULE versionCondition) * | $(RULE debugCondition) * | $(RULE staticIfCondition) * ;) */ CompileCondition parseCompileCondition() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!CompileCondition; if (!moreTokens) return null; switch (current.type) { case tok!"version": mixin(parseNodeQ!(`node.versionCondition`, `VersionCondition`)); break; case tok!"debug": mixin(parseNodeQ!(`node.debugCondition`, `DebugCondition`)); break; case tok!"static": mixin(parseNodeQ!(`node.staticIfCondition`, `StaticIfCondition`)); break; default: error("`version`, `debug`, or `static` expected"); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a ConditionalDeclaration * * $(GRAMMAR $(RULEDEF conditionalDeclaration): * $(RULE compileCondition) $(RULE declaration) * | $(RULE compileCondition) $(LITERAL '{') $(RULE declaration)* $(LITERAL '}') * | $(RULE compileCondition) $(LITERAL ':') $(RULE declaration)+ * | $(RULE compileCondition) $(RULE declaration) $(LITERAL 'else') $(LITERAL ':') $(RULE declaration)* * | $(RULE compileCondition) $(RULE declaration) $(LITERAL 'else') $(RULE declaration) * | $(RULE compileCondition) $(RULE declaration) $(LITERAL 'else') $(LITERAL '{') $(RULE declaration)* $(LITERAL '}') * | $(RULE compileCondition) $(LITERAL '{') $(RULE declaration)* $(LITERAL '}') $(LITERAL 'else') $(RULE declaration) * | $(RULE compileCondition) $(LITERAL '{') $(RULE declaration)* $(LITERAL '}') $(LITERAL 'else') $(LITERAL '{') $(RULE declaration)* $(LITERAL '}') * | $(RULE compileCondition) $(LITERAL '{') $(RULE declaration)* $(LITERAL '}') $(LITERAL 'else') $(LITERAL ':') $(RULE declaration)* * | $(RULE compileCondition) $(LITERAL ':') $(RULE declaration)+ $(LITERAL 'else') $(RULE declaration) * | $(RULE compileCondition) $(LITERAL ':') $(RULE declaration)+ $(LITERAL 'else') $(LITERAL '{') $(RULE declaration)* $(LITERAL '}') * | $(RULE compileCondition) $(LITERAL ':') $(RULE declaration)+ $(LITERAL 'else') $(LITERAL ':') $(RULE declaration)* * ;) */ ConditionalDeclaration parseConditionalDeclaration(bool strict, bool inTemplateDeclaration = false) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ConditionalDeclaration; mixin(parseNodeQ!(`node.compileCondition`, `CompileCondition`)); StackBuffer trueDeclarations; if (currentIs(tok!":") || currentIs(tok!"{")) { immutable bool brace = currentIs(tok!"{"); node.trueStyle = brace ? DeclarationListStyle.block : DeclarationListStyle.colon; advance(); while (moreTokens() && !currentIs(tok!"}") && !currentIs(tok!"else")) { immutable c = allocator.setCheckpoint(); if (!trueDeclarations.put(parseDeclaration(strict, true, inTemplateDeclaration))) { allocator.rollback(c); return null; } } if (brace) mixin(tokenCheck!"}"); } else { if (!trueDeclarations.put(parseDeclaration(strict, true, inTemplateDeclaration))) return null; node.trueStyle = DeclarationListStyle.single; } ownArray(node.trueDeclarations, trueDeclarations); if (currentIs(tok!"else")) { node.hasElse = true; advance(); } else { node.tokens = tokens[startIndex .. index]; return node; } StackBuffer falseDeclarations; if (currentIs(tok!":") || currentIs(tok!"{")) { immutable bool brace = currentIs(tok!"{"); node.falseStyle = brace ? DeclarationListStyle.block : DeclarationListStyle.colon; advance(); while (moreTokens() && !currentIs(tok!"}")) if (!falseDeclarations.put(parseDeclaration(strict, true, inTemplateDeclaration))) return null; if (brace) mixin(tokenCheck!"}"); } else { if (!falseDeclarations.put(parseDeclaration(strict, true, inTemplateDeclaration))) return null; node.falseStyle = DeclarationListStyle.single; } ownArray(node.falseDeclarations, falseDeclarations); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a ConditionalStatement * * $(GRAMMAR $(RULEDEF conditionalStatement): * $(RULE compileCondition) $(RULE declarationOrStatement) ($(LITERAL 'else') $(RULE declarationOrStatement))? * ;) */ ConditionalStatement parseConditionalStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ConditionalStatement; mixin(parseNodeQ!(`node.compileCondition`, `CompileCondition`)); mixin(parseNodeQ!(`node.trueStatement`, `DeclarationOrStatement`)); if (currentIs(tok!"else")) { advance(); mixin(parseNodeQ!(`node.falseStatement`, `DeclarationOrStatement`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Constraint * * $(GRAMMAR $(RULEDEF constraint): * $(LITERAL 'if') $(LITERAL '$(LPAREN)') $(RULE expression) $(LITERAL '$(RPAREN)') * ;) */ Constraint parseConstraint() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Constraint; auto ifToken = expect(tok!"if"); mixin (nullCheck!`ifToken`); node.location = ifToken.index; mixin(tokenCheck!"("); mixin(parseNodeQ!(`node.expression`, `Expression`)); mixin(tokenCheck!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Constructor * * $(GRAMMAR $(RULEDEF constructor): * $(LITERAL 'this') $(RULE templateParameters)? $(RULE parameters) $(RULE memberFunctionAttribute)* $(RULE constraint)? ($(RULE functionBody) | $(LITERAL ';')) * ;) */ Constructor parseConstructor() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; Constructor node = allocator.make!Constructor; node.comment = comment; comment = null; const t = expect(tok!"this"); mixin (nullCheck!`t`); node.location = t.index; node.line = t.line; node.column = t.column; const p = peekPastParens(); bool isTemplate = false; if (p !is null && p.type == tok!"(") { isTemplate = true; mixin(parseNodeQ!(`node.templateParameters`, `TemplateParameters`)); } mixin(parseNodeQ!(`node.parameters`, `Parameters`)); StackBuffer memberFunctionAttributes; while (moreTokens() && currentIsMemberFunctionAttribute()) if (!memberFunctionAttributes.put(parseMemberFunctionAttribute())) return null; ownArray(node.memberFunctionAttributes, memberFunctionAttributes); if (isTemplate && currentIs(tok!"if")) mixin(parseNodeQ!(`node.constraint`, `Constraint`)); if (currentIs(tok!";")) advance(); else mixin(parseNodeQ!(`node.functionBody`, `FunctionBody`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an ContinueStatement * * $(GRAMMAR $(RULEDEF continueStatement): * $(LITERAL 'continue') $(LITERAL Identifier)? $(LITERAL ';') * ;) */ ContinueStatement parseContinueStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; mixin(tokenCheck!"continue"); if (!moreTokens) return null; auto node = allocator.make!ContinueStatement; switch (current.type) { case tok!"identifier": node.label = advance(); mixin(tokenCheck!";"); break; case tok!";": advance(); break; default: error("Identifier or semicolon expected following `continue`"); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a DebugCondition * * $(GRAMMAR $(RULEDEF debugCondition): * $(LITERAL 'debug') ($(LITERAL '$(LPAREN)') ($(LITERAL IntegerLiteral) | $(LITERAL Identifier)) $(LITERAL '$(RPAREN)'))? * ;) */ DebugCondition parseDebugCondition() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!DebugCondition; const d = expect(tok!"debug"); mixin (nullCheck!`d`); node.debugIndex = d.index; if (currentIs(tok!"(")) { advance(); if (currentIsOneOf(tok!"intLiteral", tok!"identifier")) node.identifierOrInteger = advance(); else { error(`Integer literal or identifier expected`); return null; } mixin(tokenCheck!")"); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a DebugSpecification * * $(GRAMMAR $(RULEDEF debugSpecification): * $(LITERAL 'debug') $(LITERAL '=') ($(LITERAL Identifier) | $(LITERAL IntegerLiteral)) $(LITERAL ';') * ;) */ DebugSpecification parseDebugSpecification() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!DebugSpecification; mixin(tokenCheck!"debug"); mixin(tokenCheck!"="); if (currentIsOneOf(tok!"identifier", tok!"intLiteral")) node.identifierOrInteger = advance(); else { error("Integer literal or identifier expected"); return null; } mixin(tokenCheck!";"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Declaration * * Params: * strict = if true, do not return partial AST nodes on errors. * mustBeDeclaration = do not parse as a declaration if it could be parsed as a function call * inTemplateDeclaration = if this function is called from a templated context * * $(GRAMMAR $(RULEDEF declaration): * $(RULE attribute)* $(RULE declaration2) * | $(RULE attribute)+ $(LITERAL '{') $(RULE declaration)* $(LITERAL '}') * ; * $(RULEDEF declaration2): * $(RULE aliasDeclaration) * | $(RULR aliasAssign) * | $(RULE aliasThisDeclaration) * | $(RULE anonymousEnumDeclaration) * | $(RULE attributeDeclaration) * | $(RULE classDeclaration) * | $(RULE conditionalDeclaration) * | $(RULE constructor) * | $(RULE debugSpecification) * | $(RULE destructor) * | $(RULE enumDeclaration) * | $(RULE eponymousTemplateDeclaration) * | $(RULE functionDeclaration) * | $(RULE importDeclaration) * | $(RULE interfaceDeclaration) * | $(RULE invariant) * | $(RULE mixinDeclaration) * | $(RULE mixinTemplateDeclaration) * | $(RULE pragmaDeclaration) * | $(RULE sharedStaticConstructor) * | $(RULE sharedStaticDestructor) * | $(RULE staticAssertDeclaration) * | $(RULE staticConstructor) * | $(RULE staticDestructor) * | $(RULE structDeclaration) * | $(RULE templateDeclaration) * | $(RULE unionDeclaration) * | $(RULE unittest) * | $(RULE variableDeclaration) * | $(RULE versionSpecification) * ;) */ Declaration parseDeclaration(bool strict = false, bool mustBeDeclaration = false, bool inTemplateDeclaration = false) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Declaration; if (!moreTokens) { error("declaration expected instead of EOF"); return null; } if (current.comment !is null) comment = current.comment; size_t autoStorageClassStart = size_t.max; DecType isAuto; StackBuffer attributes; do { isAuto = isAutoDeclaration(autoStorageClassStart); if (isAuto != DecType.other && index == autoStorageClassStart) break; if (!isAttribute()) break; immutable c = allocator.setCheckpoint(); auto attr = parseAttribute(); if (attr is null) { allocator.rollback(c); break; } if (currentIs(tok!":")) { node.attributeDeclaration = parseAttributeDeclaration(attr); mixin(nullCheck!`node.attributeDeclaration`); ownArray(node.attributes, attributes); node.tokens = tokens[startIndex .. index]; return node; } else attributes.put(attr); } while (moreTokens()); ownArray(node.attributes, attributes); if (!moreTokens) { error("declaration expected instead of EOF"); return null; } if (!currentIs(tok!"enum")) // #165: handle enums separatly b/c of EponymousTemplateDeclaration { if (isAuto == DecType.autoVar) { mixin(nullCheck!`node.variableDeclaration = parseVariableDeclaration(null, true)`); node.tokens = tokens[startIndex .. index]; return node; } else if (isAuto == DecType.autoFun) { mixin(nullCheck!`node.functionDeclaration = parseFunctionDeclaration(null, true)`); node.tokens = tokens[startIndex .. index]; return node; } } switch (current.type) { case tok!"asm": case tok!"break": case tok!"case": case tok!"continue": case tok!"default": case tok!"do": case tok!"for": case tok!"foreach": case tok!"foreach_reverse": case tok!"goto": case tok!"if": case tok!"return": case tok!"switch": case tok!"throw": case tok!"try": case tok!"while": case tok!"assert": goto default; case tok!";": // http://d.puremagic.com/issues/show_bug.cgi?id=4559 warn("Empty declaration"); advance(); break; case tok!"{": if (node.attributes.empty) { error("declaration expected instead of `{`"); return null; } advance(); StackBuffer declarations; while (moreTokens() && !currentIs(tok!"}")) { auto c = allocator.setCheckpoint(); if (!declarations.put(parseDeclaration(strict, false, inTemplateDeclaration))) { allocator.rollback(c); return null; } } ownArray(node.declarations, declarations); mixin(tokenCheck!"}"); break; case tok!"alias": if (startsWith(tok!"alias", tok!"identifier", tok!"this")) mixin(parseNodeQ!(`node.aliasThisDeclaration`, `AliasThisDeclaration`)); else mixin(parseNodeQ!(`node.aliasDeclaration`, `AliasDeclaration`)); break; case tok!"class": mixin(parseNodeQ!(`node.classDeclaration`, `ClassDeclaration`)); break; case tok!"this": if (!mustBeDeclaration && peekIs(tok!"(")) { // Do not parse as a declaration if we could parse as a function call. ++index; const past = peekPastParens(); --index; if (past !is null && past.type == tok!";") return null; } if (startsWith(tok!"this", tok!"(", tok!"this", tok!")")) mixin(parseNodeQ!(`node.postblit`, `Postblit`)); else mixin(parseNodeQ!(`node.constructor`, `Constructor`)); break; case tok!"~": mixin(parseNodeQ!(`node.destructor`, `Destructor`)); break; case tok!"enum": immutable b = setBookmark(); advance(); // enum if (currentIsOneOf(tok!":", tok!"{")) { goToBookmark(b); mixin(parseNodeQ!(`node.anonymousEnumDeclaration`, `AnonymousEnumDeclaration`)); } else if (currentIs(tok!"identifier")) { advance(); if (currentIs(tok!"(")) { skipParens(); // () if (currentIs(tok!"(")) skipParens(); if (!currentIs(tok!"=")) { goToBookmark(b); node.functionDeclaration = parseFunctionDeclaration(null, true, node.attributes); mixin (nullCheck!`node.functionDeclaration`); } else { goToBookmark(b); mixin(parseNodeQ!(`node.eponymousTemplateDeclaration`, `EponymousTemplateDeclaration`)); } } else if (currentIsOneOf(tok!":", tok!"{", tok!";")) { goToBookmark(b); mixin(parseNodeQ!(`node.enumDeclaration`, `EnumDeclaration`)); } else { immutable bool eq = currentIs(tok!"="); goToBookmark(b); mixin (nullCheck!`node.variableDeclaration = parseVariableDeclaration(null, eq)`); } } else { immutable bool s = isStorageClass(); goToBookmark(b); mixin (nullCheck!`node.variableDeclaration = parseVariableDeclaration(null, s)`); } break; case tok!"import": mixin(parseNodeQ!(`node.importDeclaration`, `ImportDeclaration`)); break; case tok!"interface": mixin(parseNodeQ!(`node.interfaceDeclaration`, `InterfaceDeclaration`)); break; case tok!"mixin": if (peekIs(tok!"template")) mixin(parseNodeQ!(`node.mixinTemplateDeclaration`, `MixinTemplateDeclaration`)); else { immutable b = setBookmark(); advance(); if (currentIs(tok!"(")) { const t = peekPastParens(); if (t !is null && t.type == tok!";") { goToBookmark(b); mixin(parseNodeQ!(`node.mixinDeclaration`, `MixinDeclaration`)); } else { goToBookmark(b); error("Declaration expected"); return null; } } else { goToBookmark(b); mixin(parseNodeQ!(`node.mixinDeclaration`, `MixinDeclaration`)); } } break; case tok!"pragma": mixin(parseNodeQ!(`node.pragmaDeclaration`, `PragmaDeclaration`)); break; case tok!"shared": if (startsWith(tok!"shared", tok!"static", tok!"this")) mixin(parseNodeQ!(`node.sharedStaticConstructor`, `SharedStaticConstructor`)); else if (startsWith(tok!"shared", tok!"static", tok!"~")) mixin(parseNodeQ!(`node.sharedStaticDestructor`, `SharedStaticDestructor`)); else goto type; break; case tok!"static": if (peekIs(tok!"this")) mixin(parseNodeQ!(`node.staticConstructor`, `StaticConstructor`)); else if (peekIs(tok!"~")) mixin(parseNodeQ!(`node.staticDestructor`, `StaticDestructor`)); else if (peekIs(tok!"if")) mixin (nullCheck!`node.conditionalDeclaration = parseConditionalDeclaration(strict, inTemplateDeclaration)`); else if (peekIs(tok!"assert")) mixin(parseNodeQ!(`node.staticAssertDeclaration`, `StaticAssertDeclaration`)); else if (peekIs(tok!"foreach") || peekIs(tok!"foreach_reverse")) mixin(nullCheck!(`node.staticForeachDeclaration = parseStaticForeachDeclaration(inTemplateDeclaration)`)); else goto type; break; case tok!"struct": mixin(parseNodeQ!(`node.structDeclaration`, `StructDeclaration`)); break; case tok!"template": mixin(parseNodeQ!(`node.templateDeclaration`, `TemplateDeclaration`)); break; case tok!"union": mixin(parseNodeQ!(`node.unionDeclaration`, `UnionDeclaration`)); break; case tok!"invariant": mixin(parseNodeQ!(`node.invariant_`, `Invariant`)); break; case tok!"unittest": mixin(parseNodeQ!(`node.unittest_`, `Unittest`)); break; case tok!"identifier": if (inTemplateDeclaration && peekIs(tok!"=")) { mixin(parseNodeQ!(`node.aliasAssign`, `AliasAssign`)); break; } else goto type; case tok!".": case tok!"const": case tok!"immutable": case tok!"inout": case tok!"scope": case tok!"typeof": case tok!"__vector": case tok!"__traits": foreach (B; BasicTypes) { case B: } type: Type t = parseType(); if (t is null || !currentIs(tok!"identifier")) { if (t) error("no identifier for declarator"); return null; } const b2 = setBookmark(); auto savedComment = comment; node.variableDeclaration = parseVariableDeclaration(t, false); if (node.variableDeclaration is null) { goToBookmark(b2); if (savedComment && comment is null) comment = savedComment; node.functionDeclaration = parseFunctionDeclaration(t, false); } else abandonBookmark(b2); if (!node.variableDeclaration && !node.functionDeclaration) { error("invalid variable declaration or function declaration", false); return null; } break; case tok!"version": if (peekIs(tok!"(")) mixin (nullCheck!`node.conditionalDeclaration = parseConditionalDeclaration(strict, inTemplateDeclaration)`); else if (peekIs(tok!"=")) mixin(parseNodeQ!(`node.versionSpecification`, `VersionSpecification`)); else { error("`=` or `(` expected following `version`"); return null; } break; case tok!"debug": if (peekIs(tok!"=")) mixin(parseNodeQ!(`node.debugSpecification`, `DebugSpecification`)); else mixin (nullCheck!`node.conditionalDeclaration = parseConditionalDeclaration(strict, inTemplateDeclaration)`); break; default: error("Declaration expected"); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses DeclarationsAndStatements * * $(GRAMMAR $(RULEDEF declarationsAndStatements): * $(RULE declarationOrStatement)+ * ;) */ DeclarationsAndStatements parseDeclarationsAndStatements(bool includeCases = true) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!DeclarationsAndStatements; StackBuffer declarationsAndStatements; while (!currentIsOneOf(tok!"}", tok!"else") && moreTokens() && suppressedErrorCount <= MAX_ERRORS) { if (currentIs(tok!"case") && !includeCases) break; if (currentIs(tok!"while")) { immutable b = setBookmark(); scope (exit) goToBookmark(b); advance(); if (currentIs(tok!"(")) { const p = peekPastParens(); if (p !is null && *p == tok!";") break; } } immutable c = allocator.setCheckpoint(); if (!declarationsAndStatements.put(parseDeclarationOrStatement())) { allocator.rollback(c); // detect the pattern ".}" for DCD. This is what happens when // located at the end of a well delimited body/scope and requesting // completion. This is also a case where it's sure sure that // there's no ambiguity, even if it happens during a lookup: // it's not a decl, it's not a statement, it's an error. if (currentIs(tok!"}") && index > 0 && previous == tok!".") break; if (!suppressMessages.empty) return null; // better for DCD, if the end of the block is reached then // go back, allowing the following declarations to be in // the right scope, instead of the block we were in. if (index > 0 && previous == tok!"}") { index -= 1; break; } } } ownArray(node.declarationsAndStatements, declarationsAndStatements); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a DeclarationOrStatement * * $(GRAMMAR $(RULEDEF declarationOrStatement): * $(RULE declaration) * | $(RULE statement) * ;) */ DeclarationOrStatement parseDeclarationOrStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!DeclarationOrStatement; if (moreTokens) node.startLocation = current.index; // "Any ambiguities in the grammar between Statements and // Declarations are resolved by the declarations taking precedence." immutable b = setBookmark(); immutable c = allocator.setCheckpoint(); auto d = parseDeclaration(true, false); if (d is null) { allocator.rollback(c); goToBookmark(b); mixin(parseNodeQ!(`node.statement`, `Statement`)); } else { // TODO: Make this more efficient. Right now we parse the declaration // twice, once with errors and warnings ignored, and once with them // printed. Maybe store messages to then be abandoned or written later? allocator.rollback(c); goToBookmark(b); node.declaration = parseDeclaration(true, true); } if (moreTokens) node.endLocation = current.index; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Declarator * * $(GRAMMAR $(RULEDEF declarator): * $(LITERAL Identifier) * | $(LITERAL Identifier) $(LITERAL '=') $(RULE initializer) * | $(LITERAL Identifier) $(RULE templateParameters) $(LITERAL '=') $(RULE initializer) * ;) */ Declarator parseDeclarator() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; Declarator node = allocator.make!Declarator; const id = expect(tok!"identifier"); mixin (nullCheck!`id`); node.name = *id; if (currentIs(tok!"[")) // dmd doesn't accept pointer after identifier { warn("C-style array declaration."); StackBuffer typeSuffixes; while (moreTokens() && currentIs(tok!"[")) if (!typeSuffixes.put(parseTypeSuffix())) return null; ownArray(node.cstyle, typeSuffixes); } if (currentIs(tok!"(")) { mixin (nullCheck!`(node.templateParameters = parseTemplateParameters())`); mixin(tokenCheck!"="); mixin (nullCheck!`(node.initializer = parseInitializer())`); } else if (currentIs(tok!"=")) { advance(); mixin(parseNodeQ!(`node.initializer`, `Initializer`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a DeclaratorIdentifierList * * $(GRAMMAR $(RULEDEF declaratorIdentifierList): * $(LITERAL Identifier) ($(LITERAL ',') $(LITERAL Identifier))* * ;) */ DeclaratorIdentifierList parseDeclaratorIdentifierList() { auto node = allocator.make!DeclaratorIdentifierList; auto startIndex = index; StackBuffer identifiers; while (moreTokens()) { const ident = expect(tok!"identifier"); mixin(nullCheck!`ident`); identifiers.put(*ident); if (currentIs(tok!",")) { advance(); continue; } else break; } ownArray(node.identifiers, identifiers); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a DefaultStatement * * $(GRAMMAR $(RULEDEF defaultStatement): * $(LITERAL 'default') $(LITERAL ':') $(RULE declarationsAndStatements) * ;) */ DefaultStatement parseDefaultStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!DefaultStatement; mixin(tokenCheck!"default"); const colon = expect(tok!":"); if (colon is null) return null; node.colonLocation = colon.index; mixin(parseNodeQ!(`node.declarationsAndStatements`, `DeclarationsAndStatements`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a DeleteExpression * * $(GRAMMAR $(RULEDEF deleteExpression): * $(LITERAL 'delete') $(RULE unaryExpression) * ;) */ DeleteExpression parseDeleteExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!DeleteExpression; node.line = current.line; node.column = current.column; mixin(tokenCheck!"delete"); mixin(parseNodeQ!(`node.unaryExpression`, `UnaryExpression`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Deprecated attribute * * $(GRAMMAR $(RULEDEF deprecated): * $(LITERAL 'deprecated') ($(LITERAL '$(LPAREN)') $(LITERAL StringLiteral)+ $(LITERAL '$(RPAREN)'))? * ;) */ Deprecated parseDeprecated() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Deprecated; mixin(tokenCheck!"deprecated"); if (currentIs(tok!"(")) { advance(); mixin (parseNodeQ!(`node.assignExpression`, `AssignExpression`)); mixin (tokenCheck!")"); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a Destructor * * $(GRAMMAR $(RULEDEF destructor): * $(LITERAL '~') $(LITERAL 'this') $(LITERAL '$(LPAREN)') $(LITERAL '$(RPAREN)') $(RULE memberFunctionAttribute)* ($(RULE functionBody) | $(LITERAL ';')) * ;) */ Destructor parseDestructor() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Destructor; node.comment = comment; comment = null; mixin(tokenCheck!"~"); if (!moreTokens) { error("`this` expected"); return null; } node.index = current.index; node.line = current.line; node.column = current.column; mixin(tokenCheck!"this"); mixin(tokenCheck!"("); mixin(tokenCheck!")"); if (currentIs(tok!";")) advance(); else { StackBuffer memberFunctionAttributes; while (moreTokens() && currentIsMemberFunctionAttribute()) if (!memberFunctionAttributes.put(parseMemberFunctionAttribute())) return null; ownArray(node.memberFunctionAttributes, memberFunctionAttributes); mixin(parseNodeQ!(`node.functionBody`, `FunctionBody`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a DoStatement * * $(GRAMMAR $(RULEDEF doStatement): * $(LITERAL 'do') $(RULE statementNoCaseNoDefault) $(LITERAL 'while') $(LITERAL '$(LPAREN)') $(RULE expression) $(LITERAL '$(RPAREN)') $(LITERAL ';') * ;) */ DoStatement parseDoStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; mixin(tokenCheck!"do"); if (!moreTokens) return null; auto node = allocator.make!DoStatement; mixin(parseNodeQ!(`node.statementNoCaseNoDefault`, `StatementNoCaseNoDefault`)); mixin(tokenCheck!"while"); mixin(tokenCheck!"("); mixin(parseNodeQ!(`node.expression`, `Expression`)); mixin(tokenCheck!")"); mixin(tokenCheck!";"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an EnumBody * * $(GRAMMAR $(RULEDEF enumBody): * $(LITERAL '{') $(RULE enumMember) ($(LITERAL ',') $(RULE enumMember)?)* $(LITERAL '}') * ;) */ EnumBody parseEnumBody() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; EnumBody node = allocator.make!EnumBody; const open = expect(tok!"{"); mixin (nullCheck!`open`); node.startLocation = open.index; StackBuffer enumMembers; EnumMember last; while (moreTokens()) { if (currentIsOneOf(tok!"identifier", tok!"@", tok!"deprecated")) { auto c = allocator.setCheckpoint(); auto e = parseEnumMember(); if (!enumMembers.put(e)) allocator.rollback(c); else last = e; if (currentIs(tok!",")) { if (last !is null && last.comment is null) last.comment = current.trailingComment; advance(); if (!currentIs(tok!"}")) continue; } if (currentIs(tok!"}")) { if (last !is null && last.comment is null) last.comment = tokens[index - 1].trailingComment; break; } else { error("`,` or `}` expected"); if (currentIs(tok!"}")) break; } } else error("Enum member expected"); } ownArray(node.enumMembers, enumMembers); const close = expect (tok!"}"); if (close !is null) node.endLocation = close.index; node.tokens = tokens[startIndex .. index]; return node; } /** * $(GRAMMAR $(RULEDEF anonymousEnumMember): * $(RULE type) $(LITERAL identifier) $(LITERAL '=') $(RULE assignExpression) * | $(LITERAL identifier) $(LITERAL '=') $(RULE assignExpression) * | $(LITERAL identifier) * ;) */ AnonymousEnumMember parseAnonymousEnumMember(bool typeAllowed) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AnonymousEnumMember; if (currentIs(tok!"identifier") && peekIsOneOf(tok!",", tok!"=", tok!"}")) { node.comment = current.comment; mixin(tokenCheck!(`node.name`, `identifier`)); if (currentIs(tok!"=")) { advance(); // = goto assign; } } else if (typeAllowed) { node.comment = current.comment; mixin(parseNodeQ!(`node.type`, `Type`)); mixin(tokenCheck!(`node.name`, `identifier`)); mixin(tokenCheck!"="); assign: mixin(parseNodeQ!(`node.assignExpression`, `AssignExpression`)); } else { error("Cannot specify anonymous enum member type if anonymous enum has a base type."); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * $(GRAMMAR $(RULEDEF anonymousEnumDeclaration): * $(LITERAL 'enum') ($(LITERAL ':') $(RULE type))? $(LITERAL '{') $(RULE anonymousEnumMember)+ $(LITERAL '}') * ;) */ AnonymousEnumDeclaration parseAnonymousEnumDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!AnonymousEnumDeclaration; mixin(tokenCheck!"enum"); immutable bool hasBaseType = currentIs(tok!":"); if (hasBaseType) { advance(); mixin(parseNodeQ!(`node.baseType`, `Type`)); } mixin(tokenCheck!"{"); StackBuffer members; AnonymousEnumMember last; while (moreTokens()) { if (currentIs(tok!",")) { if (last !is null && last.comment is null) last.comment = current.trailingComment; advance(); continue; } else if (currentIs(tok!"}")) { if (last !is null && last.comment is null) last.comment = tokens[index - 1].trailingComment; break; } else { immutable c = allocator.setCheckpoint(); auto e = parseAnonymousEnumMember(!hasBaseType); if (!members.put(e)) allocator.rollback(c); else last = e; } } ownArray(node.members, members); mixin(tokenCheck!"}"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an EnumDeclaration * * $(GRAMMAR $(RULEDEF enumDeclaration): * $(LITERAL 'enum') $(LITERAL Identifier) ($(LITERAL ':') $(RULE type))? $(LITERAL ';') * | $(LITERAL 'enum') $(LITERAL Identifier) ($(LITERAL ':') $(RULE type))? $(RULE enumBody) * ;) */ EnumDeclaration parseEnumDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!EnumDeclaration; mixin(tokenCheck!"enum"); mixin (tokenCheck!(`node.name`, `identifier`)); node.comment = comment; comment = null; if (currentIs(tok!":")) { advance(); // skip ':' mixin(parseNodeQ!(`node.type`, `Type`)); } if (currentIs(tok!";")) { advance(); node.tokens = tokens[startIndex .. index]; return node; } mixin(parseNodeQ!(`node.enumBody`, `EnumBody`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an EnumMemberAttribute * * $(GRAMMAR $(RULEDEF enumMemberAttribute): * $(RULE atAttribute) * | $(RULE deprecated) * ;) */ EnumMemberAttribute parseEnumMemberAttribute() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; EnumMemberAttribute node; if (currentIs(tok!"@")) { node = allocator.make!EnumMemberAttribute; mixin(parseNodeQ!(`node.atAttribute`, `AtAttribute`)); } else if (currentIs(tok!"deprecated")) { node = allocator.make!EnumMemberAttribute; mixin(parseNodeQ!(`node.deprecated_`, `Deprecated`)); } if (node) node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an EnumMember * * $(GRAMMAR $(RULEDEF enumMember): * ($(RULE enumMemberAttribute))* $(LITERAL Identifier) ($(LITERAL '=') $(RULE assignExpression))? * ;) */ EnumMember parseEnumMember() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; EnumMember node = allocator.make!EnumMember; node.comment = current.comment; StackBuffer emas; while (moreTokens()) { if (!emas.put(parseEnumMemberAttribute())) break; } ownArray(node.enumMemberAttributes, emas); mixin (tokenCheck!(`node.name`, `identifier`)); if (currentIs(tok!"=")) { advance(); mixin(parseNodeQ!(`node.assignExpression`, `AssignExpression`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an EponymousTemplateDeclaration * * $(GRAMMAR $(RULEDEF eponymousTemplateDeclaration): * $(LITERAL 'enum') $(LITERAL Identifier) $(RULE templateParameters) $(LITERAL '=') $(RULE assignExpression) $(LITERAL ';') * ;) */ EponymousTemplateDeclaration parseEponymousTemplateDeclaration() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!EponymousTemplateDeclaration; node.comment = current.comment; advance(); // enum const ident = expect(tok!"identifier"); mixin (nullCheck!`ident`); node.name = *ident; mixin(parseNodeQ!(`node.templateParameters`, `TemplateParameters`)); expect(tok!"="); node.assignExpression = parseAssignExpression(); if (node.assignExpression is null) mixin(parseNodeQ!(`node.type`, `Type`)); expect(tok!";"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an EqualExpression * * $(GRAMMAR $(RULEDEF equalExpression): * $(RULE shiftExpression) ($(LITERAL '==') | $(LITERAL '!=')) $(RULE shiftExpression) * ;) */ EqualExpression parseEqualExpression(ExpressionNode shift = null) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!EqualExpression; node.left = shift is null ? parseShiftExpression() : shift; mixin (nullCheck!`node.left`); if (currentIsOneOf(tok!"==", tok!"!=")) node.operator = advance().type; mixin(parseNodeQ!(`node.right`, `ShiftExpression`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an Expression * * $(GRAMMAR $(RULEDEF expression): * $(RULE assignExpression) ($(LITERAL ',') $(RULE assignExpression))* * ;) */ Expression parseExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); if (suppressedErrorCount > MAX_ERRORS) return null; if (!moreTokens()) { error("Expected expression instead of EOF"); return null; } return parseCommaSeparatedRule!(Expression, AssignExpression, true)(); } /** * Parses an ExpressionStatement * * $(GRAMMAR $(RULEDEF expressionStatement): * $(RULE _expression) $(LITERAL ';') * ;) */ ExpressionStatement parseExpressionStatement(Expression expression = null) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ExpressionStatement; node.expression = expression is null ? parseExpression() : expression; if (node.expression is null || expect(tok!";") is null) return null; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a FinalSwitchStatement * * $(GRAMMAR $(RULEDEF finalSwitchStatement): * $(LITERAL 'final') $(RULE switchStatement) * ;) */ FinalSwitchStatement parseFinalSwitchStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); mixin (simpleParse!(FinalSwitchStatement, tok!"final", "switchStatement|parseSwitchStatement")); } /** * Parses a Finally * * $(GRAMMAR $(RULEDEF finally): * $(LITERAL 'finally') $(RULE declarationOrStatement) * ;) */ Finally parseFinally() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Finally; mixin(tokenCheck!"finally"); mixin(parseNodeQ!(`node.declarationOrStatement`, `DeclarationOrStatement`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a ForStatement * * $(GRAMMAR $(RULEDEF forStatement): * $(LITERAL 'for') $(LITERAL '$(LPAREN)') ($(RULE declaration) | $(RULE statementNoCaseNoDefault)) $(RULE expression)? $(LITERAL ';') $(RULE expression)? $(LITERAL '$(RPAREN)') $(RULE declarationOrStatement) * ;) */ ForStatement parseForStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ForStatement; mixin(tokenCheck!"for"); if (moreTokens) node.startIndex = current().index; mixin(tokenCheck!"("); if (currentIs(tok!";")) advance(); else mixin(parseNodeQ!(`node.initialization`, `DeclarationOrStatement`)); if (currentIs(tok!";")) advance(); else { mixin(parseNodeQ!(`node.test`, `Expression`)); expect(tok!";"); } if (!currentIs(tok!")")) mixin(parseNodeQ!(`node.increment`, `Expression`)); mixin(tokenCheck!")"); // Intentionally return an incomplete parse tree so that DCD will work // more correctly. if (currentIs(tok!"}")) { error("Statement expected", false); node.tokens = tokens[startIndex .. index]; return node; } mixin(parseNodeQ!(`node.declarationOrStatement`, `DeclarationOrStatement`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a StaticForeachDeclaration * * $(GRAMMAR $(RULEDEF staticForeachDeclaration): * $(LITERAL 'static') ($(LITERAL 'foreach') | $(LITERAL 'foreach_reverse')) $(LITERAL '$(LPAREN)') $(RULE foreachTypeList) $(LITERAL ';') $(RULE expression) $(LITERAL '$(RPAREN)') ($(RULE declaration) | $(LITERAL '{') $(RULE declaration)* $(LITERAL '}')) * | $(LITERAL 'static') ($(LITERAL 'foreach') | $(LITERAL 'foreach_reverse')) $(LITERAL '$(LPAREN)') $(RULE foreachType) $(LITERAL ';') $(RULE expression) $(LITERAL '..') $(RULE expression) $(LITERAL '$(RPAREN)') ($(RULE declaration) | $(LITERAL '{') $(RULE declaration)* $(LITERAL '}')) * ;) */ StaticForeachDeclaration parseStaticForeachDeclaration(bool inTemplateDeclaration = false) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; mixin(tokenCheck!"static"); auto decl = parseForeach!true(inTemplateDeclaration); if (decl) decl.tokens = tokens[startIndex .. index]; return decl; } /** * Parses a StaticForeachStatement * * $(GRAMMAR $(RULEDEF staticForeachStatement): * $(LITERAL 'static') $(RULE foreachStatement) * ;) */ StaticForeachStatement parseStaticForeachStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); mixin(simpleParse!(StaticForeachStatement, tok!"static", "foreachStatement|parseForeachStatement")); } /** * Parses a ForeachStatement * * $(GRAMMAR $(RULEDEF foreachStatement): * ($(LITERAL 'foreach') | $(LITERAL 'foreach_reverse')) $(LITERAL '$(LPAREN)') $(RULE foreachTypeList) $(LITERAL ';') $(RULE expression) $(LITERAL '$(RPAREN)') $(RULE declarationOrStatement) * | ($(LITERAL 'foreach') | $(LITERAL 'foreach_reverse')) $(LITERAL '$(LPAREN)') $(RULE foreachType) $(LITERAL ';') $(RULE expression) $(LITERAL '..') $(RULE expression) $(LITERAL '$(RPAREN)') $(RULE declarationOrStatement) * ;) */ ForeachStatement parseForeachStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseForeach!false(); } Foreach!declOnly parseForeach(bool declOnly = false)(bool inTemplateDeclaration = false) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; Foreach!declOnly node = allocator.make!(Foreach!declOnly); if (currentIsOneOf(tok!"foreach", tok!"foreach_reverse")) node.type = advance().type; else { error("`foreach` or `foreach_reverse` expected"); return null; } if (moreTokens) node.startIndex = current().index; mixin(tokenCheck!"("); ForeachTypeList feType = parseForeachTypeList(); mixin (nullCheck!`feType`); immutable bool canBeRange = feType.items.length == 1; mixin(tokenCheck!";"); mixin(parseNodeQ!(`node.low`, `Expression`)); mixin (nullCheck!`node.low`); if (currentIs(tok!"..")) { if (!canBeRange) { error(`Cannot have more than one foreach variable for a foreach range statement`); return null; } advance(); mixin(parseNodeQ!(`node.high`, `Expression`)); node.foreachType = feType.items[0]; mixin (nullCheck!`node.high`); } else { node.foreachTypeList = feType; } mixin(tokenCheck!")"); if (currentIs(tok!"}")) { error("Statement expected", false); node.tokens = tokens[startIndex .. index]; return node; // this line makes DCD better } static if (declOnly) { node.style = currentIs(tok!"{") ? DeclarationListStyle.block : DeclarationListStyle.single; StackBuffer declarations; if (currentIs(tok!"{")) { advance(); while (moreTokens() && !currentIs(tok!"}")) { immutable b = setBookmark(); immutable c = allocator.setCheckpoint(); if (declarations.put(parseDeclaration(true, true, inTemplateDeclaration))) abandonBookmark(b); else { goToBookmark(b); allocator.rollback(c); return null; } } mixin(tokenCheck!"}"); } else if (!declarations.put(parseDeclaration(true, true, inTemplateDeclaration))) return null; ownArray(node.declarations, declarations); } else mixin(parseNodeQ!(`node.declarationOrStatement`, `DeclarationOrStatement`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a ForeachType * * $(GRAMMAR $(RULEDEF foreachType): * ($(LITERAL 'ref') | $(LITERAL 'alias') | $(LITERAL 'enum') | $(RULE typeConstructor))* $(RULE type)? $(LITERAL Identifier) * ;) */ ForeachType parseForeachType() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!ForeachType; while (moreTokens()) { IdType typeConstructor; if (currentIs(tok!"ref")) { node.isRef = true; advance(); } else if (currentIs(tok!"alias")) { node.isAlias = true; advance(); } else if (currentIs(tok!"enum")) { node.isEnum = true; advance(); } else if (tok!"" != (typeConstructor = parseTypeConstructor(false))) { trace("\033[01;36mType constructor"); node.typeConstructors ~= typeConstructor; } else break; } if (currentIs(tok!"identifier") && peekIsOneOf(tok!",", tok!";")) { node.identifier = advance(); node.tokens = tokens[startIndex .. index]; return node; } mixin(parseNodeQ!(`node.type`, `Type`)); const ident = expect(tok!"identifier"); mixin(nullCheck!`ident`); node.identifier = *ident; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a ForeachTypeList * * $(GRAMMAR $(RULEDEF foreachTypeList): * $(RULE foreachType) ($(LITERAL ',') $(RULE foreachType))* * ;) */ ForeachTypeList parseForeachTypeList() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseCommaSeparatedRule!(ForeachTypeList, ForeachType)(); } /** * Parses a FunctionAttribute * * $(GRAMMAR $(RULEDEF functionAttribute): * $(RULE atAttribute) * | $(LITERAL 'pure') * | $(LITERAL 'nothrow') * ;) */ FunctionAttribute parseFunctionAttribute(bool validate = true) { auto startIndex = index; auto node = allocator.make!FunctionAttribute; switch (current.type) { case tok!"@": mixin(parseNodeQ!(`node.atAttribute`, `AtAttribute`)); break; case tok!"pure": case tok!"nothrow": node.token = advance(); break; default: if (validate) error("`@`attribute, `pure`, or `nothrow` expected"); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a FunctionBody. * Note that any change of this function must also be applied in dsymbol SimpleParser, which can be found * $(LINK2 https://github.com/dlang-community/dsymbol/blob/master/src/dsymbol/conversion/package.d, here). * * $(GRAMMAR $(RULEDEF functionBody): * $(RULE specifiedFunctionBody) * | $(RULE missingFunctionBody) * ;) */ FunctionBody parseFunctionBody() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!FunctionBody; immutable b = setBookmark(); immutable c = allocator.setCheckpoint(); auto missingFunctionBody = parseMissingFunctionBody(); if (missingFunctionBody !is null) { abandonBookmark(b); node.missingFunctionBody = missingFunctionBody; } else { allocator.rollback(c); goToBookmark(b, false); auto shortenedFunctionBody = parseShortenedFunctionBody(); if (shortenedFunctionBody !is null) { abandonBookmark(b); node.shortenedFunctionBody = shortenedFunctionBody; } else { allocator.rollback(c); goToBookmark(b); mixin(parseNodeQ!(`node.specifiedFunctionBody`, `SpecifiedFunctionBody`)); } } node.endLocation = previous.index; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a FunctionCallExpression * * $(GRAMMAR $(RULEDEF functionCallExpression): * $(RULE symbol) $(RULE arguments) * | $(RULE unaryExpression) $(RULE arguments) * | $(RULE type) $(RULE arguments) * ;) */ FunctionCallExpression parseFunctionCallExpression(UnaryExpression unary = null) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!FunctionCallExpression; switch (current.type) { case tok!"const": case tok!"immutable": case tok!"inout": case tok!"shared": case tok!"scope": case tok!"pure": case tok!"nothrow": mixin(parseNodeQ!(`node.type`, `Type`)); mixin(parseNodeQ!(`node.arguments`, `Arguments`)); break; default: if (unary !is null) node.unaryExpression = unary; else mixin(parseNodeQ!(`node.unaryExpression`, `UnaryExpression`)); if (currentIs(tok!"!")) mixin(parseNodeQ!(`node.templateArguments`, `TemplateArguments`)); if (unary !is null) mixin(parseNodeQ!(`node.arguments`, `Arguments`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a FunctionContract * * $(GRAMMAR $(RULEDEF functionContract): * $(RULE inOutContractExpression) * | $(RULE inOutStatement) * ;) */ FunctionContract parseFunctionContract(bool allowStatement = true) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!FunctionContract; if (allowStatement && (peekIs(tok!"{") || (currentIs(tok!"out") && peekAre(tok!"(", tok!"identifier", tok!")")))) mixin(parseNodeQ!(`node.inOutStatement`, `InOutStatement`)); else if (peekIs(tok!"(")) mixin(parseNodeQ!(`node.inOutContractExpression`, `InOutContractExpression`)); else { error(allowStatement ? "`{` or `(` expected" : "`(` expected"); return null; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a FunctionDeclaration * * $(GRAMMAR $(RULEDEF functionDeclaration): * ($(RULE storageClass)+ | $(RULE _type)) $(LITERAL Identifier) $(RULE parameters) $(RULE memberFunctionAttribute)* ($(RULE functionBody) | $(LITERAL ';')) * | ($(RULE storageClass)+ | $(RULE _type)) $(LITERAL Identifier) $(RULE templateParameters) $(RULE parameters) $(RULE memberFunctionAttribute)* $(RULE constraint)? ($(RULE functionBody) | $(LITERAL ';')) * ;) */ FunctionDeclaration parseFunctionDeclaration(Type type = null, bool isAuto = false, Attribute[] attributes = null) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!FunctionDeclaration; node.comment = comment; comment = null; StackBuffer memberFunctionAttributes; node.attributes = attributes; if (isAuto) { StackBuffer storageClasses; while (isStorageClass()) if (!storageClasses.put(parseStorageClass())) return null; ownArray(node.storageClasses, storageClasses); foreach (a; node.attributes) { if (a.attribute == tok!"auto") node.hasAuto = true; else if (a.attribute == tok!"ref") node.hasRef = true; else continue; } } else { while (moreTokens() && currentIsMemberFunctionAttribute()) if (!memberFunctionAttributes.put(parseMemberFunctionAttribute())) return null; if (type is null) mixin(parseNodeQ!(`node.returnType`, `Type`)); else node.returnType = type; } mixin(tokenCheck!(`node.name`, "identifier")); if (!currentIs(tok!"(")) { error("`(` expected"); return null; } const p = peekPastParens(); immutable bool isTemplate = p !is null && p.type == tok!"("; if (isTemplate) mixin(parseNodeQ!(`node.templateParameters`, `TemplateParameters`)); mixin(parseNodeQ!(`node.parameters`, `Parameters`)); while (moreTokens() && currentIsMemberFunctionAttribute()) if (!memberFunctionAttributes.put(parseMemberFunctionAttribute())) return null; if (isTemplate && currentIs(tok!"if")) mixin(parseNodeQ!(`node.constraint`, `Constraint`)); mixin(parseNodeQ!(`node.functionBody`, `FunctionBody`)); if (node.functionBody && node.functionBody.specifiedFunctionBody && node.functionBody.specifiedFunctionBody.blockStatement && node.functionBody.specifiedFunctionBody.blockStatement.tokens.length) attachComment(node, node.functionBody.specifiedFunctionBody.blockStatement.tokens[$ - 1].trailingComment); ownArray(node.memberFunctionAttributes, memberFunctionAttributes); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a FunctionLiteralExpression * * $(GRAMMAR $(RULEDEF functionLiteralExpression): * | $(LITERAL 'delegate') $(LITERAL 'ref')? $(RULE type)? ($(RULE parameters) $(RULE functionAttribute)*)? $(RULE specifiedFunctionBody) * | $(LITERAL 'function') $(LITERAL 'ref')? $(RULE type)? ($(RULE parameters) $(RULE functionAttribute)*)? $(RULE specifiedFunctionBody) * | $(LITERAL 'ref')? $(RULE parameters) $(RULE functionAttribute)* $(RULE specifiedFunctionBody) * | $(RULE specifiedFunctionBody) * | $(LITERAL Identifier) $(LITERAL '=>') $(RULE assignExpression) * | $(LITERAL 'function') $(LITERAL 'ref')? $(RULE type)? $(RULE parameters) $(RULE functionAttribute)* $(LITERAL '=>') $(RULE assignExpression) * | $(LITERAL 'delegate') $(LITERAL 'ref')? $(RULE type)? $(RULE parameters) $(RULE functionAttribute)* $(LITERAL '=>') $(RULE assignExpression) * | $(LITERAL 'ref')? $(RULE parameters) $(RULE functionAttribute)* $(LITERAL '=>') $(RULE assignExpression) * ;) */ FunctionLiteralExpression parseFunctionLiteralExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!FunctionLiteralExpression; node.line = current.line; node.column = current.column; if (currentIsOneOf(tok!"function", tok!"delegate")) { node.functionOrDelegate = advance().type; if (currentIs(tok!"ref")) { advance(); node.isReturnRef = true; } if (!currentIsOneOf(tok!"(", tok!"in", tok!"do", tok!"out", tok!"{", tok!"=>") && current.text != "body") mixin(parseNodeQ!(`node.returnType`, `Type`)); } if (startsWith(tok!"identifier", tok!"=>")) { node.identifier = advance(); advance(); // => mixin(parseNodeQ!(`node.assignExpression`, `AssignExpression`)); node.tokens = tokens[startIndex .. index]; return node; } else if (currentIs(tok!"(") || currentIs(tok!"ref") && peekIs(tok!"(")) { if (currentIs(tok!"ref")) { advance(); node.isReturnRef = true; } mixin(parseNodeQ!(`node.parameters`, `Parameters`)); StackBuffer memberFunctionAttributes; while (currentIsMemberFunctionAttribute()) { auto c = allocator.setCheckpoint(); if (!memberFunctionAttributes.put(parseMemberFunctionAttribute())) { allocator.rollback(c); break; } } ownArray(node.memberFunctionAttributes, memberFunctionAttributes); } if (currentIs(tok!"=>")) { advance(); mixin(parseNodeQ!(`node.assignExpression`, `AssignExpression`)); } else mixin(parseNodeQ!(`node.specifiedFunctionBody`, `SpecifiedFunctionBody`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an AsmInstruction using GCC Assembler * * $(GRAMMAR $(RULEDEF gccAsmInstruction): * | $(RULE expression) $(LITERAL ':') $(RULE gccAsmOperandList)? ($(LITERAL ':') $(RULE gccAsmOperandList)? ($(LITERAL ':') $(RULE stringLiteralList))? )? $(LITERAL ';') * | $(RULE expression) $(LITERAL ':') $(LITERAL ':') $(RULE gccAsmOperandList)? $(LITERAL ':') $(RULE stringLiteralList) $(LITERAL ';') $(LITERAL ':') $(RULE declaratorIdentifierList) $(LITERAL ';') * ;) */ /* * References: * - [1] https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html * - [2] https://wiki.dlang.org/Using_GDC * - [3] https://github.com/dlang/dmd/blob/master/src/dmd/iasmgcc.d * * Separated into a different method because one cannot interleave DMD & GCC asm * <asm-qualifiers> (volatile, inline, goto) not supperted (yet?) */ GccAsmInstruction parseGccAsmInstruction() { mixin(traceEnterAndExit!(__FUNCTION__)); const startIndex = index; auto node = allocator.make!GccAsmInstruction(); // Allow empty asm instructions if (currentIs(tok!";")) { warn("Empty asm instruction"); node.tokens = tokens[startIndex .. index]; return node; } mixin(parseNodeQ!("node.assemblerTemplate", "Expression")); // GDC allows e.g. asm { mixin(<some asm instruction>); } if (!currentIs(tok!";")) { mixin(tokenCheck!":"); if (!currentIsOneOf(tok!":", tok!";")) mixin(parseNodeQ!(`node.outputOperands`, `GccAsmOperandList`)); if (skip(tok!":")) { if (!currentIsOneOf(tok!":", tok!";")) mixin(parseNodeQ!(`node.inputOperands`, `GccAsmOperandList`)); if (skip(tok!":")) { if (!currentIs(tok!":")) mixin(parseNodeQ!("node.registers", "StringLiteralList")); if (skip(tok!":")) { size_t cp; if (node.outputOperands) { error("goto-labels only allowed without output operands!", false); cp = allocator.setCheckpoint(); } // Parse even with the error above for better error reporting mixin(parseNodeQ!("node.gotos", "DeclaratorIdentifierList")); if (cp) { allocator.rollback(cp); return null; } } } } } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a GccAsmOperandList * * $(GRAMMAR $(RULEDEF gccAsmOperandList): * $(RULE gccAsmOperand) ($(LITERAL ',') $(RULE gccAsmOperand))* * ;) */ GccAsmOperandList parseGccAsmOperandList() { mixin(traceEnterAndExit!(__FUNCTION__)); return parseCommaSeparatedRule!(GccAsmOperandList, GccAsmOperand)(); } /** * Parses a GccAsmOperand * * $(GRAMMAR $(RULEDEF gccAsmOperand): * ($(LITERAL '[') $(RULE identifier) $(LITERAL ']'))? $(RULE stringLiteral) $(LITERAL '$(LPAREN)') $(RULE assignExpression) $(LITERAL '$(RPAREN)') * ;) */ GccAsmOperand parseGccAsmOperand() { mixin(traceEnterAndExit!(__FUNCTION__)); const startIndex = index; auto node = allocator.make!GccAsmOperand(); if (currentIs(tok!"[")) { advance(); if (auto t = expect(tok!"identifier")) node.symbolicName = *t; mixin(tokenCheck!"]"); } mixin(tokenCheck!("node.constraint", "stringLiteral")); // GCC actually requires braces but GDC didn't for quite some time, // see https://github.com/dlang/dmd/pull/10820 const hasParens = skip(tok!"("); if (!hasParens) warn("Omitting parenthesis around operands is deprecated!"); mixin(parseNodeQ!("node.expression", "AssignExpression")); if (hasParens) expect(tok!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a GotoStatement * * $(GRAMMAR $(RULEDEF gotoStatement): * $(LITERAL 'goto') ($(LITERAL Identifier) | $(LITERAL 'default') | $(LITERAL 'case') $(RULE expression)?) $(LITERAL ';') * ;) */ GotoStatement parseGotoStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!GotoStatement; mixin(tokenCheck!"goto"); if (!moreTokens) return null; switch (current.type) { case tok!"identifier": case tok!"default": node.label = advance(); break; case tok!"case": node.label = advance(); if (!currentIs(tok!";")) mixin(parseNodeQ!(`node.expression`, `Expression`)); break; default: error("Identifier, `default`, or `case` expected"); return null; } mixin(tokenCheck!";"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an IdentifierChain * * $(GRAMMAR $(RULEDEF identifierChain): * $(LITERAL Identifier) ($(LITERAL '.') $(LITERAL Identifier))* * ;) */ IdentifierChain parseIdentifierChain() { auto startIndex = index; auto node = allocator.make!IdentifierChain; StackBuffer identifiers; while (moreTokens()) { const ident = expect(tok!"identifier"); mixin(nullCheck!`ident`); identifiers.put(*ident); if (currentIs(tok!".")) { advance(); continue; } else break; } ownArray(node.identifiers, identifiers); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a TypeIdentifierPart. * * $(GRAMMAR $(RULEDEF typeIdentifierPart): * $(RULE identifierOrTemplateInstance) * | $(RULE identifierOrTemplateInstance) $(LITERAL '.') $(RULE typeIdentifierPart) * | $(RULE identifierOrTemplateInstance) $(LITERAL '[') $(RULE assignExpression) $(LITERAL ']') * | $(RULE identifierOrTemplateInstance) $(LITERAL '[') $(RULE assignExpression) $(LITERAL ']') $(LITERAL '.') $(RULE typeIdentifierPart) * ;) */ TypeIdentifierPart parseTypeIdentifierPart() { auto startIndex = index; TypeIdentifierPart node = allocator.make!TypeIdentifierPart; if (currentIs(tok!".")) { node.dot = true; advance(); } mixin(parseNodeQ!(`node.identifierOrTemplateInstance`, `IdentifierOrTemplateInstance`)); if (currentIs(tok!"[")) { // dyn arrays -> type suffixes if (peekIs(tok!"]")) { node.tokens = tokens[startIndex .. index - 1]; return node; } const b = setBookmark(); advance(); const cp = allocator.setCheckpoint; node.indexer = parseAssignExpression(); // here we can have a type (AA key) if (node.indexer is null) { goToBookmark(b); return node; } // indexer followed by ".." -> sliceExp -> type suffix else if (currentIs(tok!"..")) { allocator.rollback(cp); node.indexer = null; goToBookmark(b); return node; } // otherwise either the index of a type list or a dim abandonBookmark(b); expect(tok!"]"); if (!currentIs(tok!".")) { node.tokens = tokens[startIndex .. index]; return node; } } if (currentIs(tok!".")) { advance(); mixin(parseNodeQ!(`node.typeIdentifierPart`, `TypeIdentifierPart`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an IdentifierOrTemplateChain * * $(GRAMMAR $(RULEDEF identifierOrTemplateChain): * $(RULE identifierOrTemplateInstance) ($(LITERAL '.') $(RULE identifierOrTemplateInstance))* * ;) */ IdentifierOrTemplateChain parseIdentifierOrTemplateChain() { auto startIndex = index; auto node = allocator.make!IdentifierOrTemplateChain; StackBuffer identifiersOrTemplateInstances; while (moreTokens()) { auto c = allocator.setCheckpoint(); if (!identifiersOrTemplateInstances.put(parseIdentifierOrTemplateInstance())) { allocator.rollback(c); if (identifiersOrTemplateInstances.length == 0) return null; else break; } if (!currentIs(tok!".")) break; else advance(); } ownArray(node.identifiersOrTemplateInstances, identifiersOrTemplateInstances); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an IdentifierOrTemplateInstance * * $(GRAMMAR $(RULEDEF identifierOrTemplateInstance): * $(LITERAL Identifier) * | $(RULE templateInstance) * ;) */ IdentifierOrTemplateInstance parseIdentifierOrTemplateInstance() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!IdentifierOrTemplateInstance; if (peekIs(tok!"!") && !startsWith(tok!"identifier", tok!"!", tok!"is") && !startsWith(tok!"identifier", tok!"!", tok!"in")) { mixin(parseNodeQ!(`node.templateInstance`, `TemplateInstance`)); } else { const ident = expect(tok!"identifier"); mixin(nullCheck!`ident`); node.identifier = *ident; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an IdentityExpression * * $(GRAMMAR $(RULEDEF identityExpression): * $(RULE shiftExpression) ($(LITERAL 'is') | ($(LITERAL '!') $(LITERAL 'is'))) $(RULE shiftExpression) * ;) */ ExpressionNode parseIdentityExpression(ExpressionNode shift = null) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!IdentityExpression; mixin(nullCheck!`node.left = shift is null ? parseShiftExpression() : shift`); if (currentIs(tok!"!")) { advance(); node.negated = true; } mixin(tokenCheck!"is"); mixin(parseNodeQ!(`node.right`, `ShiftExpression`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an IfStatement * * $(GRAMMAR $(RULEDEF ifStatement): * $(LITERAL 'if') $(LITERAL '$(LPAREN)') $(RULE ifCondition) $(LITERAL '$(RPAREN)') $(RULE declarationOrStatement) ($(LITERAL 'else') $(RULE declarationOrStatement))? *$(RULEDEF ifCondition): * $(LITERAL 'auto') $(LITERAL Identifier) $(LITERAL '=') $(RULE expression) * | $(RULE typeConstructors) $(LITERAL Identifier) $(LITERAL '=') $(RULE expression) * | $(RULE typeConstructors)? $(RULE type) $(LITERAL Identifier) $(LITERAL '=') $(RULE expression) * | $(RULE expression) * ;) */ IfStatement parseIfStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; IfStatement node = allocator.make!IfStatement; node.line = current().line; node.column = current().column; mixin(tokenCheck!"if"); if (moreTokens) node.startIndex = current().index; mixin(tokenCheck!"("); const b = setBookmark(); // ex. case: // `if (auto identifier = exp)` if (currentIs(tok!"auto") && peekIs(tok!"identifier")) { abandonBookmark(b); advance(); node.identifier = advance(); mixin(tokenCheck!"="); mixin(parseNodeQ!(`node.expression`, `Expression`)); } // `if (const shared ...` if (!node.expression && moreTokens && isTypeCtor(current.type)) { while (moreTokens) { // type ctor followed by open param is part of the type if (!isTypeCtor(current.type) || peekIs(tok!"(")) break; node.typeCtors ~= advance().type; } } // ex. case: // if (const shared stuff = exp) if (!node.expression && node.typeCtors.length && currentIs(tok!"identifier") && peekIs(tok!"=")) { abandonBookmark(b); node.identifier = advance(); advance(); mixin(parseNodeQ!(`node.expression`, `Expression`)); } if (!node.expression) { const c = allocator.setCheckpoint(); // ex. cases: // if (Type stuff = exp) // if (const shared Type stuff = exp) // if (const shared const(Type) stuff = exp) if (Type tp = parseType()) { if (currentIs(tok!"identifier") && peekIs(tok!"=")) { abandonBookmark(b); node.type = tp; node.identifier = advance(); advance(); mixin(parseNodeQ!(`node.expression`, `Expression`)); } // will try an expr since Type and Expression are ambiguous else allocator.rollback(c); } } // Relational expressions, unaries and such as condition if (!node.expression) { goToBookmark(b); mixin(parseNodeQ!(`node.expression`, `Expression`)); } if (!node.expression) { error("expression or declaration expected"); } mixin(tokenCheck!")"); if (currentIs(tok!"}")) { error("Statement expected", false); node.tokens = tokens[startIndex .. index]; return node; // this line makes DCD better } mixin(parseNodeQ!(`node.thenStatement`, `DeclarationOrStatement`)); if (currentIs(tok!"else")) { advance(); mixin(parseNodeQ!(`node.elseStatement`, `DeclarationOrStatement`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an ImportBind * * $(GRAMMAR $(RULEDEF importBind): * $(LITERAL Identifier) ($(LITERAL '=') $(LITERAL Identifier))? * ;) */ ImportBind parseImportBind() { auto startIndex = index; auto node = allocator.make!ImportBind; const ident = expect(tok!"identifier"); mixin(nullCheck!`ident`); node.left = *ident; if (currentIs(tok!"=")) { advance(); const id = expect(tok!"identifier"); mixin(nullCheck!`id`); node.right = *id; } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses ImportBindings * * $(GRAMMAR $(RULEDEF importBindings): * $(RULE _singleImport) $(LITERAL ':') $(RULE importBind) ($(LITERAL ',') $(RULE importBind))* * ;) */ ImportBindings parseImportBindings(SingleImport singleImport) { auto startIndex = index; auto node = allocator.make!ImportBindings; mixin(nullCheck!`node.singleImport = singleImport is null ? parseSingleImport() : singleImport`); mixin(tokenCheck!":"); StackBuffer importBinds; while (moreTokens()) { immutable c = allocator.setCheckpoint(); if (importBinds.put(parseImportBind())) { if (currentIs(tok!",")) advance(); else break; } else { allocator.rollback(c); break; } } ownArray(node.importBinds, importBinds); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an ImportDeclaration * * $(GRAMMAR $(RULEDEF importDeclaration): * $(LITERAL 'import') $(RULE singleImport) ($(LITERAL ',') $(RULE singleImport))* ($(LITERAL ',') $(RULE importBindings))? $(LITERAL ';') * | $(LITERAL 'import') $(RULE importBindings) $(LITERAL ';') * ;) */ ImportDeclaration parseImportDeclaration() { auto startIndex = index; auto node = allocator.make!ImportDeclaration; node.startIndex = current().index; mixin(tokenCheck!"import"); SingleImport si = parseSingleImport(); if (si is null) return null; if (currentIs(tok!":")) node.importBindings = parseImportBindings(si); else { StackBuffer singleImports; singleImports.put(si); if (currentIs(tok!",")) { advance(); while (moreTokens()) { auto single = parseSingleImport(); mixin(nullCheck!`single`); if (currentIs(tok!":")) { node.importBindings = parseImportBindings(single); break; } else { singleImports.put(single); if (currentIs(tok!",")) advance(); else break; } } } ownArray(node.singleImports, singleImports); } node.endIndex = (moreTokens() ? current() : previous()).index + 1; mixin(tokenCheck!";"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an ImportExpression * * $(GRAMMAR $(RULEDEF importExpression): * $(LITERAL 'import') $(LITERAL '$(LPAREN)') $(RULE assignExpression) $(LITERAL '$(RPAREN)') * ;) */ ImportExpression parseImportExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); mixin(simpleParse!(ImportExpression, tok!"import", tok!"(", "assignExpression|parseAssignExpression", tok!")")); } /** * Parses an Index * * $(GRAMMAR $(RULEDEF index): * $(RULE assignExpression) ($(LITERAL '..') $(RULE assignExpression))? * ; * ) */ Index parseIndex() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Index(); mixin(parseNodeQ!(`node.low`, `AssignExpression`)); if (currentIs(tok!"..")) { advance(); mixin(parseNodeQ!(`node.high`, `AssignExpression`)); } node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an IndexExpression * * $(GRAMMAR $(RULEDEF indexExpression): * $(RULE _unaryExpression) $(LITERAL '[') $(LITERAL ']') * | $(RULE _unaryExpression) $(LITERAL '[') $(RULE index) ($(LITERAL ',') $(RULE index))* $(LITERAL ']') * ; * ) */ IndexExpression parseIndexExpression(UnaryExpression unaryExpression = null) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!IndexExpression; mixin(nullCheck!`node.unaryExpression = unaryExpression is null ? parseUnaryExpression() : unaryExpression`); mixin(tokenCheck!"["); StackBuffer indexes; while (true) { if (currentIs(tok!"]")) break; if (!(indexes.put(parseIndex()))) return null; if (!moreTokens()) { error("Expected ',' or ']' instead of EOF"); return null; } if (currentIs(tok!",")) advance(); else break; } ownArray(node.indexes, indexes); mixin(tokenCheck!"]"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an InContractExpression * * $(GRAMMAR $(RULEDEF inContractExpression): * $(LITERAL 'in') $(LITERAL '$(LPAREN)') $(RULE assertArguments) $(LITERAL '$(RPAREN)') * ;) */ InContractExpression parseInContractExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!InContractExpression; const i = expect(tok!"in"); mixin(nullCheck!`i`); node.inTokenLocation = i.index; mixin(tokenCheck!"("); mixin(parseNodeQ!(`node.assertArguments`, `AssertArguments`)); mixin(tokenCheck!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an InExpression * * $(GRAMMAR $(RULEDEF inExpression): * $(RULE shiftExpression) ($(LITERAL 'in') | ($(LITERAL '!') $(LITERAL 'in'))) $(RULE shiftExpression) * ;) */ ExpressionNode parseInExpression(ExpressionNode shift = null) { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!InExpression; mixin(nullCheck!`node.left = shift is null ? parseShiftExpression() : shift`); if (currentIs(tok!"!")) { node.negated = true; advance(); } mixin(tokenCheck!"in"); mixin(parseNodeQ!(`node.right`, `ShiftExpression`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an InOutContractExpression * * $(GRAMMAR $(RULEDEF inOutContractExpression): * $(RULE inContractExpression) * | $(RULE outContractExpression) * ;) */ InOutContractExpression parseInOutContractExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!InOutContractExpression; if (currentIs(tok!"in")) mixin(parseNodeQ!(`node.inContractExpression`, `InContractExpression`)); else if (currentIs(tok!"out")) mixin(parseNodeQ!(`node.outContractExpression`, `OutContractExpression`)); else return null; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an InOutStatement * * $(GRAMMAR $(RULEDEF inOutStatement): * $(RULE inStatement) * | $(RULE outStatement) * ;) */ InOutStatement parseInOutStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!InOutStatement; if (currentIs(tok!"in")) mixin(parseNodeQ!(`node.inStatement`, `InStatement`)); else if (currentIs(tok!"out")) mixin(parseNodeQ!(`node.outStatement`, `OutStatement`)); else return null; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an InStatement * * $(GRAMMAR $(RULEDEF inStatement): * $(LITERAL 'in') $(RULE blockStatement) * ;) */ InStatement parseInStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!InStatement; const i = expect(tok!"in"); mixin(nullCheck!`i`); node.inTokenLocation = i.index; mixin(parseNodeQ!(`node.blockStatement`, `BlockStatement`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an Initializer * * $(GRAMMAR $(RULEDEF initializer): * $(LITERAL 'void') * | $(RULE nonVoidInitializer) * ;) */ Initializer parseInitializer() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Initializer; if (currentIs(tok!"void") && peekIsOneOf(tok!",", tok!";")) advance(); else mixin(parseNodeQ!(`node.nonVoidInitializer`, `NonVoidInitializer`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an InterfaceDeclaration * * $(GRAMMAR $(RULEDEF interfaceDeclaration): * $(LITERAL 'interface') $(LITERAL Identifier) $(LITERAL ';') * | $(LITERAL 'interface') $(LITERAL Identifier) ($(LITERAL ':') $(RULE baseClassList))? $(RULE structBody) * | $(LITERAL 'interface') $(LITERAL Identifier) $(RULE templateParameters) $(RULE constraint)? ($(LITERAL ':') $(RULE baseClassList))? $(RULE structBody) * | $(LITERAL 'interface') $(LITERAL Identifier) $(RULE templateParameters) ($(LITERAL ':') $(RULE baseClassList))? $(RULE constraint)? $(RULE structBody) * ;) */ InterfaceDeclaration parseInterfaceDeclaration() { auto startIndex = index; auto node = allocator.make!InterfaceDeclaration; expect(tok!"interface"); return parseInterfaceOrClass(node, startIndex); } /** * Parses an Invariant * * $(GRAMMAR $(RULEDEF invariant): * $(LITERAL 'invariant') ($(LITERAL '$(LPAREN)') $(LITERAL '$(LPAREN)'))? $(RULE blockStatement) * | $(LITERAL 'invariant') $(LITERAL '$(LPAREN)') $(RULE assertArguments) $(LITERAL '$(RPAREN)') $(LITERAL ';') * ;) */ Invariant parseInvariant() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!Invariant; node.index = current.index; node.line = current.line; mixin(tokenCheck!"invariant"); bool mustHaveBlock; if (currentIs(tok!"(") && peekIs(tok!")")) { mustHaveBlock = true; node.useParen = true; advance(); advance(); } if (currentIs(tok!"{")) { if (currentIs(tok!"(")) { advance(); mixin(tokenCheck!")"); } mixin(parseNodeQ!(`node.blockStatement`, `BlockStatement`)); } else if (!mustHaveBlock && currentIs(tok!"(")) { advance(); node.useParen = true; mixin(parseNodeQ!(`node.assertArguments`, `AssertArguments`)); mixin(tokenCheck!")"); mixin(tokenCheck!";"); } else return null; node.tokens = tokens[startIndex .. index]; return node; } /** * Parses an IsExpression * * $(GRAMMAR $(RULEDEF isExpression): * $(LITERAL'is') $(LITERAL '$(LPAREN)') $(RULE type) $(LITERAL identifier)? $(LITERAL '$(RPAREN)') * | $(LITERAL'is') $(LITERAL '$(LPAREN)') $(RULE type) $(LITERAL identifier)? $(LITERAL ':') $(RULE typeSpecialization) $(LITERAL '$(RPAREN)') * | $(LITERAL'is') $(LITERAL '$(LPAREN)') $(RULE type) $(LITERAL identifier)? $(LITERAL '=') $(RULE typeSpecialization) $(LITERAL '$(RPAREN)') * | $(LITERAL'is') $(LITERAL '$(LPAREN)') $(RULE type) $(LITERAL identifier)? $(LITERAL ':') $(RULE typeSpecialization) $(LITERAL ',') $(RULE templateParameterList) $(LITERAL '$(RPAREN)') * | $(LITERAL'is') $(LITERAL '$(LPAREN)') $(RULE type) $(LITERAL identifier)? $(LITERAL '=') $(RULE typeSpecialization) $(LITERAL ',') $(RULE templateParameterList) $(LITERAL '$(RPAREN)') * ;) */ IsExpression parseIsExpression() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!IsExpression; mixin(tokenCheck!"is"); mixin(tokenCheck!"("); mixin(parseNodeQ!(`node.type`, `Type`)); if (currentIs(tok!"identifier")) node.identifier = advance(); if (currentIsOneOf(tok!"==", tok!":")) { node.equalsOrColon = advance().type; mixin(parseNodeQ!(`node.typeSpecialization`, `TypeSpecialization`)); if (currentIs(tok!",")) { advance(); mixin(parseNodeQ!(`node.templateParameterList`, `TemplateParameterList`)); } } mixin(tokenCheck!")"); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a KeyValuePair * * $(GRAMMAR $(RULEDEF keyValuePair): * $(RULE assignExpression) $(LITERAL ':') $(RULE assignExpression) * ;) */ KeyValuePair parseKeyValuePair() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!KeyValuePair; mixin(parseNodeQ!(`node.key`, `AssignExpression`)); mixin(tokenCheck!":"); mixin(parseNodeQ!(`node.value`, `AssignExpression`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses KeyValuePairs * * $(GRAMMAR $(RULEDEF keyValuePairs): * $(RULE keyValuePair) ($(LITERAL ',') $(RULE keyValuePair))* $(LITERAL ',')? * ;) */ KeyValuePairs parseKeyValuePairs() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!KeyValuePairs; StackBuffer keyValuePairs; while (moreTokens()) { if (!keyValuePairs.put(parseKeyValuePair())) return null; if (currentIs(tok!",")) { advance(); if (currentIs(tok!"]")) break; } else break; } ownArray(node.keyValuePairs, keyValuePairs); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a LabeledStatement * * $(GRAMMAR $(RULEDEF labeledStatement): * $(LITERAL Identifier) $(LITERAL ':') $(RULE declarationOrStatement)? * ;) */ LabeledStatement parseLabeledStatement() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!LabeledStatement; const ident = expect(tok!"identifier"); mixin (nullCheck!`ident`); node.identifier = *ident; expect(tok!":"); if (!currentIs(tok!"}")) mixin(parseNodeQ!(`node.declarationOrStatement`, `DeclarationOrStatement`)); node.tokens = tokens[startIndex .. index]; return node; } /** * Parses a LastCatch * * $(GRAMMAR $(RULEDEF lastCatch): * $(LITERAL 'catch') $(RULE statementNoCaseNoDefault) * ;) */ LastCatch parseLastCatch() { mixin(traceEnterAndExit!(__FUNCTION__)); auto startIndex = index; auto node = allocator.make!LastCatch; const t = expect(tok!"catch"); mixin (nullCheck!`t`); node.line = t.line; no