1/*
2 * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * - Redistribution of source code must retain the above copyright
9 *   notice, this list of conditions and the following disclaimer.
10 *
11 * - Redistribution in binary form must reproduce the above copyright
12 *   notice, this list of conditions and the following disclaimer in the
13 *   documentation and/or other materials provided with the distribution.
14 *
15 * Neither the name of Sun Microsystems, Inc. or the names of
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * This software is provided "AS IS," without a warranty of any kind. ALL
20 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
21 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
22 * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN
23 * MIDROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR
24 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
25 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR
26 * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR
27 * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE
28 * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY,
29 * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
30 * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
31 *
32 * You acknowledge that this software is not designed or intended for use
33 * in the design, construction, operation or maintenance of any nuclear
34 * facility.
35 *
36 * Sun gratefully acknowledges that this software was originally authored
37 * and developed by Kenneth Bradley Russell and Christopher John Kline.
38 */
39
40header {
41        package com.sun.gluegen.cgram;
42
43        import java.io.*;
44        import java.util.*;
45
46        import antlr.CommonAST;
47        import com.sun.gluegen.cgram.types.*;
48}
49
50class HeaderParser extends GnuCTreeParser;
51options {
52        k = 1;
53}
54
55{
56    /** Name assigned to a anonymous EnumType (e.g., "enum { ... }"). */
57    public static final String ANONYMOUS_ENUM_NAME = "<anonymous>";
58
59    /** Set the dictionary mapping typedef names to types for this
60        HeaderParser. Must be done before parsing. */
61    public void setTypedefDictionary(TypeDictionary dict) {
62        this.typedefDictionary = dict;
63    }
64
65    /** Returns the typedef dictionary this HeaderParser uses. */
66    public TypeDictionary getTypedefDictionary() {
67        return typedefDictionary;
68    }
69
70    /** Set the dictionary mapping struct names (i.e., the "foo" in
71        "struct foo { ... };") to types for this HeaderParser. Must be done
72        before parsing. */
73    public void setStructDictionary(TypeDictionary dict) {
74        this.structDictionary = dict;
75    }
76
77    /** Returns the struct name dictionary this HeaderParser uses. */
78    public TypeDictionary getStructDictionary() {
79        return structDictionary;
80    }
81
82    /** Get the canonicalization map, which is a regular HashMap
83        mapping Type to Type and which is used for looking up the unique
84        instances of e.g. pointer-to-structure types that have been typedefed
85        and therefore have names. */
86    public Map getCanonMap() {
87        return canonMap;
88    }
89
90    /** Pre-define the list of EnumTypes for this HeaderParser. Must be
91		done before parsing. */
92    public void setEnums(List/*<EnumType>*/ enumTypes) {
93        // FIXME: Need to take the input set of EnumTypes, extract all
94        // the enumerates from each EnumType, and fill in the enumHash
95        // so that each enumerate maps to the enumType to which it
96        // belongs.
97		throw new RuntimeException("setEnums is Unimplemented!");
98    }
99
100    /** Returns the EnumTypes this HeaderParser processed. */
101    public List/*<EnumType>*/ getEnums() {
102        return new ArrayList(enumHash.values());
103    }
104
105    /** Clears the list of functions this HeaderParser has parsed.
106        Useful when reusing the same HeaderParser for more than one
107        header file. */
108    public void clearParsedFunctions() {
109        functions.clear();
110    }
111
112    /** Returns the list of FunctionSymbols this HeaderParser has parsed. */
113    public List getParsedFunctions() {
114        return functions;
115    }
116
117    private CompoundType lookupInStructDictionary(String typeName,
118                                                  CompoundTypeKind kind,
119                                                  int cvAttrs) {
120        CompoundType t = (CompoundType) structDictionary.get(typeName);
121        if (t == null) {
122            t = new CompoundType(null, null, kind, cvAttrs);
123            t.setStructName(typeName);
124            structDictionary.put(typeName, t);
125        }
126        return t;
127    }
128
129    private Type lookupInTypedefDictionary(String typeName) {
130        Type t = typedefDictionary.get(typeName);
131        if (t == null) {
132            throw new RuntimeException("Undefined reference to typedef name " + typeName);
133        }
134        return t;
135    }
136
137    static class ParameterDeclaration {
138        private String id;
139        private Type   type;
140
141        ParameterDeclaration(String id, Type type) {
142            this.id = id;
143            this.type = type;
144        }
145        String id()             { return id; }
146        Type   type()           { return type; }
147    }
148
149    // A box for a Type. Allows type to be passed down to be modified by recursive rules.
150    static class TypeBox {
151        private Type origType;
152        private Type type;
153        private boolean isTypedef;
154
155        TypeBox(Type type) {
156            this(type, false);
157        }
158
159        TypeBox(Type type, boolean isTypedef) {
160            this.origType = type;
161            this.isTypedef = isTypedef;
162        }
163
164        Type type() {
165            if (type == null) {
166                return origType;
167            }
168            return type;
169        }
170        void setType(Type type) {
171            this.type = type;
172        }
173        void reset() {
174            type = null;
175        }
176
177        boolean isTypedef()     { return isTypedef; }
178
179	    // for easier debugging
180	    public String toString() {
181	       String tStr = "Type=NULL_REF";
182	       if (type == origType) {
183			 tStr = "Type=ORIG_TYPE";
184	  	   } else if (type != null) {
185		     tStr = "Type: name=\"" + type.getCVAttributesString() + " " +
186                    type.getName() + "\"; signature=\"" + type + "\"; class " +
187					type.getClass().getName();
188	       }
189	       String oStr = "OrigType=NULL_REF";
190	       if (origType != null) {
191		     oStr = "OrigType: name=\"" + origType.getCVAttributesString() + " " +
192             origType.getName() + "\"; signature=\"" + origType + "\"; class " +
193			origType.getClass().getName();
194	       }
195	       return "<["+tStr + "] [" + oStr + "] " + " isTypedef=" + isTypedef+">";
196	    }
197    }
198
199    private boolean doDeclaration;   // Used to only process function typedefs
200    private String  declId;
201    private List    parameters;
202    private TypeDictionary typedefDictionary;
203    private TypeDictionary structDictionary;
204    private List/*<FunctionSymbol>*/ functions = new ArrayList();
205    // hash from name of an enumerated value to the EnumType to which it belongs
206    private HashMap/*<String,EnumType>*/ enumHash = new HashMap();
207
208    // Storage class specifiers
209    private static final int AUTO     = 1 << 0;
210    private static final int REGISTER = 1 << 1;
211    private static final int TYPEDEF  = 1 << 2;
212    // Function storage class specifiers
213    private static final int EXTERN   = 1 << 3;
214    private static final int STATIC   = 1 << 4;
215    private static final int INLINE   = 1 << 5;
216    // Type qualifiers
217    private static final int CONST    = 1 << 6;
218    private static final int VOLATILE = 1 << 7;
219    private static final int SIGNED   = 1 << 8;
220    private static final int UNSIGNED = 1 << 9;
221
222    private void initDeclaration() {
223        doDeclaration = false;
224        declId = null;
225    }
226
227    private void doDeclaration() {
228        doDeclaration = true;
229    }
230
231    private void processDeclaration(Type returnType) {
232        if (doDeclaration) {
233            FunctionSymbol sym = new FunctionSymbol(declId, new FunctionType(null, null, returnType, 0));
234	        if (parameters != null) { // handle funcs w/ empty parameter lists (e.g., "foo()")
235                for (Iterator iter = parameters.iterator(); iter.hasNext(); ) {
236                    ParameterDeclaration pd = (ParameterDeclaration) iter.next();
237                    sym.addArgument(pd.type(), pd.id());
238                }
239	        }
240            functions.add(sym);
241        }
242    }
243
244    private int attrs2CVAttrs(int attrs) {
245        int cvAttrs = 0;
246        if ((attrs & CONST) != 0) {
247            cvAttrs |= CVAttributes.CONST;
248        }
249        if ((attrs & VOLATILE) != 0) {
250            cvAttrs |= CVAttributes.VOLATILE;
251        }
252        return cvAttrs;
253    }
254
255    /** Helper routine which handles creating a pointer or array type
256        for [] expressions */
257    private void handleArrayExpr(TypeBox tb, AST t) {
258        if (t != null) {
259            try {
260                // FIXME: this doesn't take into account struct alignment, which may be necessary
261                // See also FIXMEs in ArrayType.java
262                int len = parseIntConstExpr(t);
263                tb.setType(canonicalize(new ArrayType(tb.type(), SizeThunk.mul(SizeThunk.constant(len), tb.type().getSize()), len, 0)));
264                return;
265            } catch (RecognitionException e) {
266                // Fall through
267            }
268        }
269        tb.setType(canonicalize(new PointerType(SizeThunk.POINTER,
270                                                tb.type(),
271                                                0)));
272    }
273
274    private int parseIntConstExpr(AST t) throws RecognitionException {
275        return intConstExpr(t);
276    }
277
278  /** Utility function: creates a new EnumType with the given name, or
279	  returns an existing one if it has already been created. */
280  private EnumType getEnumType(String enumTypeName) {
281	EnumType enumType = null;
282	Iterator it = enumHash.values().iterator();
283	while (it.hasNext()) {
284	  EnumType potentialMatch = (EnumType)it.next();
285	  if (potentialMatch.getName().equals(enumTypeName)) {
286		enumType = potentialMatch;
287		break;
288	  }
289	}
290
291	if (enumType == null) {
292      // This isn't quite correct. In theory the enum should expand to
293      // the size of the largest element, so if there were a long long
294      // entry the enum should expand to e.g. int64. However, using
295      // "long" here (which is what used to be the case) was
296      // definitely incorrect and caused problems.
297	  enumType = new EnumType(enumTypeName, SizeThunk.INT);
298	}
299
300	return enumType;
301  }
302
303  // Map used to canonicalize types. For example, we may typedef
304  // struct foo { ... } *pfoo; subsequent references to struct foo* should
305  // point to the same PointerType object that had its name set to "pfoo".
306  private Map canonMap = new HashMap();
307  private Type canonicalize(Type t) {
308    Type res = (Type) canonMap.get(t);
309    if (res != null) {
310      return res;
311    }
312    canonMap.put(t, t);
313    return t;
314  }
315}
316
317declarator[TypeBox tb] returns [String s] {
318    initDeclaration();
319    s = null;
320    List params = null;
321    String funcPointerName = null;
322    TypeBox dummyTypeBox = null;
323}
324        :   #( NDeclarator
325                ( pointerGroup[tb] )?
326
327                ( id:ID  { s = id.getText(); }
328                | LPAREN funcPointerName = declarator[dummyTypeBox] RPAREN
329                )
330
331                (   #( NParameterTypeList
332                      (
333                        params = parameterTypeList
334                        | (idList)?
335                      )
336                      RPAREN
337                    )  {
338                           if (id != null) {
339                               declId = id.getText();
340                               parameters = params; // FIXME: Ken, why are we setting this class member here?
341                               doDeclaration();
342                           } else if ( funcPointerName != null ) {
343                               /* TypeBox becomes function pointer in this case */
344                               FunctionType ft = new FunctionType(null, null, tb.type(), 0);
345                               if (params == null) {
346			  	                   // If the function pointer has no declared parameters, it's a
347			                       // void function. I'm not sure if the parameter name is
348			                       // ever referenced anywhere when the type is VoidType, so
349                                   // just in case I'll set it to a comment string so it will
350			                       // still compile if written out to code anywhere.
351			  	                   ft.addArgument(new VoidType(0), "/*unnamed-void*/");
352			                   } else {
353			  	                   for (Iterator iter = params.iterator(); iter.hasNext(); ) {
354                                     ParameterDeclaration pd = (ParameterDeclaration) iter.next();
355                                     ft.addArgument(pd.type(), pd.id());
356				                   }
357                               }
358                               tb.setType(canonicalize(new PointerType(SizeThunk.POINTER,
359                                                                       ft,
360                                                                       0)));
361                               s = funcPointerName;
362                           }
363                       }
364                 | LBRACKET ( e:expr )? RBRACKET { handleArrayExpr(tb, e); }
365                )*
366             )
367        ;
368
369typelessDeclaration {
370    TypeBox tb = null;
371}
372        :       #(NTypeMissing initDeclList[tb] SEMI)
373        ;
374
375declaration {
376    TypeBox tb = null;
377}
378        :       #( NDeclaration
379                    tb = declSpecifiers
380                    (
381                        initDeclList[tb]
382                    )?
383                    ( SEMI )+
384                ) { processDeclaration(tb.type()); }
385        ;
386
387parameterTypeList returns [List l] { l = new ArrayList(); ParameterDeclaration decl = null; }
388        :       ( decl = parameterDeclaration { if (decl != null) l.add(decl); } ( COMMA | SEMI )? )+ ( VARARGS )?
389        ;
390
391parameterDeclaration returns [ParameterDeclaration pd] {
392    Type t = null;
393    String decl = null;
394    pd = null;
395    TypeBox tb = null;
396}
397        :       #( NParameterDeclaration
398                tb    = declSpecifiers
399                (decl = declarator[tb] | nonemptyAbstractDeclarator[tb])?
400                ) { pd = new ParameterDeclaration(decl, tb.type()); }
401        ;
402
403functionDef {
404    TypeBox tb = null;
405}
406        :   #( NFunctionDef
407                ( functionDeclSpecifiers)?
408                declarator[tb]
409                (declaration | VARARGS)*
410                compoundStatement
411            )
412        ;
413
414declSpecifiers returns [TypeBox tb] {
415    tb = null;
416    Type t = null;
417    int x = 0;
418    int y = 0;
419}
420        :       ( y = storageClassSpecifier { x |= y; }
421                | y = typeQualifier         { x |= y; }
422                | t = typeSpecifier[x]
423                )+
424{
425            if (t == null &&
426                (x & (SIGNED | UNSIGNED)) != 0) {
427                t = new IntType("int", SizeThunk.INT, ((x & UNSIGNED) != 0), attrs2CVAttrs(x));
428            }
429            tb = new TypeBox(t, ((x & TYPEDEF) != 0));
430}
431        ;
432
433storageClassSpecifier returns [int x] { x = 0; }
434        :       "auto"     { x |= AUTO;     }
435        |       "register" { x |= REGISTER; }
436        |       "typedef"  { x |= TYPEDEF;  }
437        |       x = functionStorageClassSpecifier
438        ;
439
440
441functionStorageClassSpecifier returns [int x] { x = 0; }
442        :       "extern" { x |= EXTERN; }
443        |       "static" { x |= STATIC; }
444        |       "inline" { x |= INLINE; }
445        ;
446
447
448typeQualifier returns [int x] { x = 0; }
449        :       "const"    { x |= CONST; }
450        |       "volatile" { x |= VOLATILE; }
451        |       "signed"   { x |= SIGNED; }
452        |       "unsigned" { x |= UNSIGNED; }
453        ;
454
455typeSpecifier[int attributes] returns [Type t] {
456    t = null;
457    int cvAttrs = attrs2CVAttrs(attributes);
458    boolean unsigned = ((attributes & UNSIGNED) != 0);
459}
460        :       "void"     { t = new VoidType(cvAttrs); }
461        |       "char"     { t = new IntType("char" , SizeThunk.CHAR,  unsigned, cvAttrs); }
462        |       "short"    { t = new IntType("short", SizeThunk.SHORT, unsigned, cvAttrs); }
463        |       "int"      { t = new IntType("int"  , SizeThunk.INT,   unsigned, cvAttrs); }
464        |       "long"     { t = new IntType("long" , SizeThunk.LONG,  unsigned, cvAttrs); }
465        |       "__int32"  { t = new IntType("__int32", SizeThunk.INT, unsigned, cvAttrs); }
466        |       "__int64"  { t = new IntType("__int64", SizeThunk.INT64, unsigned, cvAttrs); }
467        |       "float"    { t = new FloatType("float", SizeThunk.FLOAT, cvAttrs); }
468        |       "double"   { t = new DoubleType("double", SizeThunk.DOUBLE, cvAttrs); }
469        |       t = structSpecifier[cvAttrs] ( attributeDecl )*
470        |       t = unionSpecifier [cvAttrs] ( attributeDecl )*
471        |       t = enumSpecifier  [cvAttrs]
472        |       t = typedefName    [cvAttrs]
473        |       #("typeof" LPAREN
474                    ( (typeName )=> typeName
475                    | expr
476                    )
477                    RPAREN
478                )
479        |       "__complex"
480        ;
481
482typedefName[int cvAttrs] returns [Type t] { t = null; }
483        :       #(NTypedefName id : ID)
484            {
485              t = canonicalize(lookupInTypedefDictionary(id.getText()).getCVVariant(cvAttrs));
486            }
487        ;
488
489structSpecifier[int cvAttrs] returns [Type t] { t = null; }
490        :   #( "struct" t = structOrUnionBody[CompoundTypeKind.STRUCT, cvAttrs] )
491        ;
492
493unionSpecifier[int cvAttrs] returns [Type t] { t = null; }
494        :   #( "union" t = structOrUnionBody[CompoundTypeKind.UNION, cvAttrs] )
495        ;
496
497structOrUnionBody[CompoundTypeKind kind, int cvAttrs] returns [CompoundType t] {
498    t = null;
499}
500        :       ( (ID LCURLY) => id:ID LCURLY {
501                    t = (CompoundType) canonicalize(lookupInStructDictionary(id.getText(), kind, cvAttrs));
502                  } ( structDeclarationList[t] )?
503                    RCURLY { t.setBodyParsed(); }
504                |   LCURLY { t = new CompoundType(null, null, kind, cvAttrs); }
505                    ( structDeclarationList[t] )?
506                    RCURLY { t.setBodyParsed(); }
507                | id2:ID { t = (CompoundType) canonicalize(lookupInStructDictionary(id2.getText(), kind, cvAttrs)); }
508                )
509        ;
510
511structDeclarationList[CompoundType t]
512        :       ( structDeclaration[t] )+
513        ;
514
515structDeclaration[CompoundType containingType] {
516    Type t = null;
517    boolean addedAny = false;
518}
519        :       t = specifierQualifierList addedAny = structDeclaratorList[containingType, t] {
520                    if (!addedAny) {
521                        if (t != null) {
522                            CompoundType ct = t.asCompound();
523                            if (ct.isUnion()) {
524                                // Anonymous union
525                                containingType.addField(new Field(null, t, null));
526                            }
527                        }
528                    }
529                }
530        ;
531
532specifierQualifierList returns [Type t] {
533    t = null; int x = 0; int y = 0;
534}
535        :       (
536                t = typeSpecifier[x]
537                | y = typeQualifier { x |= y; }
538                )+ {
539            if (t == null &&
540                (x & (SIGNED | UNSIGNED)) != 0) {
541                t = new IntType("int", SizeThunk.INT, ((x & UNSIGNED) != 0), attrs2CVAttrs(x));
542            }
543}
544        ;
545
546structDeclaratorList[CompoundType containingType, Type t] returns [boolean addedAny] {
547    addedAny = false;
548    boolean y = false;
549}
550        :       ( y = structDeclarator[containingType, t] { addedAny = y; })+
551        ;
552
553structDeclarator[CompoundType containingType, Type t] returns [boolean addedAny] {
554    addedAny = false;
555    String s = null;
556    TypeBox tb = new TypeBox(t);
557}
558        :
559        #( NStructDeclarator
560            ( s = declarator[tb] { containingType.addField(new Field(s, tb.type(), null)); addedAny = true; } )?
561            ( COLON expr     { /* FIXME: bit types not handled yet */ }        ) ?
562            ( attributeDecl )*
563        )
564        ;
565
566// FIXME: this will not correctly set the name of the enumeration when
567// encountering a declaration like this:
568//
569//     typedef enum {  } enumName;
570//
571// In this case calling getName() on the EnumType return value will
572// incorrectly return HeaderParser.ANONYMOUS_ENUM_NAME instead of
573// "enumName"
574//
575// I haven't implemented it yet because I'm not sure how to get the
576// "enumName" *before* executing the enumList rule.
577enumSpecifier [int cvAttrs] returns [Type t] {
578	t = null;
579}
580        :       #( "enum"
581                   ( ( ID LCURLY )=> i:ID LCURLY enumList[(EnumType)(t = getEnumType(i.getText()))] RCURLY
582                     | LCURLY enumList[(EnumType)(t = getEnumType(ANONYMOUS_ENUM_NAME))] RCURLY
583                     | ID { t = getEnumType(i.getText()); }
584                    )
585                  )
586        ;
587
588enumList[EnumType enumeration] {
589	long defaultEnumerantValue = 0;
590}
591      :       ( defaultEnumerantValue = enumerator[enumeration, defaultEnumerantValue] )+
592      ;
593
594enumerator[EnumType enumeration, long defaultValue] returns [long newDefaultValue] {
595	newDefaultValue = defaultValue;
596}
597        :       eName:ID ( ASSIGN eVal:expr )? {
598                    long value = 0;
599                    if (eVal != null) {
600		      String vTxt = eVal.getAllChildrenText();
601                      if (enumHash.containsKey(vTxt)) {
602                        EnumType oldEnumType = (EnumType) enumHash.get(vTxt);
603                        value = oldEnumType.getEnumValue(vTxt);
604                      } else {
605                        try {
606                          value = Long.decode(vTxt).longValue();
607                        } catch (NumberFormatException e) {
608			  System.err.println("NumberFormatException: ID[" + eName.getText() + "], VALUE=[" + vTxt + "]");
609                          throw e;
610                        }
611                      }
612                    } else {
613                      value = defaultValue;
614                    }
615
616					newDefaultValue = value+1;
617	  				String eTxt = eName.getText();
618	  				if (enumHash.containsKey(eTxt)) {
619						EnumType oldEnumType = (EnumType) enumHash.get(eTxt);
620						long oldValue = oldEnumType.getEnumValue(eTxt);
621	  					System.err.println("WARNING: redefinition of enumerated value '" + eTxt + "';" +
622		  				   " existing definition is in enumeration '" + oldEnumType.getName() +
623		  				   "' with value " + oldValue + " and new definition is in enumeration '" +
624		  				   enumeration.getName() + "' with value " + value);
625						// remove old definition
626						oldEnumType.removeEnumerate(eTxt);
627	  				}
628	  				// insert new definition
629	  				enumeration.addEnum(eTxt, value);
630	  				enumHash.put(eTxt, enumeration);
631					//System.err.println("ENUM [" + enumeration.getName() + "]: " + eTxt + " = " + enumeration.getEnumValue(eTxt) +
632				    //                   " (new default = " + newDefaultValue + ")");
633				}
634	    ;
635
636initDeclList[TypeBox tb]
637        :       ( initDecl[tb] )+
638        ;
639
640initDecl[TypeBox tb] {
641    String declName = null;
642}
643        :       #( NInitDecl
644                declName = declarator[tb] {
645					//System.err.println("GOT declName: " + declName + " TB=" + tb);
646	  			}
647                ( attributeDecl )*
648                ( ASSIGN initializer
649                | COLON expr
650                )?
651                )
652{
653    if ((declName != null) && (tb != null) && tb.isTypedef()) {
654        Type t = tb.type();
655		    //System.err.println("Adding typedef mapping: [" + declName + "] -> [" + t + "]");
656        if (!t.hasTypedefName()) {
657            t.setName(declName);
658        }
659        t = canonicalize(t);
660        typedefDictionary.put(declName, t);
661        // Clear out PointerGroup effects in case another typedef variant follows
662        tb.reset();
663    }
664}
665        ;
666
667pointerGroup[TypeBox tb] { int x = 0; int y = 0; }
668        :       #( NPointerGroup ( STAR { x = 0; y = 0; } ( y = typeQualifier { x |= y; } )*
669                                    {
670		  																	//System.err.println("IN PTR GROUP: TB=" + tb);
671                                        if (tb != null) {
672                                            tb.setType(canonicalize(new PointerType(SizeThunk.POINTER,
673                                                                                    tb.type(),
674                                                                                    attrs2CVAttrs(x))));
675                                        }
676                                    }
677                                 )+ )
678  ;
679
680
681functionDeclSpecifiers
682        :
683                ( functionStorageClassSpecifier
684                | typeQualifier
685                | typeSpecifier[0]
686                )+
687        ;
688
689typeName {
690    TypeBox tb = null;
691}
692        :       specifierQualifierList (nonemptyAbstractDeclarator[tb])?
693        ;
694
695
696/* FIXME: the handling of types in this rule has not been well thought
697   out and is known to be incomplete. Currently it is only used to handle
698   pointerGroups for unnamed parameters. */
699nonemptyAbstractDeclarator[TypeBox tb]
700        :   #( NNonemptyAbstractDeclarator
701            (   pointerGroup[tb]
702                (   (LPAREN
703                    (   nonemptyAbstractDeclarator[tb]
704                        | parameterTypeList
705                    )?
706                    RPAREN)
707                | (LBRACKET (e1:expr)? RBRACKET) { handleArrayExpr(tb, e1); }
708                )*
709
710            |  (   (LPAREN
711                    (   nonemptyAbstractDeclarator[tb]
712                        | parameterTypeList
713                    )?
714                    RPAREN)
715                | (LBRACKET (e2:expr)? RBRACKET) { handleArrayExpr(tb, e2); }
716                )+
717            )
718            )
719        ;
720
721/* Helper routine for parsing expressions which evaluate to integer
722   constants. Can be made more complicated as necessary. */
723intConstExpr returns [int i] { i = -1; }
724        : n:Number   { return Integer.parseInt(n.getText()); }
725        ;
726