diff GEMBASSY-1.0.3/gsoap/src/soapcpp2_yacc.y @ 0:8300eb051bea draft

Initial upload
author ktnyt
date Fri, 26 Jun 2015 05:19:29 -0400
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/GEMBASSY-1.0.3/gsoap/src/soapcpp2_yacc.y	Fri Jun 26 05:19:29 2015 -0400
@@ -0,0 +1,1927 @@
+/*
+	soapcpp2_yacc.y
+
+	Yacc/Bison grammar.
+
+	Build notes:
+
+	1. Bison 1.6 is known to crash on Win32 systems if YYINITDEPTH is too
+	small Compile with -DYYINITDEPTH=5000
+
+	2. This grammar has one shift/reduce conflict related to the use of a
+	class declaration with a base class (e.g. class Y : public X) and the
+	use of a maxOccurs (class Y :10). Internally the conflict is resolved
+	in favor of a shift by Bison/Yacc, which leads to the correct parsing
+	behavior. Therefore, the warning can be ignored. If this leads to an
+	error, then please enable the following directive (around line 121):
+
+%expect 1 // Bison: ignore one shift/reduce conflict
+
+--------------------------------------------------------------------------------
+gSOAP XML Web services tools
+Copyright (C) 2000-2011, Robert van Engelen, Genivia Inc. All Rights Reserved.
+This part of the software is released under ONE of the following licenses:
+GPL or Genivia's license for commercial use.
+--------------------------------------------------------------------------------
+GPL license.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place, Suite 330, Boston, MA 02111-1307 USA
+
+Author contact information:
+engelen@genivia.com / engelen@acm.org
+
+This program is released under the GPL with the additional exemption that
+compiling, linking, and/or using OpenSSL is allowed.
+--------------------------------------------------------------------------------
+A commercial use license is available from Genivia, Inc., contact@genivia.com
+--------------------------------------------------------------------------------
+*/
+
+%{
+
+#include "soapcpp2.h"
+
+#ifdef WIN32
+#ifndef __STDC__
+#define __STDC__
+#endif
+#define YYINCLUDED_STDLIB_H
+#ifdef WIN32_WITHOUT_SOLARIS_FLEX
+extern int soapcpp2lex(void);
+#else
+extern int yylex(void);
+#endif
+#else
+extern int yylex(void);
+#endif
+
+extern int is_XML(Tnode*);
+
+#define MAXNEST 16	/* max. nesting depth of scopes */
+
+struct Scope
+{	Table	*table;
+	Entry	*entry;
+	Node	node;
+	LONG64	val;
+	int	offset;
+	Bool	grow;	/* true if offset grows with declarations */
+	Bool	mask;	/* true if enum is mask */
+}	stack[MAXNEST],	/* stack of tables and offsets */
+	*sp;		/* current scope stack pointer */
+
+Table	*classtable = (Table*)0,
+	*enumtable = (Table*)0,
+	*typetable = (Table*)0,
+	*booltable = (Table*)0,
+	*templatetable = (Table*)0;
+
+char	*namespaceid = NULL;
+int	transient = 0;
+int	permission = 0;
+int	custom_header = 1;
+int	custom_fault = 1;
+Pragma	*pragmas = NULL;
+Tnode	*qname = NULL;
+Tnode	*xml = NULL;
+
+/* function prototypes for support routine section */
+static Entry	*undefined(Symbol*);
+static Tnode	*mgtype(Tnode*, Tnode*);
+static Node	op(const char*, Node, Node), iop(const char*, Node, Node), relop(const char*, Node, Node);
+static void	mkscope(Table*, int), enterscope(Table*, int), exitscope(void);
+static int	integer(Tnode*), real(Tnode*), numeric(Tnode*);
+static void	add_soap(void), add_XML(void), add_qname(void), add_header(Table*), add_fault(Table*), add_response(Entry*, Entry*), add_result(Tnode*);
+extern char	*c_storage(Storage), *c_type(Tnode*), *c_ident(Tnode*);
+extern int	is_primitive_or_string(Tnode*), is_stdstr(Tnode*), is_binary(Tnode*), is_external(Tnode*), is_mutable(Tnode*), has_attachment(Tnode*);
+
+/* Temporaries used in semantic rules */
+int	i;
+char	*s, *s1, *s2;
+Symbol	*sym;
+Entry	*p, *q;
+Tnode	*t;
+Node	tmp, c;
+Pragma	**pp;
+
+%}
+
+/* We expect one shift-reduce conflict, see build notes in the header above */
+/* %expect 1 */ /* directive is not compatible with Yacc */
+/* If Yacc complains then remove the line above to allow Yacc to proceed */
+
+%union
+{	Symbol	*sym;
+	LONG64	i;
+	double	r;
+	char	c;
+	char	*s;
+	Tnode	*typ;
+	Storage	sto;
+	Node	rec;
+	Entry	*e;
+}
+
+/* pragmas */
+%token	<s> PRAGMA
+/* keywords */
+%token	<sym> AUTO     DOUBLE  INT       STRUCT
+%token	<sym> BREAK    ELSE    LONG      SWITCH
+%token	<sym> CASE     ENUM    REGISTER  TYPEDEF
+%token	<sym> CHAR     EXTERN  RETURN    UNION
+%token	<sym> CONST    FLOAT   SHORT     UNSIGNED
+%token	<sym> CONTINUE FOR     SIGNED    VOID
+%token	<sym> DEFAULT  GOTO    SIZEOF    VOLATILE
+%token	<sym> DO       IF      STATIC    WHILE
+%token	<sym> CLASS    PRIVATE PROTECTED PUBLIC
+%token	<sym> VIRTUAL  INLINE  OPERATOR  LLONG
+%token	<sym> BOOL     CFALSE  CTRUE	 WCHAR
+%token	<sym> TIME     USING   NAMESPACE ULLONG
+%token	<sym> MUSTUNDERSTAND   SIZE      FRIEND
+%token	<sym> TEMPLATE EXPLICIT		 TYPENAME
+%token	<sym> RESTRICT null
+%token	<sym> UCHAR    USHORT  UINT      ULONG
+/* */
+%token	NONE
+/* identifiers (TYPE = typedef identifier) */
+%token	<sym> ID LAB TYPE
+/* constants */
+%token	<i> LNG
+%token	<r> DBL
+%token	<c> CHR
+%token	<s> TAG STR
+/* types and related */
+%type	<typ> type
+%type	<sto> store virtual constobj abstract
+%type	<e> fname struct class base enum
+%type	<sym> id arg name
+%type	<s> tag patt
+%type	<i> cint
+/* expressions and statements */
+%type	<rec> expr cexp oexp obex aexp abex rexp lexp pexp init spec tspec ptrs array arrayck texp qexp occurs
+/* terminals */
+%left	','
+%right	'=' PA NA TA DA MA AA XA OA LA RA  /* += -= *= /= %= &= ^= |= <<= >>= */
+%right	'?'
+%right	':'
+%left	OR		/* || */
+%left	AN		/* && */
+%left	'|'
+%left	'^'
+%left	'&'
+%left	EQ NE		/* == != */
+%left	'<' LE '>' GE	/* <= >= */
+%left	LS RS		/* << >> */
+%left	'+' '-'
+%left	'*' '/' '%'
+%left	AR		/* -> */
+%token	PP NN		/* ++ -- */
+
+%%
+
+/******************************************************************************\
+
+	Program syntax
+
+\******************************************************************************/
+
+prog	: s1 exts	{ if (lflag)
+    			  {	custom_header = 0;
+    			  	custom_fault = 0;
+			  }
+			  else
+			  {	add_header(sp->table);
+			  	add_fault(sp->table);
+			  }
+			  compile(sp->table);
+			  freetable(classtable);
+			  freetable(enumtable);
+			  freetable(typetable);
+			  freetable(booltable);
+			  freetable(templatetable);
+			}
+	;
+s1	: /* empty */	{ classtable = mktable((Table*)0);
+			  enumtable = mktable((Table*)0);
+			  typetable = mktable((Table*)0);
+			  booltable = mktable((Table*)0);
+			  templatetable = mktable((Table*)0);
+			  p = enter(booltable, lookup("false"));
+			  p->info.typ = mkint();
+			  p->info.val.i = 0;
+			  p = enter(booltable, lookup("true"));
+			  p->info.typ = mkint();
+			  p->info.val.i = 1;
+			  mkscope(mktable(mktable((Table*)0)), 0);
+			}
+	;
+exts	: NAMESPACE ID '{' exts1 '}'
+			{ namespaceid = $2->name; }
+	| exts1		{ }
+	;
+exts1	: /* empty */	{ add_soap();
+			  add_qname();
+			  add_XML();
+			}
+	| exts1 ext	{ }
+	;
+ext	: dclrs ';'	{ }
+	| pragma	{ }
+	| error ';'	{ synerror("input before ; skipped");
+			  while (sp > stack)
+			  {	freetable(sp->table);
+			  	exitscope();
+			  }
+			  yyerrok;
+			}
+	| t1		{ }
+	| t2		{ }
+	;
+pragma	: PRAGMA	{ if ($1[1] >= 'a' && $1[1] <= 'z')
+			  {	for (pp = &pragmas; *pp; pp = &(*pp)->next)
+			          ;
+				*pp = (Pragma*)emalloc(sizeof(Pragma));
+				(*pp)->pragma = (char*)emalloc(strlen($1)+1);
+				strcpy((*pp)->pragma, $1);
+				(*pp)->next = NULL;
+			  }
+			  else if ((i = atoi($1+2)) > 0)
+				yylineno = i;
+			  else
+			  {	sprintf(errbuf, "directive '%s' ignored (use #import to import files)", $1);
+			  	semwarn(errbuf);
+			  }
+			}
+	;
+
+/******************************************************************************\
+
+	Declarations
+
+\******************************************************************************/
+
+decls	: /* empty */	{ transient &= ~6;
+			  permission = 0;
+			}
+	| dclrs ';' decls
+			{ }
+	| PRIVATE ':' t3 decls
+			{ }
+	| PROTECTED ':' t4 decls
+			{ }
+	| PUBLIC ':' t5 decls
+			{ }
+	| t1 decls t2 decls
+			{ }
+	| error ';' 	{ synerror("declaration expected"); yyerrok; }
+	;
+t1	: '['		{ transient |= 1;
+			}
+	;
+t2	: ']'		{ transient &= ~1;
+			}
+	;
+t3	:		{ permission = Sprivate;
+			}
+	;
+t4	:		{ permission = Sprotected;
+			}
+	;
+t5	:		{ permission = 0;
+			}
+	;
+dclrs	: spec		{ }
+	| spec dclr	{ }
+	| spec fdclr func
+			{ }
+	| constr func	{ }
+	| destr func	{ }
+	| dclrs ',' dclr{ }
+	| dclrs ',' fdclr func
+			{ }
+	;
+dclr	: ptrs ID arrayck tag occurs init
+			{ if (($3.sto & Stypedef) && sp->table->level == GLOBAL)
+			  {	if (($3.typ->type != Tstruct && $3.typ->type != Tunion && $3.typ->type != Tenum) || strcmp($2->name, $3.typ->id->name))
+				{	p = enter(typetable, $2);
+					p->info.typ = mksymtype($3.typ, $2);
+			  		if ($3.sto & Sextern)
+						p->info.typ->transient = -1;
+					else
+						p->info.typ->transient = $3.typ->transient;
+			  		p->info.sto = $3.sto;
+					p->info.typ->pattern = $5.pattern;
+					p->info.typ->minLength = $5.minLength;
+					p->info.typ->maxLength = $5.maxLength;
+				}
+				$2->token = TYPE;
+			  }
+			  else
+			  {	p = enter(sp->table, $2);
+			  	p->tag = $4;
+			  	p->info.typ = $3.typ;
+			  	p->info.sto = (Storage)((int)$3.sto | permission);
+				if ($6.hasval)
+				{	p->info.hasval = True;
+					switch ($3.typ->type)
+					{	case Tchar:
+						case Tuchar:
+						case Tshort:
+						case Tushort:
+						case Tint:
+						case Tuint:
+						case Tlong:
+						case Tulong:
+						case Tllong:
+						case Tullong:
+						case Tenum:
+						case Ttime:
+							if ($6.typ->type == Tint || $6.typ->type == Tchar || $6.typ->type == Tenum)
+								sp->val = p->info.val.i = $6.val.i;
+							else
+							{	semerror("type error in initialization constant");
+								p->info.hasval = False;
+							}
+							break;
+						case Tfloat:
+						case Tdouble:
+						case Tldouble:
+							if ($6.typ->type == Tfloat || $6.typ->type == Tdouble || $6.typ->type == Tldouble)
+								p->info.val.r = $6.val.r;
+							else if ($6.typ->type == Tint)
+								p->info.val.r = (double)$6.val.i;
+							else
+							{	semerror("type error in initialization constant");
+								p->info.hasval = False;
+							}
+							break;
+						default:
+							if ($3.typ->type == Tpointer
+							 && (((Tnode*)$3.typ->ref)->type == Tchar || ((Tnode*)$3.typ->ref)->type == Twchar)
+							 && $6.typ->type == Tpointer
+							 && ((Tnode*)$6.typ->ref)->type == Tchar)
+								p->info.val.s = $6.val.s;
+							else if (bflag
+							 && $3.typ->type == Tarray
+							 && ((Tnode*)$3.typ->ref)->type == Tchar
+							 && $6.typ->type == Tpointer
+							 && ((Tnode*)$6.typ->ref)->type == Tchar)
+							{	if ($3.typ->width / ((Tnode*)$3.typ->ref)->width - 1 < strlen($6.val.s))
+								{	semerror("char[] initialization constant too long");
+									p->info.val.s = "";
+								}
+
+								else
+									p->info.val.s = $6.val.s;
+							}
+							else if ($3.typ->type == Tpointer
+							      && (((Tnode*)$3.typ->ref)->id == lookup("std::string") || ((Tnode*)$3.typ->ref)->id == lookup("std::wstring")))
+							      	p->info.val.s = $6.val.s;
+							else if ($3.typ->id == lookup("std::string") || $3.typ->id == lookup("std::wstring"))
+							      	p->info.val.s = $6.val.s;
+							else if ($3.typ->type == Tpointer
+							      && $6.typ->type == Tint
+							      && $6.val.i == 0)
+								p->info.val.i = 0;
+							else
+							{	semerror("type error in initialization constant");
+								p->info.hasval = False;
+							}
+							break;
+					}
+				}
+				else
+					p->info.val.i = sp->val;
+			        if ($5.minOccurs < 0)
+			        {	if ($6.hasval || ($3.sto & Sattribute) || $3.typ->type == Tpointer || $3.typ->type == Ttemplate || !strncmp($2->name, "__size", 6))
+			        		p->info.minOccurs = 0;
+			        	else
+			        		p->info.minOccurs = 1;
+				}
+				else
+					p->info.minOccurs = $5.minOccurs;
+				p->info.maxOccurs = $5.maxOccurs;
+				if (sp->mask)
+					sp->val <<= 1;
+				else
+					sp->val++;
+			  	p->info.offset = sp->offset;
+				if ($3.sto & Sextern)
+					p->level = GLOBAL;
+				else if ($3.sto & Stypedef)
+					;
+			  	else if (sp->grow)
+					sp->offset += p->info.typ->width;
+				else if (p->info.typ->width > sp->offset)
+					sp->offset = p->info.typ->width;
+			  }
+			  sp->entry = p;
+			}
+	;
+fdclr	: ptrs name	{ if ($1.sto & Stypedef)
+			  {	sprintf(errbuf, "invalid typedef qualifier for '%s'", $2->name);
+				semwarn(errbuf);
+			  }
+			  p = enter(sp->table, $2);
+			  p->info.typ = $1.typ;
+			  p->info.sto = $1.sto;
+			  p->info.hasval = False;
+			  p->info.offset = sp->offset;
+			  if (sp->grow)
+				sp->offset += p->info.typ->width;
+			  else if (p->info.typ->width > sp->offset)
+				sp->offset = p->info.typ->width;
+			  sp->entry = p;
+			}
+	;
+id	: ID		{ $$ = $1; }
+	| TYPE		{ $$ = $1; }
+	;
+name	: ID		{ $$ = $1; }
+	| OPERATOR '!'	{ $$ = lookup("operator!"); }
+	| OPERATOR '~'	{ $$ = lookup("operator~"); }
+	| OPERATOR '='	{ $$ = lookup("operator="); }
+	| OPERATOR PA	{ $$ = lookup("operator+="); }
+	| OPERATOR NA	{ $$ = lookup("operator-="); }
+	| OPERATOR TA	{ $$ = lookup("operator*="); }
+	| OPERATOR DA	{ $$ = lookup("operator/="); }
+	| OPERATOR MA	{ $$ = lookup("operator%="); }
+	| OPERATOR AA	{ $$ = lookup("operator&="); }
+	| OPERATOR XA	{ $$ = lookup("operator^="); }
+	| OPERATOR OA	{ $$ = lookup("operator|="); }
+	| OPERATOR LA	{ $$ = lookup("operator<<="); }
+	| OPERATOR RA	{ $$ = lookup("operator>>="); }
+	| OPERATOR OR	{ $$ = lookup("operator||"); }
+	| OPERATOR AN	{ $$ = lookup("operator&&"); }
+	| OPERATOR '|'	{ $$ = lookup("operator|"); }
+	| OPERATOR '^'	{ $$ = lookup("operator^"); }
+	| OPERATOR '&'	{ $$ = lookup("operator&"); }
+	| OPERATOR EQ	{ $$ = lookup("operator=="); }
+	| OPERATOR NE	{ $$ = lookup("operator!="); }
+	| OPERATOR '<'	{ $$ = lookup("operator<"); }
+	| OPERATOR LE	{ $$ = lookup("operator<="); }
+	| OPERATOR '>'	{ $$ = lookup("operator>"); }
+	| OPERATOR GE	{ $$ = lookup("operator>="); }
+	| OPERATOR LS	{ $$ = lookup("operator<<"); }
+	| OPERATOR RS	{ $$ = lookup("operator>>"); }
+	| OPERATOR '+'	{ $$ = lookup("operator+"); }
+	| OPERATOR '-'	{ $$ = lookup("operator-"); }
+	| OPERATOR '*'	{ $$ = lookup("operator*"); }
+	| OPERATOR '/'	{ $$ = lookup("operator/"); }
+	| OPERATOR '%'	{ $$ = lookup("operator%"); }
+	| OPERATOR PP	{ $$ = lookup("operator++"); }
+	| OPERATOR NN	{ $$ = lookup("operator--"); }
+	| OPERATOR AR   { $$ = lookup("operator->"); }
+	| OPERATOR'['']'{ $$ = lookup("operator[]"); }
+	| OPERATOR'('')'{ $$ = lookup("operator()"); }
+	| OPERATOR texp { s1 = c_storage($2.sto);
+			  s2 = c_type($2.typ);
+			  s = (char*)emalloc(strlen(s1) + strlen(s2) + 10);
+			  strcpy(s, "operator ");
+			  strcat(s, s1);
+			  strcat(s, s2);
+			  $$ = lookup(s);
+			  if (!$$)
+				$$ = install(s, ID);
+			}
+	;
+constr	: TYPE		{ if (!(p = entry(classtable, $1)))
+			  	semerror("invalid constructor");
+			  sp->entry = enter(sp->table, $1);
+			  sp->entry->info.typ = mknone();
+			  sp->entry->info.sto = Snone;
+			  sp->entry->info.offset = sp->offset;
+			  sp->node.typ = mkvoid();
+			  sp->node.sto = Snone;
+			}
+	;
+destr	: virtual '~' TYPE
+			{ if (!(p = entry(classtable, $3)))
+			  	semerror("invalid destructor");
+			  s = (char*)emalloc(strlen($3->name) + 2);
+			  strcpy(s, "~");
+			  strcat(s, $3->name);
+			  sym = lookup(s);
+			  if (!sym)
+				sym = install(s, ID);
+			  sp->entry = enter(sp->table, sym);
+			  sp->entry->info.typ = mknone();
+			  sp->entry->info.sto = $1;
+			  sp->entry->info.offset = sp->offset;
+			  sp->node.typ = mkvoid();
+			  sp->node.sto = Snone;
+			}
+	;
+func	: fname '(' s6 fargso ')' constobj abstract
+			{ if ($1->level == GLOBAL)
+			  {	if (!($1->info.sto & Sextern) && sp->entry && sp->entry->info.typ->type == Tpointer && ((Tnode*)sp->entry->info.typ->ref)->type == Tchar)
+			  	{	sprintf(errbuf, "last output parameter of remote method function prototype '%s' is a pointer to a char which will only return one byte: use char** instead to return a string", $1->sym->name);
+					semwarn(errbuf);
+				}
+				if ($1->info.sto & Sextern)
+				 	$1->info.typ = mkmethod($1->info.typ, sp->table);
+			  	else if (sp->entry && (sp->entry->info.typ->type == Tpointer || sp->entry->info.typ->type == Treference || sp->entry->info.typ->type == Tarray || is_transient(sp->entry->info.typ)))
+				{	if ($1->info.typ->type == Tint)
+					{	sp->entry->info.sto = (Storage)((int)sp->entry->info.sto | (int)Sreturn);
+						$1->info.typ = mkfun(sp->entry);
+						$1->info.typ->id = $1->sym;
+						if (!is_transient(sp->entry->info.typ))
+						{	if (!is_response(sp->entry->info.typ))
+							{	if (!is_XML(sp->entry->info.typ))
+									add_response($1, sp->entry);
+							}
+							else
+								add_result(sp->entry->info.typ);
+						}
+					}
+					else
+					{	sprintf(errbuf, "return type of remote method function prototype '%s' must be integer", $1->sym->name);
+						semerror(errbuf);
+					}
+				}
+			  	else
+			  	{	sprintf(errbuf, "last output parameter of remote method function prototype '%s' is a return parameter and must be a pointer or reference, or use %s(..., void) for one-way sends", $1->sym->name, $1->sym->name);
+					semerror(errbuf);
+			  	}
+				if (!($1->info.sto & Sextern))
+			  	{	unlinklast(sp->table);
+			  		if ((p = entry(classtable, $1->sym)))
+					{	if (p->info.typ->ref)
+						{	sprintf(errbuf, "remote method name clash: struct/class '%s' already declared at line %d", $1->sym->name, p->lineno);
+							semerror(errbuf);
+						}
+						else
+						{	p->info.typ->ref = sp->table;
+							p->info.typ->width = sp->offset;
+						}
+					}
+			  		else
+			  		{	p = enter(classtable, $1->sym);
+						p->info.typ = mkstruct(sp->table, sp->offset);
+						p->info.typ->id = $1->sym;
+			  		}
+			  	}
+			  }
+			  else if ($1->level == INTERNAL)
+			  {	$1->info.typ = mkmethod($1->info.typ, sp->table);
+				$1->info.sto = (Storage)((int)$1->info.sto | (int)$6 | (int)$7);
+			  	transient &= ~1;
+			  }
+			  exitscope();
+			}
+	;
+fname	:		{ $$ = sp->entry; }
+	;
+fargso	: /* empty */	{ }
+	| fargs		{ }
+	;
+fargs	: farg		{ }
+	| farg ',' fargs{ }
+	;
+farg	: tspec ptrs arg arrayck occurs init
+			{ if ($4.sto & Stypedef)
+			  	semwarn("typedef in function argument");
+			  p = enter(sp->table, $3);
+			  p->info.typ = $4.typ;
+			  p->info.sto = $4.sto;
+			  if ($5.minOccurs < 0)
+			  {	if ($6.hasval || ($4.sto & Sattribute) || $4.typ->type == Tpointer)
+			        	p->info.minOccurs = 0;
+			       	else
+			        	p->info.minOccurs = 1;
+			  }
+			  else
+				p->info.minOccurs = $5.minOccurs;
+			  p->info.maxOccurs = $5.maxOccurs;
+			  if ($6.hasval)
+			  {	p->info.hasval = True;
+				switch ($4.typ->type)
+				{	case Tchar:
+					case Tuchar:
+					case Tshort:
+					case Tushort:
+					case Tint:
+					case Tuint:
+					case Tlong:
+					case Tulong:
+					case Tenum:
+					case Ttime:
+						if ($6.typ->type == Tint || $6.typ->type == Tchar || $6.typ->type == Tenum)
+							sp->val = p->info.val.i = $6.val.i;
+						else
+						{	semerror("type error in initialization constant");
+							p->info.hasval = False;
+						}
+						break;
+					case Tfloat:
+					case Tdouble:
+					case Tldouble:
+						if ($6.typ->type == Tfloat || $6.typ->type == Tdouble || $6.typ->type == Tldouble)
+							p->info.val.r = $6.val.r;
+						else if ($6.typ->type == Tint)
+							p->info.val.r = (double)$6.val.i;
+						else
+						{	semerror("type error in initialization constant");
+							p->info.hasval = False;
+						}
+						break;
+					default:
+						if ($4.typ->type == Tpointer
+						 && (((Tnode*)$4.typ->ref)->type == Tchar || ((Tnode*)$4.typ->ref)->type == Twchar)
+						 && $6.typ->type == Tpointer
+						 && ((Tnode*)$6.typ->ref)->type == Tchar)
+							p->info.val.s = $6.val.s;
+						else if ($4.typ->type == Tpointer
+						      && (((Tnode*)$4.typ->ref)->id == lookup("std::string") || ((Tnode*)$4.typ->ref)->id == lookup("std::wstring")))
+						      	p->info.val.s = $6.val.s;
+						else if ($4.typ->id == lookup("std::string") || $4.typ->id == lookup("std::wstring"))
+						      	p->info.val.s = $6.val.s;
+						else if ($4.typ->type == Tpointer
+						      && $6.typ->type == Tint
+						      && $6.val.i == 0)
+							p->info.val.i = 0;
+						else
+						{	semerror("type error in initialization constant");
+							p->info.hasval = False;
+						}
+						break;
+				}
+			  }
+			  p->info.offset = sp->offset;
+			  if ($4.sto & Sextern)
+				p->level = GLOBAL;
+			  else if (sp->grow)
+				sp->offset += p->info.typ->width;
+			  else if (p->info.typ->width > sp->offset)
+				sp->offset = p->info.typ->width;
+			  sp->entry = p;
+			}
+	;
+arg	: /* empty */	{ if (sp->table->level != PARAM)
+			    $$ = gensymidx("param", (int)++sp->val);
+			  else if (eflag)
+				$$ = gensymidx("_param", (int)++sp->val);
+			  else
+				$$ = gensym("_param");
+			}
+	| ID		{ if (vflag == 2 && *$1->name == '_' && sp->table->level == GLOBAL)
+			  { sprintf(errbuf, "SOAP 1.2 does not support anonymous parameters '%s'", $1->name);
+			    semwarn(errbuf);
+			  }
+			  $$ = $1;
+			}
+	;
+
+/******************************************************************************\
+
+	Type specification
+
+\******************************************************************************/
+
+/* texp : type expression (subset of C) */
+texp	: tspec ptrs array
+			{ $$ = $3; }
+	| tspec ptrs ID array
+			{ $$ = $4; }
+	;
+spec	: /*empty */	{ $$.typ = mkint();
+			  $$.sto = Snone;
+			  sp->node = $$;
+			}
+	| store spec	{ if (($1 & Stypedef) && is_external($2.typ) && $2.typ->type != Tstruct && $2.typ->type != Tclass)
+			  	$$.typ = mktype($2.typ->type, $2.typ->ref, $2.typ->width);
+			  else
+			  	$$.typ = $2.typ;
+			  $$.sto = (Storage)((int)$1 | ((int)($2.sto)));
+			  if (($$.sto & Sattribute) && !is_primitive_or_string($2.typ) && !is_stdstr($2.typ) && !is_binary($2.typ) && !is_external($2.typ))
+			  {	semwarn("invalid attribute type");
+			  	$$.sto = (Storage)((int)$$.sto & ~Sattribute);
+			  }
+			  sp->node = $$;
+			  if ($1 & Sextern)
+				transient = 0;
+			}
+	| type spec	{ if ($1->type == Tint)
+				switch ($2.typ->type)
+				{ case Tchar:	$$.typ = $2.typ; break;
+				  case Tshort:	$$.typ = $2.typ; break;
+				  case Tint:	$$.typ = $1; break;
+				  case Tlong:	$$.typ = $2.typ; break;
+				  case Tllong:	$$.typ = $2.typ; break;
+				  default:	semwarn("illegal use of 'signed'");
+						$$.typ = $2.typ;
+				}
+			  else if ($1->type == Tuint)
+				switch ($2.typ->type)
+				{ case Tchar:	$$.typ = mkuchar(); break;
+				  case Tshort:	$$.typ = mkushort(); break;
+				  case Tint:	$$.typ = $1; break;
+				  case Tlong:	$$.typ = mkulong(); break;
+				  case Tllong:	$$.typ = mkullong(); break;
+				  default:	semwarn("illegal use of 'unsigned'");
+						$$.typ = $2.typ;
+				}
+			  else if ($1->type == Tlong)
+				switch ($2.typ->type)
+				{ case Tint:	$$.typ = $1; break;
+				  case Tlong:	$$.typ = mkllong(); break;
+				  case Tuint:	$$.typ = mkulong(); break;
+				  case Tulong:	$$.typ = mkullong(); break;
+				  case Tdouble:	$$.typ = mkldouble(); break;
+				  default:	semwarn("illegal use of 'long'");
+						$$.typ = $2.typ;
+				}
+			  else if ($1->type == Tulong)
+				switch ($2.typ->type)
+				{ case Tint:	$$.typ = $1; break;
+				  case Tlong:	$$.typ = mkullong(); break;
+				  case Tuint:	$$.typ = $1; break;
+				  case Tulong:	$$.typ = mkullong(); break;
+				  default:	semwarn("illegal use of 'long'");
+						$$.typ = $2.typ;
+				}
+			  else if ($2.typ->type == Tint)
+				$$.typ = $1;
+			  else
+			  	semwarn("invalid type (missing ';' or type name used as non-type identifier?)");
+			  $$.sto = $2.sto;
+			  sp->node = $$;
+			}
+	;
+tspec	: store		{ $$.typ = mkint();
+			  $$.sto = $1;
+			  sp->node = $$;
+			  if ($1 & Sextern)
+				transient = 0;
+			}
+	| type		{ $$.typ = $1;
+			  $$.sto = Snone;
+			  sp->node = $$;
+			}
+	| store tspec	{ $$.typ = $2.typ;
+			  $$.sto = (Storage)((int)$1 | (int)$2.sto);
+			  if (($$.sto & Sattribute) && !is_primitive_or_string($2.typ) && !is_stdstr($2.typ) && !is_binary($2.typ) && !is_external($2.typ))
+			  {	semwarn("invalid attribute type");
+			  	$$.sto = (Storage)((int)$$.sto & ~Sattribute);
+			  }
+			  sp->node = $$;
+			  if ($1 & Sextern)
+				transient = 0;
+			}
+	| type tspec	{ if ($1->type == Tint)
+				switch ($2.typ->type)
+				{ case Tchar:	$$.typ = $2.typ; break;
+				  case Tshort:	$$.typ = $2.typ; break;
+				  case Tint:	$$.typ = $1; break;
+				  case Tlong:	$$.typ = $2.typ; break;
+				  case Tllong:	$$.typ = $2.typ; break;
+				  default:	semwarn("illegal use of 'signed'");
+						$$.typ = $2.typ;
+				}
+			  else if ($1->type == Tuint)
+				switch ($2.typ->type)
+				{ case Tchar:	$$.typ = mkuchar(); break;
+				  case Tshort:	$$.typ = mkushort(); break;
+				  case Tint:	$$.typ = $1; break;
+				  case Tlong:	$$.typ = mkulong(); break;
+				  case Tllong:	$$.typ = mkullong(); break;
+				  default:	semwarn("illegal use of 'unsigned'");
+						$$.typ = $2.typ;
+				}
+			  else if ($1->type == Tlong)
+				switch ($2.typ->type)
+				{ case Tint:	$$.typ = $1; break;
+				  case Tlong:	$$.typ = mkllong(); break;
+				  case Tuint:	$$.typ = mkulong(); break;
+				  case Tulong:	$$.typ = mkullong(); break;
+				  case Tdouble:	$$.typ = mkldouble(); break;
+				  default:	semwarn("illegal use of 'long'");
+						$$.typ = $2.typ;
+				}
+			  else if ($1->type == Tulong)
+				switch ($2.typ->type)
+				{ case Tint:	$$.typ = $1; break;
+				  case Tlong:	$$.typ = mkullong(); break;
+				  case Tuint:	$$.typ = $1; break;
+				  case Tulong:	$$.typ = mkullong(); break;
+				  default:	semwarn("illegal use of 'long'");
+						$$.typ = $2.typ;
+				}
+			  else if ($2.typ->type == Tint)
+				$$.typ = $1;
+			  else
+			  	semwarn("invalid type");
+			  $$.sto = $2.sto;
+			  sp->node = $$;
+			}
+	;
+type	: VOID		{ $$ = mkvoid(); }
+	| BOOL		{ $$ = mkbool(); }
+	| CHAR		{ $$ = mkchar(); }
+	| WCHAR		{ $$ = mkwchart(); }
+	| SHORT		{ $$ = mkshort(); }
+	| INT		{ $$ = mkint(); }
+	| LONG		{ $$ = mklong(); }
+	| LLONG		{ $$ = mkllong(); }
+	| ULLONG	{ $$ = mkullong(); }
+	| SIZE		{ $$ = mkulong(); }
+	| FLOAT		{ $$ = mkfloat(); }
+	| DOUBLE	{ $$ = mkdouble(); }
+	| SIGNED	{ $$ = mkint(); }
+	| UNSIGNED	{ $$ = mkuint(); }
+	| UCHAR		{ $$ = mkuchar(); }
+	| USHORT	{ $$ = mkushort(); }
+	| UINT		{ $$ = mkuint(); }
+	| ULONG		{ $$ = mkulong(); }
+	| TIME		{ $$ = mktimet(); }
+	| TEMPLATE '<' tname id '>' CLASS id
+			{ if (!(p = entry(templatetable, $7)))
+			  {	p = enter(templatetable, $7);
+			  	p->info.typ = mktemplate(NULL, $7);
+			  	$7->token = TYPE;
+			  }
+			  $$ = p->info.typ;
+			}
+	| CLASS '{' s2 decls '}'
+			{ sym = gensym("_Struct");
+			  sprintf(errbuf, "anonymous class will be named '%s'", sym->name);
+			  semwarn(errbuf);
+			  if ((p = entry(classtable, sym)))
+			  {	if (p->info.typ->ref || p->info.typ->type != Tclass)
+				{	sprintf(errbuf, "class '%s' already declared at line %d", sym->name, p->lineno);
+					semerror(errbuf);
+				}
+			  }
+			  else
+			  {	p = enter(classtable, sym);
+				p->info.typ = mkclass((Table*)0, 0);
+			  }
+			  sym->token = TYPE;
+			  sp->table->sym = sym;
+			  p->info.typ->ref = sp->table;
+			  p->info.typ->width = sp->offset;
+			  p->info.typ->id = sym;
+			  $$ = p->info.typ;
+			  exitscope();
+			}
+	| class '{' s2 decls '}'
+			{ p = reenter(classtable, $1->sym);
+			  sp->table->sym = p->sym;
+			  p->info.typ->ref = sp->table;
+			  p->info.typ->width = sp->offset;
+			  p->info.typ->id = p->sym;
+			  if (p->info.typ->base)
+			  	sp->table->prev = (Table*)entry(classtable, p->info.typ->base)->info.typ->ref;
+			  $$ = p->info.typ;
+			  exitscope();
+			}
+	| class ':' base '{' s2 decls '}'
+			{ p = reenter(classtable, $1->sym);
+			  sp->table->sym = p->sym;
+			  if (!$3)
+				semerror("invalid base class");
+			  else
+			  {	sp->table->prev = (Table*)$3->info.typ->ref;
+				if (!sp->table->prev && !$3->info.typ->transient)
+				{	sprintf(errbuf, "class '%s' has incomplete type", $3->sym->name);
+					semerror(errbuf);
+				}
+			  	p->info.typ->base = $3->info.typ->id;
+			  }
+			  p->info.typ->ref = sp->table;
+			  p->info.typ->width = sp->offset;
+			  p->info.typ->id = p->sym;
+			  $$ = p->info.typ;
+			  exitscope();
+			}
+	| class		{ $1->info.typ->id = $1->sym;
+			  $$ = $1->info.typ;
+			}
+	| class ':' base
+			{ if (!$3)
+				semerror("invalid base class");
+			  else
+			  {	if (!$3->info.typ->ref && !$3->info.typ->transient)
+				{	sprintf(errbuf, "class '%s' has incomplete type", $3->sym->name);
+					semerror(errbuf);
+				}
+			  	$1->info.typ->base = $3->info.typ->id;
+			  }
+			  $1->info.typ->id = $1->sym;
+			  $$ = $1->info.typ;
+			}
+	| STRUCT '{' s2 decls '}'
+			{ sym = gensym("_Struct");
+			  sprintf(errbuf, "anonymous struct will be named '%s'", sym->name);
+			  semwarn(errbuf);
+			  if ((p = entry(classtable, sym)))
+			  {	if (p->info.typ->ref || p->info.typ->type != Tstruct)
+				{	sprintf(errbuf, "struct '%s' already declared at line %d", sym->name, p->lineno);
+					semerror(errbuf);
+				}
+				else
+				{	p->info.typ->ref = sp->table;
+					p->info.typ->width = sp->offset;
+				}
+			  }
+			  else
+			  {	p = enter(classtable, sym);
+				p->info.typ = mkstruct(sp->table, sp->offset);
+			  }
+			  p->info.typ->id = sym;
+			  $$ = p->info.typ;
+			  exitscope();
+			}
+	| struct '{' s2 decls '}'
+			{ if ((p = entry(classtable, $1->sym)) && p->info.typ->ref)
+			  {	if (is_mutable(p->info.typ))
+			  	{	if (merge((Table*)p->info.typ->ref, sp->table))
+					{	sprintf(errbuf, "member name clash in struct '%s' declared at line %d", $1->sym->name, p->lineno);
+						semerror(errbuf);
+					}
+			  		p->info.typ->width += sp->offset;
+				}
+			  }
+			  else
+			  {	p = reenter(classtable, $1->sym);
+			  	p->info.typ->ref = sp->table;
+			  	p->info.typ->width = sp->offset;
+			  	p->info.typ->id = p->sym;
+			  }
+			  $$ = p->info.typ;
+			  exitscope();
+			}
+	| STRUCT ID	{ if ((p = entry(classtable, $2)))
+			  {	if (p->info.typ->type == Tstruct)
+			  		$$ = p->info.typ;
+			  	else
+				{	sprintf(errbuf, "'struct %s' redeclaration (line %d)", $2->name, p->lineno);
+			  		semerror(errbuf);
+			  		$$ = mkint();
+				}
+			  }
+			  else
+			  {	p = enter(classtable, $2);
+			  	$$ = p->info.typ = mkstruct((Table*)0, 0);
+				p->info.typ->id = $2;
+			  }
+			}
+	| STRUCT TYPE	{ if ((p = entry(classtable, $2)))
+			  {	if (p->info.typ->type == Tstruct)
+					$$ = p->info.typ;
+			  	else
+				{	sprintf(errbuf, "'struct %s' redeclaration (line %d)", $2->name, p->lineno);
+			  		semerror(errbuf);
+			  		$$ = mkint();
+				}
+			  }
+			  else
+			  {	p = enter(classtable, $2);
+			  	$$ = p->info.typ = mkstruct((Table*)0, 0);
+				p->info.typ->id = $2;
+			  }
+			}
+	| UNION '{' s3 decls '}'
+			{ sym = gensym("_Union");
+			  sprintf(errbuf, "anonymous union will be named '%s'", sym->name);
+			  semwarn(errbuf);
+			  $$ = mkunion(sp->table, sp->offset);
+			  if ((p = entry(classtable, sym)))
+			  {	if (p->info.typ->ref)
+				{	sprintf(errbuf, "union or struct '%s' already declared at line %d", sym->name, p->lineno);
+					semerror(errbuf);
+				}
+				else
+				{	p->info.typ->ref = sp->table;
+					p->info.typ->width = sp->offset;
+				}
+			  }
+			  else
+			  {	p = enter(classtable, sym);
+				p->info.typ = mkunion(sp->table, sp->offset);
+			  }
+			  p->info.typ->id = sym;
+			  $$ = p->info.typ;
+			  exitscope();
+			}
+	| UNION id '{' s3 decls '}'
+			{ if ((p = entry(classtable, $2)))
+			  {	if (p->info.typ->ref || p->info.typ->type != Tunion)
+			  	{	sprintf(errbuf, "union '%s' already declared at line %d", $2->name, p->lineno);
+					semerror(errbuf);
+				}
+				else
+				{	p = reenter(classtable, $2);
+					p->info.typ->ref = sp->table;
+					p->info.typ->width = sp->offset;
+				}
+			  }
+			  else
+			  {	p = enter(classtable, $2);
+				p->info.typ = mkunion(sp->table, sp->offset);
+			  }
+			  p->info.typ->id = $2;
+			  $$ = p->info.typ;
+			  exitscope();
+			}
+	| UNION ID	{ if ((p = entry(classtable, $2)))
+			  {	if (p->info.typ->type == Tunion)
+					$$ = p->info.typ;
+			  	else
+				{	sprintf(errbuf, "'union %s' redeclaration (line %d)", $2->name, p->lineno);
+			  		semerror(errbuf);
+			  		$$ = mkint();
+				}
+			  }
+			  else
+			  {	p = enter(classtable, $2);
+			  	$$ = p->info.typ = mkunion((Table*) 0, 0);
+				p->info.typ->id = $2;
+			  }
+			}
+	| UNION TYPE	{ if ((p = entry(classtable, $2)))
+			  {	if (p->info.typ->type == Tunion)
+					$$ = p->info.typ;
+			  	else
+				{	sprintf(errbuf, "'union %s' redeclaration (line %d)", $2->name, p->lineno);
+			  		semerror(errbuf);
+			  		$$ = mkint();
+				}
+			  }
+			  else
+			  {	p = enter(classtable, $2);
+			  	$$ = p->info.typ = mkunion((Table*) 0, 0);
+				p->info.typ->id = $2;
+			  }
+			}
+	| ENUM '{' s2 dclrs s5 '}'
+			{ sym = gensym("_Enum");
+			  sprintf(errbuf, "anonymous enum will be named '%s'", sym->name);
+			  semwarn(errbuf);
+			  if ((p = entry(enumtable, sym)))
+			  {	if (p->info.typ->ref)
+				{	sprintf(errbuf, "enum '%s' already declared at line %d", sym->name, p->lineno);
+					semerror(errbuf);
+				}
+				else
+				{	p->info.typ->ref = sp->table;
+					p->info.typ->width = 4; /* 4 = enum */
+				}
+			  }
+			  else
+			  {	p = enter(enumtable, sym);
+				p->info.typ = mkenum(sp->table);
+			  }
+			  p->info.typ->id = sym;
+			  $$ = p->info.typ;
+			  exitscope();
+			}
+	| enum '{' s2 dclrs s5 '}'
+			{ if ((p = entry(enumtable, $1->sym)))
+			  {	if (p->info.typ->ref)
+				{	sprintf(errbuf, "enum '%s' already declared at line %d", $1->sym->name, p->lineno);
+					semerror(errbuf);
+				}
+				else
+				{	p->info.typ->ref = sp->table;
+					p->info.typ->width = 4; /* 4 = enum */
+				}
+			  }
+			  else
+			  {	p = enter(enumtable, $1->sym);
+				p->info.typ = mkenum(sp->table);
+			  }
+			  p->info.typ->id = $1->sym;
+			  $$ = p->info.typ;
+			  exitscope();
+			}
+	| ENUM '*' id '{' s4 dclrs s5 '}'
+			{ if ((p = entry(enumtable, $3)))
+			  {	if (p->info.typ->ref)
+				{	sprintf(errbuf, "enum '%s' already declared at line %d", $3->name, p->lineno);
+					semerror(errbuf);
+				}
+				else
+				{	p->info.typ->ref = sp->table;
+					p->info.typ->width = 8; /* 8 = mask */
+				}
+			  }
+			  else
+			  {	p = enter(enumtable, $3);
+				p->info.typ = mkmask(sp->table);
+			  }
+			  p->info.typ->id = $3;
+			  $$ = p->info.typ;
+			  exitscope();
+			}
+	| ENUM ID	{ if ((p = entry(enumtable, $2)))
+			  	$$ = p->info.typ;
+			  else
+			  {	p = enter(enumtable, $2);
+			  	$$ = p->info.typ = mkenum((Table*)0);
+				p->info.typ->id = $2;
+			  }
+			}
+	| ENUM TYPE	{ if ((p = entry(enumtable, $2)))
+				$$ = p->info.typ;
+			  else
+			  {	p = enter(enumtable, $2);
+			  	$$ = p->info.typ = mkenum((Table*)0);
+				p->info.typ->id = $2;
+			  }
+			}
+	| TYPE		{ if ((p = entry(typetable, $1)))
+			  	$$ = p->info.typ;
+			  else if ((p = entry(classtable, $1)))
+			  	$$ = p->info.typ;
+			  else if ((p = entry(enumtable, $1)))
+			  	$$ = p->info.typ;
+			  else if ($1 == lookup("std::string") || $1 == lookup("std::wstring"))
+			  {	p = enter(classtable, $1);
+				$$ = p->info.typ = mkclass((Table*)0, 0);
+			  	p->info.typ->id = $1;
+				if (cflag)
+			  		p->info.typ->transient = 1;	/* make std::string transient in C */
+				else
+			  		p->info.typ->transient = -2;
+			  }
+			  else
+			  {	sprintf(errbuf, "unknown type '%s'", $1->name);
+				semerror(errbuf);
+				$$ = mkint();
+			  }
+			}
+	| TYPE '<' texp '>'
+			{ if ((p = entry(templatetable, $1)))
+			  {	$$ = mktemplate($3.typ, $1);
+			  	if (has_attachment($3.typ))
+				{	sprintf(errbuf, "template type '%s<%s>' of attachment objects may lead to deserialization failures, use '%s<*%s>' instead", $1->name, $3.typ->id ? $3.typ->id->name : "", $1->name, $3.typ->id ? $3.typ->id->name : "");
+					semwarn(errbuf);
+				}
+			  }
+			  else
+			  {	sprintf(errbuf, "invalid template '%s'", $1->name);
+				semerror(errbuf);
+				$$ = mkint();
+			  }
+			}
+	| CLASS error '}'
+			{ synerror("malformed class definition (use spacing around ':' to separate derived : base)");
+			  yyerrok;
+			  $$ = mkint();
+			}
+	| STRUCT error '}'
+			{ synerror("malformed struct definition");
+			  yyerrok;
+			  $$ = mkint();
+			}
+	| UNION error '}'
+			{ synerror("malformed union definition");
+			  yyerrok;
+			  $$ = mkint();
+			}
+	| ENUM error '}'
+			{ synerror("malformed enum definition");
+			  yyerrok;
+			  $$ = mkint();
+			}
+	;
+struct	: STRUCT id	{ if ((p = entry(classtable, $2)))
+			  {	if (p->info.typ->ref)
+			   	{	if (!is_mutable(p->info.typ))
+					{	sprintf(errbuf, "struct '%s' already declared at line %d", $2->name, p->lineno);
+						semerror(errbuf);
+					}
+				}
+				else
+					p = reenter(classtable, $2);
+			  }
+			  else
+			  {	p = enter(classtable, $2);
+				p->info.typ = mkstruct((Table*)0, 0);
+			  }
+			  $$ = p;
+			}
+	;
+class	: CLASS id	{ if ((p = entry(classtable, $2)))
+			  {	if (p->info.typ->ref)
+			   	{	if (!is_mutable(p->info.typ))
+					{	sprintf(errbuf, "class '%s' already declared at line %d (redundant 'class' specifier here?)", $2->name, p->lineno);
+						semerror(errbuf);
+					}
+				}
+				else
+					p = reenter(classtable, $2);
+			  }
+			  else
+			  {	p = enter(classtable, $2);
+				p->info.typ = mkclass((Table*)0, 0);
+				p->info.typ->id = p->sym;
+			  }
+			  $2->token = TYPE;
+			  $$ = p;
+			}
+	;
+enum	: ENUM id	{ if ((p = entry(enumtable, $2)))
+			  {	if (p->info.typ->ref)
+				{	sprintf(errbuf, "enum '%s' already declared at line %d", $2->name, p->lineno);
+					semerror(errbuf);
+				}
+				/*
+				else
+					p = reenter(classtable, $2);
+			  	*/
+			  }
+			  else
+			  {	p = enter(enumtable, $2);
+				p->info.typ = mkenum(0);
+			  }
+			  $$ = p;
+			}
+	;
+tname	: CLASS		{ }
+	| TYPENAME	{ }
+	;
+base	: PROTECTED base{ $$ = $2; }
+	| PRIVATE base	{ $$ = $2; }
+	| PUBLIC base	{ $$ = $2; }
+	| TYPE		{ $$ = entry(classtable, $1);
+			  if (!$$)
+			  {	p = entry(typetable, $1);
+			  	if (p && (p->info.typ->type == Tclass || p->info.typ->type == Tstruct))
+					$$ = p;
+			  }
+			}
+	| STRUCT ID	{ $$ = entry(classtable, $2); }
+	;
+s2	: /* empty */	{ if (transient == -2)
+			  	transient = 0;
+			  permission = 0;
+			  enterscope(mktable(NULL), 0);
+			  sp->entry = NULL;
+			}
+	;
+s3	: /* empty */	{ if (transient == -2)
+			  	transient = 0;
+			  permission = 0;
+			  enterscope(mktable(NULL), 0);
+			  sp->entry = NULL;
+			  sp->grow = False;
+			}
+	;
+s4	: /* empty */	{ enterscope(mktable(NULL), 0);
+			  sp->entry = NULL;
+			  sp->mask = True;
+			  sp->val = 1;
+			}
+	;
+s5	: /* empty */	{ }
+	| ','		{ }
+	;
+s6	: /* empty */	{ if (sp->table->level == INTERNAL)
+			  	transient |= 1;
+			  permission = 0;
+			  enterscope(mktable(NULL), 0);
+			  sp->entry = NULL;
+			  sp->table->level = PARAM;
+			}
+	;
+store	: AUTO		{ $$ = Sauto; }
+	| REGISTER	{ $$ = Sregister; }
+	| STATIC	{ $$ = Sstatic; }
+	| EXPLICIT	{ $$ = Sexplicit; }
+	| EXTERN	{ $$ = Sextern; transient = 1; }
+	| TYPEDEF	{ $$ = Stypedef; }
+	| VIRTUAL	{ $$ = Svirtual; }
+	| CONST		{ $$ = Sconst; }
+	| FRIEND	{ $$ = Sfriend; }
+	| INLINE	{ $$ = Sinline; }
+	| MUSTUNDERSTAND{ $$ = SmustUnderstand; }
+	| RETURN	{ $$ = Sreturn; }
+	| '@'		{ $$ = Sattribute;
+			  if (eflag)
+			   	semwarn("SOAP RPC encoding does not support XML attributes");
+			}
+	| '$'		{ $$ = Sspecial; }
+	| VOLATILE	{ $$ = Sextern; transient = -2; }
+	;
+constobj: /* empty */	{ $$ = Snone; }
+	| CONST		{ $$ = Sconstobj; }
+	;
+abstract: /* empty */	{ $$ = Snone; }
+	| '=' LNG	{ $$ = Sabstract; }
+	;
+virtual : /* empty */	{ $$ = Snone; }
+	| VIRTUAL	{ $$ = Svirtual; }
+	;
+ptrs	: /* empty */	{ $$ = tmp = sp->node; }
+	| ptrs '*'	{ /* handle const pointers, such as const char* */
+			  if ((tmp.sto & Sconst))
+			  	tmp.sto = (Storage)(((int)tmp.sto & ~Sconst) | Sconstptr);
+			  tmp.typ = mkpointer(tmp.typ);
+			  tmp.typ->transient = transient;
+			  $$ = tmp;
+			}
+	| ptrs '&'	{ tmp.typ = mkreference(tmp.typ);
+			  tmp.typ->transient = transient;
+			  $$ = tmp;
+			}
+	;
+array	: /* empty */ 	{ $$ = tmp;	/* tmp is inherited */
+			}
+	| '[' cexp ']' array
+			{ if (!bflag && $4.typ->type == Tchar)
+			  {	sprintf(errbuf, "char[" SOAP_LONG_FORMAT "] will be serialized as an array of " SOAP_LONG_FORMAT " bytes: use soapcpp2 option -b to enable char[] string serialization or use char* for strings", $2.val.i, $2.val.i);
+			  	semwarn(errbuf);
+			  }
+			  if ($2.hasval && $2.typ->type == Tint && $2.val.i > 0 && $4.typ->width > 0)
+				$$.typ = mkarray($4.typ, (int) $2.val.i * $4.typ->width);
+			  else
+			  {	$$.typ = mkarray($4.typ, 0);
+			  	semerror("undetermined array size");
+			  }
+			  $$.sto = $4.sto;
+			}
+	| '[' ']' array	{ $$.typ = mkpointer($3.typ); /* zero size array = pointer */
+			  $$.sto = $3.sto;
+			}
+	;
+arrayck	: array		{ if ($1.typ->type == Tstruct || $1.typ->type == Tclass)
+				if (!$1.typ->ref && !$1.typ->transient && !($1.sto & Stypedef))
+			   	{	sprintf(errbuf, "struct/class '%s' has incomplete type", $1.typ->id->name);
+					semerror(errbuf);
+				}
+			  $$ = $1;
+			}
+	;
+init	: /* empty */   { $$.hasval = False; }
+	| '=' cexp      { if ($2.hasval)
+			  {	$$.typ = $2.typ;
+				$$.hasval = True;
+				$$.val = $2.val;
+			  }
+			  else
+			  {	$$.hasval = False;
+				semerror("initialization expression not constant");
+			  }
+			}
+	;
+tag	: /* empty */	{ $$ = NULL; }
+	| TAG		{ $$ = $1; }
+	;
+occurs	: patt
+			{ $$.minOccurs = -1;
+			  $$.maxOccurs = 1;
+			  $$.minLength = MINLONG64;
+			  $$.maxLength = MAXLONG64;
+			  $$.pattern = $1;
+			}
+	| patt cint
+			{ $$.minOccurs = (LONG64)$2;
+			  $$.maxOccurs = 1;
+			  $$.minLength = (LONG64)$2;
+			  $$.maxLength = MAXLONG64;
+			  $$.pattern = $1;
+			}
+	| patt cint ':'
+			{ $$.minOccurs = (LONG64)$2;
+			  $$.maxOccurs = 1;
+			  $$.minLength = (LONG64)$2;
+			  $$.maxLength = MAXLONG64;
+			  $$.pattern = $1;
+			}
+	| patt cint ':' cint
+			{ $$.minOccurs = (LONG64)$2;
+			  $$.maxOccurs = (LONG64)$4;
+			  $$.minLength = (LONG64)$2;
+			  $$.maxLength = (LONG64)$4;
+			  $$.pattern = $1;
+			}
+	| patt ':' cint
+			{ $$.minOccurs = -1;
+			  $$.maxOccurs = (LONG64)$3;
+			  $$.minLength = MINLONG64;
+			  $$.maxLength = (LONG64)$3;
+			  $$.pattern = $1;
+			}
+	;
+patt	: /* empty */	{ $$ = NULL; }
+	| STR		{ $$ = $1; }
+	;
+cint	: LNG		{ $$ = $1; }
+	| '+' LNG	{ $$ = $2; }
+	| '-' LNG	{ $$ = -$2; }
+	;
+
+/******************************************************************************\
+
+	Expressions
+
+\******************************************************************************/
+
+expr	: expr ',' expr	{ $$ = $3; }
+	| cexp		{ $$ = $1; }
+	;
+/* cexp : conditional expression */
+cexp	: obex '?' qexp ':' cexp
+			{ $$.typ = $3.typ;
+			  $$.sto = Snone;
+			  $$.hasval = False;
+			}
+	| oexp
+	;
+/* qexp : true-branch of ? : conditional expression */
+qexp	: expr		{ $$ = $1; }
+	;
+/* oexp : or-expression */
+oexp	: obex OR aexp	{ $$.hasval = False;
+			  $$.typ = mkint();
+			}
+	| aexp		{ $$ = $1; }
+	;
+obex	: oexp		{ $$ = $1; }
+	;
+/* aexp : and-expression */
+aexp	: abex AN rexp	{ $$.hasval = False;
+			  $$.typ = mkint();
+			}
+	| rexp		{ $$ = $1; }
+	;
+abex	: aexp		{ $$ = $1; }
+	;
+/* rexp : relational expression */
+rexp	: rexp '|' rexp	{ $$ = iop("|", $1, $3); }
+	| rexp '^' rexp	{ $$ = iop("^", $1, $3); }
+	| rexp '&' rexp	{ $$ = iop("&", $1, $3); }
+	| rexp EQ  rexp	{ $$ = relop("==", $1, $3); }
+	| rexp NE  rexp	{ $$ = relop("!=", $1, $3); }
+	| rexp '<' rexp	{ $$ = relop("<", $1, $3); }
+	| rexp LE  rexp	{ $$ = relop("<=", $1, $3); }
+	| rexp '>' rexp	{ $$ = relop(">", $1, $3); }
+	| rexp GE  rexp	{ $$ = relop(">=", $1, $3); }
+	| rexp LS  rexp	{ $$ = iop("<<", $1, $3); }
+	| rexp RS  rexp	{ $$ = iop(">>", $1, $3); }
+	| rexp '+' rexp	{ $$ = op("+", $1, $3); }
+	| rexp '-' rexp	{ $$ = op("-", $1, $3); }
+	| rexp '*' rexp	{ $$ = op("*", $1, $3); }
+	| rexp '/' rexp	{ $$ = op("/", $1, $3); }
+	| rexp '%' rexp	{ $$ = iop("%", $1, $3); }
+	| lexp		{ $$ = $1; }
+	;
+/* lexp : lvalue kind of expression with optional prefix contructs */
+lexp	: '!' lexp	{ if ($2.hasval)
+				$$.val.i = !$2.val.i;
+			  $$.typ = $2.typ;
+			  $$.hasval = $2.hasval;
+			}
+	| '~' lexp	{ if ($2.hasval)
+				$$.val.i = ~$2.val.i;
+			  $$.typ = $2.typ;
+			  $$.hasval = $2.hasval;
+			}
+	| '-' lexp	{ if ($2.hasval) {
+				if (integer($2.typ))
+					$$.val.i = -$2.val.i;
+				else if (real($2.typ))
+					$$.val.r = -$2.val.r;
+				else	typerror("string?");
+			  }
+			  $$.typ = $2.typ;
+			  $$.hasval = $2.hasval;
+			}
+	| '+' lexp	{ $$ = $2; }
+	| '*' lexp	{ if ($2.typ->type == Tpointer) {
+			  	$$.typ = (Tnode*)$2.typ->ref;
+			  } else
+			  	typerror("dereference of non-pointer type");
+			  $$.sto = Snone;
+			  $$.hasval = False;
+			}
+	| '&' lexp	{ $$.typ = mkpointer($2.typ);
+			  $$.sto = Snone;
+			  $$.hasval = False;
+			}
+	| SIZEOF '(' texp ')'
+			{ $$.hasval = True;
+			  $$.typ = mkint();
+			  $$.val.i = $3.typ->width;
+			}
+	| pexp		{ $$ = $1; }
+	;
+/* pexp : primitive expression with optional postfix constructs */
+pexp	: '(' expr ')'	{ $$ = $2; }
+	| ID		{ if ((p = enumentry($1)) == (Entry*) 0)
+				p = undefined($1);
+			  else
+			  	$$.hasval = True;
+			  $$.typ = p->info.typ;
+			  $$.val = p->info.val;
+			}
+	| LNG		{ $$.typ = mkint();
+			  $$.hasval = True;
+			  $$.val.i = $1;
+			}
+	| null		{ $$.typ = mkint();
+			  $$.hasval = True;
+			  $$.val.i = 0;
+			}
+	| DBL		{ $$.typ = mkfloat();
+			  $$.hasval = True;
+			  $$.val.r = $1;
+			}
+	| CHR		{ $$.typ = mkchar();
+			  $$.hasval = True;
+			  $$.val.i = $1;
+			}
+	| STR		{ $$.typ = mkstring();
+			  $$.hasval = True;
+			  $$.val.s = $1;
+			}
+	| CFALSE	{ $$.typ = mkbool();
+			  $$.hasval = True;
+			  $$.val.i = 0;
+			}
+	| CTRUE		{ $$.typ = mkbool();
+			  $$.hasval = True;
+			  $$.val.i = 1;
+			}
+	;
+
+%%
+
+/*
+ * ???
+ */
+int
+yywrap(void)
+{	return 1;
+}
+
+/******************************************************************************\
+
+	Support routines
+
+\******************************************************************************/
+
+static Node
+op(const char *op, Node p, Node q)
+{	Node	r;
+	Tnode	*typ;
+	r.typ = p.typ;
+	r.sto = Snone;
+	if (p.hasval && q.hasval) {
+		if (integer(p.typ) && integer(q.typ))
+			switch (op[0]) {
+			case '|':	r.val.i = p.val.i |  q.val.i; break;
+			case '^':	r.val.i = p.val.i ^  q.val.i; break;
+			case '&':	r.val.i = p.val.i &  q.val.i; break;
+			case '<':	r.val.i = p.val.i << q.val.i; break;
+			case '>':	r.val.i = p.val.i >> q.val.i; break;
+			case '+':	r.val.i = p.val.i +  q.val.i; break;
+			case '-':	r.val.i = p.val.i -  q.val.i; break;
+			case '*':	r.val.i = p.val.i *  q.val.i; break;
+			case '/':	r.val.i = p.val.i /  q.val.i; break;
+			case '%':	r.val.i = p.val.i %  q.val.i; break;
+			default:	typerror(op);
+			}
+		else if (real(p.typ) && real(q.typ))
+			switch (op[0]) {
+			case '+':	r.val.r = p.val.r + q.val.r; break;
+			case '-':	r.val.r = p.val.r - q.val.r; break;
+			case '*':	r.val.r = p.val.r * q.val.r; break;
+			case '/':	r.val.r = p.val.r / q.val.r; break;
+			default:	typerror(op);
+			}
+		else	semerror("illegal constant operation");
+		r.hasval = True;
+	} else {
+		typ = mgtype(p.typ, q.typ);
+		r.hasval = False;
+	}
+	return r;
+}
+
+static Node
+iop(const char *iop, Node p, Node q)
+{	if (integer(p.typ) && integer(q.typ))
+		return op(iop, p, q);
+	typerror("integer operands only");
+	return p;
+}
+
+static Node
+relop(const char *op, Node p, Node q)
+{	Node	r;
+	Tnode	*typ;
+	r.typ = mkint();
+	r.sto = Snone;
+	r.hasval = False;
+	if (p.typ->type != Tpointer || p.typ != q.typ)
+		typ = mgtype(p.typ, q.typ);
+	return r;
+}
+
+/******************************************************************************\
+
+	Scope management
+
+\******************************************************************************/
+
+/*
+mkscope - initialize scope stack with a new table and offset
+*/
+static void
+mkscope(Table *table, int offset)
+{	sp = stack-1;
+	enterscope(table, offset);
+}
+
+/*
+enterscope - enter a new scope by pushing a new table and offset on the stack
+*/
+static void
+enterscope(Table *table, int offset)
+{	if (++sp == stack+MAXNEST)
+		execerror("maximum scope depth exceeded");
+	sp->table = table;
+	sp->val = 0;
+	sp->offset = offset;
+	sp->grow = True;	/* by default, offset grows */
+	sp->mask = False;
+}
+
+/*
+exitscope - exit a scope by popping the table and offset from the stack
+*/
+static void
+exitscope(void)
+{	check(sp-- != stack, "exitscope() has no matching enterscope()");
+}
+
+/******************************************************************************\
+
+	Undefined symbol
+
+\******************************************************************************/
+
+static Entry*
+undefined(Symbol *sym)
+{	Entry	*p;
+	sprintf(errbuf, "undefined identifier '%s'", sym->name);
+	semwarn(errbuf);
+	p = enter(sp->table, sym);
+	p->level = GLOBAL;
+	p->info.typ = mkint();
+	p->info.sto = Sextern;
+	p->info.hasval = False;
+	return p;
+}
+
+/*
+mgtype - return most general type among two numerical types
+*/
+Tnode*
+mgtype(Tnode *typ1, Tnode *typ2)
+{	if (numeric(typ1) && numeric(typ2)) {
+		if (typ1->type < typ2->type)
+			return typ2;
+	} else	typerror("non-numeric type");
+	return typ1;
+}
+
+/******************************************************************************\
+
+	Type checks
+
+\******************************************************************************/
+
+static int
+integer(Tnode *typ)
+{	switch (typ->type) {
+	case Tchar:
+	case Tshort:
+	case Tint:
+	case Tlong:	return True;
+	default:	break;
+	}
+	return False;
+}
+
+static int
+real(Tnode *typ)
+{	switch (typ->type) {
+	case Tfloat:
+	case Tdouble:
+	case Tldouble:	return True;
+	default:	break;
+	}
+	return False;
+}
+
+static int
+numeric(Tnode *typ)
+{	return integer(typ) || real(typ);
+}
+
+static void
+add_fault(Table *gt)
+{ Table *t;
+  Entry *p1, *p2, *p3, *p4;
+  Symbol *s1, *s2, *s3, *s4;
+  imported = NULL;
+  s1 = lookup("SOAP_ENV__Code");
+  p1 = entry(classtable, s1);
+  if (!p1 || !p1->info.typ->ref)
+  { t = mktable((Table*)0);
+    if (!p1)
+    { p1 = enter(classtable, s1);
+      p1->info.typ = mkstruct(t, 3*4);
+      p1->info.typ->id = s1;
+    }
+    else
+      p1->info.typ->ref = t;
+    p2 = enter(t, lookup("SOAP_ENV__Value"));
+    p2->info.typ = qname;
+    p2->info.minOccurs = 0;
+    p2 = enter(t, lookup("SOAP_ENV__Subcode"));
+    p2->info.typ = mkpointer(p1->info.typ);
+    p2->info.minOccurs = 0;
+  }
+  s2 = lookup("SOAP_ENV__Detail");
+  p2 = entry(classtable, s2);
+  if (!p2 || !p2->info.typ->ref)
+  { t = mktable((Table*)0);
+    if (!p2)
+    { p2 = enter(classtable, s2);
+      p2->info.typ = mkstruct(t, 3*4);
+      p2->info.typ->id = s2;
+    }
+    else
+      p2->info.typ->ref = t;
+    p3 = enter(t, lookup("__any"));
+    p3->info.typ = xml;
+    p3->info.minOccurs = 0;
+    p3 = enter(t, lookup("__type"));
+    p3->info.typ = mkint();
+    p3->info.minOccurs = 0;
+    p3 = enter(t, lookup("fault"));
+    p3->info.typ = mkpointer(mkvoid());
+    p3->info.minOccurs = 0;
+    custom_fault = 0;
+  }
+  s4 = lookup("SOAP_ENV__Reason");
+  p4 = entry(classtable, s4);
+  if (!p4 || !p4->info.typ->ref)
+  { t = mktable((Table*)0);
+    if (!p4)
+    { p4 = enter(classtable, s4);
+      p4->info.typ = mkstruct(t, 4);
+      p4->info.typ->id = s4;
+    }
+    else
+      p4->info.typ->ref = t;
+    p3 = enter(t, lookup("SOAP_ENV__Text"));
+    p3->info.typ = mkstring();
+    p3->info.minOccurs = 0;
+  }
+  s3 = lookup("SOAP_ENV__Fault");
+  p3 = entry(classtable, s3);
+  if (!p3 || !p3->info.typ->ref)
+  { t = mktable(NULL);
+    if (!p3)
+    { p3 = enter(classtable, s3);
+      p3->info.typ = mkstruct(t, 9*4);
+      p3->info.typ->id = s3;
+    }
+    else
+      p3->info.typ->ref = t;
+    p3 = enter(t, lookup("faultcode"));
+    p3->info.typ = qname;
+    p3->info.minOccurs = 0;
+    p3 = enter(t, lookup("faultstring"));
+    p3->info.typ = mkstring();
+    p3->info.minOccurs = 0;
+    p3 = enter(t, lookup("faultactor"));
+    p3->info.typ = mkstring();
+    p3->info.minOccurs = 0;
+    p3 = enter(t, lookup("detail"));
+    p3->info.typ = mkpointer(p2->info.typ);
+    p3->info.minOccurs = 0;
+    p3 = enter(t, s1);
+    p3->info.typ = mkpointer(p1->info.typ);
+    p3->info.minOccurs = 0;
+    p3 = enter(t, s4);
+    p3->info.typ = mkpointer(p4->info.typ);
+    p3->info.minOccurs = 0;
+    p3 = enter(t, lookup("SOAP_ENV__Node"));
+    p3->info.typ = mkstring();
+    p3->info.minOccurs = 0;
+    p3 = enter(t, lookup("SOAP_ENV__Role"));
+    p3->info.typ = mkstring();
+    p3->info.minOccurs = 0;
+    p3 = enter(t, lookup("SOAP_ENV__Detail"));
+    p3->info.typ = mkpointer(p2->info.typ);
+    p3->info.minOccurs = 0;
+  }
+}
+
+static void
+add_soap(void)
+{ Symbol *s = lookup("soap");
+  p = enter(classtable, s);
+  p->info.typ = mkstruct(NULL, 0);
+  p->info.typ->transient = -2;
+  p->info.typ->id = s;
+}
+
+static void
+add_XML(void)
+{ Symbol *s = lookup("_XML");
+  s->token = TYPE;
+  p = enter(typetable, s);
+  xml = p->info.typ = mksymtype(mkstring(), s);
+  p->info.sto = Stypedef;
+}
+
+static void
+add_qname(void)
+{ Symbol *s = lookup("_QName");
+  s->token = TYPE;
+  p = enter(typetable, s);
+  qname = p->info.typ = mksymtype(mkstring(), s);
+  p->info.sto = Stypedef;
+}
+
+static void
+add_header(Table *gt)
+{ Table *t;
+  Entry *p;
+  Symbol *s = lookup("SOAP_ENV__Header");
+  imported = NULL;
+  p = entry(classtable, s);
+  if (!p || !p->info.typ->ref)
+  { t = mktable((Table*)0);
+    if (!p)
+      p = enter(classtable, s);
+    p->info.typ = mkstruct(t, 0);
+    p->info.typ->id = s;
+    custom_header = 0;
+  }
+}
+
+static void
+add_response(Entry *fun, Entry *ret)
+{ Table *t;
+  Entry *p, *q;
+  Symbol *s;
+  size_t i = 0, j, n = strlen(fun->sym->name);
+  char *r = (char*)emalloc(n+100);
+  strcpy(r, fun->sym->name);
+  strcat(r, "Response");
+  do
+  { for (j = 0; j < i; j++)
+      r[n+j+8] = '_';
+    r[n+i+8] = '\0';
+    if (!(s = lookup(r)))
+      s = install(r, ID);
+    i++;
+  } while (entry(classtable, s));
+  free(r);
+  t = mktable((Table*)0);
+  q = enter(t, ret->sym);
+  q->info = ret->info;
+  if (q->info.typ->type == Treference)
+    q->info.typ = (Tnode*)q->info.typ->ref;
+  p = enter(classtable, s);
+  p->info.typ = mkstruct(t, 4);
+  p->info.typ->id = s;
+  fun->info.typ->response = p;
+}
+
+static void
+add_result(Tnode *typ)
+{ Entry *p;
+  if (!typ->ref || !((Tnode*)typ->ref)->ref)
+  { semwarn("response struct/class must be declared before used in function prototype");
+    return;
+  }
+  for (p = ((Table*)((Tnode*)typ->ref)->ref)->list; p; p = p->next)
+    if (p->info.sto & Sreturn)
+      return;
+  for (p = ((Table*)((Tnode*)typ->ref)->ref)->list; p; p = p->next)
+  { if (p->info.typ->type != Tfun && !(p->info.sto & Sattribute) && !is_transient(p->info.typ) && !(p->info.sto & (Sprivate|Sprotected)))
+      p->info.sto = (Storage)((int)p->info.sto | (int)Sreturn);
+      return;
+  }
+}