OSDN Git Service

- Fixed: Wrong opcode for fixedsqrt.
[zandronum/zandronum-acc.git] / symbol.c
1 \r
2 //**************************************************************************\r
3 //**\r
4 //** symbol.c\r
5 //**\r
6 //**************************************************************************\r
7 \r
8 // HEADER FILES ------------------------------------------------------------\r
9 \r
10 #include <string.h>\r
11 #include <stdlib.h>\r
12 #include "common.h"\r
13 #include "symbol.h"\r
14 #include "misc.h"\r
15 #include "parse.h"\r
16 \r
17 // MACROS ------------------------------------------------------------------\r
18 \r
19 // TYPES -------------------------------------------------------------------\r
20 \r
21 typedef struct\r
22 {\r
23         char *name;\r
24         pcd_t directCommand;\r
25         pcd_t stackCommand;\r
26         int argCount;\r
27         int optMask;\r
28         int outMask;\r
29         boolean hasReturnValue;\r
30         boolean latent;\r
31 } internFuncDef_t;\r
32 \r
33 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------\r
34 \r
35 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------\r
36 \r
37 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------\r
38 \r
39 static symbolNode_t *Find(char *name, symbolNode_t *root);\r
40 static symbolNode_t *Insert(char *name, symbolType_t type,\r
41         symbolNode_t **root);\r
42 static void FreeNodes(symbolNode_t *root);\r
43 static void FreeNodesAtDepth(symbolNode_t **root, int depth);\r
44 static void DeleteNode(symbolNode_t *node, symbolNode_t **parent_p);\r
45 static void ClearShared(symbolNode_t *root);\r
46 \r
47 // EXTERNAL DATA DECLARATIONS ----------------------------------------------\r
48 \r
49 // PUBLIC DATA DEFINITIONS -------------------------------------------------\r
50 \r
51 // PRIVATE DATA DEFINITIONS ------------------------------------------------\r
52 \r
53 static symbolNode_t *LocalRoot;\r
54 static symbolNode_t *GlobalRoot;\r
55 \r
56 static internFuncDef_t InternalFunctions[] =\r
57 {\r
58         { "tagwait", PCD_TAGWAITDIRECT, PCD_TAGWAIT, 1, 0, 0, NO, YES },\r
59         { "polywait", PCD_POLYWAITDIRECT, PCD_POLYWAIT, 1, 0, 0, NO, YES },\r
60         { "scriptwait", PCD_SCRIPTWAITDIRECT, PCD_SCRIPTWAIT, 1, 0, 0, NO, YES },\r
61         { "namedscriptwait", PCD_NOP, PCD_SCRIPTWAITNAMED, 1, 0, 0, NO, YES },\r
62         { "delay", PCD_DELAYDIRECT, PCD_DELAY, 1, 0, 0, NO, YES },\r
63         { "random", PCD_RANDOMDIRECT, PCD_RANDOM, 2, 0, 0, YES, NO },\r
64         { "thingcount", PCD_THINGCOUNTDIRECT, PCD_THINGCOUNT, 2, 0, 0, YES, NO },\r
65         { "thingcountname", PCD_NOP, PCD_THINGCOUNTNAME, 2, 0, 0, YES, NO },\r
66         { "changefloor", PCD_CHANGEFLOORDIRECT, PCD_CHANGEFLOOR, 2, 0, 0, NO, NO },\r
67         { "changeceiling", PCD_CHANGECEILINGDIRECT, PCD_CHANGECEILING, 2, 0, 0, NO, NO },\r
68         { "lineside", PCD_NOP, PCD_LINESIDE, 0, 0, 0, YES, NO },\r
69         { "clearlinespecial", PCD_NOP, PCD_CLEARLINESPECIAL, 0, 0, 0, NO, NO },\r
70         { "playercount", PCD_NOP, PCD_PLAYERCOUNT, 0, 0, 0, YES, NO },\r
71         { "gametype", PCD_NOP, PCD_GAMETYPE, 0, 0, 0, YES, NO },\r
72         { "gameskill", PCD_NOP, PCD_GAMESKILL, 0, 0, 0, YES, NO },\r
73         { "timer", PCD_NOP, PCD_TIMER, 0, 0, 0, YES, NO },\r
74         { "sectorsound", PCD_NOP, PCD_SECTORSOUND, 2, 0, 0, NO, NO },\r
75         { "ambientsound", PCD_NOP, PCD_AMBIENTSOUND, 2, 0, 0, NO, NO },\r
76         { "soundsequence", PCD_NOP, PCD_SOUNDSEQUENCE, 1, 0, 0, NO, NO },\r
77         { "setlinetexture", PCD_NOP, PCD_SETLINETEXTURE, 4, 0, 0, NO, NO },\r
78         { "setlineblocking", PCD_NOP, PCD_SETLINEBLOCKING, 2, 0, 0, NO, NO },\r
79         { "setlinespecial", PCD_NOP, PCD_SETLINESPECIAL, 7, 4|8|16|32|64, 0, NO, NO },\r
80         { "thingsound", PCD_NOP, PCD_THINGSOUND, 3, 0, 0, NO, NO },\r
81         { "activatorsound", PCD_NOP, PCD_ACTIVATORSOUND, 2, 0, 0, NO, NO },\r
82         { "localambientsound", PCD_NOP, PCD_LOCALAMBIENTSOUND, 2, 0, 0, NO, NO },\r
83         { "setlinemonsterblocking", PCD_NOP, PCD_SETLINEMONSTERBLOCKING, 2, 0, 0, NO, NO },\r
84         { "fixedmul", PCD_NOP, PCD_FIXEDMUL, 2, 0, 0, YES, NO },\r
85         { "fixeddiv", PCD_NOP, PCD_FIXEDDIV, 2, 0, 0, YES, NO },\r
86 // [BC] Start of new pcodes\r
87         { "playerblueskull", PCD_NOP, PCD_PLAYERBLUESKULL, 0, 0, 0, YES, NO },\r
88         { "playerredskull", PCD_NOP, PCD_PLAYERREDSKULL, 0, 0, 0, YES, NO },\r
89         { "playeryellowskull", PCD_NOP, PCD_PLAYERYELLOWSKULL, 0, 0, 0, YES, NO },\r
90         { "playerbluecard", PCD_NOP, PCD_PLAYERBLUECARD, 0, 0, 0, YES, NO },\r
91         { "playerredcard", PCD_NOP, PCD_PLAYERREDCARD, 0, 0, 0, YES, NO },\r
92         { "playeryellowcard", PCD_NOP, PCD_PLAYERYELLOWCARD, 0, 0, 0, YES, NO },\r
93         { "playeronteam", PCD_NOP, PCD_PLAYERONTEAM, 0, 0, 0, YES, NO },\r
94         { "playerteam", PCD_NOP, PCD_PLAYERTEAM, 0, 0, 0, YES, NO },\r
95         { "playerfrags", PCD_NOP, PCD_PLAYERFRAGS, 0, 0, 0, YES, NO },\r
96         { "playerhealth", PCD_NOP, PCD_PLAYERHEALTH, 0, 0, 0, YES, NO },\r
97         { "playerarmorpoints", PCD_NOP, PCD_PLAYERARMORPOINTS, 0, 0, 0, YES, NO },\r
98         { "playerexpert", PCD_NOP, PCD_PLAYEREXPERT, 0, 0, 0, YES, NO },\r
99         { "bluecount", PCD_NOP, PCD_BLUETEAMCOUNT, 0, 0, 0, YES, NO },\r
100         { "redcount", PCD_NOP, PCD_REDTEAMCOUNT, 0, 0, 0, YES, NO },\r
101         { "bluescore", PCD_NOP, PCD_BLUETEAMSCORE, 0, 0, 0, YES, NO },\r
102         { "redscore", PCD_NOP, PCD_REDTEAMSCORE, 0, 0, 0, YES, NO },\r
103         { "isoneflagctf", PCD_NOP, PCD_ISONEFLAGCTF, 0, 0, 0, YES, NO },\r
104         { "getinvasionwave", PCD_NOP, PCD_GETINVASIONWAVE, 0, 0, 0, YES, NO },\r
105         { "getinvasionstate", PCD_NOP, PCD_GETINVASIONSTATE, 0, 0, 0, YES, NO },\r
106         { "music_change", PCD_NOP, PCD_MUSICCHANGE, 2, 0, 0, NO, NO },\r
107         { "consolecommand", PCD_CONSOLECOMMANDDIRECT, PCD_CONSOLECOMMAND, 3, 2|4, 0, NO, NO },\r
108         { "singleplayer", PCD_NOP, PCD_SINGLEPLAYER, 0, 0, 0, YES, NO },\r
109 // [RH] end of Skull Tag functions\r
110         { "setgravity", PCD_SETGRAVITYDIRECT, PCD_SETGRAVITY, 1, 0, 0, NO, NO },\r
111         { "setaircontrol", PCD_SETAIRCONTROLDIRECT, PCD_SETAIRCONTROL, 1, 0, 0, NO, NO },\r
112         { "clearinventory", PCD_NOP, PCD_CLEARINVENTORY, 0, 0, 0, NO, NO },\r
113         { "giveinventory", PCD_GIVEINVENTORYDIRECT, PCD_GIVEINVENTORY, 2, 0, 0, NO, NO },\r
114         { "takeinventory", PCD_TAKEINVENTORYDIRECT, PCD_TAKEINVENTORY, 2, 0, 0, NO, NO },\r
115         { "checkinventory", PCD_CHECKINVENTORYDIRECT, PCD_CHECKINVENTORY, 1, 0, 0, YES, NO },\r
116         { "clearactorinventory", PCD_NOP, PCD_CLEARACTORINVENTORY, 1, 0, 0, NO, NO },\r
117         { "giveactorinventory", PCD_NOP, PCD_GIVEACTORINVENTORY, 3, 0, 0, NO, NO },\r
118         { "takeactorinventory", PCD_NOP, PCD_TAKEACTORINVENTORY, 3, 0, 0, NO, NO },\r
119         { "checkactorinventory", PCD_NOP, PCD_CHECKACTORINVENTORY, 2, 0, 0, YES, NO },\r
120         { "spawn", PCD_SPAWNDIRECT, PCD_SPAWN, 6, 16|32, 0, YES, NO },\r
121         { "spawnspot", PCD_SPAWNSPOTDIRECT, PCD_SPAWNSPOT, 4, 4|8, 0, YES, NO },\r
122         { "spawnspotfacing", PCD_NOP, PCD_SPAWNSPOTFACING, 3, 4, 0, YES, NO },\r
123         { "setmusic", PCD_SETMUSICDIRECT, PCD_SETMUSIC, 3, 2|4, 0, NO, NO },\r
124         { "localsetmusic", PCD_LOCALSETMUSICDIRECT, PCD_LOCALSETMUSIC, 3, 2|4, 0, NO, NO },\r
125         { "setstyle", PCD_SETSTYLEDIRECT, PCD_SETSTYLE, 1, 0, 0, NO, NO },\r
126         { "setfont", PCD_SETFONTDIRECT, PCD_SETFONT, 1, 0, 0, NO, NO },\r
127         { "setthingspecial", PCD_NOP, PCD_SETTHINGSPECIAL, 7, 4|8|16|32|64, 0, NO, NO },\r
128         { "fadeto", PCD_NOP, PCD_FADETO, 5, 0, 0, NO, NO },\r
129         { "faderange", PCD_NOP, PCD_FADERANGE, 9, 0, 0, NO, NO },\r
130         { "cancelfade", PCD_NOP, PCD_CANCELFADE, 0, 0, 0, NO, NO },\r
131         { "playmovie", PCD_NOP, PCD_PLAYMOVIE, 1, 0, 0, YES, NO },\r
132         { "setfloortrigger", PCD_NOP, PCD_SETFLOORTRIGGER, 8, 8|16|32|64|128, 0, NO, NO },\r
133         { "setceilingtrigger", PCD_NOP, PCD_SETCEILINGTRIGGER, 8, 8|16|32|64|128, 0, NO, NO },\r
134         { "setactorposition", PCD_NOP, PCD_SETACTORPOSITION, 5, 0, 0, YES, NO },\r
135         { "getactorx", PCD_NOP, PCD_GETACTORX, 1, 0, 0, YES, NO },\r
136         { "getactory", PCD_NOP, PCD_GETACTORY, 1, 0, 0, YES, NO },\r
137         { "getactorz", PCD_NOP, PCD_GETACTORZ, 1, 0, 0, YES, NO },\r
138         { "getactorfloorz", PCD_NOP, PCD_GETACTORFLOORZ, 1, 0, 0, YES, NO },\r
139         { "getactorceilingz", PCD_NOP, PCD_GETACTORCEILINGZ, 1, 0, 0, YES, NO },\r
140         { "getactorangle", PCD_NOP, PCD_GETACTORANGLE, 1, 0, 0, YES, NO },\r
141         { "writetoini", PCD_NOP, PCD_WRITETOINI, 3, 0, 0, NO, NO },\r
142         { "getfromini", PCD_NOP, PCD_GETFROMINI, 3, 0, 0, YES, NO },\r
143         { "sin", PCD_NOP, PCD_SIN, 1, 0, 0, YES, NO },\r
144         { "cos", PCD_NOP, PCD_COS, 1, 0, 0, YES, NO },\r
145         { "vectorangle", PCD_NOP, PCD_VECTORANGLE, 2, 0, 0, YES, NO },\r
146         { "vectorlength", PCD_NOP, PCD_VECTORLENGTH, 2, 0, 0, YES, NO },\r
147         { "sqrt", PCD_NOP, PCD_SQRT, 1, 0, 0, YES, NO },\r
148         { "fixedsqrt", PCD_NOP, PCD_FIXEDSQRT, 1, 0, 0, YES, NO },\r
149         { "checkweapon", PCD_NOP, PCD_CHECKWEAPON, 1, 0, 0, YES, NO },\r
150         { "setweapon", PCD_NOP, PCD_SETWEAPON, 1, 0, 0, YES, NO },\r
151         { "setmarineweapon", PCD_NOP, PCD_SETMARINEWEAPON, 2, 0, 0, NO, NO },\r
152         { "setactorproperty", PCD_NOP, PCD_SETACTORPROPERTY, 3, 0, 0, NO, NO },\r
153         { "getactorproperty", PCD_NOP, PCD_GETACTORPROPERTY, 2, 0, 0, YES, NO },\r
154         { "playernumber", PCD_NOP, PCD_PLAYERNUMBER, 0, 0, 0, YES, NO },\r
155         { "activatortid", PCD_NOP, PCD_ACTIVATORTID, 0, 0, 0, YES, NO },\r
156         { "setmarinesprite", PCD_NOP, PCD_SETMARINESPRITE, 2, 0, 0, NO, NO },\r
157         { "getscreenwidth", PCD_NOP, PCD_GETSCREENWIDTH, 0, 0, 0, YES, NO },\r
158         { "getscreenheight", PCD_NOP, PCD_GETSCREENHEIGHT, 0, 0, 0, YES, NO },\r
159         { "thing_projectile2", PCD_NOP, PCD_THING_PROJECTILE2, 7, 0, 0, NO, NO },\r
160         { "strlen", PCD_NOP, PCD_STRLEN, 1, 0, 0, YES, NO },\r
161         { "sethudsize", PCD_NOP, PCD_SETHUDSIZE, 3, 0, 0, NO, NO },\r
162         { "getcvar", PCD_NOP, PCD_GETCVAR, 1, 0, 0, YES, NO },\r
163         { "setresultvalue", PCD_NOP, PCD_SETRESULTVALUE, 1, 0, 0, NO, NO },\r
164         { "getlinerowoffset", PCD_NOP, PCD_GETLINEROWOFFSET, 0, 0, 0, YES, NO },\r
165         { "getsectorfloorz", PCD_NOP, PCD_GETSECTORFLOORZ, 3, 0, 0, YES, NO },\r
166         { "getsectorceilingz", PCD_NOP, PCD_GETSECTORCEILINGZ, 3, 0, 0, YES, NO },\r
167         { "getsigilpieces", PCD_NOP, PCD_GETSIGILPIECES, 0, 0, 0, YES, NO },\r
168         { "getlevelinfo", PCD_NOP, PCD_GETLEVELINFO, 1, 0, 0, YES, NO },\r
169         { "changesky", PCD_NOP, PCD_CHANGESKY, 2, 0, 0, NO, NO },\r
170         { "playeringame", PCD_NOP, PCD_PLAYERINGAME, 1, 0, 0, YES, NO },\r
171         { "playerisbot", PCD_NOP, PCD_PLAYERISBOT, 1, 0, 0, YES, NO },\r
172         { "setcameratotexture", PCD_NOP, PCD_SETCAMERATOTEXTURE, 3, 0, 0, NO, NO },\r
173         { "grabinput", PCD_NOP, PCD_GRABINPUT, 2, 0, 0, NO, NO },\r
174         { "setmousepointer", PCD_NOP, PCD_SETMOUSEPOINTER, 3, 0, 0, NO, NO },\r
175         { "movemousepointer", PCD_NOP, PCD_MOVEMOUSEPOINTER, 2, 0, 0, NO, NO },\r
176         { "getammocapacity", PCD_NOP, PCD_GETAMMOCAPACITY, 1, 0, 0, YES, NO },\r
177         { "setammocapacity", PCD_NOP, PCD_SETAMMOCAPACITY, 2, 0, 0, NO, NO },\r
178         { "setactorangle", PCD_NOP, PCD_SETACTORANGLE, 2, 0, 0, NO, NO },\r
179         { "spawnprojectile", PCD_NOP, PCD_SPAWNPROJECTILE, 7, 0, 0, NO, NO },\r
180         { "getsectorlightlevel", PCD_NOP, PCD_GETSECTORLIGHTLEVEL, 1, 0, 0, YES, NO },\r
181         { "playerclass", PCD_NOP, PCD_PLAYERCLASS, 1, 0, 0, YES, NO },\r
182         { "getplayerinfo", PCD_NOP, PCD_GETPLAYERINFO, 2, 0, 0, YES, NO },\r
183         { "changelevel", PCD_NOP, PCD_CHANGELEVEL, 4, 8, 0, NO, NO },\r
184         { "sectordamage", PCD_NOP, PCD_SECTORDAMAGE, 5, 0, 0, NO, NO },\r
185         { "replacetextures", PCD_NOP, PCD_REPLACETEXTURES, 3, 4, 0, NO, NO },\r
186         { "getactorpitch", PCD_NOP, PCD_GETACTORPITCH, 1, 0, 0, YES, NO },\r
187         { "setactorpitch", PCD_NOP, PCD_SETACTORPITCH, 2, 0, 0, NO, NO },\r
188         { "setactorstate", PCD_NOP, PCD_SETACTORSTATE, 3, 4, 0, YES, NO },\r
189         { "thing_damage2", PCD_NOP, PCD_THINGDAMAGE2, 3, 0, 0, YES, NO },\r
190         { "useinventory", PCD_NOP, PCD_USEINVENTORY, 1, 0, 0, YES, NO },\r
191         { "useactorinventory", PCD_NOP, PCD_USEACTORINVENTORY, 2, 0, 0, YES, NO },\r
192         { "checkactorceilingtexture", PCD_NOP, PCD_CHECKACTORCEILINGTEXTURE, 2, 0, 0, YES, NO },\r
193         { "checkactorfloortexture", PCD_NOP, PCD_CHECKACTORFLOORTEXTURE, 2, 0, 0, YES, NO },\r
194         { "getactorlightlevel", PCD_NOP, PCD_GETACTORLIGHTLEVEL, 1, 0, 0, YES, NO },\r
195         { "setmugshotstate", PCD_NOP, PCD_SETMUGSHOTSTATE, 1, 0, 0, NO, NO },\r
196         { "thingcountsector", PCD_NOP, PCD_THINGCOUNTSECTOR, 3, 0, 0, YES, NO },\r
197         { "thingcountnamesector", PCD_NOP, PCD_THINGCOUNTNAMESECTOR, 3, 0, 0, YES, NO },\r
198         { "checkplayercamera", PCD_NOP, PCD_CHECKPLAYERCAMERA, 1, 0, 0, YES, NO },\r
199         { "morphactor", PCD_NOP, PCD_MORPHACTOR, 7, 2|4|8|16|32|64, 0, YES, NO },\r
200         { "unmorphactor", PCD_NOP, PCD_UNMORPHACTOR, 2, 2, 0, YES, NO },\r
201         { "getplayerinput", PCD_NOP, PCD_GETPLAYERINPUT, 2, 0, 0, YES, NO },\r
202         { "classifyactor", PCD_NOP, PCD_CLASSIFYACTOR, 1, 0, 0, YES, NO },\r
203         \r
204         { NULL, PCD_NOP, PCD_NOP, 0, 0, 0, NO, NO }\r
205 };\r
206 \r
207 static char *SymbolTypeNames[] =\r
208 {\r
209         "SY_DUMMY",\r
210         "SY_LABEL",\r
211         "SY_SCRIPTVAR",\r
212         "SY_SCRIPTALIAS",\r
213         "SY_MAPVAR",\r
214         "SY_WORLDVAR",\r
215         "SY_GLOBALVAR",\r
216         "SY_MAPARRAY",\r
217         "SY_WORLDARRAY",\r
218         "SY_GLOBALARRAY",\r
219         "SY_SPECIAL",\r
220         "SY_CONSTANT",\r
221         "SY_INTERNFUNC",\r
222         "SY_SCRIPTFUNC"\r
223 };\r
224 \r
225 // CODE --------------------------------------------------------------------\r
226 \r
227 //==========================================================================\r
228 //\r
229 // SY_Init\r
230 //\r
231 //==========================================================================\r
232 \r
233 void SY_Init(void)\r
234 {\r
235         symbolNode_t *sym;\r
236         internFuncDef_t *def;\r
237 \r
238         LocalRoot = NULL;\r
239         GlobalRoot = NULL;\r
240         for(def = InternalFunctions; def->name != NULL; def++)\r
241         {\r
242                 sym = SY_InsertGlobal(def->name, SY_INTERNFUNC);\r
243                 sym->info.internFunc.directCommand = def->directCommand;\r
244                 sym->info.internFunc.stackCommand = def->stackCommand;\r
245                 sym->info.internFunc.argCount = def->argCount;\r
246                 sym->info.internFunc.optMask = def->optMask;\r
247                 sym->info.internFunc.outMask = def->outMask;\r
248                 sym->info.internFunc.hasReturnValue = def->hasReturnValue;\r
249                 sym->info.internFunc.latent = def->latent;\r
250         }\r
251 }\r
252 \r
253 //==========================================================================\r
254 //\r
255 // SY_Find\r
256 //\r
257 //==========================================================================\r
258 \r
259 symbolNode_t *SY_Find(char *name)\r
260 {\r
261         symbolNode_t *node;\r
262 \r
263         if((node = SY_FindGlobal(name)) == NULL)\r
264         {\r
265                 return SY_FindLocal(name);\r
266         }\r
267         return node;\r
268 }\r
269 \r
270 //==========================================================================\r
271 //\r
272 // SY_FindGlobal\r
273 //\r
274 //==========================================================================\r
275 \r
276 symbolNode_t *SY_FindGlobal(char *name)\r
277 {\r
278         symbolNode_t *sym = Find(name, GlobalRoot);\r
279         if(sym != NULL && sym->unused)\r
280         {\r
281                 MS_Message(MSG_DEBUG, "Symbol %s marked as used.\n", name);\r
282                 sym->unused = NO;\r
283                 if(sym->type == SY_SCRIPTFUNC)\r
284                 {\r
285                         PC_AddFunction(sym);\r
286                 }\r
287                 else if(sym->type == SY_MAPVAR)\r
288                 {\r
289                         if(pa_MapVarCount >= MAX_MAP_VARIABLES)\r
290                         {\r
291                                 ERR_Error(ERR_TOO_MANY_MAP_VARS, YES);\r
292                         }\r
293                         else\r
294                         {\r
295                                 sym->info.var.index = pa_MapVarCount++;\r
296                                 PC_NameMapVariable(sym->info.var.index, sym);\r
297                         }\r
298                 }\r
299                 else if(sym->type == SY_MAPARRAY)\r
300                 {\r
301                         if(pa_MapVarCount >= MAX_MAP_VARIABLES)\r
302                         {\r
303                                 ERR_Error(ERR_TOO_MANY_MAP_VARS, YES);\r
304                         }\r
305                         else\r
306                         {\r
307                                 sym->info.array.index = pa_MapVarCount++;\r
308                                 PC_NameMapVariable(sym->info.array.index, sym);\r
309                                 if(sym->type == SY_MAPARRAY)\r
310                                 {\r
311                                         PC_AddArray(sym->info.array.index, sym->info.array.size);\r
312                                 }\r
313                         }\r
314                 }\r
315         }\r
316         return sym;\r
317 }\r
318 \r
319 //==========================================================================\r
320 //\r
321 // SY_Findlocal\r
322 //\r
323 //==========================================================================\r
324 \r
325 symbolNode_t *SY_FindLocal(char *name)\r
326 {\r
327         return Find(name, LocalRoot);\r
328 }\r
329 \r
330 //==========================================================================\r
331 //\r
332 // Find\r
333 //\r
334 //==========================================================================\r
335 \r
336 static symbolNode_t *Find(char *name, symbolNode_t *root)\r
337 {\r
338         int compare;\r
339         symbolNode_t *node;\r
340 \r
341         node = root;\r
342         while(node != NULL)\r
343         {\r
344                 compare = strcmp(name, node->name);\r
345                 if(compare == 0)\r
346                 {\r
347                         if(node->type != SY_DUMMY)\r
348                         {\r
349                                 return node;\r
350                         }\r
351                         else\r
352                         {\r
353                                 return NULL;\r
354                         }\r
355                 }\r
356                 node = compare < 0 ? node->left : node->right;\r
357         }\r
358         return NULL;\r
359 }\r
360 \r
361 //==========================================================================\r
362 //\r
363 // SY_InsertLocal\r
364 //\r
365 //==========================================================================\r
366 \r
367 symbolNode_t *SY_InsertLocal(char *name, symbolType_t type)\r
368 {\r
369         if(Find(name, GlobalRoot))\r
370         {\r
371                 ERR_Error(ERR_LOCAL_VAR_SHADOWED, YES);\r
372         }\r
373         MS_Message(MSG_DEBUG, "Inserting local identifier: %s (%s)\n",\r
374                 name, SymbolTypeNames[type]);\r
375         return Insert(name, type, &LocalRoot);\r
376 }\r
377 \r
378 //==========================================================================\r
379 //\r
380 // SY_InsertGlobal\r
381 //\r
382 //==========================================================================\r
383 \r
384 symbolNode_t *SY_InsertGlobal(char *name, symbolType_t type)\r
385 {\r
386         MS_Message(MSG_DEBUG, "Inserting global identifier: %s (%s)\n",\r
387                 name, SymbolTypeNames[type]);\r
388         return Insert(name, type, &GlobalRoot);\r
389 }\r
390 \r
391 //==========================================================================\r
392 //\r
393 // SY_InsertGlobalUnique\r
394 //\r
395 //==========================================================================\r
396 \r
397 symbolNode_t *SY_InsertGlobalUnique(char *name, symbolType_t type)\r
398 {\r
399         if(SY_FindGlobal(name) != NULL)\r
400         { // Redefined\r
401                 ERR_Exit(ERR_REDEFINED_IDENTIFIER, YES, name);\r
402         }\r
403         return SY_InsertGlobal(name, type);\r
404 }\r
405 \r
406 //==========================================================================\r
407 //\r
408 // Insert\r
409 //\r
410 //==========================================================================\r
411 \r
412 static symbolNode_t *Insert(char *name, symbolType_t type,\r
413         symbolNode_t **root)\r
414 {\r
415         int compare;\r
416         symbolNode_t *newNode;\r
417         symbolNode_t *node;\r
418 \r
419         newNode = MS_Alloc(sizeof(symbolNode_t), ERR_NO_SYMBOL_MEM);\r
420         newNode->name = MS_Alloc(strlen(name)+1, ERR_NO_SYMBOL_MEM);\r
421         strcpy(newNode->name, name);\r
422         newNode->left = newNode->right = NULL;\r
423         newNode->type = type;\r
424         newNode->unused = NO;\r
425         newNode->imported = ImportMode == IMPORT_Importing;\r
426         while((node = *root) != NULL)\r
427         {\r
428                 compare = strcmp(name, node->name);\r
429                 root = compare < 0 ? &(node->left) : &(node->right);\r
430         }\r
431         *root = newNode;\r
432         return(newNode);\r
433 }\r
434 \r
435 //==========================================================================\r
436 //\r
437 // SY_FreeLocals\r
438 //\r
439 //==========================================================================\r
440 \r
441 void SY_FreeLocals(void)\r
442 {\r
443         MS_Message(MSG_DEBUG, "Freeing local identifiers\n");\r
444         FreeNodes(LocalRoot);\r
445         LocalRoot = NULL;\r
446 }\r
447 \r
448 //==========================================================================\r
449 //\r
450 // SY_FreeGlobals\r
451 //\r
452 //==========================================================================\r
453 \r
454 void SY_FreeGlobals(void)\r
455 {\r
456         MS_Message(MSG_DEBUG, "Freeing global identifiers\n");\r
457         FreeNodes(GlobalRoot);\r
458         GlobalRoot = NULL;\r
459 }\r
460 \r
461 //==========================================================================\r
462 //\r
463 // FreeNodes\r
464 //\r
465 //==========================================================================\r
466 \r
467 static void FreeNodes(symbolNode_t *root)\r
468 {\r
469         if(root == NULL)\r
470         {\r
471                 return;\r
472         }\r
473         FreeNodes(root->left);\r
474         FreeNodes(root->right);\r
475         free(root->name);\r
476         free(root);\r
477 }\r
478 \r
479 //==========================================================================\r
480 //\r
481 // SY_FreeConstants\r
482 //\r
483 //==========================================================================\r
484 \r
485 void SY_FreeConstants(int depth)\r
486 {\r
487         MS_Message(MSG_DEBUG, "Freeing constants for depth %d\n", depth);\r
488         FreeNodesAtDepth(&GlobalRoot, depth);\r
489 }\r
490 \r
491 //==========================================================================\r
492 //\r
493 // FreeNodesAtDepth\r
494 //\r
495 // Like FreeNodes, but it only frees the nodes of type SY_CONSTANT that are\r
496 // marked at the specified depth. The other nodes are relinked to maintain a\r
497 // proper binary tree.\r
498 //\r
499 //==========================================================================\r
500 \r
501 static void FreeNodesAtDepth(symbolNode_t **root, int depth)\r
502 {\r
503         symbolNode_t *node = *root;\r
504 \r
505         if(node == NULL)\r
506         {\r
507                 return;\r
508         }\r
509         FreeNodesAtDepth(&node->left, depth);\r
510         FreeNodesAtDepth(&node->right, depth);\r
511         if(node->type == SY_CONSTANT && node->info.constant.fileDepth == depth)\r
512         {\r
513                 MS_Message(MSG_DEBUG, "Deleting constant %s\n", node->name);\r
514                 DeleteNode(node, root);\r
515         }\r
516 }\r
517 \r
518 //==========================================================================\r
519 //\r
520 // DeleteNode\r
521 //\r
522 //==========================================================================\r
523 \r
524 static void DeleteNode(symbolNode_t *node, symbolNode_t **parent_p)\r
525 {\r
526         symbolNode_t **temp;\r
527         char *nametemp;\r
528 \r
529         if(node->left == NULL)\r
530         {\r
531                 *parent_p = node->right;\r
532                 free(node->name);\r
533                 free(node);\r
534         }\r
535         else if(node->right == NULL)\r
536         {\r
537                 *parent_p = node->left;\r
538                 free(node->name);\r
539                 free(node);\r
540         }\r
541         else\r
542         {\r
543                 // "Randomly" pick the in-order successor or predecessor to take\r
544                 // the place of the deleted node.\r
545                 if(rand() & 1)\r
546                 {\r
547                         // predecessor\r
548                         temp = &node->left;\r
549                         while((*temp)->right != NULL)\r
550                         {\r
551                                 temp = &(*temp)->right;\r
552                         }\r
553                 }\r
554                 else\r
555                 {\r
556                         // successor\r
557                         temp = &node->right;\r
558                         while((*temp)->left != NULL)\r
559                         {\r
560                                 temp = &(*temp)->left;\r
561                         }\r
562                 }\r
563                 nametemp = node->name;\r
564                 node->name = (*temp)->name;\r
565                 (*temp)->name = nametemp;\r
566                 node->type = (*temp)->type;\r
567                 node->unused = (*temp)->unused;\r
568                 node->imported = (*temp)->imported;\r
569                 node->info = (*temp)->info;\r
570                 DeleteNode(*temp, temp);\r
571         }\r
572 }\r
573 \r
574 //==========================================================================\r
575 //\r
576 // SY_ClearShared\r
577 //\r
578 //==========================================================================\r
579 \r
580 void SY_ClearShared(void)\r
581 {\r
582         MS_Message(MSG_DEBUG, "Marking library exports as unused\n");\r
583         ClearShared(GlobalRoot);\r
584 }\r
585 \r
586 //==========================================================================\r
587 //\r
588 // ClearShared\r
589 //\r
590 //==========================================================================\r
591 \r
592 static void ClearShared(symbolNode_t *root)\r
593 {\r
594         while(root != NULL)\r
595         {\r
596                 if( root->type == SY_SCRIPTFUNC ||\r
597                         root->type == SY_MAPVAR ||\r
598                         root->type == SY_MAPARRAY)\r
599                 {\r
600                         root->unused = YES;\r
601                 }\r
602                 ClearShared(root->left);\r
603                 root = root->right;\r
604         }\r
605 }\r