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

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

Code cleanups. Started H file emitter.

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