source: trunk/idl-compiler/c/nom-idl-compiler.c@ 275

Last change on this file since 275 was 275, checked in by cinc, 18 years ago

Added filestem parsing.

File size: 21.4 KB
Line 
1/* ***** BEGIN LICENSE BLOCK *****
2* Version: CDDL 1.0/LGPL 2.1
3*
4* The contents of this file are subject to the COMMON DEVELOPMENT AND
5* DISTRIBUTION LICENSE (CDDL) Version 1.0 (the "License"); you may not use
6* this file except in compliance with the License. You may obtain a copy of
7* the License at http://www.sun.com/cddl/
8*
9* Software distributed under the License is distributed on an "AS IS" basis,
10* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11* for the specific language governing rights and limitations under the
12* License.
13*
14* The Original Code is "NOM" Netlabs Object Model
15*
16* The Initial Developer of the Original Code is
17* netlabs.org: Chris Wohlgemuth <cinc-ml@netlabs.org>.
18* Portions created by the Initial Developer are Copyright (C) 2007
19* the Initial Developer. All Rights Reserved.
20*
21* Contributor(s):
22*
23* Alternatively, the contents of this file may be used under the terms of
24* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
25* case the provisions of the LGPL are applicable instead of those above. If
26* you wish to allow use of your version of this file only under the terms of
27* the LGPL, and not to allow others to use your version of this file under
28* the terms of the CDDL, indicate your decision by deleting the provisions
29* above and replace them with the notice and other provisions required by the
30* LGPL. If you do not delete the provisions above, a recipient may use your
31* version of this file under the terms of any one of the CDDL or the LGPL.
32*
33* ***** END LICENSE BLOCK ***** */
34#define INCL_DOSPROCESS
35#define INCL_DOS
36#define INCL_DOSPROFILE
37#define INCL_DOSERRORS
38
39#include <os2.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43
44#include <io.h>
45#include <fcntl.h>
46#include <sys\stat.h>
47
48#include <glib.h>
49#include <glib/gprintf.h>
50
51#include "nom.h"
52#include "nomtk.h"
53
54#include "parser.h"
55
56static gchar* chrOutputDir="";
57static gchar* chrOutputName="";
58static gboolean fOptionEmitH=FALSE;
59static gboolean fOptionEmitIH=FALSE;
60static gboolean fOptionEmitC=FALSE;
61
62/* Command line options */
63static GOptionEntry gOptionEntries[] =
64{
65 {"directory", 'd', 0, G_OPTION_ARG_FILENAME, &chrOutputDir, "Output directory", NULL},
66 {"emit-h", 0, 0, G_OPTION_ARG_NONE, &fOptionEmitH, "Emmit a header file (*.h)", NULL},
67 {"emit-ih", 0, 0, G_OPTION_ARG_NONE, &fOptionEmitIH, "Emmit an include header (*.ih)", NULL},
68 {"emit-c", 0, 0, G_OPTION_ARG_NONE, &fOptionEmitC, "Emmit an implementation template (*.c)", NULL},
69 {"output", 'o', 0, G_OPTION_ARG_FILENAME, &chrOutputName, "Output name. Must not be omitted.", NULL},
70 {NULL}
71};
72
73
74/* The pointer array holding the interfaces we found */
75//GPtrArray* pInterfaceArray;
76
77/* Symbols defined for our IDL language.
78 Keep this in synch with the defined enums! */
79SYMBOL idlSymbols[]={
80 {"interface", IDL_SYMBOL_INTERFACE, KIND_UNKNOWN}, /* 71 */
81 {"NOMCLASSVERSION", IDL_SYMBOL_CLSVERSION, KIND_UNKNOWN},
82 {"NOMINSTANCEVAR", IDL_SYMBOL_INSTANCEVAR, KIND_UNKNOWN},
83 {"NOMOVERRIDE", IDL_SYMBOL_OVERRIDE, KIND_UNKNOWN},
84 {"NOMREGISTEREDIFACE", IDL_SYMBOL_REGINTERFACE, KIND_TYPESPEC},
85 {"NOMCLASSNAME", IDL_SYMBOL_CLSNAME, KIND_UNKNOWN},
86 {"NOMMETACLASS", IDL_SYMBOL_OLDMETACLASS, KIND_UNKNOWN},
87 {"metaclass", IDL_SYMBOL_METACLASS, KIND_UNKNOWN},
88 {"filestem", IDL_SYMBOL_FILESTEM, KIND_UNKNOWN},
89 {"native", IDL_SYMBOL_NATIVE, KIND_UNKNOWN},
90 {"gulong", IDL_SYMBOL_GULONG, KIND_TYPESPEC},
91 {"gint", IDL_SYMBOL_GINT, KIND_TYPESPEC},
92 {"gpointer", IDL_SYMBOL_GPOINTER, KIND_TYPESPEC},
93 {"gboolean", IDL_SYMBOL_GBOOLEAN, KIND_TYPESPEC},
94 {"gchar", IDL_SYMBOL_GCHAR, KIND_TYPESPEC},
95 {"void", IDL_SYMBOL_VOID, KIND_TYPESPEC},
96
97 {"boolean", IDL_SYMBOL_BOOLEAN, KIND_TYPESPEC},
98 {"string", IDL_SYMBOL_STRING, KIND_TYPESPEC},
99 {"long", IDL_SYMBOL_LONG, KIND_TYPESPEC},
100 {"unsigned", IDL_SYMBOL_LONG, KIND_TYPESPEC},
101
102 {"in", IDL_SYMBOL_IN, KIND_DIRECTION},
103 {"out", IDL_SYMBOL_OUT, KIND_DIRECTION},
104 {"inout", IDL_SYMBOL_INOUT, KIND_DIRECTION},
105 {"define", IDL_SYMBOL_DEFINE, KIND_UNKNOWN},
106 {"ifdef", IDL_SYMBOL_IFDEF, KIND_UNKNOWN},
107 {"endif", IDL_SYMBOL_ENDIF, KIND_UNKNOWN},
108 {NULL, 0, KIND_UNKNOWN}
109},*pSymbols=idlSymbols;
110
111GScanner *gScanner;
112
113/* Holding the current state of parsing and pointers to necessary lists.
114 Referenced by gScanner-user_data. */
115PARSEINFO parseInfo={0};
116PPARSEINFO pParseInfo=&parseInfo; /* This pointer will go away, don't use */
117
118/**
119 Helper function which scans the array of known interfaces and returns the interface
120 structure for the given name.
121
122 \PARAM chrName Name of the interface.
123 \Returns If no interface with that name can be found NULL is returned otherwise a
124 pointer to the interface structure..
125 */
126PINTERFACE findInterfaceFromName(gchar* chrName)
127{
128 int a;
129
130 for(a=0;a<parseInfo.pInterfaceArray->len;a++)
131 {
132 PINTERFACE pif=g_ptr_array_index(parseInfo.pInterfaceArray, a);
133 if(!strcmp(chrName, pif->chrName))
134 return pif;
135 }
136
137 return NULL;
138}
139
140/**
141 Helper function which returns a copy of the typespec string of the current token.
142 That is e.g. 'gint' or 'gpointer'. Note that this function is only called when the
143 current token is indeed a type specification in the IDL file.
144 */
145gchar* getTypeSpecStringFromCurToken(void)
146{
147 GTokenValue value;
148
149 value=gScanner->value;
150
151 switch(gScanner->token)
152 {
153 case G_TOKEN_IDENTIFIER:
154 return g_strdup(value.v_identifier);
155 break;
156 case G_TOKEN_SYMBOL:
157 {
158 /* It's one of our symbols. */
159 PSYMBOL pCurSymbol;
160
161 pCurSymbol=value.v_symbol;
162 return g_strdup(pCurSymbol->chrSymbolName);
163 break;
164 }
165 default:
166 g_scanner_unexp_token(gScanner,
167 G_TOKEN_SYMBOL,
168 NULL,
169 NULL,
170 NULL,
171 "",
172 TRUE); /* is_error */
173
174 break;
175 }
176 return "unknown";
177}
178
179/**
180 This function is only for removing the NOMCLASSNAME() definition from
181 the input stream. When everything is moved to the new IDL compiler
182 those statements will be removed and this parsing function eventually
183 removed.
184
185 The current token is the NOMCLASSNAME keyword.
186
187 CN:= G_TOKEN_SYMBOL '(' INDENT ')' ';'
188 */
189static void parseClassName(void)
190{
191
192 if(!matchNext('('))
193 {
194 getNextToken(); /* Make sure error references the correct token */
195 g_scanner_unexp_token(gScanner,
196 '(',
197 NULL,
198 NULL,
199 NULL,
200 "Error in NOMCLASSNAME()",
201 TRUE); /* is_error */
202 exit(1);
203 }
204
205 /* Identifier. We discard it. */
206 if(!matchNext(G_TOKEN_IDENTIFIER))
207 {
208 g_scanner_unexp_token(gScanner,
209 G_TOKEN_IDENTIFIER,
210 NULL,
211 NULL,
212 NULL,
213 "Class name is not a valid identifier.",
214 TRUE); /* is_error */
215 exit(1);
216 }
217
218 if(!matchNext(')'))
219 {
220 getNextToken(); /* Make sure error references the correct token */
221 g_scanner_unexp_token(gScanner,
222 ')',
223 NULL,
224 NULL,
225 NULL,
226 "Error in NOMCLASSNAME().",
227 TRUE); /* is_error */
228 exit(1);
229 }
230
231 if(!matchNext(';'))
232 {
233 getNextToken(); /* Make sure error references the correct token */
234 g_scanner_unexp_token(gScanner,
235 ';',
236 NULL,
237 NULL,
238 NULL,
239 "Error in NOMCLASSNAME() definition, Missing semicolon at the end.",
240 TRUE); /* is_error */
241 exit(1);
242 }
243}
244
245/**
246 This function is only for removing the NOMMETACLASS() definition from
247 the input stream. When everything is moved to the new IDL compiler
248 those statements will be removed and this parsing function eventually
249 removed.
250
251 The current token is the NOMMETACLASS keyword.
252
253 CN:= G_TOKEN_SYMBOL '(' INDENT ')' ';'
254 */
255static void parseOldMetaClass(void)
256{
257
258 if(!matchNext('('))
259 {
260 getNextToken(); /* Make sure error references the correct token */
261 g_scanner_unexp_token(gScanner,
262 '(',
263 NULL,
264 NULL,
265 NULL,
266 "Error in NOMMETACLASS()",
267 TRUE); /* is_error */
268 exit(1);
269 }
270
271 /* Identifier. We discard it. */
272 if(!matchNext(G_TOKEN_IDENTIFIER))
273 {
274 g_scanner_unexp_token(gScanner,
275 G_TOKEN_IDENTIFIER,
276 NULL,
277 NULL,
278 NULL,
279 "Class name is not a valid identifier.",
280 TRUE); /* is_error */
281 exit(1);
282 }
283
284 if(!matchNext(')'))
285 {
286 getNextToken(); /* Make sure error references the correct token */
287 g_scanner_unexp_token(gScanner,
288 ')',
289 NULL,
290 NULL,
291 NULL,
292 "Error in NOMMETACLASS().",
293 TRUE); /* is_error */
294 exit(1);
295 }
296
297 if(!matchNext(';'))
298 {
299 getNextToken(); /* Make sure error references the correct token */
300 g_scanner_unexp_token(gScanner,
301 ';',
302 NULL,
303 NULL,
304 NULL,
305 "Error in NOMMETACLASS() definition, Missing semicolon at the end.",
306 TRUE); /* is_error */
307 exit(1);
308 }
309}
310
311/**
312 Parse the declaration of a new type using the 'native' keyword.
313
314 The 'native' keyword is used to introduce new types. That's coming
315 from the Corba spec.
316
317 \Remarks The current token is the 'native' keyword.
318
319 N:= G_TOKEN_SYMBOL IDENT ';'
320 */
321static void parseNative(void)
322{
323 GTokenValue value;
324 PSYMBOL pCurSymbol=g_malloc0(sizeof(SYMBOL));
325
326 if(!matchNext(G_TOKEN_IDENTIFIER))
327 {
328 PSYMBOL pSymbol;
329
330 /* Check if it's a symbol. The following 'identifier' (word) is maybe alread
331 registered as a symbol. */
332 if(!matchNext(G_TOKEN_SYMBOL))
333 {
334 g_scanner_unexp_token(gScanner,
335 G_TOKEN_SYMBOL,
336 NULL,
337 NULL,
338 NULL,
339 "'native' statement is not followed by a valid identifier.",
340 TRUE); /* is_error */
341 exit(1);
342 }
343 /* It's a symbol. Check if it's a typespec. */
344 value=gScanner->value;
345 pSymbol=value.v_symbol;
346 if(!pSymbol || pSymbol->uiKind!=KIND_TYPESPEC)
347 {
348 g_scanner_unexp_token(gScanner,
349 G_TOKEN_SYMBOL,
350 NULL,
351 NULL,
352 NULL,
353 "'native' statement is not followed by a valid symbol.",
354 TRUE); /* is_error */
355 exit(1);
356 }
357 }
358
359 value=gScanner->value;
360 pCurSymbol->chrSymbolName=g_strdup(value.v_identifier);
361 pCurSymbol->uiKind=KIND_TYPESPEC;
362 pCurSymbol->uiSymbolToken=G_TOKEN_NONE;
363 g_tree_insert(parseInfo.pSymbolTree, pCurSymbol, pCurSymbol->chrSymbolName);
364 g_scanner_scope_add_symbol(gScanner, ID_SCOPE, pCurSymbol->chrSymbolName,
365 pCurSymbol);
366
367 if(!matchNext(';'))
368 {
369 getNextToken(); /* Make sure error references the correct token */
370 g_scanner_unexp_token(gScanner,
371 ';',
372 NULL,
373 NULL,
374 NULL,
375 "Error in 'native' definition , Missing semicolon",
376 TRUE); /* is_error */
377 exit(1);
378 }
379
380}
381
382/**
383 This is the root parse function. Here starts the fun. When a token is found in the
384 input stream which matches one of the known token types the respective parsing function
385 is called for further processing. In case of an error the parsing function in question
386 prints an error which describes the problem and exits the application.
387
388 This function scans the input until EOF is hit.
389 */
390void parseIt(void)
391{
392 while(g_scanner_peek_next_token(gScanner) != G_TOKEN_EOF) {
393 GTokenType token;
394
395 g_scanner_get_next_token(gScanner);
396 token=gScanner->token;
397 GTokenValue value=gScanner->value;
398
399 switch(token)
400 {
401 case '#':
402 parseHash();
403 break;
404 case G_TOKEN_SYMBOL:
405 {
406 PSYMBOL pCurSymbol=value.v_symbol;
407 switch(pCurSymbol->uiSymbolToken)
408 {
409 case IDL_SYMBOL_INTERFACE:
410 parseInterface(token);
411 break;
412 case IDL_SYMBOL_NATIVE:
413 parseNative();
414 break;
415 case IDL_SYMBOL_CLSNAME:
416 parseClassName();
417 break;
418 case IDL_SYMBOL_OLDMETACLASS:
419 parseOldMetaClass();
420 break;
421 default:
422 break;
423 }
424 break;
425 }
426#if 0
427 case G_TOKEN_IDENTIFIER:
428 g_message("Token: %d (G_TOKEN_IDENTIFIER)\t\t%s", token, value.v_identifier);
429 break;
430 case G_TOKEN_STRING:
431 g_message("Token: %d (G_TOKEN_STRING)\t\t\t%s", token, value.v_string);
432 break;
433 case G_TOKEN_LEFT_PAREN:
434 g_message("Token: %d (G_TOKEN_LEFT_PAREN)\t\t(", token);
435 break;
436 case G_TOKEN_RIGHT_PAREN:
437 g_message("Token: %d (G_TOKEN_RIGHT_PAREN)\t\t)", token);
438 break;
439 case ':':
440 g_message("Token: %d (colon)\t\t:", token);
441 break;
442 case ';':
443 g_message("Token: %d (semicolon)\t\t\t;", token);
444 break;
445 case '/':
446 g_message("Token: %d (slash)\t\t\t/ %s", token, value.v_comment);
447 break;
448 case G_TOKEN_COMMA:
449 g_message("Token: %d (G_TOKEN_COMMA)\t\t\t,", token);
450 break;
451 case G_TOKEN_INT:
452 g_message("Token: %d (G_TOKEN_INT)\t\t\t%ld", token, value.v_int);
453 break;
454 case IDL_SYMBOL_DEFINE:
455 g_message("Token: %d (IDL_SYMBOL_DEFINE)\t\t\t", token);
456 break;
457 case IDL_SYMBOL_IFDEF:
458 g_message("Token: %d (IDL_SYMBOL_IFDEF)\t\t\t", token);
459 break;
460 case IDL_SYMBOL_ENDIF:
461 g_message("Token: %d (IDL_SYMBOL_ENDIF)\t\t\t", token);
462 break;
463#endif
464 default:
465 printToken(token);
466 break;
467 }
468 }
469}
470
471/**
472 Support function to show help for the IDL compiler. gContext must be valid.
473*/
474static void outputCompilerHelp(GOptionContext *gContext, gchar* chrExeName)
475{
476 GError *gError = NULL;
477 int argc2=2;
478 char *helpCmd[]={"","--help"};
479 char** argv2=helpCmd;
480 helpCmd[0]=chrExeName;
481
482 g_printf("An output filename must always be specified. If the name is an absolute path\n\
483it will be used unmodified. Otherwise the output name is built from the given\n\
484name and the directory specification.\n\n\
485-If no directory is specified the output name is built from the current directory\n\
486 path and the given filename.\n\
487-If the directory is a relative path the output name is built from the current\n\
488 directory path, the given directory name (or path) and the filename.\n\
489-If the directory is a full path the output name is built from the directory\n\
490 path and the given filename.\n\n\
491Note that an emitter specific extension will always be appended to the output\n\
492filename\n\n");
493
494 /* This prints the standard option help to screen. */
495 g_option_context_parse (gContext, &argc2, &argv2, &gError);
496}
497
498/*
499 Compare function for the tree holding our private symbols.
500 */
501static gint funcSymbolCompare(gconstpointer a, gconstpointer b)
502{
503 if(a==b)
504 return 0;
505
506 if(a<b)
507 return -1;
508
509 return 1;
510};
511
512/**
513 Message output handler for the scanner. The default handler isn't used because the preprocessor
514 mangles all include files together and thus the line numbers are not as expected by the user.
515 This function prints the error messages with corrected linenumbers and the source file name
516 in which to find the problem.
517
518 */
519void funcMsgHandler(GScanner *gScanner, gchar *message, gboolean error)
520{
521 g_printf("%s:%d: error: %s (%d %d)\n", parseInfo.chrCurrentSourceFile,
522 g_scanner_cur_line(gScanner)-parseInfo.uiLineCorrection, message,
523 g_scanner_cur_line(gScanner), parseInfo.uiLineCorrection);
524}
525
526/**
527 Main entry point for the idl compiler.
528 */
529int main(int argc, char **argv)
530{
531 int a;
532 int fd;
533 /* Vars for filename building */
534 char* chrOutputFileName="";
535 char* chrTemp;
536
537 GError *gError = NULL;
538 GOptionContext* gContext;
539
540 /* Parse command line options */
541 gContext = g_option_context_new ("file");
542 g_option_context_add_main_entries (gContext, gOptionEntries, NULL);
543
544 if(!g_option_context_parse (gContext, &argc, &argv, &gError))
545 {
546 outputCompilerHelp(gContext, argv[0]);
547 }
548
549 /* Correct emitter options? Exactly one emitter must be specified. */
550 a=0;
551 if(fOptionEmitH)
552 a++;
553 if(fOptionEmitIH)
554 a++;
555 if(fOptionEmitC)
556 a++;
557
558 if(!a){
559 g_printf("An emitter must be specified.\n\n");
560 outputCompilerHelp(gContext, argv[0]);
561 }
562 if(a>1){
563 g_printf("Only one emitter must be specified.\n\n");
564 outputCompilerHelp(gContext, argv[0]);
565 }
566
567 if(strlen(chrOutputName)==0)
568 {
569 g_printf("No output file name given.\n\n");
570 outputCompilerHelp(gContext, argv[0]);
571 }
572 g_option_context_free(gContext);
573
574
575 if(argc<2)
576 {
577 g_printf("No input file name given.\n\nUse %s --help for options.\n\n", argv[0]);
578 return 1;
579 }
580
581 for(a=0; a<argc; a++)
582 {
583 g_message("arg %d: %s", a, argv[a]);
584 }
585
586
587 /*** Create output file name ****/
588 if(!g_path_is_absolute(chrOutputName))
589 {
590 if(g_path_is_absolute(chrOutputDir))
591 chrOutputFileName=g_build_filename(chrOutputDir, chrOutputName, NULL);
592 else
593 {
594 /* Yes this is a memory leak but I don't care */
595 chrOutputFileName=g_build_filename(g_get_current_dir(), chrOutputDir, chrOutputName, NULL);
596 }
597 }
598 else
599 chrOutputFileName=chrOutputName;
600
601 /* Add emitter extension */
602 if(fOptionEmitH)
603 chrTemp=g_strconcat(chrOutputFileName, ".h", NULL);
604 else if(fOptionEmitIH)
605 chrTemp=g_strconcat(chrOutputFileName, ".ih", NULL);
606 else if(fOptionEmitC)
607 chrTemp=g_strconcat(chrOutputFileName, ".c", NULL);
608 g_free(chrOutputFileName);
609 chrOutputFileName=chrTemp;
610
611 g_message("Output file: %s", chrOutputFileName);
612
613 /* Open input */
614 if(!strcmp(argv[1], "-"))
615 fd=0; /* Read from stdin */
616 else
617 fd=open(argv[1], O_RDONLY);
618
619 if(-1==fd)
620 {
621 g_message("Can't open input file %s", argv[1]);
622 exit(1);
623 }
624
625 /* Open output */
626 parseInfo.outFile=fopen(chrOutputFileName, "w");
627
628 g_printf("\n");
629
630 gScanner=g_scanner_new(NULL);
631 gScanner->user_data=(gpointer)&parseInfo;
632
633 gScanner->msg_handler=funcMsgHandler;
634 parseInfo.pInterfaceArray=g_ptr_array_new();
635
636 g_scanner_input_file(gScanner, fd);
637 gScanner->config->case_sensitive=TRUE;
638 /* No single line comments */
639 gScanner->config->skip_comment_single=FALSE;
640 gScanner->config->cpair_comment_single="";
641 /* This string is used in error messages of the parser */
642 gScanner->input_name=IDL_COMPILER_STRING;
643
644 g_scanner_set_scope(gScanner, ID_SCOPE);
645 /* Load our own symbols into the scanner. We use the default scope for now. */
646 parseInfo.pSymbolTree=g_tree_new((GCompareFunc) funcSymbolCompare);
647 while(pSymbols->chrSymbolName)
648 {
649#warning !!! Create a copy here so it is the same as with new symbols added later.
650 g_scanner_scope_add_symbol(gScanner, ID_SCOPE, pSymbols->chrSymbolName,
651 pSymbols);
652 g_tree_insert(parseInfo.pSymbolTree, pSymbols, pSymbols->chrSymbolName);
653 pSymbols++;
654 }
655
656 parseIt();
657
658 /* Write the output file */
659 if(fOptionEmitH)
660 emitHFile(parseInfo.pInterfaceArray);
661
662#if 0
663 else if(fOptionEmitIH)
664
665 else if(fOptionEmitC)
666 a++;
667
668 if(pInterfaceArray->len)
669 printInterface();
670#endif
671
672 g_scanner_destroy(gScanner);
673 close(fd);
674 fclose(parseInfo.outFile);
675 return 0;
676}
677
678#if 0
679 /* We are a folder somwhere in the chain */
680 nomRetval=WPFileSystem_wpQueryFileName((WPFileSystem*)wpParent, bFullPath, NULLHANDLE);
681
682 nomRetval=wpParent.wpQueryFileName(bFullPath, NULLHANDLE);
683
684 nomPath=NOMPathNew();
685 nomPath= (PNOMPath) NOMPath_assignCString(nomPath, _pszFullPath, ev);
686
687 return (PNOMPath)NOMPath_appendPath(nomRetval, nomPath, NULLHANDLE);
688
689#endif
690
691
692
693
694
695
696
697
698
Note: See TracBrowser for help on using the repository browser.