12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183 |
- /*
- * Copyright (c) Contributors, http://opensimulator.org/
- * See CONTRIBUTORS.TXT for a full list of copyright holders.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of the OpenSimulator Project nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- /**
- * @brief Reduce parser tokens to abstract syntax tree tokens.
- *
- * Usage:
- *
- * tokenBegin = returned by TokenBegin.Analyze ()
- * representing the whole script source
- * as a flat list of tokens
- *
- * TokenScript tokenScript = Reduce.Analyze (TokenBegin tokenBegin);
- *
- * tokenScript = represents the whole script source
- * as a tree of tokens
- */
- using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Reflection;
- using System.Reflection.Emit;
- using System.Text;
- using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
- using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
- using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
- using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
- using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
- using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
- using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
- namespace OpenSim.Region.ScriptEngine.Yengine
- {
- public class ScriptReduce
- {
- public const uint SDT_PRIVATE = 1;
- public const uint SDT_PROTECTED = 2;
- public const uint SDT_PUBLIC = 4;
- public const uint SDT_ABSTRACT = 8;
- public const uint SDT_FINAL = 16;
- public const uint SDT_NEW = 32;
- public const uint SDT_OVERRIDE = 64;
- public const uint SDT_STATIC = 128;
- public const uint SDT_VIRTUAL = 256;
- private const int ASNPR = 50;
- private readonly static Dictionary<Type, int> precedence = PrecedenceInit();
- private static readonly Type[] brkCloseOnly = new Type[] { typeof(TokenKwBrkClose) };
- private static readonly Type[] cmpGTOnly = new Type[] { typeof(TokenKwCmpGT) };
- private static readonly Type[] colonOnly = new Type[] { typeof(TokenKwColon) };
- private static readonly Type[] commaOrBrcClose = new Type[] { typeof(TokenKwComma), typeof(TokenKwBrcClose) };
- private static readonly Type[] colonOrDotDotDot = new Type[] { typeof(TokenKwColon), typeof(TokenKwDotDotDot) };
- private static readonly Type[] parCloseOnly = new Type[] { typeof(TokenKwParClose) };
- private static readonly Type[] semiOnly = new Type[] { typeof(TokenKwSemi) };
- /**
- * @brief Initialize operator precedence table
- * @returns with precedence table pointer
- */
- private static Dictionary<Type, int> PrecedenceInit()
- {
- return new Dictionary<Type, int>()
- {
- // http://www.lslwiki.net/lslwiki/wakka.php?wakka=operators
- { typeof(TokenKwComma), 30 },
- { typeof(TokenKwAsnLSh), ASNPR }, // all assignment operators of equal precedence
- { typeof(TokenKwAsnRSh), ASNPR }, // ... so they get processed strictly right-to-left
- { typeof(TokenKwAsnAdd), ASNPR },
- { typeof(TokenKwAsnAnd), ASNPR },
- { typeof(TokenKwAsnSub), ASNPR },
- { typeof(TokenKwAsnMul), ASNPR },
- { typeof(TokenKwAsnDiv), ASNPR },
- { typeof(TokenKwAsnMod), ASNPR },
- { typeof(TokenKwAsnOr), ASNPR },
- { typeof(TokenKwAsnXor), ASNPR },
- { typeof(TokenKwAssign), ASNPR },
- { typeof(TokenKwQMark), 60 },
- { typeof(TokenKwOrOrOr), 70 },
- { typeof(TokenKwAndAndAnd), 80 },
- { typeof(TokenKwOrOr), 100 },
- //{ typeof(TokenKwAndAnd), 120 },
- { typeof(TokenKwAndAnd), 100 },
- { typeof(TokenKwOr), 140 },
- { typeof(TokenKwXor), 160 },
- { typeof(TokenKwAnd), 180 },
- { typeof(TokenKwCmpEQ), 200 },
- { typeof(TokenKwCmpNE), 200 },
- { typeof(TokenKwCmpLT), 240 },
- { typeof(TokenKwCmpLE), 240 },
- { typeof(TokenKwCmpGT), 240 },
- { typeof(TokenKwCmpGE), 240 },
- { typeof(TokenKwRSh), 260 },
- { typeof(TokenKwLSh), 260 },
- { typeof(TokenKwAdd), 280 },
- { typeof(TokenKwSub), 280 },
- { typeof(TokenKwMul), 320 },
- { typeof(TokenKwDiv), 320 },
- { typeof(TokenKwMod), 320 }
- };
- }
- /**
- * @brief Reduce raw token stream to a single script token.
- * Performs a little semantic testing, ie, undefined variables, etc.
- * @param tokenBegin = points to a TokenBegin
- * followed by raw tokens
- * and last token is a TokenEnd
- * @returns null: not a valid script, error messages have been output
- * else: valid script top token
- */
- public static TokenScript Reduce(TokenBegin tokenBegin)
- {
- return new ScriptReduce(tokenBegin).tokenScript;
- }
- /*
- * Instance variables.
- */
- private bool errors = false;
- private string lastErrorFile = "";
- private int lastErrorLine = 0;
- private int numTypedefs = 0;
- private TokenDeclVar currentDeclFunc = null;
- private TokenDeclSDType currentDeclSDType = null;
- private TokenScript tokenScript;
- private TokenStmtBlock currentStmtBlock = null;
- /**
- * @brief the constructor does all the processing.
- * @param token = first token of script after the TokenBegin token
- * @returns tokenScript = null: there were errors
- * else: successful
- */
- private ScriptReduce(TokenBegin tokenBegin)
- {
- // Create a place to put the top-level script components,
- // eg, state bodies, functions, global variables.
- tokenScript = new TokenScript(tokenBegin.nextToken);
- // 'class', 'delegate', 'instance' all define types.
- // So we pre-scan the source tokens for those keywords
- // to build a script-defined type table and substitute
- // type tokens for those names in the source. This is
- // done as a separate scan so they can cross-reference
- // each other. Also does likewise for fixed array types.
- //
- // Also, all 'typedef's are processed here. Their definitions
- // remain in the source token stream after this, but they can
- // be skipped over, because their bodies have been substituted
- // in the source for any references.
- ParseSDTypePreScanPassOne(tokenBegin); // catalog definitions
- ParseSDTypePreScanPassTwo(tokenBegin); // substitute references
- /*
- int braces = 0;
- Token prevTok = null;
- for (Token token = tokenBegin; token != null; token = token.nextToken) {
- if (token is TokenKwParClose) braces -= 2;
- if (token is TokenKwBrcClose) braces -= 4;
- StringBuilder sb = new StringBuilder ("ScriptReduce*: ");
- sb.Append (token.GetHashCode ().ToString ("X8"));
- sb.Append (" ");
- sb.Append (token.line.ToString ().PadLeft (3));
- sb.Append (".");
- sb.Append (token.posn.ToString ().PadLeft (3));
- sb.Append (" ");
- sb.Append (token.GetType ().Name.PadRight (24));
- sb.Append (" : ");
- for (int i = 0; i < braces; i ++) sb.Append (' ');
- token.DebString (sb);
- Console.WriteLine (sb.ToString ());
- if (token.prevToken != prevTok) {
- Console.WriteLine ("ScriptReduce*: -- prevToken link bad => " + token.prevToken.GetHashCode ().ToString ("X8"));
- }
- if (token is TokenKwBrcOpen) braces += 4;
- if (token is TokenKwParOpen) braces += 2;
- prevTok = token;
- }
- */
- // Create a function $globalvarinit to hold all explicit
- // global variable initializations.
- TokenDeclVar gviFunc = new (tokenBegin, null, tokenScript);
- gviFunc.name = new TokenName(gviFunc, "$globalvarinit");
- gviFunc.retType = new TokenTypeVoid(gviFunc);
- gviFunc.argDecl = new TokenArgDecl(gviFunc);
- TokenStmtBlock gviBody = new (gviFunc)
- {
- function = gviFunc
- };
- gviFunc.body = gviBody;
- tokenScript.globalVarInit = gviFunc;
- tokenScript.AddVarEntry(gviFunc);
- // Scan through the tokens until we reach the end.
- for(Token token = tokenBegin.nextToken; token is not TokenEnd;)
- {
- if(token is TokenKwSemi)
- {
- token = token.nextToken;
- continue;
- }
- // Script-defined type declarations.
- if(ParseDeclSDTypes(ref token, null, SDT_PUBLIC))
- continue;
- // constant <name> = <rval> ;
- if(token is TokenKwConst)
- {
- ParseDeclVar(ref token, null);
- continue;
- }
- // <type> <name> ;
- // <type> <name> = <rval> ;
- if((token is TokenType) &&
- (token.nextToken is TokenName) &&
- ((token.nextToken.nextToken is TokenKwSemi) ||
- (token.nextToken.nextToken is TokenKwAssign)))
- {
- TokenDeclVar tdvar = ParseDeclVar(ref token, gviFunc);
- if(tdvar != null)
- {
- // <name> = <init>;
- TokenLValName left = new (tdvar.name, tokenScript.variablesStack);
- DoVarInit(gviFunc, left, tdvar.init);
- }
- continue;
- }
- // <type> <name> { [ get { <body> } ] [ set { <body> } ] }
- if((token is TokenType) &&
- (token.nextToken is TokenName) &&
- (token.nextToken.nextToken is TokenKwBrcOpen))
- {
- ParseProperty(ref token, false, true);
- continue;
- }
- // <type> <name> <funcargs> <funcbody>
- // global function returning specified type
- if(token is TokenType tokenType)
- {
- token = token.nextToken;
- if(token is not TokenName)
- {
- ErrorMsg(token, "expecting variable/function name");
- token = SkipPastSemi(token);
- continue;
- }
- TokenName tokenName = (TokenName)token;
- token = token.nextToken;
- if(token is not TokenKwParOpen)
- {
- ErrorMsg(token, "<type> <name> must be followed by ; = or (");
- token = SkipPastSemi(token);
- continue;
- }
- token = tokenType;
- TokenDeclVar tokenDeclFunc = ParseDeclFunc(ref token, false, false, false);
- if(tokenDeclFunc == null)
- continue;
- if(!tokenScript.AddVarEntry(tokenDeclFunc))
- {
- ErrorMsg(tokenName, "duplicate function " + tokenDeclFunc.funcNameSig.val);
- }
- continue;
- }
- // <name> <funcargs> <funcbody>
- // global function returning void
- if(token is TokenName)
- {
- TokenName tokenName = (TokenName)token;
- token = token.nextToken;
- if(token is not TokenKwParOpen)
- {
- ErrorMsg(token, "looking for open paren after assuming " +
- tokenName.val + " is a function name");
- token = SkipPastSemi(token);
- continue;
- }
- token = tokenName;
- TokenDeclVar tokenDeclFunc = ParseDeclFunc(ref token, false, false, false);
- if(tokenDeclFunc == null)
- continue;
- if(!tokenScript.AddVarEntry(tokenDeclFunc))
- ErrorMsg(tokenName, "duplicate function " + tokenDeclFunc.funcNameSig.val);
- continue;
- }
- // default <statebody>
- if(token is TokenKwDefault)
- {
- TokenDeclState tokenDeclState = new (token);
- token = token.nextToken;
- tokenDeclState.body = ParseStateBody(ref token);
- if(tokenDeclState.body == null)
- continue;
- if(tokenScript.defaultState != null)
- {
- ErrorMsg(tokenDeclState, "default state already declared");
- continue;
- }
- tokenScript.defaultState = tokenDeclState;
- continue;
- }
- // state <name> <statebody>
- if(token is TokenKwState)
- {
- TokenDeclState tokenDeclState = new (token);
- token = token.nextToken;
- if(token is not TokenName TokenNametoken)
- {
- ErrorMsg(token, "state must be followed by state name");
- token = SkipPastSemi(token);
- continue;
- }
- tokenDeclState.name = TokenNametoken;
- token = token.nextToken;
- tokenDeclState.body = ParseStateBody(ref token);
- if(tokenDeclState.body == null)
- continue;
- if(tokenScript.states.ContainsKey(tokenDeclState.name.val))
- {
- ErrorMsg(tokenDeclState.name, "duplicate state definition");
- continue;
- }
- tokenScript.states.Add(tokenDeclState.name.val, tokenDeclState);
- continue;
- }
- // Doesn't fit any of those forms, output message and skip to next statement.
- ErrorMsg(token, "looking for var name, type, state or default, script-defined type declaration");
- token = SkipPastSemi(token);
- continue;
- }
- // Must have a default state to start in.
- if(!errors && (tokenScript.defaultState == null))
- {
- ErrorMsg(tokenScript, "no default state defined");
- }
- // If any error messages were written out, set return value to null.
- if(errors)
- tokenScript = null;
- }
- /**
- * @brief Pre-scan the source for class, delegate, interface, typedef definition keywords.
- * Clump the keywords and name being defined together, but leave the body intact.
- * In the case of a delegate with an explicit return type, it reverses the name and return type.
- * After this completes there shouldn't be any TokenKw{Class,Delegate,Interface,Typedef}
- * keywords in the source, they are all replaced by TokenDeclSDType{Class,Delegate,Interface,
- * Typedef} tokens which also encapsulate the name of the type being defined and any generic
- * parameter names. The body remains intact in the source token stream following the
- * TokenDeclSDType* token.
- */
- private void ParseSDTypePreScanPassOne(Token tokenBegin)
- {
- Stack<int> braceLevels = new();
- Stack<TokenDeclSDType> outerLevels = new();
- int openBraceLevel = 0;
- braceLevels.Push(-1);
- outerLevels.Push(null);
- Token t = tokenBegin;
- while((t = t.nextToken) is not TokenEnd)
- {
- // Keep track of nested definitions so we can link them up.
- // We also need to detect the end of class and interface definitions.
- if(t is TokenKwBrcOpen)
- {
- openBraceLevel++;
- continue;
- }
- if(t is TokenKwBrcClose)
- {
- if(--openBraceLevel < 0)
- {
- ErrorMsg(t, "{ } mismatch");
- return;
- }
- if(braceLevels.Peek() == openBraceLevel)
- {
- braceLevels.Pop();
- outerLevels.Pop().endToken = t;
- }
- continue;
- }
- // Check for 'class' or 'interface'.
- // They always define a new class or interface.
- // They can contain nested script-defined type definitions.
- if((t is TokenKwClass) || (t is TokenKwInterface))
- {
- Token kw = t;
- t = t.nextToken;
- if(t is not TokenName)
- {
- ErrorMsg(t, "expecting class or interface name");
- t = SkipPastSemi(t).prevToken;
- continue;
- }
- TokenName name = (TokenName)t;
- t = t.nextToken;
- // Malloc the script-defined type object.
- TokenDeclSDType decl;
- if(kw is TokenKwClass)
- decl = new TokenDeclSDTypeClass(name, kw.prevToken is TokenKwPartial);
- else
- decl = new TokenDeclSDTypeInterface(name);
- decl.outerSDType = outerLevels.Peek();
- // Check for generic parameter list.
- if(!ParseGenProtoParamList(ref t, decl))
- continue;
- // Splice in a TokenDeclSDType token that replaces the keyword and the name tokens
- // and any generic parameters including the '<', ','s and '>'.
- // kw = points to 'class' or 'interface' keyword.
- // t = points to just past last part of class name parsed, hopefully a ':' or '{'.
- decl.prevToken = decl.isPartial ? kw.prevToken.prevToken : kw.prevToken;
- decl.nextToken = t;
- decl.prevToken.nextToken = decl;
- decl.nextToken.prevToken = decl;
- // Enter it in name lists so it can be seen by others.
- Token partialNewBody = CatalogSDTypeDecl(decl);
- // Start inner type definitions.
- braceLevels.Push(openBraceLevel);
- outerLevels.Push(decl);
- // Scan the body starting on for before the '{'.
- //
- // If this body had an old partial merged into it,
- // resume scanning at the beginning of the new body,
- // ie, what used to be the first token after the '{'
- // before the old body was spliced in.
- if(partialNewBody != null)
- {
- // We have a partial that has had old partial body merged
- // into new partial body. So resume scanning at the beginning
- // of the new partial body so we don't get any duplicate scanning
- // of the old partial body.
- //
- // <decl> ... { <oldbody> <newbody> }
- // ^- resume scanning here
- // but inc openBraceLevel because
- // we skipped scanning the '{'
- openBraceLevel++;
- t = partialNewBody;
- }
- t = t.prevToken;
- continue;
- }
- // Check for 'delegate'.
- // It always defines a new delegate.
- // Delegates never define nested types.
- if(t is TokenKwDelegate)
- {
- Token kw = t;
- t = t.nextToken;
- // Next thing might be an explicit return type or the delegate's name.
- // If it's a type token, then it's the return type, simple enough.
- // But if it's a name token, it might be the name of some other script-defined type.
- // The way to tell is that the delegate name is followed by a '(', whereas an
- // explicit return type is followed by the delegate name.
- Token retType = t;
- TokenName delName = null;
- Token u;
- int angles = 0;
- for(u = t; u is not TokenKwParOpen; u = u.nextToken)
- {
- if((u is TokenKwSemi) || (u is TokenEnd))
- break;
- if(u is TokenKwCmpLT)
- angles++;
- if(u is TokenKwCmpGT)
- angles--;
- if(u is TokenKwRSh)
- angles -= 2; // idiot >>
- if((angles == 0) && (u is TokenName name))
- delName = name;
- }
- if(u is not TokenKwParOpen)
- {
- ErrorMsg(u, "expecting ( for delegate parameter list");
- t = SkipPastSemi(t).prevToken;
- continue;
- }
- if(delName == null)
- {
- ErrorMsg(u, "expecting delegate name");
- t = SkipPastSemi(t).prevToken;
- continue;
- }
- if(retType == delName)
- retType = null;
- // Malloc the script-defined type object.
- TokenDeclSDTypeDelegate decl = new (delName)
- {
- outerSDType = outerLevels.Peek()
- };
- // Check for generic parameter list.
- t = delName.nextToken;
- if(!ParseGenProtoParamList(ref t, decl))
- continue;
- // Enter it in name lists so it can be seen by others.
- CatalogSDTypeDecl(decl);
- // Splice in the token that replaces the 'delegate' keyword and the whole name
- // (including the '<' name ... '>' parts). The return type token(s), if any,
- // follow the splice token and come before the '('.
- decl.prevToken = kw.prevToken;
- kw.prevToken.nextToken = decl;
- if(retType == null)
- {
- decl.nextToken = t;
- t.prevToken = decl;
- }
- else
- {
- decl.nextToken = retType;
- retType.prevToken = decl;
- retType.nextToken = t;
- t.prevToken = retType;
- }
- // Scan for terminating ';'.
- // There cannot be an intervening class, delegate, interfate, typedef, { or }.
- for(t = decl; t is not TokenKwSemi; t = u)
- {
- u = t.nextToken;
- if((u is TokenEnd) ||
- (u is TokenKwClass) ||
- (u is TokenKwDelegate) ||
- (u is TokenKwInterface) ||
- (u is TokenKwTypedef) ||
- (u is TokenKwBrcOpen) ||
- (u is TokenKwBrcClose))
- {
- ErrorMsg(t, "delegate missing terminating ;");
- break;
- }
- }
- decl.endToken = t;
- continue;
- }
- // Check for 'typedef'.
- // It always defines a new macro.
- // Typedefs never define nested types.
- if(t is TokenKwTypedef)
- {
- Token kw = t;
- t = t.nextToken;
- if(t is not TokenName)
- {
- ErrorMsg(t, "expecting typedef name");
- t = SkipPastSemi(t).prevToken;
- continue;
- }
- TokenName tdName = (TokenName)t;
- t = t.nextToken;
- // Malloc the script-defined type object.
- TokenDeclSDTypeTypedef decl = new (tdName)
- {
- outerSDType = outerLevels.Peek()
- };
- // Check for generic parameter list.
- if (!ParseGenProtoParamList(ref t, decl))
- continue;
- // Enter it in name lists so it can be seen by others.
- CatalogSDTypeDecl(decl);
- numTypedefs++;
- // Splice in the token that replaces the 'typedef' keyword and the whole name
- // (including the '<' name ... '>' parts).
- decl.prevToken = kw.prevToken;
- kw.prevToken.nextToken = decl;
- decl.nextToken = t;
- t.prevToken = decl;
- // Scan for terminating ';'.
- // There cannot be an intervening class, delegate, interfate, typedef, { or }.
- Token u;
- for(t = decl; t is not TokenKwSemi; t = u)
- {
- u = t.nextToken;
- if((u is TokenEnd) ||
- (u is TokenKwClass) ||
- (u is TokenKwDelegate) ||
- (u is TokenKwInterface) ||
- (u is TokenKwTypedef) ||
- (u is TokenKwBrcOpen) ||
- (u is TokenKwBrcClose))
- {
- ErrorMsg(t, "typedef missing terminating ;");
- break;
- }
- }
- decl.endToken = t;
- continue;
- }
- }
- }
- /**
- * @brief Parse a possibly generic type definition's parameter list.
- * @param t = points to the possible opening '<' on entry
- * points just past the closing '>' on return
- * @param decl = the generic type being declared
- * @returns false: parse error
- * true: decl.genParams = filled in with parameter list
- * decl.innerSDTypes = filled in with parameter list
- */
- private bool ParseGenProtoParamList(ref Token t, TokenDeclSDType decl)
- {
- // Maybe there aren't any generic parameters.
- // If so, leave decl.genParams = null.
- if(t is not TokenKwCmpLT)
- return true;
- // Build list of generic parameter names.
- Dictionary<string, int> parms = new();
- do
- {
- t = t.nextToken;
- if(t is not TokenName)
- {
- ErrorMsg(t, "expecting generic parameter name");
- break;
- }
- TokenName tn = (TokenName)t;
- if(parms.ContainsKey(tn.val))
- ErrorMsg(tn, "duplicate use of generic parameter name");
- else
- parms.Add(tn.val, parms.Count);
- t = t.nextToken;
- } while(t is TokenKwComma);
- if(t is not TokenKwCmpGT)
- {
- ErrorMsg(t, "expecting , for more params or > to end param list");
- return false;
- }
- t = t.nextToken;
- decl.genParams = parms;
- return true;
- }
- /**
- * @brief Catalog a script-defined type.
- * Its short name (eg, 'Node') gets put in the next outer level (eg, 'List')'s inner type definition table.
- * Its long name (eg, 'List.Node') gets put in the global type definition table.
- */
- public Token CatalogSDTypeDecl(TokenDeclSDType decl)
- {
- string longName = decl.longName.val;
- if (!tokenScript.sdSrcTypesTryGetValue(longName, out TokenDeclSDType dupDecl))
- {
- tokenScript.sdSrcTypesAdd(longName, decl);
- decl.outerSDType?.innerSDTypes.Add(decl.shortName.val, decl);
- return null;
- }
- if (!dupDecl.isPartial || !decl.isPartial)
- {
- ErrorMsg(decl, "duplicate definition of type " + longName);
- ErrorMsg(dupDecl, "previous definition here");
- return null;
- }
- if(!GenericParametersMatch(decl, dupDecl))
- ErrorMsg(decl, "all partial class generic parameters must match");
- // Have new declaration be the cataloged one because body is going to get
- // snipped out of old declaration and pasted into new declaration.
- tokenScript.sdSrcTypesRep(longName, decl);
- if(decl.outerSDType != null)
- decl.outerSDType.innerSDTypes[decl.shortName.val] = decl;
- // Find old partial definition's opening brace.
- Token dupBrcOpen;
- for(dupBrcOpen = dupDecl; dupBrcOpen is not TokenKwBrcOpen; dupBrcOpen = dupBrcOpen.nextToken)
- {
- if(dupBrcOpen == dupDecl.endToken)
- {
- ErrorMsg(dupDecl, "missing {");
- return null;
- }
- }
- // Find new partial definition's opening brace.
- Token brcOpen;
- for(brcOpen = decl; brcOpen is not TokenKwBrcOpen; brcOpen = brcOpen.nextToken)
- {
- if(brcOpen is TokenEnd)
- {
- ErrorMsg(decl, "missing {");
- return null;
- }
- }
- Token body = brcOpen.nextToken;
- // Stick old partial definition's extends/implementeds list just
- // in front of new partial definition's extends/implementeds list.
- //
- // class oldextimp { oldbody } ...
- // dupDecl dupBrcOpen dupDecl.endToken
- //
- // class newextimp { newbody } ...
- // decl brcOpen body decl.endToken
- //
- // becomes
- //
- // class ...
- // dupDecl
- // dupDecl.endToken
- //
- // class oldextimp newextimp { oldbody newbody } ...
- // decl brcOpen body decl.endToken
- if(dupBrcOpen != dupDecl.nextToken)
- {
- dupBrcOpen.prevToken.nextToken = decl.nextToken;
- dupDecl.nextToken.prevToken = decl;
- decl.nextToken.prevToken = dupBrcOpen.prevToken;
- decl.nextToken = dupDecl.nextToken;
- }
- // Stick old partial definition's body just
- // in front of new partial definition's body.
- if(dupBrcOpen.nextToken != dupDecl.endToken)
- {
- dupBrcOpen.nextToken.prevToken = brcOpen;
- dupDecl.endToken.prevToken.nextToken = body;
- body.prevToken = dupDecl.endToken.prevToken;
- brcOpen.nextToken = dupBrcOpen.nextToken;
- }
- // Null out old definition's extends/implementeds list and body
- // by having the declaration token be the only thing left.
- dupDecl.nextToken = dupDecl.endToken.nextToken;
- dupDecl.nextToken.prevToken = dupDecl;
- dupDecl.endToken = dupDecl;
- return body;
- }
- /**
- * @brief Determine whether or not the generic parameters of two class declarations match exactly.
- */
- private static bool GenericParametersMatch(TokenDeclSDType c1, TokenDeclSDType c2)
- {
- if((c1.genParams == null) && (c2.genParams == null))
- return true;
- if((c1.genParams == null) || (c2.genParams == null))
- return false;
- Dictionary<string, int> gp1 = c1.genParams;
- Dictionary<string, int> gp2 = c2.genParams;
- if(gp1.Count != gp2.Count)
- return false;
- foreach(KeyValuePair<string, int> kvp1 in gp1)
- {
- if (!gp2.TryGetValue(kvp1.Key, out int v2))
- return false;
- if (v2 != kvp1.Value)
- return false;
- }
- return true;
- }
- /**
- * @brief Replace all TokenName tokens that refer to the script-defined types with
- * corresponding TokenTypeSDType{Class,Delegate,GenParam,Interface} tokens.
- * Also handle generic references, ie, recognize that 'List<integer>' is an
- * instantiation of 'List<>' and instantiate the generic.
- */
- private const uint REPEAT_NOTYPE = 1;
- private const uint REPEAT_INSTGEN = 2;
- private const uint REPEAT_SUBST = 4;
- private void ParseSDTypePreScanPassTwo(Token tokenBegin)
- {
- List<Token> noTypes = new();
- TokenDeclSDType outerSDType;
- uint repeat;
- do
- {
- repeat = 0;
- outerSDType = null;
- noTypes.Clear();
- for(Token t = tokenBegin; (t = t.nextToken) is not TokenEnd;)
- {
- // Maybe it's time to pop out of an outer class definition.
- if((outerSDType != null) && (outerSDType.endToken == t))
- {
- outerSDType = outerSDType.outerSDType;
- continue;
- }
- // Skip completely over any script-defined generic prototypes.
- // We only need to process their instantiations which are non-
- // generic versions of the generics.
- if((t is TokenDeclSDType ttype) && ttype.genParams != null)
- {
- t = ttype.endToken;
- continue;
- }
- // Check for beginning of non-generic script-defined type definitions.
- // They can have nested definitions in their innerSDTypes[] that match
- // name tokens, so add them to the stack.
- //
- // But just ignore any preliminary partial definitions as they have had
- // their entire contents spliced out and spliced into a subsequent partial
- // definition. So if we originally had:
- // partial class Abc { public intenger one; }
- // partial class Abc { public intenger two; }
- // We now have:
- // partial_class_Abc <== if we are here, just ignore the partial_class_Abc token
- // partial_class_Abc { public intenger one; public intenger two; }
- if(t is TokenDeclSDType ttype2)
- {
- if(ttype2.endToken != t)
- outerSDType = (TokenDeclSDType)t;
- continue;
- }
- // For names not preceded by a '.', scan the script-defined type definition
- // stack for that name. Splice the name out and replace with equivalent token.
- if((t is TokenName) && t.prevToken is not TokenKwDot)
- t = TrySpliceTypeRef(t, outerSDType, ref repeat, noTypes);
- // This handles types such as integer[,][], List<string>[], etc.
- // They are an instantiation of an internally generated type of the same name, brackets and all.
- // Note that to malloc an array, use something like 'new float[,][](3,5)', not 'new float[3,5][]'.
- //
- // Note that we must not get confused by $idxprop property declarations such as:
- // float [string kee] { get { ... } }
- // ... and try to convert 'float' '[' to an array type.
- if((t is TokenType) && (t.nextToken is TokenKwBrkOpen))
- {
- if((t.nextToken.nextToken is TokenKwBrkClose) ||
- (t.nextToken.nextToken is TokenKwComma))
- {
- t = InstantiateJaggedArray(t, tokenBegin, ref repeat);
- }
- }
- }
- // If we instantiated a generic, loop back to process its contents
- // just as if the source code had the instantiated code to begin with.
- // Also repeat if we found a non-type inside the <> of a generic reference
- // provided we have made at least one name->type substitution.
- } while(((repeat & REPEAT_INSTGEN) != 0) ||
- ((repeat & (REPEAT_NOTYPE | REPEAT_SUBST)) == (REPEAT_NOTYPE | REPEAT_SUBST)));
- // These are places where we required a type be present,
- // eg, a generic type argument or the body of a typedef.
- foreach(Token t in noTypes)
- ErrorMsg(t, "looking for type");
- }
- /**
- * @brief Try to convert the source token string to a type reference
- * and splice the type reference into the source token string
- * replacing the original token(s).
- * @param t = points to the initial TokenName token
- * @param outerSDType = null: this is a top-level code reference
- * else: this code is within outerSDType
- * @returns pointer to last token parsed
- * possibly with spliced-in type token
- * repeat = possibly set true if need to do another pass
- */
- private Token TrySpliceTypeRef(Token t, TokenDeclSDType outerSDType, ref uint repeat, List<Token> noTypes)
- {
- Token start = t;
- string tnamestr = ((TokenName)t).val;
- // Look for the name as a type declared by outerSDType or anything
- // even farther out than that. If not found, simply return
- // without updating t, meaning that t isn't the name of a type.
- TokenDeclSDType decl = null;
- while(outerSDType != null)
- {
- if(outerSDType.innerSDTypes.TryGetValue(tnamestr, out decl))
- break;
- outerSDType = outerSDType.outerSDType;
- }
- if((outerSDType == null) && !tokenScript.sdSrcTypesTryGetValue(tnamestr, out decl))
- return t;
- TokenDeclSDType instdecl;
- while(true)
- {
- // If it is a generic type, it must be followed by instantiation arguments.
- instdecl = decl;
- if(decl.genParams != null)
- {
- t = t.nextToken;
- if(t is not TokenKwCmpLT)
- {
- ErrorMsg(t, "expecting < for generic argument list");
- return t;
- }
- tnamestr += "<";
- int nArgs = decl.genParams.Count;
- TokenType[] genArgs = new TokenType[nArgs];
- for(int i = 0; i < nArgs;)
- {
- t = t.nextToken;
- if(t is not TokenType)
- {
- repeat |= REPEAT_NOTYPE;
- noTypes.Add(t);
- return t.prevToken; // make sure name gets processed
- // so substitution can occur on it
- }
- TokenType ga = (TokenType)t;
- genArgs[i] = ga;
- tnamestr += ga.ToString();
- t = t.nextToken;
- if(++i < nArgs)
- {
- if(t is not TokenKwComma)
- {
- ErrorMsg(t, "expecting , for more generic arguments");
- return t;
- }
- tnamestr += ",";
- }
- }
- if(t is TokenKwRSh)
- { // idiot >>
- Token u = new TokenKwCmpGT(t);
- Token v = new TokenKwCmpGT(t);
- v.posn++;
- u.prevToken = t.prevToken;
- u.nextToken = v;
- v.nextToken = t.nextToken;
- v.prevToken = u;
- u.prevToken.nextToken = u;
- v.nextToken.prevToken = v;
- t = u;
- }
- if(t is not TokenKwCmpGT)
- {
- ErrorMsg(t, "expecting > at end of generic argument list");
- return t;
- }
- tnamestr += ">";
- if(outerSDType != null)
- {
- outerSDType.innerSDTypes.TryGetValue(tnamestr, out instdecl);
- }
- else
- {
- tokenScript.sdSrcTypesTryGetValue(tnamestr, out instdecl);
- }
- // Couldn't find 'List<string>' but found 'List' and we have genArgs = 'string'.
- // Instantiate the generic to create 'List<string>'. This splices the definition
- // of 'List<string>' into the source token stream just as if it had been there all
- // along. We have to then repeat the scan to process the instance's contents.
- if(instdecl == null)
- {
- instdecl = decl.InstantiateGeneric(tnamestr, genArgs, this);
- CatalogSDTypeDecl(instdecl);
- repeat |= REPEAT_INSTGEN;
- }
- }
- // Maybe caller wants a subtype by putting a '.' following all that.
- if(t.nextToken is not TokenKwDot)
- break;
- if(t.nextToken.nextToken is not TokenName nextnextname)
- break;
- tnamestr = nextnextname.val;
- if(!instdecl.innerSDTypes.TryGetValue(tnamestr, out decl))
- break;
- t = t.nextToken.nextToken;
- outerSDType = instdecl;
- }
- // Create a reference in the source to the definition
- // that encapsulates the long dotted type name given in
- // the source, and replace the long dotted type name in
- // the source with the reference token, eg, replace
- // 'Dictionary' '<' 'string' ',' 'integer' '>' '.' 'ValueList'
- // with 'Dictionary<string,integer>.ValueList'.
- TokenType refer = instdecl.MakeRefToken(start);
- if(refer == null)
- {
- // typedef body is not yet a type
- noTypes.Add(start);
- repeat |= REPEAT_NOTYPE;
- return start;
- }
- refer.prevToken = start.prevToken; // start points right at the first TokenName
- refer.nextToken = t.nextToken; // t points at the last TokenName or TokenKwCmpGT
- refer.prevToken.nextToken = refer;
- refer.nextToken.prevToken = refer;
- repeat |= REPEAT_SUBST;
- return refer;
- }
- /**
- * @brief We are known to have <type>'[' so make an equivalent array type.
- * @param t = points to the TokenType
- * @param tokenBegin = where we can safely splice in new array class definitions
- * @param repeat = set REPEAT_INSTGEN if new type created
- * @returns pointer to last token parsed
- * possibly with spliced-in type token
- * repeat = possibly set true if need to do another pass
- */
- private Token InstantiateJaggedArray(Token t, Token tokenBegin, ref uint repeat)
- {
- Token start = t;
- TokenType ofType = (TokenType)t;
- Stack<int> ranks = new();
- // When script specifies 'float[,][]' it means a two-dimensional matrix
- // that points to one-dimensional vectors of floats. So we would push
- // a 2 then a 1 in this parsing code...
- do
- {
- t = t.nextToken; // point at '['
- int rank = 0;
- do
- {
- rank++; // count '[' and ','s
- t = t.nextToken; // point at ',' or ']'
- } while(t is TokenKwComma);
- if(t is not TokenKwBrkClose)
- {
- ErrorMsg(t, "expecting only [ , or ] for array type specification");
- return t;
- }
- ranks.Push(rank);
- } while(t.nextToken is TokenKwBrkOpen);
- // Now we build the types in reverse order. For the example above we will:
- // first, create a type that is a one-dimensional vector of floats, float[]
- // second, create a type that is a two-dimensional matrix of that.
- // This keeps declaration and referencing similar, eg,
- // float[,][] jag = new float[,][] (3,4);
- // jag[i,j][k] ... is used to access the elements
- do
- {
- int rank = ranks.Pop();
- TokenDeclSDType decl = InstantiateFixedArray(rank, ofType, tokenBegin, ref repeat);
- ofType = decl.MakeRefToken(ofType);
- } while(ranks.Count > 0);
- // Finally splice in the resultant array type to replace the original tokens.
- ofType.prevToken = start.prevToken;
- ofType.nextToken = t.nextToken;
- ofType.prevToken.nextToken = ofType;
- ofType.nextToken.prevToken = ofType;
- // Resume parsing just after the spliced-in array type token.
- return ofType;
- }
- /**
- * @brief Instantiate a script-defined class type to handle fixed-dimension arrays.
- * @param rank = number of dimensions for the array
- * @param ofType = type of each element of the array
- * @returns script-defined class declaration created to handle the array
- */
- private TokenDeclSDType InstantiateFixedArray(int rank, TokenType ofType, Token tokenBegin, ref uint repeat)
- {
- // Create the array type's name.
- // If starting with a non-array type, just append the rank to it, eg, float + rank=1 -> float[]
- // If starting with an array type, slip this rank in front of existing array, eg, float[] + rank=2 -> float[,][].
- // This makes it consistent with what the script-writer sees for both a type specification and when
- // referencing elements in a jagged array.
- string name = ofType.ToString();
- StringBuilder sb = new(name);
- int ix = name.IndexOf('[');
- if(ix < 0)
- ix = name.Length;
- sb.Insert(ix++, '[');
- for(int i = 0; ++i < rank;)
- {
- sb.Insert(ix++, ',');
- }
- sb.Insert(ix, ']');
- name = sb.ToString();
- TokenDeclSDType fa;
- if(!tokenScript.sdSrcTypesTryGetValue(name, out fa))
- {
- char suffix = 'O';
- if(ofType is TokenTypeChar)
- suffix = 'C';
- if(ofType is TokenTypeFloat)
- suffix = 'F';
- if(ofType is TokenTypeInt)
- suffix = 'I';
- // Don't already have one, create a new skeleton struct.
- // Splice in a definition for the class at beginning of source file.
- //
- // class <arraytypename> {
- fa = new TokenDeclSDTypeClass(new TokenName(tokenScript, name), false);
- CatalogSDTypeDecl(fa);
- repeat |= REPEAT_INSTGEN;
- ((TokenDeclSDTypeClass)fa).arrayOfType = ofType;
- ((TokenDeclSDTypeClass)fa).arrayOfRank = rank;
- Token t = SpliceAfter(tokenBegin, fa);
- t = SpliceAfter(t, new TokenKwBrcOpen(t));
- // public integer len0;
- // public integer len1;
- // ...
- // public object obj;
- for(int i = 0; i < rank; i++)
- {
- t = SpliceAfter(t, new TokenKwPublic(t));
- t = SpliceAfter(t, new TokenTypeInt(t));
- t = SpliceAfter(t, new TokenName(t, "len" + i));
- t = SpliceAfter(t, new TokenKwSemi(t));
- }
- t = SpliceAfter(t, new TokenKwPublic(t));
- t = SpliceAfter(t, new TokenTypeObject(t));
- t = SpliceAfter(t, new TokenName(t, "obj"));
- t = SpliceAfter(t, new TokenKwSemi(t));
- // public constructor (integer len0, integer len1, ...) {
- // this.len0 = len0;
- // this.len1 = len1;
- // ...
- // this.obj = xmrFixedArrayAlloc<suffix> (len0 * len1 * ...);
- // }
- t = SpliceAfter(t, new TokenKwPublic(t));
- t = SpliceAfter(t, new TokenKwConstructor(t));
- t = SpliceAfter(t, new TokenKwParOpen(t));
- for(int i = 0; i < rank; i++)
- {
- if(i > 0)
- t = SpliceAfter(t, new TokenKwComma(t));
- t = SpliceAfter(t, new TokenTypeInt(t));
- t = SpliceAfter(t, new TokenName(t, "len" + i));
- }
- t = SpliceAfter(t, new TokenKwParClose(t));
- t = SpliceAfter(t, new TokenKwBrcOpen(t));
- for(int i = 0; i < rank; i++)
- {
- t = SpliceAfter(t, new TokenKwThis(t));
- t = SpliceAfter(t, new TokenKwDot(t));
- t = SpliceAfter(t, new TokenName(t, "len" + i));
- t = SpliceAfter(t, new TokenKwAssign(t));
- t = SpliceAfter(t, new TokenName(t, "len" + i));
- t = SpliceAfter(t, new TokenKwSemi(t));
- }
- t = SpliceAfter(t, new TokenKwThis(t));
- t = SpliceAfter(t, new TokenKwDot(t));
- t = SpliceAfter(t, new TokenName(t, "obj"));
- t = SpliceAfter(t, new TokenKwAssign(t));
- t = SpliceAfter(t, new TokenName(t, "xmrFixedArrayAlloc" + suffix));
- t = SpliceAfter(t, new TokenKwParOpen(t));
- for(int i = 0; i < rank; i++)
- {
- if(i > 0)
- t = SpliceAfter(t, new TokenKwMul(t));
- t = SpliceAfter(t, new TokenName(t, "len" + i));
- }
- t = SpliceAfter(t, new TokenKwParClose(t));
- t = SpliceAfter(t, new TokenKwSemi(t));
- t = SpliceAfter(t, new TokenKwBrcClose(t));
- // public integer Length { get {
- // return this.len0 * this.len1 * ... ;
- // } }
- t = SpliceAfter(t, new TokenKwPublic(t));
- t = SpliceAfter(t, new TokenTypeInt(t));
- t = SpliceAfter(t, new TokenName(t, "Length"));
- t = SpliceAfter(t, new TokenKwBrcOpen(t));
- t = SpliceAfter(t, new TokenKwGet(t));
- t = SpliceAfter(t, new TokenKwBrcOpen(t));
- t = SpliceAfter(t, new TokenKwRet(t));
- for(int i = 0; i < rank; i++)
- {
- if(i > 0)
- t = SpliceAfter(t, new TokenKwMul(t));
- t = SpliceAfter(t, new TokenKwThis(t));
- t = SpliceAfter(t, new TokenKwDot(t));
- t = SpliceAfter(t, new TokenName(t, "len" + i));
- }
- t = SpliceAfter(t, new TokenKwSemi(t));
- t = SpliceAfter(t, new TokenKwBrcClose(t));
- t = SpliceAfter(t, new TokenKwBrcClose(t));
- // public integer Length (integer dim) {
- // switch (dim) {
- // case 0: return this.len0;
- // case 1: return this.len1;
- // ...
- // }
- // return 0;
- // }
- t = SpliceAfter(t, new TokenKwPublic(t));
- t = SpliceAfter(t, new TokenTypeInt(t));
- t = SpliceAfter(t, new TokenName(t, "Length"));
- t = SpliceAfter(t, new TokenKwParOpen(t));
- t = SpliceAfter(t, new TokenTypeInt(t));
- t = SpliceAfter(t, new TokenName(t, "dim"));
- t = SpliceAfter(t, new TokenKwParClose(t));
- t = SpliceAfter(t, new TokenKwBrcOpen(t));
- t = SpliceAfter(t, new TokenKwSwitch(t));
- t = SpliceAfter(t, new TokenKwParOpen(t));
- t = SpliceAfter(t, new TokenName(t, "dim"));
- t = SpliceAfter(t, new TokenKwParClose(t));
- t = SpliceAfter(t, new TokenKwBrcOpen(t));
- for(int i = 0; i < rank; i++)
- {
- t = SpliceAfter(t, new TokenKwCase(t));
- t = SpliceAfter(t, new TokenInt(t, i));
- t = SpliceAfter(t, new TokenKwColon(t));
- t = SpliceAfter(t, new TokenKwRet(t));
- t = SpliceAfter(t, new TokenKwThis(t));
- t = SpliceAfter(t, new TokenKwDot(t));
- t = SpliceAfter(t, new TokenName(t, "len" + i));
- t = SpliceAfter(t, new TokenKwSemi(t));
- }
- t = SpliceAfter(t, new TokenKwBrcClose(t));
- t = SpliceAfter(t, new TokenKwRet(t));
- t = SpliceAfter(t, new TokenInt(t, 0));
- t = SpliceAfter(t, new TokenKwSemi(t));
- t = SpliceAfter(t, new TokenKwBrcClose(t));
- // public integer Index (integer idx0, integet idx1, ...) {
- // integer idx = idx0;
- // idx *= this.len1; idx += idx1;
- // idx *= this.len2; idx += idx2;
- // ...
- // return idx;
- // }
- t = SpliceAfter(t, new TokenKwPublic(t));
- t = SpliceAfter(t, new TokenTypeInt(t));
- t = SpliceAfter(t, new TokenName(t, "Index"));
- t = SpliceAfter(t, new TokenKwParOpen(t));
- for(int i = 0; i < rank; i++)
- {
- if(i > 0)
- t = SpliceAfter(t, new TokenKwComma(t));
- t = SpliceAfter(t, new TokenTypeInt(t));
- t = SpliceAfter(t, new TokenName(t, "idx" + i));
- }
- t = SpliceAfter(t, new TokenKwParClose(t));
- t = SpliceAfter(t, new TokenKwBrcOpen(t));
- t = SpliceAfter(t, new TokenTypeInt(t));
- t = SpliceAfter(t, new TokenName(t, "idx"));
- t = SpliceAfter(t, new TokenKwAssign(t));
- t = SpliceAfter(t, new TokenName(t, "idx0"));
- t = SpliceAfter(t, new TokenKwSemi(t));
- for(int i = 1; i < rank; i++)
- {
- t = SpliceAfter(t, new TokenName(t, "idx"));
- t = SpliceAfter(t, new TokenKwAsnMul(t));
- t = SpliceAfter(t, new TokenKwThis(t));
- t = SpliceAfter(t, new TokenKwDot(t));
- t = SpliceAfter(t, new TokenName(t, "len" + i));
- t = SpliceAfter(t, new TokenKwSemi(t));
- t = SpliceAfter(t, new TokenName(t, "idx"));
- t = SpliceAfter(t, new TokenKwAsnAdd(t));
- t = SpliceAfter(t, new TokenName(t, "idx" + i));
- t = SpliceAfter(t, new TokenKwSemi(t));
- }
- t = SpliceAfter(t, new TokenKwRet(t));
- t = SpliceAfter(t, new TokenName(t, "idx"));
- t = SpliceAfter(t, new TokenKwSemi(t));
- t = SpliceAfter(t, new TokenKwBrcClose(t));
- // public <oftype> Get (integer idx0, integet idx1, ...) {
- // integer idx = idx0;
- // idx *= this.len1; idx += idx1;
- // idx *= this.len2; idx += idx2;
- // ...
- // return (<oftype>) xmrFixedArrayGet<suffix> (this.obj, idx);
- // }
- t = SpliceAfter(t, new TokenKwPublic(t));
- t = SpliceAfter(t, ofType.CopyToken(t));
- t = SpliceAfter(t, new TokenName(t, "Get"));
- t = SpliceAfter(t, new TokenKwParOpen(t));
- for(int i = 0; i < rank; i++)
- {
- if(i > 0)
- t = SpliceAfter(t, new TokenKwComma(t));
- t = SpliceAfter(t, new TokenTypeInt(t));
- t = SpliceAfter(t, new TokenName(t, "idx" + i));
- }
- t = SpliceAfter(t, new TokenKwParClose(t));
- t = SpliceAfter(t, new TokenKwBrcOpen(t));
- t = SpliceAfter(t, new TokenTypeInt(t));
- t = SpliceAfter(t, new TokenName(t, "idx"));
- t = SpliceAfter(t, new TokenKwAssign(t));
- t = SpliceAfter(t, new TokenName(t, "idx0"));
- t = SpliceAfter(t, new TokenKwSemi(t));
- for(int i = 1; i < rank; i++)
- {
- t = SpliceAfter(t, new TokenName(t, "idx"));
- t = SpliceAfter(t, new TokenKwAsnMul(t));
- t = SpliceAfter(t, new TokenKwThis(t));
- t = SpliceAfter(t, new TokenKwDot(t));
- t = SpliceAfter(t, new TokenName(t, "len" + i));
- t = SpliceAfter(t, new TokenKwSemi(t));
- t = SpliceAfter(t, new TokenName(t, "idx"));
- t = SpliceAfter(t, new TokenKwAsnAdd(t));
- t = SpliceAfter(t, new TokenName(t, "idx" + i));
- t = SpliceAfter(t, new TokenKwSemi(t));
- }
- t = SpliceAfter(t, new TokenKwRet(t));
- if(suffix == 'O')
- {
- t = SpliceAfter(t, new TokenKwParOpen(t));
- t = SpliceAfter(t, ofType.CopyToken(t));
- t = SpliceAfter(t, new TokenKwParClose(t));
- }
- t = SpliceAfter(t, new TokenName(t, "xmrFixedArrayGet" + suffix));
- t = SpliceAfter(t, new TokenKwParOpen(t));
- t = SpliceAfter(t, new TokenKwThis(t));
- t = SpliceAfter(t, new TokenKwDot(t));
- t = SpliceAfter(t, new TokenName(t, "obj"));
- t = SpliceAfter(t, new TokenKwComma(t));
- t = SpliceAfter(t, new TokenName(t, "idx"));
- t = SpliceAfter(t, new TokenKwParClose(t));
- t = SpliceAfter(t, new TokenKwSemi(t));
- t = SpliceAfter(t, new TokenKwBrcClose(t));
- // public void Set (integer idx0, integer idx1, ..., <oftype> val) {
- // integer idx = idx0;
- // idx *= this.len1; idx += idx1;
- // idx *= this.len2; idx += idx2;
- // ...
- // xmrFixedArraySet<suffix> (this.obj, idx, val);
- // }
- t = SpliceAfter(t, new TokenKwPublic(t));
- t = SpliceAfter(t, new TokenTypeVoid(t));
- t = SpliceAfter(t, new TokenName(t, "Set"));
- t = SpliceAfter(t, new TokenKwParOpen(t));
- for(int i = 0; i < rank; i++)
- {
- t = SpliceAfter(t, new TokenTypeInt(t));
- t = SpliceAfter(t, new TokenName(t, "idx" + i));
- t = SpliceAfter(t, new TokenKwComma(t));
- }
- t = SpliceAfter(t, ofType.CopyToken(t));
- t = SpliceAfter(t, new TokenName(t, "val"));
- t = SpliceAfter(t, new TokenKwParClose(t));
- t = SpliceAfter(t, new TokenKwBrcOpen(t));
- t = SpliceAfter(t, new TokenTypeInt(t));
- t = SpliceAfter(t, new TokenName(t, "idx"));
- t = SpliceAfter(t, new TokenKwAssign(t));
- t = SpliceAfter(t, new TokenName(t, "idx0"));
- t = SpliceAfter(t, new TokenKwSemi(t));
- for(int i = 1; i < rank; i++)
- {
- t = SpliceAfter(t, new TokenName(t, "idx"));
- t = SpliceAfter(t, new TokenKwAsnMul(t));
- t = SpliceAfter(t, new TokenKwThis(t));
- t = SpliceAfter(t, new TokenKwDot(t));
- t = SpliceAfter(t, new TokenName(t, "len" + i));
- t = SpliceAfter(t, new TokenKwSemi(t));
- t = SpliceAfter(t, new TokenName(t, "idx"));
- t = SpliceAfter(t, new TokenKwAsnAdd(t));
- t = SpliceAfter(t, new TokenName(t, "idx" + i));
- t = SpliceAfter(t, new TokenKwSemi(t));
- }
- t = SpliceAfter(t, new TokenName(t, "xmrFixedArraySet" + suffix));
- t = SpliceAfter(t, new TokenKwParOpen(t));
- t = SpliceAfter(t, new TokenKwThis(t));
- t = SpliceAfter(t, new TokenKwDot(t));
- t = SpliceAfter(t, new TokenName(t, "obj"));
- t = SpliceAfter(t, new TokenKwComma(t));
- t = SpliceAfter(t, new TokenName(t, "idx"));
- t = SpliceAfter(t, new TokenKwComma(t));
- t = SpliceAfter(t, new TokenName(t, "val"));
- t = SpliceAfter(t, new TokenKwParClose(t));
- t = SpliceAfter(t, new TokenKwSemi(t));
- t = SpliceAfter(t, new TokenKwBrcClose(t));
- t = SpliceAfter(t, new TokenKwBrcClose(t));
- }
- return fa;
- }
- private Token SpliceAfter(Token before, Token after)
- {
- after.nextToken = before.nextToken;
- after.prevToken = before;
- before.nextToken = after;
- after.nextToken.prevToken = after;
- return after;
- }
- /**
- * @brief Parse script-defined type declarations.
- * @param token = points to possible script-defined type keyword
- * @param outerSDType = null: top-level type
- * else: sub-type of this type
- * @param flags = access level (SDT_{PRIVATE,PROTECTED,PUBLIC})
- * @returns true: something defined; else: not a sd type def
- */
- private bool ParseDeclSDTypes(ref Token token, TokenDeclSDType outerSDType, uint flags)
- {
- if(token is not TokenDeclSDType)
- return false;
- TokenDeclSDType decl = (TokenDeclSDType)token;
- /*
- * If declaration of generic type, skip it.
- * The instantiations get parsed (ie, when we know the concrete types)
- * below because they appear as non-generic types.
- */
- if(decl.genParams != null)
- {
- token = decl.endToken.nextToken;
- return true;
- }
- /*
- * Also skip over any typedefs. They were all processed in
- * ParseSDTypePreScanPassTwo().
- */
- if(decl is TokenDeclSDTypeTypedef)
- {
- token = decl.endToken.nextToken;
- return true;
- }
- /*
- * Non-generic types get parsed inline because we know all their types.
- */
- if(decl is TokenDeclSDTypeClass)
- {
- ParseDeclClass(ref token, outerSDType, flags);
- return true;
- }
- if(decl is TokenDeclSDTypeDelegate)
- {
- ParseDeclDelegate(ref token, outerSDType, flags);
- return true;
- }
- if(decl is TokenDeclSDTypeInterface)
- {
- ParseDeclInterface(ref token, outerSDType, flags);
- return true;
- }
- throw new Exception("unhandled token " + token.GetType().ToString());
- }
- /**
- * @brief Parse a class declaration.
- * @param token = points to TokenDeclSDTypeClass token
- * points just past closing '}' on return
- * @param outerSDType = null: this is a top-level class
- * else: this class is being defined inside this type
- * @param flags = SDT_{PRIVATE,PROTECTED,PUBLIC}
- */
- private void ParseDeclClass(ref Token token, TokenDeclSDType outerSDType, uint flags)
- {
- bool haveExplicitConstructor = false;
- Token u = token;
- TokenDeclSDTypeClass tokdeclcl;
- tokdeclcl = (TokenDeclSDTypeClass)u;
- tokdeclcl.outerSDType = outerSDType;
- tokdeclcl.accessLevel = flags;
- u = u.nextToken;
- // maybe it is a partial class that had its body snipped out
- // by a later partial class declaration of the same class
- if(tokdeclcl.endToken == tokdeclcl)
- {
- token = u;
- return;
- }
- // make this class the currently compiled class
- // used for retrieving stuff like 'this' possibly
- // in field initialization code
- TokenDeclSDType saveCurSDType = currentDeclSDType;
- currentDeclSDType = tokdeclcl;
- // next can be ':' followed by list of implemented
- // interfaces and one extended class
- if(u is TokenKwColon)
- {
- u = u.nextToken;
- while(true)
- {
- if(u is TokenTypeSDTypeClass uc)
- {
- TokenDeclSDTypeClass c = uc.decl;
- if(tokdeclcl.extends == null)
- {
- tokdeclcl.extends = c;
- }
- else if(tokdeclcl.extends != c)
- {
- ErrorMsg(u, "can extend from only one class");
- }
- }
- else if(u is TokenTypeSDTypeInterface iu)
- {
- TokenDeclSDTypeInterface i = iu.decl;
- i.AddToClassDecl(tokdeclcl);
- }
- else
- {
- ErrorMsg(u, "expecting class or interface name");
- if(u is TokenKwBrcOpen)
- break;
- }
- u = u.nextToken;
- // allow : in case it is spliced from multiple partial class definitions
- if(u is not TokenKwComma && u is not TokenKwColon)
- break;
- u = u.nextToken;
- }
- }
- // next must be '{' to open class declaration body
- if(u is not TokenKwBrcOpen)
- {
- ErrorMsg(u, "expecting { to open class declaration body");
- token = SkipPastSemi(token);
- goto ret;
- }
- token = u.nextToken;
- // push a var frame to put all the class members in
- tokdeclcl.members.thisClass = tokdeclcl;
- tokenScript.PushVarFrame(tokdeclcl.members);
- // Create a function $instfieldnit to hold all explicit
- // instance field initializations.
- TokenDeclVar ifiFunc = new(tokdeclcl, null, tokenScript);
- ifiFunc.name = new TokenName(ifiFunc, "$instfieldinit");
- ifiFunc.retType = new TokenTypeVoid(ifiFunc);
- ifiFunc.argDecl = new TokenArgDecl(ifiFunc);
- ifiFunc.sdtClass = tokdeclcl;
- ifiFunc.sdtFlags = SDT_PUBLIC | SDT_NEW;
- TokenStmtBlock ifiBody = new(ifiFunc)
- {
- function = ifiFunc
- };
- ifiFunc.body = ifiBody;
- tokdeclcl.instFieldInit = ifiFunc;
- tokenScript.AddVarEntry(ifiFunc);
- // Create a function $staticfieldnit to hold all explicit
- // static field initializations.
- TokenDeclVar sfiFunc = new(tokdeclcl, null, tokenScript);
- sfiFunc.name = new TokenName(sfiFunc, "$staticfieldinit");
- sfiFunc.retType = new TokenTypeVoid(sfiFunc);
- sfiFunc.argDecl = new TokenArgDecl(sfiFunc);
- sfiFunc.sdtClass = tokdeclcl;
- sfiFunc.sdtFlags = SDT_PUBLIC | SDT_STATIC | SDT_NEW;
- TokenStmtBlock sfiBody = new(sfiFunc)
- {
- function = sfiFunc
- };
- sfiFunc.body = sfiBody;
- tokdeclcl.staticFieldInit = sfiFunc;
- tokenScript.AddVarEntry(sfiFunc);
- // process declaration statements until '}'
- while(!(token is TokenKwBrcClose))
- {
- if(token is TokenKwSemi)
- {
- token = token.nextToken;
- continue;
- }
- // Check for all qualifiers.
- // typedef has an implied 'public' qualifier.
- flags = SDT_PUBLIC;
- if(!(token is TokenDeclSDTypeTypedef))
- {
- flags = ParseQualifierFlags(ref token);
- }
- // Parse nested script-defined type definitions.
- if(ParseDeclSDTypes(ref token, tokdeclcl, flags))
- continue;
- // constant <name> = <rval> ;
- if(token is TokenKwConst)
- {
- if((flags & (SDT_ABSTRACT | SDT_NEW | SDT_OVERRIDE | SDT_VIRTUAL)) != 0)
- {
- ErrorMsg(token, "cannot have abstract, new, override or virtual field");
- }
- TokenDeclVar var = ParseDeclVar(ref token, null);
- if(var != null)
- {
- var.sdtClass = tokdeclcl;
- var.sdtFlags = flags | SDT_STATIC;
- }
- continue;
- }
- // <type> <name> ;
- // <type> <name> = <rval> ;
- if((token is TokenType) &&
- (token.nextToken is TokenName) &&
- ((token.nextToken.nextToken is TokenKwSemi) ||
- (token.nextToken.nextToken is TokenKwAssign)))
- {
- if((flags & (SDT_ABSTRACT | SDT_FINAL | SDT_NEW | SDT_OVERRIDE | SDT_VIRTUAL)) != 0)
- {
- ErrorMsg(token, "cannot have abstract, final, new, override or virtual field");
- }
- TokenDeclVar var = ParseDeclVar(ref token, ifiFunc);
- if(var != null)
- {
- var.sdtClass = tokdeclcl;
- var.sdtFlags = flags;
- if((flags & SDT_STATIC) != 0)
- {
- // <type>.<name> = <init>;
- TokenLValSField left = new(var.init)
- {
- baseType = tokdeclcl.MakeRefToken(var),
- fieldName = var.name
- };
- DoVarInit(sfiFunc, left, var.init);
- }
- else if(var.init != null)
- {
- // this.<name> = <init>;
- TokenLValIField left = new(var.init)
- {
- baseRVal = new TokenRValThis(var.init, tokdeclcl),
- fieldName = var.name
- };
- DoVarInit(ifiFunc, left, var.init);
- }
- }
- continue;
- }
- // <type> <name> [ : <implintfs> ] { [ get { <body> } ] [ set { <body> } ] }
- // <type> '[' ... ']' [ : <implintfs> ] { [ get { <body> } ] [ set { <body> } ] }
- bool prop = (token is TokenType) &&
- (token.nextToken is TokenName) &&
- (token.nextToken.nextToken is TokenKwBrcOpen ||
- token.nextToken.nextToken is TokenKwColon);
- prop |= (token is TokenType) && (token.nextToken is TokenKwBrkOpen);
- if(prop)
- {
- TokenDeclVar var = ParseProperty(ref token, (flags & SDT_ABSTRACT) != 0, true);
- if(var != null)
- {
- var.sdtClass = tokdeclcl;
- var.sdtFlags = flags;
- if(var.getProp != null)
- {
- var.getProp.sdtClass = tokdeclcl;
- var.getProp.sdtFlags = flags;
- }
- if(var.setProp != null)
- {
- var.setProp.sdtClass = tokdeclcl;
- var.setProp.sdtFlags = flags;
- }
- }
- continue;
- }
- // 'constructor' '(' arglist ')' [ ':' [ 'base' ] '(' baseconstructorcall ')' ] '{' body '}'
- if(token is TokenKwConstructor)
- {
- ParseSDTClassCtorDecl(ref token, flags, tokdeclcl);
- haveExplicitConstructor = true;
- continue;
- }
- // <type> <name> <funcargs> <funcbody>
- // method with explicit return type
- if(token is TokenType)
- {
- ParseSDTClassMethodDecl(ref token, flags, tokdeclcl);
- continue;
- }
- // <name> <funcargs> <funcbody>
- // method returning void
- if((token is TokenName) || (token is TokenKw kwt && kwt.sdtClassOp))
- {
- ParseSDTClassMethodDecl(ref token, flags, tokdeclcl);
- continue;
- }
- // That's all we support in a class declaration.
- ErrorMsg(token, "expecting field or method declaration");
- token = SkipPastSemi(token);
- }
- // If script didn't specify any constructor, create a default no-argument one.
- if(!haveExplicitConstructor)
- {
- TokenDeclVar tokenDeclFunc = new(token, null, tokenScript)
- {
- name = new TokenName(token, "$ctor"),
- retType = new TokenTypeVoid(token),
- argDecl = new TokenArgDecl(token),
- sdtClass = tokdeclcl,
- sdtFlags = SDT_PUBLIC | SDT_NEW,
- body = new TokenStmtBlock(token)
- };
- tokenDeclFunc.body.function = tokenDeclFunc;
- if(tokdeclcl.extends != null)
- {
- SetUpDefaultBaseCtorCall(tokenDeclFunc);
- }
- else
- {
- // default constructor that doesn't do anything is trivial
- tokenDeclFunc.triviality = Triviality.trivial;
- }
- tokenScript.AddVarEntry(tokenDeclFunc);
- }
- // Skip over the closing brace and pop corresponding var frame.
- token = token.nextToken;
- tokenScript.PopVarFrame();
- ret:
- currentDeclSDType = saveCurSDType;
- }
- /**
- * @brief Parse out abstract/override/private/protected/public/static/virtual keywords.
- * @param token = first token to evaluate
- * @returns flags found; token = unprocessed token
- */
- private Dictionary<uint, Token> foundFlags = new();
- private uint ParseQualifierFlags(ref Token token)
- {
- foundFlags.Clear();
- while(true)
- {
- if(token is TokenKwPrivate)
- {
- token = AddQualifierFlag(token, SDT_PRIVATE, SDT_PROTECTED | SDT_PUBLIC);
- continue;
- }
- if(token is TokenKwProtected)
- {
- token = AddQualifierFlag(token, SDT_PROTECTED, SDT_PRIVATE | SDT_PUBLIC);
- continue;
- }
- if(token is TokenKwPublic)
- {
- token = AddQualifierFlag(token, SDT_PUBLIC, SDT_PRIVATE | SDT_PROTECTED);
- continue;
- }
- if(token is TokenKwAbstract)
- {
- token = AddQualifierFlag(token, SDT_ABSTRACT, SDT_FINAL | SDT_STATIC | SDT_VIRTUAL);
- continue;
- }
- if(token is TokenKwFinal)
- {
- token = AddQualifierFlag(token, SDT_FINAL, SDT_ABSTRACT | SDT_VIRTUAL);
- continue;
- }
- if(token is TokenKwNew)
- {
- token = AddQualifierFlag(token, SDT_NEW, SDT_OVERRIDE);
- continue;
- }
- if(token is TokenKwOverride)
- {
- token = AddQualifierFlag(token, SDT_OVERRIDE, SDT_NEW | SDT_STATIC);
- continue;
- }
- if(token is TokenKwStatic)
- {
- token = AddQualifierFlag(token, SDT_STATIC, SDT_ABSTRACT | SDT_OVERRIDE | SDT_VIRTUAL);
- continue;
- }
- if(token is TokenKwVirtual)
- {
- token = AddQualifierFlag(token, SDT_VIRTUAL, SDT_ABSTRACT | SDT_STATIC);
- continue;
- }
- break;
- }
- uint flags = 0;
- foreach(uint flag in foundFlags.Keys)
- flags |= flag;
- if((flags & (SDT_PRIVATE | SDT_PROTECTED | SDT_PUBLIC)) == 0)
- ErrorMsg(token, "must specify exactly one of private, protected or public");
- return flags;
- }
- private Token AddQualifierFlag(Token token, uint add, uint confs)
- {
- while(confs != 0)
- {
- uint conf = (uint)(confs & -confs);
- if (foundFlags.TryGetValue(conf, out Token confToken))
- {
- ErrorMsg(token, "conflicts with " + confToken.ToString());
- }
- confs -= conf;
- }
- foundFlags[add] = token;
- return token.nextToken;
- }
- /**
- * @brief Parse a property declaration.
- * @param token = points to the property type token on entry
- * points just past the closing brace on return
- * @param abs = true: property is abstract
- * false: property is concrete
- * @param imp = allow implemented interface specs
- * @returns null: parse failure
- * else: property
- *
- * <type> <name> [ : <implintfs> ] { [ get { <body> } ] [ set { <body> } ] }
- * <type> '[' ... ']' [ : <implintfs> ] { [ get { <body> } ] [ set { <body> } ] }
- */
- private TokenDeclVar ParseProperty(ref Token token, bool abs, bool imp)
- {
- // Parse out the property's type and name.
- // <type> <name>
- TokenType type = (TokenType)token;
- TokenName name;
- TokenArgDecl args;
- Token argTokens = null;
- token = token.nextToken;
- if(token is TokenKwBrkOpen)
- {
- argTokens = token;
- name = new TokenName(token, "$idxprop");
- args = ParseFuncArgs(ref token, typeof(TokenKwBrkClose));
- }
- else
- {
- name = (TokenName)token;
- token = token.nextToken;
- args = new TokenArgDecl(token);
- }
- // Maybe it claims to implement some interface properties.
- // [ ':' <ifacetype>[.<propname>] ',' ... ]
- TokenIntfImpl implements = null;
- if(token is TokenKwColon)
- {
- implements = ParseImplements(ref token, name);
- if(implements == null)
- return null;
- if(!imp)
- {
- ErrorMsg(token, "cannot implement interface property");
- }
- }
- // Should have an opening brace.
- if(token is not TokenKwBrcOpen)
- {
- ErrorMsg(token, "expect { to open property definition");
- token = SkipPastSemi(token);
- return null;
- }
- token = token.nextToken;
- // Parse out the getter and/or setter.
- // 'get' { <body> | ';' }
- // 'set' { <body> | ';' }
- TokenDeclVar getFunc = null;
- TokenDeclVar setFunc = null;
- while(token is not TokenKwBrcClose)
- {
- // Maybe create a getter function.
- if(token is TokenKwGet)
- {
- getFunc = new TokenDeclVar(token, null, tokenScript)
- {
- name = new TokenName(token, name.val + "$get"),
- retType = type,
- argDecl = args,
- implements = MakePropertyImplements(implements, "$get")
- };
- token = token.nextToken;
- if(!ParseFunctionBody(ref token, getFunc, abs))
- {
- getFunc = null;
- }
- else if(!tokenScript.AddVarEntry(getFunc))
- {
- ErrorMsg(getFunc, "duplicate getter");
- }
- continue;
- }
- // Maybe create a setter function.
- if(token is TokenKwSet)
- {
- TokenArgDecl argDecl = args;
- if(getFunc != null)
- {
- argDecl = (argTokens == null) ? new TokenArgDecl(token) :
- ParseFuncArgs(ref argTokens, typeof(TokenKwBrkClose));
- }
- argDecl.AddArg(type, new TokenName(token, "value"));
- setFunc = new TokenDeclVar(token, null, tokenScript)
- {
- name = new TokenName(token, name.val + "$set"),
- retType = new TokenTypeVoid(token),
- argDecl = argDecl,
- implements = MakePropertyImplements(implements, "$set")
- };
- token = token.nextToken;
- if(!ParseFunctionBody(ref token, setFunc, abs))
- {
- setFunc = null;
- }
- else if(!tokenScript.AddVarEntry(setFunc))
- {
- ErrorMsg(setFunc, "duplicate setter");
- }
- continue;
- }
- ErrorMsg(token, "expecting get or set");
- token = SkipPastSemi(token);
- return null;
- }
- token = token.nextToken;
- if((getFunc == null) && (setFunc == null))
- {
- ErrorMsg(name, "must specify at least one of get, set");
- return null;
- }
- // Set up a variable for the property.
- TokenDeclVar tokenDeclVar = new(name, null, tokenScript)
- {
- type = type,
- name = name,
- getProp = getFunc,
- setProp = setFunc
- };
- // Can't be same name already in block.
- if (!tokenScript.AddVarEntry(tokenDeclVar))
- {
- ErrorMsg(tokenDeclVar, "duplicate member " + name.val);
- return null;
- }
- return tokenDeclVar;
- }
- /**
- * @brief Given a list of implemented interface methods, create a similar list with suffix added to all the names
- * @param implements = original list of implemented interface methods
- * @param suffix = string to be added to end of implemented interface method names
- * @returns list similar to implements with suffix added to end of implemented interface method names
- */
- private TokenIntfImpl MakePropertyImplements(TokenIntfImpl implements, string suffix)
- {
- TokenIntfImpl gsimpls = null;
- for(TokenIntfImpl impl = implements; impl != null; impl = (TokenIntfImpl)impl.nextToken)
- {
- TokenIntfImpl gsimpl = new(impl.intfType, new TokenName(impl.methName, impl.methName.val + suffix))
- {
- nextToken = gsimpls
- };
- gsimpls = gsimpl;
- }
- return gsimpls;
- }
- /**
- * @brief Parse a constructor definition for a script-defined type class.
- * @param token = points to 'constructor' keyword
- * @param flags = abstract/override/static/virtual flags
- * @param tokdeclcl = which script-defined type class this method is in
- * @returns with method parsed and cataloged (or error message(s) printed)
- */
- private void ParseSDTClassCtorDecl(ref Token token, uint flags, TokenDeclSDTypeClass tokdeclcl)
- {
- if((flags & (SDT_ABSTRACT | SDT_OVERRIDE | SDT_STATIC | SDT_VIRTUAL)) != 0)
- {
- ErrorMsg(token, "cannot have abstract, override, static or virtual constructor");
- }
- TokenDeclVar tokenDeclFunc = new(token, null, tokenScript);
- tokenDeclFunc.name = new TokenName(tokenDeclFunc, "$ctor");
- tokenDeclFunc.retType = new TokenTypeVoid(token);
- tokenDeclFunc.sdtClass = tokdeclcl;
- tokenDeclFunc.sdtFlags = flags | SDT_NEW;
- token = token.nextToken;
- if(token is not TokenKwParOpen)
- {
- ErrorMsg(token, "expecting ( for constructor argument list");
- token = SkipPastSemi(token);
- return;
- }
- tokenDeclFunc.argDecl = ParseFuncArgs(ref token, typeof(TokenKwParClose));
- if(tokenDeclFunc.argDecl == null)
- return;
- TokenDeclVar saveDeclFunc = currentDeclFunc;
- currentDeclFunc = tokenDeclFunc;
- tokenScript.PushVarFrame(tokenDeclFunc.argDecl.varDict);
- try
- {
- // Set up reference to base constructor.
- TokenLValBaseField baseCtor = new(token, new TokenName(token, "$ctor"), tokdeclcl);
- // Parse any base constructor call as if it were the first statement of the
- // constructor itself.
- if(token is TokenKwColon)
- {
- token = token.nextToken;
- if(token is TokenKwBase)
- {
- token = token.nextToken;
- }
- if(token is not TokenKwParOpen)
- {
- ErrorMsg(token, "expecting ( for base constructor call arguments");
- token = SkipPastSemi(token);
- return;
- }
- TokenRValCall rvc = ParseRValCall(ref token, baseCtor);
- if(rvc == null)
- return;
- if(tokdeclcl.extends != null)
- {
- tokenDeclFunc.baseCtorCall = rvc;
- tokenDeclFunc.unknownTrivialityCalls.AddLast(rvc);
- }
- else
- {
- ErrorMsg(rvc, "base constructor call cannot be specified if not extending anything");
- }
- }
- else if(tokdeclcl.extends != null)
- {
- // Caller didn't specify a constructor but we are extending, so we will
- // call the extended class's default constructor.
- SetUpDefaultBaseCtorCall(tokenDeclFunc);
- }
- // Parse the constructor body.
- tokenDeclFunc.body = ParseStmtBlock(ref token);
- if(tokenDeclFunc.body == null)
- return;
- if(tokenDeclFunc.argDecl == null)
- return;
- }
- finally
- {
- tokenScript.PopVarFrame();
- currentDeclFunc = saveDeclFunc;
- }
- // Add to list of methods defined by this class.
- // It has the name "$ctor(argsig)".
- if(!tokenScript.AddVarEntry(tokenDeclFunc))
- {
- ErrorMsg(tokenDeclFunc, "duplicate constructor definition");
- }
- }
- /**
- * @brief Set up a call from a constructor to its default base constructor.
- */
- private void SetUpDefaultBaseCtorCall(TokenDeclVar thisCtor)
- {
- TokenLValBaseField baseCtor = new(thisCtor, new TokenName(thisCtor, "$ctor"), (TokenDeclSDTypeClass)thisCtor.sdtClass);
- TokenRValCall rvc = new(thisCtor)
- {
- meth = baseCtor
- };
- thisCtor.baseCtorCall = rvc;
- thisCtor.unknownTrivialityCalls.AddLast(rvc);
- }
- /**
- * @brief Parse a method definition for a script-defined type class.
- * @param token = points to return type (or method name for implicit return type of void)
- * @param flags = abstract/override/static/virtual flags
- * @param tokdeclcl = which script-defined type class this method is in
- * @returns with method parsed and cataloged (or error message(s) printed)
- */
- private void ParseSDTClassMethodDecl(ref Token token, uint flags, TokenDeclSDTypeClass tokdeclcl)
- {
- TokenDeclVar tokenDeclFunc = ParseDeclFunc(ref token,
- (flags & SDT_ABSTRACT) != 0,
- (flags & SDT_STATIC) == 0,
- (flags & SDT_STATIC) == 0);
- if(tokenDeclFunc != null)
- {
- tokenDeclFunc.sdtClass = tokdeclcl;
- tokenDeclFunc.sdtFlags = flags;
- if(!tokenScript.AddVarEntry(tokenDeclFunc))
- {
- string funcNameSig = tokenDeclFunc.funcNameSig.val;
- ErrorMsg(tokenDeclFunc.funcNameSig, "duplicate method name " + funcNameSig);
- }
- }
- }
- /**
- * @brief Parse a delegate declaration statement.
- * @param token = points to TokenDeclSDTypeDelegate token on entry
- * points just past ';' on return
- * @param outerSDType = null: this is a top-level delegate
- * else: this delegate is being defined inside this type
- * @param flags = SDT_{PRIVATE,PROTECTED,PUBLIC}
- */
- private void ParseDeclDelegate(ref Token token, TokenDeclSDType outerSDType, uint flags)
- {
- Token u = token;
- TokenDeclSDTypeDelegate tokdecldel;
- TokenType retType;
- tokdecldel = (TokenDeclSDTypeDelegate)u;
- tokdecldel.outerSDType = outerSDType;
- tokdecldel.accessLevel = flags;
- // first thing following that should be return type
- // but we will fill in 'void' if it is missing
- u = u.nextToken;
- if(u is TokenType tu)
- {
- retType = tu;
- u = u.nextToken;
- }
- else
- {
- retType = new TokenTypeVoid(u);
- }
- // get list of argument types until we see a ')'
- List<TokenType> args = new();
- bool first = true;
- do
- {
- if(first)
- {
- // first time should have '(' ')' or '(' <type>
- if(u is not TokenKwParOpen)
- {
- ErrorMsg(u, "expecting ( after delegate name");
- token = SkipPastSemi(token);
- return;
- }
- first = false;
- u = u.nextToken;
- if(u is TokenKwParClose)
- break;
- }
- else
- {
- // other times should have ',' <type>
- if(u is not TokenKwComma)
- {
- ErrorMsg(u, "expecting , separating arg types");
- token = SkipPastSemi(token);
- return;
- }
- u = u.nextToken;
- }
- if(u is not TokenType)
- {
- ErrorMsg(u, "expecting argument type");
- token = SkipPastSemi(token);
- return;
- }
- args.Add((TokenType)u);
- u = u.nextToken;
- // they can put in a dummy name that we toss out
- if(u is TokenName)
- u = u.nextToken;
- // scanning ends on a ')'
- } while(u is not TokenKwParClose);
- // fill in the return type and argment type array
- tokdecldel.SetRetArgTypes(retType, args.ToArray());
- // and finally must have ';' to finish the delegate declaration statement
- u = u.nextToken;
- if(u is not TokenKwSemi)
- {
- ErrorMsg(u, "expecting ; after ) in delegate");
- token = SkipPastSemi(token);
- return;
- }
- token = u.nextToken;
- }
- /**
- * @brief Parse an interface declaration.
- * @param token = points to TokenDeclSDTypeInterface token on entry
- * points just past closing '}' on return
- * @param outerSDType = null: this is a top-level interface
- * else: this interface is being defined inside this type
- * @param flags = SDT_{PRIVATE,PROTECTED,PUBLIC}
- */
- private void ParseDeclInterface(ref Token token, TokenDeclSDType outerSDType, uint flags)
- {
- Token u = token;
- TokenDeclSDTypeInterface tokdeclin;
- tokdeclin = (TokenDeclSDTypeInterface)u;
- tokdeclin.outerSDType = outerSDType;
- tokdeclin.accessLevel = flags;
- u = u.nextToken;
- // next can be ':' followed by list of implemented interfaces
- if(u is TokenKwColon)
- {
- u = u.nextToken;
- while(true)
- {
- if(u is TokenTypeSDTypeInterface iu)
- {
- TokenDeclSDTypeInterface i = iu.decl;
- if(!tokdeclin.implements.Contains(i))
- {
- tokdeclin.implements.Add(i);
- }
- }
- else
- {
- ErrorMsg(u, "expecting interface name");
- if(u is TokenKwBrcOpen)
- break;
- }
- u = u.nextToken;
- if(u is not TokenKwComma)
- break;
- u = u.nextToken;
- }
- }
- // next must be '{' to open interface declaration body
- if(u is not TokenKwBrcOpen)
- {
- ErrorMsg(u, "expecting { to open interface declaration body");
- token = SkipPastSemi(token);
- return;
- }
- token = u.nextToken;
- // start a var definition frame to collect the interface members
- tokenScript.PushVarFrame(false);
- tokdeclin.methsNProps = tokenScript.variablesStack;
- // process declaration statements until '}'
- while(token is not TokenKwBrcClose)
- {
- if(token is TokenKwSemi)
- {
- token = token.nextToken;
- continue;
- }
- // Parse nested script-defined type definitions.
- if(ParseDeclSDTypes(ref token, tokdeclin, SDT_PUBLIC))
- continue;
- // <type> <name> <funcargs> ;
- // abstract method with explicit return type
- if((token is TokenType) &&
- (token.nextToken is TokenName) &&
- (token.nextToken.nextToken is TokenKwParOpen))
- {
- Token name = token.nextToken;
- TokenDeclVar tokenDeclFunc = ParseDeclFunc(ref token, true, false, false);
- if(tokenDeclFunc == null)
- continue;
- if(!tokenScript.AddVarEntry(tokenDeclFunc))
- {
- ErrorMsg(name, "duplicate method name");
- continue;
- }
- continue;
- }
- // <name> <funcargs> ;
- // abstract method returning void
- if((token is TokenName) &&
- (token.nextToken is TokenKwParOpen))
- {
- Token name = token;
- TokenDeclVar tokenDeclFunc = ParseDeclFunc(ref token, true, false, false);
- if(tokenDeclFunc == null)
- continue;
- if(!tokenScript.AddVarEntry(tokenDeclFunc))
- {
- ErrorMsg(name, "duplicate method name");
- }
- continue;
- }
- // <type> <name> { [ get ; ] [ set ; ] }
- // <type> '[' ... ']' { [ get ; ] [ set ; ] }
- // abstract property
- bool prop = (token is TokenType) &&
- (token.nextToken is TokenName) &&
- (token.nextToken.nextToken is TokenKwBrcOpen ||
- token.nextToken.nextToken is TokenKwColon);
- prop |= (token is TokenType) && (token.nextToken is TokenKwBrkOpen);
- if(prop)
- {
- ParseProperty(ref token, true, false);
- continue;
- }
- // That's all we support in an interface declaration.
- ErrorMsg(token, "expecting method or property prototype");
- token = SkipPastSemi(token);
- }
- // Skip over the closing brace and pop the corresponding var frame.
- token = token.nextToken;
- tokenScript.PopVarFrame();
- }
- /**
- * @brief parse state body (including all its event handlers)
- * @param token = points to TokenKwBrcOpen
- * @returns null: state body parse error
- * else: token representing state
- * token = points past close brace
- */
- private TokenStateBody ParseStateBody(ref Token token)
- {
- TokenStateBody tokenStateBody = new(token);
- if(token is not TokenKwBrcOpen)
- {
- ErrorMsg(token, "expecting { at beg of state");
- token = SkipPastSemi(token);
- return null;
- }
- token = token.nextToken;
- while(token is not TokenKwBrcClose)
- {
- if(token is TokenEnd)
- {
- ErrorMsg(tokenStateBody, "eof parsing state body");
- return null;
- }
- TokenDeclVar tokenDeclFunc = ParseDeclFunc(ref token, false, false, false);
- if(tokenDeclFunc == null)
- return null;
- if(tokenDeclFunc.retType is not TokenTypeVoid)
- {
- ErrorMsg(tokenDeclFunc.retType, "event handlers don't have return types");
- return null;
- }
- tokenDeclFunc.nextToken = tokenStateBody.eventFuncs;
- tokenStateBody.eventFuncs = tokenDeclFunc;
- }
- token = token.nextToken;
- return tokenStateBody;
- }
- /**
- * @brief Parse a function declaration, including its arg list and body
- * @param token = points to function return type token (or function name token if return type void)
- * @param abs = false: concrete function; true: abstract declaration
- * @param imp = allow implemented interface specs
- * @param ops = accept operators (==, +, etc) for function name
- * @returns null: error parsing function definition
- * else: function declaration
- * token = advanced just past function, ie, just past the closing brace
- */
- private TokenDeclVar ParseDeclFunc(ref Token token, bool abs, bool imp, bool ops)
- {
- if(token is TokenType retType)
- token = token.nextToken;
- else
- retType = new TokenTypeVoid(token);
- TokenName simpleName;
- if((token is TokenKw Kwt) && Kwt.sdtClassOp)
- {
- if(!ops)
- ErrorMsg(token, "operator functions disallowed in static contexts");
- simpleName = new TokenName(token, "$op" + token.ToString());
- }
- else if(token is not TokenName tname)
- {
- ErrorMsg(token, "expecting function name");
- token = SkipPastSemi(token);
- return null;
- }
- else
- {
- simpleName = tname;
- }
- token = token.nextToken;
- return ParseDeclFunc(ref token, abs, imp, retType, simpleName);
- }
- /**
- * @brief Parse a function declaration, including its arg list and body
- * This version enters with token pointing to the '(' at beginning of arg list
- * @param token = points to the '(' of the arg list
- * @param abs = false: concrete function; true: abstract declaration
- * @param imp = allow implemented interface specs
- * @param retType = return type (TokenTypeVoid if void, never null)
- * @param simpleName = function name without any signature
- * @returns null: error parsing remainder of function definition
- * else: function declaration
- * token = advanced just past function, ie, just past the closing brace
- */
- private TokenDeclVar ParseDeclFunc(ref Token token, bool abs, bool imp, TokenType retType, TokenName simpleName)
- {
- TokenDeclVar tokenDeclFunc = new(simpleName, null, tokenScript)
- {
- name = simpleName,
- retType = retType,
- argDecl = ParseFuncArgs(ref token, typeof(TokenKwParClose))
- };
- if (tokenDeclFunc.argDecl == null)
- return null;
- if(token is TokenKwColon)
- {
- tokenDeclFunc.implements = ParseImplements(ref token, simpleName);
- if(tokenDeclFunc.implements == null)
- return null;
- if(!imp)
- {
- ErrorMsg(tokenDeclFunc.implements, "cannot implement interface method");
- tokenDeclFunc.implements = null;
- }
- }
- if(!ParseFunctionBody(ref token, tokenDeclFunc, abs))
- return null;
- if(tokenDeclFunc.argDecl == null)
- return null;
- return tokenDeclFunc;
- }
- /**
- * @brief Parse interface implementation list.
- * @param token = points to ':' on entry
- * points just past list on return
- * @param simpleName = simple name (no arg signature) of method/property that
- * is implementing the interface method/property
- * @returns list of implemented interface methods/properties
- */
- private TokenIntfImpl ParseImplements(ref Token token, TokenName simpleName)
- {
- TokenIntfImpl implements = null;
- do
- {
- token = token.nextToken;
- if(token is not TokenTypeSDTypeInterface)
- {
- ErrorMsg(token, "expecting interface type");
- token = SkipPastSemi(token);
- return null;
- }
- TokenTypeSDTypeInterface intfType = (TokenTypeSDTypeInterface)token;
- token = token.nextToken;
- TokenName methName = simpleName;
- if((token is TokenKwDot) && (token.nextToken is TokenName tname))
- {
- methName = tname;
- token = token.nextToken.nextToken;
- }
- TokenIntfImpl intfImpl = new(intfType, methName)
- {
- nextToken = implements
- };
- implements = intfImpl;
- } while(token is TokenKwComma);
- return implements;
- }
- /**
- * @brief Parse function declaration's body
- * @param token = points to body, ie, ';' or '{'
- * @param tokenDeclFunc = function being declared
- * @param abs = false: concrete function; true: abstract declaration
- * @returns whether or not the function definition parsed correctly
- */
- private bool ParseFunctionBody(ref Token token, TokenDeclVar tokenDeclFunc, bool abs)
- {
- if(token is TokenKwSemi)
- {
- if(!abs)
- {
- ErrorMsg(token, "concrete function must have body");
- token = SkipPastSemi(token);
- return false;
- }
- token = token.nextToken;
- return true;
- }
- // Declare this function as being the one currently being processed
- // for anything that cares. We also start a variable frame that
- // includes all the declared parameters.
- TokenDeclVar saveDeclFunc = currentDeclFunc;
- currentDeclFunc = tokenDeclFunc;
- tokenScript.PushVarFrame(tokenDeclFunc.argDecl.varDict);
- // Now parse the function statement block.
- tokenDeclFunc.body = ParseStmtBlock(ref token);
- // Pop the var frame that contains the arguments.
- tokenScript.PopVarFrame();
- currentDeclFunc = saveDeclFunc;
- // Check final errors.
- if(tokenDeclFunc.body == null)
- return false;
- if(abs)
- {
- ErrorMsg(tokenDeclFunc.body, "abstract function must not have body");
- tokenDeclFunc.body = null;
- return false;
- }
- return true;
- }
- /**
- * @brief Parse statement
- * @param token = first token of statement
- * @returns null: parse error
- * else: token representing whole statement
- * token = points past statement
- */
- private TokenStmt ParseStmt(ref Token token)
- {
- // Statements that begin with a specific keyword.
- if(token is TokenKwAt)
- return ParseStmtLabel(ref token);
- if(token is TokenKwBrcOpen)
- return ParseStmtBlock(ref token);
- if(token is TokenKwBreak)
- return ParseStmtBreak(ref token);
- if(token is TokenKwCont)
- return ParseStmtCont(ref token);
- if(token is TokenKwDo)
- return ParseStmtDo(ref token);
- if(token is TokenKwFor)
- return ParseStmtFor(ref token);
- if(token is TokenKwForEach)
- return ParseStmtForEach(ref token);
- if(token is TokenKwIf)
- return ParseStmtIf(ref token);
- if(token is TokenKwJump)
- return ParseStmtJump(ref token);
- if(token is TokenKwRet)
- return ParseStmtRet(ref token);
- if(token is TokenKwSemi)
- return ParseStmtNull(ref token);
- if(token is TokenKwState)
- return ParseStmtState(ref token);
- if(token is TokenKwSwitch)
- return ParseStmtSwitch(ref token);
- if(token is TokenKwThrow)
- return ParseStmtThrow(ref token);
- if(token is TokenKwTry)
- return ParseStmtTry(ref token);
- if(token is TokenKwWhile)
- return ParseStmtWhile(ref token);
- // Try to parse anything else as an expression, possibly calling
- // something and/or writing to a variable.
- TokenRVal tokenRVal = ParseRVal(ref token, semiOnly);
- if(tokenRVal != null)
- {
- TokenStmtRVal tokenStmtRVal = new(tokenRVal) { rVal = tokenRVal };
- return tokenStmtRVal;
- }
- // Who knows what it is...
- ErrorMsg(token, "unknown statement");
- token = SkipPastSemi(token);
- return null;
- }
- /**
- * @brief parse a statement block, ie, group of statements between braces
- * @param token = points to { token
- * @returns null: error parsing
- * else: statements bundled in this token
- * token = advanced just past the } token
- */
- private TokenStmtBlock ParseStmtBlock(ref Token token)
- {
- if(token is not TokenKwBrcOpen)
- {
- ErrorMsg(token, "statement block body must begin with a {");
- token = SkipPastSemi(token);
- return null;
- }
- TokenStmtBlock tokenStmtBlock = new(token)
- {
- function = currentDeclFunc,
- outerStmtBlock = currentStmtBlock
- };
- currentStmtBlock = tokenStmtBlock;
- VarDict outerVariablesStack = tokenScript.variablesStack;
- try
- {
- Token prevStmt = null;
- token = token.nextToken;
- while(token is not TokenKwBrcClose)
- {
- if(token is TokenEnd)
- {
- ErrorMsg(tokenStmtBlock, "missing }");
- return null;
- }
- Token thisStmt;
- if(((token is TokenType) && (token.nextToken is TokenName)) ||
- (token is TokenKwConst))
- {
- thisStmt = ParseDeclVar(ref token, null);
- }
- else
- {
- thisStmt = ParseStmt(ref token);
- }
- if(thisStmt == null)
- return null;
- if(prevStmt == null)
- tokenStmtBlock.statements = thisStmt;
- else
- prevStmt.nextToken = thisStmt;
- prevStmt = thisStmt;
- }
- token = token.nextToken;
- }
- finally
- {
- tokenScript.variablesStack = outerVariablesStack;
- currentStmtBlock = tokenStmtBlock.outerStmtBlock;
- }
- return tokenStmtBlock;
- }
- /**
- * @brief parse a 'break' statement
- * @param token = points to break keyword token
- * @returns null: error parsing
- * else: statements bundled in this token
- * token = advanced just past the ; token
- */
- private TokenStmtBreak ParseStmtBreak(ref Token token)
- {
- TokenStmtBreak tokenStmtBreak = new(token);
- token = token.nextToken;
- if(token is not TokenKwSemi)
- {
- ErrorMsg(token, "expecting ;");
- token = SkipPastSemi(token);
- return null;
- }
- token = token.nextToken;
- return tokenStmtBreak;
- }
- /**
- * @brief parse a 'continue' statement
- * @param token = points to continue keyword token
- * @returns null: error parsing
- * else: statements bundled in this token
- * token = advanced just past the ; token
- */
- private TokenStmtCont ParseStmtCont(ref Token token)
- {
- TokenStmtCont tokenStmtCont = new(token);
- token = token.nextToken;
- if(token is not TokenKwSemi)
- {
- ErrorMsg(token, "expecting ;");
- token = SkipPastSemi(token);
- return null;
- }
- token = token.nextToken;
- return tokenStmtCont;
- }
- /**
- * @brief parse a 'do' statement
- * @params token = points to 'do' keyword token
- * @returns null: parse error
- * else: pointer to token encapsulating the do statement, including body
- * token = advanced just past the body statement
- */
- private TokenStmtDo ParseStmtDo(ref Token token)
- {
- currentDeclFunc.triviality = Triviality.complex;
- TokenStmtDo tokenStmtDo = new(token);
- token = token.nextToken;
- tokenStmtDo.bodyStmt = ParseStmt(ref token);
- if(tokenStmtDo.bodyStmt == null)
- return null;
- if(token is not TokenKwWhile)
- {
- ErrorMsg(token, "expecting while clause");
- return null;
- }
- token = token.nextToken;
- tokenStmtDo.testRVal = ParseRValParen(ref token);
- if(tokenStmtDo.testRVal == null)
- return null;
- if(token is not TokenKwSemi)
- {
- ErrorMsg(token, "while clause must terminate on semicolon");
- token = SkipPastSemi(token);
- return null;
- }
- token = token.nextToken;
- return tokenStmtDo;
- }
- /**
- * @brief parse a for statement
- * @param token = points to 'for' keyword token
- * @returns null: parse error
- * else: pointer to encapsulated for statement token
- * token = advanced just past for body statement
- */
- private TokenStmt ParseStmtFor(ref Token token)
- {
- currentDeclFunc.triviality = Triviality.complex;
- // Create encapsulating token and skip past 'for ('
- TokenStmtFor tokenStmtFor = new(token);
- token = token.nextToken;
- if(token is not TokenKwParOpen)
- {
- ErrorMsg(token, "for must be followed by (");
- return null;
- }
- token = token.nextToken;
- // If a plain for, ie, not declaring a variable, it's straightforward.
- if(token is not TokenType)
- {
- tokenStmtFor.initStmt = ParseStmt(ref token);
- if(tokenStmtFor.initStmt == null)
- return null;
- return ParseStmtFor2(tokenStmtFor, ref token) ? tokenStmtFor : null;
- }
- // Initialization declares a variable, so encapsulate it in a block so
- // variable has scope only in the for statement, including its body.
- TokenStmtBlock forStmtBlock = new(tokenStmtFor)
- {
- outerStmtBlock = currentStmtBlock,
- function = currentDeclFunc
- };
- currentStmtBlock = forStmtBlock;
- tokenScript.PushVarFrame(true);
- TokenDeclVar tokenDeclVar = ParseDeclVar(ref token, null);
- if(tokenDeclVar == null)
- {
- tokenScript.PopVarFrame();
- currentStmtBlock = forStmtBlock.outerStmtBlock;
- return null;
- }
- forStmtBlock.statements = tokenDeclVar;
- tokenDeclVar.nextToken = tokenStmtFor;
- bool ok = ParseStmtFor2(tokenStmtFor, ref token);
- tokenScript.PopVarFrame();
- currentStmtBlock = forStmtBlock.outerStmtBlock;
- return ok ? forStmtBlock : null;
- }
- /**
- * @brief parse rest of 'for' statement starting with the test expression.
- * @param tokenStmtFor = token encapsulating the for statement
- * @param token = points to test expression
- * @returns false: parse error
- * true: successful
- * token = points just past body statement
- */
- private bool ParseStmtFor2(TokenStmtFor tokenStmtFor, ref Token token)
- {
- if(token is TokenKwSemi)
- {
- token = token.nextToken;
- }
- else
- {
- tokenStmtFor.testRVal = ParseRVal(ref token, semiOnly);
- if(tokenStmtFor.testRVal == null)
- return false;
- }
- if(token is TokenKwParClose)
- {
- token = token.nextToken;
- }
- else
- {
- tokenStmtFor.incrRVal = ParseRVal(ref token, parCloseOnly);
- if(tokenStmtFor.incrRVal == null)
- return false;
- }
- tokenStmtFor.bodyStmt = ParseStmt(ref token);
- return tokenStmtFor.bodyStmt != null;
- }
- /**
- * @brief parse a foreach statement
- * @param token = points to 'foreach' keyword token
- * @returns null: parse error
- * else: pointer to encapsulated foreach statement token
- * token = advanced just past for body statement
- */
- private TokenStmt ParseStmtForEach(ref Token token)
- {
- currentDeclFunc.triviality = Triviality.complex;
- // Create encapsulating token and skip past 'foreach ('
- TokenStmtForEach tokenStmtForEach = new(token);
- token = token.nextToken;
- if(token is not TokenKwParOpen)
- {
- ErrorMsg(token, "foreach must be followed by (");
- return null;
- }
- token = token.nextToken;
- if(token is TokenName tname)
- {
- tokenStmtForEach.keyLVal = new TokenLValName(tname, tokenScript.variablesStack);
- token = token.nextToken;
- }
- if(token is not TokenKwComma)
- {
- ErrorMsg(token, "expecting comma");
- token = SkipPastSemi(token);
- return null;
- }
- token = token.nextToken;
- if(token is TokenName tname2)
- {
- tokenStmtForEach.valLVal = new TokenLValName(tname2, tokenScript.variablesStack);
- token = token.nextToken;
- }
- if(token is not TokenKwIn)
- {
- ErrorMsg(token, "expecting 'in'");
- token = SkipPastSemi(token);
- return null;
- }
- token = token.nextToken;
- tokenStmtForEach.arrayRVal = GetOperand(ref token);
- if(tokenStmtForEach.arrayRVal == null)
- return null;
- if(token is not TokenKwParClose)
- {
- ErrorMsg(token, "expecting )");
- token = SkipPastSemi(token);
- return null;
- }
- token = token.nextToken;
- tokenStmtForEach.bodyStmt = ParseStmt(ref token);
- if(tokenStmtForEach.bodyStmt == null)
- return null;
- return tokenStmtForEach;
- }
- private TokenStmtIf ParseStmtIf(ref Token token)
- {
- TokenStmtIf tokenStmtIf = new(token);
- token = token.nextToken;
- tokenStmtIf.testRVal = ParseRValParen(ref token);
- if(tokenStmtIf.testRVal == null)
- return null;
- tokenStmtIf.trueStmt = ParseStmt(ref token);
- if(tokenStmtIf.trueStmt == null)
- return null;
- if(token is TokenKwElse)
- {
- token = token.nextToken;
- tokenStmtIf.elseStmt = ParseStmt(ref token);
- if(tokenStmtIf.elseStmt == null)
- return null;
- }
- return tokenStmtIf;
- }
- private TokenStmtJump ParseStmtJump(ref Token token)
- {
- // Create jump statement token to encapsulate the whole statement.
- TokenStmtJump tokenStmtJump = new(token);
- token = token.nextToken;
- if(token is not TokenName || token.nextToken is not TokenKwSemi)
- {
- ErrorMsg(token, "expecting label;");
- token = SkipPastSemi(token);
- return null;
- }
- tokenStmtJump.label = (TokenName)token;
- token = token.nextToken.nextToken;
- // If label is already defined, it means this is a backward (looping)
- // jump, so remember the label has backward jump references.
- // We also then assume the function is complex, ie, it has a loop.
- if(currentDeclFunc.labels.ContainsKey(tokenStmtJump.label.val))
- {
- currentDeclFunc.labels[tokenStmtJump.label.val].hasBkwdRefs = true;
- currentDeclFunc.triviality = Triviality.complex;
- }
- return tokenStmtJump;
- }
- /**
- * @brief parse a jump target label statement
- * @param token = points to the '@' token
- * @returns null: error parsing
- * else: the label
- * token = advanced just past the ;
- */
- private TokenStmtLabel ParseStmtLabel(ref Token token)
- {
- if(token.nextToken is not TokenName tname || token.nextToken.nextToken is not TokenKwSemi)
- {
- ErrorMsg(token, "invalid label");
- token = SkipPastSemi(token);
- return null;
- }
- TokenStmtLabel stmtLabel = new(token)
- {
- name = tname,
- block = currentStmtBlock
- };
- if (currentDeclFunc.labels.ContainsKey(stmtLabel.name.val))
- {
- ErrorMsg(token.nextToken, "duplicate label");
- ErrorMsg(currentDeclFunc.labels[stmtLabel.name.val], "previously defined here");
- token = SkipPastSemi(token);
- return null;
- }
- currentDeclFunc.labels.Add(stmtLabel.name.val, stmtLabel);
- token = token.nextToken.nextToken.nextToken;
- return stmtLabel;
- }
- private TokenStmtNull ParseStmtNull(ref Token token)
- {
- TokenStmtNull tokenStmtNull = new(token);
- token = token.nextToken;
- return tokenStmtNull;
- }
- private TokenStmtRet ParseStmtRet(ref Token token)
- {
- TokenStmtRet tokenStmtRet = new(token);
- token = token.nextToken;
- if(token is TokenKwSemi)
- {
- token = token.nextToken;
- }
- else
- {
- tokenStmtRet.rVal = ParseRVal(ref token, semiOnly);
- if(tokenStmtRet.rVal == null)
- return null;
- }
- return tokenStmtRet;
- }
- private TokenStmtSwitch ParseStmtSwitch(ref Token token)
- {
- TokenStmtSwitch tokenStmtSwitch = new(token);
- token = token.nextToken;
- tokenStmtSwitch.testRVal = ParseRValParen(ref token);
- if(tokenStmtSwitch.testRVal == null)
- return null;
- if(token is not TokenKwBrcOpen)
- {
- ErrorMsg(token, "expecting open brace");
- token = SkipPastSemi(token);
- return null;
- }
- token = token.nextToken;
- TokenSwitchCase tokenSwitchCase = null;
- bool haveComplained = false;
- while(token is not TokenEnd && token is not TokenKwBrcClose)
- {
- if(token is TokenKwCase)
- {
- tokenSwitchCase = new TokenSwitchCase(token);
- if(tokenStmtSwitch.lastCase == null)
- {
- tokenStmtSwitch.cases = tokenSwitchCase;
- }
- else
- {
- tokenStmtSwitch.lastCase.nextCase = tokenSwitchCase;
- }
- tokenStmtSwitch.lastCase = tokenSwitchCase;
- token = token.nextToken;
- tokenSwitchCase.rVal1 = ParseRVal(ref token, colonOrDotDotDot);
- if(tokenSwitchCase.rVal1 == null)
- return null;
- if(token is TokenKwDotDotDot)
- {
- token = token.nextToken;
- tokenSwitchCase.rVal2 = ParseRVal(ref token, colonOnly);
- if(tokenSwitchCase.rVal2 == null)
- return null;
- }
- else
- {
- if(!(token is TokenKwColon))
- {
- ErrorMsg(token, "expecting : or ...");
- token = SkipPastSemi(token);
- return null;
- }
- token = token.nextToken;
- }
- }
- else if(token is TokenKwDefault)
- {
- tokenSwitchCase = new TokenSwitchCase(token);
- if(tokenStmtSwitch.lastCase == null)
- {
- tokenStmtSwitch.cases = tokenSwitchCase;
- }
- else
- {
- tokenStmtSwitch.lastCase.nextCase = tokenSwitchCase;
- }
- tokenStmtSwitch.lastCase = tokenSwitchCase;
- token = token.nextToken;
- if(!(token is TokenKwColon))
- {
- ErrorMsg(token, "expecting :");
- token = SkipPastSemi(token);
- return null;
- }
- token = token.nextToken;
- }
- else if(tokenSwitchCase != null)
- {
- TokenStmt bodyStmt = ParseStmt(ref token);
- if(bodyStmt == null)
- return null;
- if(tokenSwitchCase.lastStmt == null)
- {
- tokenSwitchCase.stmts = bodyStmt;
- }
- else
- {
- tokenSwitchCase.lastStmt.nextToken = bodyStmt;
- }
- tokenSwitchCase.lastStmt = bodyStmt;
- bodyStmt.nextToken = null;
- }
- else if(!haveComplained)
- {
- ErrorMsg(token, "expecting switch case or default");
- token = SkipPastSemi(token);
- haveComplained = true;
- }
- }
- if(tokenSwitchCase is null)
- ErrorMsg(token, "expecting switch case or default");
- token = token.nextToken;
- return tokenStmtSwitch;
- }
- private TokenStmtState ParseStmtState(ref Token token)
- {
- TokenStmtState tokenStmtState = new(token);
- token = token.nextToken;
- if((token is not TokenName && token is not TokenKwDefault) || token.nextToken is not TokenKwSemi)
- {
- ErrorMsg(token, "expecting state;");
- token = SkipPastSemi(token);
- return null;
- }
- if(token is TokenName tname)
- {
- tokenStmtState.state = tname;
- }
- token = token.nextToken.nextToken;
- return tokenStmtState;
- }
- private TokenStmtThrow ParseStmtThrow(ref Token token)
- {
- TokenStmtThrow tokenStmtThrow = new(token);
- token = token.nextToken;
- if(token is TokenKwSemi)
- {
- token = token.nextToken;
- }
- else
- {
- tokenStmtThrow.rVal = ParseRVal(ref token, semiOnly);
- if(tokenStmtThrow.rVal == null)
- return null;
- }
- return tokenStmtThrow;
- }
- /**
- * @brief Parse a try { ... } catch { ... } finally { ... } statement block
- * @param token = point to 'try' keyword on entry
- * points past last '}' processed on return
- * @returns encapsulated try/catch/finally or null if parsing error
- */
- private TokenStmtTry ParseStmtTry(ref Token token)
- {
- // Parse out the 'try { ... }' part
- Token tryKw = token;
- token = token.nextToken;
- TokenStmt body = ParseStmtBlock(ref token);
- while(true)
- {
- TokenStmtTry tokenStmtTry;
- if(token is TokenKwCatch)
- {
- if(token.nextToken is not TokenKwParOpen ||
- token.nextToken.nextToken is not TokenType ||
- token.nextToken.nextToken.nextToken is not TokenName ||
- token.nextToken.nextToken.nextToken.nextToken is not TokenKwParClose)
- {
- ErrorMsg(token, "catch must be followed by ( <type> <varname> ) { <statement>... }");
- return null;
- }
- token = token.nextToken.nextToken; // skip over 'catch' '('
- TokenDeclVar tag = new(token.nextToken, currentDeclFunc, tokenScript) { type = (TokenType)token };
- token = token.nextToken; // skip over <type>
- tag.name = (TokenName)token;
- token = token.nextToken.nextToken; // skip over <varname> ')'
- if((tag.type is not TokenTypeExc) && (tag.type is not TokenTypeStr))
- {
- ErrorMsg(tag.type, "must be type 'exception' or 'string'");
- }
- tokenStmtTry = new TokenStmtTry(tryKw)
- {
- tryStmt = WrapTryCatFinInBlock(body),
- catchVar = tag
- };
- tokenScript.PushVarFrame(false);
- tokenScript.AddVarEntry(tag);
- tokenStmtTry.catchStmt = ParseStmtBlock(ref token);
- tokenScript.PopVarFrame();
- if(tokenStmtTry.catchStmt == null)
- return null;
- tokenStmtTry.tryStmt.isTry = true;
- tokenStmtTry.tryStmt.tryStmt = tokenStmtTry;
- tokenStmtTry.catchStmt.isCatch = true;
- tokenStmtTry.catchStmt.tryStmt = tokenStmtTry;
- }
- else if(token is TokenKwFinally)
- {
- token = token.nextToken;
- tokenStmtTry = new TokenStmtTry(tryKw)
- {
- tryStmt = WrapTryCatFinInBlock(body),
- finallyStmt = ParseStmtBlock(ref token)
- };
- if (tokenStmtTry.finallyStmt == null)
- return null;
- tokenStmtTry.tryStmt.isTry = true;
- tokenStmtTry.tryStmt.tryStmt = tokenStmtTry;
- tokenStmtTry.finallyStmt.isFinally = true;
- tokenStmtTry.finallyStmt.tryStmt = tokenStmtTry;
- }
- else
- break;
- body = tokenStmtTry;
- }
- if(!(body is TokenStmtTry))
- {
- ErrorMsg(body, "try must have a matching catch and/or finally");
- return null;
- }
- return (TokenStmtTry)body;
- }
- /**
- * @brief Wrap a possible try/catch/finally statement block in a block statement.
- *
- * Given body = try { } catch (string s) { }
- *
- * we return { try { } catch (string s) { } }
- *
- * @param body = a TokenStmtTry or a TokenStmtBlock
- * @returns a TokenStmtBlock
- */
- private TokenStmtBlock WrapTryCatFinInBlock(TokenStmt body)
- {
- if(body is TokenStmtBlock tb)
- return tb;
- TokenStmtTry innerTry = (TokenStmtTry)body;
- TokenStmtBlock wrapper = new(body)
- {
- statements = innerTry,
- outerStmtBlock = currentStmtBlock,
- function = currentDeclFunc
- };
- innerTry.tryStmt.outerStmtBlock = wrapper;
- if(innerTry.catchStmt != null)
- innerTry.catchStmt.outerStmtBlock = wrapper;
- if(innerTry.finallyStmt != null)
- innerTry.finallyStmt.outerStmtBlock = wrapper;
- return wrapper;
- }
- private TokenStmtWhile ParseStmtWhile(ref Token token)
- {
- currentDeclFunc.triviality = Triviality.complex;
- TokenStmtWhile tokenStmtWhile = new(token);
- token = token.nextToken;
- tokenStmtWhile.testRVal = ParseRValParen(ref token);
- if(tokenStmtWhile.testRVal == null)
- return null;
- tokenStmtWhile.bodyStmt = ParseStmt(ref token);
- if(tokenStmtWhile.bodyStmt == null)
- return null;
- return tokenStmtWhile;
- }
- /**
- * @brief parse a variable declaration statement, including init value if any.
- * @param token = points to type or 'constant' token
- * @param initFunc = null: parsing a local var declaration
- * put initialization code in .init
- * else: parsing a global var or field var declaration
- * put initialization code in initFunc.body
- * @returns null: parsing error
- * else: variable declaration encapulating token
- * token = advanced just past semi-colon
- * variables = modified to include the new variable
- */
- private TokenDeclVar ParseDeclVar(ref Token token, TokenDeclVar initFunc)
- {
- TokenDeclVar tokenDeclVar = new(token.nextToken, currentDeclFunc, tokenScript);
- // Handle constant declaration.
- // It ends up in the declared variables list for the statement block just like
- // any other variable, except it has .constant = true.
- // The code generator will test that the initialization expression is constant.
- //
- // constant <name> = <value> ;
- if(token is TokenKwConst)
- {
- token = token.nextToken;
- if(token is not TokenName)
- {
- ErrorMsg(token, "expecting constant name");
- token = SkipPastSemi(token);
- return null;
- }
- tokenDeclVar.name = (TokenName)token;
- token = token.nextToken;
- if(token is not TokenKwAssign)
- {
- ErrorMsg(token, "expecting =");
- token = SkipPastSemi(token);
- return null;
- }
- token = token.nextToken;
- TokenRVal rVal = ParseRVal(ref token, semiOnly);
- if(rVal == null)
- return null;
- tokenDeclVar.init = rVal;
- tokenDeclVar.constant = true;
- }
- // Otherwise, normal variable declaration with optional initialization value.
- else
- {
- // Build basic encapsulating token with type and name.
- tokenDeclVar.type = (TokenType)token;
- token = token.nextToken;
- if(!(token is TokenName))
- {
- ErrorMsg(token, "expecting variable name");
- token = SkipPastSemi(token);
- return null;
- }
- tokenDeclVar.name = (TokenName)token;
- token = token.nextToken;
- // If just a ;, there is no explicit initialization value.
- // Otherwise, look for an =RVal; expression that has init value.
- if(token is TokenKwSemi)
- {
- token = token.nextToken;
- if(initFunc != null)
- {
- tokenDeclVar.init = TokenRValInitDef.Construct(tokenDeclVar);
- }
- }
- else if(token is TokenKwAssign)
- {
- token = token.nextToken;
- if(initFunc != null)
- {
- currentDeclFunc = initFunc;
- tokenDeclVar.init = ParseRVal(ref token, semiOnly);
- currentDeclFunc = null;
- }
- else
- {
- tokenDeclVar.init = ParseRVal(ref token, semiOnly);
- }
- if(tokenDeclVar.init == null)
- return null;
- }
- else
- {
- ErrorMsg(token, "expecting = or ;");
- token = SkipPastSemi(token);
- return null;
- }
- }
- // If doing local vars, each var goes in its own var frame,
- // to make sure no code before this point can reference it.
- if (currentStmtBlock != null)
- {
- tokenScript.PushVarFrame(true);
- }
- /*
- ScriptConst scriptConst = ScriptConst.Lookup(tokenDeclVar.name.val);
- if (scriptConst != null)
- {
- ErrorMsg(tokenDeclVar, "reserved constant name " + tokenDeclVar.name.val);
- return null;
- }
- */
- // Can't be same name already in block.
- if (!tokenScript.AddVarEntry(tokenDeclVar))
- {
- ErrorMsg(tokenDeclVar, "duplicate variable " + tokenDeclVar.name.val);
- return null;
- }
- /*
- if (TokenDeclInline.inlineFunctions.HasAnyExact(tokenDeclVar.name.val))
- {
- ErrorMsg(tokenDeclVar, "reserved name " + tokenDeclVar.name.val);
- return null;
- }
- */
- return tokenDeclVar;
- }
- /**
- * @brief Add variable initialization to $globalvarinit, $staticfieldinit or $instfieldinit function.
- * @param initFunc = $globalvarinit, $staticfieldinit or $instfieldinit function
- * @param left = variable being initialized
- * @param init = null: initialize to default value
- * else: initialize to this value
- */
- private void DoVarInit(TokenDeclVar initFunc, TokenLVal left, TokenRVal init)
- {
- // Make a statement that assigns the initialization value to the variable.
- TokenStmt stmt;
- if(init == null)
- {
- stmt = new TokenStmtVarIniDef(left) { var = left };
- }
- else
- {
- TokenKw op = new TokenKwAssign(left);
- stmt = new TokenStmtRVal(init) { rVal = new TokenRValOpBin(left, op, init) };
- }
- // Add statement to end of initialization function.
- // Be sure to execute them in same order as in source
- // as some doofus scripts depend on it.
- Token lastStmt = initFunc.body.statements;
- if(lastStmt == null)
- {
- initFunc.body.statements = stmt;
- }
- else
- {
- Token nextStmt;
- while((nextStmt = lastStmt.nextToken) != null)
- {
- lastStmt = nextStmt;
- }
- lastStmt.nextToken = stmt;
- }
- }
- /**
- * @brief parse function declaration argument list
- * @param token = points to TokenKwParOpen
- * @returns null: parse error
- * else: points to token with types and names
- * token = updated past the TokenKw{Brk,Par}Close
- */
- private TokenArgDecl ParseFuncArgs(ref Token token, Type end)
- {
- TokenArgDecl tokenArgDecl = new(token);
- bool first = true;
- do
- {
- token = token.nextToken;
- if((token.GetType() == end) && first)
- break;
- if(!(token is TokenType type))
- {
- ErrorMsg(token, "expecting arg type");
- token = SkipPastSemi(token);
- return null;
- }
- token = token.nextToken;
- if(token is not TokenName name)
- {
- ErrorMsg(token, "expecting arg name");
- token = SkipPastSemi(token);
- return null;
- }
- token = token.nextToken;
- if(!tokenArgDecl.AddArg(type, name))
- {
- ErrorMsg(name, "duplicate arg name");
- }
- first = false;
- } while(token is TokenKwComma);
- if(token.GetType() != end)
- {
- ErrorMsg(token, "expecting comma or close bracket/paren");
- token = SkipPastSemi(token);
- return null;
- }
- token = token.nextToken;
- return tokenArgDecl;
- }
- /**
- * @brief parse right-hand value expression
- * this is where arithmetic-like expressions are processed
- * @param token = points to first token expression
- * @param termTokenType = expression termination token type
- * @returns null: not an RVal
- * else: single token representing whole expression
- * token = if termTokenType.Length == 1, points just past terminating token
- * else, points right at terminating token
- */
- public TokenRVal ParseRVal(ref Token token, Type[] termTokenTypes)
- {
- // Start with pushing the first operand on operand stack.
- BinOp binOps = null;
- TokenRVal operands = GetOperand(ref token);
- if(operands == null)
- return null;
- // Keep scanning until we hit the termination token.
- while(true)
- {
- Type tokType = token.GetType();
- for(int i = termTokenTypes.Length; --i >= 0;)
- {
- if(tokType == termTokenTypes[i])
- goto done;
- }
- // Special form:
- // <operand> is <typeexp>
- if(token is TokenKwIs)
- {
- TokenRValIsType tokenRValIsType = new(token);
- token = token.nextToken;
- // Parse the <typeexp>.
- tokenRValIsType.typeExp = ParseTypeExp(ref token);
- if(tokenRValIsType.typeExp == null)
- return null;
- // Replace top operand with result of <operand> is <typeexp>
- tokenRValIsType.rValExp = operands;
- tokenRValIsType.nextToken = operands.nextToken;
- operands = tokenRValIsType;
- // token points just past <typeexp> so see if it is another operator.
- continue;
- }
- // Peek at next operator.
- BinOp binOp = GetOperator(ref token);
- if(binOp == null)
- return null;
- // If there are stacked operators of higher or same precedence than new one,
- // perform their computation then push result back on operand stack.
- //
- // higher or same = left-to-right application of operators
- // eg, a - b - c becomes (a - b) - c
- //
- // higher precedence = right-to-left application of operators
- // eg, a - b - c becomes a - (b - c)
- //
- // Now of course, there is some ugliness necessary:
- // we want: a - b - c => (a - b) - c so we do 'higher or same'
- // but we want: a += b = c => a += (b = c) so we do 'higher only'
- //
- // binOps is the first operator (or null if only one)
- // binOp is the second operator (or first if only one)
- while(binOps != null)
- {
- if(binOps.preced < binOp.preced)
- break; // 1st operator lower than 2nd, so leave 1st on stack to do later
- if(binOps.preced > binOp.preced)
- goto do1st; // 1st op higher than 2nd, so we always do 1st op first
- if(binOps.preced == ASNPR)
- break; // equal preced, if assignment type, leave 1st on stack to do later
- // if non-asn type, do 1st op first (ie left-to-right)
- do1st:
- TokenRVal result = PerformBinOp((TokenRVal)operands.prevToken, binOps, (TokenRVal)operands);
- result.prevToken = operands.prevToken.prevToken;
- operands = result;
- binOps = binOps.pop;
- }
- // Handle conditional expression as a special form:
- // <condexp> ? <trueexp> : <falseexp>
- if(binOp.token is TokenKwQMark)
- {
- TokenRValCondExpr condExpr = new(binOp.token)
- {
- condExpr = operands,
- trueExpr = ParseRVal(ref token, new Type[] { typeof(TokenKwColon) }),
- falseExpr = ParseRVal(ref token, termTokenTypes),
- prevToken = operands.prevToken
- };
- operands = condExpr;
- termTokenTypes = Array.Empty<Type>();
- goto done;
- }
- // Push new operator on its stack.
- binOp.pop = binOps;
- binOps = binOp;
- // Push next operand on its stack.
- TokenRVal operand = GetOperand(ref token);
- if(operand == null)
- return null;
- operand.prevToken = operands;
- operands = operand;
- }
- done:
- // At end of expression, perform any stacked computations.
- while(binOps != null)
- {
- TokenRVal result = PerformBinOp((TokenRVal)operands.prevToken, binOps, (TokenRVal)operands);
- result.prevToken = operands.prevToken.prevToken;
- operands = result;
- binOps = binOps.pop;
- }
- // There should be exactly one remaining operand on the stack which is our final result.
- if(operands.prevToken != null)
- throw new Exception("too many operands");
- // If only one terminator type possible, advance past the terminator.
- if(termTokenTypes.Length == 1)
- token = token.nextToken;
- return operands;
- }
- private TokenTypeExp ParseTypeExp(ref Token token)
- {
- TokenTypeExp leftOperand = GetTypeExp(ref token);
- if(leftOperand == null)
- return null;
- while((token is TokenKwAnd) || (token is TokenKwOr))
- {
- Token typeBinOp = token;
- token = token.nextToken;
- TokenTypeExp rightOperand = GetTypeExp(ref token);
- if(rightOperand == null)
- return null;
- TokenTypeExpBinOp typeExpBinOp = new(typeBinOp)
- {
- leftOp = leftOperand,
- binOp = typeBinOp,
- rightOp = rightOperand
- };
- leftOperand = typeExpBinOp;
- }
- return leftOperand;
- }
- private TokenTypeExp GetTypeExp(ref Token token)
- {
- if(token is TokenKwTilde)
- {
- TokenTypeExpNot typeExpNot = new(token);
- token = token.nextToken;
- typeExpNot.typeExp = GetTypeExp(ref token);
- if(typeExpNot.typeExp == null)
- return null;
- return typeExpNot;
- }
- if(token is TokenKwParOpen)
- {
- TokenTypeExpPar typeExpPar = new(token);
- token = token.nextToken;
- typeExpPar.typeExp = GetTypeExp(ref token);
- if(typeExpPar.typeExp == null)
- return null;
- if(!(token is TokenKwParClose))
- {
- ErrorMsg(token, "expected close parenthesis");
- token = SkipPastSemi(token);
- return null;
- }
- return typeExpPar;
- }
- if(token is TokenKwUndef)
- {
- TokenTypeExpUndef typeExpUndef = new(token);
- token = token.nextToken;
- return typeExpUndef;
- }
- if(token is TokenType ttype)
- {
- TokenTypeExpType typeExpType = new(token) { typeToken = ttype };
- token = token.nextToken;
- return typeExpType;
- }
- ErrorMsg(token, "expected type");
- token = SkipPastSemi(token);
- return null;
- }
- /**
- * @brief get a right-hand operand expression token
- * @param token = first token of operand to parse
- * @returns null: invalid operand
- * else: token that bundles or wraps the operand
- * token = points to token following last operand token
- */
- private TokenRVal GetOperand(ref Token token)
- {
- // Prefix unary operators (eg ++, --) requiring an L-value.
- if((token is TokenKwIncr) || (token is TokenKwDecr))
- {
- TokenRValAsnPre asnPre = new(token) { prefix = token };
- token = token.nextToken;
- TokenRVal op = GetOperand(ref token);
- if(op == null)
- return null;
- if(op is not TokenLVal)
- {
- ErrorMsg(op, "can pre{in,de}crement only an L-value");
- return null;
- }
- asnPre.lVal = (TokenLVal)op;
- return asnPre;
- }
- // Get the bulk of the operand, ie, without any of the below suffixes.
- TokenRVal operand = GetOperandNoMods(ref token);
- if(operand == null)
- return null;
- modifiers:
- // If followed by '++' or '--', it is post-{in,de}cremented.
- if((token is TokenKwIncr) || (token is TokenKwDecr))
- {
- TokenRValAsnPost asnPost = new(token) { postfix = token };
- token = token.nextToken;
- if(operand is not TokenLVal)
- {
- ErrorMsg(operand, "can post{in,de}crement only an L-value");
- return null;
- }
- asnPost.lVal = (TokenLVal)operand;
- return asnPost;
- }
- // If followed by a '.', it is an instance field or instance method reference.
- if(token is TokenKwDot)
- {
- token = token.nextToken;
- if(token is not TokenName)
- {
- ErrorMsg(token, ". must be followed by field/method name");
- return null;
- }
- TokenLValIField field = new(token)
- {
- baseRVal = operand,
- fieldName = (TokenName)token
- };
- operand = field;
- token = token.nextToken;
- goto modifiers;
- }
- // If followed by a '[', it is an array subscript.
- if(token is TokenKwBrkOpen)
- {
- TokenLValArEle tokenLValArEle = new(token);
- token = token.nextToken;
- // Parse subscript(s) expression.
- tokenLValArEle.subRVal = ParseRVal(ref token, brkCloseOnly);
- if(tokenLValArEle.subRVal == null)
- {
- ErrorMsg(tokenLValArEle, "invalid subscript");
- return null;
- }
- // See if comma-separated list of values.
- int numSubscripts = SplitCommaRVals(tokenLValArEle.subRVal, out TokenRVal subscriptRVals);
- if (numSubscripts > 1)
- {
- // If so, put the values in an LSL_List object.
- TokenRValList rValList = new(tokenLValArEle)
- {
- rVal = subscriptRVals,
- nItems = numSubscripts
- };
- tokenLValArEle.subRVal = rValList;
- }
- // Either way, save array variable name
- // and substitute whole reference for L-value
- tokenLValArEle.baseRVal = operand;
- operand = tokenLValArEle;
- goto modifiers;
- }
- // If followed by a '(', it is a function/method call.
- if(token is TokenKwParOpen)
- {
- operand = ParseRValCall(ref token, operand);
- goto modifiers;
- }
- // If 'new' arraytipe '{', it is an array initializer.
- if((token is TokenKwBrcOpen) && (operand is TokenLValSField) &&
- (((TokenLValSField)operand).fieldName.val == "$new") &&
- ((TokenLValSField)operand).baseType.ToString().EndsWith("]"))
- {
- operand = ParseRValNewArIni(ref token, (TokenLValSField)operand);
- if(operand != null)
- goto modifiers;
- }
- return operand;
- }
- /**
- * @brief same as GetOperand() except doesn't check for any modifiers
- */
- private TokenRVal GetOperandNoMods(ref Token token)
- {
- // Simple unary operators.
- if((token is TokenKwSub) ||
- (token is TokenKwTilde) ||
- (token is TokenKwExclam))
- {
- Token uop = token;
- token = token.nextToken;
- TokenRVal rVal = GetOperand(ref token);
- if(rVal == null)
- return null;
- return PerformUnOp(uop, rVal);
- }
- // Type casting.
- if((token is TokenKwParOpen) &&
- (token.nextToken is TokenType) &&
- (token.nextToken.nextToken is TokenKwParClose))
- {
- TokenType type = (TokenType)token.nextToken;
- token = token.nextToken.nextToken.nextToken;
- TokenRVal rVal = GetOperand(ref token);
- if(rVal == null)
- return null;
- return new TokenRValCast(type, rVal);
- }
- // Parenthesized expression.
- if(token is TokenKwParOpen)
- {
- return ParseRValParen(ref token);
- }
- // Constants.
- if(token is TokenChar tchar)
- {
- TokenRValConst rValConst = new(token, tchar.val);
- token = token.nextToken;
- return rValConst;
- }
- if(token is TokenFloat tfloat)
- {
- TokenRValConst rValConst = new(token, tfloat.val);
- token = token.nextToken;
- return rValConst;
- }
- if(token is TokenInt tint)
- {
- TokenRValConst rValConst = new(token, tint.val);
- token = token.nextToken;
- return rValConst;
- }
- if(token is TokenStr tstr)
- {
- TokenRValConst rValConst = new(token, tstr.val);
- token = token.nextToken;
- return rValConst;
- }
- if(token is TokenKwUndef tundef)
- {
- TokenRValUndef rValUndef = new(tundef);
- token = token.nextToken;
- return rValUndef;
- }
- // '<'value,...'>', ie, rotation or vector
- if(token is TokenKwCmpLT)
- {
- Token openBkt = token;
- token = token.nextToken;
- TokenRVal rValAll = ParseRVal(ref token, cmpGTOnly);
- if(rValAll == null)
- return null;
- int nVals = SplitCommaRVals(rValAll, out TokenRVal rVals);
- switch (nVals)
- {
- case 3:
- {
- return new TokenRValVec(openBkt)
- {
- xRVal = rVals,
- yRVal = (TokenRVal)rVals.nextToken,
- zRVal = (TokenRVal)rVals.nextToken.nextToken
- };
- }
- case 4:
- {
- return new TokenRValRot(openBkt)
- {
- xRVal = rVals,
- yRVal = (TokenRVal)rVals.nextToken,
- zRVal = (TokenRVal)rVals.nextToken.nextToken,
- wRVal = (TokenRVal)rVals.nextToken.nextToken.nextToken
- };
- }
- default:
- {
- ErrorMsg(openBkt, "bad rotation/vector");
- token = SkipPastSemi(token);
- return null;
- }
- }
- }
- // '['value,...']', ie, list
- if(token is TokenKwBrkOpen)
- {
- TokenRValList rValList = new(token);
- token = token.nextToken;
- if(token is TokenKwBrkClose)
- {
- token = token.nextToken; // empty list
- }
- else
- {
- TokenRVal rValAll = ParseRVal(ref token, brkCloseOnly);
- if(rValAll == null)
- return null;
- rValList.nItems = SplitCommaRVals(rValAll, out rValList.rVal);
- }
- return rValList;
- }
- // Maybe we have <type>.<name> referencing a static field or method of some type.
- if((token is TokenType) && (token.nextToken is TokenKwDot) && (token.nextToken.nextToken is TokenName))
- {
- TokenLValSField field = new(token.nextToken.nextToken)
- {
- baseType = (TokenType)token,
- fieldName = (TokenName)token.nextToken.nextToken
- };
- token = token.nextToken.nextToken.nextToken;
- return field;
- }
- // Maybe we have 'this' referring to the object of the instance method.
- if(token is TokenKwThis)
- {
- if((currentDeclSDType == null) || currentDeclSDType is not TokenDeclSDTypeClass)
- {
- ErrorMsg(token, "using 'this' outside class definition");
- token = SkipPastSemi(token);
- return null;
- }
- TokenRValThis zhis = new(token, (TokenDeclSDTypeClass)currentDeclSDType);
- token = token.nextToken;
- return zhis;
- }
- // Maybe we have 'base' referring to a field/method of the extended class.
- if(token is TokenKwBase)
- {
- if((currentDeclFunc == null) || (currentDeclFunc.sdtClass == null) || !(currentDeclFunc.sdtClass is TokenDeclSDTypeClass))
- {
- ErrorMsg(token, "using 'base' outside method");
- token = SkipPastSemi(token);
- return null;
- }
- if(token.nextToken is not TokenKwDot || token.nextToken.nextToken is not TokenName nextnexttname)
- {
- ErrorMsg(token, "base must be followed by . then field or method name");
- TokenRValThis zhis = new(token, (TokenDeclSDTypeClass)currentDeclFunc.sdtClass);
- token = token.nextToken;
- return zhis;
- }
- TokenLValBaseField baseField = new(token, nextnexttname, (TokenDeclSDTypeClass)currentDeclFunc.sdtClass);
- token = token.nextToken.nextToken.nextToken;
- return baseField;
- }
- // Maybe we have 'new <script-defined-type>' saying to create an object instance.
- // This ends up generating a call to static function <script-defined-type>.$new(...)
- // whose CIL code is generated by GenerateNewobjBody().
- if(token is TokenKwNew)
- {
- if(token.nextToken is not TokenType)
- {
- ErrorMsg(token.nextToken, "new must be followed by type");
- token = SkipPastSemi(token);
- return null;
- }
- TokenLValSField field = new(token.nextToken.nextToken)
- {
- baseType = (TokenType)token.nextToken,
- fieldName = new TokenName(token, "$new")
- };
- token = token.nextToken.nextToken;
- return field;
- }
- // All we got left is <name>, eg, arg, function, global or local variable reference
- if(token is TokenName)
- {
- TokenLValName name = new((TokenName)token, tokenScript.variablesStack);
- token = token.nextToken;
- return name;
- }
- // Who knows what it is supposed to be?
- ErrorMsg(token, "invalid operand token");
- token = SkipPastSemi(token);
- return null;
- }
- /**
- * @brief Parse a call expression
- * @param token = points to arg list '('
- * @param meth = points to method name being called
- * @returns call expression value
- * token = points just past arg list ')'
- */
- private TokenRValCall ParseRValCall(ref Token token, TokenRVal meth)
- {
- // Set up basic function call struct with function name.
- TokenRValCall rValCall = new(token) { meth = meth };
- // Parse the call parameters, if any.
- token = token.nextToken;
- if(token is TokenKwParClose)
- {
- token = token.nextToken;
- }
- else
- {
- rValCall.args = ParseRVal(ref token, parCloseOnly);
- if(rValCall.args == null)
- return null;
- rValCall.nArgs = SplitCommaRVals(rValCall.args, out rValCall.args);
- }
- currentDeclFunc.unknownTrivialityCalls.AddLast(rValCall);
- return rValCall;
- }
- /**
- * @brief decode binary operator token
- * @param token = points to token to decode
- * @returns null: invalid operator token
- * else: operator token and precedence
- */
- private BinOp GetOperator(ref Token token)
- {
- BinOp binOp = new();
- if(precedence.TryGetValue(token.GetType(), out binOp.preced))
- {
- binOp.token = (TokenKw)token;
- token = token.nextToken;
- return binOp;
- }
- if((token is TokenKwSemi) || (token is TokenKwBrcOpen) || (token is TokenKwBrcClose))
- {
- ErrorMsg(token, "premature expression end");
- }
- else
- {
- ErrorMsg(token, "invalid operator");
- }
- token = SkipPastSemi(token);
- return null;
- }
- private class BinOp
- {
- public BinOp pop;
- public TokenKw token;
- public int preced;
- }
- /**
- * @brief Return an R-value expression token that will be used to
- * generate code to perform the operation at runtime.
- * @param left = left-hand operand
- * @param binOp = operator
- * @param right = right-hand operand
- * @returns resultant expression
- */
- private static TokenRVal PerformBinOp(TokenRVal left, BinOp binOp, TokenRVal right)
- {
- return new TokenRValOpBin(left, binOp.token, right);
- }
- /**
- * @brief Return an R-value expression token that will be used to
- * generate code to perform the operation at runtime.
- * @param unOp = operator
- * @param right = right-hand operand
- * @returns resultant constant or expression
- */
- private static TokenRVal PerformUnOp(Token unOp, TokenRVal right)
- {
- return new TokenRValOpUn((TokenKw)unOp, right);
- }
- /**
- * @brief Parse an array initialization expression.
- * @param token = points to '{' on entry
- * @param newCall = encapsulates a '$new' call
- * @return resultant operand encapsulating '$new' call and initializers
- * token = points just past terminating '}'
- * ...or null if parse error
- */
- private TokenRVal ParseRValNewArIni(ref Token token, TokenLValSField newCall)
- {
- Stack<TokenList> stack = new();
- TokenRValNewArIni arini = new(token) { arrayType = newCall.baseType };
- TokenList values = null;
- while(true)
- {
- // open brace means start a (sub-)list
- if(token is TokenKwBrcOpen)
- {
- stack.Push(values);
- values = new TokenList(token);
- token = token.nextToken;
- continue;
- }
- // close brace means end of (sub-)list
- // if final '}' all done parsing
- if(token is TokenKwBrcClose)
- {
- token = token.nextToken; // skip over the '}'
- TokenList innerds = values; // save the list just closed
- arini.valueList = innerds; // it's the top list if it's the last closed
- values = stack.Pop(); // pop to next outer list
- if(values == null)
- return arini; // final '}', we are done
- values.tl.Add(innerds); // put the inner list on end of outer list
- if(token is TokenKwComma)
- { // should have a ',' or '}' next
- token = token.nextToken; // skip over the ','
- }
- else if(!(token is TokenKwBrcClose))
- {
- ErrorMsg(token, "expecting , or } after sublist");
- }
- continue;
- }
- // this is a comma that doesn't have a value expression before it
- // so we take it to mean skip initializing element (leave it zeroes/null etc)
- if(token is TokenKwComma)
- {
- values.tl.Add(token);
- token = token.nextToken;
- continue;
- }
- // parse value expression and skip terminating ',' if any
- TokenRVal append = ParseRVal(ref token, commaOrBrcClose);
- if(append == null)
- return null;
- values.tl.Add(append);
- if(token is TokenKwComma)
- {
- token = token.nextToken;
- }
- }
- }
- /**
- * @brief parse out a parenthesized expression.
- * @param token = points to open parenthesis
- * @returns null: invalid expression
- * else: parenthesized expression token or constant token
- * token = points past the close parenthesis
- */
- private TokenRValParen ParseRValParen(ref Token token)
- {
- if(!(token is TokenKwParOpen))
- {
- ErrorMsg(token, "expecting (");
- token = SkipPastSemi(token);
- return null;
- }
- TokenRValParen tokenRValParen = new(token);
- token = token.nextToken;
- tokenRValParen.rVal = ParseRVal(ref token, parCloseOnly);
- if(tokenRValParen.rVal == null)
- return null;
- return tokenRValParen;
- }
- /**
- * @brief Split a comma'd RVal into separate expressions
- * @param rValAll = expression containing commas
- * @returns number of comma separated values
- * rVals = values in a null-terminated list linked by rVals.nextToken
- */
- private int SplitCommaRVals(TokenRVal rValAll, out TokenRVal rVals)
- {
- if(rValAll is not TokenRValOpBin opBin || opBin.opcode is not TokenKwComma)
- {
- rVals = rValAll;
- if(rVals.nextToken != null)
- throw new Exception("expected null");
- return 1;
- }
- int leftCount = SplitCommaRVals(opBin.rValLeft, out TokenRVal rValLeft);
- int rightCount = SplitCommaRVals(opBin.rValRight, out TokenRVal rValRight);
- rVals = rValLeft;
- while(rValLeft.nextToken != null)
- rValLeft = (TokenRVal)rValLeft.nextToken;
- rValLeft.nextToken = rValRight;
- return leftCount + rightCount;
- }
- /**
- * @brief output error message and remember that there is an error.
- * @param token = what token is associated with the error
- * @param message = error message string
- */
- private void ErrorMsg(Token token, string message)
- {
- if(!errors || (token.file != lastErrorFile) || (token.line > lastErrorLine))
- {
- errors = true;
- lastErrorFile = token.file;
- lastErrorLine = token.line;
- token.ErrorMsg(message);
- }
- }
- /**
- * @brief Skip past the next semicolon (or matched braces)
- * @param token = points to token to skip over
- * @returns token just after the semicolon or close brace
- */
- private Token SkipPastSemi(Token token)
- {
- int braceLevel = 0;
- while(token is not TokenEnd)
- {
- if((token is TokenKwSemi) && (braceLevel == 0))
- {
- return token.nextToken;
- }
- if(token is TokenKwBrcOpen)
- {
- braceLevel++;
- }
- if((token is TokenKwBrcClose) && (--braceLevel <= 0))
- {
- return token.nextToken;
- }
- token = token.nextToken;
- }
- return token;
- }
- }
- /**
- * @brief Script-defined type declarations
- */
- public abstract class TokenDeclSDType: Token
- {
- protected const byte CLASS = 0;
- protected const byte DELEGATE = 1;
- protected const byte INTERFACE = 2;
- protected const byte TYPEDEF = 3;
- // stuff that gets cloned/copied/transformed when instantiating a generic
- // see InstantiateGeneric() below
- public TokenDeclSDType outerSDType; // null if top-level
- // else points to defining script-defined type
- public Dictionary<string, TokenDeclSDType> innerSDTypes = new();
- // indexed by shortName
- public Token begToken; // token that begins the definition (might be this or something like 'public')
- public Token endToken; // the '}' or ';' that ends the definition
- // generic instantiation assumes none of the rest needs to be cloned (well except for the shortName)
- public int sdTypeIndex = -1; // index in scriptObjCode.sdObjTypesIndx[] array
- public TokenDeclSDTypeClass extends; // only non-null for TokenDeclSDTypeClass's
- public uint accessLevel; // SDT_PRIVATE, SDT_PROTECTED or SDT_PUBLIC
- // ... all top-level types are SDT_PUBLIC
- public VarDict members = new (false); // declared fields, methods, properties if any
- public Dictionary<string, int> genParams; // list of parameters for generic prototypes
- // null for non-generic prototypes
- // eg, for 'Dictionary<K,V>'
- // ...genParams gives K->0; V->1
- public bool isPartial; // was declared with 'partial' keyword
- // classes only, all others always false
- /*
- * Name of the type.
- * shortName = doesn't include outer class type names
- * eg, 'Engine' for non-generic
- * 'Dictionary<,>' for generic prototype
- * 'Dictionary<string,integer>' for generic instantiation
- * longName = includes all outer class type names if any
- */
- private TokenName _shortName;
- private TokenName _longName;
- public TokenName shortName
- {
- get
- {
- return _shortName;
- }
- set
- {
- _shortName = value;
- _longName = null;
- }
- }
- public TokenName longName
- {
- get
- {
- if(_longName == null)
- {
- _longName = _shortName;
- if(outerSDType != null)
- {
- _longName = new TokenName(_shortName, outerSDType.longName.val + "." + _shortName.val);
- }
- }
- return _longName;
- }
- }
- /*
- * Dictionary used when reading from object file that holds all script-defined types.
- * Not complete though until all types have been read from the object file.
- */
- private Dictionary<string, TokenDeclSDType> sdTypes;
- public TokenDeclSDType(Token t) : base(t) { }
- protected abstract TokenDeclSDType MakeBlank(TokenName shortName);
- public abstract TokenType MakeRefToken(Token t);
- public abstract Type GetSysType();
- public abstract void WriteToFile(BinaryWriter objFileWriter);
- public abstract void ReadFromFile(BinaryReader objFileReader, TextWriter asmFileWriter);
- /**
- * @brief Given that this is a generic prototype, apply the supplied genArgs
- * to create an equivalent instantiated non-generic. This basically
- * makes a copy replacing all the parameter types with the actual
- * argument types.
- * @param this = the prototype to be instantiated, eg, 'Dictionary<string,integer>.Converter'
- * @param name = short name with arguments, eg, 'Converter<float>'.
- * @param genArgs = argument types of just this level, eg, 'float'.
- * @returns clone of this but with arguments applied and spliced in source token stream
- */
- public TokenDeclSDType InstantiateGeneric(string name, TokenType[] genArgs, ScriptReduce reduce)
- {
- // Malloc the struct and give it a name.
- TokenDeclSDType instdecl = this.MakeBlank(new TokenName(this, name));
- // If the original had an outer type, then so does the new one.
- // The outer type will never be a generic prototype, eg, if this
- // is 'ValueList' it will always be inside 'Dictionary<string,integer>'
- // not 'Dictionary' at this point.
- if((this.outerSDType != null) && (this.outerSDType.genParams != null))
- throw new Exception();
- instdecl.outerSDType = this.outerSDType;
- // The generic prototype may have stuff like 'public' just before it and we need to copy that too.
- Token prefix = this;
- while((prefix = prefix.prevToken) != null)
- {
- if(prefix is not TokenKwPublic && prefix is not TokenKwProtected && prefix is not TokenKwPrivate)
- break;
- }
- this.begToken = prefix.nextToken;
- // Splice in a copy of the prefix tokens, just before the beginning token of prototype (this.begToken).
- while((prefix = prefix.nextToken) != this)
- {
- SpliceSourceToken(prefix.CopyToken(prefix));
- }
- // Splice instantiation (instdecl) in just before the beginning token of prototype (this.begToken).
- SpliceSourceToken(instdecl);
- // Now for the fun part... Copy the rest of the prototype body to the
- // instantiated body, replacing all generic parameter type tokens with
- // the corresponding generic argument types. Note that the parameters
- // are numbered starting with the outermost so we need the full genArgs
- // array. Eg if we are doing 'Converter<V=float>' from
- // 'Dictionary<T=string,U=integer>.Converter<V=float>', any V's are
- // numbered [2]. Any [0]s or [1]s should be gone by now but it doesn't
- // matter.
- Token it, pt;
- TokenDeclSDType innerProto = this;
- TokenDeclSDType innerInst = instdecl;
- for(pt = this; (pt = pt.nextToken) != this.endToken;)
- {
- // Coming across a sub-type's declaration involves a deep copy of the
- // declaration token. Fortunately we are early on in parsing, so there
- // really isn't much to copy:
- // 1) short name is the same, eg, doing List of Dictionary<string,integer>.List is same short name as Dictionary<T,U>.List
- // if generic, eg doing Converter<W> of Dictionary<T,U>.Converter<W>, we have to manually copy the W as well.
- // 2) outerSDType is transformed from Dictionary<T,U> to Dictionary<string,integer>.
- // 3) innerSDTypes is rebuilt when/if we find classes that are inner to this one.
- if(pt is TokenDeclSDType ptSDType)
- {
- // Make a new TokenDeclSDType{Class,Delegate,Interface}.
- TokenDeclSDType itSDType = ptSDType.MakeBlank(new TokenName(ptSDType.shortName, ptSDType.shortName.val));
- // Set up the transformed outerSDType.
- // Eg, if we are creating Enumerator of Dictionary<string,integer>.Enumerator,
- // innerProto = Dictionary<T,U> and innerInst = Dictionary<string,integer>.
- itSDType.outerSDType = innerInst;
- // This clone is an inner type of its next outer level.
- reduce.CatalogSDTypeDecl(itSDType);
- // We need to manually copy any generic parameters of the class declaration being cloned.
- // eg, if we are cloning Converter<W>, this is where the W gets copied.
- // Since it is an immutable array of strings, just copy the array pointer, if any.
- itSDType.genParams = ptSDType.genParams;
- // We are now processing tokens for this cloned type declaration.
- innerProto = ptSDType;
- innerInst = itSDType;
- // Splice this clone token in.
- it = itSDType;
- }
- // Check for an generic parameter to substitute out.
- else if((pt is TokenName namept) && this.genParams.TryGetValue(namept.val, out int index))
- {
- it = genArgs[index].CopyToken(pt);
- }
- // Everything else is a simple copy.
- else
- it = pt.CopyToken(pt);
- // Whatever we came up with, splice it into the source token stream.
- SpliceSourceToken(it);
- // Maybe we just finished copying an inner type definition.
- // If so, remember where it ends and pop it from the stack.
- if(innerProto.endToken == pt)
- {
- innerInst.endToken = it;
- innerProto = innerProto.outerSDType;
- innerInst = innerInst.outerSDType;
- }
- }
- // Clone and insert the terminator, either '}' or ';'
- it = pt.CopyToken(pt);
- SpliceSourceToken(it);
- instdecl.endToken = it;
- return instdecl;
- }
- /**
- * @brief Splice a source token in just before the type's beginning keyword.
- */
- private void SpliceSourceToken(Token it)
- {
- it.nextToken = this.begToken;
- (it.prevToken = this.begToken.prevToken).nextToken = it;
- this.begToken.prevToken = it;
- }
- /**
- * @brief Read one of these in from the object file.
- * @param sdTypes = dictionary of script-defined types, not yet complete
- * @param name = script-visible name of this type
- * @param objFileReader = reads from the object file
- * @param asmFileWriter = writes to the disassembly file (might be null)
- */
- public static TokenDeclSDType ReadFromFile(Dictionary<string, TokenDeclSDType> sdTypes, string name,
- BinaryReader objFileReader, TextWriter asmFileWriter)
- {
- string file = objFileReader.ReadString();
- int line = objFileReader.ReadInt32();
- int posn = objFileReader.ReadInt32();
- byte code = objFileReader.ReadByte();
- TokenName n = new(null, file, line, posn, name);
- TokenDeclSDType sdt;
- switch(code)
- {
- case CLASS:
- {
- sdt = new TokenDeclSDTypeClass(n, false);
- break;
- }
- case DELEGATE:
- {
- sdt = new TokenDeclSDTypeDelegate(n);
- break;
- }
- case INTERFACE:
- {
- sdt = new TokenDeclSDTypeInterface(n);
- break;
- }
- case TYPEDEF:
- {
- sdt = new TokenDeclSDTypeTypedef(n);
- break;
- }
- default:
- throw new Exception();
- }
- sdt.sdTypes = sdTypes;
- sdt.sdTypeIndex = objFileReader.ReadInt32();
- sdt.ReadFromFile(objFileReader, asmFileWriter);
- return sdt;
- }
- /**
- * @brief Convert a typename string to a type token
- * @param name = script-visible name of token to create,
- * either a script-defined type or an LSL-defined type
- * @returns type token
- */
- protected TokenType MakeTypeToken(string name)
- {
- if (sdTypes.TryGetValue(name, out TokenDeclSDType sdtdecl))
- return sdtdecl.MakeRefToken(this);
- return TokenType.FromLSLType(this, name);
- }
- // debugging - returns, eg, 'Dictionary<T,U>.Enumerator.Node'
- public override void DebString(StringBuilder sb)
- {
- // get long name broken down into segments from outermost to this
- Stack<TokenDeclSDType> declStack = new();
- for(TokenDeclSDType decl = this; decl != null; decl = decl.outerSDType)
- {
- declStack.Push(decl);
- }
- // output each segment's name followed by our args for it
- // starting with outermost and ending with this
- while(declStack.Count > 0)
- {
- TokenDeclSDType decl = declStack.Pop();
- sb.Append(decl.shortName.val);
- if(decl.genParams != null)
- {
- sb.Append('<');
- string[] parms = new string[decl.genParams.Count];
- foreach(KeyValuePair<string, int> kvp in decl.genParams)
- {
- parms[kvp.Value] = kvp.Key;
- }
- for(int j = 0; j < parms.Length;)
- {
- sb.Append(parms[j]);
- if(++j < parms.Length)
- sb.Append(',');
- }
- sb.Append('>');
- }
- if(declStack.Count > 0)
- sb.Append('.');
- }
- }
- }
- public class TokenDeclSDTypeClass: TokenDeclSDType
- {
- public List<TokenDeclSDTypeInterface> implements = new();
- public TokenDeclVar instFieldInit; // $instfieldinit function to do instance field initializations
- public TokenDeclVar staticFieldInit; // $staticfieldinit function to do static field initializations
- public Dictionary<string, int> intfIndices = new(); // longname => this.iFaces index
- public TokenDeclSDTypeInterface[] iFaces; // array of implemented interfaces
- // low-end entries copied from rootward classes
- public TokenDeclVar[][] iImplFunc; // iImplFunc[i][j]:
- // low-end [i] entries copied from rootward classes
- // i = interface number from this.intfIndices[name]
- // j = method of interface from iface.methods[name].vTableIndex
- public TokenType arrayOfType; // if array, it's an array of this type, else null
- public int arrayOfRank; // if array, it has this number of dimensions, else zero
- public bool slotsAssigned; // set true when slots have been assigned...
- public XMRInstArSizes instSizes = new();
- // number of instance fields of various types
- public int numVirtFuncs; // number of virtual functions
- public int numInterfaces; // number of implemented interfaces
- private string extendsStr;
- private string arrayOfTypeStr;
- private List<StackedMethod> stackedMethods;
- private List<StackedIFace> stackedIFaces;
- public DynamicMethod[] vDynMeths; // virtual method entrypoints
- public Type[] vMethTypes; // virtual method delegate types
- public DynamicMethod[][] iDynMeths; // interface method entrypoints
- public Type[][] iMethTypes; // interface method types
- // low-end [i] entries copied from rootward classes
- // i = interface number from this.intfIndices[name]
- // j = method of interface from iface.methods[name].vTableIndex
- public TokenDeclSDTypeClass(TokenName shortName, bool isPartial) : base(shortName)
- {
- this.shortName = shortName;
- this.isPartial = isPartial;
- }
- protected override TokenDeclSDType MakeBlank(TokenName shortName)
- {
- return new TokenDeclSDTypeClass(shortName, false);
- }
- public override TokenType MakeRefToken(Token t)
- {
- return new TokenTypeSDTypeClass(t, this);
- }
- public override Type GetSysType()
- {
- return typeof(XMRSDTypeClObj);
- }
- /**
- * @brief See if the class implements the interface.
- * Do a recursive (deep) check in all rootward classes.
- */
- public bool CanCastToIntf(TokenDeclSDTypeInterface intf)
- {
- if(this.implements.Contains(intf))
- return true;
- if(this.extends == null)
- return false;
- return this.extends.CanCastToIntf(intf);
- }
- /**
- * @brief Write enough out so we can reconstruct with ReadFromFile.
- */
- public override void WriteToFile(BinaryWriter objFileWriter)
- {
- objFileWriter.Write(this.file);
- objFileWriter.Write(this.line);
- objFileWriter.Write(this.posn);
- objFileWriter.Write((byte)CLASS);
- objFileWriter.Write(this.sdTypeIndex);
- this.instSizes.WriteToFile(objFileWriter);
- objFileWriter.Write(numVirtFuncs);
- if(extends == null)
- {
- objFileWriter.Write("");
- }
- else
- {
- objFileWriter.Write(extends.longName.val);
- }
- objFileWriter.Write(arrayOfRank);
- if(arrayOfRank > 0)
- objFileWriter.Write(arrayOfType.ToString());
- foreach(TokenDeclVar meth in members)
- {
- if((meth.retType != null) && (meth.vTableIndex >= 0))
- {
- objFileWriter.Write(meth.vTableIndex);
- objFileWriter.Write(meth.GetObjCodeName());
- objFileWriter.Write(meth.GetDelType().decl.GetWholeSig());
- }
- }
- objFileWriter.Write(-1);
- int numIFaces = iImplFunc.Length;
- objFileWriter.Write(numIFaces);
- for(int i = 0; i < numIFaces; i++)
- {
- objFileWriter.Write(iFaces[i].longName.val);
- TokenDeclVar[] meths = iImplFunc[i];
- int numMeths = 0;
- if(meths != null)
- numMeths = meths.Length;
- objFileWriter.Write(numMeths);
- for(int j = 0; j < numMeths; j++)
- {
- TokenDeclVar meth = meths[j];
- objFileWriter.Write(meth.vTableIndex);
- objFileWriter.Write(meth.GetObjCodeName());
- objFileWriter.Write(meth.GetDelType().decl.GetWholeSig());
- }
- }
- }
- /**
- * @brief Reconstruct from the file.
- */
- public override void ReadFromFile(BinaryReader objFileReader, TextWriter asmFileWriter)
- {
- instSizes.ReadFromFile(objFileReader);
- numVirtFuncs = objFileReader.ReadInt32();
- extendsStr = objFileReader.ReadString();
- arrayOfRank = objFileReader.ReadInt32();
- if(arrayOfRank > 0)
- arrayOfTypeStr = objFileReader.ReadString();
- if(asmFileWriter != null)
- {
- instSizes.WriteAsmFile(asmFileWriter, extendsStr + "." + shortName.val + ".numInst");
- }
- stackedMethods = new List<StackedMethod>();
- int vTableIndex;
- while((vTableIndex = objFileReader.ReadInt32()) >= 0)
- {
- StackedMethod sm;
- sm.methVTI = vTableIndex;
- sm.methName = objFileReader.ReadString();
- sm.methSig = objFileReader.ReadString();
- stackedMethods.Add(sm);
- }
- int numIFaces = objFileReader.ReadInt32();
- if(numIFaces > 0)
- {
- iDynMeths = new DynamicMethod[numIFaces][];
- iMethTypes = new Type[numIFaces][];
- stackedIFaces = new List<StackedIFace>();
- for(int i = 0; i < numIFaces; i++)
- {
- string iFaceName = objFileReader.ReadString();
- intfIndices[iFaceName] = i;
- int numMeths = objFileReader.ReadInt32();
- iDynMeths[i] = new DynamicMethod[numMeths];
- iMethTypes[i] = new Type[numMeths];
- for(int j = 0; j < numMeths; j++)
- {
- StackedIFace si;
- si.iFaceIndex = i;
- si.methIndex = j;
- si.vTableIndex = objFileReader.ReadInt32();
- si.methName = objFileReader.ReadString();
- si.methSig = objFileReader.ReadString();
- stackedIFaces.Add(si);
- }
- }
- }
- }
- private struct StackedMethod
- {
- public int methVTI;
- public string methName;
- public string methSig;
- }
- private struct StackedIFace
- {
- public int iFaceIndex; // which implemented interface
- public int methIndex; // which method of that interface
- public int vTableIndex; // <0: implemented by non-virtual; else: implemented by virtual
- public string methName; // object code name of implementing method (GetObjCodeName)
- public string methSig; // method signature incl return type (GetWholeSig)
- }
- /**
- * @brief Called after all dynamic method code has been generated to fill in vDynMeths and vMethTypes
- * Also fills in iDynMeths, iMethTypes.
- */
- public void FillVTables(ScriptObjCode scriptObjCode)
- {
- if(extendsStr != null)
- {
- if(extendsStr != "")
- {
- extends = (TokenDeclSDTypeClass)scriptObjCode.sdObjTypesName[extendsStr];
- extends.FillVTables(scriptObjCode);
- }
- extendsStr = null;
- }
- if(arrayOfTypeStr != null)
- {
- arrayOfType = MakeTypeToken(arrayOfTypeStr);
- arrayOfTypeStr = null;
- }
- if((numVirtFuncs > 0) && (stackedMethods != null))
- {
- // Allocate arrays big enough for mine plus type we are extending.
- vDynMeths = new DynamicMethod[numVirtFuncs];
- vMethTypes = new Type[numVirtFuncs];
- // Fill in low parts from type we are extending.
- if(extends != null)
- {
- int n = extends.numVirtFuncs;
- for(int i = 0; i < n; i++)
- {
- vDynMeths[i] = extends.vDynMeths[i];
- vMethTypes[i] = extends.vMethTypes[i];
- }
- }
- // Fill in high parts with my own methods.
- // Might also overwrite lower ones with 'override' methods.
- foreach(StackedMethod sm in stackedMethods)
- {
- int i = sm.methVTI;
- string methName = sm.methName;
- if (scriptObjCode.dynamicMethods.TryGetValue(methName, out DynamicMethod dm))
- {
- // method is not abstract
- vDynMeths[i] = dm;
- vMethTypes[i] = GetDynamicMethodDelegateType(dm, sm.methSig);
- }
- }
- stackedMethods = null;
- }
- if(stackedIFaces != null)
- {
- foreach(StackedIFace si in stackedIFaces)
- {
- int i = si.iFaceIndex;
- int j = si.methIndex;
- int vti = si.vTableIndex;
- string methName = si.methName;
- DynamicMethod dm = scriptObjCode.dynamicMethods[methName];
- iDynMeths[i][j] = (vti < 0) ? dm : vDynMeths[vti];
- iMethTypes[i][j] = GetDynamicMethodDelegateType(dm, si.methSig);
- }
- stackedIFaces = null;
- }
- }
- private Type GetDynamicMethodDelegateType(DynamicMethod dm, string methSig)
- {
- Type retType = dm.ReturnType;
- ParameterInfo[] pi = dm.GetParameters();
- Type[] argTypes = new Type[pi.Length];
- for(int j = 0; j < pi.Length; j++)
- {
- argTypes[j] = pi[j].ParameterType;
- }
- return DelegateCommon.GetType(retType, argTypes, methSig);
- }
- public override void DebString(StringBuilder sb)
- {
- // Don't output if array of some type.
- // They will be re-instantiated as referenced by rest of script.
- if(arrayOfType != null)
- return;
- // This class name and extended/implemented type declaration.
- sb.Append("class ");
- sb.Append(shortName.val);
- bool first = true;
- if(extends != null)
- {
- sb.Append(" : ");
- sb.Append(extends.longName);
- first = false;
- }
- foreach(TokenDeclSDType impld in implements)
- {
- sb.Append(first ? " : " : ", ");
- sb.Append(impld.longName);
- first = false;
- }
- sb.Append(" {");
- // Inner type definitions.
- foreach(TokenDeclSDType subs in innerSDTypes.Values)
- {
- subs.DebString(sb);
- }
- // Members (fields, methods, properties).
- foreach(TokenDeclVar memb in members)
- {
- if((memb == instFieldInit) || (memb == staticFieldInit))
- {
- memb.DebStringInitFields(sb);
- }
- else if(memb.retType != null)
- {
- memb.DebString(sb);
- }
- }
- sb.Append('}');
- }
- }
- public class TokenDeclSDTypeDelegate: TokenDeclSDType
- {
- private TokenType retType;
- private TokenType[] argTypes;
- private string argSig;
- private string wholeSig;
- private Type sysType;
- private Type retSysType;
- private Type[] argSysTypes;
- private string retStr;
- private string[] argStrs;
- private static Dictionary<string, TokenDeclSDTypeDelegate> inlines = new();
- private static Dictionary<Type, string> inlrevs = new();
- public TokenDeclSDTypeDelegate(TokenName shortName) : base(shortName)
- {
- this.shortName = shortName;
- }
- public void SetRetArgTypes(TokenType retType, TokenType[] argTypes)
- {
- this.retType = retType;
- this.argTypes = argTypes;
- }
- protected override TokenDeclSDType MakeBlank(TokenName shortName)
- {
- return new TokenDeclSDTypeDelegate(shortName);
- }
- public override TokenType MakeRefToken(Token t)
- {
- return new TokenTypeSDTypeDelegate(t, this);
- }
- /**
- * @brief Get system type for the whole delegate.
- */
- public override Type GetSysType()
- {
- if(sysType == null)
- FillInStuff();
- return sysType;
- }
- /**
- * @brief Get the function's return value type (TokenTypeVoid if void, never null)
- */
- public TokenType GetRetType()
- {
- if(retType == null)
- FillInStuff();
- return retType;
- }
- /**
- * @brief Get the function's argument types
- */
- public TokenType[] GetArgTypes()
- {
- if(argTypes == null)
- FillInStuff();
- return argTypes;
- }
- /**
- * @brief Get signature for the whole delegate, eg, "void(integer,list)"
- */
- public string GetWholeSig()
- {
- if(wholeSig == null)
- FillInStuff();
- return wholeSig;
- }
- /**
- * @brief Get signature for the arguments, eg, "(integer,list)"
- */
- public string GetArgSig()
- {
- if(argSig == null)
- FillInStuff();
- return argSig;
- }
- /**
- * @brief Find out how to create one of these delegates.
- */
- public ConstructorInfo GetConstructorInfo()
- {
- if(sysType == null)
- FillInStuff();
- return sysType.GetConstructor(DelegateCommon.constructorArgTypes);
- }
- /**
- * @brief Find out how to call what one of these delegates points to.
- */
- public MethodInfo GetInvokerInfo()
- {
- if(sysType == null)
- FillInStuff();
- return sysType.GetMethod("Invoke", argSysTypes);
- }
- /**
- * @brief Write enough out to a file so delegate can be reconstructed in ReadFromFile().
- */
- public override void WriteToFile(BinaryWriter objFileWriter)
- {
- objFileWriter.Write(this.file);
- objFileWriter.Write(this.line);
- objFileWriter.Write(this.posn);
- objFileWriter.Write((byte)DELEGATE);
- objFileWriter.Write(this.sdTypeIndex);
- objFileWriter.Write(retType.ToString());
- int nArgs = argTypes.Length;
- objFileWriter.Write(nArgs);
- for(int i = 0; i < nArgs; i++)
- {
- objFileWriter.Write(argTypes[i].ToString());
- }
- }
- /**
- * @brief Read that data from file so we can reconstruct.
- * Don't actually reconstruct yet in case any forward-referenced types are undefined.
- */
- public override void ReadFromFile(BinaryReader objFileReader, TextWriter asmFileWriter)
- {
- retStr = objFileReader.ReadString();
- int nArgs = objFileReader.ReadInt32();
- asmFileWriter?.Write(" delegate " + retStr + " " + longName.val + "(");
- argStrs = new string[nArgs];
- for(int i = 0; i < nArgs; i++)
- {
- argStrs[i] = objFileReader.ReadString();
- if(asmFileWriter != null)
- {
- if(i > 0)
- asmFileWriter.Write(",");
- asmFileWriter.Write(argStrs[i]);
- }
- }
- asmFileWriter?.WriteLine(");");
- }
- /**
- * @brief Fill in missing internal data.
- */
- private void FillInStuff()
- {
- // This happens when the node was restored via ReadFromFile().
- // It leaves the types in retStr/argStrs for resolution after
- // all definitions have been read from the object file in case
- // there are forward references.
- retType ??= MakeTypeToken(retStr);
- if(argTypes == null)
- {
- argTypes = new TokenType[argStrs.Length];
- for(int i = 0; i < argStrs.Length; i++)
- {
- argTypes[i] = MakeTypeToken(argStrs[i]);
- }
- }
- // Fill in system types from token types.
- // Might as well build the signature strings too from token types.
- retSysType = retType.ToSysType();
- StringBuilder sb = new();
- sb.Append('(');
- argSysTypes = new Type[argTypes.Length];
- for(int i = 0; i < argTypes.Length; i++)
- {
- if(i > 0)
- sb.Append(',');
- sb.Append(argTypes[i].ToString());
- argSysTypes[i] = argTypes[i].ToSysType();
- }
- sb.Append(')');
- argSig = sb.ToString();
- wholeSig = retType.ToString() + argSig;
- // Now we can create a system delegate type from the given
- // return and argument types. Give it an unique name using
- // the whole signature string.
- sysType = DelegateCommon.GetType(retSysType, argSysTypes, wholeSig);
- }
- /**
- * @brief create delegate reference token for inline functions.
- * there is just one instance of these per inline function
- * shared by all scripts, and it is just used when the
- * script engine is loaded.
- */
- public static TokenDeclSDTypeDelegate CreateInline(TokenType retType, TokenType[] argTypes)
- {
- // Name it after the whole signature string.
- StringBuilder sb = new("$inline");
- sb.Append(retType.ToString());
- sb.Append('(');
- bool first = true;
- foreach(TokenType at in argTypes)
- {
- if(!first)
- sb.Append(',');
- sb.Append(at.ToString());
- first = false;
- }
- sb.Append(')');
- string inlname = sb.ToString();
- if(!inlines.TryGetValue(inlname, out TokenDeclSDTypeDelegate decldel))
- {
- // Create the corresponding declaration and link to it
- TokenName name = new(null, inlname);
- decldel = new TokenDeclSDTypeDelegate(name)
- {
- retType = retType,
- argTypes = argTypes
- };
- inlines.Add(inlname, decldel);
- inlrevs.Add(decldel.GetSysType(), inlname);
- }
- return decldel;
- }
- public static string TryGetInlineName(Type sysType)
- {
- if(inlrevs.TryGetValue(sysType, out string name))
- return name;
- return null;
- }
- public static Type TryGetInlineSysType(string name)
- {
- if (inlines.TryGetValue(name, out TokenDeclSDTypeDelegate decl))
- return decl.GetSysType();
- return null;
- }
- }
- public class TokenDeclSDTypeInterface: TokenDeclSDType
- {
- public VarDict methsNProps = new(false);
- // any class that implements this interface
- // must implement all of these methods & properties
- public List<TokenDeclSDTypeInterface> implements = new();
- // any class that implements this interface
- // must also implement all of the methods & properties
- // of all of these interfaces
- public TokenDeclSDTypeInterface(TokenName shortName) : base(shortName)
- {
- this.shortName = shortName;
- }
- protected override TokenDeclSDType MakeBlank(TokenName shortName)
- {
- return new TokenDeclSDTypeInterface(shortName);
- }
- public override TokenType MakeRefToken(Token t)
- {
- return new TokenTypeSDTypeInterface(t, this);
- }
- public override Type GetSysType()
- {
- // interfaces are implemented as arrays of delegates
- // they are taken from iDynMeths[interfaceIndex] of a script-defined class object
- return typeof(Delegate[]);
- }
- public override void WriteToFile(BinaryWriter objFileWriter)
- {
- objFileWriter.Write(this.file);
- objFileWriter.Write(this.line);
- objFileWriter.Write(this.posn);
- objFileWriter.Write((byte)INTERFACE);
- objFileWriter.Write(this.sdTypeIndex);
- }
- public override void ReadFromFile(BinaryReader objFileReader, TextWriter asmFileWriter)
- {
- }
- /**
- * @brief Add this interface to the list of interfaces implemented by a class if not already.
- * And also add this interface's implemented interfaces to the class for those not already there,
- * just as if the class itself had declared to implement those interfaces.
- */
- public void AddToClassDecl(TokenDeclSDTypeClass tokdeclcl)
- {
- if(!tokdeclcl.implements.Contains(this))
- {
- tokdeclcl.implements.Add(this);
- foreach(TokenDeclSDTypeInterface subimpl in this.implements)
- {
- subimpl.AddToClassDecl(tokdeclcl);
- }
- }
- }
- /**
- * @brief See if the 'this' interface implements the new interface.
- * Do a recursive (deep) check.
- */
- public bool Implements(TokenDeclSDTypeInterface newDecl)
- {
- foreach(TokenDeclSDTypeInterface ii in this.implements)
- {
- if(ii == newDecl)
- return true;
- if(ii.Implements(newDecl))
- return true;
- }
- return false;
- }
- /**
- * @brief Scan an interface and all its implemented interfaces for a method or property
- * @param scg = script code generator (ie, which script is being compiled)
- * @param fieldName = name of the member being looked for
- * @param argsig = the method's argument signature
- * @returns null: no such member; intf = undefined
- * else: member; intf = which interface actually found in
- */
- public TokenDeclVar FindIFaceMember(ScriptCodeGen scg, TokenName fieldName, TokenType[] argsig, out TokenDeclSDTypeInterface intf)
- {
- intf = this;
- TokenDeclVar var = scg.FindSingleMember(this.methsNProps, fieldName, argsig);
- if(var == null)
- {
- foreach(TokenDeclSDTypeInterface ii in this.implements)
- {
- var = ii.FindIFaceMember(scg, fieldName, argsig, out intf);
- if(var != null)
- break;
- }
- }
- return var;
- }
- }
- public class TokenDeclSDTypeTypedef: TokenDeclSDType
- {
- public TokenDeclSDTypeTypedef(TokenName shortName) : base(shortName)
- {
- this.shortName = shortName;
- }
- protected override TokenDeclSDType MakeBlank(TokenName shortName)
- {
- return new TokenDeclSDTypeTypedef(shortName);
- }
- public override TokenType MakeRefToken(Token t)
- {
- // if our body is a single type token, that is what we return
- // otherwise return null saying maybe our body needs some substitutions
- if(nextToken is not TokenType)
- return null;
- if(nextToken.nextToken != this.endToken)
- {
- nextToken.nextToken.ErrorMsg("extra tokens for typedef");
- return null;
- }
- return (TokenType)nextToken.CopyToken(t);
- }
- public override Type GetSysType()
- {
- // we are just a macro
- // we are asked for system type because we are cataloged
- // but we don't really have one so return null
- return null;
- }
- public override void WriteToFile(BinaryWriter objFileWriter)
- {
- objFileWriter.Write(this.file);
- objFileWriter.Write(this.line);
- objFileWriter.Write(this.posn);
- objFileWriter.Write((byte)TYPEDEF);
- objFileWriter.Write(this.sdTypeIndex);
- }
- public override void ReadFromFile(BinaryReader objFileReader, TextWriter asmFileWriter)
- {
- }
- }
- /**
- * @brief Script-defined type references.
- * These occur in the source code wherever it specifies (eg, variable declaration) a script-defined type.
- * These must be copyable via CopyToken().
- */
- public abstract class TokenTypeSDType: TokenType
- {
- public TokenTypeSDType(TokenErrorMessage emsg, string file, int line, int posn) : base(emsg, file, line, posn) { }
- public TokenTypeSDType(Token t) : base(t) { }
- public abstract TokenDeclSDType GetDecl();
- public abstract void SetDecl(TokenDeclSDType decl);
- }
- public class TokenTypeSDTypeClass: TokenTypeSDType
- {
- private static readonly FieldInfo iarSDTClObjsFieldInfo = typeof(XMRInstArrays).GetField("iarSDTClObjs");
- public TokenDeclSDTypeClass decl;
- public TokenTypeSDTypeClass(Token t, TokenDeclSDTypeClass decl) : base(t)
- {
- this.decl = decl;
- }
- public override TokenDeclSDType GetDecl()
- {
- return decl;
- }
- public override void SetDecl(TokenDeclSDType decl)
- {
- this.decl = (TokenDeclSDTypeClass)decl;
- }
- public override string ToString()
- {
- return decl.longName.val;
- }
- public override Type ToSysType()
- {
- return typeof(XMRSDTypeClObj);
- }
- public override void AssignVarSlot(TokenDeclVar declVar, XMRInstArSizes ias)
- {
- declVar.vTableArray = iarSDTClObjsFieldInfo;
- declVar.vTableIndex = ias.iasSDTClObjs++;
- }
- // debugging
- public override void DebString(StringBuilder sb)
- {
- sb.Append(decl.longName);
- }
- }
- public class TokenTypeSDTypeDelegate: TokenTypeSDType
- {
- private static readonly FieldInfo iarObjectsFieldInfo = typeof(XMRInstArrays).GetField("iarObjects");
- public TokenDeclSDTypeDelegate decl;
- /**
- * @brief create a reference to an explicitly declared delegate
- * @param t = where the reference is being made in the source file
- * @param decl = the explicit delegate declaration
- */
- public TokenTypeSDTypeDelegate(Token t, TokenDeclSDTypeDelegate decl) : base(t)
- {
- this.decl = decl;
- }
- public override TokenDeclSDType GetDecl()
- {
- return decl;
- }
- public override void SetDecl(TokenDeclSDType decl)
- {
- this.decl = (TokenDeclSDTypeDelegate)decl;
- }
- /**
- * @brief create a reference to a possibly anonymous delegate
- * @param t = where the reference is being made in the source file
- * @param retType = return type (TokenTypeVoid if void, never null)
- * @param argTypes = script-visible argument types
- * @param tokenScript = what script this is part of
- */
- public TokenTypeSDTypeDelegate(Token t, TokenType retType, TokenType[] argTypes, TokenScript tokenScript) : base(t)
- {
- TokenDeclSDTypeDelegate decldel;
- // See if we already have a matching declared one cataloged.
- int nArgs = argTypes.Length;
- foreach(TokenDeclSDType decl in tokenScript.sdSrcTypesValues)
- {
- if(decl is TokenDeclSDTypeDelegate decldelg)
- {
- TokenType rt = decldelg.GetRetType();
- TokenType[] ats = decldelg.GetArgTypes();
- if((rt.ToString() == retType.ToString()) && (ats.Length == nArgs))
- {
- for(int i = 0; i < nArgs; i++)
- {
- if(ats[i].ToString() != argTypes[i].ToString())
- goto nomatch;
- }
- this.decl = decldelg;
- return;
- }
- }
- nomatch:
- ;
- }
- // No such luck, create a new anonymous declaration.
- StringBuilder sb = new("$anondel$");
- sb.Append(retType.ToString());
- sb.Append('(');
- bool first = true;
- foreach(TokenType at in argTypes)
- {
- if(!first)
- sb.Append(',');
- sb.Append(at.ToString());
- first = false;
- }
- sb.Append(')');
- TokenName name = new(t, sb.ToString());
- decldel = new TokenDeclSDTypeDelegate(name);
- decldel.SetRetArgTypes(retType, argTypes);
- tokenScript.sdSrcTypesAdd(name.val, decldel);
- this.decl = decldel;
- }
- public override Type ToSysType()
- {
- return decl.GetSysType();
- }
- public override string ToString()
- {
- return decl.longName.val;
- }
- /**
- * @brief Assign slots in the gblObjects[] array because we have to typecast out in any case.
- * Likewise with the sdtcObjects[] array.
- */
- public override void AssignVarSlot(TokenDeclVar declVar, XMRInstArSizes ias)
- {
- declVar.vTableArray = iarObjectsFieldInfo;
- declVar.vTableIndex = ias.iasObjects++;
- }
- /**
- * @brief create delegate reference token for inline functions.
- */
- public TokenTypeSDTypeDelegate(TokenType retType, TokenType[] argTypes) : base(null)
- {
- this.decl = TokenDeclSDTypeDelegate.CreateInline(retType, argTypes);
- }
- // debugging
- public override void DebString(StringBuilder sb)
- {
- sb.Append(decl.longName);
- }
- }
- public class TokenTypeSDTypeInterface: TokenTypeSDType
- {
- private static readonly FieldInfo iarSDTIntfObjsFieldInfo = typeof(XMRInstArrays).GetField("iarSDTIntfObjs");
- public TokenDeclSDTypeInterface decl;
- public TokenTypeSDTypeInterface(Token t, TokenDeclSDTypeInterface decl) : base(t)
- {
- this.decl = decl;
- }
- public override TokenDeclSDType GetDecl()
- {
- return decl;
- }
- public override void SetDecl(TokenDeclSDType decl)
- {
- this.decl = (TokenDeclSDTypeInterface)decl;
- }
- public override string ToString()
- {
- return decl.longName.val;
- }
- public override Type ToSysType()
- {
- return typeof(Delegate[]);
- }
- /**
- * @brief Assign slots in the gblSDTIntfObjs[] array
- * Likewise with the sdtcSDTIntfObjs[] array.
- */
- public override void AssignVarSlot(TokenDeclVar declVar, XMRInstArSizes ias)
- {
- declVar.vTableArray = iarSDTIntfObjsFieldInfo;
- declVar.vTableIndex = ias.iasSDTIntfObjs++;
- }
- // debugging
- public override void DebString(StringBuilder sb)
- {
- sb.Append(decl.longName);
- }
- }
- /**
- * @brief function argument list declaration
- */
- public class TokenArgDecl: Token
- {
- public VarDict varDict = new (false);
- public TokenArgDecl(Token original) : base(original) { }
- public bool AddArg(TokenType type, TokenName name)
- {
- TokenDeclVar var = new (name, null, null)
- {
- name = name,
- type = type,
- vTableIndex = varDict.Count
- };
- return varDict.AddEntry(var);
- }
- /**
- * @brief Get an array of the argument types.
- */
- private TokenType[] _types;
- public TokenType[] types
- {
- get
- {
- if(_types == null)
- {
- _types = new TokenType[varDict.Count];
- foreach(TokenDeclVar var in varDict)
- {
- _types[var.vTableIndex] = var.type;
- }
- }
- return _types;
- }
- }
- /**
- * @brief Access the arguments as an array of variables.
- */
- private TokenDeclVar[] _vars;
- public TokenDeclVar[] vars
- {
- get
- {
- if(_vars == null)
- {
- _vars = new TokenDeclVar[varDict.Count];
- foreach(TokenDeclVar var in varDict)
- {
- _vars[var.vTableIndex] = var;
- }
- }
- return _vars;
- }
- }
- /**
- * @brief Get argument signature string, eg, "(list,vector,integer)"
- */
- private string argSig = null;
- public string GetArgSig()
- {
- argSig ??= ScriptCodeGen.ArgSigString(types);
- return argSig;
- }
- }
- /**
- * @brief encapsulate a state declaration in a single token
- */
- public class TokenDeclState: Token
- {
- public TokenName name; // null for default state
- public TokenStateBody body;
- public TokenDeclState(Token original) : base(original) { }
- public override void DebString(StringBuilder sb)
- {
- if(name == null)
- {
- sb.Append("default");
- }
- else
- {
- sb.Append("state ");
- sb.Append(name);
- }
- body.DebString(sb);
- }
- }
- /**
- * @brief encapsulate the declaration of a field/function/method/property/variable.
- */
- public enum Triviality
- { // function triviality: has no loops and doesn't call anything that has loops
- // such a function does not need all the CheckRun() and stack serialization stuff
- unknown, // function's Triviality unknown as of yet
- // - it does not have any loops or backward gotos
- // - nothing it calls is known to be complex
- trivial, // function known to be trivial
- // - it does not have any loops or backward gotos
- // - everything it calls is known to be trivial
- complex, // function known to be complex
- // - it has loops or backward gotos
- // - something it calls is known to be complex
- analyzing // triviality is being analyzed (used to detect recursive loops)
- };
- public class TokenDeclVar: TokenStmt
- {
- public TokenName name; // vars: name; funcs: bare name, ie, no signature
- public TokenRVal init; // vars: null if none; funcs: null
- public bool constant; // vars: 'constant'; funcs: false
- public uint sdtFlags; // SDT_<*> flags
- public CompValu location; // used by codegen to keep track of location
- public FieldInfo vTableArray;
- public int vTableIndex = -1; // local vars: not used (-1)
- // arg vars: index in the arg list
- // global vars: which slot in gbl<Type>s[] array it is stored
- // instance vars: which slot in inst<Types>s[] array it is stored
- // static vars: which slot in gbl<Type>s[] array it is stored
- // global funcs: not used (-1)
- // virt funcs: which slot in vTable[] array it is stored
- // instance func: not used (-1)
- public TokenDeclVar getProp; // if property, function that reads value
- public TokenDeclVar setProp; // if property, function that writes value
- public TokenScript tokenScript; // what script this function is part of
- public TokenDeclSDType sdtClass; // null: script global member
- // else: member is part of this script-defined type
- // function-only data:
- public TokenType retType; // vars: null; funcs: TokenTypeVoid if void
- public TokenArgDecl argDecl; // vars: null; funcs: argument list prototypes
- public TokenStmtBlock body; // vars: null; funcs: statements (null iff abstract)
- public Dictionary<string, TokenStmtLabel> labels = new();
- // all labels defined in the function
- public LinkedList<TokenDeclVar> localVars = new();
- // all local variables declared by this function
- // - doesn't include argument variables
- public TokenIntfImpl implements; // if script-defined type method, what interface method(s) this func implements
- public TokenRValCall baseCtorCall; // if script-defined type constructor, call to base constructor, if any
- public Triviality triviality = Triviality.unknown;
- // vars: unknown (not used for any thing); funcs: unknown/trivial/complex
- public LinkedList<TokenRValCall> unknownTrivialityCalls = new();
- // reduction puts all calls here
- // compilation sorts it all out
- public ScriptObjWriter ilGen; // codegen stores emitted code here
- /**
- * @brief Set up a variable declaration token.
- * @param original = original source token that triggered definition
- * (for error messages)
- * @param func = null: global variable
- * else: local to the given function
- */
- public TokenDeclVar(Token original, TokenDeclVar func, TokenScript ts) : base(original)
- {
- func?.localVars.AddLast(this);
- tokenScript = ts;
- }
- /**
- * @brief Get/Set overall type
- * For vars, this is the type of the location
- * For funcs, this is the delegate type
- */
- private TokenType _type;
- public TokenType type
- {
- get
- {
- if(_type == null)
- {
- GetDelType();
- }
- return _type;
- }
- set
- {
- _type = value;
- }
- }
- /**
- * @brief Full name: <fulltype>.<name>(<argsig>)
- * (<argsig>) missing for fields/variables
- * <fulltype>. missing for top-level functions/variables
- */
- public string fullName
- {
- get
- {
- if(sdtClass == null)
- {
- if(retType == null)
- return name.val;
- return funcNameSig.val;
- }
- if(retType == null)
- return sdtClass.longName.val + "." + name.val;
- return sdtClass.longName.val + "." + funcNameSig.val;
- }
- }
- /**
- * @brief See if reading or writing the variable is trivial.
- * Note that for functions, this is reading the function itself,
- * as in 'someDelegate = SomeFunction;', not calling it as such.
- * The triviality of actually calling the function is IsFuncTrivial().
- */
- public bool IsVarTrivial(ScriptCodeGen scg)
- {
- // reading or writing a property involves a function call however
- // so we need to check the triviality of the property functions
- if((getProp != null) && !getProp.IsFuncTrivial(scg))
- return false;
- if((setProp != null) && !setProp.IsFuncTrivial(scg))
- return false;
- // otherwise for variables it is a trivial access
- // and likewise for getting a delegate that points to a function
- return true;
- }
- /***************************\
- * FUNCTION-only methods *
- \***************************/
- private TokenName _funcNameSig; // vars: null; funcs: function name including argumet signature, eg, "PrintStuff(list,string)"
- public TokenName funcNameSig
- {
- get
- {
- if(_funcNameSig == null)
- {
- if(argDecl == null)
- return null;
- _funcNameSig = new TokenName(name, name.val + argDecl.GetArgSig());
- }
- return _funcNameSig;
- }
- }
- /**
- * @brief The bare function name, ie, without any signature info
- */
- public string GetSimpleName()
- {
- return name.val;
- }
- /**
- * @brief The function name as it appears in the object code,
- * ie, script-defined type name if any,
- * bare function name and argument signature,
- * eg, "MyClass.PrintStuff(string)"
- */
- public string GetObjCodeName()
- {
- if(sdtClass != null)
- {
- return sdtClass.longName.val + "." + funcNameSig.val;
- }
- return funcNameSig.val;
- }
- /**
- * @brief Get delegate type.
- * This is the function's script-visible type,
- * It includes return type and all script-visible argument types.
- * @returns null for vars; else delegate type for funcs
- */
- public TokenTypeSDTypeDelegate GetDelType()
- {
- if(argDecl == null)
- return null;
- if(_type == null)
- {
- if(tokenScript == null)
- {
- // used during startup to define inline function delegate types
- _type = new TokenTypeSDTypeDelegate(retType, argDecl.types);
- }
- else
- {
- // used for normal script processing
- _type = new TokenTypeSDTypeDelegate(this, retType, argDecl.types, tokenScript);
- }
- }
- if(_type is TokenTypeSDTypeDelegate TokenTypeSDTypeDelegate_type)
- return TokenTypeSDTypeDelegate_type;
- return null;
- }
- /**
- * @brief See if the function's code itself is trivial or not.
- * If it contains any loops (calls to CheckRun()), it is not trivial.
- * If it calls anything that is not trivial, it is not trivial.
- * Otherwise it is trivial.
- */
- public bool IsFuncTrivial(ScriptCodeGen scg)
- {
- // If not really a function, assume it's a delegate.
- // And since we don't really know what functions it can point to,
- // assume it can point to a non-trivial one.
- if(retType == null)
- return false;
- // All virtual functions are non-trivial because although a particular
- // one might be trivial, it might be overidden with a non-trivial one.
- if((sdtFlags & (ScriptReduce.SDT_ABSTRACT | ScriptReduce.SDT_OVERRIDE |
- ScriptReduce.SDT_VIRTUAL)) != 0)
- {
- return false;
- }
- // Check the triviality status of the function.
- switch(triviality)
- {
- // Don't yet know if it is trivial.
- // We know at this point it doesn't have any direct looping.
- // But if it calls something that has loops, it isn't trivial.
- // Otherwise it is trivial.
- case Triviality.unknown:
- {
- // Mark that we are analyzing this function now. So if there are
- // any recursive call loops, that will show that the function is
- // non-trivial and the analysis will terminate at that point.
- triviality = Triviality.analyzing;
- // Check out everything else this function calls. If any say they
- // aren't trivial, then we say this function isn't trivial.
- foreach(TokenRValCall call in unknownTrivialityCalls)
- {
- if(!call.IsRValTrivial(scg, null))
- {
- triviality = Triviality.complex;
- return false;
- }
- }
- // All functions called by this function are trivial, and this
- // function's code doesn't have any loops, so we can mark this
- // function as being trivial.
- triviality = Triviality.trivial;
- return true;
- }
- // We already know that it is trivial.
- case Triviality.trivial:
- {
- return true;
- }
- // We either know it is complex or are trying to analyze it already.
- // If we are already analyzing it, it means it has a recursive loop
- // and we assume those are non-trivial.
- default:
- return false;
- }
- }
- // debugging
- public override void DebString(StringBuilder sb)
- {
- DebStringSDTFlags(sb);
- if(retType == null)
- {
- sb.Append(constant ? "constant" : type.ToString());
- sb.Append(' ');
- sb.Append(name.val);
- if(init != null)
- {
- sb.Append(" = ");
- init.DebString(sb);
- }
- sb.Append(';');
- }
- else
- {
- if(retType is not TokenTypeVoid)
- {
- sb.Append(retType.ToString());
- sb.Append(' ');
- }
- string namestr = name.val;
- if(namestr == "$ctor")
- namestr = "constructor";
- sb.Append(namestr);
- sb.Append(" (");
- for(int i = 0; i < argDecl.vars.Length; i++)
- {
- if(i > 0)
- sb.Append(", ");
- sb.Append(argDecl.vars[i].type.ToString());
- sb.Append(' ');
- sb.Append(argDecl.vars[i].name.val);
- }
- sb.Append(')');
- if(body == null)
- sb.Append(';');
- else
- {
- sb.Append(' ');
- body.DebString(sb);
- }
- }
- }
- // debugging
- // - used to output contents of a $globalvarinit(), $instfieldinit() or $statisfieldinit() function
- // as a series of variable declaration statements with initial value assignments
- // so we get the initial value assignments done in same order as specified in script
- public void DebStringInitFields(StringBuilder sb)
- {
- if(retType is not TokenTypeVoid)
- throw new Exception("bad return type " + retType.GetType().Name);
- if(argDecl.vars.Length != 0)
- throw new Exception("has " + argDecl.vars.Length + " arg(s)");
- for(Token stmt = body.statements; stmt != null; stmt = stmt.nextToken)
- {
- // Body of the function should all be arithmetic statements (not eg for loops, if statements etc).
- TokenRVal rval = ((TokenStmtRVal)stmt).rVal;
- // And the opcode should be a simple assignment operator.
- TokenRValOpBin rvob = (TokenRValOpBin)rval;
- if(rvob.opcode is not TokenKwAssign)
- throw new Exception("bad op type " + rvob.opcode.GetType().Name);
- // Get field or variable being assigned to.
- TokenDeclVar tdvar = null;
- TokenRVal left = rvob.rValLeft;
- if(left is TokenLValIField ifield)
- {
- TokenRValThis zhis = (TokenRValThis)ifield.baseRVal;
- TokenDeclSDTypeClass sdt = zhis.sdtClass;
- tdvar = sdt.members.FindExact(ifield.fieldName.val, null);
- }
- if(left is TokenLValName global)
- {
- tdvar = global.stack.FindExact(global.name.val, null);
- }
- if(left is TokenLValSField sfield)
- {
- TokenTypeSDTypeClass sdtc = (TokenTypeSDTypeClass)sfield.baseType;
- TokenDeclSDTypeClass decl = sdtc.decl;
- tdvar = decl.members.FindExact(sfield.fieldName.val, null);
- }
- if(tdvar == null)
- throw new Exception("unknown var type " + left.GetType().Name);
- // Output flags, type name and bare variable name.
- // This should look like a declaration in the 'sb'
- // as it is not enclosed in a function.
- tdvar.DebStringSDTFlags(sb);
- tdvar.type.DebString(sb);
- sb.Append(' ');
- sb.Append(tdvar.name.val);
- // Maybe it has a non-default initialization value.
- if((tdvar.init != null) && tdvar.init is not TokenRValInitDef)
- {
- sb.Append(" = ");
- tdvar.init.DebString(sb);
- }
- // End of declaration statement.
- sb.Append(';');
- }
- }
- private void DebStringSDTFlags(StringBuilder sb)
- {
- if((sdtFlags & ScriptReduce.SDT_PRIVATE) != 0)
- sb.Append("private ");
- if((sdtFlags & ScriptReduce.SDT_PROTECTED) != 0)
- sb.Append("protected ");
- if((sdtFlags & ScriptReduce.SDT_PUBLIC) != 0)
- sb.Append("public ");
- if((sdtFlags & ScriptReduce.SDT_ABSTRACT) != 0)
- sb.Append("abstract ");
- if((sdtFlags & ScriptReduce.SDT_FINAL) != 0)
- sb.Append("final ");
- if((sdtFlags & ScriptReduce.SDT_NEW) != 0)
- sb.Append("new ");
- if((sdtFlags & ScriptReduce.SDT_OVERRIDE) != 0)
- sb.Append("override ");
- if((sdtFlags & ScriptReduce.SDT_STATIC) != 0)
- sb.Append("static ");
- if((sdtFlags & ScriptReduce.SDT_VIRTUAL) != 0)
- sb.Append("virtual ");
- }
- }
- /**
- * @brief Indicates an interface type.method that is implemented by the function
- */
- public class TokenIntfImpl: Token
- {
- public TokenTypeSDTypeInterface intfType;
- public TokenName methName; // simple name, no arg signature
- public TokenIntfImpl(TokenTypeSDTypeInterface intfType, TokenName methName) : base(intfType)
- {
- this.intfType = intfType;
- this.methName = methName;
- }
- }
- /**
- * @brief any expression that can go on left side of an "="
- */
- public abstract class TokenLVal: TokenRVal
- {
- public TokenLVal(Token original) : base(original) { }
- public abstract override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig);
- public abstract override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig);
- }
- /**
- * @brief an element of an array is an L-value
- */
- public class TokenLValArEle: TokenLVal
- {
- public TokenRVal baseRVal;
- public TokenRVal subRVal;
- public TokenLValArEle(Token original) : base(original) { }
- public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig)
- {
- TokenType baseType = baseRVal.GetRValType(scg, null);
- // Maybe referencing element of a fixed-dimension array.
- if((baseType is TokenTypeSDTypeClass bbtype) && bbtype.decl.arrayOfType != null)
- {
- return bbtype.decl.arrayOfType;
- }
- // Maybe referencing $idxprop property of script-defined class or interface.
- if(baseType is TokenTypeSDTypeClass bc)
- {
- TokenDeclSDTypeClass sdtDecl = bc.decl;
- TokenDeclVar idxProp = scg.FindSingleMember(sdtDecl.members, new TokenName(this, "$idxprop"), null);
- if(idxProp != null)
- return idxProp.type;
- }
- if(baseType is TokenTypeSDTypeInterface bi)
- {
- TokenDeclSDTypeInterface sdtDecl = bi.decl;
- TokenDeclVar idxProp = sdtDecl.FindIFaceMember(scg, new TokenName(this, "$idxprop"), null, out sdtDecl);
- if(idxProp != null)
- return idxProp.type;
- }
- // Maybe referencing single character of a string.
- if((baseType is TokenTypeKey) || (baseType is TokenTypeStr))
- {
- return new TokenTypeChar(this);
- }
- // Assume XMR_Array element or extracting element from list.
- if((baseType is TokenTypeArray) || (baseType is TokenTypeList))
- {
- return new TokenTypeObject(this);
- }
- scg.ErrorMsg(this, "unknown array reference");
- return new TokenTypeVoid(this);
- }
- public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- return baseRVal.IsRValTrivial(scg, null) && subRVal.IsRValTrivial(scg, null);
- }
- public override void DebString(StringBuilder sb)
- {
- baseRVal.DebString(sb);
- sb.Append('[');
- subRVal.DebString(sb);
- sb.Append(']');
- }
- }
- /**
- * @brief 'base.' being used to reference a field/method of the extended class.
- */
- public class TokenLValBaseField: TokenLVal
- {
- public TokenName fieldName;
- private TokenDeclSDTypeClass thisClass;
- public TokenLValBaseField(Token original, TokenName fieldName, TokenDeclSDTypeClass thisClass) : base(original)
- {
- this.fieldName = fieldName;
- this.thisClass = thisClass;
- }
- public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig)
- {
- TokenDeclVar var = scg.FindThisMember(thisClass.extends, fieldName, argsig);
- if(var != null)
- return var.type;
- scg.ErrorMsg(fieldName, "unknown member of " + thisClass.extends.ToString());
- return new TokenTypeVoid(fieldName);
- }
- public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- TokenDeclVar var = scg.FindThisMember(thisClass.extends, fieldName, argsig);
- return (var != null) && var.IsVarTrivial(scg);
- }
- public override bool IsCallTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- TokenDeclVar var = scg.FindThisMember(thisClass.extends, fieldName, argsig);
- return (var != null) && var.IsFuncTrivial(scg);
- }
- }
- /**
- * @brief a field within an struct is an L-value
- */
- public class TokenLValIField: TokenLVal
- {
- public TokenRVal baseRVal;
- public TokenName fieldName;
- public TokenLValIField(Token original) : base(original) { }
- public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig)
- {
- TokenType baseType = baseRVal.GetRValType(scg, null);
- if(baseType is TokenTypeSDTypeClass TokenTypeSDTypeClassbaseType)
- {
- TokenDeclVar tvar = scg.FindThisMember(TokenTypeSDTypeClassbaseType, fieldName, argsig);
- if(tvar != null)
- return tvar.type;
- }
- if(baseType is TokenTypeSDTypeInterface TokenTypeSDTypeInterfacebaseType)
- {
- TokenDeclSDTypeInterface baseIntfDecl = (TokenTypeSDTypeInterfacebaseType).decl;
- TokenDeclVar tvar = baseIntfDecl.FindIFaceMember(scg, fieldName, argsig, out baseIntfDecl);
- if(tvar != null)
- return tvar.type;
- }
- if(baseType is TokenTypeArray)
- {
- return XMR_Array.GetRValType(fieldName);
- }
- if((baseType is TokenTypeRot) || (baseType is TokenTypeVec))
- {
- return new TokenTypeFloat(fieldName);
- }
- scg.ErrorMsg(fieldName, "unknown member of " + baseType.ToString());
- return new TokenTypeVoid(fieldName);
- }
- public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- // If getting pointer to instance isn't trivial, then accessing the member isn't trivial either.
- if(!baseRVal.IsRValTrivial(scg, null))
- return false;
- // Accessing a member of a class depends on the member.
- // In the case of a method, this is accessing it as a delegate, not calling it, and
- // argsig simply serves as selecting which of possibly overloaded methods to select.
- // The case of accessing a property, however, depends on the property implementation,
- // as there could be looping inside the property code.
- TokenType baseType = baseRVal.GetRValType(scg, null);
- if(baseType is TokenTypeSDTypeClass TokenTypeSDTypeClassbaseType)
- {
- TokenDeclVar tvar = scg.FindThisMember(TokenTypeSDTypeClassbaseType, fieldName, argsig);
- return (tvar != null) && tvar.IsVarTrivial(scg);
- }
- // Accessing the members of anything else (arrays, rotations, vectors) is always trivial.
- return true;
- }
- /**
- * @brief Check to see if the case of calling an instance method of some object is trivial.
- * @param scg = script making the call
- * @param argsig = argument types of the call (used to select among overloads)
- * @returns true iff we can tell at compile time that the call will always call a trivial method
- */
- public override bool IsCallTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- // If getting pointer to instance isn't trivial, then calling the method isn't trivial either.
- if(!baseRVal.IsRValTrivial(scg, null))
- return false;
- // Calling a method of a class depends on the method.
- TokenType baseType = baseRVal.GetRValType(scg, null);
- if(baseType is TokenTypeSDTypeClass TokenTypeSDTypeClassbaseType)
- {
- TokenDeclVar tvar = scg.FindThisMember(TokenTypeSDTypeClassbaseType, fieldName, argsig);
- return (tvar != null) && tvar.IsFuncTrivial(scg);
- }
- // Calling via a pointer to an interface instance is never trivial.
- // (It is really a pointer to an array of delegates).
- // We can't tell for this call site whether the actual method being called is trivial or not,
- // so we have to assume it isn't.
- // ??? We could theoretically check to see if *all* implementations of this method of
- // this interface are trivial, then we could conclude that this call is trivial.
- if(baseType is TokenTypeSDTypeInterface)
- return false;
- // Calling a method of anything else (arrays, rotations, vectors) is always trivial.
- // Even methods of delegates, such as ".GetArgTypes()" that we may someday do is trivial.
- return true;
- }
- // debugging
- public override void DebString(StringBuilder sb)
- {
- baseRVal.DebString(sb);
- sb.Append('.');
- sb.Append(fieldName.val);
- }
- }
- /**
- * @brief a name is being used as an L-value
- */
- public class TokenLValName: TokenLVal
- {
- public TokenName name;
- public VarDict stack;
- public TokenLValName(TokenName name, VarDict stack) : base(name)
- {
- // Save name of variable/method/function/field.
- this.name = name;
- // Save where in the stack it can be looked up.
- // If the current stack is for locals, do not allow forward references.
- // this allows idiocy like:
- // list buttons = [ 1, 2, 3 ];
- // x () {
- // list buttons = llList2List (buttons, 0, 1);
- // llOwnerSay (llList2CSV (buttons));
- // }
- // If it is not locals, allow forward references.
- // this allows function X() to call Y() and Y() to call X().
- this.stack = stack.FreezeLocals();
- }
- public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig)
- {
- TokenDeclVar var = scg.FindNamedVar(this, argsig);
- if(var != null)
- return var.type;
- scg.ErrorMsg(name, "undefined name " + name.val + ScriptCodeGen.ArgSigString(argsig));
- return new TokenTypeVoid(name);
- }
- public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- TokenDeclVar var = scg.FindNamedVar(this, argsig);
- return (var != null) && var.IsVarTrivial(scg);
- }
- /**
- * @brief Check to see if the case of calling a global method is trivial.
- * @param scg = script making the call
- * @param argsig = argument types of the call (used to select among overloads)
- * @returns true iff we can tell at compile time that the call will always call a trivial method
- */
- public override bool IsCallTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- TokenDeclVar var = scg.FindNamedVar(this, argsig);
- return (var != null) && var.IsFuncTrivial(scg);
- }
- // debugging
- public override void DebString(StringBuilder sb)
- {
- sb.Append(name.val);
- }
- }
- /**
- * @brief a static field within a struct is an L-value
- */
- public class TokenLValSField: TokenLVal
- {
- public TokenType baseType;
- public TokenName fieldName;
- public TokenLValSField(Token original) : base(original) { }
- public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig)
- {
- if(baseType is TokenTypeSDTypeClass TokenTypeSDTypeClassbaseType)
- {
- TokenDeclVar var = scg.FindThisMember(TokenTypeSDTypeClassbaseType, fieldName, argsig);
- if(var != null)
- return var.type;
- }
- scg.ErrorMsg(fieldName, "unknown member of " + baseType.ToString());
- return new TokenTypeVoid(fieldName);
- }
- public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- // Accessing a member of a class depends on the member.
- // In the case of a method, this is accessing it as a delegate, not calling it, and
- // argsig simply serves as selecting which of possibly overloaded methods to select.
- // The case of accessing a property, however, depends on the property implementation,
- // as there could be looping inside the property code.
- if(baseType is TokenTypeSDTypeClass TokenTypeSDTypeClassbaseType)
- {
- TokenDeclVar tvar = scg.FindThisMember(TokenTypeSDTypeClassbaseType, fieldName, argsig);
- return (tvar != null) && tvar.IsVarTrivial(scg);
- }
- // Accessing the fields/methods/properties of anything else (arrays, rotations, vectors) is always trivial.
- return true;
- }
- /**
- * @brief Check to see if the case of calling a class' static method is trivial.
- * @param scg = script making the call
- * @param argsig = argument types of the call (used to select among overloads)
- * @returns true iff we can tell at compile time that the call will always call a trivial method
- */
- public override bool IsCallTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- // Calling a static method of a class depends on the method.
- if(baseType is TokenTypeSDTypeClass TokenTypeSDTypeClassbaseType)
- {
- TokenDeclVar tvar = scg.FindThisMember(TokenTypeSDTypeClassbaseType, fieldName, argsig);
- return (tvar != null) && tvar.IsFuncTrivial(scg);
- }
- // Calling a static method of anything else (arrays, rotations, vectors) is always trivial.
- return true;
- }
- public override void DebString(StringBuilder sb)
- {
- if(fieldName.val == "$new")
- {
- sb.Append("new ");
- baseType.DebString(sb);
- }
- else
- {
- baseType.DebString(sb);
- sb.Append('.');
- fieldName.DebString(sb);
- }
- }
- }
- /**
- * @brief any expression that can go on right side of "="
- */
- public delegate TokenRVal TCCLookup(TokenRVal rVal, ref bool didOne);
- public abstract class TokenRVal: Token
- {
- public TokenRVal(Token original) : base(original) { }
- /**
- * @brief Tell us the type of the expression.
- */
- public abstract TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig);
- /**
- * @brief Tell us if reading and writing the value is trivial.
- *
- * @param scg = script code generator of script making the access
- * @param argsig = argument types of the call (used to select among overloads)
- * @returns true: we can tell at compile time that reading/writing this location
- * will always be trivial (no looping or CheckRun() calls possible).
- * false: otherwise
- */
- public abstract bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig);
- /**
- * @brief Tell us if calling the method is trivial.
- *
- * This is the default implementation that returns false.
- * It is only used if the location is holding a delegate
- * and the method that the delegate is pointing to is being
- * called. Since we can't tell if the actual runtime method
- * is trivial or not, we assume it isn't.
- *
- * For the more usual ways of calling functions, see the
- * various overrides of IsCallTrivial().
- *
- * @param scg = script code generator of script making the call
- * @param argsig = argument types of the call (used to select among overloads)
- * @returns true: we can tell at compile time that this call will always
- * be to a trivial function/method (no looping or CheckRun()
- * calls possible).
- * false: otherwise
- */
- public virtual bool IsCallTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- return false;
- }
- /**
- * @brief If the result of the expression is a constant,
- * create a TokenRValConst equivalent, set didOne, and return that.
- * Otherwise, just return the original without changing didOne.
- */
- public virtual TokenRVal TryComputeConstant(TCCLookup lookup, ref bool didOne)
- {
- return lookup(this, ref didOne);
- }
- }
- /**
- * @brief a postfix operator and corresponding L-value
- */
- public class TokenRValAsnPost: TokenRVal
- {
- public TokenLVal lVal;
- public Token postfix;
- public TokenRValAsnPost(Token original) : base(original) { }
- public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig)
- {
- return lVal.GetRValType(scg, argsig);
- }
- public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- return lVal.IsRValTrivial(scg, null);
- }
- public override void DebString(StringBuilder sb)
- {
- lVal.DebString(sb);
- sb.Append(' ');
- postfix.DebString(sb);
- }
- }
- /**
- * @brief a prefix operator and corresponding L-value
- */
- public class TokenRValAsnPre: TokenRVal
- {
- public Token prefix;
- public TokenLVal lVal;
- public TokenRValAsnPre(Token original) : base(original) { }
- public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig)
- {
- return lVal.GetRValType(scg, argsig);
- }
- public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- return lVal.IsRValTrivial(scg, null);
- }
- public override void DebString(StringBuilder sb)
- {
- prefix.DebString(sb);
- sb.Append(' ');
- lVal.DebString(sb);
- }
- }
- /**
- * @brief calling a function or method, ie, may have side-effects
- */
- public class TokenRValCall: TokenRVal
- {
- public TokenRVal meth; // points to the function to be called
- // - might be a reference to a global function (TokenLValName)
- // - or an instance method of a class (TokenLValIField)
- // - or a static method of a class (TokenLValSField)
- // - or a delegate stored in a variable (assumption for anything else)
- public TokenRVal args; // null-terminated TokenRVal list
- public int nArgs; // number of elements in args
- public TokenRValCall(Token original) : base(original) { }
- private TokenType[] myArgSig;
- /**
- * @brief The type of a call is the type of the return value.
- */
- public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig)
- {
- // Build type signature so we select correct overloaded function.
- if(myArgSig == null)
- {
- myArgSig = new TokenType[nArgs];
- int i = 0;
- for(Token t = args; t != null; t = t.nextToken)
- {
- myArgSig[i++] = ((TokenRVal)t).GetRValType(scg, null);
- }
- }
- // Get the type of the method itself. This should get us a delegate type.
- TokenType delType = meth.GetRValType(scg, myArgSig);
- if(delType is not TokenTypeSDTypeDelegate)
- {
- scg.ErrorMsg(meth, "must be function or method");
- return new TokenTypeVoid(meth);
- }
- // Get the return type from the delegate type.
- return ((TokenTypeSDTypeDelegate)delType).decl.GetRetType();
- }
- /**
- * @brief See if the call to the function/method is trivial.
- * It is trivial if all the argument computations are trivial and
- * the function is not being called via virtual table or delegate
- * and the function body is trivial.
- */
- public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- // Build type signature so we select correct overloaded function.
- if(myArgSig == null)
- {
- myArgSig = new TokenType[nArgs];
- int i = 0;
- for(Token t = args; t != null; t = t.nextToken)
- {
- myArgSig[i++] = ((TokenRVal)t).GetRValType(scg, null);
- }
- }
- // Make sure all arguments can be computed trivially.
- for(Token t = args; t != null; t = t.nextToken)
- {
- if(!((TokenRVal)t).IsRValTrivial(scg, null))
- return false;
- }
- // See if the function call itself and the function body are trivial.
- return meth.IsCallTrivial(scg, myArgSig);
- }
- // debugging
- public override void DebString(StringBuilder sb)
- {
- meth.DebString(sb);
- sb.Append(" (");
- bool first = true;
- for(Token t = args; t != null; t = t.nextToken)
- {
- if(!first)
- sb.Append(", ");
- t.DebString(sb);
- first = false;
- }
- sb.Append(')');
- }
- }
- /**
- * @brief encapsulates a typecast, ie, (type)
- */
- public class TokenRValCast: TokenRVal
- {
- public TokenType castTo;
- public TokenRVal rVal;
- public TokenRValCast(TokenType type, TokenRVal value) : base(type)
- {
- castTo = type;
- rVal = value;
- }
- public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig)
- {
- return castTo;
- }
- public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- if(castTo is TokenTypeSDTypeDelegate tokencastto)
- argsig = tokencastto.decl.GetArgTypes();
- else
- argsig = null;
- return rVal.IsRValTrivial(scg, argsig);
- }
- /**
- * @brief If operand is constant, maybe we can say the whole thing is a constant.
- */
- public override TokenRVal TryComputeConstant(TCCLookup lookup, ref bool didOne)
- {
- rVal = rVal.TryComputeConstant(lookup, ref didOne);
- if(rVal is TokenRValConst TokenRValConstrVal)
- {
- try
- {
- object val = TokenRValConstrVal.val;
- object nval = null;
- if(castTo is TokenTypeChar)
- {
- if(val is char)
- return rVal;
- if(val is int intval)
- nval = (char)intval;
- }
- else if (castTo is TokenTypeFloat)
- {
- if(val is double)
- return rVal;
- if(val is int intval)
- nval = (double)intval;
- if(val is string sval)
- nval = new LSL_Float(sval).value;
- }
- else if (castTo is TokenTypeInt)
- {
- if(val is int)
- return rVal;
- if(val is char charval)
- nval = (int)charval;
- if(val is double dval)
- nval = (int)dval;
- if(val is string sval)
- nval = new LSL_Integer(sval).value;
- }
- else if (castTo is TokenTypeRot)
- {
- if(val is LSL_Rotation)
- return rVal;
- if(val is string sval)
- nval = new LSL_Rotation(sval);
- }
- else if ((castTo is TokenTypeKey) || (castTo is TokenTypeStr))
- {
- if(val is string)
- nval = val; // in case of key/string conversion
- if(val is char cval)
- nval = TypeCast.CharToString(cval);
- if(val is double dval)
- nval = TypeCast.FloatToString(dval);
- if(val is int ival)
- nval = TypeCast.IntegerToString(ival);
- if(val is LSL_Rotation lslrot)
- nval = TypeCast.RotationToString(lslrot);
- if(val is LSL_Vector lslvec)
- nval = TypeCast.VectorToString(lslvec);
- }
- else if (castTo is TokenTypeVec)
- {
- if(val is LSL_Vector)
- return rVal;
- if(val is string sval)
- nval = new LSL_Vector(sval);
- }
- if(nval != null)
- {
- TokenRVal rValConst = new TokenRValConst(castTo, nval);
- didOne = true;
- return rValConst;
- }
- }
- catch
- {
- }
- }
- return this;
- }
- public override void DebString(StringBuilder sb)
- {
- sb.Append('(');
- castTo.DebString(sb);
- sb.Append(')');
- rVal.DebString(sb);
- }
- }
- /**
- * @brief Encapsulate a conditional expression:
- * <condExpr> ? <trueExpr> : <falseExpr>
- */
- public class TokenRValCondExpr: TokenRVal
- {
- public TokenRVal condExpr;
- public TokenRVal trueExpr;
- public TokenRVal falseExpr;
- public TokenRValCondExpr(Token original) : base(original)
- {
- }
- public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig)
- {
- TokenType trueType = trueExpr.GetRValType(scg, argsig);
- TokenType falseType = falseExpr.GetRValType(scg, argsig);
- if(trueType.ToString() != falseType.ToString())
- {
- scg.ErrorMsg(condExpr, "true & false expr types don't match");
- }
- return trueType;
- }
- public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- return condExpr.IsRValTrivial(scg, null) &&
- trueExpr.IsRValTrivial(scg, argsig) &&
- falseExpr.IsRValTrivial(scg, argsig);
- }
- /**
- * @brief If condition is constant, then the whole expression is constant
- * iff the corresponding trueExpr or falseExpr is constant.
- */
- public override TokenRVal TryComputeConstant(TCCLookup lookup, ref bool didOne)
- {
- TokenRVal rValCond = condExpr.TryComputeConstant(lookup, ref didOne);
- if(rValCond is TokenRValConst TokenRValConstrValCond)
- {
- didOne = true;
- return (TokenRValConstrValCond.IsConstBoolTrue() ? trueExpr : falseExpr).TryComputeConstant(lookup, ref didOne);
- }
- return this;
- }
- // debugging
- public override void DebString(StringBuilder sb)
- {
- condExpr.DebString(sb);
- sb.Append(" ? ");
- trueExpr.DebString(sb);
- sb.Append(" : ");
- falseExpr.DebString(sb);
- }
- }
- /**
- * @brief all constants supposed to end up here
- */
- public enum TokenRValConstType: byte { CHAR = 0, FLOAT = 1, INT = 2, KEY = 3, STRING = 4 };
- public class TokenRValConst: TokenRVal
- {
- public object val; // always a system type (char, int, double, string), never LSL-wrapped
- public TokenRValConstType type;
- public TokenType tokType;
- public TokenRValConst(Token original, object value) : base(original)
- {
- val = value;
- TokenType tt;
- if(val is char)
- {
- type = TokenRValConstType.CHAR;
- tt = new TokenTypeChar(this);
- }
- else if(val is int)
- {
- type = TokenRValConstType.INT;
- tt = new TokenTypeInt(this);
- }
- else if(val is double)
- {
- type = TokenRValConstType.FLOAT;
- tt = new TokenTypeFloat(this);
- }
- else if(val is string)
- {
- type = TokenRValConstType.STRING;
- tt = new TokenTypeStr(this);
- }
- else
- {
- throw new Exception("invalid constant type " + val.GetType());
- }
- tokType = (original is TokenType type1) ? type1 : tt;
- if(tokType is TokenTypeKey)
- {
- type = TokenRValConstType.KEY;
- }
- }
- public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig)
- {
- return tokType;
- }
- public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- return true;
- }
- public CompValu GetCompValu()
- {
- switch(type)
- {
- case TokenRValConstType.CHAR:
- {
- return new CompValuChar(tokType, (char)val);
- }
- case TokenRValConstType.FLOAT:
- {
- return new CompValuFloat(tokType, (double)val);
- }
- case TokenRValConstType.INT:
- {
- return new CompValuInteger(tokType, (int)val);
- }
- case TokenRValConstType.KEY:
- case TokenRValConstType.STRING:
- {
- return new CompValuString(tokType, (string)val);
- }
- default:
- throw new Exception("unknown type");
- }
- }
- public override TokenRVal TryComputeConstant(TCCLookup lookup, ref bool didOne)
- {
- // gotta end somewhere
- return this;
- }
- public bool IsConstBoolTrue()
- {
- switch(type)
- {
- case TokenRValConstType.CHAR:
- {
- return (char)val != 0;
- }
- case TokenRValConstType.FLOAT:
- {
- return (double)val != 0;
- }
- case TokenRValConstType.INT:
- {
- return (int)val != 0;
- }
- case TokenRValConstType.KEY:
- {
- return (string)val != "" && (string)val != ScriptBaseClass.NULL_KEY;
- }
- case TokenRValConstType.STRING:
- {
- return (string)val != "";
- }
- default:
- throw new Exception("unknown type");
- }
- }
- public override void DebString(StringBuilder sb)
- {
- if(val is char charval)
- {
- sb.Append('\'');
- EscapeQuotes(sb, new string(new char[] { charval }));
- sb.Append('\'');
- }
- else if(val is int intval)
- {
- sb.Append(intval);
- }
- else if(val is double dval)
- {
- string str = dval.ToString();
- sb.Append(str);
- if((str.IndexOf('.') < 0) &&
- (str.IndexOf('E') < 0) &&
- (str.IndexOf('e') < 0))
- {
- sb.Append(".0");
- }
- }
- else if(val is string sval)
- {
- sb.Append('"');
- EscapeQuotes(sb, sval);
- sb.Append('"');
- }
- else
- {
- throw new Exception("invalid constant type " + val.GetType());
- }
- }
- private static void EscapeQuotes(StringBuilder sb, string s)
- {
- foreach(char c in s)
- {
- switch(c)
- {
- case '\n':
- {
- sb.Append("\\n");
- break;
- }
- case '\t':
- {
- sb.Append("\\t");
- break;
- }
- case '\\':
- {
- sb.Append("\\\\");
- break;
- }
- case '\'':
- {
- sb.Append("\\'");
- break;
- }
- case '\"':
- {
- sb.Append("\\\"");
- break;
- }
- default:
- {
- sb.Append(c);
- break;
- }
- }
- }
- }
- }
- /**
- * @brief Default initialization value for the corresponding variable.
- */
- public class TokenRValInitDef: TokenRVal
- {
- public TokenType type;
- public static TokenRValInitDef Construct(TokenDeclVar tokenDeclVar)
- {
- TokenRValInitDef zhis = new(tokenDeclVar)
- {
- type = tokenDeclVar.type
- };
- return zhis;
- }
- private TokenRValInitDef(Token original) : base(original) { }
- public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig)
- {
- return type;
- }
- public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- // it's always just a constant so it's always very trivial
- return true;
- }
- public override void DebString(StringBuilder sb)
- {
- sb.Append("<default ");
- sb.Append(type.ToString());
- sb.Append('>');
- }
- }
- /**
- * @brief encapsulation of <rval> is <typeexp>
- */
- public class TokenRValIsType: TokenRVal
- {
- public TokenRVal rValExp;
- public TokenTypeExp typeExp;
- public TokenRValIsType(Token original) : base(original) { }
- public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig)
- {
- return new TokenTypeBool(rValExp);
- }
- public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- return rValExp.IsRValTrivial(scg, argsig);
- }
- }
- /**
- * @brief an R-value enclosed in brackets is an LSLList
- */
- public class TokenRValList: TokenRVal
- {
- public TokenRVal rVal; // null-terminated list of TokenRVal objects
- public int nItems;
- public TokenRValList(Token original) : base(original) { }
- public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig)
- {
- return new TokenTypeList(rVal);
- }
- public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- for(Token t = rVal; t != null; t = t.nextToken)
- {
- if(!((TokenRVal)t).IsRValTrivial(scg, null))
- return false;
- }
- return true;
- }
- public override void DebString(StringBuilder sb)
- {
- bool first = true;
- sb.Append('[');
- for(Token t = rVal; t != null; t = t.nextToken)
- {
- if(!first)
- sb.Append(',');
- sb.Append(' ');
- t.DebString(sb);
- first = false;
- }
- sb.Append(" ]");
- }
- }
- /**
- * @brief encapsulates '$new' arraytype '{' ... '}'
- */
- public class TokenRValNewArIni: TokenRVal
- {
- public TokenType arrayType;
- public TokenList valueList; // TokenList : a sub-list
- // TokenKwComma : a default value
- // TokenRVal : init expression
- public TokenRValNewArIni(Token original) : base(original)
- {
- valueList = new TokenList(original);
- }
- // type of the expression = the array type allocated by $new()
- public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig)
- {
- return arrayType;
- }
- // The expression is trivial if all the initializers are trivial.
- // An array's constructor is always trivial (no CheckRun() calls).
- public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- return ListIsTrivial(scg, valueList);
- }
- private bool ListIsTrivial(ScriptCodeGen scg, TokenList valList)
- {
- foreach(Token val in valList.tl)
- {
- if(val is TokenRVal TokenRValval)
- {
- if(!TokenRValval.IsRValTrivial(scg, null))
- return false;
- }
- if(val is TokenList TokenListval)
- {
- if(!ListIsTrivial(scg, TokenListval))
- return false;
- }
- }
- return true;
- }
- public override void DebString(StringBuilder sb)
- {
- sb.Append("new ");
- arrayType.DebString(sb);
- sb.Append(' ');
- valueList.DebString(sb);
- }
- }
- public class TokenList: Token
- {
- public List<Token> tl = new();
- public TokenList(Token original) : base(original) { }
- public override void DebString(StringBuilder sb)
- {
- sb.Append('{');
- bool first = true;
- foreach(Token t in tl)
- {
- if(!first)
- sb.Append(", ");
- t.DebString(sb);
- first = false;
- }
- sb.Append('}');
- }
- }
- /**
- * @brief a binary operator and its two operands
- */
- public class TokenRValOpBin: TokenRVal
- {
- public TokenRVal rValLeft;
- public TokenKw opcode;
- public TokenRVal rValRight;
- public TokenRValOpBin(TokenRVal left, TokenKw op, TokenRVal right) : base(op)
- {
- rValLeft = left;
- opcode = op;
- rValRight = right;
- }
- public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig)
- {
- // Comparisons and the like always return bool.
- string opstr = opcode.ToString();
- if((opstr == "==") || (opstr == "!=") || (opstr == ">=") || (opstr == ">") ||
- (opstr == "&&") || (opstr == "||") || (opstr == "<=") || (opstr == "<") ||
- (opstr == "&&&") || (opstr == "|||"))
- {
- return new TokenTypeBool(opcode);
- }
- // Comma is always type of right-hand operand.
- if(opstr == ",")
- return rValRight.GetRValType(scg, argsig);
- // Assignments are always the type of the left-hand operand,
- // including stuff like "+=".
- if(opstr.EndsWith("="))
- {
- return rValLeft.GetRValType(scg, argsig);
- }
- // string+something or something+string is always string.
- // except list+something or something+list is always a list.
- string lType = rValLeft.GetRValType(scg, argsig).ToString();
- string rType = rValRight.GetRValType(scg, argsig).ToString();
- if((opstr == "+") && ((lType == "list") || (rType == "list")))
- {
- return new TokenTypeList(opcode);
- }
- if((opstr == "+") && ((lType == "key") || (lType == "string") ||
- (rType == "key") || (rType == "string")))
- {
- return new TokenTypeStr(opcode);
- }
- // Everything else depends on both operands.
- string key = lType + opstr + rType;
- if(BinOpStr.defined.TryGetValue(key, out BinOpStr binOpStr))
- {
- return TokenType.FromSysType(opcode, binOpStr.outtype);
- }
- scg.ErrorMsg(opcode, "undefined operation " + key);
- return new TokenTypeVoid(opcode);
- }
- public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- return rValLeft.IsRValTrivial(scg, null) && rValRight.IsRValTrivial(scg, null);
- }
- /**
- * @brief If both operands are constants, maybe we can say the whole thing is a constant.
- */
- public override TokenRVal TryComputeConstant(TCCLookup lookup, ref bool didOne)
- {
- rValLeft = rValLeft.TryComputeConstant(lookup, ref didOne);
- rValRight = rValRight.TryComputeConstant(lookup, ref didOne);
- if((rValLeft is TokenRValConst TRValConstrValLeft) && (rValRight is TokenRValConst TRValConstrValRight))
- {
- //try
- {
- object val = opcode.binOpConst(TRValConstrValLeft.val, TRValConstrValRight.val);
- TokenRVal rValConst = new TokenRValConst(opcode, val);
- didOne = true;
- return rValConst;
- }
- //catch
- {
- }
- }
- return this;
- }
- // debugging
- public override void DebString(StringBuilder sb)
- {
- rValLeft.DebString(sb);
- sb.Append(' ');
- sb.Append(opcode.ToString());
- sb.Append(' ');
- rValRight.DebString(sb);
- }
- }
- /**
- * @brief an unary operator and its one operand
- */
- public class TokenRValOpUn: TokenRVal
- {
- public TokenKw opcode;
- public TokenRVal rVal;
- public TokenRValOpUn(TokenKw op, TokenRVal right) : base(op)
- {
- opcode = op;
- rVal = right;
- }
- public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig)
- {
- if(opcode is TokenKwExclam)
- return new TokenTypeInt(opcode);
- return rVal.GetRValType(scg, null);
- }
- public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- return rVal.IsRValTrivial(scg, null);
- }
- /**
- * @brief If operand is constant, maybe we can say the whole thing is a constant.
- */
- public override TokenRVal TryComputeConstant(TCCLookup lookup, ref bool didOne)
- {
- rVal = rVal.TryComputeConstant(lookup, ref didOne);
- if(rVal is TokenRValConst TokenRValConstrVal)
- {
- try
- {
- object val = opcode.unOpConst(TokenRValConstrVal.val);
- TokenRVal rValConst = new TokenRValConst(opcode, val);
- didOne = true;
- return rValConst;
- }
- catch
- {
- }
- }
- return this;
- }
- /**
- * @brief Serialization/Deserialization.
- */
- public TokenRValOpUn(Token original) : base(original) { }
- // debugging
- public override void DebString(StringBuilder sb)
- {
- sb.Append(opcode.ToString());
- rVal.DebString(sb);
- }
- }
- /**
- * @brief an R-value enclosed in parentheses
- */
- public class TokenRValParen: TokenRVal
- {
- public TokenRVal rVal;
- public TokenRValParen(Token original) : base(original) { }
- public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig)
- {
- // pass argsig through in this simple case, ie, let
- // them do something like (llOwnerSay)("blabla...");
- return rVal.GetRValType(scg, argsig);
- }
- public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- // pass argsig through in this simple case, ie, let
- // them do something like (llOwnerSay)("blabla...");
- return rVal.IsRValTrivial(scg, argsig);
- }
- /**
- * @brief If operand is constant, we can say the whole thing is a constant.
- */
- public override TokenRVal TryComputeConstant(TCCLookup lookup, ref bool didOne)
- {
- rVal = rVal.TryComputeConstant(lookup, ref didOne);
- if(rVal is TokenRValConst)
- {
- didOne = true;
- return rVal;
- }
- return this;
- }
- public override void DebString(StringBuilder sb)
- {
- sb.Append('(');
- rVal.DebString(sb);
- sb.Append(')');
- }
- }
- public class TokenRValRot: TokenRVal
- {
- public TokenRVal xRVal;
- public TokenRVal yRVal;
- public TokenRVal zRVal;
- public TokenRVal wRVal;
- public TokenRValRot(Token original) : base(original) { }
- public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig)
- {
- return new TokenTypeRot(xRVal);
- }
- public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- return xRVal.IsRValTrivial(scg, null) &&
- yRVal.IsRValTrivial(scg, null) &&
- zRVal.IsRValTrivial(scg, null) &&
- wRVal.IsRValTrivial(scg, null);
- }
- public override void DebString(StringBuilder sb)
- {
- sb.Append('<');
- xRVal.DebString(sb);
- sb.Append(',');
- yRVal.DebString(sb);
- sb.Append(',');
- zRVal.DebString(sb);
- sb.Append(',');
- wRVal.DebString(sb);
- sb.Append('>');
- }
- }
- /**
- * @brief 'this' is being used as an rval inside an instance method.
- */
- public class TokenRValThis: TokenRVal
- {
- public Token original;
- public TokenDeclSDTypeClass sdtClass;
- public TokenRValThis(Token original, TokenDeclSDTypeClass sdtClass) : base(original)
- {
- this.original = original;
- this.sdtClass = sdtClass;
- }
- public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig)
- {
- return sdtClass.MakeRefToken(original);
- }
- public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- return true; // ldarg.0/starg.0 can't possibly loop
- }
- // debugging
- public override void DebString(StringBuilder sb)
- {
- sb.Append("this");
- }
- }
- /**
- * @brief the 'undef' keyword is being used as a value in an expression.
- * It is the null object pointer and has type TokenTypeUndef.
- */
- public class TokenRValUndef: TokenRVal
- {
- readonly Token original;
- public TokenRValUndef(Token original) : base(original)
- {
- this.original = original;
- }
- public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig)
- {
- return new TokenTypeUndef(original);
- }
- public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- return true;
- }
- public override void DebString(StringBuilder sb)
- {
- sb.Append("undef");
- }
- }
- /**
- * @brief put 3 RVals together as a Vector value.
- */
- public class TokenRValVec: TokenRVal
- {
- public TokenRVal xRVal;
- public TokenRVal yRVal;
- public TokenRVal zRVal;
- public TokenRValVec(Token original) : base(original) { }
- public override TokenType GetRValType(ScriptCodeGen scg, TokenType[] argsig)
- {
- return new TokenTypeVec(xRVal);
- }
- public override bool IsRValTrivial(ScriptCodeGen scg, TokenType[] argsig)
- {
- return xRVal.IsRValTrivial(scg, null) &&
- yRVal.IsRValTrivial(scg, null) &&
- zRVal.IsRValTrivial(scg, null);
- }
- public override void DebString(StringBuilder sb)
- {
- sb.Append('<');
- xRVal.DebString(sb);
- sb.Append(',');
- yRVal.DebString(sb);
- sb.Append(',');
- zRVal.DebString(sb);
- sb.Append('>');
- }
- }
- /**
- * @brief encapsulates the whole script in a single token
- */
- public class TokenScript: Token
- {
- public int expiryDays = Int32.MaxValue;
- public TokenDeclState defaultState;
- public Dictionary<string, TokenDeclState> states = new();
- public VarDict variablesStack = new (false); // initial one is used for global functions and variables
- public TokenDeclVar globalVarInit; // $globalvarinit function
- // - performs explicit global var and static field inits
- private Dictionary<string, TokenDeclSDType> sdSrcTypes = new();
- private bool sdSrcTypesSealed = false;
- public TokenScript(Token original) : base(original) { }
- /*
- * Handle variable definition stack.
- * Generally a '{' pushes a new frame and a '}' pops the frame.
- * Function parameters are pushed in an additional frame (just outside the body's { ... } block)
- */
- public void PushVarFrame(bool locals)
- {
- PushVarFrame(new VarDict(locals));
- }
- public void PushVarFrame(VarDict newFrame)
- {
- newFrame.outerVarDict = variablesStack;
- variablesStack = newFrame;
- }
- public void PopVarFrame()
- {
- variablesStack = variablesStack.outerVarDict;
- }
- public bool AddVarEntry(TokenDeclVar var)
- {
- return variablesStack.AddEntry(var);
- }
- /*
- * Handle list of script-defined types.
- */
- public void sdSrcTypesSeal()
- {
- sdSrcTypesSealed = true;
- }
- public bool sdSrcTypesContainsKey(string key)
- {
- return sdSrcTypes.ContainsKey(key);
- }
- public bool sdSrcTypesTryGetValue(string key, out TokenDeclSDType value)
- {
- return sdSrcTypes.TryGetValue(key, out value);
- }
- public void sdSrcTypesAdd(string key, TokenDeclSDType value)
- {
- if(sdSrcTypesSealed)
- throw new Exception("sdSrcTypes is sealed");
- value.sdTypeIndex = sdSrcTypes.Count;
- sdSrcTypes.Add(key, value);
- }
- public void sdSrcTypesRep(string key, TokenDeclSDType value)
- {
- if(sdSrcTypesSealed)
- throw new Exception("sdSrcTypes is sealed");
- value.sdTypeIndex = sdSrcTypes[key].sdTypeIndex;
- sdSrcTypes[key] = value;
- }
- public void sdSrcTypesReplace(string key, TokenDeclSDType value)
- {
- if(sdSrcTypesSealed)
- throw new Exception("sdSrcTypes is sealed");
- sdSrcTypes[key] = value;
- }
- public Dictionary<string, TokenDeclSDType>.ValueCollection sdSrcTypesValues
- {
- get
- {
- return sdSrcTypes.Values;
- }
- }
- public int sdSrcTypesCount
- {
- get
- {
- return sdSrcTypes.Count;
- }
- }
- /**
- * @brief Debug output.
- */
- public override void DebString(StringBuilder sb)
- {
- /*
- * Script-defined types.
- */
- foreach(TokenDeclSDType srcType in sdSrcTypes.Values)
- {
- srcType.DebString(sb);
- }
- /*
- * Global constants.
- * Variables are handled by outputting the $globalvarinit function.
- */
- foreach(TokenDeclVar var in variablesStack)
- {
- if(var.constant)
- {
- var.DebString(sb);
- }
- }
- /*
- * Global functions.
- */
- foreach(TokenDeclVar var in variablesStack)
- {
- if(var == globalVarInit)
- {
- var.DebStringInitFields(sb);
- }
- else if(var.retType != null)
- {
- var.DebString(sb);
- }
- }
- /*
- * States and their event handler functions.
- */
- defaultState.DebString(sb);
- foreach(TokenDeclState st in states.Values)
- {
- st.DebString(sb);
- }
- }
- }
- /**
- * @brief state body declaration
- */
- public class TokenStateBody: Token
- {
- public TokenDeclVar eventFuncs;
- public int index = -1; // (codegen) row in ScriptHandlerEventTable (0=default)
- public TokenStateBody(Token original) : base(original) { }
- public override void DebString(StringBuilder sb)
- {
- sb.Append(" { ");
- for(Token t = eventFuncs; t != null; t = t.nextToken)
- {
- t.DebString(sb);
- }
- sb.Append(" } ");
- }
- }
- /**
- * @brief a single statement, such as ending on a semicolon or enclosed in braces
- * TokenStmt includes the terminating semicolon or the enclosing braces
- * Also includes @label; for jump targets.
- * Also includes stray ; null statements.
- * Also includes local variable declarations with or without initialization value.
- */
- public class TokenStmt: Token
- {
- public TokenStmt(Token original) : base(original) { }
- }
- /**
- * @brief a group of statements enclosed in braces
- */
- public class TokenStmtBlock: TokenStmt
- {
- public Token statements; // null-terminated list of statements, can also have TokenDeclVar's in here
- public TokenStmtBlock outerStmtBlock; // next outer stmtBlock or null if top-level, ie, function definition
- public TokenDeclVar function; // function it is part of
- public bool isTry; // true iff it's a try statement block
- public bool isCatch; // true iff it's a catch statement block
- public bool isFinally; // true iff it's a finally statement block
- public TokenStmtTry tryStmt; // set iff isTry|isCatch|isFinally is set
- public TokenStmtBlock(Token original) : base(original) { }
- // debugging
- public override void DebString(StringBuilder sb)
- {
- sb.Append("{ ");
- for(Token stmt = statements; stmt != null; stmt = stmt.nextToken)
- {
- stmt.DebString(sb);
- }
- sb.Append("} ");
- }
- }
- /**
- * @brief definition of branch target name
- */
- public class TokenStmtLabel: TokenStmt
- {
- public TokenName name; // the label's name
- public TokenStmtBlock block; // which block it is defined in
- public bool hasBkwdRefs = false;
- public bool labelTagged; // code gen: location of label
- public ScriptMyLabel labelStruct;
- public TokenStmtLabel(Token original) : base(original) { }
- public override void DebString(StringBuilder sb)
- {
- sb.Append('@');
- name.DebString(sb);
- sb.Append(';');
- }
- }
- /**
- * @brief those types of RVals with a semi-colon on the end
- * that are allowed to stand alone as statements
- */
- public class TokenStmtRVal: TokenStmt
- {
- public TokenRVal rVal;
- public TokenStmtRVal(Token original) : base(original) { }
- // debugging
- public override void DebString(StringBuilder sb)
- {
- rVal.DebString(sb);
- sb.Append("; ");
- }
- }
- public class TokenStmtBreak: TokenStmt
- {
- public TokenStmtBreak(Token original) : base(original) { }
- public override void DebString(StringBuilder sb)
- {
- sb.Append("break;");
- }
- }
- public class TokenStmtCont: TokenStmt
- {
- public TokenStmtCont(Token original) : base(original) { }
- public override void DebString(StringBuilder sb)
- {
- sb.Append("continue;");
- }
- }
- /**
- * @brief "do" statement
- */
- public class TokenStmtDo: TokenStmt
- {
- public TokenStmt bodyStmt;
- public TokenRValParen testRVal;
- public TokenStmtDo(Token original) : base(original) { }
- public override void DebString(StringBuilder sb)
- {
- sb.Append("do ");
- bodyStmt.DebString(sb);
- sb.Append(" while ");
- testRVal.DebString(sb);
- sb.Append(';');
- }
- }
- /**
- * @brief "for" statement
- */
- public class TokenStmtFor: TokenStmt
- {
- public TokenStmt initStmt; // there is always an init statement, though it may be a null statement
- public TokenRVal testRVal; // there may or may not be a test (null if not)
- public TokenRVal incrRVal; // there may or may not be an increment (null if not)
- public TokenStmt bodyStmt; // there is always a body statement, though it may be a null statement
- public TokenStmtFor(Token original) : base(original) { }
- public override void DebString(StringBuilder sb)
- {
- sb.Append("for (");
- if(initStmt != null)
- initStmt.DebString(sb);
- else
- sb.Append(';');
- testRVal?.DebString(sb);
- sb.Append(';');
- incrRVal?.DebString(sb);
- sb.Append(") ");
- bodyStmt.DebString(sb);
- }
- }
- /**
- * @brief "foreach" statement
- */
- public class TokenStmtForEach: TokenStmt
- {
- public TokenLVal keyLVal;
- public TokenLVal valLVal;
- public TokenRVal arrayRVal;
- public TokenStmt bodyStmt; // there is always a body statement, though it may be a null statement
- public TokenStmtForEach(Token original) : base(original) { }
- public override void DebString(StringBuilder sb)
- {
- sb.Append("foreach (");
- keyLVal?.DebString(sb);
- sb.Append(',');
- valLVal?.DebString(sb);
- sb.Append(" in ");
- arrayRVal.DebString(sb);
- sb.Append(')');
- bodyStmt.DebString(sb);
- }
- }
- public class TokenStmtIf: TokenStmt
- {
- public TokenRValParen testRVal;
- public TokenStmt trueStmt;
- public TokenStmt elseStmt;
- public TokenStmtIf(Token original) : base(original) { }
- public override void DebString(StringBuilder sb)
- {
- sb.Append("if ");
- testRVal.DebString(sb);
- sb.Append(' ');
- trueStmt.DebString(sb);
- if(elseStmt != null)
- {
- sb.Append(" else ");
- elseStmt.DebString(sb);
- }
- }
- }
- public class TokenStmtJump: TokenStmt
- {
- public TokenName label;
- public TokenStmtJump(Token original) : base(original) { }
- public override void DebString(StringBuilder sb)
- {
- sb.Append("jump ");
- label.DebString(sb);
- sb.Append(';');
- }
- }
- public class TokenStmtNull: TokenStmt
- {
- public TokenStmtNull(Token original) : base(original) { }
- public override void DebString(StringBuilder sb)
- {
- sb.Append(';');
- }
- }
- public class TokenStmtRet: TokenStmt
- {
- public TokenRVal rVal; // null if void
- public TokenStmtRet(Token original) : base(original) { }
- public override void DebString(StringBuilder sb)
- {
- sb.Append("return");
- if(rVal != null)
- {
- sb.Append(' ');
- rVal.DebString(sb);
- }
- sb.Append(';');
- }
- }
- /**
- * @brief statement that changes the current state.
- */
- public class TokenStmtState: TokenStmt
- {
- public TokenName state; // null for default
- public TokenStmtState(Token original) : base(original) { }
- public override void DebString(StringBuilder sb)
- {
- sb.Append("state ");
- sb.Append((state == null) ? "default" : state.val);
- sb.Append(';');
- }
- }
- /**
- * @brief Encapsulates a whole switch statement including the body and all cases.
- */
- public class TokenStmtSwitch: TokenStmt
- {
- public TokenRValParen testRVal; // the integer index expression
- public TokenSwitchCase cases = null; // list of all cases, linked by .nextCase
- public TokenSwitchCase lastCase = null; // used during reduce to point to last in 'cases' list
- public TokenStmtSwitch(Token original) : base(original) { }
- public override void DebString(StringBuilder sb)
- {
- sb.Append("switch ");
- testRVal.DebString(sb);
- sb.Append('{');
- for(TokenSwitchCase kase = cases; kase != null; kase = kase.nextCase)
- {
- kase.DebString(sb);
- }
- sb.Append('}');
- }
- }
- /**
- * @brief Encapsulates a case/default clause from a switch statement including the
- * two values and the corresponding body statements.
- */
- public class TokenSwitchCase: Token
- {
- public TokenSwitchCase nextCase; // next case in source-code order
- public TokenRVal rVal1; // null means 'default', else 'case'
- public TokenRVal rVal2; // null means 'case expr:', else 'case expr ... expr:'
- public TokenStmt stmts; // statements associated with the case
- public TokenStmt lastStmt; // used during reduce for building statement list
- public int val1; // codegen: value of rVal1 here
- public int val2; // codegen: value of rVal2 here
- public ScriptMyLabel label; // codegen: target label here
- public TokenSwitchCase nextSortedCase; // codegen: next case in ascending val order
- public string str1;
- public string str2;
- public TokenSwitchCase lowerCase;
- public TokenSwitchCase higherCase;
- public TokenSwitchCase(Token original) : base(original) { }
- public override void DebString(StringBuilder sb)
- {
- if(rVal1 == null)
- {
- sb.Append("default: ");
- }
- else
- {
- sb.Append("case ");
- rVal1.DebString(sb);
- if(rVal2 != null)
- {
- sb.Append(" ... ");
- rVal2.DebString(sb);
- }
- sb.Append(": ");
- }
- for(Token t = stmts; t != null; t = t.nextToken)
- {
- t.DebString(sb);
- }
- }
- }
- public class TokenStmtThrow: TokenStmt
- {
- public TokenRVal rVal; // null if rethrow style
- public TokenStmtThrow(Token original) : base(original) { }
- public override void DebString(StringBuilder sb)
- {
- sb.Append("throw ");
- rVal.DebString(sb);
- sb.Append(';');
- }
- }
- /**
- * @brief Encapsulates related try, catch and finally statements.
- */
- public class TokenStmtTry: TokenStmt
- {
- public TokenStmtBlock tryStmt;
- public TokenDeclVar catchVar; // null iff catchStmt is null
- public TokenStmtBlock catchStmt; // can be null
- public TokenStmtBlock finallyStmt; // can be null
- public Dictionary<string, IntermediateLeave> iLeaves = new();
- public TokenStmtTry(Token original) : base(original) { }
- public override void DebString(StringBuilder sb)
- {
- sb.Append("try ");
- tryStmt.DebString(sb);
- if(catchStmt != null)
- {
- sb.Append("catch (");
- sb.Append(catchVar.type.ToString());
- sb.Append(' ');
- sb.Append(catchVar.name.val);
- sb.Append(") ");
- catchStmt.DebString(sb);
- }
- if(finallyStmt != null)
- {
- sb.Append("finally ");
- finallyStmt.DebString(sb);
- }
- }
- }
- public class IntermediateLeave
- {
- public ScriptMyLabel jumpIntoLabel;
- public ScriptMyLabel jumpAwayLabel;
- }
- public class TokenStmtVarIniDef: TokenStmt
- {
- public TokenLVal var;
- public TokenStmtVarIniDef(Token original) : base(original) { }
- }
- public class TokenStmtWhile: TokenStmt
- {
- public TokenRValParen testRVal;
- public TokenStmt bodyStmt;
- public TokenStmtWhile(Token original) : base(original) { }
- public override void DebString(StringBuilder sb)
- {
- sb.Append("while ");
- testRVal.DebString(sb);
- sb.Append(' ');
- bodyStmt.DebString(sb);
- }
- }
- /**
- * @brief type expressions (right-hand of 'is' keyword).
- */
- public class TokenTypeExp: Token
- {
- public TokenTypeExp(Token original) : base(original) { }
- }
- public class TokenTypeExpBinOp: TokenTypeExp
- {
- public TokenTypeExp leftOp;
- public Token binOp;
- public TokenTypeExp rightOp;
- public TokenTypeExpBinOp(Token original) : base(original) { }
- }
- public class TokenTypeExpNot: TokenTypeExp
- {
- public TokenTypeExp typeExp;
- public TokenTypeExpNot(Token original) : base(original) { }
- }
- public class TokenTypeExpPar: TokenTypeExp
- {
- public TokenTypeExp typeExp;
- public TokenTypeExpPar(Token original) : base(original) { }
- }
- public class TokenTypeExpType: TokenTypeExp
- {
- public TokenType typeToken;
- public TokenTypeExpType(Token original) : base(original) { }
- }
- public class TokenTypeExpUndef: TokenTypeExp
- {
- public TokenTypeExpUndef(Token original) : base(original) { }
- }
- }
|