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

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

Work on IH file emitter.

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