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

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

Code cleanups.

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