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

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

Allow 'foo:override;' for method overriding

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