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

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

Output file creation and better option parsing

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