/* $Id$
 *
 * BuiltinSymbolTable: SymbolTable preloaded with symbols from std.standard.
 *
 * Copyright (C) 2008-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#if 1 /* obsolete, use <cstdint> instead. */
/* want INT64_MAX and others defined in c++ */
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif /* __STDC_LIMIT_MACROS */
extern "C" {
#include "stdint.h"
};
#endif

#include <climits>
#include <cfloat>
#include <cassert>

#include "frontend/misc/BuiltinSymbolTable.hpp"
#include "frontend/ast/Library.hpp"
#include "frontend/ast/Package.hpp"
#include "frontend/ast/FunctionDeclaration.hpp"
#include "frontend/ast/ConstantDeclaration.hpp"
#include "frontend/ast/EnumerationType.hpp"
#include "frontend/ast/UnconstrainedArrayType.hpp"
#include "frontend/ast/ConstInteger.hpp"
#include "frontend/ast/ConstReal.hpp"
#include "frontend/ast/RangeConstraintType.hpp"
#include "frontend/ast/NodeFactory.hpp"
#include "frontend/ast/PhysicalTypeUnit.hpp"
#include "frontend/ast/AttributeDeclaration.hpp"

namespace ast {

BuiltinSymbolTable::BuiltinSymbolTable() : mayAddStdStandard(false)
{
	this->registerBuiltinSymbols();
	this->mayAddStdStandard = true;
}

void
BuiltinSymbolTable::pushNewRegion(void)
{
	SymbolTable::pushNewRegion();
	if ((this->regionStack.size() == 2) && this->mayAddStdStandard) {
		// must be a library unit
		// add "library std;"
		this->addlibrary("std");

		// and add "using std.standard;"
		std::list<Symbol*> c = this->lookup("std");
		assert(c.size() == 1);

		Symbol *sym = c.front();
		assert(sym->type == SYMBOL_LIBRARY);
		assert(sym->region != NULL);

		c = sym->region->lookup("standard");
		assert(c.size() == 1);
		sym = c.front();
		assert(sym->type == SYMBOL_PACKAGE);
		assert(sym->region != NULL);
		this->importSymbols(*sym->region);
	}
}


void
BuiltinSymbolTable::registerBuiltinSymbols(void)
{
	Library *std = new Library(new std::string("std"));
	this->pushLibrary(*std);

	std::list<SymbolDeclaration*>* standardSyms = 
				new std::list<SymbolDeclaration*>();

	Package *standard = new Package(
					new std::string("standard"),
					NULL, 	// lib clauses 
					NULL, 	// use clauses
					standardSyms,
					Location("standard"));
	std->units.push_back(standard);

	this->registerSymbolWithRegion(SYMBOL_PACKAGE, *standard);

	this->addStandardStd(*standardSyms);

	this->popRegion(); /* package standard */
	this->popRegion(); /* library std */
	
	this->attributeRegion = new DeclarativeRegion(NULL);
	assert(this->regionStack.empty());
	this->regionStack.push(this->attributeRegion);
	this->addAttributes();
	this->popRegion(); // "__attributes__"
}

/* helper macros only for adding symbols */
// FIXME maybe templates might be better? or private member functions?
/* define an unary operator for the last defined enumeration type. 
 * The operand and the 
 * result type will be the last defined enumeration type.
 * No different type may get defined in between.
 */
#define UNOP_ENUM(name, gcBuiltinClass, cpBuiltinClass)\
	args = new std::list<ValDeclaration*>();\
	/* operand */\
	subtypeIndic = new SubtypeIndication(et, \
				Location("std.standard"));\
	c = new ConstantDeclaration(new std::string("__anonymous__"), \
				NULL,\
				subtypeIndic,\
				false,\
				Location("std.standard"));\
	args->push_back(c);\
	/* return type */\
	subtypeIndic = new SubtypeIndication(et, \
				Location("std.standard"));\
	/* create function declaration */\
	f = new FunctionDeclaration(new std::string(name),\
				args,\
				subtypeIndic,\
				true,\
				Location("std.standard"));\
	f->isBuiltin = true; \
	f->builtin = new cpBuiltinClass(); \
	f->gcBuiltin = new gcBuiltinClass(); \
	this->registerSymbol(SYMBOL_FUNCTION, *f);\
	standardSyms.push_back(f);


/* define a binary operator for the last enumeration defined type. 
 * Both operands and the
 * result type will be the last defined enumeration type.
 * No different type may get defined in between.
 */
#define BINOP_ENUM(name, gcBuiltinClass, cpBuiltinClass)\
	args = new std::list<ValDeclaration*>();\
	/* left operand */\
	subtypeIndic = new SubtypeIndication(et, \
				Location("std.standard"));\
	c = new ConstantDeclaration(new std::string("__anonymous__"), \
				NULL,\
				subtypeIndic,\
				false, \
				Location("std.standard"));\
	args->push_back(c);\
	/* right operand */\
	subtypeIndic = new SubtypeIndication(et, \
				Location("std.standard"));\
	c = new ConstantDeclaration(new std::string("__anonymous__"), \
				NULL,\
				subtypeIndic,\
				false, \
				Location("std.standard"));\
	args->push_back(c);\
	/* return type */\
	subtypeIndic = new SubtypeIndication(et, \
				Location("std.standard"));\
	/* create function declaration */\
	f = new FunctionDeclaration(new std::string(name),\
				args,\
				subtypeIndic,\
				true,\
				Location("std.standard"));\
	f->isBuiltin = true; \
	f->builtin = new cpBuiltinClass(); \
	f->gcBuiltin = new gcBuiltinClass(); \
	this->registerSymbol(SYMBOL_FUNCTION, *f);\
	standardSyms.push_back(f);


#define RANGE_CONSTRAINT_BINOP(name, left, right, result) \
	args = new std::list<ValDeclaration*>();\
	/* left operand */\
	subtypeIndic = new SubtypeIndication(left, \
				Location("std.standard"));\
	c = new ConstantDeclaration(new std::string("__anonymous__"),\
				NULL, \
				subtypeIndic, \
				false, \
				Location("std.standard"));\
	args->push_back(c);\
	/* right operand */\
	subtypeIndic = new SubtypeIndication(right, \
				Location("std.standard"));\
	c = new ConstantDeclaration(new std::string("__anonymous__"),\
				NULL, \
				subtypeIndic, \
				false, \
				Location("std.standard"));\
	args->push_back(c);\
	/* return type */\
	subtypeIndic = new SubtypeIndication(result, \
				Location("std.standard"));\
	f = new FunctionDeclaration(new std::string(name),\
				args, \
				subtypeIndic, \
				true, \
				Location("std.standard"));\
	f->isBuiltin = true; \
	this->registerSymbol(SYMBOL_FUNCTION, *f);\
	standardSyms.push_back(f);
	

/* begin an enumeration type */
#define ENUM_BEGIN(sym) \
	s = new std::string(sym);\
	enumElems = new std::list<FunctionDeclaration*>();

/* end en enumeration type */
#define ENUM_END \
	et = new EnumerationType(s, enumElems, \
				Location("std.standard"));\
	sym = this->registerSymbol(SYMBOL_TYPE, *et);\
	NodeFactory::registerEnumElems(et, *this, sym);\
	standardSyms.push_back(et);

#define RANGE_CONSTRAINT_INT(name, lower, upper) \
	s = new std::string(name);\
	i1 = new ConstInteger(lower, Location("lower bound"));\
	i2 = new ConstInteger(upper, Location("upper bound"));\
	dr = new DiscreteRange(i1, i2, DiscreteRange::DIRECTION_UP, \
				Location("constraint"));\
	r = new RangeConstraintType(s, dr, Location(name));\
	r->baseType = BASE_TYPE_INTEGER;\
	\
	this->registerSymbol(SYMBOL_TYPE, *r);\
	standardSyms.push_back(r);


#define RANGE_CONSTRAINT_REAL(name, lower, upper) \
	s = new std::string(name);\
	r1 = new ConstReal(lower, Location("lower bound"));\
	r2 = new ConstReal(upper, Location("upper bound"));\
	dr = new DiscreteRange(r1, r2, DiscreteRange::DIRECTION_UP, \
				Location("constraint"));\
	r = new RangeConstraintType(s, dr, Location(name));\
	r->baseType = BASE_TYPE_REAL;\
	\
	this->registerSymbol(SYMBOL_TYPE, *r);\
	standardSyms.push_back(r);

/** define a subtype of the *last* defined integer range constraint type */
#define RANGE_CONSTRAINT_SUBTYPE_INT(name1, lower, upper) \
	subtypeIndic = new SubtypeIndication(r, Location("std.standard")); \
	subtypeIndic->name = new std::string(name1); \
	i1 = new ConstInteger(lower, Location("lower bound")); \
	i2 = new ConstInteger(upper, Location("upper bound")); \
	dr = new DiscreteRange(i1, i2, DiscreteRange::DIRECTION_UP, \
				Location("constraint")); \
	subtypeIndic->constraint = dr; \
	this->registerSymbol(SYMBOL_TYPE, *subtypeIndic); \
	standardSyms.push_back(subtypeIndic); 

#define DEFINELOCALS() \
	std::string *s;\
	ConstReal *r1;\
	ConstReal *r2;\
	ConstInteger *i1;\
	ConstInteger *i2;\
	DiscreteRange *dr;\
	RangeConstraintType *r;\
	std::list<FunctionDeclaration*> *enumElems;\
	EnumerationType *et;\
	SubtypeIndication *subtypeIndic;\
	ConstantDeclaration *c;\
	std::list<ValDeclaration*> *args;\
	FunctionDeclaration *f;\
	Symbol *sym;


void
BuiltinSymbolTable::addStandardStd(
	std::list<SymbolDeclaration*> &standardSyms
)
{
	// tricky: each type that gets registered, will create a number
	//         of operations (cf. RegisterBuiltins and LRM 7.2).
	//         Hence the order, in which the types get registered
	//         is important.
	//         As an example, every range constraint type t will define
	//         boolean "=" (t, t). 
	//         For this, the type boolean must be defined already.
	//
	//         Operators, that apply only to specific types, will get 
	//         defined here, every generic operator will get defined
	//         via RegisterBuiltins.
	//
	//         Generic operators will get (automatically) defined *after* 
	//         a specific type was registered. That way it is possible to
	//         register the type boolean, since the generic operators of
	//         enumeration types use boolean as a result type.
	DEFINELOCALS();
	// type boolean
	ENUM_BEGIN("boolean")
	BuiltinSymbolTable::addEnumStr(*enumElems,"false");
	BuiltinSymbolTable::addEnumStr(*enumElems,"true");
	ENUM_END

	BINOP_ENUM("and", GCBuiltinsAnd, EnumAND)
	BINOP_ENUM("or", GCBuiltinsOr, EnumOR)
	BINOP_ENUM("nand", GCBuiltinsNand, EnumNAND)
	BINOP_ENUM("nor", GCBuiltinsNor, EnumNOR)
	BINOP_ENUM("xor", GCBuiltinsXor, EnumXOR)
	BINOP_ENUM("xnor", GCBuiltinsXnor, EnumXNOR)
	UNOP_ENUM("not", GCBuiltinsNot, EnumNOT)

	//type bit
	ENUM_BEGIN("bit")
	BuiltinSymbolTable::addEnumChar(*enumElems,'0');
	BuiltinSymbolTable::addEnumChar(*enumElems,'1');
	ENUM_END

	BINOP_ENUM("and", GCBuiltinsAnd, EnumAND)
	BINOP_ENUM("or", GCBuiltinsOr, EnumOR)
	BINOP_ENUM("nand", GCBuiltinsNand, EnumNAND)
	BINOP_ENUM("nor", GCBuiltinsNor, EnumNOR)
	BINOP_ENUM("xor", GCBuiltinsXor, EnumXOR)
	BINOP_ENUM("xnor", GCBuiltinsXnor, EnumXNOR)
	UNOP_ENUM("not", GCBuiltinsNot, EnumNOT)

	//type character
	ENUM_BEGIN("character")
	BuiltinSymbolTable::addEnumStr(*enumElems,"NUL"); // 0
	BuiltinSymbolTable::addEnumStr(*enumElems,"SOH");
	BuiltinSymbolTable::addEnumStr(*enumElems,"STX");
	BuiltinSymbolTable::addEnumStr(*enumElems,"ETX");
	BuiltinSymbolTable::addEnumStr(*enumElems,"EOT");
	BuiltinSymbolTable::addEnumStr(*enumElems,"ENQ");
	BuiltinSymbolTable::addEnumStr(*enumElems,"ACK");
	BuiltinSymbolTable::addEnumStr(*enumElems,"BEL");
	BuiltinSymbolTable::addEnumStr(*enumElems,"BS");
	BuiltinSymbolTable::addEnumStr(*enumElems,"HT");
	BuiltinSymbolTable::addEnumStr(*enumElems,"LF"); // 10
	BuiltinSymbolTable::addEnumStr(*enumElems,"VT");
	BuiltinSymbolTable::addEnumStr(*enumElems,"FF");
	BuiltinSymbolTable::addEnumStr(*enumElems,"CR");
	BuiltinSymbolTable::addEnumStr(*enumElems,"SO");
	BuiltinSymbolTable::addEnumStr(*enumElems,"SI");
	BuiltinSymbolTable::addEnumStr(*enumElems,"DLE");
	BuiltinSymbolTable::addEnumStr(*enumElems,"DC1");
	BuiltinSymbolTable::addEnumStr(*enumElems,"DC2");
	BuiltinSymbolTable::addEnumStr(*enumElems,"DC3");
	BuiltinSymbolTable::addEnumStr(*enumElems,"DC4"); // 20
	BuiltinSymbolTable::addEnumStr(*enumElems,"NAK");
	BuiltinSymbolTable::addEnumStr(*enumElems,"SYN");
	BuiltinSymbolTable::addEnumStr(*enumElems,"ETB");
	BuiltinSymbolTable::addEnumStr(*enumElems,"CAN");
	BuiltinSymbolTable::addEnumStr(*enumElems,"EM");
	BuiltinSymbolTable::addEnumStr(*enumElems,"SUB");
	BuiltinSymbolTable::addEnumStr(*enumElems,"ESC");
	BuiltinSymbolTable::addEnumStr(*enumElems,"FSP");
	BuiltinSymbolTable::addEnumStr(*enumElems,"GSP");
	BuiltinSymbolTable::addEnumStr(*enumElems,"RSP"); // 30
	BuiltinSymbolTable::addEnumStr(*enumElems,"USB");
	BuiltinSymbolTable::addEnumChar(*enumElems,' ');
	BuiltinSymbolTable::addEnumChar(*enumElems,'!');
	BuiltinSymbolTable::addEnumChar(*enumElems,'"');
	BuiltinSymbolTable::addEnumChar(*enumElems,'#');
	BuiltinSymbolTable::addEnumChar(*enumElems,'$');
	BuiltinSymbolTable::addEnumChar(*enumElems,'%');
	BuiltinSymbolTable::addEnumChar(*enumElems,'&');
	BuiltinSymbolTable::addEnumChar(*enumElems,'\'');
	BuiltinSymbolTable::addEnumChar(*enumElems,'('); // 40
	BuiltinSymbolTable::addEnumChar(*enumElems,')');
	BuiltinSymbolTable::addEnumChar(*enumElems,'*');
	BuiltinSymbolTable::addEnumChar(*enumElems,'+');
	BuiltinSymbolTable::addEnumChar(*enumElems,',');
	BuiltinSymbolTable::addEnumChar(*enumElems,'-');
	BuiltinSymbolTable::addEnumChar(*enumElems,'.');
	BuiltinSymbolTable::addEnumChar(*enumElems,'/');
	BuiltinSymbolTable::addEnumChar(*enumElems,'0');
	BuiltinSymbolTable::addEnumChar(*enumElems,'1');
	BuiltinSymbolTable::addEnumChar(*enumElems,'2'); // 50
	BuiltinSymbolTable::addEnumChar(*enumElems,'3');
	BuiltinSymbolTable::addEnumChar(*enumElems,'4');
	BuiltinSymbolTable::addEnumChar(*enumElems,'5');
	BuiltinSymbolTable::addEnumChar(*enumElems,'6');
	BuiltinSymbolTable::addEnumChar(*enumElems,'7');
	BuiltinSymbolTable::addEnumChar(*enumElems,'8');
	BuiltinSymbolTable::addEnumChar(*enumElems,'9');
	BuiltinSymbolTable::addEnumChar(*enumElems,':');
	BuiltinSymbolTable::addEnumChar(*enumElems,';');
	BuiltinSymbolTable::addEnumChar(*enumElems,'<'); // 60
	BuiltinSymbolTable::addEnumChar(*enumElems,'=');
	BuiltinSymbolTable::addEnumChar(*enumElems,'>');
	BuiltinSymbolTable::addEnumChar(*enumElems,'?');
	BuiltinSymbolTable::addEnumChar(*enumElems,'@');
	BuiltinSymbolTable::addEnumChar(*enumElems,'A');
	BuiltinSymbolTable::addEnumChar(*enumElems,'B');
	BuiltinSymbolTable::addEnumChar(*enumElems,'C');
	BuiltinSymbolTable::addEnumChar(*enumElems,'D');
	BuiltinSymbolTable::addEnumChar(*enumElems,'E');
	BuiltinSymbolTable::addEnumChar(*enumElems,'F'); // 70
	BuiltinSymbolTable::addEnumChar(*enumElems,'G');
	BuiltinSymbolTable::addEnumChar(*enumElems,'H');
	BuiltinSymbolTable::addEnumChar(*enumElems,'I');
	BuiltinSymbolTable::addEnumChar(*enumElems,'J');
	BuiltinSymbolTable::addEnumChar(*enumElems,'K');
	BuiltinSymbolTable::addEnumChar(*enumElems,'L');
	BuiltinSymbolTable::addEnumChar(*enumElems,'M');
	BuiltinSymbolTable::addEnumChar(*enumElems,'N');
	BuiltinSymbolTable::addEnumChar(*enumElems,'O');
	BuiltinSymbolTable::addEnumChar(*enumElems,'P'); // 80
	BuiltinSymbolTable::addEnumChar(*enumElems,'Q'); 
	BuiltinSymbolTable::addEnumChar(*enumElems,'R');
	BuiltinSymbolTable::addEnumChar(*enumElems,'S');
	BuiltinSymbolTable::addEnumChar(*enumElems,'T');
	BuiltinSymbolTable::addEnumChar(*enumElems,'U');
	BuiltinSymbolTable::addEnumChar(*enumElems,'V');
	BuiltinSymbolTable::addEnumChar(*enumElems,'W');
	BuiltinSymbolTable::addEnumChar(*enumElems,'X');
	BuiltinSymbolTable::addEnumChar(*enumElems,'Y');
	BuiltinSymbolTable::addEnumChar(*enumElems,'Z'); // 90
	BuiltinSymbolTable::addEnumChar(*enumElems,'[');
	BuiltinSymbolTable::addEnumChar(*enumElems,'\\');
	BuiltinSymbolTable::addEnumChar(*enumElems,']');
	BuiltinSymbolTable::addEnumChar(*enumElems,'^');
	BuiltinSymbolTable::addEnumChar(*enumElems,'_');
	BuiltinSymbolTable::addEnumChar(*enumElems,'`');
	BuiltinSymbolTable::addEnumChar(*enumElems,'a');
	BuiltinSymbolTable::addEnumChar(*enumElems,'b');
	BuiltinSymbolTable::addEnumChar(*enumElems,'c');
	BuiltinSymbolTable::addEnumChar(*enumElems,'d'); // 100
	BuiltinSymbolTable::addEnumChar(*enumElems,'e');
	BuiltinSymbolTable::addEnumChar(*enumElems,'f');
	BuiltinSymbolTable::addEnumChar(*enumElems,'g');
	BuiltinSymbolTable::addEnumChar(*enumElems,'h');
	BuiltinSymbolTable::addEnumChar(*enumElems,'i');
	BuiltinSymbolTable::addEnumChar(*enumElems,'j');
	BuiltinSymbolTable::addEnumChar(*enumElems,'k');
	BuiltinSymbolTable::addEnumChar(*enumElems,'l');
	BuiltinSymbolTable::addEnumChar(*enumElems,'m');
	BuiltinSymbolTable::addEnumChar(*enumElems,'n'); // 110
	BuiltinSymbolTable::addEnumChar(*enumElems,'o');
	BuiltinSymbolTable::addEnumChar(*enumElems,'p');
	BuiltinSymbolTable::addEnumChar(*enumElems,'q');
	BuiltinSymbolTable::addEnumChar(*enumElems,'r');
	BuiltinSymbolTable::addEnumChar(*enumElems,'s');
	BuiltinSymbolTable::addEnumChar(*enumElems,'t');
	BuiltinSymbolTable::addEnumChar(*enumElems,'u');
	BuiltinSymbolTable::addEnumChar(*enumElems,'v');
	BuiltinSymbolTable::addEnumChar(*enumElems,'w');
	BuiltinSymbolTable::addEnumChar(*enumElems,'x'); // 120
	BuiltinSymbolTable::addEnumChar(*enumElems,'y');
	BuiltinSymbolTable::addEnumChar(*enumElems,'z');
	BuiltinSymbolTable::addEnumChar(*enumElems,'{');
	BuiltinSymbolTable::addEnumChar(*enumElems,'|');
	BuiltinSymbolTable::addEnumChar(*enumElems,'}');
	BuiltinSymbolTable::addEnumChar(*enumElems,'~');
	BuiltinSymbolTable::addEnumStr(*enumElems,"DEL");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C128");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C129");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C130"); // 130
	BuiltinSymbolTable::addEnumStr(*enumElems,"C131");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C132");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C133");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C134");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C135");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C136");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C137");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C138");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C139");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C140"); // 140
	BuiltinSymbolTable::addEnumStr(*enumElems,"C141");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C142");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C143");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C144");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C145");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C146");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C147");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C148");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C149");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C150"); // 150 
	BuiltinSymbolTable::addEnumStr(*enumElems,"C151");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C152");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C153");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C154");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C155");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C156");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C157");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C158");
	BuiltinSymbolTable::addEnumStr(*enumElems,"C159");
	// TODO non ascii characters. not supported atm., since scanner
	// doesn't support these as well.
	ENUM_END
	TypeDeclaration *character = et;

	// severity_level
	ENUM_BEGIN("severity_level")
	BuiltinSymbolTable::addEnumStr(*enumElems,"note");
	BuiltinSymbolTable::addEnumStr(*enumElems,"warning");
	BuiltinSymbolTable::addEnumStr(*enumElems,"error");
	BuiltinSymbolTable::addEnumStr(*enumElems,"failure");
	ENUM_END

	// tricky: instead of LRM, integer *must* get defined before 
	//         universal_integer, since the exponentation of a
	//         range constraint type has integer as a result type!
	RANGE_CONSTRAINT_INT("integer", INT64_MIN, INT64_MAX)
	RANGE_CONSTRAINT_SUBTYPE_INT("natural", 0, INT64_MAX)
	RANGE_CONSTRAINT_SUBTYPE_INT("positive", 1, INT64_MAX)
	TypeDeclaration *positive = subtypeIndic;
	RANGE_CONSTRAINT_INT("__universal_integer__", INT64_MIN, INT64_MAX)
	TypeDeclaration *universal_integer = r;
	universal_integer->isUniversal = true;

	RANGE_CONSTRAINT_REAL("__universal_real__", DBL_MIN, DBL_MAX)
	TypeDeclaration *universal_real = r;
	universal_real->isUniversal = true;
	// additional operations for universal types.
	RANGE_CONSTRAINT_BINOP("*", universal_real, universal_integer, 
				universal_real);
	RANGE_CONSTRAINT_BINOP("*", universal_integer, universal_real, 
				universal_real);
	RANGE_CONSTRAINT_BINOP("/", universal_real, universal_integer, 
				universal_real);

	RANGE_CONSTRAINT_REAL("real", DBL_MIN, DBL_MAX)

	this->addStdStandardTime(standardSyms);

	// FIXME delay_length, now
	// ...

	// string
	std::list<TypeDeclaration *> *indices = 
		new std::list<TypeDeclaration*>();
	indices->push_back(positive);
	UnconstrainedArrayType *stringT = 
		new UnconstrainedArrayType(new std::string("string"), indices,
					character, Location("std.standard"));
	this->registerSymbol(SYMBOL_TYPE, *stringT);
	standardSyms.push_back(stringT);

	// "foreign" attribute
	this->addForeignAttribute(stringT, standardSyms);
}

#undef UNOP
#undef BINOP_ENUM
#undef ENUM_BEGIN
#undef ENUM_END
#undef RANGE_CONSTRAINT_INT
#undef RANGE_CONSTRAINT_REAL
#undef RANGE_CONSTRAINT_BINOP
#undef DEFINELOCALS


void
BuiltinSymbolTable::addAttributes(void)
{
	this->addAttributeDecl("event", "boolean");
	// TODO
}

/* FIXME make this a runtime parameter! */
#if 0 	/* correct VHDL */
void
BuiltinSymbolTable::addStdStandardTime(
	std::list<SymbolDeclaration*> &standardSyms
)
{
	ConstInteger *i1;
	ConstInteger *i2;
	DiscreteRange *dr;

	std::list<PhysicalTypeUnit*> *units = 
			new std::list<PhysicalTypeUnit*>();

#if 0 /* minimum simulation resolution. */
	this->addPhysUnit(standardSyms, *units, "ps", 1, NULL);
	this->addPhysUnit(standardSyms, *units, "ns", 1000, "ps");
#else
	this->addPhysUnit(standardSyms, *units, "ns", 1, NULL);
#endif
	this->addPhysUnit(standardSyms, *units, "us", 1000, "ns");
	this->addPhysUnit(standardSyms, *units, "ms", 1000, "us");
	this->addPhysUnit(standardSyms, *units, "sec", 1000, "ms");
	this->addPhysUnit(standardSyms, *units, "min", 60, "sec");
	this->addPhysUnit(standardSyms, *units, "hr", 60, "min");
	
	i1 = new ConstInteger(INT64_MIN, Location("lower bound"));
	i2 = new ConstInteger(INT64_MAX, Location("upper bound"));
	dr = new DiscreteRange(i1, i2, DiscreteRange::DIRECTION_UP,
				Location("constraint"));
	PhysicalType *timeT = new PhysicalType(
					new std::string("time"), dr,
					units,
					Location("std.standard"));
	this->registerSymbol(SYMBOL_TYPE, *timeT);
	standardSyms.push_back(timeT);
}
#else /* above: correct VHDL, below: FAUmachine base time */
void
BuiltinSymbolTable::addStdStandardTime(
	std::list<SymbolDeclaration*> &standardSyms
)
{
	ConstInteger *i1;
	ConstInteger *i2;
	DiscreteRange *dr;

	std::list<PhysicalTypeUnit*> *units = 
			new std::list<PhysicalTypeUnit*>();

	/* minimum simulation unit for FAUmachine is
	 * 1/2^32 sec
	 */
	this->addPhysUnit(standardSyms, *units, "__time_hz__", 1, NULL);
	// ns would be too unsharp (4 vs. ~4.3)
	this->addPhysUnit(standardSyms, *units, "us", (1ull << 32) / 1000000,
			"__time_hz__");
	this->addPhysUnit(standardSyms, *units, "ms", (1ull << 32) / 1000, 
			"__time_hz__");
	this->addPhysUnit(standardSyms, *units, "sec", 1ull << 32, 
			"__time_hz__");
	this->addPhysUnit(standardSyms, *units, "min", 60, "sec");
	this->addPhysUnit(standardSyms, *units, "hr", 60, "min");
	
	i1 = new ConstInteger(INT64_MIN, Location("lower bound"));
	i2 = new ConstInteger(INT64_MAX, Location("upper bound"));
	dr = new DiscreteRange(i1, i2, DiscreteRange::DIRECTION_UP,
				Location("constraint"));
	PhysicalType *timeT = new PhysicalType(
					new std::string("time"), dr,
					units,
					Location("std.standard"));
	this->registerSymbol(SYMBOL_TYPE, *timeT);
	standardSyms.push_back(timeT);
}
#endif /* correct VHDL / FAUmachine base unit switch */

void
BuiltinSymbolTable::addPhysUnit(
	std::list<SymbolDeclaration*> &standardSyms,
	std::list<PhysicalTypeUnit*> &units,
	const char *name,
	universal_integer factor,
	const char *refName
)
{
	ConstInteger *unit;

	if (refName == NULL) {
		unit = new ConstInteger(factor, Location("std.standard"));
	} else {
		std::string *s = new std::string(refName);
		SimpleName *sn = new SimpleName(s, this->lookup(*s), 
						Location("std.stdandard"));
		unit = new ConstInteger(factor, sn, Location("std.standard"));
	}

	PhysicalTypeUnit *pu = 
		new PhysicalTypeUnit(
			new std::string(name),
			unit,
			Location("std.standard"));

	this->registerSymbol(SYMBOL_UNIT, *pu);
	units.push_back(pu);
	standardSyms.push_back(pu);
}

void
BuiltinSymbolTable::addEnumChar(
	std::list<FunctionDeclaration*> &elems, 
	char c
)
{
	std::string *s = new std::string("'");
	s->append(1, c);
	s->append("'");
	FunctionDeclaration *elem = 
		new FunctionDeclaration(s, 
					NULL,
					NULL,
					true,
					Location("std.standard"));
	elems.push_back(elem);
}

void
BuiltinSymbolTable::addEnumStr(
	std::list<FunctionDeclaration*> &elems, 
	const char *s
)
{
	std::string *st = new std::string(s);
	FunctionDeclaration *elem = 
		new FunctionDeclaration(st, 
					NULL,
					NULL,
					true,
					Location("std.standard"));
	elems.push_back(elem);
}

void
BuiltinSymbolTable::addForeignAttribute(
	const TypeDeclaration *stringT,
	std::list<SymbolDeclaration *> &standardSyms
)
{
	AttributeDeclaration *a = 
		new AttributeDeclaration(new std::string("foreign"),
					stringT,
					Location("std.standard"));
	this->registerSymbol(SYMBOL_ATTRIBUTE, *a);
	standardSyms.push_back(a);
}

void
BuiltinSymbolTable::addAttributeDecl(
	const char *name,
	const char *type
)
{
	const TypeDeclaration *t = this->getStdStandardType(type);
	assert(t != NULL);

	AttributeDeclaration *a = new AttributeDeclaration(
					new std::string(name),
					t,
					Location("builtin"));
	this->registerSymbol(SYMBOL_ATTRIBUTE, *a);
	// FIXME an Attribute should have either an 
	// associated constant builtin function,
	// an associated inline builtin generator
	// or both
	// or a constant value related to it.
	//
	// FIXME result types can be variadic!
}

}; /* namespace ast */
