/*  $Id: deco.cc,v 1.100 1998/07/12 03:58:49 shaggy Exp $
 *      Dynamic Encapsulator of C++ Objects
 *
 *      relative path: ./lib/cparse
 *  
 *      Shag/OS: an Object-Oriented MicroKernel Operating System
 *       Libraries
 *        C/C++ Parsing classes
 *  
 *  Copyright (c) 1996,1997 Frank E. Barrus.  All rights reserved.
 *  
 *  This file is part of the Shag/OS distribution, and may be freely used and
 *  distributed by the terms of the ShagWare General Public License (SGPL),
 *  version 2, or, at your option, any newer official version of that License.
 *  
 *  THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY, EXPRESS OR IMPLIED.
 *  THE AUTHOR SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF ANY KIND,
 *  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 *  FITNESS FOR A PARTICULAR PURPOSE, AND WILL NOT BE LIABLE FOR ANY DAMAGES.
 *  For more warranty details, see the ShagWare General Public License, which
 *  should have been received with this program, in a file called 'lic_spl2.txt'
 *  If this license is missing, please contact the author and describe how
 *  this software was obtained.  Email: shaggy@mail.csh.rit.edu
 *  
 *  Complete, unaltered, original distributions of this and related code
 *  may be obtained from:   http://www.csh.rit.edu/~shaggy/software.html
 *  or:	ftp://ftp.csh.rit.edu/pub/csh/shaggy
 */

//  $Log: deco.cc,v $
//  Revision 1.100  1998/07/12 03:58:49  shaggy
//  lots of const fixes, -frealtype option, -dT option; better debugging
//
//  Revision 1.99  1998/07/11 23:06:52  shaggy
//  fixed labels and goto
//
//  Revision 1.98  1998/07/11 19:03:04  shaggy
//  declarations in for() work now (even multiple decls)
//
//  Revision 1.97  1998/07/09 04:53:30  shaggy
//  more fn matching fixes
//
//  Revision 1.96  1998/07/08 21:45:04  shaggy
//  lots of enhancements for function call matching
//
//  Revision 1.95  1998/07/08 13:42:43  shaggy
//  started adding proper function calls
//
//  Revision 1.94  1998/07/07 13:41:42  shaggy
//  enum cleanup, and can define types inside functions now
//
//  Revision 1.93  1998/07/07 02:51:15  shaggy
//  more fixes for declaration ordering
//
//  Revision 1.92  1998/07/06 22:49:43  shaggy
//  reworked symtab, output ordering, etc
//
//  Revision 1.91  1998/07/06 13:39:09  shaggy
//  many more changes/fixes for first pre-release
//
//  Revision 1.90  1998/07/06 05:01:28  shaggy
//  prep for pre-release
//
//  Revision 1.89  1998/07/05 20:32:57  shaggy
//  tons of bugfixes, enhancements and parser improvements
//
//  Revision 1.88  1998/07/05 02:47:39  shaggy
//  tons of bug fixes
//
//  Revision 1.87  1998/07/04 20:39:01  shaggy
//  lots of bug fixes; fn, aggr, etc improvements, new/delete
//
//  Revision 1.86  1998/07/04 01:23:31  shaggy
//  anonymous unions work now
//
//  Revision 1.85  1998/07/03 19:06:26  shaggy
//  changed scope semantics; fn name is available in scope now
//
//  Revision 1.84  1998/07/03 16:53:53  shaggy
//  new Scope wrapper class, and dcast_XXX functions eliminate lots of casts
//
//  Revision 1.83  1998/07/02 13:25:53  shaggy
//  public/private/protected works for inheritance
//
//  Revision 1.82  1998/07/01 13:14:32  shaggy
//  handles default code for dtype member fns
//
//  Revision 1.81  1998/06/30 18:19:02  shaggy
//  more fixes for member pointers
//
//  Revision 1.80  1998/06/30 15:38:38  shaggy
//  better support for member fns and member fn ptrs
//
//  Revision 1.79  1998/06/30 13:23:33  shaggy
//  fcall fixes for d++ conv
//
//  Revision 1.78  1998/06/30 12:22:32  shaggy
//  many fixes for "this"
//
//  Revision 1.77  1998/06/29 14:14:18  shaggy
//  more DECO conversions, support for 'this', etc
//
//  Revision 1.76  1998/06/29 01:36:56  shaggy
//  fixed inline attr, output of pure virtuals, etc...
//
//  Revision 1.75  1998/06/07 18:09:05  shaggy
//  added types to Expr()'s, and fixed output of members (. and ->)
//
//  Revision 1.74  1998/06/07 15:45:13  shaggy
//  realtype() works, and . and -> now work
//
//  Revision 1.73  1998/06/04 13:40:39  shaggy
//  more fixes
//
//  Revision 1.72  1998/06/04 02:28:10  shaggy
//  various config updates
//
//  Revision 1.71  1998/06/03 13:39:14  shaggy
//  function re-parsing almost works (for within classes)
//
//  Revision 1.70  1998/06/02 14:50:43  shaggy
//  in the midst of more full parsing updates
//
//  Revision 1.69  1998/06/01 13:29:00  shaggy
//  switchable code save/parse
//
//  Revision 1.68  1998/06/01 02:10:36  shaggy
//  more code parsing working
//
//  Revision 1.67  1998/05/26 21:34:17  shaggy
//  C/C++/DC++ header formats now supported
//  header output includes external requirements
//
//  Revision 1.66  1998/05/26 16:40:52  shaggy
//  fixed and cleaned up config
//
//  Revision 1.65  1998/05/26 13:40:15  shaggy
//  many more fixes
//
//  Revision 1.64  1998/05/26 05:02:55  shaggy
//  parameter scoping works correctly
//  most basic C/C++ statements now work
//
//  Revision 1.63  1998/05/26 01:39:47  shaggy
//  params almost working right
//
//  Revision 1.62  1998/05/25 23:28:46  shaggy
//  major rework of func/param info
//
//  Revision 1.61  1998/05/25 20:08:42  shaggy
//  more bugfixes
//
//  Revision 1.60  1998/05/25 04:22:15  shaggy
//  enough for tonight
//
//  Revision 1.59  1998/05/24 23:39:34  shaggy
//  various bug fixes and code cleanup
//
//  Revision 1.58  1998/05/24 18:42:01  shaggy
//  improved regression testing
//  proper scoping for initializers
//  decls available for their initializer
//
//  Revision 1.57  1998/05/24 01:41:43  shaggy
//  internal support for most statements
//
//  Revision 1.56  1998/05/20 14:41:25  shaggy
//  better code output
//
//  Revision 1.55  1998/05/19 14:55:37  shaggy
//  a bit closer to generating code (stmtlists work)
//
//  Revision 1.54  1998/05/18 13:22:37  shaggy
//  more decl changes
//
//  Revision 1.53  1998/05/18 05:24:29  shaggy
//  more cleanup of declaration support functions
//
//  Revision 1.52  1998/05/17 23:14:59  shaggy
//  expressions, expression lists, etc
//
//  Revision 1.51  1998/05/16 23:30:45  shaggy
//  added CTOR_ID and some rules to help fix ctor ambiguity problem
//  output turned off for #include'ed files
//  better error checking, and ability to list locations
//  of symbol declaration and definition
//
//  Revision 1.50  1998/05/15 07:03:38  shaggy
//  dependency ordering fixed; constructor initializers; lots of bug fixes, etc
//
//  Revision 1.49  1998/05/15 00:17:28  shaggy
//  ctor initializers work, and enums are fixed so declarations
//  in an enum can be immediately re-used
//
//  Revision 1.48  1998/05/14 18:02:05  shaggy
//  fixed constant handling, enums, function cv tags, etc
//
//  Revision 1.47  1998/05/14 05:08:43  shaggy
//  inheritence works and lots of bugs fixed
//
//  Revision 1.46  1998/05/13 21:39:37  shaggy
//  switched to dtype/ptype/rtype
//
//  Revision 1.45  1998/05/13 21:02:50  shaggy
//  deinitions/initializations work better now
//
//  Revision 1.44  1998/05/13 00:21:36  shaggy
//  fixed enums (mostly)
//
//  Revision 1.43  1998/05/12 22:40:26  shaggy
//  scoping FINALLY works right (or seems to)
//
//  Revision 1.42  1998/05/12 20:14:10  shaggy
//  scoping works except for output with nested dependencies
//
//  Revision 1.41  1998/05/12 15:53:22  shaggy
//  scoping and class tags almost work
//
//  Revision 1.39  1998/05/10 18:53:42  shaggy
//  just about ready to start fixing scoping
//
//  Revision 1.38  1998/05/10 00:03:33  shaggy
//  string concatenation works, and comments are being
//  added to get ready to fix scoping
//
//  Revision 1.37  1998/05/08 13:03:24  shaggy
//  fixed const/volatile
//
//  Revision 1.36  1998/05/07 14:46:40  shaggy
//  const/volatile partially working
//
//  Revision 1.35  1998/05/05 13:00:42  shaggy
//  better function parameter matching
//
//  Revision 1.34  1998/05/04 04:39:50  shaggy
//  first stage of function overloading
//
//  Revision 1.33  1998/05/04 03:09:21  shaggy
//  support for ctors, dtors, conversion operators, operator overloading, etc
//
//  Revision 1.32  1998/05/03 17:07:30  shaggy
//  more improvements to make internal declarations easier
//
//  Revision 1.31  1998/05/03 02:47:08  shaggy
//  added inheritence to parser
//
//  Revision 1.30  1998/04/27 04:36:49  shaggy
//  started auto-generation of code
//
//  Revision 1.29  1998/04/26 15:21:23  shaggy
//  better implicit int handling
//
//  Revision 1.28  1998/04/26 14:58:52  shaggy
//  fixed to output standard C++ types (instead of int32,etc)
//
//  Revision 1.27  1998/04/26 14:29:27  shaggy
//  better error output
//
//  Revision 1.26  1998/04/26 14:07:03  shaggy
//  better error handling
//
//  Revision 1.25  1998/04/23 20:11:41  shaggy
//  more changes for Solaris
//
//  Revision 1.24  1998/04/23 13:47:51  shaggy
//  changes to make enums and constants work better
//
//  Revision 1.23  1998/04/23 12:32:57  shaggy
//  more portability fixes
//
//  Revision 1.22  1998/04/22 13:06:40  shaggy
//  better enum handling
//
//  Revision 1.21  1998/04/22 12:17:51  shaggy
//  changed ID to Id for consistency
//
//  Revision 1.20  1998/04/17 18:02:04  shaggy
//  more stuff to make it build under Solaris
//
//  Revision 1.19  1998/04/16 13:35:33  shaggy
//  access control
//
//  Revision 1.18  1998/04/15 14:47:58  shaggy
//  array output works
//
//  Revision 1.17  1998/04/15 13:39:57  shaggy
//  added types to constants
//
//  Revision 1.16  1998/04/14 03:57:09  shaggy
//  forward reference support
//
//  Revision 1.15  1998/04/13 01:30:23  shaggy
//  even better C++ output
//
//  Revision 1.14  1998/04/12 18:38:08  shaggy
//  improved code output
//
//  Revision 1.13  1998/04/12 03:19:35  shaggy
//  got function output working
//
//  Revision 1.12  1998/04/11 21:48:42  shaggy
//  added some simple regurgitation
//
//  Revision 1.11  1998/04/11 01:47:41  shaggy
//  deco uses cpp
//  tests fixed for dc++
//  dc++ grammar fixed
//
//  Revision 1.10  1998/04/07 13:38:20  shaggy
//  fixed up enums some more
//
//  Revision 1.9  1998/04/06 14:27:43  shaggy
//  lots more changes, and added some tests
//
//  Revision 1.8  1998/03/23 14:17:50  shaggy
//  more updates
//
//  Revision 1.7  1998/02/11 04:33:44  shaggy
//  fixed to handle joining type lists
//
//  Revision 1.6  1998/02/09 00:51:57  shaggy
//  *** empty log message ***
//
//  Revision 1.5  1998/02/08 23:41:58  shaggy
//  fixed dependency ordering of output
//
//  Revision 1.4  1998/02/08 19:07:05  shaggy
//  about to start splitting up everything
//
//  Revision 1.3  1998/01/24 15:41:17  shaggy
//  old style of pointing to decls
//
//  Revision 1.2  1998/01/12 04:12:48  shaggy
//  got dependencies working when re-generating code
//
//  Revision 1.1  1998/01/11 05:06:33  shaggy
//  Initial revision
//
//  Revision 1.10  1997/12/09 16:41:20  shaggy
//  param decls fixed
//
//  Revision 1.9  1997/12/09 15:23:05  shaggy
//  *** empty log message ***
//
//  Revision 1.8  1997/12/08 03:50:56  shaggy
//  *** empty log message ***
//
//  Revision 1.7  1996/11/29  18:51:25  shaggy
//  last checkin before removing unnecessary 'List' class
//
//  Revision 1.6  1996/11/23  17:22:39  shaggy
//  last checkin before switching to virtual functions
//
//  Revision 1.5  1996/10/19  21:47:53  shaggy
//  modified to handle new token location
//
//  Revision 1.4  1996/10/19  16:41:24  shaggy
//  supports abstract parameter declarations
//
//  Revision 1.3  1996/10/18  16:37:19  shaggy
//  basic type declarations (including functions) work
//
//  Revision 1.2  1996/10/18  00:53:16  shaggy
//  added ptrs, arrays, basic types
//
//  Revision 1.1  1996/10/06  20:33:57  shaggy
//  Initial revision
//
//      last modified: Fri May 24 00:51:49 1996
//      creation date: Mon Mar  4 00:35:59 1996


#define deco_VER_MAJOR	0
#define deco_VER_MINOR	6
#define deco_VER_PATCH	0
#define deco_VER_STR	"0.6"
#define deco_VER_DATE	"July 05, 1998"
#define deco_VER_NDATE	"07/05/98"

// #pragma Make Config_Needed += local
#pragma Make Includes_Banned += -nostdinc -nostdinc++
#pragma Make Flags_Banned += -static

#define DEBUG

#define USE_STDLIB

#include <sys/types.h>
#include <defines>
#include <unistd.h>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <fcntl.h>

#include <CObj>
#include <CLex>
#include <CParse>
#include <CAction>
#include <StrTab>
#include <SymTab>
#include <ParsePath>

#include <posix/assert.h>

#include "ccint.h"
#include "ccout.h"

static bool debugaction = false;
#include "printaction.h"


#define INTERNAL_ERROR  { internal_error(__FILE__, __LINE__, __FUNCTION__); \
			  for(;;); }

#include "decoconf.h"


// XXX - ideas:
//	- extend Item structure by making it a class that can
//	  be derived from and have methods added to
//	- extend Token by putting base definition in a file that can
//	  be included and then have more entries added to
//	- make sure specific/generic print routines are available for it 
//	  too

// XXX - at some point, any knowledge specific to certain data
// types should be removed from DECO, since it is supposed to be 
// a generic C++ pre-processor that generates code that can compile
// on any architecture. 


// Need to clean up use of Item by providing a local
// Item class that has a separate structure for each token ID,
// so that casting won't be necessary and the expected layout for
// each token is clear and can be checked for errors


typedef vStrTab::Id Id;

class Deco : public CAction {
private:
	const int _bufsize = 4096;
	char _buf[_bufsize];
	int _temp;
	int _input_fd;
	int _anon;
	bool _cxx, _dcxx, _hdr;
	bool _realtypes;

	Id _id_this;
	Id _id__dcobj;
	Id _id_BTRef, _id_Obj, _id__DCBind; 
	Id _id_instance, _id_dtobj, _id_dataclass;
	Id _id_dbind, _id_dcbind;
	Id _id_o, _id_btrp;
	Id _id_D_Type, _id_D_Class;
	Id _id__objp;
	Id _id__itabp;
public:
	CCOut ccout;
	bool _notedecl, _notedef;

	Deco(CLex &clp, vStrTab &strtab, vSymTab &symtab);
	bool open_input(csz *fname = 0, csz *cpp = 0);
	void close_input();
	void get_input(bool split_token);
	Id string_id(Token, csz *str);
	void abort_exit();
	void internal_error_exit();
	void veprintf(csz *fmt, va_list args);
	void realtypes(bool tf=true) { _realtypes = tf; }
	int new_temp() { return ++_temp; }
	int new_temp(Item *r)
		{ r->token = TEMP; return (r->ival = ++_temp); }
	Id new_anon(Scope, csz *tname);

	// void print_type(const type_t type);
	// sz* type_string(const type_t type, sz *s);

// these should be part of CAction, but are here for testing:
	type_t decl_type(Item_ item, bool impwarn = true);
	type_t decl_type(Item *r, Item_ item, bool impwarn = true);
	int type_sizeof(type_t type);
	int type_alignment(type_t type);

	void print_syms()
		{ Scope s(&_global_scope);
		  ccout.print_decls(s);
		  ccout.print_defs(s);
		  if(_hdr) ccout.print_reqs(s); }

	void convert();
	void convert_dtype(Decl *decl, Scope);
	void convert_dclass(Decl *decl, Scope);
	void convert_scope(Scope);
	void predefine();
	Decl *forwref(TDecl::DataType type, Id id, Scope = 0);
	type_t declref(Id id, Scope = 0);
	static Token decltoken(Decl *decl);

	vSymTab* get_symtab(const Decl *scope);
	vSymTab* get_symtab(Item_ scope);
	vSymTab* get_symtab(Item_ id, Item_ scope);
	action Literal;
	action UnaryOp, BinaryOp, CondExpr, OpNew;
	action DeclSym, DeclParam;
	action DeclPtr, DeclArray, DeclFunc;
	action SetAttr;
	action Define, DefParam;
	action BuildTypeList, BuildParamList;
	action AggrRef, EnumRef;
	action NewScope, FuncScope, SetAccess, Inherit;
	action EndAggr;
	action NewEnum, EnumNext;
	action FindSym, SpecialId, SpecialStr;

	action BuildExprList, BuildStmtList, BuildArray;
	action Cast, Call, CtorCall, Goto;
	action CtorInit;

	action IfElse, While, DoWhile, For, ForExpr, Switch;
	action Label, Return;

	action Undeclared;

	long check_repeat(long a, long b, csz *name);

	void parse_dcxx()
		{ _hdr = false; _dcxx = _cxx = true; _clex.parse_dcxx(); }
	void parse_dhxx()
		{ _hdr = _dcxx = _cxx = true; _clex.parse_dcxx(); }
	void parse_cxx()
		{ _hdr = _dcxx = false; _cxx = true; _clex.parse_cxx(); }
	void parse_hxx()
		{ _hdr = _cxx = true; _dcxx = false; _clex.parse_cxx(); }
	void parse_c()
		{ _hdr = _dcxx = _cxx = false; _clex.parse_c(); }
	void parse_h()
		{ _hdr = true; _dcxx = _cxx = false; _clex.parse_c(); }

	/*
	type_t type_ptrto(type_t t);
	type_t type_refto(type_t t);
	type_t type_declref(Decl *d);
	type_t type_array(type_t t, int size);
	type_t type_aggr(Id id, Scope);
	type_t type_func(type_t t, FuncParam *fp);
	*/
	Decl *newdecl(Id id, Scope parent,
			TDecl::DataType = TDecl::dt_default, bool err = true);
	Decl *newdecl(Id id, Scope parent, type_t type,
			TDecl::DataType = TDecl::dt_default, bool err = true);
	Decl *newdecl_item(Item_ type, Item_ sym, Item_ scope, bool err);
	sz* scope_string(Scope, sz *s);
	csz* printable_scope_string(Scope);
	void error_id(csz *what, Id id, Scope, bool showtype);
	void error_dupdecl(Id id, Scope s)
		{ error_id("already declared", id, s, true); }
	void error_dupdef(Id id, Scope s)
		{ error_id("already defined", id, s, true); }
	void error_dupinit(Id id, Scope s)
		{ error_id("already initialized", id, s, false); }
	void error_notdecl(Id id, Scope s)
		{ error_id("not declared", id, s, false); }
	void error_notdef(Id id, Scope s)
		{ error_id("not defined", id, s, false); }
	void error_notinit(Id id, Scope s)
		{ error_id("not initialized", id, s, false); }
	void error_notfunc(Id id, Scope s)
		{ error_id("is not a function", id, s, false); }
	void error_shadows(Id id, Scope s)
		{ error_id("shadows parameter", id, s, false); }
	void error_noderef(Id id, Scope s)
		{ error_id("cannot be dereferenced", id, s, false); }
	void error_notaggr(Id id, Scope s)
		{ error_id("is not an aggregate", id, s, false); }
	csz* opname(Token t);
	bool makeconst(Item *r, Item_ arg);
	Expr exprof(Item_ item);
	Stmt stmtof(Item_ item);
	StmtList stmtlistof(Item_ item);
	Expr dotexpr(type_t t, Item_ m, type_t *rt = 0);
	Expr ptrexpr(type_t t, Item_ m, type_t *rt = 0);
	void inherit(Decl *decl, Decl *base, Decl::Access acc = Decl::ac_unspecified);

	void notesymdecl(Decl *decl);
	void notesymdef(Decl *decl);
	sz * scoped_string(Decl *decl, sz *s);

	Decl* addparam(int i, Scope, const Param &p);
	void memberfn(Decl *decl);
	Decl *findfn(Decl *df, ExprList arg);
	int _typematch(type_t to, type_t from);
	int typematch(type_t to, type_t from);
	int fnmatch(type_t f, ExprList arg);

	type_t rtypeof(Item_ val);
	type_t deref(type_t type);

	// test code
	void codetest(Scope, StmtList &s);
};


// Deco Item types
//	(see CAction.hh for CParse Item types
//
//	AnyId:		any identifier (might not be declared)
//		= DeclId | NewId
//
//	AnyDeclId:	any declared indentifier
//		= AnyTypeId | ValId | NspaceId | TplateId
//
//	AnyTypeId:	identifier for any type descriptor
//		= AggrId | EnumId | TypeId
//
//	AnyValId:	identifier for any symbol with a value
//		= ValId
//	    destructor:
//		token= DTOR_ID
//		id= string id (~classname)
//		tuple.right= original string id (without the ~)
//	    operator:
//		token= OPER_ID
//		id= string id (full operator name)
//	    conversion:
//		token= CONV_ID
//		id= string id (full type name)
//		tuple.right= type ptr
//
//	AnyScopeId:	identifier for any scope
//		= AggrId | NspaceId
//
//	AggrId:		identifier for an aggregate (class/struct/union)
//		token= AGGR_ID
//		id= string id
//		tuple.right = Decl*
//
//	EnumId:		identifier for an enumeration
//		token= ENUM_ID
//		id= string id
//		tuple.right = Decl*
//
//	TypeId:		identifier for a type (not enum/aggr)
//		token= TYPE_ID
//		id= string id
//		tuple.right = Decl*
//
//	ValId:		identifier for a symbol with a value
//		token= VAL_ID
//		id= string id
//		tuple.right = Decl*
//
//	NewId:		undefined identifier
//		token= ID
//		id= string id
//
//	NspaceId:
//		token= NSPACE_ID | ASPACE_ID
//		id= string id
//
//	TypeList:	compound list of identifiers to describe a type
//		token= [TYPELIST]
//
//	Lit:		literal (and synthesized literals, which
//				are essentially constants)
//		token= [LITERAL] | NONE
//
//	Expr:		compound expression
//
//

static type_t realtype(type_t ptype);

static TDecl::DataType
datatypeof(CAction::Token tok)
{
	switch(tok) {
	case CAction::KW_INT: return TDecl::dt_int;
	case CAction::KW_CHAR: return TDecl::dt_char;
	case CAction::KW_WCHAR: return TDecl::dt_wchar;
	case CAction::KW_BOOL: return TDecl::dt_bool;
	case CAction::KW_VOID: return TDecl::dt_void;
	case CAction::KW_FLOAT:	return TDecl::dt_float;
	case CAction::KW_DOUBLE: return TDecl::dt_double;
	case CAction::KW_ENUM: return TDecl::dt_enum;
	case CAction::ENUM_ID: return TDecl::dt_enumval;
	case CAction::KW_STRUCT: return TDecl::dt_struct;
	case CAction::KW_CLASS:	return TDecl::dt_class;
	case CAction::KW_UNION:	return TDecl::dt_union;
	case CAction::KW_NAMESPACE: return TDecl::dt_namespace;
	case CAction::KW_DTYPE:	return TDecl::dt_dtype;
	case CAction::KW_DCLASS: return TDecl::dt_dclass;
	default: return TDecl::dt_default;
	}
}

/*
static CAction::Token
tokenof(TDecl::DataType dt)
{
	switch(dt) {
	case TDecl::dt_int: return CAction::KW_INT;
	case TDecl::dt_char: return CAction::KW_CHAR;
	case TDecl::dt_wchar: return CAction::KW_WCHAR;
	case TDecl::dt_bool: return CAction::KW_BOOL;
	case TDecl::dt_void: return CAction::KW_VOID;
	case TDecl::dt_float: return CAction::KW_FLOAT;
	case TDecl::dt_double: return CAction::KW_DOUBLE;
	case TDecl::dt_enum: return CAction::KW_ENUM;
	case TDecl::dt_struct: return CAction::KW_STRUCT;
	case TDecl::dt_class: return CAction::KW_CLASS;
	case TDecl::dt_union: return CAction::KW_UNION;
	case TDecl::dt_namespace: return CAction::KW_NAMESPACE;
	case TDecl::dt_dtype: return CAction::KW_DTYPE;
	case TDecl::dt_dclass: return CAction::KW_DCLASS;
	default: return CAction::NONE;
	}
}
*/

type_t
Deco::rtypeof(Item_ val)
{
	switch(val.token) {
	case LITERAL:
		return (type_t)val.cptr;
	case VAL_ID:
		return ((Decl*)val.tuple.right)->rtype;
	case EXPR:
		return ((Expr&)val.tuple.right).rtype();
	default:
		assertfail(val.token == LITERAL|VAL_ID|EXPR);
		for(;;);
	}
}

type_t
Deco::deref(type_t type)
{
	if(PtrTo *ptr = dcast_PtrTo(type)) {
		return ptr->rtype();
	} else {
		char buf[MAXTYPELEN];
		buf[0] = '\0';
		error("cannot dereference `%s'", 
			ccout.type_string(type, buf));
		return 0;
	}
}

Expr
Deco::exprof(Item_ val)
{
	Item v;
	switch(val.token) {
	case LITERAL:
	case VAL_ID:
		if(makeconst(&v, val)) {
			type_t t = (type_t)v.cptr;
			return Expr(Lit(t, v.val)).set_rtype(realtype(t));
		} else 
			return Expr((Decl*)val.tuple.right);
	case EXPRLIST:
	case EXPR:
	case STMTLIST:
		return Expr((ExprNode*)val.tuple.right);
	case TOKENLIST:
		return Expr((TokenList*)val.ptr);
	case NONE:
		return Expr((ExprNode*)0);
	case ID:
		return Expr(val.id);
	default:
		assertfail(val.token == EXPRLIST|LITERAL|NONE);
		for(;;);
	}
}

Stmt
Deco::stmtof(Item_ val)
{
	switch(val.token) {
	case STMTLIST:
	case STMT:
	case EXPRLIST:
	case EXPR:
		return (Stmt&)val.tuple.right;
	case DECL: 
		return Stmt(ExprDecl((Decl*)val.tuple.right));
	case NONE:
		return Stmt((ExprNode*)0);
	default:
		INTERNAL_ERROR;
	}
}

StmtList
Deco::stmtlistof(Item_ val)
{
	switch(val.token) {
	case STMTLIST:
		return (StmtList&)val.tuple.right;
	default:
		INTERNAL_ERROR;
	}
}



Deco::Deco(CLex &clp, vStrTab &strtab, vSymTab &symtab)
	 : CAction(clp, strtab, symtab),
	   ccout(*this, &_global_scope)
{
	_temp = 0;
	_anon = 0;
	_notedecl = false;
	_notedef = false;
}


Id
Deco::new_anon(Scope sc, csz *tname)
{
	char buf[80];
	vStrTab *strtab = sc.strtab();
	sprintf(buf, "__anon_%s_%d__", tname, _anon++);
	return strtab->id(buf);
}

/*
sz*
Deco::scope_string(Scope sc, sz *s)
{
	if(sc.parent()) {
		s = scope_string(sc.parent(), s);
		if(*s)
			strcat(s, "::");
		if(sc.pchid())
			strcat(s, string(sc.pchid()));
		else
			strcat(s, "?");
	} else
		strcpy(s, "");
	return s;
}
*/

csz*
Deco::printable_scope_string(Scope sc)
{
	static char buf[MAXIDLEN];
	sz *s;
	if(sc.parent()) {
		s = ccout.scoped_string(sc, buf);
		strpcat(buf, "scope '");
		strcat(buf, "'");
	} else {
		s = "global scope";
	}
	return s;
}

sz *
Deco::scoped_string(Decl *decl, sz *s)
{
	*s = '\0';
	if(decl->tagged()) 
		sprintf(s, "%s ", TDecl::dtname[decl->dtype]);
	ccout.scoped_string(decl, s+strlen(s), false);
	return s;
}

void
Deco::error_id(csz *what, Id id, Scope sc, bool showtype)
{
	sz buf[MAXIDLEN];
	Decl *decl = sc.get(id);
	if(decl && decl->tagged() && !showtype)
		sprintf(buf, "%s %s", 
			Decl::dtname[decl->dtype], string(id));
	else
		strcpy(buf, string(id));
	if(showtype && decl && decl->ptype) {
		csz *s;
		sz buf2[MAXTYPELEN];
		buf2[0] = '\0';
		if(decl->tagged())
			s = Decl::dtname[decl->dtype];
		else
			s = ccout.type_string(decl->ptype, buf2);
		error("'%s' %s as `%s' in %s",
			 buf, what, s, printable_scope_string(sc));
	} else  
		error("'%s' %s in %s",
			 buf, what, printable_scope_string(sc));
}


vSymTab*
Deco::get_symtab(const Decl *scope) 
{
	vSymTab *symtab = 0;
	if(!scope || !scope->rtype)
		return 0;
	if(Aggr *aggr = dcast_Aggr(scope->rtype)) {
		if(!(symtab = aggr->scope())) {
			error("aggregate `%s' is undefined", string(scope->id));
			symtab = new SymTab(&_global_scope, (Decl*)scope);
			aggr->set_scope(symtab);
		}
	}
	return symtab;
}

vSymTab*
Deco::get_symtab(Item_ scope) 
{
	vSymTab *symtab;
	switch(scope.token) {
	case NONE:
		symtab = &_global_scope;
		break;
	case AGGR_ID:
		symtab = get_symtab((Decl*)scope.tuple.right); 
		break; 
	case NSPACE_ID: case ASPACE_ID:
		symtab = 0;
		break;
	case SCOPE:
		symtab = (vSymTab*)scope.tuple.right;
		break;
	default:
		assert(scope.token == NONE|SCOPE|AGGR_ID);
		return 0;
	}
	assert(symtab);
	return symtab;
}

vSymTab*
Deco::get_symtab(Item_ sym, Item_ scope) 
{
	vSymTab *symtab = sym.scope;
	if(!symtab)
		symtab = get_symtab(scope);
	return symtab;
}


struct TypeList {
	type_t type;
	TypeList *next;
};

static type_t
addtype(TypeList **tl, type_t type)
{
	TypeList *tn = new TypeList;
	tn->type = type;
	tn->next = *tl;
	*tl = tn;
	return type;
}

static type_t
type_ptrto(type_t t, bool is_const = false, bool is_volatile = false)
{
	static TypeList *tl = 0;
	for(TypeList *tp = tl; tp; tp = tp->next) {
		PtrTo *ptr = (PtrTo*)tp->type->ptr();
		if(ptr->rtype() == t && ptr->is_const() == is_const
				&& ptr->is_volatile() == is_volatile) 
			return tp->type;
	}
	return addtype(&tl, new obj_PtrTo(t, is_const, is_volatile));
}

static type_t
type_refto(type_t t, bool is_const = false, bool is_volatile = false)
{ 
	static TypeList *tl = 0;
	for(TypeList *tp = tl; tp; tp = tp->next) {
		RefTo *ref = (RefTo*)tp->type->ptr();
		if(ref->rtype() == t && ref->is_const() == is_const
				&& ref->is_volatile() == is_volatile) 
			return tp->type;
	}
	return addtype(&tl, new obj_RefTo(t, is_const, is_volatile));
}

static type_t
type_qualifier(type_t t, bool is_const, bool is_volatile)
{ 
	static TypeList *tl = 0;
	for(TypeList *tp = tl; tp; tp = tp->next) {
		Qualifier *qual = (Qualifier*)tp->type->ptr();
		if(qual->rtype() == t && qual->is_const() == is_const
				&& qual->is_volatile() == is_volatile) 
			return tp->type;
	}
	return addtype(&tl, new obj_Qualifier(t, is_const, is_volatile));
}

static type_t
type_declref(Decl *d)
{
	static TypeList *tl = 0;
	for(TypeList *tp = tl; tp; tp = tp->next)
		if(((DeclRef*)tp->type->ptr())->decl() == d)
			return tp->type;
	return addtype(&tl, new obj_DeclRef(d));
}

static type_t
type_array(type_t t, uint size)
{ 
	static TypeList *tl = 0;
	for(TypeList *tp = tl; tp; tp = tp->next) {
		Array *array = (Array*)tp->type->ptr();
		if(array->rtype() == t && array->size() == size)
			return tp->type;
	}
	return addtype(&tl, new obj_Array(t, size));
}

static type_t
type_aggr(Id id, Scope sc)
{ return new obj_Aggr(id, sc); }

static type_t
type_enum(Decl *d)
{ return new obj_Enum(d); }

static type_t
type_psig(uint np, const type_t *p, bool var = false)
{ 
	static TypeList *tl = 0;
	for(TypeList *tp = tl; tp; tp = tp->next) {
		Params *par = (Params*)tp->type->ptr();
		if(par->nparam() == np
		    && par->is_variable() == var) {
			for(uint i=0; i<np; i++) {
				if(par->ptype(i) != p[i])
					goto typeloop;
			}
			return tp->type;
		}
	    typeloop: ;
	}
	p = (type_t*)memcpy(new type_t[np], p, sizeof(type_t)*np);
	return addtype(&tl, new obj_Params(np, p, var));
}

static type_t
type_func(type_t rt, type_t p, vSymTab *s)
{ 
	static TypeList *tl = 0;
	for(TypeList *tp = tl; tp; tp = tp->next) {
		Func *func = (Func*)tp->type->ptr();
		if(func->rtype() == rt && func->psig() == p
		    && func->scope() == s)
			return tp->type;
	}
	return addtype(&tl, new obj_Func(rt, p, s));
}

static type_t
type_method(type_t o, type_t f, bool c = false, bool v = false)
{
	static TypeList *tl = 0;
	for(TypeList *tp = tl; tp; tp = tp->next) {
		Method *meth = (Method*)tp->type->ptr();
		if(meth->otype() == o && meth->ftype() == f
		   && meth->is_const() == c
		   && meth->is_volatile() == v)
			return tp->type;
	}
	return addtype(&tl, new obj_Method(o, f, c, v));
}

static type_t
realtype(type_t ptype)
{
	bool is_const = false;
	bool is_volatile = false;
	type_t rt;
	for(;;) {
		if(Qualifier *qual = dcast_Qualifier(ptype)) {
			if(qual->is_const())
				is_const = true;
			if(qual->is_volatile())
				is_volatile = true;
			ptype = qual->rtype();
			continue;
		} else if(DeclRef *dref = dcast_DeclRef(ptype)) {
			ptype = dref->rtype();
			continue;
		}
		break;
	} 
	if(PtrTo *ptr = dcast_PtrTo(ptype)) {
		rt = type_ptrto(realtype(ptr->rtype()),
				ptr->is_const() || is_const,
				ptr->is_volatile() || is_volatile);
		is_const = is_volatile = false;
	} else if(RefTo *ref = dcast_RefTo(ptype)) {
		/* XXX - what about const/volatile? */
		rt = realtype(ref->rtype());
	} else if(Array *arr = dcast_Array(ptype)) {
		rt = type_array(realtype(arr->rtype()), arr->size());
	} else if(ptype == &Type_Str) {
		rt = type_ptrto(&Type_char);
	} else if(Method *meth = dcast_Method(ptype)) {
		rt = type_method(realtype(meth->otype()),
				realtype(meth->ftype()),
				meth->is_const || is_const,
				meth->is_volatile || is_volatile);
		is_const = is_volatile = false;
	} else if(Func *func = dcast_Func(ptype)) {
		rt = type_func(realtype(func->rtype()),
				realtype(func->psig()), func->scope());
	} else if(Params *par = dcast_Params(ptype)) {
		int np = par->nparam();
		type_t pt[np];
		for(int i=0; i<np; i++)
			pt[i] = realtype(par->ptype(i));
		rt = type_psig(np, pt, par->is_variable());
	} else
		rt = ptype;
	if(is_const || is_volatile)  {
		rt = type_qualifier(rt, is_const, is_volatile);
	}
	return rt;
}


int
Deco::type_sizeof(type_t type)
{
	if(type->is_type(Type_Func))
		return 0;
	else if(DeclRef *dref = dcast_DeclRef(type)) {
		Decl *decl = dref->decl();
		return type_sizeof(decl->rtype);
	} else if(type->is_type(Type_Aggr)) {
		// Aggr *aggr = (Aggr*)type->ptr();
		// XXX - need to loop
		return 0;
	} else if(Array *array = dcast_Array(type)) {
		return array->size()*type_sizeof(array->rtype());
	} else if(type->is_type(Type_PtrTo)) {
		return sizeof(void*);
	} else if(RefTo *ref = dcast_RefTo(type)) {
		return type_sizeof(ref->rtype());
	} else if(Integral *integral = dcast_Integral(type)) {
		return integral->size();
	} else if(type == &Type_bool) {
		return 1;
	} else if(type == &Type_void) {
		return 0;
	} else
		return 0; 
}

int
Deco::type_alignment(type_t type)
{
	if(type->is_type(Type_Func))
		return 0;
	else if(DeclRef *dref = dcast_DeclRef(type)) {
		Decl *decl = dref->decl();
		return type_alignment(decl->rtype);
	} else if(type->is_type(Type_Aggr)) {
		// Aggr *aggr = (Aggr*)type->ptr();
		// XXX - need to loop
		return 0;
	} else if(Array *array = dcast_Array(type)) {
		return type_alignment(array->rtype());
	} else if(type->is_type(Type_PtrTo)) {
		return sizeof(void*);
	} else if(RefTo *ref = dcast_RefTo(type)) {
		return type_alignment(ref->rtype());
	} else if(Integral *integral = dcast_Integral(type)) {
		return 1+(((1<<integral->alignment())-1)>>3);
	} else if(type == &Type_bool) {
		return 1;
	} else if(type == &Type_void) {
		return 0;
	} else
		return 0; 
}

Expr
Deco::dotexpr(type_t t, Item_ m, type_t *rt)
{
	bool is_const = false;
	bool is_volatile = false;
	if(Qualifier *qual = dcast_Qualifier(t)) {
		if(qual->is_const())
			is_const = true;
		if(qual->is_volatile())
			is_volatile = true;
		t = qual->rtype();
	}
	if(Aggr *aggr = dcast_Aggr(t)) {
		Scope scope = m.scope ? m.scope : aggr->scope();
		Decl *d = symfind(m.id, scope, false);
		if(!d) {
			char buf[MAXIDLEN];
			error("`%s' is not a member of aggregate `%s'",
				_strtab.string(m.id),
				ccout.scoped_string(scope.decl(), buf));
			return Expr((ExprNode*)0);
		}
		Expr e(d);
		if(is_const || is_volatile)
			e.set_rtype(realtype(type_qualifier(d->rtype,
				is_const,
				is_volatile)));
		if(rt)
			*rt = e.rtype();
		return e;
	} else {
		char buf[MAXTYPELEN];
		buf[0] = '\0';
		error("`%s' is not an aggregate", ccout.type_string(t, buf));
		if(rt)
			*rt = 0;
		return Expr((ExprNode*)0);
	}
}

Expr
Deco::ptrexpr(type_t t, Item_ m, type_t *rt)
{
	if(!(t = deref(t))) 
		return Expr((ExprNode*)0);
	return dotexpr(t, m, rt);
}

type_t
Deco::decl_type(Item_ item, bool impwarn)
{
	if(item.token == NONE) {
		if(impwarn) {
			warning("implicit int");
			return &Type_int;
		} else
			return 0;
	}
	if(item.token == ENUM_ID) {
		INTERNAL_ERROR;
	}
	assert(item.token == TYPELIST);

	type_t t;

	TDecl &decl = *(TDecl*)&item.val;
	if(decl.ptype) {
		if(DeclRef *dr = dcast_DeclRef(decl.ptype)) 
			dr->decl()->anon = false;
		t = decl.ptype;
	} else {
	  if(decl.longcount && !(TDecl::dtflags[decl.dtype] & TDecl::DTF_LONGOK)) 
		  error("long invalid for %s", TDecl::dtname[decl.dtype]);
	  if(decl.is_short && !(TDecl::dtflags[decl.dtype] & TDecl::DTF_SHORTOK)) 
		  error("short invalid for %s", TDecl::dtname[decl.dtype]);
	  if((decl.is_signed || decl.is_unsigned) 
			  && !(TDecl::dtflags[decl.dtype] & TDecl::DTF_SIGNOK)) 
		  error("signed/unsigned invalid for %s",
				  TDecl::dtname[decl.dtype]);
	  if(decl.longcount && decl.is_short)
		  error("long and short specified together");
	  if(decl.is_signed && decl.is_unsigned)
		  error("signed and unsigned specified together");
	  if(decl.longcount > 2)
		  error("long long long not supported");
	  switch(decl.dtype) {
	  case TDecl::dt_void: t = &Type_void; break;
	  case TDecl::dt_bool: t = &Type_bool; break;
	  case TDecl::dt_char: t = decl.is_signed ? &Type_schar
				  : decl.is_unsigned ? &Type_uchar
				  : &Type_char; break;
	  case TDecl::dt_wchar: t = &Type_wchar; break;
	  case TDecl::dt_float: t = &Type_float; break;
	  case TDecl::dt_double: t = &Type_double; break;
	  case TDecl::dt_default:
	  case TDecl::dt_int:
		  switch(decl.is_short ? -1 : (int)decl.longcount) {
		  case -1: t = decl.is_unsigned ? &Type_ushort : &Type_short;
			  break;
		  case 0: t = decl.is_unsigned ? &Type_uint : 
				  ((impwarn || decl.dtype == TDecl::dt_int)
				  ? &Type_int : (type_t)0);
			  break;
		  case 1:	t = decl.is_unsigned ? &Type_ulong : &Type_long;
			  break;
		  default: t = decl.is_unsigned ? &Type_ulong2 : &Type_long2;
			  break;
		  }
		  break;
	  default:
		  INTERNAL_ERROR;
	  }
	}
	if(decl.is_const || decl.is_volatile) 
		t = type_qualifier(t, decl.is_const, decl.is_volatile);
	if(_realtypes) 
		t = realtype(t);
	return t;
}

type_t
Deco::decl_type(Item *r, Item_ type, bool impwarn)
{
	if(type.token == NONE) 
		BuildTypeList(r, none, none, none);
	else
		*r = type;
	TDecl *td = (TDecl*)&r->val;
	td->ptype = decl_type(type, impwarn);
	td->is_const = false;
	td->is_volatile = false;
	return td->ptype;
}


/* need a "getconst" function that gets a constant value if possible ...
	CParse might need access to this too, so it should probably
	be a GetConst function */


bool
Deco::makeconst(Item *r, Item_ arg)
{
	switch(arg.token) {	
	case LITERAL:
		*r = arg;
		return true;
	case VAL_ID: {
		Decl *decl = (Decl*)arg.tuple.right;
		if(decl && decl->storage == Decl::sc_constant) {
			r->token = LITERAL;
			r->cptr = decl->ptype;
			r->val = decl->c.val;
			return true;
		}
/*
		if(decl && decl->initialized && decl->ptype->is_type(Type_Qualifier)) {
			r->token = LITERAL;
			r->cptr = decl->ptype;
			r->val = decl->val;
			return true;
		}
*/
		} break; 
	default:
		r->token = LITERAL;
		r->cptr = &Type_int;
		r->val = 0;
		break;
	}
	return false;
}

void
Deco::Literal(Item *r, Item_ val, Item_, Item_ )
{
	PRINT_ACTION(r, val, none, none);
	switch(val.token) {
	case NUM:
		switch(val.c.is_long) {
		case 0: r->cptr = val.c.is_signed ? &Type_int : &Type_uint; break;
		case 1: r->cptr = val.c.is_signed ? &Type_long : &Type_ulong; break;
		case 2: r->cptr = val.c.is_signed ? &Type_long2 : &Type_ulong2; break;
		default:
			error("%d L's too large", val.c.is_long);
		}
		r->val = val.val;
		break;
	case CHAR:
		r->cptr = &Type_char;
		r->val = val.val;
		break;
	case BOOL:
		r->cptr = &Type_bool;
		r->val = val.val;
		break;
	case STR:
		r->cptr = &Type_Str;
		r->val = (int)val.id;
		break;
	case FLOAT:		/* unsupported */
		*r = val;
		return;
	case VAL_ID:
	case TEMP:
		if(makeconst(r, val)) 
			return;
	case EXPR:
		error("not a constant");
		break;
	case LITERAL:
		*r = val;
		return;
	default:
		assertfail(val.token ==
				NUM|CHAR|STR|BOOL|FLOAT|VAL_ID|LITERAL);
	}
	r->token = LITERAL;
}

void
Deco::EnumNext(Item *r, Item_ prev, Item_, Item_ )
{
	PRINT_ACTION(r, prev, none, none);
	if(prev.token == NONE) {
		r->token = LITERAL;
		r->cptr = &Type_int;
		r->val = 0;
	} else {
		assert(prev.token == LITERAL);
		*r = prev;
		r->val = r->val+1;
	}
}

void
Deco::Undeclared(Item *r, Item_ sym, Item_ scope, Item_)
{
	PRINT_ACTION(r, sym, scope, none);
	vSymTab *symtab = get_symtab(sym, scope);
	error_notdecl(sym.id, Scope(symtab));
	*r = none;
}

csz*
Deco::opname(Token t)
{
	switch(t) {
	case OP_REF:	return "take the address of";
	case OP_PTRTO:
	case OP_PTR:	return "dereference";
	case OP_INC:	return "increment";
	case OP_DEC:	return "decrement";
	case OP_ASN:
	case OP_ASNADD: case OP_ASNSUB:
	case OP_ASNMUL: case OP_ASNDIV: case OP_ASNMOD:
	case OP_ASNAND: case OP_ASNOR: case OP_ASNXOR:
	case OP_ASNLSHIFT: case OP_ASNRSHIFT:
		return "assign";
	default:
		INTERNAL_ERROR;
	}
}

void
Deco::UnaryOp(Item *r, Item_ op, Item_ arg, Item_ )
{
	PRINT_ACTION(r, op, arg, none);
	Item a;
	if(makeconst(&a, arg)) {
		switch(op.token) {
		case OP_UPLUS:	r->val = a.val; break;
		case OP_UMINUS:	r->val = -a.val; break;
		case OP_NOT:	r->val = ~a.val; break;
		case OP_LNOT:	r->val = !a.val; break;
		case OP_REF:
		case OP_PTR:
		case OP_INC:
		case OP_DEC:
			error("cannot %s a constant",
				opname(op.token));
			/* XXX - these imply assignment as well... */
		default:
			assertfail(op.token == operator);
		}
		r->token = LITERAL;
		r->cptr = a.cptr;
	} else if(op.token == OP_TSIZEOF) {
		type_t type = decl_type(arg);
		if(type)
			r->val = type_sizeof(type);
		r->token = LITERAL;
		r->cptr = &Type_int;
	} else {
		type_t t = rtypeof(arg);
		switch(op.token) {
		case OP_REF: t = type_ptrto(t); break;
		case OP_PTR: t = deref(t); break;
		case OP_SIZEOF: t = &Type_int; break;
		default: ;
		}
		r->tuple.right = Expr(op.token, exprof(arg)).set_rtype(t);
		r->token = EXPR;
	}
}

void
Deco::BinaryOp(Item *r, Item_ op, Item_ arg1, Item_ arg2)
{
	PRINT_ACTION(r, op, arg1, arg2);
	Item a1, a2;
	if(makeconst(&a1, arg1)) {
		switch(op.token) {
		case OP_ASN:
		case OP_ASNADD: case OP_ASNSUB:
		case OP_ASNMUL: case OP_ASNDIV: case OP_ASNMOD:
		case OP_ASNAND: case OP_ASNOR: case OP_ASNXOR:
		case OP_ASNLSHIFT: case OP_ASNRSHIFT:
			error("cannot %s a constant", opname(op.token));
		default: ;
		}
		if(makeconst(&a2, arg2)) {
			switch(op.token) {
			case OP_ADD:  r->val = a1.val + a2.val; break;
			case OP_SUB:  r->val = a1.val - a2.val; break;
			case OP_MUL:  r->val = a1.val * a2.val; break;
			case OP_DIV:  r->val = a1.val / a2.val; break;
			case OP_MOD:  r->val = a1.val % a2.val; break;
			case OP_AND:  r->val = a1.val & a2.val; break;
			case OP_OR:   r->val = a1.val | a2.val; break;
			case OP_XOR:  r->val = a1.val ^ a2.val; break;
			case OP_LAND: r->val = a1.val && a2.val; break;
			case OP_LOR:  r->val = a1.val || a2.val; break;
			case OP_LXOR: r->val=!(!a1.val && !a2.val); break;
			case OP_LSS:  r->val = a1.val < a2.val; break;
			case OP_GTR:  r->val = a1.val > a2.val; break;
			case OP_LEQ:  r->val = a1.val <= a2.val; break;
			case OP_GEQ:  r->val = a1.val >= a2.val; break;
			case OP_EQU:  r->val = a1.val == a2.val; break;
			case OP_NEQ:  r->val = a1.val != a2.val; break;
			case OP_LSHIFT:  r->val = a1.val << a2.val; break;
			case OP_RSHIFT:  r->val = a1.val >> a2.val; break;
			case OP_COMMA:	r->val = a2.val; break;
			default:
				assertfail(op.token == operator);
			}
			r->cptr = a1.cptr;	// XXX - need table
			r->token = LITERAL;
			return;
		}
	} 
	Expr e1 = exprof(arg1);
	type_t t1 = rtypeof(arg1);
	type_t rt;
	Expr e2((ExprNode*)0);
	switch(op.token) {
	case OP_DOT:
		e2 = dotexpr(t1, arg2, &rt);
		break;
	case OP_PTRTO:
		e2 = ptrexpr(t1, arg2, &rt);
		break;
	case OP_MDOT:
	case OP_MPTRTO:
		{
		type_t pt;
		e2 = exprof(arg2);
		assert((pt = e2.rtype()));
		if(!(pt = deref(pt)) || !pt->is_type(Type_Method)) {
			error("not a member function pointer");
		} else {
			Method *meth = (Method*)pt->ptr();
			Func *func = (Func*)meth->ftype()->ptr();
			rt = func->rtype();
		}
		break; }
	default:
		e2 = exprof(arg2);
		rt = t1;
	}
	r->tuple.right = Expr(op.token, e1, e2).set_rtype(rt);
	r->token = EXPR;
}

void
Deco::OpNew(Item *r, Item_ type, Item_ init, Item_ params)
{
	PRINT_ACTION(r, type, init, params);

	assert(type.token == TYPELIST);
	type_t nt = decl_type(type);
	Expr e(OP_NEW, nt, exprof(init), exprof(params));
	e.set_rtype(nt);
	r->tuple.right = e;
	r->token = EXPR;
}

void
Deco::CondExpr(Item *r, Item_ cond, Item_ iftrue, Item_ iffalse)
{
	PRINT_ACTION(r, cond, iftrue, iffalse);
	Item c, t, f;
	if(makeconst(&c, cond) && makeconst(&t, iftrue)
				&& makeconst(&f, iffalse)) {
		r->val = c.val ? t.val : f.val;
		r->cptr = c.val ? t.cptr : f.cptr;
		r->token = LITERAL;
		return;
	}
	r->token = EXPR;
	r->tuple.tval = QMARK;
	r->tuple.right = Expr(QMARK, exprof(cond), exprof(iftrue),
					exprof(iffalse));
}



long
Deco::check_repeat(long a, long b, csz *name)
{
	if(a && b) {
		if(a == b)
			error("repeated %s", name);
		else
			error("more than one %s", name);
	}
	return a ? a : b;
}

void
Deco::BuildTypeList(Item *r,
		Item_ declist, Item_ item, Item_ )
{
	PRINT_ACTION(r, declist, item, none);
	switch(declist.token) {
	case TYPELIST:
		*r = declist;
		break;
	case NONE:
		r->token = TYPELIST;	
		r->val = 0;
		break;
	default:
		assertfail(declist.token == TYPELIST|NONE);
		break;
	}

	TDecl n;
	n.clear();
	TDecl &td = (TDecl&)r->val;

	switch(item.token) {
	case KW_AUTO:		n.storage = TDecl::sc_auto; break;
	case KW_STATIC:		n.storage = TDecl::sc_static; break;
	case KW_EXTERN:		n.storage = TDecl::sc_extern; break;
	case KW_REGISTER:	n.storage = TDecl::sc_register; break;
	case KW_MUTABLE:	n.storage = TDecl::sc_mutable; break;
	case KW_TYPEDEF:	n.storage = TDecl::sc_typedef; break;
	case KW_INT:		n.dtype = TDecl::dt_int; break;
	case KW_CHAR:		n.dtype = TDecl::dt_char; break;
	case KW_WCHAR:		n.dtype = TDecl::dt_wchar; break;
	case KW_FLOAT: 		n.dtype = TDecl::dt_float; break;
	case KW_DOUBLE:		n.dtype = TDecl::dt_double; break;
	case KW_BOOL:		n.dtype = TDecl::dt_bool; break;
	case KW_VOID:		n.dtype = TDecl::dt_void; break;
	case KW_VOLATILE:	n.is_volatile = true; break;
	case KW_CONST:		n.is_const = true; break;
	case KW_SHORT:		n.is_short = true; break;
	case KW_SIGNED:		n.is_signed = true; break;
	case KW_UNSIGNED:	n.is_unsigned = true; break;
	case KW_LONG:		n.longcount++; break;
	case KW_INLINE:		n.is_inline = true; break;
	case KW_VIRTUAL:	n.is_virtual = true; break;
	case KW_EXPLICIT:	n.is_explicit = true; break;
	case KW_FRIEND:		n.is_friend = true; break;
	case_ANY_TYPE_ID: {
		Decl *d = (Decl*)item.tuple.right;
		n.ptype = type_declref(d);
		td.odefined = td.odefined || d->odefined;
		d->odefined = false;
		break; }
	case TYPELIST:
		n = (TDecl&)item.val;
		break;
	case NONE:
		break;
	default:
		error("unsupported declaration");
	}
	td.storage = (TDecl::StorageClass)check_repeat(td.storage,
				n.storage, "storage class");
	td.dtype = (TDecl::DataType)check_repeat(td.dtype, n.dtype, "type");
	td.ptype = (type_t)check_repeat((long)td.ptype,
				(long)n.ptype, "type");
	td.is_volatile = check_repeat(td.is_volatile,
					n.is_volatile, "volatile");
	td.is_const = check_repeat(td.is_const,
					n.is_const, "const");
	td.is_unsigned = check_repeat(td.is_unsigned,
					n.is_unsigned, "unsigned");
	td.is_signed = check_repeat(td.is_signed,
					n.is_signed, "signed");
	td.is_short = check_repeat(td.is_short,
					n.is_short, "short");
	td.is_inline = check_repeat(td.is_inline,
					n.is_inline, "inline");
	td.is_virtual = check_repeat(td.is_virtual,
					n.is_virtual, "virtual");
	td.is_explicit = check_repeat(td.is_explicit,
					n.is_explicit, "explicit");
	td.is_friend = check_repeat(td.is_friend,
					n.is_friend, "friend");
	td.longcount += n.longcount;
}


void
Deco::BuildParamList(Item *r,
		Item_ paramlist, Item_ item, Item_ scope)
{
	PRINT_ACTION(r, paramlist, item, scope);
	assert((paramlist.token == PARMLIST)
		| (paramlist.token == NONE));
	r->val = paramlist.val;
	ParamList &plist = (ParamList&)r->val;
	r->token = PARMLIST;
	Param *p;
	switch(item.token) {
	case PARM:
		p = (Param*)item.ptr;
		p->next = plist.head;
		plist.head = p;
		plist.nparam++;
		break;
	case ELIPSIS:
		plist.variable = true;
		break;
	case NONE:
		break;
	default:
		assertfail(item.token == PARMLIST|ELIPSIS|NONE);
	}
	r->scope = get_symtab(scope);
}


void
Deco::DeclArray(Item *r, Item_ type, Item_ size,
			Item_ /*lower*/)
{
	PRINT_ACTION(r, type, size, none);
	TDecl *td = (TDecl*)&r->val;
	type_t t = decl_type(r, type);
	if(size.token == LITERAL)
		td->ptype = type_array(t, size.val);
	else
		td->ptype = type_array(t, NPOS);
}



void
Deco::DeclPtr(Item *r, Item_ ptrtype, Item_ type, Item_ )
{
	PRINT_ACTION(r, ptrtype, type, none);
	TDecl *td = (TDecl*)&r->val;
	const TDecl &cv = (const TDecl&)ptrtype.val;
	type_t t = decl_type(r, type);
	switch(ptrtype.token) {
	case OP_MPTR: 
		t = type_method(Scope(ptrtype.scope).decl()->ptype, t);
	case OP_PTR:
		td->ptype = type_ptrto(t, cv.is_const, cv.is_volatile);
		break;
	case OP_REF:
		td->ptype = type_refto(t, cv.is_const, cv.is_volatile);
		break;
	default:
		assertfail(ptrtype.token == OP_REF|OP_PTR);
	}
}


void
Deco::FuncScope(Item *r, Item_ sym, Item_ scope, Item_ )
{
	PRINT_ACTION(r, sym, scope, none);
	assert(sym.token == VAL_ID);
	assert(sym.tuple.right);
	Decl *d = (Decl*)sym.tuple.right;
	Func *func;
	if(Method *meth = dcast_Method(d->ptype))
		func = dcast_Func(meth->ftype());
	else
		func = dcast_Func(d->ptype);
	if(!func) {
		error_notfunc(d->id, d->scope); 
		return;
	}
	vSymTab *symtab = func->scope();
	if(!symtab)
		symtab = get_symtab(scope);
	r->token = SCOPE;
	r->tuple.right = r->scope = symtab;
}


Decl *
Deco::addparam(int i, Scope scope, const Param &p)
{
	Decl *d = new Decl;
	d->clear();
	d->scope = scope;
	d->id = p.name;
	d->ptype = p.type;
	d->rtype = realtype(d->ptype);
	d->where = i;
	d->storage = Decl::sc_param;
	d->defined = true;
	if(p.defval.token != NONE) {
		d->initialized = true;	
		d->v.expr = exprof(p.defval);
	}
	decladd(d);
	return d;
}

void
Deco::memberfn(Decl *decl)
{
	type_t ptype = decl->ptype;
	type_t rtype = decl->rtype;
	bool is_const = false;
	bool is_volatile = false;
	if(Qualifier *qual = dcast_Qualifier(rtype)) {
		is_const = qual->is_const();
		is_volatile = qual->is_volatile();
		rtype = qual->rtype();
	}
	if(Qualifier *qual = dcast_Qualifier(ptype)) 
		ptype = qual->rtype();
	Func *func = dcast_Func(rtype);
	assert(func);
	Decl *d = decl->scope.decl();
	assert(d);
	Param p;
	p.type = type_ptrto(d->rtype);
	p.name = _id_this;
	p.defval.token = NONE;
	addparam(-1, func->scope(), p);
	decl->ptype = type_method(d->rtype, ptype, is_const, is_volatile);
	decl->rtype = realtype(decl->ptype);
}


void
Deco::DeclFunc(Item *r, Item_ type, Item_ paramlist, Item_ cvqual)
{
	PRINT_ACTION(r, type, paramlist, cvqual);
	assert(type.token == TYPELIST || type.token == NONE);
	assert(paramlist.token == PARMLIST);

	ParamList &plist = (ParamList&)paramlist.val;
	const TDecl &cv = (const TDecl&)cvqual.val;

	uint np = plist.nparam;	
	type_t ptype[np];
	Param *param = plist.head;
	Scope scope = new SymTab(paramlist.scope);
	for(int i=np-1; i>=0; i--, param = param->next) {
		ptype[i] = param->type;
		if(param->name || param->defval.token != NONE)
			addparam(i, scope, *param);
	}
	type_t psig = type_psig(np, ptype, plist.variable);
	TDecl *td = (TDecl*)&r->val;
	type_t rt = decl_type(r, type, false);
	td->ptype = type_func(rt, psig, scope);
	if(cv.is_const || cv.is_volatile)
		td->ptype = type_qualifier(td->ptype, cv.is_const, cv.is_volatile);
}


void
Deco::DeclSym(Item *r, Item_ def, Item_ sym, Item_ scope)
{
	PRINT_ACTION(r, def, sym, scope);
	switch(sym.token) {
	case_ANY_ID:
	case NONE:
		break;
	default:
		error("unknown declaration type");
		return;
	}

	if(sym.token == NONE) {
		assert(def.token == TYPELIST);
		const TDecl &td = (const TDecl&)def.val;
		if(!td.odefined) {
			warning("useless declaration");
			return;
		}
		if(DeclRef *dref = dcast_DeclRef(td.ptype)) {
			Decl *d = dref->decl();
			if(d->anon) {
				Decl *decl = newdecl(ID_ANON, d->scope,
					decl_type(def), td.dtype, false);
				decl->storage = td.storage;
				decl->is_explicit = td.is_explicit;
				decl->is_virtual = td.is_virtual;
				decl->is_friend = td.is_friend;
				decl->is_inline = td.is_inline;
				if(decl->odeclared) {
					decl->odeclared = false;
					decl->scope.set(ID_ANON, decl);
				}
				d->anon = true;
			}
		} 
		return;
	}

	Decl *decl = newdecl_item(def, sym, scope, false);

	assert(decl->scope);

	type_t rtype = decl->rtype;
	bool is_const = false;
	bool is_volatile = false;
	if(Qualifier *qual = dcast_Qualifier(rtype)) {
		is_const = qual->is_const();
		is_volatile = qual->is_volatile();
		rtype = qual->rtype();
	}
	if(Func *func = dcast_Func(rtype)) {
		if(sym.token == DTOR_ID) {
			if((Id)sym.tuple.right != decl->scope.pchid())
				error("destructor does not match class name");
			if(func->rtype())
				error("destructor cannot have return type");
		} else if(sym.token == CTOR_ID) {
			if(func->rtype())
				error("constructor cannot have return type");
		} else if(sym.token == CONV_ID) {
			if(func->rtype()) {
				warning("conversion operator has return type");
				if(func->rtype() != sym.tuple.right)
					error("return type doesn't match operator");
			}
		} else {
			if(!func->rtype()) {
				warning("function implicitly returns int");
				func->change_rtype(&Type_int);
			}
		}
		Decl *pd = decl->scope.decl();
		bool ismem = pd && pd->ptype && pd->ptype->is_type(Type_Aggr);
		if(ismem && decl->storage != Decl::sc_static)
			memberfn(decl);
		else if(is_const || is_volatile)
			error("cannot apply const/volatile to %s function",
				ismem ? "static member" : "non-member");
	} else {
		if(get_symtab(scope) == &_global_scope
				&& decl->storage != TDecl::sc_extern) {
			decl->defined = true;
			notesymdef(decl);
		}
		if(sym.token == DTOR_ID)
			error("destructor must be a function");
	}

	r->token = decltoken(decl);
	r->tuple.id = sym.id;
	r->tuple.right = decl;
	if(!decl->odeclared) 
		return;
	decl->odeclared = false;

	Decl *odecl = decl->link;
	if(get_symtab(scope) != &_global_scope)
		error_dupdecl(decl->id, decl->scope);
	if(odecl->defined) {
		if(decl->defined) {
			if(decl->storage != TDecl::sc_typedef
			   || decl->storage != odecl->storage
			   || decl->rtype != odecl->rtype)
				error_dupdef(sym.id, decl->scope);
		}
	} else {
		if(decl->defined) {
			odecl->defined = true;
			notesymdef(odecl);
			if(odecl->storage == TDecl::sc_extern)
				odecl->storage = TDecl::sc_default;
		}
	}
	r->tuple.right = odecl;
	delete decl;
}


void
Deco::DeclParam(Item *r, Item_ type, Item_ name, Item_)
{
	PRINT_ACTION(r, type, name, none);
	Param *p = new Param;
	p->type = decl_type(type);
	p->name = (name.token == NONE) ? 0 : name.id;
	p->defval = NONE;
	p->next = 0;
	r->token = PARM;
	r->ptr = p;
}	

void
Deco::SetAttr(Item *r, Item_ sym, Item_ attr, Item_ val)
{
	PRINT_ACTION(r, sym, attr, val);
	Decl *decl = (Decl*)sym.tuple.right;
	switch(attr.token) {
	case KW_INLINE:
		decl->is_inline = true;
		break;
	default:
		assertfail(attr.token == KW_INLINE);
	}
	
}

void
Deco::Define(Item *r, Item_ sym, Item_ val, Item_)
{
	PRINT_ACTION(r, sym, val, none);
	Decl *decl = (Decl*)sym.tuple.right;
	if(decl->initialized) {
		if(decl->is_func())
			error_dupdef(decl->id, decl->scope);
		else
			error_dupinit(decl->id, decl->scope);
	}
	notesymdef(decl);
	decl->defined = true;
	decl->initialized = true;
	if(val.token == LITERAL) {
		if(Qualifier *q = dcast_Qualifier(decl->rtype)) {
			if(q->is_const())
				decl->storage = Decl::sc_constant;
		}
	}
	if(val.token == LITERAL && decl->storage == Decl::sc_constant) 
		decl->c.val = val.val;
	else
		decl->v.expr = exprof(val);
	*r = sym;
}

void
Deco::DefParam(Item *r, Item_ par, Item_ val, Item_ )
{
	PRINT_ACTION(r, par, val, none);
	((Param*)par.ptr)->defval = val;
	*r = par;
}


void
Deco::notesymdecl(Decl *decl)
{
	if(!_notedecl)
		return;
	char buf[MAXIDLEN];
	printf("%s:%d: decl: %s\n",
		_clex.source(), _clex.line_num(), scoped_string(decl, buf));
}

void
Deco::notesymdef(Decl *decl)
{
	if(!_notedef)
		return;
	char buf[MAXIDLEN];
	printf("%s:%d: def: %s\n",
		_clex.source(), _clex.line_num(), scoped_string(decl, buf));
}

// create a new Decl, and store it 
// optionally report an error if it already exists
// otherwise return a link to it
Decl *
Deco::newdecl(Id id, Scope parent,
			TDecl::DataType dtype, bool err)
{
	Decl *decl = new Decl;
	decl->clear();
	decl->access = TDecl::ac_public;
	decl->scope = parent;
	decl->id = id;
	decl->storage = Decl::sc_typedef;
	if((decl->dtype = dtype) == TDecl::dt_class)
		decl->subaccess = TDecl::ac_private;
	if(!id) {
		id = new_anon(parent, TDecl::dtname[decl->dtype]);
		decl->anon = true;
	}
	decl->id = id;
	if(_hdr && _clex.include_level() > 0)
		decl->nooutput = true;

	notesymdecl(decl);

	if(parent.add(id, decl)) 
		return decl;

	Decl *d = parent.get(id);
	decl->link = d;
	if(decl->tagged()) {
		while(d->link)
			d = d->link;
		if(!d->tagged()) {
			d->link = decl;
			decl->link = 0;
			return decl;
		}
	} else {
		if(d->tagged() || id == ID_ANON) {
			parent.set(id, decl);
			return decl;
		}
	}
	decl->odeclared = true; 
	if(err)
		error_dupdecl(id, parent);
	return decl;
}

void
mergedefs(Func *to, Func *from)
{
	Scope sto = to->scope();
	Scope sfrom = from->scope();
	if(!sfrom)
		return;
	if(!sto) {
		sto = new SymTab(sfrom.parent(), sfrom.decl());
		to->change_scope(sto);
	}
	for(uint i=0; i<to->nparam(); i++) {
		Decl *dfrom = param_decl(from, i);
		Decl *dto = param_decl(to, i);
		if(dfrom) {
			if(!dto) {
				dto = new Decl;
				*dto = *dfrom;
				dto->scope = sto;
				decladd(dto);
			} else if(dfrom->initialized) {
				/* XXX - should check for value clash */
				dto->initialized = true;
				dto->v.expr = dfrom->v.expr;
			}
		}
	}
}

Decl *
Deco::newdecl(Id id, Scope parent, type_t type,
			TDecl::DataType dtype, bool err)
{
	Decl *d = newdecl(id, parent, dtype, false);
	d->ptype = type;
	d->rtype = realtype(d->ptype);
	if(dtype == TDecl::dt_enumval)
		d->storage = TDecl::sc_constant;
	else
		d->storage = TDecl::sc_default;	
	Decl *od = d->link;
	Func *f1;
	if(Method *meth = dcast_Method(d->rtype))
		f1 = dcast_Func(meth->ftype());
	else
		f1 = dcast_Func(d->rtype);
	if(d->odeclared && f1 && od->is_func()) {
		for(Decl *dp = od; dp; dp = dp->link) {
		    Func *f2;
		    if(Method *meth = dcast_Method(dp->rtype))
			f2 = dcast_Func(meth->ftype());
		    else
			f2 = dcast_Func(dp->rtype);
		    if(f1->psig() == f2->psig()) {
			if(d->is_Func() && dp->is_Method()) {
				memberfn(d);
			}
			if(f1->rtype() != f2->rtype())
				error("return type changed");
			if(!dp->defined) {
				mergedefs(f1, f2);
				dp->ptype = d->ptype;
			} else {
				mergedefs(f2, f1);
				d->ptype = dp->ptype;
			}
			d->link = dp;
			goto found;
		    }
		}
		d->odeclared = false;
		d->scope.set(id, d);
	} else if(d->odeclared) {
		if(d->rtype != od->rtype && id != ID_ANON)
			error("type of `%s' redeclared", string(id));
	}
	found:
	if(!d->odeclared) {
		Decl *pd;
		if(dtype == TDecl::dt_enumval) {
			if(Enum *e = dcast_Enum(d->rtype)) {
				pd = e->decl();
			} else
				INTERNAL_ERROR;
		} else if((pd = d->scope.decl())) {
			if(d->scope != &_global_scope
			   && !pd->ptype->is_type(Type_Aggr))
				pd = 0;
		}
		if(pd) {
			Decl **dpp = &pd->a.first;
			while(*dpp)
				dpp = &(*dpp)->next;
			(*dpp) = d;
			if(!d->is_func()) 
				pd->a.tsize++;
		}
	} else if(err)
		error_dupdecl(id, parent);
	if(f1 && f1->scope()) 
		Scope(f1->scope()).decl(d->odeclared ? d->link : d);
	return d;
}

Decl *
Deco::newdecl_item(Item_ type, Item_ sym, Item_ scope, bool err)
{
	Scope parent = get_symtab(sym, scope);
	Decl *decl;
	Decl *pdecl = 0;
	if(sym.id) 
		pdecl = symfind(sym.id, parent, false);
	switch(type.token) {
	case ENUM_ID:
		decl = newdecl(sym.id, parent, 
			type_declref((Decl*)type.tuple.right),
			datatypeof(type.token), err);
		break;
	case TYPELIST:
	case NONE: {
		const TDecl &td = (const TDecl&)type.val;
		decl = newdecl(sym.id, parent, decl_type(type), td.dtype, err);
		decl->storage = td.storage;
		decl->is_explicit = td.is_explicit;
		decl->is_virtual = td.is_virtual;
		decl->is_friend = td.is_friend;
		decl->is_inline = td.is_inline;
		break; }
	default:
		decl = newdecl(sym.id, parent, 0, datatypeof(type.token), err);
		decl->storage = Decl::sc_typedef;
	}
	if(scope.token == AGGR_ID)
		decl->access = ((Decl*)scope.tuple.right)->subaccess;
	if(DeclRef *dr = dcast_DeclRef(decl->ptype)) 
		dr->decl()->anon = false;
	if(pdecl && pdecl->storage == Decl::sc_param
	    && pdecl->scope == parent.parent()) 
		error_shadows(sym.id, parent);
	return decl;
}


void
Deco::NewScope(Item *r,
	Item_ type, Item_ sym, Item_ scope)
{
	PRINT_ACTION(r, type, sym, scope);
	Scope sc = get_symtab(sym, scope);
	Scope new_symtab = new SymTab(sc);
	if(type.token == NONE) {
		r->tuple.right = new_symtab;
		r->token = SCOPE;
	} else {
		Decl *d = forwref(datatypeof(type.token), sym.id, sc);
		assert(d);
		if(scope.token == AGGR_ID) 
			d->access = ((Decl*)scope.tuple.right)->subaccess;
		if(Aggr *aggr = dcast_Aggr(d->ptype)) {
			if(aggr->scope())
				error_dupdef(d->id, sc);
			else {
				new_symtab.decl(d);
				aggr->set_scope(new_symtab);
				notesymdef(d);
			}
		} else
			INTERNAL_ERROR;
		r->tuple.id = d->id;
		r->tuple.right = d;
		r->token = AGGR_ID;
	}
	r->scope = new_symtab;
}

void
Deco::EndAggr(Item *r,
	Item_ aggr, Item_ scope, Item_)
{
	PRINT_ACTION(r, aggr, scope, none);
	assert(aggr.token == AGGR_ID);
	Decl *d = (Decl*)aggr.tuple.right;
	assert(d);
	d->odefined = true;
	*r = aggr;
}

Decl *
Deco::forwref(TDecl::DataType dtype, Id id, Scope sc)
{
	if(!sc)
		sc = &_global_scope;
	Decl *decl = id ? symget(id, sc, true) : 0;
	if(decl) {
		if(dtype != decl->dtype) {
			error("`%s %s' redeclared as %s",
				TDecl::dtname[decl->dtype],
				string(id),
				TDecl::dtname[dtype]);
		}
	} else if(dtype == TDecl::dt_enum) {
		char buf[MAXIDLEN];
		decl = newdecl(id, sc, 0, dtype, true);
		decl->storage = Decl::sc_typedef;
		error("`%s' cannot be forward declared",
			scoped_string(decl, buf));
	} else {
		type_t newtype = type_aggr(id, 0);
		decl = newdecl(id, sc, newtype, dtype, true);
		decl->storage = Decl::sc_typedef;
	}
	return decl;
}

void
Deco::AggrRef(Item *r,
	Item_ type, Item_ sym, Item_ scope)
{
	PRINT_ACTION(r, type, sym, scope);
	Scope sc = get_symtab(sym, scope);
	Decl *d = forwref(datatypeof(type.token), sym.id, sc);
	if(/*sym.token != AGGR_ID &&*/ scope.token == AGGR_ID) 
		d->access = ((Decl*)scope.tuple.right)->subaccess;
	d->odefined = true;
	r->token = decltoken(d);
	r->tuple.id = d->id;
	r->tuple.right = d;
	r->scope = sym.scope;
}

void
Deco::SetAccess(Item *r, Item_ scope, Item_ acc, Item_ )
{
	PRINT_ACTION(r, scope, acc, none);
	TDecl::Access a;
	assert(scope.token == AGGR_ID);
	switch(acc.token) {
	case KW_PUBLIC:		a = TDecl::ac_public; break;
	case KW_PROTECTED:	a = TDecl::ac_protected; break;
	case KW_PRIVATE:	a = TDecl::ac_private; break;
	case AGGR_ID: {
		a = TDecl::ac_public;
		Decl *sdecl = (Decl*)scope.tuple.right;
		Decl *decl = (Decl*)acc.tuple.right;
		if(decl->dtype != Decl::dt_dtype)
			error("access specifier is not a dynamic type");
		else if(sdecl->dtype == Decl::dt_dtype) {
		} else if(sdecl->dtype == Decl::dt_dclass) {
		} else {
			char buf[MAXIDLEN];
			error("dynamic access specifier `%s:'"
				" cannot be used in non-dynamic aggregate"
				" `%s'",
				string(decl->id), scoped_string(sdecl, buf)
				);
		}
		break; }
	default:
		assertfail(acc.token == PUBLIC|PROTECTED|PRIVATE|AGGR_ID);
		return;
	}
	((Decl*)scope.tuple.right)->subaccess = a;
	*r = scope; 
}


void
Deco::inherit(Decl *decl, Decl *base, Decl::Access acc)
{
	Scope sc = get_symtab(decl);
	Decl *d = new Decl;
	d->clear();
	if(acc == TDecl::ac_unspecified)
		acc = decl->dtype == Decl::dt_class
				? Decl::ac_private : Decl::ac_public;
	d->access = acc;
	d->scope = get_symtab(base);
	d->id = ID_INHERIT;
	d->subaccess = TDecl::ac_public; // unused?
	if(!sc.add(ID_INHERIT, d)) {
		d->link = sc.get(ID_INHERIT);
		sc.set(ID_INHERIT, d);
	}
	d->ptype = base->ptype;
	d->rtype = base->rtype;
	d->i.decl = base;
}

void
Deco::Inherit(Item *r,
	Item_ scope, Item_ itype, Item_ base)
{
	PRINT_ACTION(r, scope, itype, base);
	assert(scope.token == AGGR_ID && base.token == AGGR_ID);
	Decl::Access acc;
	switch(itype.token) {
	case KW_PUBLIC:		acc = TDecl::ac_public; break;
	case KW_PROTECTED:	acc = TDecl::ac_protected; break;
	case KW_PRIVATE:	acc = TDecl::ac_private; break;
	default:		acc = TDecl::ac_unspecified; break;
	}
	inherit((Decl*)scope.tuple.right, (Decl*)base.tuple.right, acc);
	*r = scope;
}
 

void
Deco::EnumRef(Item *r,
	Item_ type, Item_ sym, Item_ scope)
{
	PRINT_ACTION(r, type, sym, scope);
	Scope sc = get_symtab(sym, scope);
	Decl *d = forwref(datatypeof(type.token), sym.id, sc);
	if(sym.token != ENUM_ID && scope.token == AGGR_ID) 
		d->access = ((Decl*)scope.tuple.right)->subaccess;
	d->odefined = true;
	r->token = decltoken(d);
	r->tuple.id = d->id;
	r->tuple.right = d;
	r->scope = sym.scope;
}

void
Deco::NewEnum(Item *r,
	Item_ type, Item_ sym, Item_ scope)
{
	PRINT_ACTION(r, type, sym, scope);
	Decl *decl = newdecl_item(type, sym, scope, false);
	if(decl->odeclared) {
		Decl *nextdecl = decl->link;
		delete decl;
		decl = nextdecl;
		if(decl->rtype)
			error_dupdef(decl->id, decl->scope);
		decl->rtype = decl->ptype = type_enum(decl);
	} else {
		decl->rtype = decl->ptype = type_enum(decl);
	}
	decl->odefined = true;
	r->tuple.id = decl->id;
	r->tuple.right = decl;
	r->scope = sym.scope;
	r->token = ENUM_ID;
}


Deco::Token
Deco::decltoken(Decl *decl)
{
	if(decl->storage == TDecl::sc_typedef) {
		if(TDecl::dtflags[decl->dtype] & TDecl::DTF_AGGR) 
			return AGGR_ID;
		else if(decl->dtype == TDecl::dt_enum)
			return ENUM_ID; 
		else if(decl->dtype == TDecl::dt_namespace)
			return NSPACE_ID; 
		else
			return TYPE_ID;
	} else
		return VAL_ID;
}

void
Deco::FindSym(Item *r,
	Item_ type, Item_ sym, Item_ scope)
{
	PRINT_ACTION(r, type, sym, scope);
	Scope sc = get_symtab(sym, scope);
	r->scope = sym.scope;
	if(sym.token == NONE && type.token == SCOPE) {
		r->token = SCOPE;
		r->tuple.right = &_global_scope;
		r->scope = &_global_scope;
		return;
	}
	Decl *d = symfind(sym.id, sc, type.token != NONE,
			!sym.scope);
	if(d) {
		r->token = decltoken(d);
		r->tuple.id = sym.id;
		r->tuple.right = d;
		if(type.token == SCOPE) 
			r->scope = get_symtab(*r);
	} else 
		*r = NONE;
	if((type.token == VAL_ID || type.token == AGGR_ID)
			&& r->token != type.token) {
		if(r->token == NONE)
			error_notdecl(sym.id, sc);
		else
			error("`%s' not valid", string(sym.id));
	}
	if(r->token == AGGR_ID) {
		if(scope.token == AGGR_ID && d == scope.tuple.right)
			r->token = CTOR_ID;
		else if(sym.scope) {
			Aggr *aggr = (Aggr*)d->rtype->ptr();
			if(sym.scope == aggr->scope()) 
				r->token = CTOR_ID;
		}
	} else if(r->token == VAL_ID && d->id == d->scope.pchid()) {
		r->token = AGGR_ID;	/* convert ctors fn's back to aggr's */
		r->tuple.right = symget(sym.id, d->scope.parent(), true);
	}
}

void
Deco::SpecialStr(Item *r,
	Item_ type, Item_ scope, Item_ )
{
	PRINT_ACTION(r, type, scope, none);
	char buf[MAXTYPELEN];
	buf[0] = '\0';
	Decl *d = 0;
	Scope s = get_symtab(scope);
	while(s && !(d = s.decl()))
		s = s.parent();
	assert(d);
	switch(type.token) {
	case KW__FUNC:
		ccout.scoped_string(d, buf);
		break;
	case KW__PFUNC:
		ccout.scoped_string(d, buf);
		ccout.type_string(d->ptype, buf);
		break;
	default:
		assertfail(type.token != CPP_FUNC|CPP__PFUNC);
	}
	r->token = STR;
	r->id = _strtab.id(buf);
}

void
Deco::SpecialId(Item *r,
	Item_ type, Item_ val, Item_ scope)
{
	PRINT_ACTION(r, type, val, scope);
	char buf[MAXTYPELEN];
	csz *n;
	switch(type.token) {
	case CONV_ID:
		assert(val.token == TYPELIST);
		r->token = CONV_ID;
		r->tuple.right = decl_type(val);
		buf[0] = '\0';
		ccout.type_string((type_t)r->tuple.right, buf);
		strpcat(buf, "operator ");
		r->id = _strtab.id(buf);
		r->scope = 0;
		break;
	case OPER_ID:
		r->token = OPER_ID;
		switch(val.token) {
		case OP_FUNC: n = "()"; break;
		case OP_INDEX: n = "[]"; break;
		case OP_NEWA: n = "new[]"; break;
		case OP_DELETEA: n = "delete[]"; break;
		default:
			n = string(val.token);
		}
		sprintf(buf, "operator%s%s", n[0] >= 'a' && n[0] <= 'z'
				? " " : "", n);
		r->id = _strtab.id(buf);
		r->scope = 0;
		/*
		r->tval = val.token;
		*/
		break;
	case CTOR_ID: case DTOR_ID:
		r->token = type.token;
		sprintf(buf, "%c%s", type.token == DTOR_ID ? '~' : ' ', string(val.id));
		r->id = _strtab.id(buf);
		r->tuple.right = (void*)val.id;
		r->scope = val.scope;
		break;
	case KW_THIS:
		r->token = VAL_ID;
		r->id = _id_this;
		r->scope = val.scope;
		if(!(r->tuple.right = symfind(_id_this,
					get_symtab(scope), false))) {
			error("`this' used outside of"
				" non-static member function");
			r->token = NONE;
		}
		break;
	default:
		assertfail(type.token != CONV_ID|OPER_ID|CTOR_ID|DTOR_ID|KW_THIS);
	}
}

type_t
Deco::declref(Id id, Scope sc)
{
	assert(id);
	if(!sc)
		sc = &_global_scope;
	Decl *d = symget(id, sc, false);
	if(!d) {
		error_notdecl(id, sc);
		return 0;
	}
	return type_declref(d);
}

void
Deco::BuildArray(Item *r, Item_ array, Item_ item, Item_)
{
	PRINT_ACTION(r, array, item, none);
	switch(array.token) {
	case EXPRLIST:
		*r = array;
		break;
	case NONE:
		r->token = EXPRLIST;
		r->tuple.right = ExprList(ARRAY);
		break;
	default:
		assertfail(array.token == EXPRLIST|NONE);
		break;
	}
	if(item.token != NONE)
		((ExprList&)r->tuple.right) += exprof(item);
}

void
Deco::BuildExprList(Item *r, Item_ explist, Item_ item, Item_)
{
	PRINT_ACTION(r, explist, item, none);
	switch(explist.token) {
	case EXPRLIST:
		*r = explist;
		break;
	case NONE:
		r->token = EXPRLIST;
		r->tuple.right = ExprList(EXPRLIST);
		break;
	default:
		assertfail(explist.token == EXPRLIST|NONE);
		break;
	}
	if(item.token != NONE)
		((ExprList&)r->tuple.right) += exprof(item);
}

void
Deco::BuildStmtList(Item *r, Item_ stmtlist, Item_ item, Item_ scope)
{
	PRINT_ACTION(r, stmtlist, item, none);
	Scope sc = get_symtab(scope);
	switch(stmtlist.token) {
	case STMTLIST:
		*r = stmtlist;
		break;
	case NONE:
		r->token = STMTLIST;
		r->tuple.right = StmtList(sc);
		break;
	default:
		assertfail(stmtlist.token == STMTLIST|NONE);
		break;
	}
	if(item.token != NONE) {
		Stmt s = stmtof(item);
		((StmtList&)r->tuple.right) += stmtof(item);
	}
}

void
Deco::IfElse(Item *r, Item_ cond, Item_ st, Item_ sf)
{
	PRINT_ACTION(r, cond, st, sf);
	r->token = STMT;
	r->tuple.right = StmtIfElse(exprof(cond), stmtof(st), stmtof(sf));
}

void
Deco::While(Item *r, Item_ sc, Item_ s, Item_ )
{
	PRINT_ACTION(r, sc, s, none);
	r->token = STMT;
	r->tuple.right = StmtWhile(exprof(sc), stmtof(s));
}

void
Deco::DoWhile(Item *r, Item_ , Item_ s, Item_ ec)
{
	PRINT_ACTION(r, none, s, ec);
	r->token = STMT;
	r->tuple.right = StmtDoWhile(stmtof(s), exprof(ec));
}

void
Deco::ForExpr(Item *r, Item_ i, Item_ c, Item_ n)
{
	PRINT_ACTION(r, i, c, n);
	r->token = EXPR;
	r->tuple.right = ExprFor(stmtof(i), exprof(c), exprof(n));
}

void
Deco::For(Item *r, Item_ fe, Item_ s, Item_ )
{
	PRINT_ACTION(r, fe, s, none);
	r->token = STMT;
	r->tuple.right = StmtFor(exprof(fe), stmtof(s));
}

void
Deco::Switch(Item *r, Item_ v, Item_ s, Item_ )
{
	PRINT_ACTION(r, v, s, none);
	r->token = STMT;
	r->tuple.right = StmtSwitch(exprof(v), stmtof(s));
}

void
Deco::Return(Item *r, Item_ v, Item_ , Item_ )
{
	PRINT_ACTION(r, v, none, none);
	r->token = STMT;
	r->tuple.right = StmtReturn(exprof(v));
}

void
Deco::Label(Item *r, Item_ type, Item_ name, Item_ stmt)
{
	PRINT_ACTION(r, type, name, stmt);
	r->token = STMT;
	switch(type.token) {
	case NONE: r->tuple.right = StmtLabel(exprof(name), stmtof(stmt)); break;
	case KW_CASE: r->tuple.right = StmtCase(exprof(name), stmtof(stmt)); break;
	case KW_DEFAULT: r->tuple.right = StmtDefault(stmtof(stmt)); break;
	default:
		INTERNAL_ERROR;
	}
}

void
Deco::CtorInit(Item *r, Item_ ct, Item_ st, Item_)
{
	PRINT_ACTION(r, ct, st, none);
	r->tuple.right = ExprCtor(stmtlistof(ct), stmtlistof(st));
	r->token = EXPR;
}


void
Deco::Cast(Item *r, Item_ arg, Item_ type, Item_ ctype)
{
	PRINT_ACTION(r, arg, type, none);
	Item a;
	if(makeconst(&a, arg)) {
		r->cptr = realtype(decl_type(type));
		r->token = LITERAL;
		r->val = a.val;
	} else {
		assert(type.token == TYPELIST);
		Expr ev = exprof(arg);
		Token t = ctype.token == NONE ? OP_CAST : ctype.token;
		type_t nt = decl_type(type);
		Expr e(t, ev, nt);
		e.set_rtype(realtype(nt));
		r->tuple.right = e;
		r->token = EXPR;
	}
}


int
Deco::_typematch(type_t to, type_t from)
{
	if(from == to)
		return 1;
	if(Qualifier *qfrom = dcast_Qualifier(from)) {
		if(Qualifier *qto = dcast_Qualifier(to)) {
			return _typematch(qto->rtype(), qfrom->rtype());
		}
	} else if(Qualifier *qto = dcast_Qualifier(to)) {
		return 1+_typematch(qto->rtype(), from);
	} else if(PtrTo *pfrom = dcast_PtrTo(from)) {
		if(PtrTo *pto = dcast_PtrTo(to)) 
			return _typematch(pto->rtype(), pfrom->rtype());
	} else if(Array *afrom = dcast_Array(from)) {
		if(Array *ato = dcast_Array(to)) {
			if(afrom->size() != ato->size())
				return 0;
			return _typematch(ato->rtype(), afrom->rtype());
		}
		if(PtrTo *pto = dcast_PtrTo(to)) 
			return 1+_typematch(pto->rtype(), afrom->rtype());
	} else if(Func *ffrom = dcast_Func(from)) {
/*	
		if(PtrTo *pto = dcast_PtrTo(to)) 
			return _typematch(pto->rtype(), from);
*/
		if(Func *fto = dcast_Func(to)) {
			int cr = _typematch(fto->rtype(), ffrom->rtype());
			int cp = _typematch(fto->psig(), ffrom->psig());
			if(cr && cp)
				return cr+cp;
		}
/*
	} else if(Params *pfrom = dcast_Params(from)) {
		if(Params *pto = dcast_Params(to)) {
			uint np = pfrom->nparam();
			if(np != pto->nparam() 
			  || pto->is_variable() != pfrom->is_variable())
				return 0;
			for(uint i=0; i<np; i++) {
				if(pto->ptype(i) != pfrom->ptype(i))
					return 0;
			}
			return 1;
		} 
*/
	} else if(Method *mfrom = dcast_Method(from)) {
		if(Method *mto = dcast_Method(to)) {
			if(mto->otype() != mfrom->otype())
				return 0;
			return _typematch(mto->ftype(), mfrom->ftype());
		}
	} else if(Integral *ifrom = dcast_Integral(from)) {
		if(Integral *ito = dcast_Integral(to)) 
			return 2;
	}
	return 0;
}

int
Deco::typematch(type_t to, type_t from)
{
	if(from == to)
		return 1;
	if(Qualifier *qfrom = dcast_Qualifier(from)) 
		from = qfrom->rtype();
	if(Qualifier *qto = dcast_Qualifier(to)) 
		to = qto->rtype();
	if(PtrTo *pto = dcast_PtrTo(to)) {
		if(pto->rtype() == &Type_void
				&& from->is_type(Type_PtrTo))
			return 1;
	}
	if(Func *ffrom = dcast_Func(from)) {
		if(PtrTo *pto = dcast_PtrTo(to))
			to = pto->rtype();
	}
	return _typematch(to, from);
}

int
Deco::fnmatch(type_t ft, ExprList arg)
{
	Func *f;
	if(Method *meth = dcast_Method(ft))
		f = dcast_Func(meth->ftype());
	else
		f = dcast_Func(ft);
	Params *p = dcast_Params(f->psig());
	uint np = arg.len();
	uint c = 1;
	if(np < p->nparam())
		return 0;
	else if(np > p->nparam()) {
		if(p->is_variable())
			c += (1<<15);	// big enough to override all
					// non-elipsis matches
		else
			return 0;
	}
	Expr ep = arg.first();
	for(uint i=0; i<p->nparam(); i++, ep = ep.next()) {
		uint cc = typematch(p->ptype(i), ep.rtype());
		// printf("p[%d]=%d\n", i, cc);
		if(!cc) {
			// this breaks type-safety, but C++
			// gives us no choice since 0 == NULL
			if(ep.op() == LITERAL && ep.lit().type == &Type_int
			   && ep.lit().val == 0
			   && p->ptype(i)->is_type(Type_PtrTo))
				c += 3;
			else	
				return 0;
		}
		c += cc;
	}
	return c;
}
 
Decl *
Deco::findfn(Decl *df, ExprList arg)
{
	Decl *dm = 0;
	bool ambig = false;
	uint c = (uint)-1;
	Decl *d;
	for(d = df; d; d = d->link) {
		uint cc = fnmatch(d->rtype, arg);
		if(cc && cc <= c) {
			if(cc == c)
				ambig = true;
			else {
				c = cc;
				dm = d;
				ambig = false;
			}
		}	
	}
	if(!dm) {
		char buf[MAXTYPELEN];
		char *bp = buf;
		ccout.scoped_string(df, buf);
		bp += strlen(bp);
		bp += sprintf(bp, "(");
		Expr ep = arg.first();
		while(ep) {
			ccout.type_string(ep.rtype(), bp);
			bp += strlen(bp);
			if((ep = ep.next()))
				bp += sprintf(bp, ", ");
		}
		bp += sprintf(bp, ")");
		error("no match for call `%s'", buf);
		mprintf("", "must correspond to one of:");
		c = 0;
	}
	if(ambig) {
		error("ambiguous function call");
		mprintf("", "could be any of:");
		dm = 0;
	}
	if(!dm) {
		for(d = df; d; d = d->link) {
			char buf[MAXTYPELEN];
			uint cc = fnmatch(d->rtype, arg);
			if(cc == c || !c) {
				ccout.scoped_string(d, buf);
				ccout.type_string(realtype(d->ptype), buf);
				mprintf("", "  %s", buf);
			}
		}
		dm = df;
	}
	return dm;
}

void
Deco::Call(Item *r, Item_ f, Item_ a, Item_ )
{
	PRINT_ACTION(r, f, a, none);
	if(f.token == NONE)
		return;
	r->token = EXPR;
	Expr ef = exprof(f);
	ExprList ela = ExprList(exprof(a));
	if(ef.op() == VAL_ID)
		ef = Expr(findfn(ef.decl(), ela));
	ExprCall e(ef, ela);
	type_t ft = ef.rtype();
	type_t rt = ft;

	Func *f1;
	if(Method *meth = dcast_Method(ft))
		f1 = dcast_Func(meth->ftype());
	else
		f1 = dcast_Func(ft);

	if(f1) {
		rt = f1->rtype();
	} else {
		char buf[MAXTYPELEN];
		buf[0] = '\0';
		error("`%s' is not a function", ccout.type_string(ft, buf));
	}
	e.set_rtype(realtype(rt));
	r->tuple.right = e;
}

void
Deco::CtorCall(Item *r, Item_ cc, Item_ a, Item_ )
{
	PRINT_ACTION(r, cc, a, none);
	Decl *d = (Decl*)cc.tuple.right;
	Item cf = cc;
	cf.token = VAL_ID;
	if(cc.token == VAL_ID) {
		r->token = EXPR;
		r->tuple.right = ExprCall(Expr((Decl*)cc.tuple.right), ExprList(exprof(a)));
		return;
	}
	if(Aggr *aggr = dcast_Aggr(d->rtype)) {
		char buf[MAXIDLEN];
		sprintf(buf, " %s", _strtab.string(aggr->name()));
		Scope s = aggr->scope();
		if(!s)
			error("not yet defined");
		else if(!(cf.tuple.right = s.get(_strtab.id(buf))))
			error("no constructor");
		else {
			Call(r, cf, a, none);
			r->token = EXPR;
			r->tuple.right = ExprCall(Expr((Decl*)cc.tuple.right), ExprList(exprof(a))).set_rtype(d->rtype);
		}
	} else
		error("not an aggr");
}

void
Deco::Goto(Item *r, Item_ type, Item_ name, Item_ )
{
	PRINT_ACTION(r, type, name, none);
	switch(type.token) {
	case KW_BREAK: r->tuple.right = StmtBreak(); break;
	case KW_CONTINUE: r->tuple.right = StmtContinue(); break;
	case NONE: r->tuple.right = StmtGoto(exprof(name)); break;
	default:
		INTERNAL_ERROR;
	}
	r->token = STMT;
}

/////////////////////////////////////////////////////////////////////////////
// test stuff

void
Deco::codetest(Scope scope, StmtList &s)
{
	Decl *d;
	d = newdecl(_strtab.id("x"), scope, declref(_id_D_Type));
	d->set_storage(Decl::sc_static).set_const();
	s += StmtDecl(d);	
	s += StmtIfElse(Lit(&Type_int, 3),
			Stmt(OP_ASN, d, Lit(&Type_int, 7)),
			Stmt((StmtList()
				+= Stmt(OP_ASN, d, Lit(&Type_int, 9)))
				+= Stmt(OP_ASN, d, Lit(&Type_int, 16))
			));
	Stmt s3 = Stmt((StmtList()
			+= Stmt(OP_ASN, d, Lit(&Type_int, 9)))
			+= Stmt(OP_ASN, d, Lit(&Type_int, 16)));
	Stmt s1 = StmtIfElse(Lit(&Type_int, 3),
			Stmt(OP_ASN, d, Lit(&Type_int, 7)), s3);
	Stmt s2 = StmtIfElse(Lit(&Type_int, 4),
			Stmt(OP_ASN, d, Lit(&Type_int, 9)), s1);
	s2 = StmtIfElse(Lit(&Type_int, 5),
			Stmt(OP_ASN, d, Lit(&Type_int, 10)), s2);
	s += s2;
	s += StmtWhile(Lit(&Type_int, 1),
			Stmt(OP_ASN, d, Lit(&Type_int, 10)));
	s += StmtDoWhile( Stmt(OP_ASN, d, Lit(&Type_int, 10)),
			Lit(&Type_int, 1));
	s += StmtWhile(Lit(&Type_int, 1), s3);
	s += StmtDoWhile( s3, Lit(&Type_int, 2));
	s += StmtFor(Stmt(OP_ASN, d, Lit(&Type_int, 1)),
			Expr(OP_LSS, d, Lit(&Type_int, 10)),
			Expr(OP_INCP, d),
			s2);

	StmtList cs;
	s1 = StmtIfElse(Lit(&Type_int, 3),
			Stmt(OP_ASN, d, Lit(&Type_int, 7)), s3);
	s2 = StmtIfElse(Lit(&Type_int, 4),
			Stmt(OP_ASN, d, Lit(&Type_int, 9)), s1);
	s2 = StmtIfElse(Lit(&Type_int, 5),
			Stmt(OP_ASN, d, Lit(&Type_int, 10)), s2);
	s3 = Stmt((StmtList()
			+= Stmt(OP_ASN, d, Lit(&Type_int, 9)))
			+= Stmt(OP_ASN, d, Lit(&Type_int, 16)));
	cs += StmtCase(Lit(&Type_int, 2));
	cs += s1;
	cs += StmtBreak();
	cs += StmtCase(Lit(&Type_int, 3));
	cs += s2;
	cs += StmtBreak();
	cs += StmtCase(Lit(&Type_int, 4));
	cs += s3;
	cs += StmtBreak();
	cs += StmtCase(Lit(&Type_int, 5));
	cs += Stmt(StmtList());
	cs += StmtCase(Lit(&Type_int, 6));
	s2 = StmtIfElse(Lit(&Type_int, 4),
			Stmt(OP_ASN, d, Lit(&Type_int, 9)), s1);
	cs += Stmt(StmtList() += s2);
	cs += StmtDefault();
	s3 = Stmt((StmtList()
			+= Stmt(OP_ASN, d, Lit(&Type_int, 9)))
			+= Stmt(OP_ASN, d, Lit(&Type_int, 16)));
	cs += s3;
	s += StmtSwitch(Expr(OP_ADD, d, Lit(&Type_int, 3)), Stmt(cs));
}

/////////////////////////////////////////////////////////////////////////////
// DC++ to C++ conversions


void
Deco::convert_dtype(Decl *decl, Scope sc)
{
	Id id = decl->id;
	Aggr *aggr = (Aggr*)decl->rtype->ptr();
	Scope ags = aggr->scope();
	Decl *d;
	// FuncParam *fparam;
	Decl *dbt = symfind(_id_BTRef, &_global_scope, true);
	if(!_global_scope.get(_id__dcobj)) {
		error("must include <dcobj> to declare dynamic type '%s'",
			string(id));
		return;
	}

	Decl *dc = newdecl(id, sc,
			decl->rtype, Decl::dt_class, false);
	dc->odeclared = false;
	sc.set(id, decl);
	inherit(dc, dbt, TDecl::ac_public);

	// gen:  static D_Type dtobj;
	Decl *ddtobj = newdecl(_id_dtobj, ags, declref(_id_D_Type));
	ddtobj->set_storage(Decl::sc_static).set_const();

	type_t ptype[2];
	Param param;
	param.clear();
	param.defval = NONE;

	// member fns
	Id fid;
	for(int n=0; (fid = ags.index(n)); n++) {
		if(!idreal(fid))
			continue;
		for(d = ags.get(fid); d; d = d->link) {
			if(!d->is_Method() || d->where < 0)
				continue;
			if(d->initialized) {
				char buf[MAXIDLEN];
				sprintf(buf, "default__%s", _strtab.string(d->id));
				Decl *nd = new Decl;
				*nd = *d;
				nd->id = _strtab.id(buf);
				nd->where = -1;
				decladd(nd);
			}
			Method *m = (Method*)d->rtype->ptr();
			Func *f = (Func*)m->ftype()->ptr();
			Decl *dthis = symget(_id_this, f->scope(), false);
			assert(dthis);
			d->initialized = true;
			StmtList s(f->scope());
			d->set_inline().set_val(s);
			ExprList p;
			for(uint i=0; i<f->nparam(); i++) {
				Decl *pd = param_decl(f, i);
				if(!pd || !pd->id) {
					char buf[10];
					sprintf(buf, "_a%d_", i);
					Id id = _strtab.id(buf);
					if(pd)
						declrename(pd, id);
					else {
						Param np;
						np.type = f->ptype(i);
						np.name = id;
						np.defval.token = NONE;
						pd = addparam(i, f->scope(), np);
					}
				}
				p += Expr(pd);
			}
			Expr e = Expr(OP_INDEX, symget(_id__itabp, ags, false),
						Lit(&Type_int, (int)(d->where+1)));
			e = Expr(KW_RCAST, e, Expr(type_ptrto(d->rtype)));
			e = Expr(OP_MPTRTO, Expr(dthis), e);
			e = ExprCall(e, p);
			s += StmtReturn(e);
		}
	}

	// gen: inline x(Obj &o, _DCBind) : BTRef(dtobj, &o) {}
	Scope pscope = new SymTab(ags, 0);
	param.type = type_refto(declref(_id_Obj));
	param.name = _id_o;
	ptype[0] = param.type;
	addparam(0, pscope, param);
	param.type = declref(_id__DCBind, ags);
	param.name = 0;
	ptype[1] = param.type;
	addparam(1, pscope, param);
	type_t psig = type_psig(2, ptype);
	type_t fn = type_func(0, psig, pscope);
	newdecl(id, ags, fn)
		->set_inline().set_val(ExprCtor(StmtList(pscope)
			+= StmtCall(Expr(dbt),
				(ExprList() += ddtobj)
				 += symget(_id_o, pscope, false)),
			StmtList()));

	// gen: inline x() : BTRef(dtobj) { _objp = 0; }
	pscope = new SymTab(ags, 0);
	psig = type_psig(0, 0);
	fn = type_func(0, psig, pscope);
	StmtList s2;
	newdecl(id, ags, type_func(0, psig, pscope))
		->set_inline().set_val(ExprCtor(StmtList(pscope)
			+= StmtCall(Expr(dbt),
				(ExprList() += ddtobj)), s2));
	d = symfind(_id__objp, ags, false);
	s2 += Stmt(Expr(OP_ASN, Expr(d), Lit(&Type_int, 0)));
	
	// gen: class instance : Obj {};
	d = forwref(Decl::dt_class, _id_instance, ags);
	// classdef
	((Aggr*)d->rtype->ptr())->set_scope(new SymTab(d->scope, d));
	inherit(d, symfind(_id_Obj, &_global_scope, true), TDecl::ac_public);

	

	if(0)
		codetest(ags, s2);

	decl->dtype = Decl::dt_class;
	/*
	Decl dtmp = *d;
	*d = *decl;
	*decl = dtmp;
	d = decl;
	*/
}


void
Deco::convert_dclass(Decl *decl, Scope /*sc*/)
{
	if(!_global_scope.get(_id__dcobj)) {
		error("must include <dcobj> to declare dynamic class '%s'",
			string(decl->id));
		return;
	}
}

void
Deco::convert_scope(Scope sc)
{
	Id id;
	for(int n=0; (id = sc.index(n)); n++) {
		Decl *decl = sc.get(id);
		type_t type = decl->rtype;
		if(Aggr *aggr = dcast_Aggr(type)) {
			if(Scope subsym = aggr->scope()) 
				convert_scope(subsym);
		}
		if(decl->dtype == Decl::dt_dtype) 
			convert_dtype(decl, sc);
		else if(decl->dtype == Decl::dt_dclass) 
			convert_dclass(decl, sc);
	}
}

void
Deco::convert()
{
	convert_scope(&_global_scope);
}


void
Deco::predefine()
{
	if(!_cxx)
		return;

	_id_this = _strtab.id("this");

	if(!_dcxx)
		return;

	_id__dcobj = _strtab.id("__dcobj__");

	_id_instance = _strtab.id("instance");
	_id_dtobj = _strtab.id("dtobj");
	_id_dataclass = _strtab.id("dataclass");
	_id_dbind = _strtab.id("dbind");
	_id_dcbind = _strtab.id("dcbind");
	_id_o = _strtab.id("o");
	_id_btrp = _strtab.id("btrp");
	_id__objp = _strtab.id("_objp");
	_id__itabp = _strtab.id("_itabp");

	_id_BTRef = _strtab.id("BTRef");
	_id_Obj = _strtab.id("Obj");
	_id__DCBind = _strtab.id("_DCBind");
	_id_D_Type = _strtab.id("D_Type");
	_id_D_Class = _strtab.id("D_Class");
/*
	forwref(Decl::dt_class, (_id_BTRef = _strtab.id("BTRef")));
	Decl *d = forwref(Decl::dt_class, (_id_Obj = _strtab.id("Obj")));
	((Aggr*)d->rtype->ptr())->set_scope(new SymTab(d->scope, d));
	forwref(Decl::dt_class, (_id__DCBind = _strtab.id("_DCBind")));
	forwref(Decl::dt_class, (_id_D_Type = _strtab.id("D_Type")));
	forwref(Decl::dt_class, (_id_D_Class = _strtab.id("D_Class")));
*/
}


/////////////////////////////////////////////////////////////////////////////

void
Deco::veprintf(csz *fmt, va_list args)
{
	vfprintf(stderr, fmt, args);
}



void
Deco::abort_exit()
{
	exit(1);
}


void
Deco::internal_error_exit()
{
	mprintf("", "Please send a full bug report to `shaggy@csh.rit.edu'");
	exit(2);
}



Id Deco::string_id(Token /*token*/, csz *str)
{
	return _strtab.id(str);
}


bool
Deco::open_input(csz *fname, csz *cpp)
{
	_input_fd = fname ? open(fname, O_RDONLY) : 0;
	if(_input_fd >= 0 && cpp) {
		char c[1024];
		sprintf(c, "%s %s", cpp, fname ? fname : "-");
		FILE *f = popen(c, "r");
		_input_fd = fileno(f);
	}
	_clex.source(fname ? fname : "");
	return _input_fd >= 0;
}


void
Deco::close_input()
{
	if(_input_fd)
		close(_input_fd);
}


void
Deco::get_input(bool split_token)
{
	if(isatty(_input_fd) && !split_token) 
		write(1, (complete() ? "> " : "? "), 2);
	int n = read(_input_fd, _buf, _bufsize);
	if(n <= 0)
		_clex.set_eof();
	else
		_clex.set_src(_buf, n);
}
 

int
main(int argc, sz **argv)
{
	char strbuf[4096];
	char bufbuf[4096];
	char namebuf[1024];
	bool stop_occ = false;
	bool stop_cpp = false;
	bool nolang = true;
	csz *cpp = DECOCPP " " DECODEFINES " " DECOINCLUDES " ";
//	csz *cxx = DECOCXX;
	int rv = 0;
	
	CLex lex(strbuf, sizeof(strbuf));
	lex.set_buf(bufbuf, sizeof(bufbuf));
	lex.set_name(namebuf, sizeof(namebuf));
	lex.parse_keywords();
	lex.parse_space(false).parse_indent(false);
//	lex.parse_cpp();
//	lex.parse_eol();
//	lex.parse_indent();

	/* XXX - main loop needs restructuring.... */

	StrTab strtab;
	SymTab symtab(&strtab);
	Decl gdecl;
	gdecl.clear();
	Scope(&symtab).decl(&gdecl);
	Deco deco(lex, strtab, symtab);
	CParse cparse(lex, deco);

	int argnum = 0;
	while(++argnum < argc) {
		if(!strcmp(argv[argnum], "-dC")) 
			cparse.debug(true);
		else if(!strcmp(argv[argnum], "-dA"))
			debugaction = true;
		else if(!strcmp(argv[argnum], "-dy"))
			cparse.yydebug(true);
		else if(!strcmp(argv[argnum], "-da")) {
			debugaction = true;
			cparse.yydebug(true);
		} else if(!strcmp(argv[argnum], "-dT"))
			deco.ccout.debugtypes();
		else if(!strcmp(argv[argnum], "-nc"))
			cpp = 0;
		else if(!strcmp(argv[argnum], "-ddef"))
			deco._notedef = true;
		else if(!strcmp(argv[argnum], "-ddecl"))
			deco._notedecl = true;
		else if(!strcmp(argv[argnum], "-OCC"))
			stop_occ = true;
		else if(!strcmp(argv[argnum], "-E"))
			stop_cpp = true;
		else if(!strcmp(argv[argnum], "-fsavecode"))
			cparse.savecode(true);
		else if(!strcmp(argv[argnum], "-frealtypes"))
			deco.realtypes();
		else if(!strcmp(argv[argnum], "-ffullscope"))
			deco.ccout.fullscope();
		else if(!strcmp(argv[argnum], "-x")) {
			++argnum;
			nolang = false;
			if(!strcmp(argv[argnum], "c"))
				deco.parse_c();
			else if(!strcmp(argv[argnum], "c-hdr"))
				deco.parse_h();
			else if(!strcmp(argv[argnum], "c-header"))
				deco.parse_h();
			else if(!strcmp(argv[argnum], "c++"))
				deco.parse_cxx();
			else if(!strcmp(argv[argnum], "c++hdr"))
				deco.parse_hxx();
			else if(!strcmp(argv[argnum], "dc++"))
				deco.parse_dcxx();
			else if(!strcmp(argv[argnum], "dc++hdr"))
				deco.parse_dhxx();
			else if(!strcmp(argv[argnum], "none"))
				nolang = true;
			else {
				printf("unknown language\n");
				exit(1);
			}
		}
		else if(!strcmp(argv[argnum], "-v")) {
			printf("deco " deco_VER_STR " ("
				deco_VER_DATE ")\n");
			return 0;
		} else
			break;
	}
	do {
		csz *fname = (argnum < argc) ? argv[argnum] : 0;
		if(nolang) {
		    deco.parse_dcxx();
		    if(fname) {
			ParsePath p(fname);
			csz *ext = p.get_ext(".");
			if(ext) {
				if(strcmp(ext, "c") == 0) 
					deco.parse_c();
				else if(strcmp(ext, "cc") == 0
						|| strcmp(ext, "C") == 0
						|| strcmp(ext, "c++") == 0
						|| strcmp(ext, "cxx") == 0)
					deco.parse_cxx();
				else if(strcmp(ext, "dh") == 0)
					deco.parse_dhxx();
				else if(strcmp(ext, "hh") == 0
						|| strcmp(ext, "H") == 0)
					deco.parse_hxx();
				else if(strcmp(ext, "h") == 0)
					deco.parse_h();
			}
		    }
		}
		if(!deco.open_input(fname, cpp)) {
			perror(fname ? fname : "stdin" );
			break;
		}
		if(stop_cpp) {
			while(!lex.eof()) {
				while(!lex.is_empty()) {
					printf("%c", lex.get_char());
					lex.next_char();
				} 
				deco.get_input(false);
			}
			continue;
		}
		deco.predefine();
		if(!cparse.parse()) 
			deco.mprintf("", "cannot recover from previous errors");
		if(deco.errors() == 0)
			deco.convert();
		if(deco.warnings() > 0)
			deco.mprintf("", "%d warning%s",
				deco.warnings(), deco.warnings() == 1 ? "" : "s");
		if(deco.errors() > 0) {
			deco.mprintf("", "%d error%s",
				deco.errors(), deco.errors() == 1 ? "" : "s");
			rv = 1;
		} else {
			deco.ccout.oprintf("// this file was auto-generated"
				" by deco " deco_VER_STR " from the command:");
			deco.ccout.onl();
			deco.ccout.oprintf("//\t");
			for(int i=0; i<argc; i++)
				deco.ccout.oprintf("%s ", argv[i]);
			deco.ccout.onl(2); 
			deco.print_syms();
		}
		deco.close_input();
	} while(++argnum < argc);
	return rv;
}

