123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649 |
- /*
- ===========================================================================
- Doom 3 BFG Edition GPL Source Code
- Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
- This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
- Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
- In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
- If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
- ===========================================================================
- */
- #pragma hdrstop
- #include "../../idlib/precompiled.h"
- #include "../Game_local.h"
- #define FUNCTION_PRIORITY 2
- #define INT_PRIORITY 2
- #define NOT_PRIORITY 5
- #define TILDE_PRIORITY 5
- #define TOP_PRIORITY 7
- bool idCompiler::punctuationValid[ 256 ];
- char *idCompiler::punctuation[] = {
- "+=", "-=", "*=", "/=", "%=", "&=", "|=", "++", "--",
- "&&", "||", "<=", ">=", "==", "!=", "::", ";", ",",
- "~", "!", "*", "/", "%", "(", ")", "-", "+",
- "=", "[", "]", ".", "<", ">" , "&", "|", ":", NULL
- };
- opcode_t idCompiler::opcodes[] = {
- { "<RETURN>", "RETURN", -1, false, &def_void, &def_void, &def_void },
-
- { "++", "UINC_F", 1, true, &def_float, &def_void, &def_void },
- { "++", "UINCP_F", 1, true, &def_object, &def_field, &def_float },
- { "--", "UDEC_F", 1, true, &def_float, &def_void, &def_void },
- { "--", "UDECP_F", 1, true, &def_object, &def_field, &def_float },
- { "~", "COMP_F", -1, false, &def_float, &def_void, &def_float },
-
- { "*", "MUL_F", 3, false, &def_float, &def_float, &def_float },
- { "*", "MUL_V", 3, false, &def_vector, &def_vector, &def_float },
- { "*", "MUL_FV", 3, false, &def_float, &def_vector, &def_vector },
- { "*", "MUL_VF", 3, false, &def_vector, &def_float, &def_vector },
-
- { "/", "DIV", 3, false, &def_float, &def_float, &def_float },
- { "%", "MOD_F", 3, false, &def_float, &def_float, &def_float },
-
- { "+", "ADD_F", 4, false, &def_float, &def_float, &def_float },
- { "+", "ADD_V", 4, false, &def_vector, &def_vector, &def_vector },
- { "+", "ADD_S", 4, false, &def_string, &def_string, &def_string },
- { "+", "ADD_FS", 4, false, &def_float, &def_string, &def_string },
- { "+", "ADD_SF", 4, false, &def_string, &def_float, &def_string },
- { "+", "ADD_VS", 4, false, &def_vector, &def_string, &def_string },
- { "+", "ADD_SV", 4, false, &def_string, &def_vector, &def_string },
-
- { "-", "SUB_F", 4, false, &def_float, &def_float, &def_float },
- { "-", "SUB_V", 4, false, &def_vector, &def_vector, &def_vector },
-
- { "==", "EQ_F", 5, false, &def_float, &def_float, &def_float },
- { "==", "EQ_V", 5, false, &def_vector, &def_vector, &def_float },
- { "==", "EQ_S", 5, false, &def_string, &def_string, &def_float },
- { "==", "EQ_E", 5, false, &def_entity, &def_entity, &def_float },
- { "==", "EQ_EO", 5, false, &def_entity, &def_object, &def_float },
- { "==", "EQ_OE", 5, false, &def_object, &def_entity, &def_float },
- { "==", "EQ_OO", 5, false, &def_object, &def_object, &def_float },
-
- { "!=", "NE_F", 5, false, &def_float, &def_float, &def_float },
- { "!=", "NE_V", 5, false, &def_vector, &def_vector, &def_float },
- { "!=", "NE_S", 5, false, &def_string, &def_string, &def_float },
- { "!=", "NE_E", 5, false, &def_entity, &def_entity, &def_float },
- { "!=", "NE_EO", 5, false, &def_entity, &def_object, &def_float },
- { "!=", "NE_OE", 5, false, &def_object, &def_entity, &def_float },
- { "!=", "NE_OO", 5, false, &def_object, &def_object, &def_float },
-
- { "<=", "LE", 5, false, &def_float, &def_float, &def_float },
- { ">=", "GE", 5, false, &def_float, &def_float, &def_float },
- { "<", "LT", 5, false, &def_float, &def_float, &def_float },
- { ">", "GT", 5, false, &def_float, &def_float, &def_float },
-
- { ".", "INDIRECT_F", 1, false, &def_object, &def_field, &def_float },
- { ".", "INDIRECT_V", 1, false, &def_object, &def_field, &def_vector },
- { ".", "INDIRECT_S", 1, false, &def_object, &def_field, &def_string },
- { ".", "INDIRECT_E", 1, false, &def_object, &def_field, &def_entity },
- { ".", "INDIRECT_BOOL", 1, false, &def_object, &def_field, &def_boolean },
- { ".", "INDIRECT_OBJ", 1, false, &def_object, &def_field, &def_object },
- { ".", "ADDRESS", 1, false, &def_entity, &def_field, &def_pointer },
- { ".", "EVENTCALL", 2, false, &def_entity, &def_function, &def_void },
- { ".", "OBJECTCALL", 2, false, &def_object, &def_function, &def_void },
- { ".", "SYSCALL", 2, false, &def_void, &def_function, &def_void },
- { "=", "STORE_F", 6, true, &def_float, &def_float, &def_float },
- { "=", "STORE_V", 6, true, &def_vector, &def_vector, &def_vector },
- { "=", "STORE_S", 6, true, &def_string, &def_string, &def_string },
- { "=", "STORE_ENT", 6, true, &def_entity, &def_entity, &def_entity },
- { "=", "STORE_BOOL", 6, true, &def_boolean, &def_boolean, &def_boolean },
- { "=", "STORE_OBJENT", 6, true, &def_object, &def_entity, &def_object },
- { "=", "STORE_OBJ", 6, true, &def_object, &def_object, &def_object },
- { "=", "STORE_OBJENT", 6, true, &def_entity, &def_object, &def_object },
-
- { "=", "STORE_FTOS", 6, true, &def_string, &def_float, &def_string },
- { "=", "STORE_BTOS", 6, true, &def_string, &def_boolean, &def_string },
- { "=", "STORE_VTOS", 6, true, &def_string, &def_vector, &def_string },
- { "=", "STORE_FTOBOOL", 6, true, &def_boolean, &def_float, &def_boolean },
- { "=", "STORE_BOOLTOF", 6, true, &def_float, &def_boolean, &def_float },
- { "=", "STOREP_F", 6, true, &def_pointer, &def_float, &def_float },
- { "=", "STOREP_V", 6, true, &def_pointer, &def_vector, &def_vector },
- { "=", "STOREP_S", 6, true, &def_pointer, &def_string, &def_string },
- { "=", "STOREP_ENT", 6, true, &def_pointer, &def_entity, &def_entity },
- { "=", "STOREP_FLD", 6, true, &def_pointer, &def_field, &def_field },
- { "=", "STOREP_BOOL", 6, true, &def_pointer, &def_boolean, &def_boolean },
- { "=", "STOREP_OBJ", 6, true, &def_pointer, &def_object, &def_object },
- { "=", "STOREP_OBJENT", 6, true, &def_pointer, &def_object, &def_object },
- { "<=>", "STOREP_FTOS", 6, true, &def_pointer, &def_float, &def_string },
- { "<=>", "STOREP_BTOS", 6, true, &def_pointer, &def_boolean, &def_string },
- { "<=>", "STOREP_VTOS", 6, true, &def_pointer, &def_vector, &def_string },
- { "<=>", "STOREP_FTOBOOL", 6, true, &def_pointer, &def_float, &def_boolean },
- { "<=>", "STOREP_BOOLTOF", 6, true, &def_pointer, &def_boolean, &def_float },
-
- { "*=", "UMUL_F", 6, true, &def_float, &def_float, &def_void },
- { "*=", "UMUL_V", 6, true, &def_vector, &def_float, &def_void },
- { "/=", "UDIV_F", 6, true, &def_float, &def_float, &def_void },
- { "/=", "UDIV_V", 6, true, &def_vector, &def_float, &def_void },
- { "%=", "UMOD_F", 6, true, &def_float, &def_float, &def_void },
- { "+=", "UADD_F", 6, true, &def_float, &def_float, &def_void },
- { "+=", "UADD_V", 6, true, &def_vector, &def_vector, &def_void },
- { "-=", "USUB_F", 6, true, &def_float, &def_float, &def_void },
- { "-=", "USUB_V", 6, true, &def_vector, &def_vector, &def_void },
- { "&=", "UAND_F", 6, true, &def_float, &def_float, &def_void },
- { "|=", "UOR_F", 6, true, &def_float, &def_float, &def_void },
-
- { "!", "NOT_BOOL", -1, false, &def_boolean, &def_void, &def_float },
- { "!", "NOT_F", -1, false, &def_float, &def_void, &def_float },
- { "!", "NOT_V", -1, false, &def_vector, &def_void, &def_float },
- { "!", "NOT_S", -1, false, &def_vector, &def_void, &def_float },
- { "!", "NOT_ENT", -1, false, &def_entity, &def_void, &def_float },
- { "<NEG_F>", "NEG_F", -1, false, &def_float, &def_void, &def_float },
- { "<NEG_V>", "NEG_V", -1, false, &def_vector, &def_void, &def_vector },
- { "int", "INT_F", -1, false, &def_float, &def_void, &def_float },
-
- { "<IF>", "IF", -1, false, &def_float, &def_jumpoffset, &def_void },
- { "<IFNOT>", "IFNOT", -1, false, &def_float, &def_jumpoffset, &def_void },
-
- // calls returns REG_RETURN
- { "<CALL>", "CALL", -1, false, &def_function, &def_argsize, &def_void },
- { "<THREAD>", "THREAD", -1, false, &def_function, &def_argsize, &def_void },
- { "<THREAD>", "OBJTHREAD", -1, false, &def_function, &def_argsize, &def_void },
-
- { "<PUSH>", "PUSH_F", -1, false, &def_float, &def_float, &def_void },
- { "<PUSH>", "PUSH_V", -1, false, &def_vector, &def_vector, &def_void },
- { "<PUSH>", "PUSH_S", -1, false, &def_string, &def_string, &def_void },
- { "<PUSH>", "PUSH_ENT", -1, false, &def_entity, &def_entity, &def_void },
- { "<PUSH>", "PUSH_OBJ", -1, false, &def_object, &def_object, &def_void },
- { "<PUSH>", "PUSH_OBJENT", -1, false, &def_entity, &def_object, &def_void },
- { "<PUSH>", "PUSH_FTOS", -1, false, &def_string, &def_float, &def_void },
- { "<PUSH>", "PUSH_BTOF", -1, false, &def_float, &def_boolean, &def_void },
- { "<PUSH>", "PUSH_FTOB", -1, false, &def_boolean, &def_float, &def_void },
- { "<PUSH>", "PUSH_VTOS", -1, false, &def_string, &def_vector, &def_void },
- { "<PUSH>", "PUSH_BTOS", -1, false, &def_string, &def_boolean, &def_void },
-
- { "<GOTO>", "GOTO", -1, false, &def_jumpoffset, &def_void, &def_void },
-
- { "&&", "AND", 7, false, &def_float, &def_float, &def_float },
- { "&&", "AND_BOOLF", 7, false, &def_boolean, &def_float, &def_float },
- { "&&", "AND_FBOOL", 7, false, &def_float, &def_boolean, &def_float },
- { "&&", "AND_BOOLBOOL", 7, false, &def_boolean, &def_boolean, &def_float },
- { "||", "OR", 7, false, &def_float, &def_float, &def_float },
- { "||", "OR_BOOLF", 7, false, &def_boolean, &def_float, &def_float },
- { "||", "OR_FBOOL", 7, false, &def_float, &def_boolean, &def_float },
- { "||", "OR_BOOLBOOL", 7, false, &def_boolean, &def_boolean, &def_float },
-
- { "&", "BITAND", 3, false, &def_float, &def_float, &def_float },
- { "|", "BITOR", 3, false, &def_float, &def_float, &def_float },
- { "<BREAK>", "BREAK", -1, false, &def_float, &def_void, &def_void },
- { "<CONTINUE>", "CONTINUE", -1, false, &def_float, &def_void, &def_void },
- { NULL }
- };
- /*
- ================
- idCompiler::idCompiler()
- ================
- */
- idCompiler::idCompiler() {
- char **ptr;
- int id;
- // make sure we have the right # of opcodes in the table
- assert( ( sizeof( opcodes ) / sizeof( opcodes[ 0 ] ) ) == ( NUM_OPCODES + 1 ) );
- parserPtr = &parser;
- callthread = false;
- loopDepth = 0;
- eof = false;
- braceDepth = 0;
- immediateType = NULL;
- basetype = NULL;
- currentLineNumber = 0;
- currentFileNumber = 0;
- errorCount = 0;
- console = false;
- scope = &def_namespace;
- memset( &immediate, 0, sizeof( immediate ) );
- memset( punctuationValid, 0, sizeof( punctuationValid ) );
- for( ptr = punctuation; *ptr != NULL; ptr++ ) {
- id = parserPtr->GetPunctuationId( *ptr );
- if ( ( id >= 0 ) && ( id < 256 ) ) {
- punctuationValid[ id ] = true;
- }
- }
- }
- /*
- ============
- idCompiler::Error
- Aborts the current file load
- ============
- */
- void idCompiler::Error( const char *message, ... ) const {
- va_list argptr;
- char string[ 1024 ];
- va_start( argptr, message );
- vsprintf( string, message, argptr );
- va_end( argptr );
- throw idCompileError( string );
- }
- /*
- ============
- idCompiler::Warning
- Prints a warning about the current line
- ============
- */
- void idCompiler::Warning( const char *message, ... ) const {
- va_list argptr;
- char string[ 1024 ];
- va_start( argptr, message );
- vsprintf( string, message, argptr );
- va_end( argptr );
- parserPtr->Warning( "%s", string );
- }
- /*
- ============
- idCompiler::VirtualFunctionConstant
- Creates a def for an index into a virtual function table
- ============
- */
- ID_INLINE idVarDef *idCompiler::VirtualFunctionConstant( idVarDef *func ) {
- eval_t eval;
- memset( &eval, 0, sizeof( eval ) );
- eval._int = func->scope->TypeDef()->GetFunctionNumber( func->value.functionPtr );
- if ( eval._int < 0 ) {
- Error( "Function '%s' not found in scope '%s'", func->Name(), func->scope->Name() );
- }
-
- return GetImmediate( &type_virtualfunction, &eval, "" );
- }
- /*
- ============
- idCompiler::SizeConstant
- Creates a def for a size constant
- ============
- */
- ID_INLINE idVarDef *idCompiler::SizeConstant( int size ) {
- eval_t eval;
- memset( &eval, 0, sizeof( eval ) );
- eval._int = size;
- return GetImmediate( &type_argsize, &eval, "" );
- }
- /*
- ============
- idCompiler::JumpConstant
- Creates a def for a jump constant
- ============
- */
- ID_INLINE idVarDef *idCompiler::JumpConstant( int value ) {
- eval_t eval;
- memset( &eval, 0, sizeof( eval ) );
- eval._int = value;
- return GetImmediate( &type_jumpoffset, &eval, "" );
- }
- /*
- ============
- idCompiler::JumpDef
- Creates a def for a relative jump from one code location to another
- ============
- */
- ID_INLINE idVarDef *idCompiler::JumpDef( int jumpfrom, int jumpto ) {
- return JumpConstant( jumpto - jumpfrom );
- }
- /*
- ============
- idCompiler::JumpTo
- Creates a def for a relative jump from current code location
- ============
- */
- ID_INLINE idVarDef *idCompiler::JumpTo( int jumpto ) {
- return JumpDef( gameLocal.program.NumStatements(), jumpto );
- }
- /*
- ============
- idCompiler::JumpFrom
- Creates a def for a relative jump from code location to current code location
- ============
- */
- ID_INLINE idVarDef *idCompiler::JumpFrom( int jumpfrom ) {
- return JumpDef( jumpfrom, gameLocal.program.NumStatements() );
- }
- /*
- ============
- idCompiler::Divide
- ============
- */
- ID_INLINE float idCompiler::Divide( float numerator, float denominator ) {
- if ( denominator == 0 ) {
- Error( "Divide by zero" );
- }
- return numerator / denominator;
- }
- /*
- ============
- idCompiler::FindImmediate
- tries to find an existing immediate with the same value
- ============
- */
- idVarDef *idCompiler::FindImmediate( const idTypeDef *type, const eval_t *eval, const char *string ) const {
- idVarDef *def;
- etype_t etype;
- etype = type->Type();
- // check for a constant with the same value
- for( def = gameLocal.program.GetDefList( "<IMMEDIATE>" ); def != NULL; def = def->Next() ) {
- if ( def->TypeDef() != type ) {
- continue;
- }
- switch( etype ) {
- case ev_field :
- if ( *def->value.intPtr == eval->_int ) {
- return def;
- }
- break;
- case ev_argsize :
- if ( def->value.argSize == eval->_int ) {
- return def;
- }
- break;
- case ev_jumpoffset :
- if ( def->value.jumpOffset == eval->_int ) {
- return def;
- }
- break;
- case ev_entity :
- if ( *def->value.intPtr == eval->entity ) {
- return def;
- }
- break;
- case ev_string :
- if ( idStr::Cmp( def->value.stringPtr, string ) == 0 ) {
- return def;
- }
- break;
- case ev_float :
- if ( *def->value.floatPtr == eval->_float ) {
- return def;
- }
- break;
- case ev_virtualfunction :
- if ( def->value.virtualFunction == eval->_int ) {
- return def;
- }
- break;
- case ev_vector :
- if ( ( def->value.vectorPtr->x == eval->vector[ 0 ] ) &&
- ( def->value.vectorPtr->y == eval->vector[ 1 ] ) &&
- ( def->value.vectorPtr->z == eval->vector[ 2 ] ) ) {
- return def;
- }
- break;
- default :
- Error( "weird immediate type" );
- break;
- }
- }
- return NULL;
- }
- /*
- ============
- idCompiler::GetImmediate
- returns an existing immediate with the same value, or allocates a new one
- ============
- */
- idVarDef *idCompiler::GetImmediate( idTypeDef *type, const eval_t *eval, const char *string ) {
- idVarDef *def;
- def = FindImmediate( type, eval, string );
- if ( def ) {
- def->numUsers++;
- } else {
- // allocate a new def
- def = gameLocal.program.AllocDef( type, "<IMMEDIATE>", &def_namespace, true );
- if ( type->Type() == ev_string ) {
- def->SetString( string, true );
- } else {
- def->SetValue( *eval, true );
- }
- }
- return def;
- }
- /*
- ============
- idCompiler::OptimizeOpcode
- try to optimize when the operator works on constants only
- ============
- */
- idVarDef *idCompiler::OptimizeOpcode( const opcode_t *op, idVarDef *var_a, idVarDef *var_b ) {
- eval_t c;
- idTypeDef *type;
- if ( var_a == NULL || var_a->initialized != idVarDef::initializedConstant ) {
- return NULL;
- }
- if ( var_b == NULL || var_b->initialized != idVarDef::initializedConstant ) {
- return NULL;
- }
- idVec3 &vec_c = *reinterpret_cast<idVec3 *>( &c.vector[ 0 ] );
- memset( &c, 0, sizeof( c ) );
- switch( op - opcodes ) {
- case OP_ADD_F: c._float = *var_a->value.floatPtr + *var_b->value.floatPtr; type = &type_float; break;
- case OP_ADD_V: vec_c = *var_a->value.vectorPtr + *var_b->value.vectorPtr; type = &type_vector; break;
- case OP_SUB_F: c._float = *var_a->value.floatPtr - *var_b->value.floatPtr; type = &type_float; break;
- case OP_SUB_V: vec_c = *var_a->value.vectorPtr - *var_b->value.vectorPtr; type = &type_vector; break;
- case OP_MUL_F: c._float = *var_a->value.floatPtr * *var_b->value.floatPtr; type = &type_float; break;
- case OP_MUL_V: c._float = *var_a->value.vectorPtr * *var_b->value.vectorPtr; type = &type_float; break;
- case OP_MUL_FV: vec_c = *var_b->value.vectorPtr * *var_a->value.floatPtr; type = &type_vector; break;
- case OP_MUL_VF: vec_c = *var_a->value.vectorPtr * *var_b->value.floatPtr; type = &type_vector; break;
- case OP_DIV_F: c._float = Divide( *var_a->value.floatPtr, *var_b->value.floatPtr ); type = &type_float; break;
- case OP_MOD_F: c._float = (int)*var_a->value.floatPtr % (int)*var_b->value.floatPtr; type = &type_float; break;
- case OP_BITAND: c._float = ( int )*var_a->value.floatPtr & ( int )*var_b->value.floatPtr; type = &type_float; break;
- case OP_BITOR: c._float = ( int )*var_a->value.floatPtr | ( int )*var_b->value.floatPtr; type = &type_float; break;
- case OP_GE: c._float = *var_a->value.floatPtr >= *var_b->value.floatPtr; type = &type_float; break;
- case OP_LE: c._float = *var_a->value.floatPtr <= *var_b->value.floatPtr; type = &type_float; break;
- case OP_GT: c._float = *var_a->value.floatPtr > *var_b->value.floatPtr; type = &type_float; break;
- case OP_LT: c._float = *var_a->value.floatPtr < *var_b->value.floatPtr; type = &type_float; break;
- case OP_AND: c._float = *var_a->value.floatPtr && *var_b->value.floatPtr; type = &type_float; break;
- case OP_OR: c._float = *var_a->value.floatPtr || *var_b->value.floatPtr; type = &type_float; break;
- case OP_NOT_BOOL: c._int = !*var_a->value.intPtr; type = &type_boolean; break;
- case OP_NOT_F: c._float = !*var_a->value.floatPtr; type = &type_float; break;
- case OP_NOT_V: c._float = !var_a->value.vectorPtr->x && !var_a->value.vectorPtr->y && !var_a->value.vectorPtr->z; type = &type_float; break;
- case OP_NEG_F: c._float = -*var_a->value.floatPtr; type = &type_float; break;
- case OP_NEG_V: vec_c = -*var_a->value.vectorPtr; type = &type_vector; break;
- case OP_INT_F: c._float = ( int )*var_a->value.floatPtr; type = &type_float; break;
- case OP_EQ_F: c._float = ( *var_a->value.floatPtr == *var_b->value.floatPtr ); type = &type_float; break;
- case OP_EQ_V: c._float = var_a->value.vectorPtr->Compare( *var_b->value.vectorPtr ); type = &type_float; break;
- case OP_EQ_E: c._float = ( *var_a->value.intPtr == *var_b->value.intPtr ); type = &type_float; break;
- case OP_NE_F: c._float = ( *var_a->value.floatPtr != *var_b->value.floatPtr ); type = &type_float; break;
- case OP_NE_V: c._float = !var_a->value.vectorPtr->Compare( *var_b->value.vectorPtr ); type = &type_float; break;
- case OP_NE_E: c._float = ( *var_a->value.intPtr != *var_b->value.intPtr ); type = &type_float; break;
- case OP_UADD_F: c._float = *var_b->value.floatPtr + *var_a->value.floatPtr; type = &type_float; break;
- case OP_USUB_F: c._float = *var_b->value.floatPtr - *var_a->value.floatPtr; type = &type_float; break;
- case OP_UMUL_F: c._float = *var_b->value.floatPtr * *var_a->value.floatPtr; type = &type_float; break;
- case OP_UDIV_F: c._float = Divide( *var_b->value.floatPtr, *var_a->value.floatPtr ); type = &type_float; break;
- case OP_UMOD_F: c._float = ( int ) *var_b->value.floatPtr % ( int )*var_a->value.floatPtr; type = &type_float; break;
- case OP_UOR_F: c._float = ( int )*var_b->value.floatPtr | ( int )*var_a->value.floatPtr; type = &type_float; break;
- case OP_UAND_F: c._float = ( int )*var_b->value.floatPtr & ( int )*var_a->value.floatPtr; type = &type_float; break;
- case OP_UINC_F: c._float = *var_a->value.floatPtr + 1; type = &type_float; break;
- case OP_UDEC_F: c._float = *var_a->value.floatPtr - 1; type = &type_float; break;
- case OP_COMP_F: c._float = ( float )~( int )*var_a->value.floatPtr; type = &type_float; break;
- default: type = NULL; break;
- }
- if ( !type ) {
- return NULL;
- }
- if ( var_a ) {
- var_a->numUsers--;
- if ( var_a->numUsers <= 0 ) {
- gameLocal.program.FreeDef( var_a, NULL );
- }
- }
- if ( var_b ) {
- var_b->numUsers--;
- if ( var_b->numUsers <= 0 ) {
- gameLocal.program.FreeDef( var_b, NULL );
- }
- }
- return GetImmediate( type, &c, "" );
- }
- /*
- ============
- idCompiler::EmitOpcode
- Emits a primitive statement, returning the var it places it's value in
- ============
- */
- idVarDef *idCompiler::EmitOpcode( const opcode_t *op, idVarDef *var_a, idVarDef *var_b ) {
- statement_t *statement;
- idVarDef *var_c;
- var_c = OptimizeOpcode( op, var_a, var_b );
- if ( var_c ) {
- return var_c;
- }
- if ( var_a && !strcmp( var_a->Name(), RESULT_STRING ) ) {
- var_a->numUsers++;
- }
- if ( var_b && !strcmp( var_b->Name(), RESULT_STRING ) ) {
- var_b->numUsers++;
- }
-
- statement = gameLocal.program.AllocStatement();
- statement->linenumber = currentLineNumber;
- statement->file = currentFileNumber;
-
- if ( ( op->type_c == &def_void ) || op->rightAssociative ) {
- // ifs, gotos, and assignments don't need vars allocated
- var_c = NULL;
- } else {
- // allocate result space
- // try to reuse result defs as much as possible
- var_c = gameLocal.program.FindFreeResultDef( op->type_c->TypeDef(), RESULT_STRING, scope, var_a, var_b );
- // set user count back to 1, a result def needs to be used twice before it can be reused
- var_c->numUsers = 1;
- }
- statement->op = op - opcodes;
- statement->a = var_a;
- statement->b = var_b;
- statement->c = var_c;
- if ( op->rightAssociative ) {
- return var_a;
- }
- return var_c;
- }
- /*
- ============
- idCompiler::EmitOpcode
- Emits a primitive statement, returning the var it places it's value in
- ============
- */
- ID_INLINE idVarDef *idCompiler::EmitOpcode( int op, idVarDef *var_a, idVarDef *var_b ) {
- return EmitOpcode( &opcodes[ op ], var_a, var_b );
- }
- /*
- ============
- idCompiler::EmitPush
- Emits an opcode to push the variable onto the stack.
- ============
- */
- bool idCompiler::EmitPush( idVarDef *expression, const idTypeDef *funcArg ) {
- opcode_t *op;
- opcode_t *out;
- out = NULL;
- for( op = &opcodes[ OP_PUSH_F ]; op->name && !strcmp( op->name, "<PUSH>" ); op++ ) {
- if ( ( funcArg->Type() == op->type_a->Type() ) && ( expression->Type() == op->type_b->Type() ) ) {
- out = op;
- break;
- }
- }
- if ( !out ) {
- if ( ( expression->TypeDef() != funcArg ) && !expression->TypeDef()->Inherits( funcArg ) ) {
- return false;
- }
- out = &opcodes[ OP_PUSH_ENT ];
- }
- EmitOpcode( out, expression, 0 );
- return true;
- }
- /*
- ==============
- idCompiler::NextToken
- Sets token, immediateType, and possibly immediate
- ==============
- */
- void idCompiler::NextToken() {
- int i;
- // reset our type
- immediateType = NULL;
- memset( &immediate, 0, sizeof( immediate ) );
- // Save the token's line number and filename since when we emit opcodes the current
- // token is always the next one to be read
- currentLineNumber = token.line;
- currentFileNumber = gameLocal.program.GetFilenum( parserPtr->GetFileName() );
- if ( !parserPtr->ReadToken( &token ) ) {
- eof = true;
- return;
- }
- if ( currentFileNumber != gameLocal.program.GetFilenum( parserPtr->GetFileName() ) ) {
- if ( ( braceDepth > 0 ) && ( token != "}" ) ) {
- // missing a closing brace. try to give as much info as possible.
- if ( scope->Type() == ev_function ) {
- Error( "Unexpected end of file inside function '%s'. Missing closing braces.", scope->Name() );
- } else if ( scope->Type() == ev_object ) {
- Error( "Unexpected end of file inside object '%s'. Missing closing braces.", scope->Name() );
- } else if ( scope->Type() == ev_namespace ) {
- Error( "Unexpected end of file inside namespace '%s'. Missing closing braces.", scope->Name() );
- } else {
- Error( "Unexpected end of file inside braced section" );
- }
- }
- }
- switch( token.type ) {
- case TT_STRING:
- // handle quoted strings as a unit
- immediateType = &type_string;
- return;
- case TT_LITERAL: {
- // handle quoted vectors as a unit
- immediateType = &type_vector;
- idLexer lex( token, token.Length(), parserPtr->GetFileName(), LEXFL_NOERRORS );
- idToken token2;
- for( i = 0; i < 3; i++ ) {
- if ( !lex.ReadToken( &token2 ) ) {
- Error( "Couldn't read vector. '%s' is not in the form of 'x y z'", token.c_str() );
- }
- if ( token2.type == TT_PUNCTUATION && token2 == "-" ) {
- if ( !lex.CheckTokenType( TT_NUMBER, 0, &token2 ) ) {
- Error( "expected a number following '-' but found '%s' in vector '%s'", token2.c_str(), token.c_str() );
- }
- immediate.vector[ i ] = -token2.GetFloatValue();
- } else if ( token2.type == TT_NUMBER ) {
- immediate.vector[ i ] = token2.GetFloatValue();
- } else {
- Error( "vector '%s' is not in the form of 'x y z'. expected float value, found '%s'", token.c_str(), token2.c_str() );
- }
- }
- return;
- }
- case TT_NUMBER:
- immediateType = &type_float;
- immediate._float = token.GetFloatValue();
- return;
- case TT_PUNCTUATION:
- // entity names
- if ( token == "$" ) {
- immediateType = &type_entity;
- parserPtr->ReadToken( &token );
- return;
- }
- if ( token == "{" ) {
- braceDepth++;
- return;
- }
- if ( token == "}" ) {
- braceDepth--;
- return;
- }
- if ( punctuationValid[ token.subtype ] ) {
- return;
- }
- Error( "Unknown punctuation '%s'", token.c_str() );
- break;
- case TT_NAME:
- return;
- default:
- Error( "Unknown token '%s'", token.c_str() );
- }
- }
- /*
- =============
- idCompiler::ExpectToken
- Issues an Error if the current token isn't equal to string
- Gets the next token
- =============
- */
- void idCompiler::ExpectToken( const char *string ) {
- if ( token != string ) {
- Error( "expected '%s', found '%s'", string, token.c_str() );
- }
- NextToken();
- }
- /*
- =============
- idCompiler::CheckToken
- Returns true and gets the next token if the current token equals string
- Returns false and does nothing otherwise
- =============
- */
- bool idCompiler::CheckToken( const char *string ) {
- if ( token != string ) {
- return false;
- }
-
- NextToken();
-
- return true;
- }
- /*
- ============
- idCompiler::ParseName
- Checks to see if the current token is a valid name
- ============
- */
- void idCompiler::ParseName( idStr &name ) {
- if ( token.type != TT_NAME ) {
- Error( "'%s' is not a name", token.c_str() );
- }
- name = token;
- NextToken();
- }
- /*
- ============
- idCompiler::SkipOutOfFunction
- For error recovery, pops out of nested braces
- ============
- */
- void idCompiler::SkipOutOfFunction() {
- while( braceDepth ) {
- parserPtr->SkipBracedSection( false );
- braceDepth--;
- }
- NextToken();
- }
- /*
- ============
- idCompiler::SkipToSemicolon
- For error recovery
- ============
- */
- void idCompiler::SkipToSemicolon() {
- do {
- if ( CheckToken( ";" ) ) {
- return;
- }
- NextToken();
- } while( !eof );
- }
- /*
- ============
- idCompiler::CheckType
- Parses a variable type, including functions types
- ============
- */
- idTypeDef *idCompiler::CheckType() {
- idTypeDef *type;
-
- if ( token == "float" ) {
- type = &type_float;
- } else if ( token == "vector" ) {
- type = &type_vector;
- } else if ( token == "entity" ) {
- type = &type_entity;
- } else if ( token == "string" ) {
- type = &type_string;
- } else if ( token == "void" ) {
- type = &type_void;
- } else if ( token == "object" ) {
- type = &type_object;
- } else if ( token == "boolean" ) {
- type = &type_boolean;
- } else if ( token == "namespace" ) {
- type = &type_namespace;
- } else if ( token == "scriptEvent" ) {
- type = &type_scriptevent;
- } else {
- type = gameLocal.program.FindType( token.c_str() );
- if ( type != NULL && !type->Inherits( &type_object ) ) {
- type = NULL;
- }
- }
-
- return type;
- }
- /*
- ============
- idCompiler::ParseType
- Parses a variable type, including functions types
- ============
- */
- idTypeDef *idCompiler::ParseType() {
- idTypeDef *type;
-
- type = CheckType();
- if ( !type ) {
- Error( "\"%s\" is not a type", token.c_str() );
- }
- if ( ( type == &type_scriptevent ) && ( scope != &def_namespace ) ) {
- Error( "scriptEvents can only defined in the global namespace" );
- }
- if ( ( type == &type_namespace ) && ( scope->Type() != ev_namespace ) ) {
- Error( "A namespace may only be defined globally, or within another namespace" );
- }
- NextToken();
-
- return type;
- }
- /*
- ============
- idCompiler::ParseImmediate
- Looks for a preexisting constant
- ============
- */
- idVarDef *idCompiler::ParseImmediate() {
- idVarDef *def;
- def = GetImmediate( immediateType, &immediate, token.c_str() );
- NextToken();
- return def;
- }
- /*
- ============
- idCompiler::EmitFunctionParms
- ============
- */
- idVarDef *idCompiler::EmitFunctionParms( int op, idVarDef *func, int startarg, int startsize, idVarDef *object ) {
- idVarDef *e;
- const idTypeDef *type;
- const idTypeDef *funcArg;
- idVarDef *returnDef;
- idTypeDef *returnType;
- int arg;
- int size;
- int resultOp;
- type = func->TypeDef();
- if ( func->Type() != ev_function ) {
- Error( "'%s' is not a function", func->Name() );
- }
- // copy the parameters to the global parameter variables
- arg = startarg;
- size = startsize;
- if ( !CheckToken( ")" ) ) {
- do {
- if ( arg >= type->NumParameters() ) {
- Error( "too many parameters" );
- }
- e = GetExpression( TOP_PRIORITY );
- funcArg = type->GetParmType( arg );
- if ( !EmitPush( e, funcArg ) ) {
- Error( "type mismatch on parm %i of call to '%s'", arg + 1, func->Name() );
- }
- if ( funcArg->Type() == ev_object ) {
- size += type_object.Size();
- } else {
- size += funcArg->Size();
- }
- arg++;
- } while( CheckToken( "," ) );
-
- ExpectToken( ")" );
- }
- if ( arg < type->NumParameters() ) {
- Error( "too few parameters for function '%s'", func->Name() );
- }
- if ( op == OP_CALL ) {
- EmitOpcode( op, func, 0 );
- } else if ( ( op == OP_OBJECTCALL ) || ( op == OP_OBJTHREAD ) ) {
- EmitOpcode( op, object, VirtualFunctionConstant( func ) );
- // need arg size seperate since script object may be NULL
- statement_t &statement = gameLocal.program.GetStatement( gameLocal.program.NumStatements() - 1 );
- statement.c = SizeConstant( func->value.functionPtr->parmTotal );
- } else {
- EmitOpcode( op, func, SizeConstant( size ) );
- }
- // we need to copy off the result into a temporary result location, so figure out the opcode
- returnType = type->ReturnType();
- if ( returnType->Type() == ev_string ) {
- resultOp = OP_STORE_S;
- returnDef = gameLocal.program.returnStringDef;
- } else {
- gameLocal.program.returnDef->SetTypeDef( returnType );
- returnDef = gameLocal.program.returnDef;
- switch( returnType->Type() ) {
- case ev_void :
- resultOp = OP_STORE_F;
- break;
- case ev_boolean :
- resultOp = OP_STORE_BOOL;
- break;
- case ev_float :
- resultOp = OP_STORE_F;
- break;
- case ev_vector :
- resultOp = OP_STORE_V;
- break;
- case ev_entity :
- resultOp = OP_STORE_ENT;
- break;
- case ev_object :
- resultOp = OP_STORE_OBJ;
- break;
- default :
- // shut up compiler
- resultOp = OP_STORE_OBJ;
- Error( "Invalid return type for function '%s'", func->Name() );
- }
- }
- if ( returnType->Type() == ev_void ) {
- // don't need result space since there's no result, so just return the normal result def.
- return returnDef;
- }
- // allocate result space
- // try to reuse result defs as much as possible
- statement_t &statement = gameLocal.program.GetStatement( gameLocal.program.NumStatements() - 1 );
- idVarDef *resultDef = gameLocal.program.FindFreeResultDef( returnType, RESULT_STRING, scope, statement.a, statement.b );
- // set user count back to 0, a result def needs to be used twice before it can be reused
- resultDef->numUsers = 0;
- EmitOpcode( resultOp, returnDef, resultDef );
- return resultDef;
- }
- /*
- ============
- idCompiler::ParseFunctionCall
- ============
- */
- idVarDef *idCompiler::ParseFunctionCall( idVarDef *funcDef ) {
- assert( funcDef );
- if ( funcDef->Type() != ev_function ) {
- Error( "'%s' is not a function", funcDef->Name() );
- }
- if ( funcDef->initialized == idVarDef::uninitialized ) {
- Error( "Function '%s' has not been defined yet", funcDef->GlobalName() );
- }
- assert( funcDef->value.functionPtr );
- if ( callthread ) {
- if ( ( funcDef->initialized != idVarDef::uninitialized ) && funcDef->value.functionPtr->eventdef ) {
- Error( "Built-in functions cannot be called as threads" );
- }
- callthread = false;
- return EmitFunctionParms( OP_THREAD, funcDef, 0, 0, NULL );
- } else {
- if ( ( funcDef->initialized != idVarDef::uninitialized ) && funcDef->value.functionPtr->eventdef ) {
- if ( ( scope->Type() != ev_namespace ) && ( scope->scope->Type() == ev_object ) ) {
- // get the local object pointer
- idVarDef *thisdef = gameLocal.program.GetDef( scope->scope->TypeDef(), "self", scope );
- if ( !thisdef ) {
- Error( "No 'self' within scope" );
- }
- return ParseEventCall( thisdef, funcDef );
- } else {
- Error( "Built-in functions cannot be called without an object" );
- }
- }
- return EmitFunctionParms( OP_CALL, funcDef, 0, 0, NULL );
- }
- }
- /*
- ============
- idCompiler::ParseObjectCall
- ============
- */
- idVarDef *idCompiler::ParseObjectCall( idVarDef *object, idVarDef *func ) {
- EmitPush( object, object->TypeDef() );
- if ( callthread ) {
- callthread = false;
- return EmitFunctionParms( OP_OBJTHREAD, func, 1, type_object.Size(), object );
- } else {
- return EmitFunctionParms( OP_OBJECTCALL, func, 1, 0, object );
- }
- }
- /*
- ============
- idCompiler::ParseEventCall
- ============
- */
- idVarDef *idCompiler::ParseEventCall( idVarDef *object, idVarDef *funcDef ) {
- if ( callthread ) {
- Error( "Cannot call built-in functions as a thread" );
- }
- if ( funcDef->Type() != ev_function ) {
- Error( "'%s' is not a function", funcDef->Name() );
- }
- if ( !funcDef->value.functionPtr->eventdef ) {
- Error( "\"%s\" cannot be called with object notation", funcDef->Name() );
- }
- if ( object->Type() == ev_object ) {
- EmitPush( object, &type_entity );
- } else {
- EmitPush( object, object->TypeDef() );
- }
- return EmitFunctionParms( OP_EVENTCALL, funcDef, 0, type_object.Size(), NULL );
- }
- /*
- ============
- idCompiler::ParseSysObjectCall
- ============
- */
- idVarDef *idCompiler::ParseSysObjectCall( idVarDef *funcDef ) {
- if ( callthread ) {
- Error( "Cannot call built-in functions as a thread" );
- }
- if ( funcDef->Type() != ev_function ) {
- Error( "'%s' is not a function", funcDef->Name() );
- }
- if ( funcDef->value.functionPtr->eventdef == NULL ) {
- Error( "\"%s\" cannot be called with object notation", funcDef->Name() );
- }
- assert( funcDef->value.functionPtr->eventdef != NULL ); // to remove stupid analyze warning
- if ( !idThread::Type.RespondsTo( *funcDef->value.functionPtr->eventdef ) ) {
- Error( "\"%s\" is not callable as a 'sys' function", funcDef->Name() );
- }
- return EmitFunctionParms( OP_SYSCALL, funcDef, 0, 0, NULL );
- }
- /*
- ============
- idCompiler::LookupDef
- ============
- */
- idVarDef *idCompiler::LookupDef( const char *name, const idVarDef *baseobj ) {
- idVarDef *def;
- idVarDef *field;
- etype_t type_b;
- etype_t type_c;
- opcode_t *op;
- // check if we're accessing a field
- if ( baseobj && ( baseobj->Type() == ev_object ) ) {
- const idVarDef *tdef;
- def = NULL;
- for( tdef = baseobj; tdef != &def_object; tdef = tdef->TypeDef()->SuperClass()->def ) {
- def = gameLocal.program.GetDef( NULL, name, tdef );
- if ( def ) {
- break;
- }
- }
- } else {
- // first look through the defs in our scope
- def = gameLocal.program.GetDef( NULL, name, scope );
- if ( !def ) {
- // if we're in a member function, check types local to the object
- if ( ( scope->Type() != ev_namespace ) && ( scope->scope->Type() == ev_object ) ) {
- // get the local object pointer
- idVarDef *thisdef = gameLocal.program.GetDef( scope->scope->TypeDef(), "self", scope );
- field = LookupDef( name, scope->scope->TypeDef()->def );
- if ( !field ) {
- Error( "Unknown value \"%s\"", name );
- }
- // type check
- type_b = field->Type();
- if ( field->Type() == ev_function ) {
- type_c = field->TypeDef()->ReturnType()->Type();
- } else {
- type_c = field->TypeDef()->FieldType()->Type(); // field access gets type from field
- if ( CheckToken( "++" ) ) {
- if ( type_c != ev_float ) {
- Error( "Invalid type for ++" );
- }
- def = EmitOpcode( OP_UINCP_F, thisdef, field );
- return def;
- } else if ( CheckToken( "--" ) ) {
- if ( type_c != ev_float ) {
- Error( "Invalid type for --" );
- }
- def = EmitOpcode( OP_UDECP_F, thisdef, field );
- return def;
- }
- }
- op = &opcodes[ OP_INDIRECT_F ];
- while( ( op->type_a->Type() != ev_object )
- || ( type_b != op->type_b->Type() ) || ( type_c != op->type_c->Type() ) ) {
- if ( ( op->priority == FUNCTION_PRIORITY ) && ( op->type_a->Type() == ev_object ) && ( op->type_c->Type() == ev_void ) &&
- ( type_c != op->type_c->Type() ) ) {
- // catches object calls that return a value
- break;
- }
- op++;
- if ( !op->name || strcmp( op->name, "." ) ) {
- Error( "no valid opcode to access type '%s'", field->TypeDef()->SuperClass()->Name() );
- }
- }
- if ( ( op - opcodes ) == OP_OBJECTCALL ) {
- ExpectToken( "(" );
- def = ParseObjectCall( thisdef, field );
- } else {
- // emit the conversion opcode
- def = EmitOpcode( op, thisdef, field );
- // field access gets type from field
- def->SetTypeDef( field->TypeDef()->FieldType() );
- }
- }
- }
- }
- return def;
- }
- /*
- ============
- idCompiler::ParseValue
- Returns the def for the current token
- ============
- */
- idVarDef *idCompiler::ParseValue() {
- idVarDef *def;
- idVarDef *namespaceDef;
- idStr name;
-
- if ( immediateType == &type_entity ) {
- // if an immediate entity ($-prefaced name) then create or lookup a def for it.
- // when entities are spawned, they'll lookup the def and point it to them.
- def = gameLocal.program.GetDef( &type_entity, "$" + token, &def_namespace );
- if ( !def ) {
- def = gameLocal.program.AllocDef( &type_entity, "$" + token, &def_namespace, true );
- }
- NextToken();
- return def;
- } else if ( immediateType ) {
- // if the token is an immediate, allocate a constant for it
- return ParseImmediate();
- }
- ParseName( name );
- def = LookupDef( name, basetype );
- if ( def == NULL ) {
- if ( basetype ) {
- Error( "%s is not a member of %s", name.c_str(), basetype->TypeDef()->Name() );
- } else {
- Error( "Unknown value \"%s\"", name.c_str() );
- }
- // if namespace, then look up the variable in that namespace
- } else if ( def->Type() == ev_namespace ) {
- while( def->Type() == ev_namespace ) {
- ExpectToken( "::" );
- ParseName( name );
- namespaceDef = def;
- def = gameLocal.program.GetDef( NULL, name, namespaceDef );
- if ( def == NULL ) {
- if ( namespaceDef != NULL ) {
- Error( "Unknown value \"%s::%s\"", namespaceDef->GlobalName(), name.c_str() );
- } else {
- Error( "Unknown value \"%s\"", name.c_str() );
- }
- break;
- }
- }
- //def = LookupDef( name, basetype );
- }
- return def;
- }
- /*
- ============
- idCompiler::GetTerm
- ============
- */
- idVarDef *idCompiler::GetTerm() {
- idVarDef *e;
- int op;
-
- if ( !immediateType && CheckToken( "~" ) ) {
- e = GetExpression( TILDE_PRIORITY );
- switch( e->Type() ) {
- case ev_float :
- op = OP_COMP_F;
- break;
- default :
- // shut up compiler
- op = OP_COMP_F;
- Error( "type mismatch for ~" );
- }
- return EmitOpcode( op, e, 0 );
- }
- if ( !immediateType && CheckToken( "!" ) ) {
- e = GetExpression( NOT_PRIORITY );
- switch( e->Type() ) {
- case ev_boolean :
- op = OP_NOT_BOOL;
- break;
- case ev_float :
- op = OP_NOT_F;
- break;
- case ev_string :
- op = OP_NOT_S;
- break;
- case ev_vector :
- op = OP_NOT_V;
- break;
- case ev_entity :
- op = OP_NOT_ENT;
- break;
- case ev_function :
- // shut up compiler
- op = OP_NOT_F;
- Error( "Invalid type for !" );
- break;
- case ev_object :
- op = OP_NOT_ENT;
- break;
- default :
- // shut up compiler
- op = OP_NOT_F;
- Error( "type mismatch for !" );
- }
- return EmitOpcode( op, e, 0 );
- }
- // check for negation operator
- if ( !immediateType && CheckToken( "-" ) ) {
- // constants are directly negated without an instruction
- if ( immediateType == &type_float ) {
- immediate._float = -immediate._float;
- return ParseImmediate();
- } else if ( immediateType == &type_vector ) {
- immediate.vector[0] = -immediate.vector[0];
- immediate.vector[1] = -immediate.vector[1];
- immediate.vector[2] = -immediate.vector[2];
- return ParseImmediate();
- } else {
- e = GetExpression( NOT_PRIORITY );
- switch( e->Type() ) {
- case ev_float :
- op = OP_NEG_F;
- break;
- case ev_vector :
- op = OP_NEG_V;
- break;
- default :
- // shut up compiler
- op = OP_NEG_F;
- Error( "type mismatch for -" );
- }
- return EmitOpcode( &opcodes[ op ], e, 0 );
- }
- }
-
- if ( CheckToken( "int" ) ) {
- ExpectToken( "(" );
- e = GetExpression( INT_PRIORITY );
- if ( e->Type() != ev_float ) {
- Error( "type mismatch for int()" );
- }
- ExpectToken( ")" );
- return EmitOpcode( OP_INT_F, e, 0 );
- }
-
- if ( CheckToken( "thread" ) ) {
- callthread = true;
- e = GetExpression( FUNCTION_PRIORITY );
- if ( callthread ) {
- Error( "Invalid thread call" );
- }
- // threads return the thread number
- gameLocal.program.returnDef->SetTypeDef( &type_float );
- return gameLocal.program.returnDef;
- }
-
- if ( !immediateType && CheckToken( "(" ) ) {
- e = GetExpression( TOP_PRIORITY );
- ExpectToken( ")" );
- return e;
- }
-
- return ParseValue();
- }
- /*
- ==============
- idCompiler::TypeMatches
- ==============
- */
- bool idCompiler::TypeMatches( etype_t type1, etype_t type2 ) const {
- if ( type1 == type2 ) {
- return true;
- }
- //if ( ( type1 == ev_entity ) && ( type2 == ev_object ) ) {
- // return true;
- //}
-
- //if ( ( type2 == ev_entity ) && ( type1 == ev_object ) ) {
- // return true;
- //}
- return false;
- }
- /*
- ==============
- idCompiler::GetExpression
- ==============
- */
- idVarDef *idCompiler::GetExpression( int priority ) {
- opcode_t *op;
- opcode_t *oldop;
- idVarDef *e;
- idVarDef *e2;
- const idVarDef *oldtype;
- etype_t type_a;
- etype_t type_b;
- etype_t type_c;
-
- if ( priority == 0 ) {
- return GetTerm();
- }
-
- e = GetExpression( priority - 1 );
- if ( token == ";" ) {
- // save us from searching through the opcodes unneccesarily
- return e;
- }
- while( 1 ) {
- if ( ( priority == FUNCTION_PRIORITY ) && CheckToken( "(" ) ) {
- return ParseFunctionCall( e );
- }
- // has to be a punctuation
- if ( immediateType ) {
- break;
- }
- for( op = opcodes; op->name; op++ ) {
- if ( ( op->priority == priority ) && CheckToken( op->name ) ) {
- break;
- }
- }
- if ( !op->name ) {
- // next token isn't at this priority level
- break;
- }
- // unary operators act only on the left operand
- if ( op->type_b == &def_void ) {
- e = EmitOpcode( op, e, 0 );
- return e;
- }
- // preserve our base type
- oldtype = basetype;
- // field access needs scope from object
- if ( ( op->name[ 0 ] == '.' ) && e->TypeDef()->Inherits( &type_object ) ) {
- // save off what type this field is part of
- basetype = e->TypeDef()->def;
- }
- if ( op->rightAssociative ) {
- // if last statement is an indirect, change it to an address of
- if ( gameLocal.program.NumStatements() > 0 ) {
- statement_t &statement = gameLocal.program.GetStatement( gameLocal.program.NumStatements() - 1 );
- if ( ( statement.op >= OP_INDIRECT_F ) && ( statement.op < OP_ADDRESS ) ) {
- statement.op = OP_ADDRESS;
- type_pointer.SetPointerType( e->TypeDef() );
- e->SetTypeDef( &type_pointer );
- }
- }
- e2 = GetExpression( priority );
- } else {
- e2 = GetExpression( priority - 1 );
- }
- // restore type
- basetype = oldtype;
-
- // type check
- type_a = e->Type();
- type_b = e2->Type();
- // field access gets type from field
- if ( op->name[ 0 ] == '.' ) {
- if ( ( e2->Type() == ev_function ) && e2->TypeDef()->ReturnType() ) {
- type_c = e2->TypeDef()->ReturnType()->Type();
- } else if ( e2->TypeDef()->FieldType() ) {
- type_c = e2->TypeDef()->FieldType()->Type();
- } else {
- // not a field
- type_c = ev_error;
- }
- } else {
- type_c = ev_void;
- }
- oldop = op;
- while( !TypeMatches( type_a, op->type_a->Type() ) || !TypeMatches( type_b, op->type_b->Type() ) ||
- ( ( type_c != ev_void ) && !TypeMatches( type_c, op->type_c->Type() ) ) ) {
- if ( ( op->priority == FUNCTION_PRIORITY ) && TypeMatches( type_a, op->type_a->Type() ) && TypeMatches( type_b, op->type_b->Type() ) ) {
- break;
- }
- op++;
- if ( !op->name || strcmp( op->name, oldop->name ) ) {
- Error( "type mismatch for '%s'", oldop->name );
- }
- }
- switch( op - opcodes ) {
- case OP_SYSCALL :
- ExpectToken( "(" );
- e = ParseSysObjectCall( e2 );
- break;
- case OP_OBJECTCALL :
- ExpectToken( "(" );
- if ( ( e2->initialized != idVarDef::uninitialized ) && e2->value.functionPtr->eventdef ) {
- e = ParseEventCall( e, e2 );
- } else {
- e = ParseObjectCall( e, e2 );
- }
- break;
-
- case OP_EVENTCALL :
- ExpectToken( "(" );
- if ( ( e2->initialized != idVarDef::uninitialized ) && e2->value.functionPtr->eventdef ) {
- e = ParseEventCall( e, e2 );
- } else {
- e = ParseObjectCall( e, e2 );
- }
- break;
- default:
- if ( callthread ) {
- Error( "Expecting function call after 'thread'" );
- }
- if ( ( type_a == ev_pointer ) && ( type_b != e->TypeDef()->PointerType()->Type() ) ) {
- // FIXME: need to make a general case for this
- if ( ( op - opcodes == OP_STOREP_F ) && ( e->TypeDef()->PointerType()->Type() == ev_boolean ) ) {
- // copy from float to boolean pointer
- op = &opcodes[ OP_STOREP_FTOBOOL ];
- } else if ( ( op - opcodes == OP_STOREP_BOOL ) && ( e->TypeDef()->PointerType()->Type() == ev_float ) ) {
- // copy from boolean to float pointer
- op = &opcodes[ OP_STOREP_BOOLTOF ];
- } else if ( ( op - opcodes == OP_STOREP_F ) && ( e->TypeDef()->PointerType()->Type() == ev_string ) ) {
- // copy from float to string pointer
- op = &opcodes[ OP_STOREP_FTOS ];
- } else if ( ( op - opcodes == OP_STOREP_BOOL ) && ( e->TypeDef()->PointerType()->Type() == ev_string ) ) {
- // copy from boolean to string pointer
- op = &opcodes[ OP_STOREP_BTOS ];
- } else if ( ( op - opcodes == OP_STOREP_V ) && ( e->TypeDef()->PointerType()->Type() == ev_string ) ) {
- // copy from vector to string pointer
- op = &opcodes[ OP_STOREP_VTOS ];
- } else if ( ( op - opcodes == OP_STOREP_ENT ) && ( e->TypeDef()->PointerType()->Type() == ev_object ) ) {
- // store an entity into an object pointer
- op = &opcodes[ OP_STOREP_OBJENT ];
- } else {
- Error( "type mismatch for '%s'", op->name );
- }
- }
-
- if ( op->rightAssociative ) {
- e = EmitOpcode( op, e2, e );
- } else {
- e = EmitOpcode( op, e, e2 );
- }
- if ( op - opcodes == OP_STOREP_OBJENT ) {
- // statement.b points to type_pointer, which is just a temporary that gets its type reassigned, so we store the real type in statement.c
- // so that we can do a type check during run time since we don't know what type the script object is at compile time because it
- // comes from an entity
- statement_t &statement = gameLocal.program.GetStatement( gameLocal.program.NumStatements() - 1 );
- statement.c = type_pointer.PointerType()->def;
- }
- // field access gets type from field
- if ( type_c != ev_void ) {
- e->SetTypeDef( e2->TypeDef()->FieldType() );
- }
- break;
- }
- }
- return e;
- }
- /*
- ================
- idCompiler::PatchLoop
- ================
- */
- void idCompiler::PatchLoop( int start, int continuePos ) {
- int i;
- statement_t *pos;
- pos = &gameLocal.program.GetStatement( start );
- for( i = start; i < gameLocal.program.NumStatements(); i++, pos++ ) {
- if ( pos->op == OP_BREAK ) {
- pos->op = OP_GOTO;
- pos->a = JumpFrom( i );
- } else if ( pos->op == OP_CONTINUE ) {
- pos->op = OP_GOTO;
- pos->a = JumpDef( i, continuePos );
- }
- }
- }
- /*
- ================
- idCompiler::ParseReturnStatement
- ================
- */
- void idCompiler::ParseReturnStatement() {
- idVarDef *e;
- etype_t type_a;
- etype_t type_b;
- opcode_t *op;
- if ( CheckToken( ";" ) ) {
- if ( scope->TypeDef()->ReturnType()->Type() != ev_void ) {
- Error( "expecting return value" );
- }
- EmitOpcode( OP_RETURN, 0, 0 );
- return;
- }
- e = GetExpression( TOP_PRIORITY );
- ExpectToken( ";" );
- type_a = e->Type();
- type_b = scope->TypeDef()->ReturnType()->Type();
- if ( TypeMatches( type_a, type_b ) ) {
- EmitOpcode( OP_RETURN, e, 0 );
- return;
- }
- for( op = opcodes; op->name; op++ ) {
- if ( !strcmp( op->name, "=" ) ) {
- break;
- }
- }
- assert( op->name );
- while( !TypeMatches( type_a, op->type_a->Type() ) || !TypeMatches( type_b, op->type_b->Type() ) ) {
- op++;
- if ( !op->name || strcmp( op->name, "=" ) ) {
- Error( "type mismatch for return value" );
- }
- }
- idTypeDef *returnType = scope->TypeDef()->ReturnType();
- if ( returnType->Type() == ev_string ) {
- EmitOpcode( op, e, gameLocal.program.returnStringDef );
- } else {
- gameLocal.program.returnDef->SetTypeDef( returnType );
- EmitOpcode( op, e, gameLocal.program.returnDef );
- }
- EmitOpcode( OP_RETURN, 0, 0 );
- }
-
- /*
- ================
- idCompiler::ParseWhileStatement
- ================
- */
- void idCompiler::ParseWhileStatement() {
- idVarDef *e;
- int patch1;
- int patch2;
- loopDepth++;
- ExpectToken( "(" );
-
- patch2 = gameLocal.program.NumStatements();
- e = GetExpression( TOP_PRIORITY );
- ExpectToken( ")" );
- if ( ( e->initialized == idVarDef::initializedConstant ) && ( *e->value.intPtr != 0 ) ) {
- //FIXME: we can completely skip generation of this code in the opposite case
- ParseStatement();
- EmitOpcode( OP_GOTO, JumpTo( patch2 ), 0 );
- } else {
- patch1 = gameLocal.program.NumStatements();
- EmitOpcode( OP_IFNOT, e, 0 );
- ParseStatement();
- EmitOpcode( OP_GOTO, JumpTo( patch2 ), 0 );
- gameLocal.program.GetStatement( patch1 ).b = JumpFrom( patch1 );
- }
- // fixup breaks and continues
- PatchLoop( patch2, patch2 );
- loopDepth--;
- }
- /*
- ================
- idCompiler::ParseForStatement
- Form of for statement with a counter:
- a = 0;
- start: << patch4
- if ( !( a < 10 ) ) {
- goto end; << patch1
- } else {
- goto process; << patch3
- }
- increment: << patch2
- a = a + 1;
- goto start; << goto patch4
- process:
- statements;
- goto increment; << goto patch2
- end:
- Form of for statement without a counter:
- a = 0;
- start: << patch2
- if ( !( a < 10 ) ) {
- goto end; << patch1
- }
- process:
- statements;
- goto start; << goto patch2
- end:
- ================
- */
- void idCompiler::ParseForStatement() {
- idVarDef *e;
- int start;
- int patch1;
- int patch2;
- int patch3;
- int patch4;
- loopDepth++;
- start = gameLocal.program.NumStatements();
- ExpectToken( "(" );
-
- // init
- if ( !CheckToken( ";" ) ) {
- do {
- GetExpression( TOP_PRIORITY );
- } while( CheckToken( "," ) );
- ExpectToken( ";" );
- }
- // condition
- patch2 = gameLocal.program.NumStatements();
- e = GetExpression( TOP_PRIORITY );
- ExpectToken( ";" );
- //FIXME: add check for constant expression
- patch1 = gameLocal.program.NumStatements();
- EmitOpcode( OP_IFNOT, e, 0 );
- // counter
- if ( !CheckToken( ")" ) ) {
- patch3 = gameLocal.program.NumStatements();
- EmitOpcode( OP_IF, e, 0 );
- patch4 = patch2;
- patch2 = gameLocal.program.NumStatements();
- do {
- GetExpression( TOP_PRIORITY );
- } while( CheckToken( "," ) );
-
- ExpectToken( ")" );
- // goto patch4
- EmitOpcode( OP_GOTO, JumpTo( patch4 ), 0 );
- // fixup patch3
- gameLocal.program.GetStatement( patch3 ).b = JumpFrom( patch3 );
- }
- ParseStatement();
- // goto patch2
- EmitOpcode( OP_GOTO, JumpTo( patch2 ), 0 );
- // fixup patch1
- gameLocal.program.GetStatement( patch1 ).b = JumpFrom( patch1 );
- // fixup breaks and continues
- PatchLoop( start, patch2 );
- loopDepth--;
- }
- /*
- ================
- idCompiler::ParseDoWhileStatement
- ================
- */
- void idCompiler::ParseDoWhileStatement() {
- idVarDef *e;
- int patch1;
- loopDepth++;
- patch1 = gameLocal.program.NumStatements();
- ParseStatement();
- ExpectToken( "while" );
- ExpectToken( "(" );
- e = GetExpression( TOP_PRIORITY );
- ExpectToken( ")" );
- ExpectToken( ";" );
- EmitOpcode( OP_IF, e, JumpTo( patch1 ) );
- // fixup breaks and continues
- PatchLoop( patch1, patch1 );
- loopDepth--;
- }
- /*
- ================
- idCompiler::ParseIfStatement
- ================
- */
- void idCompiler::ParseIfStatement() {
- idVarDef *e;
- int patch1;
- int patch2;
- ExpectToken( "(" );
- e = GetExpression( TOP_PRIORITY );
- ExpectToken( ")" );
- //FIXME: add check for constant expression
- patch1 = gameLocal.program.NumStatements();
- EmitOpcode( OP_IFNOT, e, 0 );
- ParseStatement();
-
- if ( CheckToken( "else" ) ) {
- patch2 = gameLocal.program.NumStatements();
- EmitOpcode( OP_GOTO, 0, 0 );
- gameLocal.program.GetStatement( patch1 ).b = JumpFrom( patch1 );
- ParseStatement();
- gameLocal.program.GetStatement( patch2 ).a = JumpFrom( patch2 );
- } else {
- gameLocal.program.GetStatement( patch1 ).b = JumpFrom( patch1 );
- }
- }
- /*
- ============
- idCompiler::ParseStatement
- ============
- */
- void idCompiler::ParseStatement() {
- if ( CheckToken( ";" ) ) {
- // skip semicolons, which are harmless and ok syntax
- return;
- }
- if ( CheckToken( "{" ) ) {
- do {
- ParseStatement();
- } while( !CheckToken( "}" ) );
- return;
- }
- if ( CheckToken( "return" ) ) {
- ParseReturnStatement();
- return;
- }
-
- if ( CheckToken( "while" ) ) {
- ParseWhileStatement();
- return;
- }
- if ( CheckToken( "for" ) ) {
- ParseForStatement();
- return;
- }
- if ( CheckToken( "do" ) ) {
- ParseDoWhileStatement();
- return;
- }
- if ( CheckToken( "break" ) ) {
- ExpectToken( ";" );
- if ( !loopDepth ) {
- Error( "cannot break outside of a loop" );
- }
- EmitOpcode( OP_BREAK, 0, 0 );
- return;
- }
- if ( CheckToken( "continue" ) ) {
- ExpectToken( ";" );
- if ( !loopDepth ) {
- Error( "cannot contine outside of a loop" );
- }
- EmitOpcode( OP_CONTINUE, 0, 0 );
- return;
- }
- if ( CheckType() != NULL ) {
- ParseDefs();
- return;
- }
- if ( CheckToken( "if" ) ) {
- ParseIfStatement();
- return;
- }
- GetExpression( TOP_PRIORITY );
- ExpectToken(";");
- }
- /*
- ================
- idCompiler::ParseObjectDef
- ================
- */
- void idCompiler::ParseObjectDef( const char *objname ) {
- idTypeDef *objtype;
- idTypeDef *type;
- idTypeDef *parentType;
- idTypeDef *fieldtype;
- idStr name;
- const char *fieldname;
- idTypeDef newtype( ev_field, NULL, "", 0, NULL );
- idVarDef *oldscope;
- int num;
- int i;
- oldscope = scope;
- if ( scope->Type() != ev_namespace ) {
- Error( "Objects cannot be defined within functions or other objects" );
- }
- // make sure it doesn't exist before we create it
- if ( gameLocal.program.FindType( objname ) != NULL ) {
- Error( "'%s' : redefinition; different basic types", objname );
- }
- // base type
- if ( !CheckToken( ":" ) ) {
- parentType = &type_object;
- } else {
- parentType = ParseType();
- if ( !parentType->Inherits( &type_object ) ) {
- Error( "Objects may only inherit from objects." );
- }
- }
-
- objtype = gameLocal.program.AllocType( ev_object, NULL, objname, parentType == &type_object ? 0 : parentType->Size(), parentType );
- objtype->def = gameLocal.program.AllocDef( objtype, objname, scope, true );
- scope = objtype->def;
- // inherit all the functions
- num = parentType->NumFunctions();
- for( i = 0; i < parentType->NumFunctions(); i++ ) {
- const function_t *func = parentType->GetFunction( i );
- objtype->AddFunction( func );
- }
- ExpectToken( "{" );
- do {
- if ( CheckToken( ";" ) ) {
- // skip semicolons, which are harmless and ok syntax
- continue;
- }
- fieldtype = ParseType();
- newtype.SetFieldType( fieldtype );
- fieldname = va( "%s field", fieldtype->Name() );
- newtype.SetName( fieldname );
- ParseName( name );
- // check for a function prototype or declaraction
- if ( CheckToken( "(" ) ) {
- ParseFunctionDef( newtype.FieldType(), name );
- } else {
- type = gameLocal.program.GetType( newtype, true );
- assert( !type->def );
- gameLocal.program.AllocDef( type, name, scope, true );
- objtype->AddField( type, name );
- ExpectToken( ";" );
- }
- } while( !CheckToken( "}" ) );
- scope = oldscope;
- ExpectToken( ";" );
- }
- /*
- ============
- idCompiler::ParseFunction
- parse a function type
- ============
- */
- idTypeDef *idCompiler::ParseFunction( idTypeDef *returnType, const char *name ) {
- idTypeDef newtype( ev_function, NULL, name, type_function.Size(), returnType );
- idTypeDef *type;
-
- if ( scope->Type() != ev_namespace ) {
- // create self pointer
- newtype.AddFunctionParm( scope->TypeDef(), "self" );
- }
- if ( !CheckToken( ")" ) ) {
- idStr parmName;
- do {
- type = ParseType();
- ParseName( parmName );
- newtype.AddFunctionParm( type, parmName );
- } while( CheckToken( "," ) );
- ExpectToken( ")" );
- }
- return gameLocal.program.GetType( newtype, true );
- }
- /*
- ================
- idCompiler::ParseFunctionDef
- ================
- */
- void idCompiler::ParseFunctionDef( idTypeDef *returnType, const char *name ) {
- idTypeDef *type;
- idVarDef *def;
- const idVarDef *parm;
- idVarDef *oldscope;
- int i;
- int numParms;
- const idTypeDef *parmType;
- function_t *func;
- statement_t *pos;
- if ( ( scope->Type() != ev_namespace ) && !scope->TypeDef()->Inherits( &type_object ) ) {
- Error( "Functions may not be defined within other functions" );
- }
- type = ParseFunction( returnType, name );
- def = gameLocal.program.GetDef( type, name, scope );
- if ( !def ) {
- def = gameLocal.program.AllocDef( type, name, scope, true );
- type->def = def;
- func = &gameLocal.program.AllocFunction( def );
- if ( scope->TypeDef()->Inherits( &type_object ) ) {
- scope->TypeDef()->AddFunction( func );
- }
- } else {
- func = def->value.functionPtr;
- assert( func );
- if ( func->firstStatement ) {
- Error( "%s redeclared", def->GlobalName() );
- }
- }
- // check if this is a prototype or declaration
- if ( !CheckToken( "{" ) ) {
- // it's just a prototype, so get the ; and move on
- ExpectToken( ";" );
- return;
- }
- // calculate stack space used by parms
- numParms = type->NumParameters();
- func->parmSize.SetNum( numParms );
- for( i = 0; i < numParms; i++ ) {
- parmType = type->GetParmType( i );
- if ( parmType->Inherits( &type_object ) ) {
- func->parmSize[ i ] = type_object.Size();
- } else {
- func->parmSize[ i ] = parmType->Size();
- }
- func->parmTotal += func->parmSize[ i ];
- }
- // define the parms
- for( i = 0; i < numParms; i++ ) {
- if ( gameLocal.program.GetDef( type->GetParmType( i ), type->GetParmName( i ), def ) ) {
- Error( "'%s' defined more than once in function parameters", type->GetParmName( i ) );
- }
- parm = gameLocal.program.AllocDef( type->GetParmType( i ), type->GetParmName( i ), def, false );
- }
- oldscope = scope;
- scope = def;
- func->firstStatement = gameLocal.program.NumStatements();
- // check if we should call the super class constructor
- if ( oldscope->TypeDef()->Inherits( &type_object ) && !idStr::Icmp( name, "init" ) ) {
- idTypeDef *superClass;
- function_t *constructorFunc = NULL;
- // find the superclass constructor
- for( superClass = oldscope->TypeDef()->SuperClass(); superClass != &type_object; superClass = superClass->SuperClass() ) {
- constructorFunc = gameLocal.program.FindFunction( va( "%s::init", superClass->Name() ) );
- if ( constructorFunc ) {
- break;
- }
- }
- // emit the call to the constructor
- if ( constructorFunc ) {
- idVarDef *selfDef = gameLocal.program.GetDef( type->GetParmType( 0 ), type->GetParmName( 0 ), def );
- assert( selfDef );
- EmitPush( selfDef, selfDef->TypeDef() );
- EmitOpcode( &opcodes[ OP_CALL ], constructorFunc->def, 0 );
- }
- }
- // parse regular statements
- while( !CheckToken( "}" ) ) {
- ParseStatement();
- }
- // check if we should call the super class destructor
- if ( oldscope->TypeDef()->Inherits( &type_object ) && !idStr::Icmp( name, "destroy" ) ) {
- idTypeDef *superClass;
- function_t *destructorFunc = NULL;
- // find the superclass destructor
- for( superClass = oldscope->TypeDef()->SuperClass(); superClass != &type_object; superClass = superClass->SuperClass() ) {
- destructorFunc = gameLocal.program.FindFunction( va( "%s::destroy", superClass->Name() ) );
- if ( destructorFunc ) {
- break;
- }
- }
- if ( destructorFunc ) {
- if ( func->firstStatement < gameLocal.program.NumStatements() ) {
- // change all returns to point to the call to the destructor
- pos = &gameLocal.program.GetStatement( func->firstStatement );
- for( i = func->firstStatement; i < gameLocal.program.NumStatements(); i++, pos++ ) {
- if ( pos->op == OP_RETURN ) {
- pos->op = OP_GOTO;
- pos->a = JumpDef( i, gameLocal.program.NumStatements() );
- }
- }
- }
- // emit the call to the destructor
- idVarDef *selfDef = gameLocal.program.GetDef( type->GetParmType( 0 ), type->GetParmName( 0 ), def );
- assert( selfDef );
- EmitPush( selfDef, selfDef->TypeDef() );
- EmitOpcode( &opcodes[ OP_CALL ], destructorFunc->def, 0 );
- }
- }
- // Disabled code since it caused a function to fall through to the next function when last statement is in the form "if ( x ) { return; }"
- #if 0
- // don't bother adding a return opcode if the "return" statement was used.
- if ( ( func->firstStatement == gameLocal.program.NumStatements() ) || ( gameLocal.program.GetStatement( gameLocal.program.NumStatements() - 1 ).op != OP_RETURN ) ) {
- // emit an end of statements opcode
- EmitOpcode( OP_RETURN, 0, 0 );
- }
- #else
- // always emit the return opcode
- EmitOpcode( OP_RETURN, 0, 0 );
- #endif
- // record the number of statements in the function
- func->numStatements = gameLocal.program.NumStatements() - func->firstStatement;
- scope = oldscope;
- }
- /*
- ================
- idCompiler::ParseVariableDef
- ================
- */
- void idCompiler::ParseVariableDef( idTypeDef *type, const char *name ) {
- idVarDef *def, *def2;
- bool negate;
- def = gameLocal.program.GetDef( type, name, scope );
- if ( def ) {
- Error( "%s redeclared", name );
- }
-
- def = gameLocal.program.AllocDef( type, name, scope, false );
- // check for an initialization
- if ( CheckToken( "=" ) ) {
- // if a local variable in a function then write out interpreter code to initialize variable
- if ( scope->Type() == ev_function ) {
- def2 = GetExpression( TOP_PRIORITY );
- if ( ( type == &type_float ) && ( def2->TypeDef() == &type_float ) ) {
- EmitOpcode( OP_STORE_F, def2, def );
- } else if ( ( type == &type_vector ) && ( def2->TypeDef() == &type_vector ) ) {
- EmitOpcode( OP_STORE_V, def2, def );
- } else if ( ( type == &type_string ) && ( def2->TypeDef() == &type_string ) ) {
- EmitOpcode( OP_STORE_S, def2, def );
- } else if ( ( type == &type_entity ) && ( ( def2->TypeDef() == &type_entity ) || ( def2->TypeDef()->Inherits( &type_object ) ) ) ) {
- EmitOpcode( OP_STORE_ENT, def2, def );
- } else if ( ( type->Inherits( &type_object ) ) && ( def2->TypeDef() == &type_entity ) ) {
- EmitOpcode( OP_STORE_OBJENT, def2, def );
- } else if ( ( type->Inherits( &type_object ) ) && ( def2->TypeDef()->Inherits( type ) ) ) {
- EmitOpcode( OP_STORE_OBJ, def2, def );
- } else if ( ( type == &type_boolean ) && ( def2->TypeDef() == &type_boolean ) ) {
- EmitOpcode( OP_STORE_BOOL, def2, def );
- } else if ( ( type == &type_string ) && ( def2->TypeDef() == &type_float ) ) {
- EmitOpcode( OP_STORE_FTOS, def2, def );
- } else if ( ( type == &type_string ) && ( def2->TypeDef() == &type_boolean ) ) {
- EmitOpcode( OP_STORE_BTOS, def2, def );
- } else if ( ( type == &type_string ) && ( def2->TypeDef() == &type_vector ) ) {
- EmitOpcode( OP_STORE_VTOS, def2, def );
- } else if ( ( type == &type_boolean ) && ( def2->TypeDef() == &type_float ) ) {
- EmitOpcode( OP_STORE_FTOBOOL, def2, def );
- } else if ( ( type == &type_float ) && ( def2->TypeDef() == &type_boolean ) ) {
- EmitOpcode( OP_STORE_BOOLTOF, def2, def );
- } else {
- Error( "bad initialization for '%s'", name );
- }
- } else {
- // global variables can only be initialized with immediate values
- negate = false;
- if ( token.type == TT_PUNCTUATION && token == "-" ) {
- negate = true;
- NextToken();
- if ( immediateType != &type_float ) {
- Error( "wrong immediate type for '-' on variable '%s'", name );
- }
- }
- if ( immediateType != type ) {
- Error( "wrong immediate type for '%s'", name );
- }
- // global variables are initialized at start up
- if ( type == &type_string ) {
- def->SetString( token, false );
- } else {
- if ( negate ) {
- immediate._float = -immediate._float;
- }
- def->SetValue( immediate, false );
- }
- NextToken();
- }
- } else if ( type == &type_string ) {
- // local strings on the stack are initialized in the interpreter
- if ( scope->Type() != ev_function ) {
- def->SetString( "", false );
- }
- } else if ( type->Inherits( &type_object ) ) {
- if ( scope->Type() != ev_function ) {
- def->SetObject( NULL );
- }
- }
- }
- /*
- ================
- idCompiler::GetTypeForEventArg
- ================
- */
- idTypeDef *idCompiler::GetTypeForEventArg( char argType ) {
- idTypeDef *type;
- switch( argType ) {
- case D_EVENT_INTEGER :
- // this will get converted to int by the interpreter
- type = &type_float;
- break;
- case D_EVENT_FLOAT :
- type = &type_float;
- break;
- case D_EVENT_VECTOR :
- type = &type_vector;
- break;
- case D_EVENT_STRING :
- type = &type_string;
- break;
- case D_EVENT_ENTITY :
- case D_EVENT_ENTITY_NULL :
- type = &type_entity;
- break;
- case D_EVENT_VOID :
- type = &type_void;
- break;
- case D_EVENT_TRACE :
- // This data type isn't available from script
- type = NULL;
- break;
- default:
- // probably a typo
- type = NULL;
- break;
- }
-
- return type;
- }
- /*
- ================
- idCompiler::ParseEventDef
- ================
- */
- void idCompiler::ParseEventDef( idTypeDef *returnType, const char *name ) {
- const idTypeDef *expectedType;
- idTypeDef *argType;
- idTypeDef *type;
- int i;
- int num;
- const char *format;
- const idEventDef *ev;
- idStr parmName;
- ev = idEventDef::FindEvent( name );
- if ( ev == NULL ) {
- Error( "Unknown event '%s'", name );
- return;
- }
- // set the return type
- expectedType = GetTypeForEventArg( ev->GetReturnType() );
- if ( expectedType == NULL ) {
- Error( "Invalid return type '%c' in definition of '%s' event.", ev->GetReturnType(), name );
- return;
- }
- if ( returnType != expectedType ) {
- Error( "Return type doesn't match internal return type '%s'", expectedType->Name() );
- }
- idTypeDef newtype( ev_function, NULL, name, type_function.Size(), returnType );
- ExpectToken( "(" );
- format = ev->GetArgFormat();
- num = strlen( format );
- for( i = 0; i < num; i++ ) {
- expectedType = GetTypeForEventArg( format[ i ] );
- if ( expectedType == NULL || ( expectedType == &type_void ) ) {
- Error( "Invalid parameter '%c' in definition of '%s' event.", format[ i ], name );
- return;
- }
- argType = ParseType();
- ParseName( parmName );
- if ( argType != expectedType ) {
- Error( "The type of parm %d ('%s') does not match the internal type '%s' in definition of '%s' event.",
- i + 1, parmName.c_str(), expectedType->Name(), name );
- }
- newtype.AddFunctionParm( argType, "" );
- if ( i < num - 1 ) {
- if ( CheckToken( ")" ) ) {
- Error( "Too few parameters for event definition. Internal definition has %d parameters.", num );
- }
- ExpectToken( "," );
- }
- }
- if ( !CheckToken( ")" ) ) {
- Error( "Too many parameters for event definition. Internal definition has %d parameters.", num );
- }
- ExpectToken( ";" );
- type = gameLocal.program.FindType( name );
- if ( type ) {
- if ( !newtype.MatchesType( *type ) || ( type->def->value.functionPtr->eventdef != ev ) ) {
- Error( "Type mismatch on redefinition of '%s'", name );
- }
- } else {
- type = gameLocal.program.AllocType( newtype );
- type->def = gameLocal.program.AllocDef( type, name, &def_namespace, true );
- function_t &func = gameLocal.program.AllocFunction( type->def );
- func.eventdef = ev;
- func.parmSize.SetNum( num );
- for( i = 0; i < num; i++ ) {
- argType = newtype.GetParmType( i );
- func.parmTotal += argType->Size();
- func.parmSize[ i ] = argType->Size();
- }
- // mark the parms as local
- func.locals = func.parmTotal;
- }
- }
- /*
- ================
- idCompiler::ParseDefs
- Called at the outer layer and when a local statement is hit
- ================
- */
- void idCompiler::ParseDefs() {
- idStr name;
- idTypeDef *type;
- idVarDef *def;
- idVarDef *oldscope;
- if ( CheckToken( ";" ) ) {
- // skip semicolons, which are harmless and ok syntax
- return;
- }
- type = ParseType();
- if ( type == &type_scriptevent ) {
- type = ParseType();
- ParseName( name );
- ParseEventDef( type, name );
- return;
- }
-
- ParseName( name );
- if ( type == &type_namespace ) {
- def = gameLocal.program.GetDef( type, name, scope );
- if ( !def ) {
- def = gameLocal.program.AllocDef( type, name, scope, true );
- }
- ParseNamespace( def );
- } else if ( CheckToken( "::" ) ) {
- def = gameLocal.program.GetDef( NULL, name, scope );
- if ( !def ) {
- Error( "Unknown object name '%s'", name.c_str() );
- }
- ParseName( name );
- oldscope = scope;
- scope = def;
- ExpectToken( "(" );
- ParseFunctionDef( type, name.c_str() );
- scope = oldscope;
- } else if ( type == &type_object ) {
- ParseObjectDef( name.c_str() );
- } else if ( CheckToken( "(" ) ) { // check for a function prototype or declaraction
- ParseFunctionDef( type, name.c_str() );
- } else {
- ParseVariableDef( type, name.c_str() );
- while( CheckToken( "," ) ) {
- ParseName( name );
- ParseVariableDef( type, name.c_str() );
- }
- ExpectToken( ";" );
- }
- }
- /*
- ================
- idCompiler::ParseNamespace
- Parses anything within a namespace definition
- ================
- */
- void idCompiler::ParseNamespace( idVarDef *newScope ) {
- idVarDef *oldscope;
- oldscope = scope;
- if ( newScope != &def_namespace ) {
- ExpectToken( "{" );
- }
- while( !eof ) {
- scope = newScope;
- callthread = false;
- if ( ( newScope != &def_namespace ) && CheckToken( "}" ) ) {
- break;
- }
- ParseDefs();
- }
- scope = oldscope;
- }
- /*
- ============
- idCompiler::CompileFile
- compiles the 0 terminated text, adding definitions to the program structure
- ============
- */
- void idCompiler::CompileFile( const char *text, const char *filename, bool toConsole ) {
- idTimer compile_time;
- bool error;
- compile_time.Start();
- scope = &def_namespace;
- basetype = NULL;
- callthread = false;
- loopDepth = 0;
- eof = false;
- braceDepth = 0;
- immediateType = NULL;
- currentLineNumber = 0;
- console = toConsole;
-
- memset( &immediate, 0, sizeof( immediate ) );
- parser.SetFlags( LEXFL_ALLOWMULTICHARLITERALS );
- parser.LoadMemory( text, strlen( text ), filename );
- parserPtr = &parser;
- // unread tokens to include script defines
- token = SCRIPT_DEFAULTDEFS;
- token.type = TT_STRING;
- token.subtype = token.Length();
- token.line = token.linesCrossed = 0;
- parser.UnreadToken( &token );
- token = "include";
- token.type = TT_NAME;
- token.subtype = token.Length();
- token.line = token.linesCrossed = 0;
- parser.UnreadToken( &token );
- token = "#";
- token.type = TT_PUNCTUATION;
- token.subtype = P_PRECOMP;
- token.line = token.linesCrossed = 0;
- parser.UnreadToken( &token );
- // init the current token line to be the first line so that currentLineNumber is set correctly in NextToken
- token.line = 1;
- error = false;
- try {
- // read first token
- NextToken();
- while( !eof && !error ) {
- // parse from global namespace
- ParseNamespace( &def_namespace );
- }
- }
-
- catch( idCompileError &err ) {
- idStr error;
- if ( console ) {
- // don't print line number of an error if were calling script from the console using the "script" command
- sprintf( error, "Error: %s\n", err.GetError() );
- } else {
- sprintf( error, "Error: file %s, line %d: %s\n", gameLocal.program.GetFilename( currentFileNumber ), currentLineNumber, err.GetError() );
- }
- parser.FreeSource();
- throw idCompileError( error );
- }
- parser.FreeSource();
- compile_time.Stop();
- if ( !toConsole ) {
- gameLocal.Printf( "Compiled '%s': %.1f ms\n", filename, compile_time.Milliseconds() );
- }
- }
|