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

Last change on this file was 326, checked in by cinc, 17 years ago

Portability patches for Windows, Linux, Darwin by Bird.

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