]> icculus.org git repositories - divverent/nexuiz.git/blob - data/menuqc/system/parser.qc
rename menu directories
[divverent/nexuiz.git] / data / menuqc / 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         lDefine = spawn();
163         lDefine.type = "Parser_Define";
164         lDefine.name = String_Zone( Parser_Token );
165
166         //Parser_Expect( PARSER_TT_BRACKETCLOSE );
167         // read the rest of the line
168         String_EntityZone( lDefine, value );
169         lOldLine = Parser_LineNumber;
170         while( 1 )
171                 if( !Parser_GetToken() )
172                         break;
173                 else if( lOldLine != Parser_LineNumber ) {
174                         --Parser_TokenNum;
175                         break;
176                 } else if( Parser_Token == "\\" )
177                         ++lOldLine;
178                 else
179                         String_EntitySet( lDefine, value, strcat( lDefine.value, "\"", Parser_Token, "\" " ) );
180
181         Parser_Print( PARSER_HIGH, strcat( " Name = '", lDefine.name, "' Replacement = '", lDefine.value, "'" ) );
182
183         lDefine.chain = Parser_DefineChain;
184         Parser_DefineChain = lDefine;
185
186         Parser_Print( PARSER_HIGH, "Done parsing #define" );
187 };
188
189 void() Parser_ParseUndef =
190 {
191         local entity lEntity, lPrevious;
192
193         // #undef Name
194         Parser_Print( PARSER_HIGH, "Parsing #undef..." );
195         Parser_Expect( PARSER_TT_TOKEN );
196
197         lPrevious = null_entity;
198         for( lEntity = Parser_DefineChain ; lEntity ; lPrevious = lEntity, lEntity = lEntity.chain )
199                 if( lEntity.name == Parser_Token ) {
200                         if( lPrevious )
201                                 lPrevious.chain = lEntity.chain;
202                         else
203                                 Parser_DefineChain = lEntity.chain;
204
205                         Parser_Print( PARSER_INFO, strcat( "#undef: Removed [", Parser_Token, "]" ) );
206
207                         String_Free( lEntity.name );
208                         String_Free( lEntity.value );
209
210                         remove( lEntity );
211
212                         return;
213                 }
214
215         Parser_Print( PARSER_INFO, strcat( "#undef: [", Parser_Token, "] not found!" ) );
216 };
217
218 void() Parser_ProcessDefine =
219 {
220         local string lConstant;
221         local entity lDefine;
222
223         // [Name]
224         Parser_Expect( PARSER_TT_TOKEN );
225         lConstant = String_Zone( Parser_Token );
226
227         Parser_Expect( PARSER_TT_BRACKETCLOSE );
228
229         Parser_Print( PARSER_HIGH, strcat( "Processing [", lConstant, "]..." ) );
230
231         for( lDefine = Parser_DefineChain ; lDefine ; lDefine = lDefine.chain )
232                 if( lDefine.name == lConstant ) {
233                         // if you want to have a single token use \" or '
234                         Parser_Line = String_Set( Parser_Line, strcat( " ", lDefine.value ) );
235                         Parser_Print( PARSER_HIGH, strcat( "Replacing with '", Parser_Line, "'" ) );
236                         for( ; Parser_TokenNum < Parser_NumTokens ; Parser_TokenNum++ )
237                                 Parser_Line = String_Set( Parser_Line, strcat( Parser_Line, " \"", argv( Parser_TokenNum ), "\"" ) );
238                         Parser_TokenizeLine();
239                         String_Free( lConstant );
240                         return;
241                 }
242
243         Parser_Token = String_Set( Parser_Token, String_Normal( lConstant ) );
244         Parser_Print( PARSER_NORMAL, strcat( "#define: Couldn't find constant '", Parser_Token, "'!" ) );
245         Parser_Error( "constant not found" );
246 };
247
248 // Item [Template]
249 // Template
250 void( string pNamespace ) Parser_ParseDefinition =
251 {
252         local entity lEntity;
253
254         if( Parser_TokenType == PARSER_TT_ITEM )
255                 Parser_ParseItem( pNamespace );
256         else if( Parser_TokenType == PARSER_TT_TEMPLATE ) {
257                 lEntity = Parser_ParseItem( pNamespace );
258                 lEntity.flag = lEntity.flag | FLAG_TEMPLATE;
259         } else if( Parser_TokenType == PARSER_TT_DEFINE )
260                 Parser_ParseDefine();
261         else if( Parser_TokenType == PARSER_TT_DERIVE )
262                 Parser_DeriveItem( pNamespace );
263         else if( Parser_TokenType == PARSER_TT_DERIVETEMPLATE ) {
264                 lEntity = Parser_DeriveItem( pNamespace );
265                 lEntity.flag = lEntity.flag | FLAG_TEMPLATE;
266         } else if( Parser_TokenType == PARSER_TT_UNDEF )
267                 Parser_ParseUndef();
268         else if( Parser_TokenType == PARSER_TT_NAMESPACE )
269                 Parser_ParseNamespace( pNamespace );
270         else if( Parser_TokenType == PARSER_TT_INCLUDE )
271                 Parser_IncludeFile( pNamespace );
272         else if( Parser_TokenType == PARSER_TT_IGNORE )
273                 Parser_ParseIgnore();
274         else
275                 Parser_Error( "couldn't find type in Parser_ParseDef" );
276 };
277
278 void() Parser_ParseIgnore =
279 {
280         local float lBraceCount;
281
282         Parser_Expect( PARSER_TT_BRACEOPEN );
283         for( lBraceCount = 1 ; lBraceCount > 0 ; )
284                 if( !Parser_GetToken() )
285                         break;
286                 else if( Parser_TokenType == PARSER_TT_BRACEOPEN )
287                         ++lBraceCount;
288                 else if( Parser_TokenType == PARSER_TT_BRACECLOSE )
289                         --lBraceCount;
290 };
291
292 entity( string pNamespace ) Parser_ParseItem =
293 {
294         local string lNamespace;
295         local string lEntityText;
296         local entity lEntity;
297
298         Parser_Print( PARSER_HIGH, "Parsing item.." );
299
300         // get the item type
301         Parser_Expect( PARSER_TT_TOKEN );
302         lEntityText = String_Zone( strcat( "{ \"type\" \"Item_", Parser_Token,"\" " ) );
303
304         Parser_Print( PARSER_HIGH, strcat( " Type = '", Parser_Token, "'" ) );
305
306         // get the item name
307         Parser_Expect( PARSER_TT_TOKEN );
308         if( pNamespace != "" )
309                 lNamespace = String_Zone( strcat( pNamespace, "::", Parser_Token ) );
310         else
311                 lNamespace = String_Zone( Parser_Token );
312
313         lEntityText = String_Set( lEntityText, strcat( lEntityText, "\"name\" \"", lNamespace, "\" " ) );
314         lEntityText = String_Set( lEntityText, strcat( lEntityText, "\"parent\" \"", pNamespace, "\" " ) );
315
316         Parser_Print( PARSER_HIGH, strcat( " Name = '", Parser_Token, "' Parent = '", pNamespace,
317                                 "' Namespace = '", lNamespace, "'" ) );
318
319         Parser_Expect( PARSER_TT_BRACEOPEN );
320         lEntityText = Parser_ParseEntity( lNamespace, lEntityText );
321
322         // parse the entity (builtin)
323         lEntity = spawn();
324         parseentitydata( lEntity, lEntityText );
325
326         String_Free( lEntityText );
327         String_Free( lNamespace );
328
329         Parser_Print( PARSER_HIGH, strcat( "Parsing '", lEntity.name, "' finished" ) );
330
331         return lEntity;
332 };
333
334 void( entity pSource, entity pTarget ) Parser_CloneChildren =
335 {
336         // we search for all items that are direct children of pSource and copy them
337         // and adapt their parent and names
338         local entity lNode;
339
340         lNode = null_entity;
341         while ( (lNode = findstring( lNode, parent, pSource.name )) != null_entity ) {
342                 local entity lClone;
343                 local string lModifierString;
344
345                 lClone = spawn();
346                 copyentity( lNode, lClone );
347
348                 if( lClone.flag & FLAG_TEMPLATE )
349                         lClone.flag = lClone.flag - FLAG_TEMPLATE;
350
351                 lModifierString = strcat( "{ name \"", pTarget.name,
352                         substring( lNode.name, strlen( pSource.name ), 100000 ), "\" parent \"", pTarget.name, "\" }" );
353
354                 parseentitydata( lClone, lModifierString );
355
356                 Parser_CloneChildren( lNode, lClone );
357         }
358 };
359
360 entity( string pNamespace ) Parser_DeriveItem =
361 {
362         local string lNamespace;
363         local string lEntityText;
364         local entity lBase;
365         local string lBaseName;
366         local entity lEntity;
367
368         Parser_Print( PARSER_HIGH, "Parsing derived item.." );
369
370         // get the base item
371         Parser_Expect( PARSER_TT_TOKEN );
372
373         if( substring( Parser_Token, 0, 2 ) == "::" )
374                 lBaseName = String_Zone( substring( Parser_Token, 2, strlen( Parser_Token ) - 2 ) );
375         else if( pNamespace == "" )
376                 lBaseName = String_Zone( Parser_Token );
377         else {
378                 lBaseName = String_Zone( strcat( pNamespace, "::", Parser_Token ) );
379
380                 // try the local namespace first, then try to find it in the global
381                 if( findstring( null_entity,  name, lBaseName ) == null_entity )
382                         lBaseName = String_Set( lBaseName, Parser_Token );
383         }
384
385         Parser_Print( PARSER_HIGH, strcat( " Base = '", lBaseName, "'" ) );
386
387         lBase = findstring( null_entity, name, lBaseName );
388         if( lBase == null_entity )
389                 Parser_Error( "couldnt find item" );
390
391         // get the item name
392         Parser_Expect( PARSER_TT_TOKEN );
393         if( pNamespace != "" )
394                 lNamespace = String_Zone( strcat( pNamespace, "::", Parser_Token ) );
395         else
396                 lNamespace = String_Zone( Parser_Token );
397
398         lEntityText = String_Zone( strcat( "{ \"name\" \"", lNamespace, "\" " ) );
399         lEntityText = String_Set( lEntityText, strcat( lEntityText, "\"parent\" \"", pNamespace, "\" " ) );
400
401         Parser_Print( PARSER_HIGH, strcat( " Name = '", Parser_Token, "' Parent = '", pNamespace,
402                                 "' Namespace = '", lNamespace, "'" ) );
403
404         Parser_Expect( PARSER_TT_BRACEOPEN );
405         lEntityText = Parser_ParseEntity( lNamespace, lEntityText );
406
407         // parse the entity (builtin)
408         lEntity = spawn();
409         copyentity( lBase, lEntity );
410         if( lEntity.flag & FLAG_TEMPLATE )
411                 lEntity.flag = lEntity.flag - FLAG_TEMPLATE;
412         parseentitydata( lEntity, lEntityText );
413
414         String_Free( lEntityText );
415         String_Free( lNamespace );
416         String_Free( lBaseName );
417
418         // now copy over all children
419         Parser_CloneChildren( lBase, lEntity );
420
421         Parser_Print( PARSER_HIGH, strcat( "Parsing '", lEntity.name, "' finished" ) );
422
423         return lEntity;
424 };
425
426 string( string pNamespace, string pEntityText ) Parser_ParseEntity =
427 {
428         while( Parser_GetToken() ) {
429                 if( Parser_TokenType == PARSER_TT_TOKEN ) {
430                         // must be a property...
431                         // store the key value
432                         pEntityText = String_Set( pEntityText, strcat( pEntityText, "\"", Parser_Token, "\" " ) );
433                         Parser_Expect( PARSER_TT_TOKEN );
434                         pEntityText = String_Set( pEntityText, strcat( pEntityText, "\"", Parser_Token, "\" " ) );
435                 } else if( Parser_TokenType == PARSER_TT_BRACECLOSE )
436                         break;
437                 else
438                         Parser_ParseDefinition( pNamespace );
439         }
440
441         return String_Append( pEntityText, " }" );
442 };
443
444 void( string pNamespace ) Parser_ParseNamespace =
445 {
446         local string lNamespace;
447
448         Parser_Print( PARSER_HIGH, "Parsing Namespace..." );
449         // namespace Name {
450         Parser_Expect( PARSER_TT_TOKEN );
451         if( pNamespace != "" )
452                 lNamespace = String_Zone( strcat( pNamespace, "::", Parser_Token ) );
453         else
454                 lNamespace = String_Zone( Parser_Token );
455         Parser_Print( PARSER_HIGH, strcat( " Subnamespace = '", Parser_Token, "' New namespace = '", lNamespace, "'" ) );
456
457         Parser_Expect( PARSER_TT_BRACEOPEN );
458
459         while( Parser_GetToken() ) {
460                 if( Parser_TokenType == PARSER_TT_BRACECLOSE )
461                         break;
462
463                 Parser_ParseDefinition( lNamespace );
464         }
465
466         Parser_Print( PARSER_HIGH, strcat( "Finished parsing Namespace. Namespace = '", pNamespace, "'" ) );
467
468         String_Free( lNamespace );
469 };
470
471 void( string pFilename, string pNamespace ) Parser_ParseFile =
472 {
473         Parser_Filename = String_Zone( pFilename );
474         Parser_File = fopen( Parser_Filename, FILE_READ );
475         if( Parser_File == -1 ) {
476                 print( "Parser: Couldn't open ", Parser_Filename, "\n" );
477                 return;
478         }
479
480         Parser_Line = String_Create();
481         Parser_LineNumber = 0;
482         Parser_NumTokens = Parser_TokenNum = 0;
483
484         while( Parser_GetToken() )
485                 Parser_ParseDefinition( pNamespace );
486
487         fclose( Parser_File );
488         String_Free( Parser_Filename );
489         String_Free( Parser_Line );
490 };
491
492 void() Parser_Init =
493 {
494         Parser_Token = String_Create();
495         Parser_DefineChain = null_entity;
496         Parser_IncludeDepth = 0;
497
498         Parser_FileList = String_Create();
499 };
500
501 void() Parser_Quit =
502 {
503         local entity lNext;
504
505         _Parser_Print( PARSER_HIGH, "Deleting #defines:" );
506         while( Parser_DefineChain ) {
507                 lNext = Parser_DefineChain.chain;
508                 _Parser_Print( PARSER_HIGH, strcat( "  [", Parser_DefineChain.name, "]" ) );
509
510                 String_Free( Parser_DefineChain.name );
511                 String_Free( Parser_DefineChain.value );
512
513                 remove( Parser_DefineChain );
514                 Parser_DefineChain = lNext;
515         }
516
517
518         String_Free( Parser_Token );
519         String_Free( Parser_FileList );
520 };
521
522 void( string pPluginDir ) Parser_ParsePlugins =
523 {
524         local float lSearchHandle;
525         local float lCounter, lCount;
526
527         lSearchHandle = search_begin( strcat( pPluginDir, "/*.plugin" ), true, true );
528         if( lSearchHandle < 0 ) {
529                 return;
530         }
531
532         lCount = search_getsize( lSearchHandle );
533
534         for( lCounter = 0 ; lCounter < lCount ; lCounter++ ) {
535                 local string pPlugin;
536
537                 Parser_FileList = String_Set( Parser_FileList, "" );
538
539                 pPlugin = String_Zone( search_getfilename( lSearchHandle, lCounter ) );
540                 Parser_FileList = Util_AltStringPush( Parser_FileList, pPlugin );
541
542                 Parser_ParseFile( pPlugin, "" );
543
544                 _Parser_Print( PARSER_INFO, strcat( "Plugin '", pPlugin, "': files parsed: ", Parser_FileList ) );
545                 String_Free( pPlugin );
546         }
547
548         search_end( lSearchHandle );
549 };
550
551 void( string pMain, string pPluginDir ) Parser_ParseMenu =
552 {
553         Parser_Init();
554
555         pMain = String_Zone( pMain );
556         Parser_FileList = Util_AltStringPush( Parser_FileList, pMain );
557
558         Parser_ParseFile( pMain, "" );
559
560         _Parser_Print( PARSER_INFO, strcat( "Files parsed: ", Parser_FileList ) );
561         String_Free( pMain );
562
563         // plugins
564         if( pPluginDir != "" ) {
565                 Parser_ParsePlugins( pPluginDir );
566         }
567
568         Parser_Quit();
569 };