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

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

IDL compiler: improved outfile handling.

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