]> icculus.org git repositories - divverent/nexuiz.git/blob - scmenu/source/system/parser.qc
Adding my current version of the scmenu to the nexuiz cvs.
[divverent/nexuiz.git] / scmenu / source / system / parser.qc
1 // DP/Nex Menu
2 // system/parser.qc
3
4 void() Parser_Define_Spawn = {};
5
6 void() Parser_TokenizeLine =
7 {
8         Parser_NumTokens = tokenize( Parser_Line );
9         Parser_TokenNum = 0;
10 };
11
12 bool() Parser_GetToken =
13 {
14         local string lLine;
15
16         if( Parser_TokenNum >= Parser_NumTokens ) { // get a new line
17                 lLine = fgets( Parser_File );
18                 ++Parser_LineNumber;
19                 if( !lLine )
20                         if( !validstring( lLine ) )
21                                 return false;
22                         else
23                                 return Parser_GetToken();
24
25                 Parser_Line = String_Set( Parser_Line, lLine );
26                 Parser_TokenizeLine();
27                 return Parser_GetToken();
28         } else {
29                 Parser_Token = String_Set( Parser_Token, argv( Parser_TokenNum ) );
30                 Parser_TokenNum++;
31         }
32
33         Parser_Print( PARSER_LOW, strcat( "Read token '", Parser_Token, "'" ) );
34
35         Parser_TokenType = Parser_GetTokenType();
36         if( Parser_TokenType == PARSER_TT_BRACKETOPEN ) {
37                 Parser_ProcessDefine();
38                 return Parser_GetToken();
39         }
40
41         return true;
42 };
43
44 float() Parser_GetTokenType =
45 {
46         if( Parser_Token == "Item" )
47                 return PARSER_TT_ITEM;
48         else if( Parser_Token == "Template" )
49                 return PARSER_TT_TEMPLATE;
50         else if( Parser_Token == "Derive" )
51                 return PARSER_TT_DERIVE;
52         else if( Parser_Token == "DeriveTemplate" )
53                 return PARSER_TT_DERIVETEMPLATE;
54         else if( Parser_Token == "#define" )
55                 return PARSER_TT_DEFINE;
56         else if( Parser_Token == "Ignore" )
57                 return PARSER_TT_IGNORE;
58         else if( Parser_Token == "#undef" )
59                 return PARSER_TT_UNDEF;
60         else if( Parser_Token == "Namespace" )
61                 return PARSER_TT_NAMESPACE;
62         else if( Parser_Token == "#include" )
63                 return PARSER_TT_INCLUDE;
64         else if( Parser_Token == "}" )
65                 return PARSER_TT_BRACECLOSE;
66         else if( Parser_Token == "{" )
67                 return PARSER_TT_BRACEOPEN;
68         else if( Parser_Token == "[" )
69                 return PARSER_TT_BRACKETOPEN;
70         else if( Parser_Token == "]" )
71                 return PARSER_TT_BRACKETCLOSE;
72
73         return PARSER_TT_TOKEN;
74 };
75
76 void( float pLevel, string pText ) _Parser_Print =
77 {
78         if( pLevel == 0 || sys_debug_parser & pLevel )
79                 print( "Parser: ", pText, "\n" );
80 };
81
82 void( float pLevel, string pInfo ) Parser_Print =
83 {
84         if( pLevel == 0 || sys_debug_parser & pLevel )
85                 print( "Parser: ", Parser_Filename, ":", ftos( Parser_LineNumber ), ": ", pInfo, "\n" );
86 };
87
88 void( string pInfo ) Parser_Error =
89 {
90         print( "Parser: ", Parser_Filename, ":", ftos( Parser_LineNumber ), ": Error: '");
91         print( Parser_Token, "' not expected (", pInfo, ")!\n" );
92         fclose( Parser_File );
93         error( "Error in the menu parser!" );
94 };
95
96 void( float pType ) Parser_Expect =
97 {
98         if( !Parser_GetToken() || ( Parser_TokenType != pType && Parser_TokenType != PARSER_TT_BRACKETOPEN ) )
99                 Parser_Error( strcat( "expected ", PARSER_TT_TEXT[ pType - PARSER_TT_ITEM ] ) );
100 };
101
102 void( string pNamespace ) Parser_IncludeFile =
103 {
104         local string    lFilename, lLine;
105         local float     lFile, lLineNumber, lNumTokens, lTokenNum;
106
107         // #include file
108         Parser_Expect( PARSER_TT_TOKEN );
109
110         Parser_Print( PARSER_INFO, strcat( "#include: Including file '", Parser_Token, "'" ) );
111
112         Parser_FileList = String_Append( Parser_FileList, strcat( " {'", String_Normal( Util_AltStringPrepare( Parser_Token ) ), "'" ) );
113
114         lFilename = Parser_Filename;
115         lLine = Parser_Line;
116
117         lFile = Parser_File;
118         lLineNumber = Parser_LineNumber;
119         lNumTokens = Parser_NumTokens;
120         lTokenNum = Parser_TokenNum;
121
122         --Parser_IncludeDepth;
123         if( Parser_IncludeDepth > Parser_MaxIncludeDepth )
124                 Parser_Print( PARSER_NORMAL, "#include: Maximum depth reached!" );
125         else
126                 Parser_ParseFile( Parser_Token, pNamespace );
127         --Parser_IncludeDepth;
128
129         Parser_Filename = lFilename;
130         Parser_Line = lLine;
131
132         Parser_File = lFile;
133         Parser_LineNumber = lLineNumber;
134
135         // tokenize the line again, but jump to the old position
136         Parser_TokenizeLine();
137         Parser_NumTokens = lNumTokens;
138         Parser_TokenNum = lTokenNum;
139
140         Parser_FileList = String_Append( Parser_FileList, "}" );
141 };
142
143 void() Parser_ParseDefine =
144 {
145         local entity lDefine;
146         local float lOldLine;
147
148         Parser_Print( 2, "Parsing #define..." );
149
150         // #define NAME CONSTANT
151         //Parser_Expect( PARSER_TT_BRACKETOPEN );
152         Parser_Expect( PARSER_TT_TOKEN );
153
154         // check for double definitions (dont error just print a warning)
155         for( lDefine = Parser_DefineChain ; lDefine ; lDefine = lDefine.chain )
156                 if( Parser_Token == lDefine.name ) {
157                         Parser_Print( PARSER_INFO, strcat( "#define: [", Parser_Token, "] already defined!" ) );
158                         Parser_Expect( PARSER_TT_TOKEN );
159                         return;
160                 }
161
162
163         lDefine = spawn();
164         lDefine.type = "Parser_Define";
165         lDefine.name = String_Zone( Parser_Token );
166
167         //Parser_Expect( PARSER_TT_BRACKETCLOSE );
168         // read the rest of the line
169         String_EntityZone( lDefine, value );
170         lOldLine = Parser_LineNumber;
171         while( 1 )
172                 if( !Parser_GetToken() )
173                         break;
174                 else if( lOldLine != Parser_LineNumber ) {
175                         --Parser_TokenNum;
176                         break;
177                 } else if( Parser_Token == "\\" )
178                         ++lOldLine;
179                 else
180                         String_EntitySet( lDefine, value, strcat( lDefine.value, "\"", Parser_Token, "\" " ) );
181
182         Parser_Print( PARSER_HIGH, strcat( " Name = '", lDefine.name, "' Replacement = '", lDefine.value, "'" ) );
183
184         lDefine.chain = Parser_DefineChain;
185         Parser_DefineChain = lDefine;
186
187         Parser_Print( PARSER_HIGH, "Done parsing #define" );
188 };
189
190 void() Parser_ParseUndef =
191 {
192         local entity lEntity, lPrevious;
193
194         // #undef Name
195         Parser_Print( PARSER_HIGH, "Parsing #undef..." );
196         Parser_Expect( PARSER_TT_TOKEN );
197
198         lPrevious = null_entity;
199         for( lEntity = Parser_DefineChain ; lEntity ; lPrevious = lEntity, lEntity = lEntity.chain )
200                 if( lEntity.name == Parser_Token ) {
201                         if( lPrevious )
202                                 lPrevious.chain = lEntity.chain;
203                         else
204                                 Parser_DefineChain = lEntity.chain;
205
206                         Parser_Print( PARSER_INFO, strcat( "#undef: Removed [", Parser_Token, "]" ) );
207
208                         String_Free( lEntity.name );
209                         String_Free( lEntity.value );
210
211                         remove( lEntity );
212
213                         return;
214                 }
215
216         Parser_Print( PARSER_INFO, strcat( "#undef: [", Parser_Token, "] not found!" ) );
217 };
218
219 void() Parser_ProcessDefine =
220 {
221         local string lConstant;
222         local entity lDefine;
223
224         // [Name]
225         Parser_Expect( PARSER_TT_TOKEN );
226         lConstant = String_Zone( Parser_Token );
227
228         Parser_Expect( PARSER_TT_BRACKETCLOSE );
229
230         Parser_Print( PARSER_HIGH, strcat( "Processing [", lConstant, "]..." ) );
231
232         for( lDefine = Parser_DefineChain ; lDefine ; lDefine = lDefine.chain )
233                 if( lDefine.name == lConstant ) {
234                         // if you want to have a single token use \" or '
235                         Parser_Line = String_Set( Parser_Line, strcat( " ", lDefine.value ) );
236                         Parser_Print( PARSER_HIGH, strcat( "Replacing with '", Parser_Line, "'" ) );
237                         for( ; Parser_TokenNum < Parser_NumTokens ; Parser_TokenNum++ )
238                                 Parser_Line = String_Set( Parser_Line, strcat( Parser_Line, " \"", argv( Parser_TokenNum ), "\"" ) );
239                         Parser_TokenizeLine();
240                         String_Free( lConstant );
241                         return;
242                 }
243
244         Parser_Token = String_Set( Parser_Token, String_Normal( lConstant ) );
245         Parser_Print( PARSER_NORMAL, strcat( "#define: Couldn't find constant '", Parser_Token, "'!" ) );
246         Parser_Error( "constant not found" );
247 };
248
249 // Item [Template]
250 // Template
251 void( string pNamespace ) Parser_ParseDefinition =
252 {
253         local entity lEntity;
254
255         if( Parser_TokenType == PARSER_TT_ITEM )
256                 Parser_ParseItem( pNamespace );
257         else if( Parser_TokenType == PARSER_TT_TEMPLATE ) {
258                 lEntity = Parser_ParseItem( pNamespace );
259                 lEntity.flag = lEntity.flag | FLAG_TEMPLATE;
260         } else if( Parser_TokenType == PARSER_TT_DEFINE )
261                 Parser_ParseDefine();
262         else if( Parser_TokenType == PARSER_TT_DERIVE )
263                 Parser_DeriveItem( pNamespace );
264         else if( Parser_TokenType == PARSER_TT_DERIVETEMPLATE ) {
265                 lEntity = Parser_DeriveItem( pNamespace );
266                 lEntity.flag = lEntity.flag | FLAG_TEMPLATE;
267         } else if( Parser_TokenType == PARSER_TT_UNDEF )
268                 Parser_ParseUndef();
269         else if( Parser_TokenType == PARSER_TT_NAMESPACE )
270                 Parser_ParseNamespace( pNamespace );
271         else if( Parser_TokenType == PARSER_TT_INCLUDE )
272                 Parser_IncludeFile( pNamespace );
273         else if( Parser_TokenType == PARSER_TT_IGNORE )
274                 Parser_ParseIgnore();
275         else
276                 Parser_Error( "couldn't find type in Parser_ParseDef" );
277 };
278
279 void() Parser_ParseIgnore =
280 {
281         local float lBraceCount;
282
283         Parser_Expect( PARSER_TT_BRACEOPEN );
284         for( lBraceCount = 1 ; lBraceCount > 0 ; )
285                 if( !Parser_GetToken() )
286                         break;
287                 else if( Parser_TokenType == PARSER_TT_BRACEOPEN )
288                         ++lBraceCount;
289                 else if( Parser_TokenType == PARSER_TT_BRACECLOSE )
290                         --lBraceCount;
291 };
292
293 entity( string pNamespace ) Parser_ParseItem =
294 {
295         local string lNamespace;
296         local string lEntityText;
297         local entity lEntity;
298
299         Parser_Print( PARSER_HIGH, "Parsing item.." );
300
301         // get the item type
302         Parser_Expect( PARSER_TT_TOKEN );
303         lEntityText = String_Zone( strcat( "{ \"type\" \"Item_", Parser_Token,"\" " ) );
304
305         Parser_Print( PARSER_HIGH, strcat( " Type = '", Parser_Token, "'" ) );
306
307         // get the item name
308         Parser_Expect( PARSER_TT_TOKEN );
309         if( pNamespace != "" )
310                 lNamespace = String_Zone( strcat( pNamespace, "::", Parser_Token ) );
311         else
312                 lNamespace = String_Zone( Parser_Token );
313
314         lEntityText = String_Set( lEntityText, strcat( lEntityText, "\"name\" \"", lNamespace, "\" " ) );
315         lEntityText = String_Set( lEntityText, strcat( lEntityText, "\"parent\" \"", pNamespace, "\" " ) );
316
317         Parser_Print( PARSER_HIGH, strcat( " Name = '", Parser_Token, "' Parent = '", pNamespace,
318                                 "' Namespace = '", lNamespace, "'" ) );
319
320         Parser_Expect( PARSER_TT_BRACEOPEN );
321         lEntityText = Parser_ParseEntity( lNamespace, lEntityText );
322
323         // parse the entity (builtin)
324         lEntity = spawn();
325         parseentitydata( lEntity, lEntityText );
326
327         String_Free( lEntityText );
328         String_Free( lNamespace );
329
330         Parser_Print( PARSER_HIGH, strcat( "Parsing '", lEntity.name, "' finished" ) );
331
332         return lEntity;
333 };
334
335 void( entity pSource, entity pTarget ) Parser_CloneChildren =
336 {
337         // we search for all items that are direct children of pSource and copy them
338         // and adapt their parent and names
339         local entity lNode;
340
341         lNode = null_entity;
342         while ( (lNode = findstring( lNode, parent, pSource.name )) != null_entity ) {
343                 local entity lClone;
344                 local string lModifierString;
345
346                 lClone = spawn();
347                 copyentity( lNode, lClone );
348
349                 if( lClone.flag & FLAG_TEMPLATE )
350                         lClone.flag = lClone.flag - FLAG_TEMPLATE;
351
352                 lModifierString = strcat( "{ name \"", pTarget.name,
353                         substring( lNode.name, strlen( pSource.name ), 100000 ), "\" parent \"", pTarget.name, "\" }" );
354
355                 parseentitydata( lClone, lModifierString );
356
357                 Parser_CloneChildren( lNode, lClone );
358         }
359 };
360
361 entity( string pNamespace ) Parser_DeriveItem =
362 {
363         local string lNamespace;
364         local string lEntityText;
365         local entity lBase;
366         local string lBaseName;
367         local entity lEntity;
368
369         Parser_Print( PARSER_HIGH, "Parsing derived item.." );
370
371         // get the base item
372         Parser_Expect( PARSER_TT_TOKEN );
373
374         if( substring( Parser_Token, 0, 2 ) == "::" )
375                 lBaseName = String_Zone( substring( Parser_Token, 2, strlen( Parser_Token ) - 2 ) );
376         else if( pNamespace == "" )
377                 lBaseName = String_Zone( Parser_Token );
378         else {
379                 lBaseName = String_Zone( strcat( pNamespace, "::", Parser_Token ) );
380
381                 // try the local namespace first, then try to find it in the global
382                 if( findstring( null_entity,  name, lBaseName ) == null_entity )
383                         lBaseName = String_Set( lBaseName, Parser_Token );
384         }
385
386         Parser_Print( PARSER_HIGH, strcat( " Base = '", lBaseName, "'" ) );
387
388         lBase = findstring( null_entity, name, lBaseName );
389         if( lBase == null_entity )
390                 Parser_Error( "couldnt find item" );
391
392         // get the item name
393         Parser_Expect( PARSER_TT_TOKEN );
394         if( pNamespace != "" )
395                 lNamespace = String_Zone( strcat( pNamespace, "::", Parser_Token ) );
396         else
397                 lNamespace = String_Zone( Parser_Token );
398
399         lEntityText = String_Zone( strcat( "{ \"name\" \"", lNamespace, "\" " ) );
400         lEntityText = String_Set( lEntityText, strcat( lEntityText, "\"parent\" \"", pNamespace, "\" " ) );
401
402         Parser_Print( PARSER_HIGH, strcat( " Name = '", Parser_Token, "' Parent = '", pNamespace,
403                                 "' Namespace = '", lNamespace, "'" ) );
404
405         Parser_Expect( PARSER_TT_BRACEOPEN );
406         lEntityText = Parser_ParseEntity( lNamespace, lEntityText );
407
408         // parse the entity (builtin)
409         lEntity = spawn();
410         copyentity( lBase, lEntity );
411         if( lEntity.flag & FLAG_TEMPLATE )
412                 lEntity.flag = lEntity.flag - FLAG_TEMPLATE;
413         parseentitydata( lEntity, lEntityText );
414
415         String_Free( lEntityText );
416         String_Free( lNamespace );
417         String_Free( lBaseName );
418
419         // now copy over all children
420         Parser_CloneChildren( lBase, lEntity );
421
422         Parser_Print( PARSER_HIGH, strcat( "Parsing '", lEntity.name, "' finished" ) );
423
424         return lEntity;
425 };
426
427 string( string pNamespace, string pEntityText ) Parser_ParseEntity =
428 {
429         while( Parser_GetToken() ) {
430                 if( Parser_TokenType == PARSER_TT_TOKEN ) {
431                         // must be a property...
432                         // store the key value
433                         pEntityText = String_Set( pEntityText, strcat( pEntityText, "\"", Parser_Token, "\" " ) );
434                         Parser_Expect( PARSER_TT_TOKEN );
435                         pEntityText = String_Set( pEntityText, strcat( pEntityText, "\"", Parser_Token, "\" " ) );
436                 } else if( Parser_TokenType == PARSER_TT_BRACECLOSE )
437                         break;
438                 else
439                         Parser_ParseDefinition( pNamespace );
440         }
441
442         return String_Append( pEntityText, " }" );
443 };
444
445 void( string pNamespace ) Parser_ParseNamespace =
446 {
447         local string lNamespace;
448
449         Parser_Print( PARSER_HIGH, "Parsing Namespace..." );
450         // namespace Name {
451         Parser_Expect( PARSER_TT_TOKEN );
452         if( pNamespace != "" )
453                 lNamespace = String_Zone( strcat( pNamespace, "::", Parser_Token ) );
454         else
455                 lNamespace = String_Zone( Parser_Token );
456         Parser_Print( PARSER_HIGH, strcat( " Subnamespace = '", Parser_Token, "' New namespace = '", lNamespace, "'" ) );
457
458         Parser_Expect( PARSER_TT_BRACEOPEN );
459
460         while( Parser_GetToken() ) {
461                 if( Parser_TokenType == PARSER_TT_BRACECLOSE )
462                         break;
463
464                 Parser_ParseDefinition( lNamespace );
465         }
466
467         Parser_Print( PARSER_HIGH, strcat( "Finished parsing Namespace. Namespace = '", pNamespace, "'" ) );
468
469         String_Free( lNamespace );
470 };
471
472 void( string pFilename, string pNamespace ) Parser_ParseFile =
473 {
474         Parser_Filename = String_Zone( pFilename );
475         Parser_File = fopen( Parser_Filename, FILE_READ );
476         if( Parser_File == -1 ) {
477                 print( "Parser: Couldn't open ", Parser_Filename, "\n" );
478                 return;
479         }
480
481         Parser_Line = String_Create();
482         Parser_LineNumber = 0;
483         Parser_NumTokens = Parser_TokenNum = 0;
484
485         while( Parser_GetToken() )
486                 Parser_ParseDefinition( pNamespace );
487
488         fclose( Parser_File );
489         String_Free( Parser_Filename );
490         String_Free( Parser_Line );
491 };
492
493 void() Parser_Init =
494 {
495         Parser_Token = String_Create();
496         Parser_DefineChain = null_entity;
497         Parser_IncludeDepth = 0;
498
499         Parser_FileList = String_Create();
500 };
501
502 void() Parser_Quit =
503 {
504         local entity lNext;
505
506         _Parser_Print( PARSER_HIGH, "Deleting #defines:" );
507         while( Parser_DefineChain ) {
508                 lNext = Parser_DefineChain.chain;
509                 _Parser_Print( PARSER_HIGH, strcat( "  [", Parser_DefineChain.name, "]" ) );
510
511                 String_Free( Parser_DefineChain.name );
512                 String_Free( Parser_DefineChain.value );
513
514                 remove( Parser_DefineChain );
515                 Parser_DefineChain = lNext;
516         }
517
518
519         String_Free( Parser_Token );
520         String_Free( Parser_FileList );
521 };
522
523 void( string pMain ) Parser_ParseMenu =
524 {
525         Parser_Init();
526
527         pMain = String_Zone( pMain );
528         Parser_FileList = Util_AltStringPush( Parser_FileList, pMain );
529
530         Parser_ParseFile( pMain, "" );
531
532         _Parser_Print( PARSER_INFO, strcat( "Files parsed: ", Parser_FileList ) );
533
534         String_Free( pMain );
535         Parser_Quit();
536 };