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

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

IDL file parsing more or less working.

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