packages feed

hdirect-0.21.0: src/Parser.y

{
{-
%
% (c) The GRASP/AQUA Project, Glasgow University, 1998
%
% @(#) $Docid: Jan. 15th 2004  10:33  Sigbjorn Finne $
% @(#) $Contactid: sof@galois.com $
%

A grammar for IDL, DCE / MS (IDL/ODL) style.

Conflicts:
   - 1 reduce/reduce conflict due to `default'
     both being an attribute and a keyword.
   - 7 shift/reduce conflicts due to the overloading
     of `const' (t. qualifier and keyword.)

ToDo: 
  - fix above conflicts.

-}
module Parser ( parseIDL ) where
 
import LexM
import Lex
import IDLToken
import IDLSyn
import IDLUtils ( mkFunId, mkMethodId, toCConvAttrib,
                  mkGNUAttrib, toPackedAttrib, exprType )
import BasicTypes
import Literal
import IO ( hPutStrLn, stderr )
{-
BEGIN_GHC_ONLY
import GlaExts
END_GHC_ONLY
-}
}

%name parseIDL
%tokentype { IDLToken }
%monad { LexM } {thenLexM } { returnLexM }
%lexer { lexIDL } { T_eof }
%token
	';'	      { T_semi }
        MODULE	      { T_module }
        INTERFACE     { T_interface }
        '('           { T_oparen }
        ')'           { T_cparen }
        '{'           { T_ocurly }
        '}'           { T_ccurly }
        ':'           { T_colon  }
        ','           { T_comma }
        '.'           { T_dot }
        '...'         { T_dotdotdot }
        CONST         { T_const }
        VOLATILE      { T_volatile }
        '='           { T_equal }
        '=='          { T_eqeq }
        '!='          { T_neq }
        '|'           { T_or }
        '||'          { T_rel_or }
        '^'           { T_xor }
        '&'           { T_and }
        '&&'          { T_rel_and }
        SHIFT         { T_shift $$ }
        '/'           { T_div }
        '%'           { T_mod }
        '~'           { T_not }
        '!'           { T_negate }
        '?'           { T_question }
        TYPEDEF       { T_typedef }
        EXTERN        { T_extern }
        TYPE          { T_type $$ }
	ITYPE         { T_idl_type $$ }
        FLOAT         { T_float $$ }
	SHORT         { (T_int Short) }
	LONG          { (T_int Long) }
	LONGLONG      { (T_int LongLong) }
	ULONGLONG     { (T_uint LongLong) }
        INT           { (T_int Natural) }
        UNSIGNED      { T_unsigned }
        SIGNED        { T_signed }
        CHAR          { T_char }
        WCHAR         { T_wchar }
        STRUCT        { T_struct }
        UNION         { T_union }
        SWITCH        { T_switch }
        CASE          { T_case }
        DEFAULT       { T_default }
        ENUM          { T_enum }
        '<'           { T_lt }
        '<='          { T_le }
        '>'           { T_gt }
        '>='          { T_ge }
        '['           { T_osquare }
        ']'           { T_csquare }
        SIZEOF        { T_sizeof }
        VOID          { T_void }
        MODE          { T_mode $$ }
        LITERAL       { T_literal $$ }
        STRING_LIT    { T_string_lit $$ }
	CALLCONV      { T_callconv $$ }
	ID	      { T_id $$ }
	DISPINTERFACE { T_dispinterface }
	COCLASS       { T_coclass }
	LIBRARY       { T_library }
	'+'	      { T_plus }
	'*'	      { T_times }
	'-'	      { T_minus }
	STRING        { T_string }
	WSTRING       { T_wstring }
	METHODS	      { T_methods }
	PROPERTIES    { T_properties }
	CPP_QUOTE     { T_cpp_quote }
	HS_QUOTE      { T_hs_quote }
	INCLUDE	      { T_include $$ }
        IMPORTLIB     { T_importlib }
        INCLUDE_START { T_include_start $$ }
        INCLUDE_END   { T_include_end }
	ATTRIBUTE     { T_gnu_attribute }
        IMPORT        { T_import }
	PRAGMA        { T_pragma $$ }
        HDEFINE       { T_hdefine }
	SAFEARRAY     { T_safearray }
        UNKNOWN       { T_unknown $$ }
%%

{-
 Share the same parser for DCE IDL input and ASFs. To avoid
 running into gratuitous conflicts, ASF input is assumed to
 be prefixed by a '=' (inserted by the compiler.)
-}

specification :: { Either [Defn] [(Name, Bool, [Attribute])] }
   : definitions	     { Left  (reverse $1) }
   | '=' attr_defs           { Right (reverse $2) }

definitions  :: { [Defn] }
   :                         {    []   }
   | definitions definition  { $2 : $1 }

attr_defs :: { [(String, Bool, [Attribute])] }
   : {- empty -}             {    []   }
   | attr_defs attr_def      { $2 : $1 }

attr_def :: { (String, Bool, [Attribute] ) }
   : attr_name ':' opt_attributes  { ($1, True, $3) }
   | attr_name '=' opt_attributes  { ($1, False, $3) }

attr_name :: { String }
   : ID    { $1 }
   | TYPE  { $1 }

definition   :: { Defn }
   : type_dcl      ';'            { $1 }
   | DISPINTERFACE identifier ';' {% let (Id i) = $2 in addIfaceTypedef i >>= \ v -> return (Forward v) }
   | attributed    semi           { $1 }
   | cpp_quote     semi           { $1 }
   | hs_quote      semi           { $1 }
   | define                       { $1 }
   | import_dcl    ';'            { $1 }
   | pragma_dcl                   { $1 }

attributed   :: { Defn }
   : attributes attr_defn { Attributed $1 $2 }
   | attr_defn            { $1 }

attr_defn    :: { Defn }
   : interface_dcl             {         $1 }
   | MODULE identifier         { Forward $2 }
   | MODULE identifier  '{' definitions semi '}'
                               { Module $2 (reverse $4) }
   | dispinterface             { $1 }
   | COCLASS identifier '{' coclass_members ';' '}'
                               {% let (Id i) = $2 in addIfaceTypedef i >>= \ v -> return (CoClass v (reverse $4)) }
   | COCLASS TYPE '{' coclass_members ';' '}'
                               { CoClass (Id $2) (reverse $4) }
   | LIBRARY identifier '{' definitions '}'
	                       { Library $2 (reverse $4) }
   | op_decl                   { $1 }

{- op_decls at the toplevel isn't legal, but we allow it to be parsed
   anyhow, and report the presence of these later on -}

dispinterface :: { Defn }
   : dispinterface_hdr '{'
       PROPERTIES ':' attr_id_list 
       METHODS    ':' method_decls
     '}'    {% let (Id i) = $1 in addIfaceTypedef i >>= \ v -> return (DispInterface v $5 (reverse $8)) }
   | dispinterface_hdr '{' 
       INTERFACE simple_declarator ';'
     '}'    {% let (Id i) = $1 in addIfaceTypedef i >>= \ v -> return (DispInterfaceDecl v $4) }

attr_id_list  :: { [([Attribute], Type, Id)] }
   : {- empty -} 			            { [] } 
   | attr_id_list opt_attributes type_spec declarator ';' { ($2,$3,$4):$1 }

coclass_members :: { [CoClassMember] }
   : coclass_member 		        {   [$1]  }
   | coclass_members ';' coclass_member { $3 : $1 }

coclass_member  :: { CoClassMember }
   : opt_coclass_attrs INTERFACE TYPE           { (True,  Id $3, $1) }
   | opt_coclass_attrs DISPINTERFACE TYPE       { (False, Id $3, $1) }
   | opt_coclass_attrs INTERFACE identifier     { (True,  $3, $1) }
   | opt_coclass_attrs DISPINTERFACE identifier { (False, $3, $1) }

opt_coclass_attrs :: { [Attribute] }
   : {- empty -}                   {  [] }
   | '[' cc_attributes ']'         {  $2 }

interface_dcl :: { Defn }
   : interface_hdr { Forward $1 }
   | interface_hdr inheritance_spec '{' exports '}' 
       { Interface $1 $2 (reverse $4) }

{- 
  We eagerly add the name of an interface as a type name, so
  that typedefs inside the interface body can refer to it.
-}

interface_hdr :: { Id }
   : INTERFACE identifier {% let (Id i) = $2 in addIfaceTypedef i >>= return  }
   | INTERFACE TYPE       { (Id $2) }
   | INTERFACE ITYPE      { (Id "Object") }

dispinterface_hdr :: { Id }
   : DISPINTERFACE identifier {% let (Id i) = $2 in addIfaceTypedef i >>= return  }
   | DISPINTERFACE TYPE       { (Id $2) }

cpp_quote  :: { Defn }
   : CPP_QUOTE '(' STRING_LIT ')' { CppQuote $3 }

hs_quote  :: { Defn }
   : HS_QUOTE '(' STRING_LIT ')' { HsQuote $3 }
   | INCLUDE  			 { CInclude $1 }

exports   :: { [Defn] }
   : {-empty -}      {    []   }
   | exports export  { $2 : $1 }


export    :: { Defn }
   :  attributed_exp  ';' { $1 }
   |  type_dcl   ';'      { $1 }
   |  cpp_quote semi      { $1 }
   |  hs_quote  semi      { $1 }
   |  import_dcl ';'      { $1 }

attributed_exp :: { Defn }
   : attributes op_decl  { Attributed $1 $2 }
   | op_decl             { $1 }

inheritance_spec :: { Inherit }
   : 	            { [] }
   | ':' TYPE       { [ $2 ]  }
   | ':' identifier { let (Id i) = $2 in [ i ]  }

import_dcl     :: { Defn }
   : IMPORT string_lit_list       {% slurpImports (parseIDL >>= \ (Left y) -> return y) $2 }
   | IMPORTLIB '(' STRING_LIT ')' {% handleImportLib (parseIDL >>= \ (Left y) -> return y) $3 }

{- No validation here of what's on the #pragma line -}
pragma_dcl  :: { Defn }
   : PRAGMA 		       { Pragma $1 }
   | INCLUDE_START	       { IncludeStart $1 }
   | INCLUDE_END               { IncludeEnd }
   
define :: { Defn }
   : HDEFINE ID const_expr     { Constant (Id $2) [] (exprType (TyInteger Natural) $3) $3 }

string_lit_list :: { [String] }
   : STRING_LIT			  { [ $1 ] }
   | STRING_LIT ',' string_lit_list { ($1 : $3) }

{- 
 Not using type_spec for constant types minimises
 the shift/reduce conflicts (i.e., no CONST qualifiers
 allowed below.)
-}
const_type    :: { Type }
   : integer_ty         { $1 }
   | CHAR               { TyChar  }
   | WCHAR              { TyWChar }
   | FLOAT              { TyFloat $1 } 
   | VOID		{ TyVoid }
   | VOID '*'           { TyPointer TyVoid }
   | CHAR '*'           { TyString Nothing }
   | WCHAR '*'          { TyWString Nothing }
   | string_type        { $1 }
   | const_type '[' ']' { TyArray $1 [] }
     -- my word, what junk C allows here.
   | SIGNED               { TySigned True  }
   | UNSIGNED             { TySigned False }
   | UNSIGNED integer_ty        { TyApply (TySigned False) $2 }
   | SIGNED   integer_ty        { TyApply (TySigned True)  $2 }
   | UNSIGNED CHAR        { TyApply (TySigned False) TyChar }
   | integer SIGNED INT   { TyApply (TySigned True)  $1 }
   | integer UNSIGNED INT { TyApply (TySigned False) $1 }
   | SIGNED CHAR        { TyApply (TySigned True)  TyChar }
   | ID                 { TyName $1 Nothing }
   | TYPE               { TyName $1 Nothing }
   | TYPE '*'           { TyPointer (TyName $1 Nothing) }
   | ITYPE		{ $1 }
   | error              {% dumpErrMsg >> return TyVoid }

integer :: { Type }
   : SHORT      { TyInteger Short }
   | LONG       { TyInteger Long  }
   | LONG LONG  { TyInteger LongLong }
   | LONGLONG   { TyInteger LongLong }
   | ULONGLONG  { TyApply (TySigned False) (TyInteger LongLong) }

integer_ty :: { Type }
   : integer      { $1 }
   | integer INT  { $1 }
   | INT          { TyInteger Natural }

const_type_cast    :: { Type }
   : integer_ty         { $1 }
   | CHAR               { TyChar  }
   | WCHAR              { TyWChar }
   | FLOAT              { TyFloat $1 } 
   | VOID		{ TyVoid }
   | string_type        { $1 }
   | const_type '[' ']' { TyArray $1 [] }
   | SIGNED               { TySigned True  }
   | UNSIGNED           { TySigned False }
   | UNSIGNED integer_ty        { TyApply (TySigned False) $2 }
   | SIGNED integer_ty    { TyApply (TySigned True)  $2 }
   | integer UNSIGNED INT { TyApply (TySigned False) $1 }
   | integer SIGNED INT   { TyApply (TySigned True)  $1 }
   | UNSIGNED CHAR        { TyApply (TySigned False) TyChar }
   | SIGNED CHAR          { TyApply (TySigned True)  TyChar }
   | TYPE                 { TyName $1 Nothing }
   | ITYPE		  { $1 }
   | error                {% dumpErrMsg >> return TyVoid }

const_expr    :: { Expr }
   : cond_expr	 { $1 }

cond_expr     :: { Expr }
   : log_or_expr		              { $1 }
   | cond_expr '?' const_expr ':' log_or_expr { Cond $1 $3 $5 }

log_or_expr   :: { Expr }
   : log_and_expr			{ $1 }
   | log_or_expr '||' log_and_expr	{ Binary LogOr $1 $3 }
   
log_and_expr  :: { Expr }
   : or_expr			 { $1 }
   | log_and_expr '&&' or_expr   { Binary LogAnd $1 $3 }

or_expr     :: { Expr }
   : xor_expr	           { $1 }
   | or_expr '|' xor_expr  { Binary Or $1 $3 }

xor_expr      :: { Expr }
   : and_expr	           { $1 }
   | xor_expr '^' and_expr { Binary Xor $1 $3 }

and_expr      :: { Expr }
   : eq_expr		  { $1 }
   | and_expr '&' eq_expr { Binary And $1 $3 }

eq_expr       :: { Expr }
   : rel_expr			{ $1 }
   | eq_expr eq_op rel_expr	{ Binary Eq $1 $3 }

eq_op :: { BinaryOp }
   : '=='  { Eq }
   | '!='  { Ne }

rel_expr      :: { Expr }
   : shift_expr			{ $1 }
   | rel_expr '<'  shift_expr   { Binary Lt $1 $3 }
   | rel_expr '<=' shift_expr   { Binary Le $1 $3 }
   | rel_expr '>=' shift_expr   { Binary Ge $1 $3 }
   | rel_expr '>'  shift_expr   { Binary Gt $1 $3 }

shift_expr    :: { Expr }
   : add_expr		    { $1 }
   | shift_expr SHIFT add_expr  { Binary (Shift $2) $1 $3 }

add_expr      :: { Expr }
   : mult_expr		 { $1 }
   | add_expr '+' mult_expr  { Binary Add $1 $3 }
   | add_expr '-' mult_expr  { Binary Sub $1 $3 }

mult_expr     :: { Expr }
   : cast_expr	         { $1 }
   | mult_expr '*' cast_expr { Binary Mul $1 $3 }
   | mult_expr '/' cast_expr { Binary Div $1 $3 }
   | mult_expr '%' cast_expr { Binary Mod $1 $3 }

cast_expr     :: { Expr }
   : unary_expr		      { $1 }
   | '(' const_type_cast ')' cast_expr { Cast $2 $4 }

unary_expr    :: { Expr }
   : primary_expr  	          { $1 }
   | SIZEOF '(' const_type ')'    { Sizeof $3 }
   | unary_operator primary_expr  { Unary $1 $2}

unary_operator :: { UnaryOp }
   : '-' { Minus } | '+' { Plus } | '~' { Not } | '!' { Negate }

primary_expr  :: { Expr }
   : identifier  	 { let (Id i) = $1 in Var i }
   | LITERAL	         { Lit $1 }
   | STRING_LIT	         { Lit (StringLit $1) }
   | '(' const_expr ')'  { $2 }

type_dcl      :: { Defn }
   : mb_gnu_attributes TYPEDEF opt_attributes type_spec declarators mb_gnu_attributes 
     {% let decls = reverse $5 in addTypes decls >> return (Typedef $4 $3 decls) }

   | attributes struct_or_union_or_enum_spec               { Attributed $1 (TypeDecl $2) }
   | struct_or_union_or_enum_spec 		           { TypeDecl $1 }
   | attributes CONST const_type identifier '=' const_expr { Constant $4 $1 $3 $6 }
   | CONST const_type identifier '=' const_expr            { Constant $3 [] $2 $5 }
   | mb_gnu_attributes EXTERN mb_gnu_attributes type_spec declarators mb_gnu_attributes
     {% let decls = reverse $5 in addTypes decls >> return (ExternDecl $4 decls) }

type_spec     :: { Type }
   : type_specifier                { $1 }
   | type_spec '[' ']'             { TyArray $1 [] }
   | type_spec '[' const_expr ']'  { TyArray $1 [$3] }
   | type_specifier type_qualifier { TyApply (TyQualifier $2) $1 }
   | type_qualifier type_spec      { TyApply (TyQualifier $1) $2 }

type_spec_no_leading_qual :: { Type }
   : type_specifier		       { $1 }
   | type_spec_no_leading_qual '[' ']' { TyArray $1 [] }
   | type_specifier type_qualifier     { TyApply (TyQualifier $2) $1 }

type_qualifier  :: { Qualifier }
   : CONST     { Const    }
   | VOLATILE  { Volatile }

type_specifier  :: { Type }
   : FLOAT                { TyFloat $1 }
   | CHAR                 { TyChar  }
   | WCHAR                { TyWChar }
   | integer_ty           { $1 }
   | VOID                 { TyVoid }
   | SIGNED               { TySigned True  }
   | UNSIGNED             { TySigned False }
   | SIGNED signed_type_specifier          { TyApply (TySigned True) $2  }
   | SIGNED CONST signed_type_specifier    { TyApply (TySigned True) $3  }
   | UNSIGNED signed_type_specifier        { TyApply (TySigned False) $2 }
   | UNSIGNED CONST signed_type_specifier  { TyApply (TySigned False) $3 }
   | integer SIGNED INT   { TyApply (TySigned True)  $1 }
   | integer UNSIGNED INT { TyApply (TySigned False) $1 }
   | ID                   { TyName $1 Nothing } 
   | TYPE                 { TyName $1 Nothing } 
   | ITYPE		{ $1 }
   | SAFEARRAY safearray_type_spec ')' { TySafeArray $2 }  {- the oparen is part of the SAFEARRAY lexeme..-}
   | struct_or_union_spec { $1 }
   | enum_type            { $1 }
   | error                {% dumpErrMsg >> return TyVoid }

signed_type_specifier :: { Type }
                      : integer_ty   { $1 }
		      | CHAR         { TyChar }
		      | FLOAT        { TyFloat $1 }
--		      | ID           { TyName $1 Nothing }
		      | TYPE         { TyName $1 Nothing }
   		      | ITYPE	     { $1 }


safearray_type_spec :: { Type }
         : type_specifier  { $1 }
	 | safearray_type_spec '*' { TyPointer $1 }

struct_or_union_or_enum_spec :: { Type }
     : struct_or_union_spec  { $1 }
     | enum_type	     { $1 }

struct_or_union_spec :: { Type }
     : struct_type      { $1 }
     | union_type       { $1 }

struct_type :: { Type }
     : STRUCT simple_declarator '{' member_list '}' mb_gnu_attributes { TyStruct (Just $2) (reverse $4) (toPackedAttrib $6) }
     | STRUCT            '{' member_list '}'        mb_gnu_attributes { TyStruct Nothing (reverse $3) (toPackedAttrib $5) }
     | STRUCT simple_declarator                     { TyStruct (Just $2) [] Nothing }
              

union_type           :: { Type }
     : UNION switch_id SWITCH  '(' switch_type_spec identifier ')' 
       switch_id '{' switch_body  '}' { TyUnion $2 $5 $6 $8 (reverse $10) }

     | UNION switch_id  '{' member_or_switch_list '}' mb_gnu_attributes
                        { case $4 of { Left sw -> TyUnionNon $2 (reverse sw) ; Right mem -> TyCUnion $2 (reverse mem) (toPackedAttrib $6) } }
     | UNION identifier mb_gnu_attributes { TyCUnion (Just $2) [] (toPackedAttrib $3) }

member_or_switch_list :: { Either [Switch] [Member] }
     : switch_body       { Left $1 }
     | member_list       { Right $1 }

declarators        :: { [ Id ] }
     : declarator		  { [ $1 ]  }
     | declarators ',' declarator { $3 : $1 }

declarator :: { Id }
     : callconv_or_attr pointer_declarator  { $1 $2 }
     | pointer_declarator           { $1 }
     | callconv_or_attr direct_declarator   { $1 $2 }
     | direct_declarator            { $1 }

callconv_or_attr :: { (Id -> Id) }
     : gnu_attributes    { toCConvAttrib $1 }
     | CALLCONV          { CConvId $1 }

direct_declarator :: { Id }
     : simple_declarator          { $1 }
     | '(' declarator ')'         { $2 }
     | function_declarator        { $1 }
     | array_declarator           { $1 }

simple_declarator :: { Id }
     : identifier	       { $1 }
         -- bit field info is currently thrown away. ToDo: fix.
     | identifier ':' LITERAL  { (let { (Id nm) = $1 ; x = mkBitField nm $3 } in BitFieldId x $1) }
     | TYPE ':' LITERAL        { (let x = mkBitField $1 $3 in BitFieldId x (Id $1)) }
     | ':' LITERAL             { (let x = mkBitField "" $2 in BitFieldId x (Id "")) }
     | TYPE                    { (Id $1) }
     | MODE                    { (if $1 == In then Id "in" else Id "out") }

pointer_declarator :: { Id }
     : pointer direct_declarator {  Pointed $1 $2 }
     | pointer CALLCONV direct_declarator {  Pointed $1 (CConvId $2 $3) }
     | pointer gnu_attributes direct_declarator {  Pointed $1 (toCConvAttrib $2 $3) }

array_declarator :: { Id }
     : direct_declarator '[' ']'             { ArrayId $1 []   } 
     | direct_declarator '[' '*' ']'         { ArrayId $1 []   }
     | direct_declarator '[' const_expr ']'  { ArrayId $1 [$3] }
     | direct_declarator '[' primary_expr '.' '.' primary_expr ']'  { ArrayId $1 [$3,$6] }

function_declarator :: { Id }
     : direct_declarator '(' param_type_spec_list ')' { mkFunId $1 (reverse $3) }
     | direct_declarator '(' ')'		      { mkFunId $1 [] }

pointer  :: { [[Qualifier]] }
     : '*'		      {   [[]]  }
     | '*' type_quals 	      {   [$2]  }
     | '*' pointer            { [] : $2 }
     | '*' type_quals pointer { $2 : $3 }


{- Storage qualifiers on pointers -}

type_quals   :: { [ Qualifier ] }
     : type_qualifier	          { [$1] }
     | type_quals type_qualifier  { $2 : $1 }


member_list  :: { [Member] }
     : member mb_gnu_attributes ';'             { [ $1 ]  }
     | member_list member mb_gnu_attributes ';' { $2 : $1 }

member    :: { Member  }
     : opt_attributes type_spec declarators { ($2, $1, reverse $3) }
     | opt_attributes type_spec             { ($2, $1, []) }
     -- The last one is unpleasant, unnamed members.

switch_id	 :: { Maybe Id }
     : 			{ Nothing }
     | identifier	{ Just $1 }

switch_type_spec :: { Type }
     : integer_ty           { $1 }
     | CHAR	            { TyChar }
     | enum_type	    { $1 }
     | struct_or_union_spec { $1 }
     | ID                   { TyName $1 Nothing }
     | TYPE                 { TyName $1 Nothing }

switch_body      :: { [Switch] }
     : case ';'			{ [ $1 ]  }
     | switch_body case ';'	{ $2 : $1 }

case	 :: { Switch }
     : case_labels switch_arm          { Switch  $1  $2 }
     | '[' case_label1 ']' switch1_arm { Switch [$2] $4 }

switch1_arm  :: { Maybe SwitchArm }
     : {- empty -} 	    { Nothing }
     | type_spec declarator { Just (Param $2 $1 []) }
     | type_spec            { Just (Param (Id "") $1 []) }

switch_arm  :: { Maybe SwitchArm }
     : {- empty -} 			   { Nothing }
     | opt_attributes type_spec declarator { Just (Param $3 $2 $1) }
     | opt_attributes type_spec            { Just (Param (Id "") $2 $1) }

case_labels      :: { [CaseLabel] }
     : case_label	      { [ $1 ] }
     | case_labels case_label { $2 : $1 }

case_label	 :: { CaseLabel }
     : CASE const_expr ':' { Case [$2] }
     | DEFAULT ':'   	   { Default }

case_label1	 :: { CaseLabel }
     : CASE '(' const_expr_list ')' { Case (reverse $3) }
     | DEFAULT                      { Default }

const_expr_list  :: { [Expr] }
     : const_expr		      { [ $1 ]  }
     | const_expr_list ',' const_expr { $3 : $1 }

expr_list :: { [Expr] }
     : {- empty -}      { [] }
     | const_expr_list  { $1 }

enum_type	 :: { Type }
   : ENUM '{' enumerators opt_comma '}' { TyEnum Nothing (reverse $3) }
   | ENUM identifier '{' 
          enumerators opt_comma
     '}'                    { TyEnum (Just $2) (reverse $4) }
   | ENUM identifier        { TyEnum (Just $2) [] }

{- NOTE: MIDL does allow a comma trailing the enumerator list -}
enumerators      :: { [(Id, [Attribute], Maybe Expr)] }
   : enumerator	                { [ $1 ]  }
   | enumerators ',' enumerator { $3 : $1 }

enumerator  :: { (Id, [Attribute], Maybe Expr) }
   : identifier	                           { ($1, [], Nothing) } 
   | attributes identifier                 { ($2, $1, Nothing) } 
   | identifier '=' const_expr             { ($1, [], Just $3) }
   | attributes identifier '=' const_expr  { ($2, $1, Just $4) }

string_type :: { Type }
   : string_or_wstring '<' shift_expr '>' { $1 (Just $3) }
   | string_or_wstring		          { $1 Nothing   }

string_or_wstring :: { (Maybe Expr -> Type) }
   : STRING         { TyString  }
   | WSTRING        { TyWString }

method_decls :: { [Defn] }
   : {- empty -}	          {    []   }
   | method_decls method_decl ';' { $2 : $1 }

method_decl  :: { Defn }
   : opt_attributes op_type_spec declarator mb_gnu_attributes
      { let m_id = mkMethodId $3 in (Attributed $1 (Operation m_id $2 Nothing Nothing)) }

op_decl	     :: { Defn }
--   : op_type_spec declarator { Operation $2 $1 Nothing Nothing }
    : op_type_spec declarator mb_gnu_attributes { let m_id = mkMethodId $2 in (Operation m_id $1 Nothing Nothing) }


opt_attributes   :: { [Attribute] }
    : opt_attributes1  { concat $1 }

opt_attributes1  :: { [[Attribute]] }
    : {- empty -} { [] }
    | attributes opt_attributes1 { $1 : $2 }

attributes       :: { [Attribute] }
    : '[' ']'                       {  []          }
    | '[' attributes1 opt_comma ']' { (reverse $2) }

attributes1      :: { [Attribute] }
    : attribute                 {  [ $1 ] }
    | attributes1 ',' attribute { $3 : $1 }

attribute        :: { Attribute }
    : identifier opt_attr_params   { Attrib $1 $2 }
    | STRING		           { Attrib (Id "string") [] }
    | MODE		           { Mode $1 }

cc_attributes     :: { [Attribute] }
    : cc_attribute                   {  [ $1 ] }
    | cc_attributes ',' cc_attribute { $3 : $1 }

cc_attribute        :: { Attribute }
    : identifier                   { Attrib $1 [] }
    | DEFAULT			   { Attrib (Id "default") [] }

opt_attr_params  :: { [AttrParam] }
    : {- empty -}	    { [] }
    | '(' attr_params ')'   { (reverse $2) }

attr_params	 :: { [AttrParam] }
    : attr_param		 {   [$1] }
    | attr_params ',' attr_param {  $3:$1 }

attr_param	:: { AttrParam }
    : const_expr		      { (AttrExpr $1) }
    | {-empty-}			      { EmptyAttr }
    | TYPE			      { (AttrLit (TypeConst $1)) }
    | UNSIGNED TYPE                   { (AttrLit (TypeConst ("unsigned " ++ $2))) }
    | SIGNED TYPE                     { (AttrLit (TypeConst ("signed " ++ $2))) }
    | '{' LITERAL '}'                 { AttrLit $2 }  {- just a guid here, please! -}
    | '*' attr_param		      { (AttrPtr $2)  }
    
op_type_spec   :: { Type }
    : type_spec_no_leading_qual    { $1 }
    | string_type                  { $1 }

param_decl :: { Param }
    : opt_attributes type_spec                      { Param (Id "") $2 $1 }
    | opt_attributes type_spec declarator           { Param $3 $2 $1  }
    | opt_attributes type_spec abstract_declarator  { Param $3 $2 $1  }
    | '...'				            { Param (Id "vararg") TyVoid [] }

param_type_spec_list :: { [Param] }
    : param_decl 	                  { [$1]  }
    | param_type_spec_list ',' param_decl { $3 : $1 }

abstract_declarator :: { Id }
    : pointer                               { Pointed $1 (Id "") }
    | callconv_or_attr abstract_declarator  { $1 $2 }
    | direct_abstract_declarator            { $1 }
    | pointer direct_abstract_declarator    { Pointed $1 $2 }

direct_abstract_declarator :: { Id }
    : '(' abstract_declarator ')'        { $2 }
    | '(' ')'                            { FunId (Id "") Nothing [] }
    | '(' param_type_spec_list ')'       { FunId (Id "") Nothing $2 }
    | direct_abstract_declarator '(' ')' { FunId $1 Nothing [] }
    | direct_abstract_declarator '(' param_type_spec_list ')' { FunId $1 Nothing $3 }

mb_gnu_attributes :: { [GNUAttrib] }
    : {- empty -}    { [] }
    | gnu_attributes { (reverse $1) }

gnu_attributes :: { [GNUAttrib] }
    : gnu_attribute                {   [$1]  }
    | gnu_attributes gnu_attribute { ($2:$1) }

gnu_attribute :: { GNUAttrib }
    : ATTRIBUTE '(' '(' gnu_attrib ')' ')' { $4 }

gnu_attrib :: { GNUAttrib }
    : a_word            { mkGNUAttrib $1 [] }
    | CALLCONV          { CConv $1 }
    | a_word '(' ID ')' { mkGNUAttrib $1 [Var $3] }
    | a_word '(' ID ',' const_expr_list ')' { mkGNUAttrib $1 (Var $3:$5) }

a_word :: { String }
    : ID     { $1 }
    | TYPE   { $1 }

identifier :: { Id }
    : ID   { (Id $1) }
    
semi ::     { () }
    : error { () }
    | ';'   { () }

opt_comma ::      { () }
    : {- empty -} { () }
    | ','         { () }

{------------------ END OF GRAMMAR --------------}

{

addTypes :: [Id] -> LexM ()
addTypes ids = do
  sequence (map addTypedef ls)
  return ()
 where
  ls = map getName ids

  getName (Id s) = s
  getName (ArrayId i _) = getName i
  getName (Pointed _ i) = getName i
  getName (CConvId _ i) = getName i
  getName (FunId i _ _) = getName i

addIfaceTypedef :: String -> LexM Id
addIfaceTypedef nm = addTypedef nm >> return (Id nm)

mkBitField :: String -> Literal -> Int
mkBitField nm l = 
  case l of
    IntegerLit (ILit _ i) -> fromInteger i
    _ -> error ("bitfield " ++ show nm ++ " not an int.")

warningMsg :: String -> LexM ()
warningMsg msg = do
  l <- getSrcLoc
  ioToLexM (hPutStrLn stderr (show l ++ ": warning: "++msg))

dumpErrMsg :: LexM ()
dumpErrMsg = do
 l   <- getSrcLoc
 str <- getStream
 ioToLexM (ioError (userError (show l ++ ": Parse error on input: " ++ takeWhile (/='\n') str)))

happyError :: LexM a
happyError = do
 l   <- getSrcLoc
 str <- getStream
 ioToLexM (ioError (userError (show l ++ ": Parse error: " ++ takeWhile (/='\n') str)))
}