1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650 |
- /*
- ===========================================================================
- Doom 3 GPL Source Code
- Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
- This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
- Doom 3 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 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 Source Code. If not, see <http://www.gnu.org/licenses/>.
- In addition, the Doom 3 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 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.
- ===========================================================================
- */
- #include "../../idlib/precompiled.h"
- #pragma hdrstop
- #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 ) );
- eof = true;
- 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 0;
- }
- 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 && var_a->initialized != idVarDef::initializedConstant ) {
- return NULL;
- }
- if ( var_b && 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( void ) {
- 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( void ) {
- while( braceDepth ) {
- parserPtr->SkipBracedSection( false );
- braceDepth--;
- }
- NextToken();
- }
- /*
- ============
- idCompiler::SkipToSemicolon
- For error recovery
- ============
- */
- void idCompiler::SkipToSemicolon( void ) {
- do {
- if ( CheckToken( ";" ) ) {
- return;
- }
- NextToken();
- } while( !eof );
- }
- /*
- ============
- idCompiler::CheckType
- Parses a variable type, including functions types
- ============
- */
- idTypeDef *idCompiler::CheckType( void ) {
- 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 && !type->Inherits( &type_object ) ) {
- type = NULL;
- }
- }
-
- return type;
- }
- /*
- ============
- idCompiler::ParseType
- Parses a variable type, including functions types
- ============
- */
- idTypeDef *idCompiler::ParseType( void ) {
- 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( void ) {
- 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 :
- Error( "Invalid return type for function '%s'", func->Name() );
- // shut up compiler
- resultOp = OP_STORE_OBJ;
- break;
- }
- }
- 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 ) {
- Error( "\"%s\" cannot be called with object notation", funcDef->Name() );
- }
- 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( void ) {
- 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 ) {
- 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 ) {
- Error( "Unknown value \"%s::%s\"", namespaceDef->GlobalName(), name.c_str() );
- }
- }
- //def = LookupDef( name, basetype );
- }
- return def;
- }
- /*
- ============
- idCompiler::GetTerm
- ============
- */
- idVarDef *idCompiler::GetTerm( void ) {
- idVarDef *e;
- int op;
-
- if ( !immediateType && CheckToken( "~" ) ) {
- e = GetExpression( TILDE_PRIORITY );
- switch( e->Type() ) {
- case ev_float :
- op = OP_COMP_F;
- break;
- default :
- Error( "type mismatch for ~" );
- // shut up compiler
- op = OP_COMP_F;
- break;
- }
- 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 :
- Error( "Invalid type for !" );
- // shut up compiler
- op = OP_NOT_F;
- break;
- case ev_object :
- op = OP_NOT_ENT;
- break;
- default :
- Error( "type mismatch for !" );
- // shut up compiler
- op = OP_NOT_F;
- break;
- }
- 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 :
- Error( "type mismatch for -" );
- // shut up compiler
- op = OP_NEG_F;
- break;
- }
- 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( void ) {
- 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( void ) {
- 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( void ) {
- 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( void ) {
- 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( void ) {
- 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( void ) {
- 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 ) {
- Error( "Unknown event '%s'", name );
- }
- // set the return type
- expectedType = GetTypeForEventArg( ev->GetReturnType() );
- if ( !expectedType ) {
- Error( "Invalid return type '%c' in definition of '%s' event.", ev->GetReturnType(), name );
- }
- 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 || ( expectedType == &type_void ) ) {
- Error( "Invalid parameter '%c' in definition of '%s' event.", format[ i ], name );
- }
- 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( void ) {
- 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.error );
- } else {
- sprintf( error, "Error: file %s, line %d: %s\n", gameLocal.program.GetFilename( currentFileNumber ), currentLineNumber, err.error );
- }
- parser.FreeSource();
- throw idCompileError( error );
- }
- parser.FreeSource();
- compile_time.Stop();
- if ( !toConsole ) {
- gameLocal.Printf( "Compiled '%s': %.1f ms\n", filename, compile_time.Milliseconds() );
- }
- }
|