<commit_before>#include "ExpressionTree.h"

#include "ExpressionEval.h"

#define FMT_ISTR(x) unsigned(x.end - x.begin), x.begin

namespace
{
	jmp_buf errorHandler;

	void Stop(ExpressionContext &ctx, const char *pos, const char *msg, va_list args)
	{
		ctx.errorPos = pos;

		char errorText[4096];

		vsnprintf(errorText, 4096, msg, args);
		errorText[4096 - 1] = '\0';

		ctx.errorMsg = InplaceStr(errorText);

		longjmp(errorHandler, 1);
	}

	void Stop(ExpressionContext &ctx, const char *pos, const char *msg, ...)
	{
		va_list args;
		va_start(args, msg);

		Stop(ctx, pos, msg, args);
	}

	unsigned char ParseEscapeSequence(ExpressionContext &ctx, const char* str)
	{
		assert(str[0] == '\\');

		switch(str[1])
		{
		case 'n':
			return '\n';
		case 'r':
			return '\r';
		case 't':
			return '\t';
		case '0':
			return '\0';
		case '\'':
			return '\'';
		case '\"':
			return '\"';
		case '\\':
			return '\\';
		}

		Stop(ctx, str, "ERROR: unknown escape sequence");

		return 0;
	}

	int ParseInteger(ExpressionContext &ctx, const char* str)
	{
		(void)ctx;

		unsigned int digit;
		int a = 0;

		while((digit = *str - '0') < 10)
		{
			a = a * 10 + digit;
			str++;
		}

		return a;
	}

	long long ParseLong(ExpressionContext &ctx, const char* s, const char* e, int base)
	{
		unsigned long long res = 0;

		for(const char *p = s; p < e; p++)
		{
			int digit = ((*p >= '0' && *p <= '9') ? *p - '0' : (*p & ~0x20) - 'A' + 10);

			if(digit < 0 || digit >= base)
				Stop(ctx, p, "ERROR: digit %d is not allowed in base %d", digit, base);

			res = res * base + digit;
		}

		return res;
	}

	double ParseDouble(ExpressionContext &ctx, const char *str)
	{
		unsigned int digit;
		double integer = 0.0;

		while((digit = *str - '0') < 10)
		{
			integer = integer * 10.0 + digit;
			str++;
		}

		double fractional = 0.0;
	
		if(*str == '.')
		{
			double power = 0.1f;
			str++;

			while((digit = *str - '0') < 10)
			{
				fractional = fractional + power * digit;
				power /= 10.0;
				str++;
			}
		}

		if(*str == 'e')
		{
			str++;

			if(*str == '-')
				return (integer + fractional) * pow(10.0, (double)-ParseInteger(ctx, str + 1));
			else
				return (integer + fractional) * pow(10.0, (double)ParseInteger(ctx, str));
		}

		return integer + fractional;
	}

	bool IsBinaryOp(SynUnaryOpType type)
	{
		return type == SYN_UNARY_OP_BIT_NOT;
	}

	bool IsLogicalOp(SynUnaryOpType type)
	{
		return type == SYN_UNARY_OP_LOGICAL_NOT;
	}

	const char* GetOpName(SynUnaryOpType type)
	{
		switch(type)
		{
		case SYN_UNARY_OP_PLUS:
			return "+";
		case SYN_UNARY_OP_NEGATE:
			return "-";
		case SYN_UNARY_OP_BIT_NOT:
			return "~";
		case SYN_UNARY_OP_LOGICAL_NOT:
			return "!";
		}

		assert(!"unknown operation type");
		return "";
	}

	bool IsBinaryOp(SynBinaryOpType type)
	{
		switch(type)
		{
		case SYN_BINARY_OP_SHL:
		case SYN_BINARY_OP_SHR:
		case SYN_BINARY_OP_BIT_AND:
		case SYN_BINARY_OP_BIT_OR:
		case SYN_BINARY_OP_BIT_XOR:
			return true;
		}

		return false;
	}

	bool IsComparisonOp(SynBinaryOpType type)
	{
		switch(type)
		{
		case SYN_BINARY_OP_LESS:
		case SYN_BINARY_OP_LESS_EQUAL:
		case SYN_BINARY_OP_GREATER:
		case SYN_BINARY_OP_GREATER_EQUAL:
		case SYN_BINARY_OP_EQUAL:
		case SYN_BINARY_OP_NOT_EQUAL:
			return true;
		}

		return false;
	}

	bool IsLogicalOp(SynBinaryOpType type)
	{
		switch(type)
		{
		case SYN_BINARY_OP_LOGICAL_AND:
		case SYN_BINARY_OP_LOGICAL_OR:
		case SYN_BINARY_OP_LOGICAL_XOR:
			return true;
		}

		return false;
	}

	const char* GetOpName(SynBinaryOpType type)
	{
		switch(type)
		{
		case SYN_BINARY_OP_ADD:
			return "+";
		case SYN_BINARY_OP_SUB:
			return "-";
		case SYN_BINARY_OP_MUL:
			return "*";
		case SYN_BINARY_OP_DIV:
			return "/";
		case SYN_BINARY_OP_MOD:
			return "%";
		case SYN_BINARY_OP_POW:
			return "**";
		case SYN_BINARY_OP_SHL:
			return "<<";
		case SYN_BINARY_OP_SHR:
			return ">>";
		case SYN_BINARY_OP_LESS:
			return "<";
		case SYN_BINARY_OP_LESS_EQUAL:
			return "<=";
		case SYN_BINARY_OP_GREATER:
			return ">";
		case SYN_BINARY_OP_GREATER_EQUAL:
			return ">=";
		case SYN_BINARY_OP_EQUAL:
			return "==";
		case SYN_BINARY_OP_NOT_EQUAL:
			return "!=";
		case SYN_BINARY_OP_BIT_AND:
			return "&";
		case SYN_BINARY_OP_BIT_OR:
			return "|";
		case SYN_BINARY_OP_BIT_XOR:
			return "^";
		case SYN_BINARY_OP_LOGICAL_AND:
			return "&&";
		case SYN_BINARY_OP_LOGICAL_OR:
			return "||";
		case SYN_BINARY_OP_LOGICAL_XOR:
			return "^^";
		case SYN_BINARY_OP_IN:
			return "in";
		}

		assert(!"unknown operation type");
		return "";
	}
}

ExpressionContext::ExpressionContext()
{
	errorPos = NULL;

	typeVoid = NULL;

	typeBool = NULL;

	typeChar = NULL;
	typeShort = NULL;
	typeInt = NULL;
	typeLong = NULL;

	typeFloat = NULL;
	typeDouble = NULL;

	typeTypeID = NULL;
	typeFunctionID = NULL;

	typeAuto = NULL;
	typeAutoRef = NULL;
	typeAutoArray = NULL;

	typeMap.init();
	functionMap.init();
	variableMap.init();

	genericTypeMap.init();
}

void ExpressionContext::Stop(const char *pos, const char *msg, ...)
{
	va_list args;
	va_start(args, msg);

	::Stop(*this, pos, msg, args);
}

void ExpressionContext::PushScope()
{
	unsigned depth = scopes.size();
	ScopeData *scope = scopes.empty() ? NULL : scopes.back();

	scopes.push_back(new ScopeData(depth, scope));
}

void ExpressionContext::PushScope(NamespaceData *nameSpace)
{
	unsigned depth = scopes.size();
	ScopeData *scope = scopes.empty() ? NULL : scopes.back();

	scopes.push_back(new ScopeData(depth, scope, nameSpace));
}

void ExpressionContext::PushScope(FunctionData *function)
{
	unsigned depth = scopes.size();
	ScopeData *scope = scopes.empty() ? NULL : scopes.back();

	scopes.push_back(new ScopeData(depth, scope, function));
}

void ExpressionContext::PushScope(TypeBase *type)
{
	unsigned depth = scopes.size();
	ScopeData *scope = scopes.empty() ? NULL : scopes.back();

	scopes.push_back(new ScopeData(depth, scope, type));
}

void ExpressionContext::PopScope()
{
	// Remove scope members from lookup maps
	ScopeData *scope = scopes.back();

	for(int i = int(scope->variables.size()) - 1; i >= 0; i--)
	{
		VariableData *variable = scope->variables[i];

		variableMap.remove(variable->nameHash, variable);
	}

	for(int i = int(scope->functions.size()) - 1; i >= 0; i--)
	{
		FunctionData *function = scope->functions[i];

		// Keep class functions visible
		if(function->scope->ownerType)
			continue;

		functionMap.remove(function->nameHash, function);
	}

	for(int i = int(scope->types.size()) - 1; i >= 0; i--)
	{
		TypeBase *type = scope->types[i];

		typeMap.remove(type->nameHash, type);
	}

	for(int i = int(scope->aliases.size()) - 1; i >= 0; i--)
	{
		AliasData *alias = scope->aliases[i];

		typeMap.remove(alias->nameHash, alias->type);
	}

	delete scopes.back();
	scopes.pop_back();
}

NamespaceData* ExpressionContext::GetCurrentNamespace()
{
	// Simply walk up the scopes and find the current one
	for(int i = int(scopes.size()) - 1; i >= 0; i--)
	{
		ScopeData *scope = scopes[i];

		if(NamespaceData *ns = scope->ownerNamespace)
			return ns;
	}

	return NULL;
}

FunctionData* ExpressionContext::GetCurrentFunction()
{
	// Walk up, but if we reach a type owner, stop - we're not in a context of a function
	for(int i = int(scopes.size()) - 1; i >= 0; i--)
	{
		ScopeData *scope = scopes[i];

		if(scope->ownerType)
			return NULL;

		if(FunctionData *function = scope->ownerFunction)
			return function;
	}

	return NULL;
}

TypeBase* ExpressionContext::GetCurrentType()
{
	// Walk up, but if we reach a function, return it's owner
	for(int i = int(scopes.size()) - 1; i >= 0; i--)
	{
		ScopeData *scope = scopes[i];

		if(scope->ownerFunction)
			return scope->ownerFunction->scope->ownerType;

		if(TypeBase *type = scope->ownerType)
			return type;
	}

	return NULL;
}

unsigned ExpressionContext::GetGenericClassInstantiationDepth()
{
	unsigned depth = 0;

	for(unsigned i = 0; i < scopes.size(); i++)
	{
		ScopeData *scope = scopes[i];

		if(TypeClass *type = getType<TypeClass>(scope->ownerType))
		{
			if(!type->genericTypes.empty())
				depth++;
		}
	}

	return depth;
}

void ExpressionContext::AddType(TypeBase *type)
{
	scopes.back()->types.push_back(type);

	if(!isType<TypeGenericClassProto>(type))
		assert(!type->isGeneric);

	types.push_back(type);
	typeMap.insert(type->nameHash, type);
}

void ExpressionContext::AddFunction(FunctionData *function)
{
	scopes.back()->functions.push_back(function);

	functions.push_back(function);
	functionMap.insert(function->nameHash, function);
}

void ExpressionContext::AddVariable(VariableData *variable)
{
	scopes.back()->variables.push_back(variable);

	variables.push_back(variable);
	variableMap.insert(variable->nameHash, variable);
}

void ExpressionContext::AddAlias(AliasData *alias)
{
	scopes.back()->aliases.push_back(alias);

	typeMap.insert(alias->nameHash, alias->type);
}

bool ExpressionContext::IsIntegerType(TypeBase* type)
{
	if(type == typeBool)
		return true;

	if(type == typeChar)
		return true;

	if(type == typeShort)
		return true;

	if(type == typeInt)
		return true;

	if(type == typeLong)
		return true;

	return false;
}

bool ExpressionContext::IsFloatingPointType(TypeBase* type)
{
	if(type == typeFloat)
		return true;

	if(type == typeDouble)
		return true;

	return false;
}

bool ExpressionContext::IsNumericType(TypeBase* type)
{
	return IsIntegerType(type) || IsFloatingPointType(type);
}

TypeBase* ExpressionContext::GetBinaryOpResultType(TypeBase* a, TypeBase* b)
{
	if(a == typeDouble || b == typeDouble)
		return typeDouble;

	if(a == typeFloat || b == typeFloat)
		return typeFloat;

	if(a == typeLong || b == typeLong)
		return typeLong;

	if(a == typeInt || b == typeInt)
		return typeInt;

	if(a == typeShort || b == typeShort)
		return typeShort;

	if(a == typeChar || b == typeChar)
		return typeChar;

	if(a == typeBool || b == typeBool)
		return typeBool;

	return NULL;
}

TypeRef* ExpressionContext::GetReferenceType(TypeBase* type)
{
	if(type->refType)
		return type->refType;

	// Create new type
	TypeRef* result = new TypeRef(GetReferenceTypeName(type), type);

	if(!type->isGeneric)
	{
		// Save it for future use
		type->refType = result;

		types.push_back(result);
	}

	return result;
}

TypeArray* ExpressionContext::GetArrayType(TypeBase* type, long long length)
{
	for(unsigned i = 0; i < type->arrayTypes.size(); i++)
	{
		if(type->arrayTypes[i]->length == length)
			return type->arrayTypes[i];
	}

	// Create new type
	TypeArray* result = new TypeArray(GetArrayTypeName(type, length), type, length);

	if(!type->isGeneric)
	{
		// Save it for future use
		type->arrayTypes.push_back(result);

		types.push_back(result);
	}

	return result;
}

TypeUnsizedArray* ExpressionContext::GetUnsizedArrayType(TypeBase* type)
{
	if(type->unsizedArrayType)
		return type->unsizedArrayType;

	// Create new type
	TypeUnsizedArray* result = new TypeUnsizedArray(GetUnsizedArrayTypeName(type), type);

	result->members.push_back(new VariableHandle(new VariableData(scopes.back(), 4, typeInt, InplaceStr("size"))));

	if(!type->isGeneric)
	{
		// Save it for future use
		type->unsizedArrayType = result;

		types.push_back(result);
	}

	return result;
}

TypeFunction* ExpressionContext::GetFunctionType(TypeBase* returnType, IntrusiveList<TypeHandle> arguments)
{
	for(unsigned i = 0; i < types.size(); i++)
	{
		if(TypeFunction *type = getType<TypeFunction>(types[i]))
		{
			if(type->returnType != returnType)
				continue;

			TypeHandle *leftArg = type->arguments.head;
			TypeHandle *rightArg = arguments.head;

			while(leftArg && rightArg && leftArg->type == rightArg->type)
			{
				leftArg = leftArg->next;
				rightArg = rightArg->next;
			}

			if(leftArg != rightArg)
				continue;

			return type;
		}
	}

	// Create new type
	TypeFunction* result = new TypeFunction(GetFunctionTypeName(returnType, arguments), returnType, arguments);

	if(!result->isGeneric)
	{
		types.push_back(result);
	}

	return result;
}

ExprBase* CreateCast(ExpressionContext &ctx, const char *pos, ExprBase *value, TypeBase *type)
{
	if(value->type == type)
		return value;

	if(ctx.IsNumericType(value->type) && ctx.IsNumericType(value->type))
		return new ExprTypeCast(value->source, type, value);

	if(type == ctx.typeBool)
	{
		if(isType<TypeRef>(value->type))
			return new ExprTypeCast(value->source, type, value);

		if(isType<TypeUnsizedArray>(value->type))
			return new ExprTypeCast(value->source, type, value);

		if(isType<TypeFunction>(value->type))
			return new ExprTypeCast(value->source, type, value);
	}

	Stop(ctx, pos, "ERROR: can't convert '%.*s' to '%.*s'", FMT_ISTR(value->type->name), FMT_ISTR(type->name));

	return NULL;
}

ExprBase* AnalyzeNumber(ExpressionContext &ctx, SynNumber *syntax);
ExprBase* AnalyzeExpression(ExpressionContext &ctx, SynBase *syntax);
ExprBase* AnalyzeStatement(ExpressionContext &ctx, SynBase *syntax);
ExprBlock* AnalyzeBlock(ExpressionContext &ctx, SynBlock *syntax, bool createScope);
ExprBase* AnalyzeClassDefinition(ExpressionContext &ctx, SynClassDefinition *syntax, TypeGenericClassProto *proto, IntrusiveList<TypeHandle> generics);
void AnalyzeClassElements(ExpressionContext &ctx, ExprClassDefinition *classDefinition, SynClassElements *syntax);

TypeBase* AnalyzeType(ExpressionContext &ctx, SynBase *syntax, bool onlyType = true)
{
	if(SynTypeAuto *node = getType<SynTypeAuto>(syntax))
	{
		return ctx.typeAuto;
	}

	if(SynTypeGeneric *node = getType<SynTypeGeneric>(syntax))
	{
		return new TypeGeneric(InplaceStr("generic"));
	}

	if(SynTypeAlias *node = getType<SynTypeAlias>(syntax))
	{
		TypeGeneric *type = new TypeGeneric(node->name);

		return type;
	}

	if(SynTypeReference *node = getType<SynTypeReference>(syntax))
	{
		TypeBase *type = AnalyzeType(ctx, node->type);

		if(isType<TypeAuto>(type))
			return ctx.typeAutoRef;

		return ctx.GetReferenceType(type);
	}

	if(SynTypeArray *node = getType<SynTypeArray>(syntax))
	{
		TypeBase *type = AnalyzeType(ctx, node->type, onlyType);

		if(!onlyType && !type)
			return NULL;

		if(isType<TypeAuto>(type))
		{
			if(node->size)
				Stop(ctx, syntax->pos, "ERROR: cannot specify array size for auto");

			return ctx.typeAutoArray;
		}

		if(!node->size)
			return ctx.GetUnsizedArrayType(type);

		ExprBase *size = AnalyzeExpression(ctx, node->size);
		
		if(ExprIntegerLiteral *number = getType<ExprIntegerLiteral>(Evaluate(ctx, CreateCast(ctx, node->pos, size, ctx.typeLong))))
			return ctx.GetArrayType(type, number->value);

		Stop(ctx, syntax->pos, "ERROR: can't get array size");
	}

	if(SynArrayIndex *node = getType<SynArrayIndex>(syntax))
	{
		TypeBase *type = AnalyzeType(ctx, node->value, onlyType);

		if(!onlyType && !type)
			return NULL;

		if(isType<TypeAuto>(type))
		{
			if(!node->arguments.empty())
				Stop(ctx, syntax->pos, "ERROR: cannot specify array size for auto");

			return ctx.typeAutoArray;
		}

		if(node->arguments.empty())
			return ctx.GetUnsizedArrayType(type);

		if(node->arguments.size() > 1)
			Stop(ctx, syntax->pos, "ERROR: ',' is not expected in array type size");

		ExprBase *size = AnalyzeExpression(ctx, node->arguments.head);
		
		if(ExprIntegerLiteral *number = getType<ExprIntegerLiteral>(Evaluate(ctx, CreateCast(ctx, node->pos, size, ctx.typeLong))))
			return ctx.GetArrayType(type, number->value);

		Stop(ctx, syntax->pos, "ERROR: can't get array size");
	}

	if(SynTypeFunction *node = getType<SynTypeFunction>(syntax))
	{
		TypeBase *returnType = AnalyzeType(ctx, node->returnType);

		IntrusiveList<TypeHandle> arguments;

		for(SynBase *el = node->arguments.head; el; el = el->next)
		{
			TypeBase *argType = AnalyzeType(ctx, el);

			if(argType == ctx.typeAuto)
				Stop(ctx, syntax->pos, "ERROR: function parameter cannot be an auto type");

			arguments.push_back(new TypeHandle(argType));
		}

		return ctx.GetFunctionType(returnType, arguments);
	}

	if(SynTypeof *node = getType<SynTypeof>(syntax))
	{
		ExprBase *value = AnalyzeExpression(ctx, node->value);

		if(value->type == ctx.typeAuto)
			Stop(ctx, syntax->pos, "ERROR: cannot take typeid from auto type");

		return value->type;
	}

	if(SynTypeSimple *node = getType<SynTypeSimple>(syntax))
	{
		if(node->path.empty())
		{
			// TODO: current namespace

			if(TypeBase **type = ctx.typeMap.find(GetStringHash(node->name.begin, node->name.end)))
				return *type;
		}

		// TODO: namespace path

		// Might be a variable
		if(!onlyType)
			return NULL;

		Stop(ctx, syntax->pos, "ERROR: unknown type");
	}

	if(SynMemberAccess *node = getType<SynMemberAccess>(syntax))
	{
		TypeBase *value = AnalyzeType(ctx, node->value, onlyType);

		if(!onlyType && !value)
			return NULL;

		if(node->member == InplaceStr("argument"))
		{
			if(TypeFunction *type = getType<TypeFunction>(value))
			{
				// first/last/[n]/size
				Stop(ctx, syntax->pos, "ERROR: not implemented");
			}

			Stop(ctx, syntax->pos, "ERROR: 'argument' can only be applied to a function type, but we have '%s'", value->name);
		}
		else if(node->member == InplaceStr("return"))
		{
			if(TypeFunction *type = getType<TypeFunction>(value))
				return type->returnType;

			Stop(ctx, syntax->pos, "ERROR: 'return' can only be applied to a function type, but we have '%s'", value->name);
		}
		else if(node->member == InplaceStr("target"))
		{
			if(TypeRef *type = getType<TypeRef>(value))
				return type->subType;
			else if(TypeArray *type = getType<TypeArray>(value))
				return type->subType;
			else if(TypeUnsizedArray *type = getType<TypeUnsizedArray>(value))
				return type->subType;

			Stop(ctx, syntax->pos, "ERROR: 'target' can only be applied to a pointer or array type, but we have '%s'", value->name);
		}

		if(!onlyType)
			return NULL;

		// isReference/isArray/isFunction/arraySize/hasMember(x)/class member/class typedef

		Stop(ctx, syntax->pos, "ERROR: unknown type");

		return NULL;
	}

	if(SynTypeGenericInstance *node = getType<SynTypeGenericInstance>(syntax))
	{
		TypeBase *baseType = AnalyzeType(ctx, node->baseType);

		// TODO: overloads with a different number of generic arguments

		if(TypeGenericClassProto *proto = getType<TypeGenericClassProto>(baseType))
		{
			IntrusiveList<SynIdentifier> aliases = proto->definition->aliases;

			if(node->types.size() < aliases.size())
				Stop(ctx, syntax->pos, "ERROR: there where only '%d' argument(s) to a generic type that expects '%d'", node->types.size(), aliases.size());

			if(node->types.size() > aliases.size())
				Stop(ctx, syntax->pos, "ERROR: type has only '%d' generic argument(s) while '%d' specified", aliases.size(), node->types.size());

			bool isGeneric = false;
			IntrusiveList<TypeHandle> types;

			for(SynBase *el = node->types.head; el; el = el->next)
			{
				TypeBase *type = AnalyzeType(ctx, el);

				isGeneric |= type->isGeneric;

				types.push_back(new TypeHandle(type));
			}

			// TODO: apply current namespace
			InplaceStr className = GetGenericClassName(proto, types);

			if(isGeneric)
				return new TypeGenericClass(className, proto, types);
			
			// Check if type already exists
			if(TypeClass **prev = ctx.genericTypeMap.find(className.hash()))
				return *prev;

			ExprBase *result = AnalyzeClassDefinition(ctx, proto->definition, proto, types);

			if(ExprClassDefinition *definition = getType<ExprClassDefinition>(result))
			{
				return definition->classType;
			}

			Stop(ctx, syntax->pos, "ERROR: type '%s' couldn't be instantiated", baseType->name);
		}

		Stop(ctx, syntax->pos, "ERROR: type '%s' can't have generic arguments", baseType->name);
	}

	if(!onlyType)
		return NULL;

	Stop(ctx, syntax->pos, "ERROR: unknown type");

	return NULL;
}

unsigned AnalyzeAlignment(ExpressionContext &ctx, SynAlign *syntax)
{
	// noalign
	if(!syntax->value)
		return 1;

	ExprBase *align = AnalyzeNumber(ctx, syntax->value);

	if(ExprIntegerLiteral *alignValue = getType<ExprIntegerLiteral>(Evaluate(ctx, CreateCast(ctx, syntax->pos, align, ctx.typeLong))))
	{
		if(alignValue->value > 16)
			Stop(ctx, syntax->pos, "ERROR: alignment must be less than 16 bytes");

		if(alignValue->value & (alignValue->value - 1))
			Stop(ctx, syntax->pos, "ERROR: alignment must be power of two");

		return unsigned(alignValue->value);
	}

	Stop(ctx, syntax->pos, "ERROR: alignment must be a constant expression");

	return NULL;
}

ExprBase* AnalyzeNumber(ExpressionContext &ctx, SynNumber *syntax)
{
	InplaceStr &value = syntax->value;

	// Hexadecimal
	if(value.length() > 1 && value.begin[1] == 'x')
	{
		if(value.length() == 2)
			Stop(ctx, value.begin + 2, "ERROR: '0x' must be followed by number");

		// Skip 0x
		unsigned pos = 2;

		// Skip leading zeros
		while(value.begin[pos] == '0')
			pos++;

		if(int(value.length() - pos) > 16)
			Stop(ctx, value.begin, "ERROR: overflow in hexadecimal constant");

		long long num = ParseLong(ctx, value.begin + pos, value.end, 16);

		// If number overflows integer number, create long number
		if(int(num) == num)
			return new ExprIntegerLiteral(syntax, ctx.typeInt, num);

		return new ExprIntegerLiteral(syntax, ctx.typeLong, num);
	}

	bool isFP = false;

	for(unsigned int i = 0; i < value.length(); i++)
	{
		if(value.begin[i] == '.' || value.begin[i] == 'e')
			isFP = true;
	}

	if(!isFP)
	{
		if(syntax->suffix == InplaceStr("b"))
		{
			unsigned pos = 0;

			// Skip leading zeros
			while(value.begin[pos] == '0')
				pos++;

			if(int(value.length() - pos) > 64)
				Stop(ctx, value.begin, "ERROR: overflow in binary constant");

			long long num = ParseLong(ctx, value.begin + pos, value.end, 2);

			// If number overflows integer number, create long number
			if(int(num) == num)
				return new ExprIntegerLiteral(syntax, ctx.typeInt, num);

			return new ExprIntegerLiteral(syntax, ctx.typeLong, num);
		}
		else if(syntax->suffix == InplaceStr("l"))
		{
			long long num = ParseLong(ctx, value.begin, value.end, 10);

			return new ExprIntegerLiteral(syntax, ctx.typeLong, num);
		}
		else if(!syntax->suffix.empty())
		{
			Stop(ctx, syntax->suffix.begin, "ERROR: unknown number suffix '%.*s'", syntax->suffix.length(), syntax->suffix.begin);
		}

		if(value.length() > 1 && value.begin[0] == '0' && isDigit(value.begin[1]))
		{
			unsigned pos = 0;

			// Skip leading zeros
			while(value.begin[pos] == '0')
				pos++;

			if(int(value.length() - pos) > 22 || (int(value.length() - pos) > 21 && value.begin[pos] != '1'))
				Stop(ctx, value.begin, "ERROR: overflow in octal constant");

			long long num = ParseLong(ctx, value.begin, value.end, 8);

			// If number overflows integer number, create long number
			if(int(num) == num)
				return new ExprIntegerLiteral(syntax, ctx.typeInt, num);

			return new ExprIntegerLiteral(syntax, ctx.typeLong, num);
		}

		long long num = ParseLong(ctx, value.begin, value.end, 10);

		if(int(num) == num)
			return new ExprIntegerLiteral(syntax, ctx.typeInt, num);

		Stop(ctx, value.begin, "ERROR: overflow in decimal constant");
	}
		
	if(syntax->suffix == InplaceStr("f"))
	{
		double num = ParseDouble(ctx, value.begin);

		return new ExprRationalLiteral(syntax, ctx.typeFloat, float(num));
	}
	else if(!syntax->suffix.empty())
	{
		Stop(ctx, syntax->suffix.begin, "ERROR: unknown number suffix '%.*s'", syntax->suffix.length(), syntax->suffix.begin);
	}

	double num = ParseDouble(ctx, value.begin);

	return new ExprRationalLiteral(syntax, ctx.typeDouble, num);
}

ExprArray* AnalyzeArray(ExpressionContext &ctx, SynArray *syntax)
{
	assert(syntax->values.head);

	IntrusiveList<ExprBase> values;

	TypeBase *subType = NULL;

	for(SynBase *el = syntax->values.head; el; el = el->next)
	{
		ExprBase *value = AnalyzeExpression(ctx, el);

		if(subType == NULL)
			subType = value->type;
		else if(subType != value->type)
			Stop(ctx, el->pos, "ERROR: array element type mismatch");

		values.push_back(value);
	}

	return new ExprArray(syntax, ctx.GetArrayType(subType, values.size()), values);
}

ExprBase* AnalyzeVariableAccess(ExpressionContext &ctx, SynBase *syntax, InplaceStr name)
{
	if(VariableData **variable = ctx.variableMap.find(GetStringHash(name.begin, name.end)))
	{
		VariableData *data = *variable;

		// TODO: check external variable access

		if(data->type == ctx.typeAuto)
			Stop(ctx, name.begin, "ERROR: variable '%.*s' is being used while its type is unknown", FMT_ISTR(name));

		return new ExprVariableAccess(syntax, data->type, data);
	}

	if(FunctionData **function = ctx.functionMap.find(GetStringHash(name.begin, name.end)))
	{
		return new ExprFunctionAccess(syntax, (*function)->type, *function);
	}

	Stop(ctx, name.begin, "ERROR: unknown variable");

	return NULL;
}

ExprBase* AnalyzeVariableAccess(ExpressionContext &ctx, SynIdentifier *syntax)
{
	return AnalyzeVariableAccess(ctx, syntax, syntax->name);
}

ExprBase* AnalyzeVariableAccess(ExpressionContext &ctx, SynTypeSimple *syntax)
{
	if(syntax->path.empty())
	{
		// TODO: current namespace

		return AnalyzeVariableAccess(ctx, syntax, syntax->name);
	}

	// TODO: namespace path

	Stop(ctx, syntax->pos, "ERROR: unknown namespaced variable");

	return NULL;
}

ExprPreModify* AnalyzePreModify(ExpressionContext &ctx, SynPreModify *syntax)
{
	ExprBase *value = AnalyzeExpression(ctx, syntax->value);

	return new ExprPreModify(syntax, value->type, value, syntax->isIncrement);
}

ExprPostModify* AnalyzePostModify(ExpressionContext &ctx, SynPostModify *syntax)
{
	ExprBase *value = AnalyzeExpression(ctx, syntax->value);

	return new ExprPostModify(syntax, value->type, value, syntax->isIncrement);
}

ExprUnaryOp* AnalyzeUnaryOp(ExpressionContext &ctx, SynUnaryOp *syntax)
{
	ExprBase *value = AnalyzeExpression(ctx, syntax->value);

	// TODO: check operator overload

	bool binaryOp = IsBinaryOp(syntax->type);
	bool logicalOp = IsLogicalOp(syntax->type);

	// Type check
	if(ctx.IsFloatingPointType(value->type))
	{
		if(binaryOp || logicalOp)
			Stop(ctx, syntax->pos, "ERROR: unary operation '%s' is not supported on '%.*s'", GetOpName(syntax->type), FMT_ISTR(value->type->name));
	}
	else if(value->type == ctx.typeBool || value->type == ctx.typeAutoRef)
	{
		if(!logicalOp)
			Stop(ctx, syntax->pos, "ERROR: unary operation '%s' is not supported on '%.*s'", GetOpName(syntax->type), FMT_ISTR(value->type->name));
	}
	else if(isType<TypeRef>(value->type))
	{
		if(!logicalOp)
			Stop(ctx, syntax->pos, "ERROR: unary operation '%s' is not supported on '%.*s'", GetOpName(syntax->type), FMT_ISTR(value->type->name));
	}
	else if(!ctx.IsNumericType(value->type))
	{
		Stop(ctx, syntax->pos, "ERROR: unary operation '%s' is not supported on '%.*s'", GetOpName(syntax->type), FMT_ISTR(value->type->name));
	}

	TypeBase *resultType = NULL;

	if(logicalOp)
		resultType = ctx.typeBool;
	else
		resultType = value->type;

	return new ExprUnaryOp(syntax, resultType, syntax->type, value);
}

ExprBinaryOp* AnalyzeBinaryOp(ExpressionContext &ctx, SynBinaryOp *syntax)
{
	ExprBase *lhs = AnalyzeExpression(ctx, syntax->lhs);
	ExprBase *rhs = AnalyzeExpression(ctx, syntax->rhs);

	// TODO: 'in' is a function call
	// TODO: == and != for typeAutoRef is a function call
	// TODO: == and != for function types is a function call
	// TODO: == and != for unsized arrays is an overload call or a function call
	// TODO: && and || could have an operator overload where second argument is wrapped in a function for short-circuit evaluation
	// TODO: check operator overload

	if(lhs->type == ctx.typeVoid)
		Stop(ctx, syntax->pos, "ERROR: first operand type is 'void'");

	if(rhs->type == ctx.typeVoid)
		Stop(ctx, syntax->pos, "ERROR: second operand type is 'void'");

	bool binaryOp = IsBinaryOp(syntax->type);
	bool comparisonOp = IsComparisonOp(syntax->type);
	bool logicalOp = IsLogicalOp(syntax->type);

	if(ctx.IsFloatingPointType(lhs->type) || ctx.IsFloatingPointType(rhs->type))
	{
		if(logicalOp || binaryOp)
			Stop(ctx, syntax->pos, "ERROR: operation %s is not supported on '%.*s' and '%.*s'", GetOpName(syntax->type), FMT_ISTR(lhs->type->name), FMT_ISTR(rhs->type->name));
	}

	if(logicalOp)
	{
		// Logical operations require both operands to be 'bool'
		lhs = CreateCast(ctx, syntax->lhs->pos, lhs, ctx.typeBool);
		rhs = CreateCast(ctx, syntax->rhs->pos, rhs, ctx.typeBool);
	}
	else if(ctx.IsNumericType(lhs->type) && ctx.IsNumericType(rhs->type))
	{
		// Numeric operations promote both operands to a common type
		TypeBase *commonType = ctx.GetBinaryOpResultType(lhs->type, rhs->type);

		lhs = CreateCast(ctx, syntax->lhs->pos, lhs, commonType);
		rhs = CreateCast(ctx, syntax->rhs->pos, rhs, commonType);
	}

	if(lhs->type != rhs->type)
		Stop(ctx, syntax->pos, "ERROR: operation %s is not supported on '%.*s' and '%.*s'", GetOpName(syntax->type), FMT_ISTR(lhs->type->name), FMT_ISTR(rhs->type->name));

	TypeBase *resultType = NULL;

	if(comparisonOp || logicalOp)
		resultType = ctx.typeBool;
	else
		resultType = lhs->type;

	return new ExprBinaryOp(syntax, resultType, syntax->type, lhs, rhs);
}

ExprGetAddress* AnalyzeGetAddress(ExpressionContext &ctx, SynGetAddress *syntax)
{
	ExprBase *value = AnalyzeExpression(ctx, syntax->value);

	return new ExprGetAddress(syntax, ctx.GetReferenceType(value->type), value);
}

ExprDereference* AnalyzeDereference(ExpressionContext &ctx, SynDereference *syntax)
{
	ExprBase *value = AnalyzeExpression(ctx, syntax->value);

	if(TypeRef *type = getType<TypeRef>(value->type))
	{
		return new ExprDereference(syntax, type->subType, value);
	}

	Stop(ctx, syntax->pos, "ERROR: cannot dereference type '%.*s' that is not a pointer", FMT_ISTR(value->type->name));

	return NULL;
}

ExprConditional* AnalyzeConditional(ExpressionContext &ctx, SynConditional *syntax)
{
	ExprBase *condition = AnalyzeExpression(ctx, syntax->condition);

	ExprBase *trueBlock = AnalyzeStatement(ctx, syntax->trueBlock);
	ExprBase *falseBlock = AnalyzeStatement(ctx, syntax->falseBlock);

	TypeBase *resultType = NULL;

	if(trueBlock->type == falseBlock->type)
		resultType = trueBlock->type;
	else if(ctx.IsNumericType(trueBlock->type) && ctx.IsNumericType(falseBlock->type))
		resultType = ctx.GetBinaryOpResultType(trueBlock->type, falseBlock->type);
	else
		Stop(ctx, syntax->pos, "ERROR: Unknown common type");

	return new ExprConditional(syntax, resultType, condition, trueBlock, falseBlock);
}

ExprAssignment* AnalyzeAssignment(ExpressionContext &ctx, SynAssignment *syntax)
{
	ExprBase *lhs = AnalyzeExpression(ctx, syntax->lhs);
	ExprBase *rhs = AnalyzeExpression(ctx, syntax->rhs);

	return new ExprAssignment(syntax, lhs->type, lhs, rhs);
}

ExprModifyAssignment* AnalyzeModifyAssignment(ExpressionContext &ctx, SynModifyAssignment *syntax)
{
	ExprBase *lhs = AnalyzeExpression(ctx, syntax->lhs);
	ExprBase *rhs = AnalyzeExpression(ctx, syntax->rhs);

	return new ExprModifyAssignment(syntax, lhs->type, syntax->type, lhs, rhs);
}

ExprBase* AnalyzeMemberAccess(ExpressionContext &ctx, SynMemberAccess *syntax)
{
	// It could be a type property
	if(TypeBase *type = AnalyzeType(ctx, syntax->value, false))
	{
		if(syntax->member == InplaceStr("isReference"))
		{
			return new ExprBoolLiteral(syntax, ctx.typeBool, isType<TypeRef>(type));
		}

		if(syntax->member == InplaceStr("isArray"))
		{
			return new ExprBoolLiteral(syntax, ctx.typeBool, isType<TypeArray>(type) || isType<TypeUnsizedArray>(type));
		}

		if(syntax->member == InplaceStr("isFunction"))
		{
			return new ExprBoolLiteral(syntax, ctx.typeBool, isType<TypeFunction>(type));
		}

		if(syntax->member == InplaceStr("arraySize"))
		{
			if(TypeArray *arrType = getType<TypeArray>(type))
				return new ExprIntegerLiteral(syntax, ctx.typeInt, arrType->length);
				
			if(TypeUnsizedArray *arrType = getType<TypeUnsizedArray>(type))
				return new ExprIntegerLiteral(syntax, ctx.typeInt, -1);

			Stop(ctx, syntax->pos, "ERROR: 'arraySize' can only be applied to an array type, but we have '%s'", type->name);
		}

		Stop(ctx, syntax->pos, "ERROR: unknown expression type");
		// isReference/isArray/isFunction/arraySize/hasMember(x)/class constant/class typedef
	}

	ExprBase* value = AnalyzeExpression(ctx, syntax->value);

	if(TypeArray *node = getType<TypeArray>(value->type))
	{
		if(syntax->member == InplaceStr("size"))
			return new ExprIntegerLiteral(syntax, ctx.typeInt, node->length);

		Stop(ctx, syntax->pos, "ERROR: array doesn't have member with this name");
	}

	if(TypeStruct *node = getType<TypeStruct>(value->type))
	{
		for(VariableHandle *el = node->members.head; el; el = el->next)
		{
			if(el->variable->name == syntax->member)
				return new ExprMemberAccess(syntax, el->variable->type, value, el->variable); 
		}
	}

	Stop(ctx, syntax->pos, "ERROR: member variable or function '%.*s' is not defined in class '%.*s'", FMT_ISTR(syntax->member), FMT_ISTR(value->type->name));

	return NULL;
}

ExprArrayIndex* AnalyzeArrayIndex(ExpressionContext &ctx, SynTypeArray *syntax)
{
	ExprBase *value = AnalyzeExpression(ctx, syntax->type);
	ExprBase *index = AnalyzeExpression(ctx, syntax->size);

	if(TypeArray *type = getType<TypeArray>(value->type))
		return new ExprArrayIndex(syntax, type->subType, value, index);

	if(TypeUnsizedArray *type = getType<TypeUnsizedArray>(value->type))
		return new ExprArrayIndex(syntax, type->subType, value, index);

	Stop(ctx, syntax->pos, "ERROR: type '%.*s' is not an array", FMT_ISTR(value->type->name));

	return NULL;
}

ExprArrayIndex* AnalyzeArrayIndex(ExpressionContext &ctx, SynArrayIndex *syntax)
{
	if(syntax->arguments.size() > 1)
		Stop(ctx, syntax->pos, "ERROR: multiple array indexes are not supported");

	if(syntax->arguments.empty())
		Stop(ctx, syntax->pos, "ERROR: array index is missing");

	ExprBase *value = AnalyzeExpression(ctx, syntax->value);

	ExprBase *index = AnalyzeExpression(ctx, syntax->arguments.head);

	if(TypeArray *type = getType<TypeArray>(value->type))
		return new ExprArrayIndex(syntax, type->subType, value, index);

	if(TypeUnsizedArray *type = getType<TypeUnsizedArray>(value->type))
		return new ExprArrayIndex(syntax, type->subType, value, index);

	Stop(ctx, syntax->pos, "ERROR: type '%.*s' is not an array", FMT_ISTR(value->type->name));

	return NULL;
}

ExprFunctionCall* AnalyzeFunctionCall(ExpressionContext &ctx, SynFunctionCall *syntax)
{
	ExprBase *function = AnalyzeExpression(ctx, syntax->value);

	if(!syntax->aliases.empty())
		Stop(ctx, syntax->pos, "ERROR: function call with explicit generic arguments is not supported");

	IntrusiveList<ExprBase> arguments;

	for(SynCallArgument *el = syntax->arguments.head; el; el = getType<SynCallArgument>(el->next))
	{
		if(!el->name.empty())
			Stop(ctx, syntax->pos, "ERROR: named function arguments are not supported");

		arguments.push_back(AnalyzeExpression(ctx, el->value));
	}

	if(TypeFunction *type = getType<TypeFunction>(function->type))
	{
		return new ExprFunctionCall(syntax, type->returnType, function, arguments);
	}

	Stop(ctx, syntax->pos, "ERROR: unknown call");

	return NULL;
}

ExprFunctionCall* AnalyzeNew(ExpressionContext &ctx, SynNew *syntax)
{
	if(!syntax->constructor.empty())
		Stop(ctx, syntax->pos, "ERROR: custom constructors are not supported");

	if(!syntax->arguments.empty())
		Stop(ctx, syntax->pos, "ERROR: constructor call is not supported");

	TypeBase *type = AnalyzeType(ctx, syntax->type);

	ExprFunctionCall *call = NULL;

	if(syntax->count)
	{
		// TODO: implicit cast for manual function call
		ExprBase *count = AnalyzeExpression(ctx, syntax->count);

		FunctionData **function = ctx.functionMap.find(GetStringHash("__newA"));

		if(!function || (*function)->name != InplaceStr("__newA"))
			Stop(ctx, syntax->pos, "ERROR: internal error, __newA not found");

		IntrusiveList<ExprBase> arguments;

		arguments.push_back(new ExprIntegerLiteral(syntax, ctx.typeInt, type->size));
		arguments.push_back(count);
		arguments.push_back(new ExprTypeLiteral(syntax, ctx.typeTypeID, type));
<commit_msg>Fixed array allocation<commit_after>#include "ExpressionTree.h"

#include "ExpressionEval.h"

#define FMT_ISTR(x) unsigned(x.end - x.begin), x.begin

namespace
{
	jmp_buf errorHandler;

	void Stop(ExpressionContext &ctx, const char *pos, const char *msg, va_list args)
	{
		ctx.errorPos = pos;

		char errorText[4096];

		vsnprintf(errorText, 4096, msg, args);
		errorText[4096 - 1] = '\0';

		ctx.errorMsg = InplaceStr(errorText);

		longjmp(errorHandler, 1);
	}

	void Stop(ExpressionContext &ctx, const char *pos, const char *msg, ...)
	{
		va_list args;
		va_start(args, msg);

		Stop(ctx, pos, msg, args);
	}

	unsigned char ParseEscapeSequence(ExpressionContext &ctx, const char* str)
	{
		assert(str[0] == '\\');

		switch(str[1])
		{
		case 'n':
			return '\n';
		case 'r':
			return '\r';
		case 't':
			return '\t';
		case '0':
			return '\0';
		case '\'':
			return '\'';
		case '\"':
			return '\"';
		case '\\':
			return '\\';
		}

		Stop(ctx, str, "ERROR: unknown escape sequence");

		return 0;
	}

	int ParseInteger(ExpressionContext &ctx, const char* str)
	{
		(void)ctx;

		unsigned int digit;
		int a = 0;

		while((digit = *str - '0') < 10)
		{
			a = a * 10 + digit;
			str++;
		}

		return a;
	}

	long long ParseLong(ExpressionContext &ctx, const char* s, const char* e, int base)
	{
		unsigned long long res = 0;

		for(const char *p = s; p < e; p++)
		{
			int digit = ((*p >= '0' && *p <= '9') ? *p - '0' : (*p & ~0x20) - 'A' + 10);

			if(digit < 0 || digit >= base)
				Stop(ctx, p, "ERROR: digit %d is not allowed in base %d", digit, base);

			res = res * base + digit;
		}

		return res;
	}

	double ParseDouble(ExpressionContext &ctx, const char *str)
	{
		unsigned int digit;
		double integer = 0.0;

		while((digit = *str - '0') < 10)
		{
			integer = integer * 10.0 + digit;
			str++;
		}

		double fractional = 0.0;
	
		if(*str == '.')
		{
			double power = 0.1f;
			str++;

			while((digit = *str - '0') < 10)
			{
				fractional = fractional + power * digit;
				power /= 10.0;
				str++;
			}
		}

		if(*str == 'e')
		{
			str++;

			if(*str == '-')
				return (integer + fractional) * pow(10.0, (double)-ParseInteger(ctx, str + 1));
			else
				return (integer + fractional) * pow(10.0, (double)ParseInteger(ctx, str));
		}

		return integer + fractional;
	}

	bool IsBinaryOp(SynUnaryOpType type)
	{
		return type == SYN_UNARY_OP_BIT_NOT;
	}

	bool IsLogicalOp(SynUnaryOpType type)
	{
		return type == SYN_UNARY_OP_LOGICAL_NOT;
	}

	const char* GetOpName(SynUnaryOpType type)
	{
		switch(type)
		{
		case SYN_UNARY_OP_PLUS:
			return "+";
		case SYN_UNARY_OP_NEGATE:
			return "-";
		case SYN_UNARY_OP_BIT_NOT:
			return "~";
		case SYN_UNARY_OP_LOGICAL_NOT:
			return "!";
		}

		assert(!"unknown operation type");
		return "";
	}

	bool IsBinaryOp(SynBinaryOpType type)
	{
		switch(type)
		{
		case SYN_BINARY_OP_SHL:
		case SYN_BINARY_OP_SHR:
		case SYN_BINARY_OP_BIT_AND:
		case SYN_BINARY_OP_BIT_OR:
		case SYN_BINARY_OP_BIT_XOR:
			return true;
		}

		return false;
	}

	bool IsComparisonOp(SynBinaryOpType type)
	{
		switch(type)
		{
		case SYN_BINARY_OP_LESS:
		case SYN_BINARY_OP_LESS_EQUAL:
		case SYN_BINARY_OP_GREATER:
		case SYN_BINARY_OP_GREATER_EQUAL:
		case SYN_BINARY_OP_EQUAL:
		case SYN_BINARY_OP_NOT_EQUAL:
			return true;
		}

		return false;
	}

	bool IsLogicalOp(SynBinaryOpType type)
	{
		switch(type)
		{
		case SYN_BINARY_OP_LOGICAL_AND:
		case SYN_BINARY_OP_LOGICAL_OR:
		case SYN_BINARY_OP_LOGICAL_XOR:
			return true;
		}

		return false;
	}

	const char* GetOpName(SynBinaryOpType type)
	{
		switch(type)
		{
		case SYN_BINARY_OP_ADD:
			return "+";
		case SYN_BINARY_OP_SUB:
			return "-";
		case SYN_BINARY_OP_MUL:
			return "*";
		case SYN_BINARY_OP_DIV:
			return "/";
		case SYN_BINARY_OP_MOD:
			return "%";
		case SYN_BINARY_OP_POW:
			return "**";
		case SYN_BINARY_OP_SHL:
			return "<<";
		case SYN_BINARY_OP_SHR:
			return ">>";
		case SYN_BINARY_OP_LESS:
			return "<";
		case SYN_BINARY_OP_LESS_EQUAL:
			return "<=";
		case SYN_BINARY_OP_GREATER:
			return ">";
		case SYN_BINARY_OP_GREATER_EQUAL:
			return ">=";
		case SYN_BINARY_OP_EQUAL:
			return "==";
		case SYN_BINARY_OP_NOT_EQUAL:
			return "!=";
		case SYN_BINARY_OP_BIT_AND:
			return "&";
		case SYN_BINARY_OP_BIT_OR:
			return "|";
		case SYN_BINARY_OP_BIT_XOR:
			return "^";
		case SYN_BINARY_OP_LOGICAL_AND:
			return "&&";
		case SYN_BINARY_OP_LOGICAL_OR:
			return "||";
		case SYN_BINARY_OP_LOGICAL_XOR:
			return "^^";
		case SYN_BINARY_OP_IN:
			return "in";
		}

		assert(!"unknown operation type");
		return "";
	}
}

ExpressionContext::ExpressionContext()
{
	errorPos = NULL;

	typeVoid = NULL;

	typeBool = NULL;

	typeChar = NULL;
	typeShort = NULL;
	typeInt = NULL;
	typeLong = NULL;

	typeFloat = NULL;
	typeDouble = NULL;

	typeTypeID = NULL;
	typeFunctionID = NULL;

	typeAuto = NULL;
	typeAutoRef = NULL;
	typeAutoArray = NULL;

	typeMap.init();
	functionMap.init();
	variableMap.init();

	genericTypeMap.init();
}

void ExpressionContext::Stop(const char *pos, const char *msg, ...)
{
	va_list args;
	va_start(args, msg);

	::Stop(*this, pos, msg, args);
}

void ExpressionContext::PushScope()
{
	unsigned depth = scopes.size();
	ScopeData *scope = scopes.empty() ? NULL : scopes.back();

	scopes.push_back(new ScopeData(depth, scope));
}

void ExpressionContext::PushScope(NamespaceData *nameSpace)
{
	unsigned depth = scopes.size();
	ScopeData *scope = scopes.empty() ? NULL : scopes.back();

	scopes.push_back(new ScopeData(depth, scope, nameSpace));
}

void ExpressionContext::PushScope(FunctionData *function)
{
	unsigned depth = scopes.size();
	ScopeData *scope = scopes.empty() ? NULL : scopes.back();

	scopes.push_back(new ScopeData(depth, scope, function));
}

void ExpressionContext::PushScope(TypeBase *type)
{
	unsigned depth = scopes.size();
	ScopeData *scope = scopes.empty() ? NULL : scopes.back();

	scopes.push_back(new ScopeData(depth, scope, type));
}

void ExpressionContext::PopScope()
{
	// Remove scope members from lookup maps
	ScopeData *scope = scopes.back();

	for(int i = int(scope->variables.size()) - 1; i >= 0; i--)
	{
		VariableData *variable = scope->variables[i];

		variableMap.remove(variable->nameHash, variable);
	}

	for(int i = int(scope->functions.size()) - 1; i >= 0; i--)
	{
		FunctionData *function = scope->functions[i];

		// Keep class functions visible
		if(function->scope->ownerType)
			continue;

		functionMap.remove(function->nameHash, function);
	}

	for(int i = int(scope->types.size()) - 1; i >= 0; i--)
	{
		TypeBase *type = scope->types[i];

		typeMap.remove(type->nameHash, type);
	}

	for(int i = int(scope->aliases.size()) - 1; i >= 0; i--)
	{
		AliasData *alias = scope->aliases[i];

		typeMap.remove(alias->nameHash, alias->type);
	}

	delete scopes.back();
	scopes.pop_back();
}

NamespaceData* ExpressionContext::GetCurrentNamespace()
{
	// Simply walk up the scopes and find the current one
	for(int i = int(scopes.size()) - 1; i >= 0; i--)
	{
		ScopeData *scope = scopes[i];

		if(NamespaceData *ns = scope->ownerNamespace)
			return ns;
	}

	return NULL;
}

FunctionData* ExpressionContext::GetCurrentFunction()
{
	// Walk up, but if we reach a type owner, stop - we're not in a context of a function
	for(int i = int(scopes.size()) - 1; i >= 0; i--)
	{
		ScopeData *scope = scopes[i];

		if(scope->ownerType)
			return NULL;

		if(FunctionData *function = scope->ownerFunction)
			return function;
	}

	return NULL;
}

TypeBase* ExpressionContext::GetCurrentType()
{
	// Walk up, but if we reach a function, return it's owner
	for(int i = int(scopes.size()) - 1; i >= 0; i--)
	{
		ScopeData *scope = scopes[i];

		if(scope->ownerFunction)
			return scope->ownerFunction->scope->ownerType;

		if(TypeBase *type = scope->ownerType)
			return type;
	}

	return NULL;
}

unsigned ExpressionContext::GetGenericClassInstantiationDepth()
{
	unsigned depth = 0;

	for(unsigned i = 0; i < scopes.size(); i++)
	{
		ScopeData *scope = scopes[i];

		if(TypeClass *type = getType<TypeClass>(scope->ownerType))
		{
			if(!type->genericTypes.empty())
				depth++;
		}
	}

	return depth;
}

void ExpressionContext::AddType(TypeBase *type)
{
	scopes.back()->types.push_back(type);

	if(!isType<TypeGenericClassProto>(type))
		assert(!type->isGeneric);

	types.push_back(type);
	typeMap.insert(type->nameHash, type);
}

void ExpressionContext::AddFunction(FunctionData *function)
{
	scopes.back()->functions.push_back(function);

	functions.push_back(function);
	functionMap.insert(function->nameHash, function);
}

void ExpressionContext::AddVariable(VariableData *variable)
{
	scopes.back()->variables.push_back(variable);

	variables.push_back(variable);
	variableMap.insert(variable->nameHash, variable);
}

void ExpressionContext::AddAlias(AliasData *alias)
{
	scopes.back()->aliases.push_back(alias);

	typeMap.insert(alias->nameHash, alias->type);
}

bool ExpressionContext::IsIntegerType(TypeBase* type)
{
	if(type == typeBool)
		return true;

	if(type == typeChar)
		return true;

	if(type == typeShort)
		return true;

	if(type == typeInt)
		return true;

	if(type == typeLong)
		return true;

	return false;
}

bool ExpressionContext::IsFloatingPointType(TypeBase* type)
{
	if(type == typeFloat)
		return true;

	if(type == typeDouble)
		return true;

	return false;
}

bool ExpressionContext::IsNumericType(TypeBase* type)
{
	return IsIntegerType(type) || IsFloatingPointType(type);
}

TypeBase* ExpressionContext::GetBinaryOpResultType(TypeBase* a, TypeBase* b)
{
	if(a == typeDouble || b == typeDouble)
		return typeDouble;

	if(a == typeFloat || b == typeFloat)
		return typeFloat;

	if(a == typeLong || b == typeLong)
		return typeLong;

	if(a == typeInt || b == typeInt)
		return typeInt;

	if(a == typeShort || b == typeShort)
		return typeShort;

	if(a == typeChar || b == typeChar)
		return typeChar;

	if(a == typeBool || b == typeBool)
		return typeBool;

	return NULL;
}

TypeRef* ExpressionContext::GetReferenceType(TypeBase* type)
{
	if(type->refType)
		return type->refType;

	// Create new type
	TypeRef* result = new TypeRef(GetReferenceTypeName(type), type);

	if(!type->isGeneric)
	{
		// Save it for future use
		type->refType = result;

		types.push_back(result);
	}

	return result;
}

TypeArray* ExpressionContext::GetArrayType(TypeBase* type, long long length)
{
	for(unsigned i = 0; i < type->arrayTypes.size(); i++)
	{
		if(type->arrayTypes[i]->length == length)
			return type->arrayTypes[i];
	}

	// Create new type
	TypeArray* result = new TypeArray(GetArrayTypeName(type, length), type, length);

	if(!type->isGeneric)
	{
		// Save it for future use
		type->arrayTypes.push_back(result);

		types.push_back(result);
	}

	return result;
}

TypeUnsizedArray* ExpressionContext::GetUnsizedArrayType(TypeBase* type)
{
	if(type->unsizedArrayType)
		return type->unsizedArrayType;

	// Create new type
	TypeUnsizedArray* result = new TypeUnsizedArray(GetUnsizedArrayTypeName(type), type);

	result->members.push_back(new VariableHandle(new VariableData(scopes.back(), 4, typeInt, InplaceStr("size"))));

	if(!type->isGeneric)
	{
		// Save it for future use
		type->unsizedArrayType = result;

		types.push_back(result);
	}

	return result;
}

TypeFunction* ExpressionContext::GetFunctionType(TypeBase* returnType, IntrusiveList<TypeHandle> arguments)
{
	for(unsigned i = 0; i < types.size(); i++)
	{
		if(TypeFunction *type = getType<TypeFunction>(types[i]))
		{
			if(type->returnType != returnType)
				continue;

			TypeHandle *leftArg = type->arguments.head;
			TypeHandle *rightArg = arguments.head;

			while(leftArg && rightArg && leftArg->type == rightArg->type)
			{
				leftArg = leftArg->next;
				rightArg = rightArg->next;
			}

			if(leftArg != rightArg)
				continue;

			return type;
		}
	}

	// Create new type
	TypeFunction* result = new TypeFunction(GetFunctionTypeName(returnType, arguments), returnType, arguments);

	if(!result->isGeneric)
	{
		types.push_back(result);
	}

	return result;
}

ExprBase* CreateCast(ExpressionContext &ctx, const char *pos, ExprBase *value, TypeBase *type)
{
	if(value->type == type)
		return value;

	if(ctx.IsNumericType(value->type) && ctx.IsNumericType(value->type))
		return new ExprTypeCast(value->source, type, value);

	if(type == ctx.typeBool)
	{
		if(isType<TypeRef>(value->type))
			return new ExprTypeCast(value->source, type, value);

		if(isType<TypeUnsizedArray>(value->type))
			return new ExprTypeCast(value->source, type, value);

		if(isType<TypeFunction>(value->type))
			return new ExprTypeCast(value->source, type, value);
	}

	Stop(ctx, pos, "ERROR: can't convert '%.*s' to '%.*s'", FMT_ISTR(value->type->name), FMT_ISTR(type->name));

	return NULL;
}

ExprBase* AnalyzeNumber(ExpressionContext &ctx, SynNumber *syntax);
ExprBase* AnalyzeExpression(ExpressionContext &ctx, SynBase *syntax);
ExprBase* AnalyzeStatement(ExpressionContext &ctx, SynBase *syntax);
ExprBlock* AnalyzeBlock(ExpressionContext &ctx, SynBlock *syntax, bool createScope);
ExprBase* AnalyzeClassDefinition(ExpressionContext &ctx, SynClassDefinition *syntax, TypeGenericClassProto *proto, IntrusiveList<TypeHandle> generics);
void AnalyzeClassElements(ExpressionContext &ctx, ExprClassDefinition *classDefinition, SynClassElements *syntax);

TypeBase* AnalyzeType(ExpressionContext &ctx, SynBase *syntax, bool onlyType = true)
{
	if(SynTypeAuto *node = getType<SynTypeAuto>(syntax))
	{
		return ctx.typeAuto;
	}

	if(SynTypeGeneric *node = getType<SynTypeGeneric>(syntax))
	{
		return new TypeGeneric(InplaceStr("generic"));
	}

	if(SynTypeAlias *node = getType<SynTypeAlias>(syntax))
	{
		TypeGeneric *type = new TypeGeneric(node->name);

		return type;
	}

	if(SynTypeReference *node = getType<SynTypeReference>(syntax))
	{
		TypeBase *type = AnalyzeType(ctx, node->type);

		if(isType<TypeAuto>(type))
			return ctx.typeAutoRef;

		return ctx.GetReferenceType(type);
	}

	if(SynTypeArray *node = getType<SynTypeArray>(syntax))
	{
		TypeBase *type = AnalyzeType(ctx, node->type, onlyType);

		if(!onlyType && !type)
			return NULL;

		if(isType<TypeAuto>(type))
		{
			if(node->size)
				Stop(ctx, syntax->pos, "ERROR: cannot specify array size for auto");

			return ctx.typeAutoArray;
		}

		if(!node->size)
			return ctx.GetUnsizedArrayType(type);

		ExprBase *size = AnalyzeExpression(ctx, node->size);
		
		if(ExprIntegerLiteral *number = getType<ExprIntegerLiteral>(Evaluate(ctx, CreateCast(ctx, node->pos, size, ctx.typeLong))))
			return ctx.GetArrayType(type, number->value);

		Stop(ctx, syntax->pos, "ERROR: can't get array size");
	}

	if(SynArrayIndex *node = getType<SynArrayIndex>(syntax))
	{
		TypeBase *type = AnalyzeType(ctx, node->value, onlyType);

		if(!onlyType && !type)
			return NULL;

		if(isType<TypeAuto>(type))
		{
			if(!node->arguments.empty())
				Stop(ctx, syntax->pos, "ERROR: cannot specify array size for auto");

			return ctx.typeAutoArray;
		}

		if(node->arguments.empty())
			return ctx.GetUnsizedArrayType(type);

		if(node->arguments.size() > 1)
			Stop(ctx, syntax->pos, "ERROR: ',' is not expected in array type size");

		ExprBase *size = AnalyzeExpression(ctx, node->arguments.head);
		
		if(ExprIntegerLiteral *number = getType<ExprIntegerLiteral>(Evaluate(ctx, CreateCast(ctx, node->pos, size, ctx.typeLong))))
			return ctx.GetArrayType(type, number->value);

		Stop(ctx, syntax->pos, "ERROR: can't get array size");
	}

	if(SynTypeFunction *node = getType<SynTypeFunction>(syntax))
	{
		TypeBase *returnType = AnalyzeType(ctx, node->returnType);

		IntrusiveList<TypeHandle> arguments;

		for(SynBase *el = node->arguments.head; el; el = el->next)
		{
			TypeBase *argType = AnalyzeType(ctx, el);

			if(argType == ctx.typeAuto)
				Stop(ctx, syntax->pos, "ERROR: function parameter cannot be an auto type");

			arguments.push_back(new TypeHandle(argType));
		}

		return ctx.GetFunctionType(returnType, arguments);
	}

	if(SynTypeof *node = getType<SynTypeof>(syntax))
	{
		ExprBase *value = AnalyzeExpression(ctx, node->value);

		if(value->type == ctx.typeAuto)
			Stop(ctx, syntax->pos, "ERROR: cannot take typeid from auto type");

		return value->type;
	}

	if(SynTypeSimple *node = getType<SynTypeSimple>(syntax))
	{
		if(node->path.empty())
		{
			// TODO: current namespace

			if(TypeBase **type = ctx.typeMap.find(GetStringHash(node->name.begin, node->name.end)))
				return *type;
		}

		// TODO: namespace path

		// Might be a variable
		if(!onlyType)
			return NULL;

		Stop(ctx, syntax->pos, "ERROR: unknown type");
	}

	if(SynMemberAccess *node = getType<SynMemberAccess>(syntax))
	{
		TypeBase *value = AnalyzeType(ctx, node->value, onlyType);

		if(!onlyType && !value)
			return NULL;

		if(node->member == InplaceStr("argument"))
		{
			if(TypeFunction *type = getType<TypeFunction>(value))
			{
				// first/last/[n]/size
				Stop(ctx, syntax->pos, "ERROR: not implemented");
			}

			Stop(ctx, syntax->pos, "ERROR: 'argument' can only be applied to a function type, but we have '%s'", value->name);
		}
		else if(node->member == InplaceStr("return"))
		{
			if(TypeFunction *type = getType<TypeFunction>(value))
				return type->returnType;

			Stop(ctx, syntax->pos, "ERROR: 'return' can only be applied to a function type, but we have '%s'", value->name);
		}
		else if(node->member == InplaceStr("target"))
		{
			if(TypeRef *type = getType<TypeRef>(value))
				return type->subType;
			else if(TypeArray *type = getType<TypeArray>(value))
				return type->subType;
			else if(TypeUnsizedArray *type = getType<TypeUnsizedArray>(value))
				return type->subType;

			Stop(ctx, syntax->pos, "ERROR: 'target' can only be applied to a pointer or array type, but we have '%s'", value->name);
		}

		if(!onlyType)
			return NULL;

		// isReference/isArray/isFunction/arraySize/hasMember(x)/class member/class typedef

		Stop(ctx, syntax->pos, "ERROR: unknown type");

		return NULL;
	}

	if(SynTypeGenericInstance *node = getType<SynTypeGenericInstance>(syntax))
	{
		TypeBase *baseType = AnalyzeType(ctx, node->baseType);

		// TODO: overloads with a different number of generic arguments

		if(TypeGenericClassProto *proto = getType<TypeGenericClassProto>(baseType))
		{
			IntrusiveList<SynIdentifier> aliases = proto->definition->aliases;

			if(node->types.size() < aliases.size())
				Stop(ctx, syntax->pos, "ERROR: there where only '%d' argument(s) to a generic type that expects '%d'", node->types.size(), aliases.size());

			if(node->types.size() > aliases.size())
				Stop(ctx, syntax->pos, "ERROR: type has only '%d' generic argument(s) while '%d' specified", aliases.size(), node->types.size());

			bool isGeneric = false;
			IntrusiveList<TypeHandle> types;

			for(SynBase *el = node->types.head; el; el = el->next)
			{
				TypeBase *type = AnalyzeType(ctx, el);

				isGeneric |= type->isGeneric;

				types.push_back(new TypeHandle(type));
			}

			// TODO: apply current namespace
			InplaceStr className = GetGenericClassName(proto, types);

			if(isGeneric)
				return new TypeGenericClass(className, proto, types);
			
			// Check if type already exists
			if(TypeClass **prev = ctx.genericTypeMap.find(className.hash()))
				return *prev;

			ExprBase *result = AnalyzeClassDefinition(ctx, proto->definition, proto, types);

			if(ExprClassDefinition *definition = getType<ExprClassDefinition>(result))
			{
				return definition->classType;
			}

			Stop(ctx, syntax->pos, "ERROR: type '%s' couldn't be instantiated", baseType->name);
		}

		Stop(ctx, syntax->pos, "ERROR: type '%s' can't have generic arguments", baseType->name);
	}

	if(!onlyType)
		return NULL;

	Stop(ctx, syntax->pos, "ERROR: unknown type");

	return NULL;
}

unsigned AnalyzeAlignment(ExpressionContext &ctx, SynAlign *syntax)
{
	// noalign
	if(!syntax->value)
		return 1;

	ExprBase *align = AnalyzeNumber(ctx, syntax->value);

	if(ExprIntegerLiteral *alignValue = getType<ExprIntegerLiteral>(Evaluate(ctx, CreateCast(ctx, syntax->pos, align, ctx.typeLong))))
	{
		if(alignValue->value > 16)
			Stop(ctx, syntax->pos, "ERROR: alignment must be less than 16 bytes");

		if(alignValue->value & (alignValue->value - 1))
			Stop(ctx, syntax->pos, "ERROR: alignment must be power of two");

		return unsigned(alignValue->value);
	}

	Stop(ctx, syntax->pos, "ERROR: alignment must be a constant expression");

	return NULL;
}

ExprBase* AnalyzeNumber(ExpressionContext &ctx, SynNumber *syntax)
{
	InplaceStr &value = syntax->value;

	// Hexadecimal
	if(value.length() > 1 && value.begin[1] == 'x')
	{
		if(value.length() == 2)
			Stop(ctx, value.begin + 2, "ERROR: '0x' must be followed by number");

		// Skip 0x
		unsigned pos = 2;

		// Skip leading zeros
		while(value.begin[pos] == '0')
			pos++;

		if(int(value.length() - pos) > 16)
			Stop(ctx, value.begin, "ERROR: overflow in hexadecimal constant");

		long long num = ParseLong(ctx, value.begin + pos, value.end, 16);

		// If number overflows integer number, create long number
		if(int(num) == num)
			return new ExprIntegerLiteral(syntax, ctx.typeInt, num);

		return new ExprIntegerLiteral(syntax, ctx.typeLong, num);
	}

	bool isFP = false;

	for(unsigned int i = 0; i < value.length(); i++)
	{
		if(value.begin[i] == '.' || value.begin[i] == 'e')
			isFP = true;
	}

	if(!isFP)
	{
		if(syntax->suffix == InplaceStr("b"))
		{
			unsigned pos = 0;

			// Skip leading zeros
			while(value.begin[pos] == '0')
				pos++;

			if(int(value.length() - pos) > 64)
				Stop(ctx, value.begin, "ERROR: overflow in binary constant");

			long long num = ParseLong(ctx, value.begin + pos, value.end, 2);

			// If number overflows integer number, create long number
			if(int(num) == num)
				return new ExprIntegerLiteral(syntax, ctx.typeInt, num);

			return new ExprIntegerLiteral(syntax, ctx.typeLong, num);
		}
		else if(syntax->suffix == InplaceStr("l"))
		{
			long long num = ParseLong(ctx, value.begin, value.end, 10);

			return new ExprIntegerLiteral(syntax, ctx.typeLong, num);
		}
		else if(!syntax->suffix.empty())
		{
			Stop(ctx, syntax->suffix.begin, "ERROR: unknown number suffix '%.*s'", syntax->suffix.length(), syntax->suffix.begin);
		}

		if(value.length() > 1 && value.begin[0] == '0' && isDigit(value.begin[1]))
		{
			unsigned pos = 0;

			// Skip leading zeros
			while(value.begin[pos] == '0')
				pos++;

			if(int(value.length() - pos) > 22 || (int(value.length() - pos) > 21 && value.begin[pos] != '1'))
				Stop(ctx, value.begin, "ERROR: overflow in octal constant");

			long long num = ParseLong(ctx, value.begin, value.end, 8);

			// If number overflows integer number, create long number
			if(int(num) == num)
				return new ExprIntegerLiteral(syntax, ctx.typeInt, num);

			return new ExprIntegerLiteral(syntax, ctx.typeLong, num);
		}

		long long num = ParseLong(ctx, value.begin, value.end, 10);

		if(int(num) == num)
			return new ExprIntegerLiteral(syntax, ctx.typeInt, num);

		Stop(ctx, value.begin, "ERROR: overflow in decimal constant");
	}
		
	if(syntax->suffix == InplaceStr("f"))
	{
		double num = ParseDouble(ctx, value.begin);

		return new ExprRationalLiteral(syntax, ctx.typeFloat, float(num));
	}
	else if(!syntax->suffix.empty())
	{
		Stop(ctx, syntax->suffix.begin, "ERROR: unknown number suffix '%.*s'", syntax->suffix.length(), syntax->suffix.begin);
	}

	double num = ParseDouble(ctx, value.begin);

	return new ExprRationalLiteral(syntax, ctx.typeDouble, num);
}

ExprArray* AnalyzeArray(ExpressionContext &ctx, SynArray *syntax)
{
	assert(syntax->values.head);

	IntrusiveList<ExprBase> values;

	TypeBase *subType = NULL;

	for(SynBase *el = syntax->values.head; el; el = el->next)
	{
		ExprBase *value = AnalyzeExpression(ctx, el);

		if(subType == NULL)
			subType = value->type;
		else if(subType != value->type)
			Stop(ctx, el->pos, "ERROR: array element type mismatch");

		values.push_back(value);
	}

	return new ExprArray(syntax, ctx.GetArrayType(subType, values.size()), values);
}

ExprBase* AnalyzeVariableAccess(ExpressionContext &ctx, SynBase *syntax, InplaceStr name)
{
	if(VariableData **variable = ctx.variableMap.find(GetStringHash(name.begin, name.end)))
	{
		VariableData *data = *variable;

		// TODO: check external variable access

		if(data->type == ctx.typeAuto)
			Stop(ctx, name.begin, "ERROR: variable '%.*s' is being used while its type is unknown", FMT_ISTR(name));

		return new ExprVariableAccess(syntax, data->type, data);
	}

	if(FunctionData **function = ctx.functionMap.find(GetStringHash(name.begin, name.end)))
	{
		return new ExprFunctionAccess(syntax, (*function)->type, *function);
	}

	Stop(ctx, name.begin, "ERROR: unknown variable");

	return NULL;
}

ExprBase* AnalyzeVariableAccess(ExpressionContext &ctx, SynIdentifier *syntax)
{
	return AnalyzeVariableAccess(ctx, syntax, syntax->name);
}

ExprBase* AnalyzeVariableAccess(ExpressionContext &ctx, SynTypeSimple *syntax)
{
	if(syntax->path.empty())
	{
		// TODO: current namespace

		return AnalyzeVariableAccess(ctx, syntax, syntax->name);
	}

	// TODO: namespace path

	Stop(ctx, syntax->pos, "ERROR: unknown namespaced variable");

	return NULL;
}

ExprPreModify* AnalyzePreModify(ExpressionContext &ctx, SynPreModify *syntax)
{
	ExprBase *value = AnalyzeExpression(ctx, syntax->value);

	return new ExprPreModify(syntax, value->type, value, syntax->isIncrement);
}

ExprPostModify* AnalyzePostModify(ExpressionContext &ctx, SynPostModify *syntax)
{
	ExprBase *value = AnalyzeExpression(ctx, syntax->value);

	return new ExprPostModify(syntax, value->type, value, syntax->isIncrement);
}

ExprUnaryOp* AnalyzeUnaryOp(ExpressionContext &ctx, SynUnaryOp *syntax)
{
	ExprBase *value = AnalyzeExpression(ctx, syntax->value);

	// TODO: check operator overload

	bool binaryOp = IsBinaryOp(syntax->type);
	bool logicalOp = IsLogicalOp(syntax->type);

	// Type check
	if(ctx.IsFloatingPointType(value->type))
	{
		if(binaryOp || logicalOp)
			Stop(ctx, syntax->pos, "ERROR: unary operation '%s' is not supported on '%.*s'", GetOpName(syntax->type), FMT_ISTR(value->type->name));
	}
	else if(value->type == ctx.typeBool || value->type == ctx.typeAutoRef)
	{
		if(!logicalOp)
			Stop(ctx, syntax->pos, "ERROR: unary operation '%s' is not supported on '%.*s'", GetOpName(syntax->type), FMT_ISTR(value->type->name));
	}
	else if(isType<TypeRef>(value->type))
	{
		if(!logicalOp)
			Stop(ctx, syntax->pos, "ERROR: unary operation '%s' is not supported on '%.*s'", GetOpName(syntax->type), FMT_ISTR(value->type->name));
	}
	else if(!ctx.IsNumericType(value->type))
	{
		Stop(ctx, syntax->pos, "ERROR: unary operation '%s' is not supported on '%.*s'", GetOpName(syntax->type), FMT_ISTR(value->type->name));
	}

	TypeBase *resultType = NULL;

	if(logicalOp)
		resultType = ctx.typeBool;
	else
		resultType = value->type;

	return new ExprUnaryOp(syntax, resultType, syntax->type, value);
}

ExprBinaryOp* AnalyzeBinaryOp(ExpressionContext &ctx, SynBinaryOp *syntax)
{
	ExprBase *lhs = AnalyzeExpression(ctx, syntax->lhs);
	ExprBase *rhs = AnalyzeExpression(ctx, syntax->rhs);

	// TODO: 'in' is a function call
	// TODO: == and != for typeAutoRef is a function call
	// TODO: == and != for function types is a function call
	// TODO: == and != for unsized arrays is an overload call or a function call
	// TODO: && and || could have an operator overload where second argument is wrapped in a function for short-circuit evaluation
	// TODO: check operator overload

	if(lhs->type == ctx.typeVoid)
		Stop(ctx, syntax->pos, "ERROR: first operand type is 'void'");

	if(rhs->type == ctx.typeVoid)
		Stop(ctx, syntax->pos, "ERROR: second operand type is 'void'");

	bool binaryOp = IsBinaryOp(syntax->type);
	bool comparisonOp = IsComparisonOp(syntax->type);
	bool logicalOp = IsLogicalOp(syntax->type);

	if(ctx.IsFloatingPointType(lhs->type) || ctx.IsFloatingPointType(rhs->type))
	{
		if(logicalOp || binaryOp)
			Stop(ctx, syntax->pos, "ERROR: operation %s is not supported on '%.*s' and '%.*s'", GetOpName(syntax->type), FMT_ISTR(lhs->type->name), FMT_ISTR(rhs->type->name));
	}

	if(logicalOp)
	{
		// Logical operations require both operands to be 'bool'
		lhs = CreateCast(ctx, syntax->lhs->pos, lhs, ctx.typeBool);
		rhs = CreateCast(ctx, syntax->rhs->pos, rhs, ctx.typeBool);
	}
	else if(ctx.IsNumericType(lhs->type) && ctx.IsNumericType(rhs->type))
	{
		// Numeric operations promote both operands to a common type
		TypeBase *commonType = ctx.GetBinaryOpResultType(lhs->type, rhs->type);

		lhs = CreateCast(ctx, syntax->lhs->pos, lhs, commonType);
		rhs = CreateCast(ctx, syntax->rhs->pos, rhs, commonType);
	}

	if(lhs->type != rhs->type)
		Stop(ctx, syntax->pos, "ERROR: operation %s is not supported on '%.*s' and '%.*s'", GetOpName(syntax->type), FMT_ISTR(lhs->type->name), FMT_ISTR(rhs->type->name));

	TypeBase *resultType = NULL;

	if(comparisonOp || logicalOp)
		resultType = ctx.typeBool;
	else
		resultType = lhs->type;

	return new ExprBinaryOp(syntax, resultType, syntax->type, lhs, rhs);
}

ExprGetAddress* AnalyzeGetAddress(ExpressionContext &ctx, SynGetAddress *syntax)
{
	ExprBase *value = AnalyzeExpression(ctx, syntax->value);

	return new ExprGetAddress(syntax, ctx.GetReferenceType(value->type), value);
}

ExprDereference* AnalyzeDereference(ExpressionContext &ctx, SynDereference *syntax)
{
	ExprBase *value = AnalyzeExpression(ctx, syntax->value);

	if(TypeRef *type = getType<TypeRef>(value->type))
	{
		return new ExprDereference(syntax, type->subType, value);
	}

	Stop(ctx, syntax->pos, "ERROR: cannot dereference type '%.*s' that is not a pointer", FMT_ISTR(value->type->name));

	return NULL;
}

ExprConditional* AnalyzeConditional(ExpressionContext &ctx, SynConditional *syntax)
{
	ExprBase *condition = AnalyzeExpression(ctx, syntax->condition);

	ExprBase *trueBlock = AnalyzeStatement(ctx, syntax->trueBlock);
	ExprBase *falseBlock = AnalyzeStatement(ctx, syntax->falseBlock);

	TypeBase *resultType = NULL;

	if(trueBlock->type == falseBlock->type)
		resultType = trueBlock->type;
	else if(ctx.IsNumericType(trueBlock->type) && ctx.IsNumericType(falseBlock->type))
		resultType = ctx.GetBinaryOpResultType(trueBlock->type, falseBlock->type);
	else
		Stop(ctx, syntax->pos, "ERROR: Unknown common type");

	return new ExprConditional(syntax, resultType, condition, trueBlock, falseBlock);
}

ExprAssignment* AnalyzeAssignment(ExpressionContext &ctx, SynAssignment *syntax)
{
	ExprBase *lhs = AnalyzeExpression(ctx, syntax->lhs);
	ExprBase *rhs = AnalyzeExpression(ctx, syntax->rhs);

	return new ExprAssignment(syntax, lhs->type, lhs, rhs);
}

ExprModifyAssignment* AnalyzeModifyAssignment(ExpressionContext &ctx, SynModifyAssignment *syntax)
{
	ExprBase *lhs = AnalyzeExpression(ctx, syntax->lhs);
	ExprBase *rhs = AnalyzeExpression(ctx, syntax->rhs);

	return new ExprModifyAssignment(syntax, lhs->type, syntax->type, lhs, rhs);
}

ExprBase* AnalyzeMemberAccess(ExpressionContext &ctx, SynMemberAccess *syntax)
{
	// It could be a type property
	if(TypeBase *type = AnalyzeType(ctx, syntax->value, false))
	{
		if(syntax->member == InplaceStr("isReference"))
		{
			return new ExprBoolLiteral(syntax, ctx.typeBool, isType<TypeRef>(type));
		}

		if(syntax->member == InplaceStr("isArray"))
		{
			return new ExprBoolLiteral(syntax, ctx.typeBool, isType<TypeArray>(type) || isType<TypeUnsizedArray>(type));
		}

		if(syntax->member == InplaceStr("isFunction"))
		{
			return new ExprBoolLiteral(syntax, ctx.typeBool, isType<TypeFunction>(type));
		}

		if(syntax->member == InplaceStr("arraySize"))
		{
			if(TypeArray *arrType = getType<TypeArray>(type))
				return new ExprIntegerLiteral(syntax, ctx.typeInt, arrType->length);
				
			if(TypeUnsizedArray *arrType = getType<TypeUnsizedArray>(type))
				return new ExprIntegerLiteral(syntax, ctx.typeInt, -1);

			Stop(ctx, syntax->pos, "ERROR: 'arraySize' can only be applied to an array type, but we have '%s'", type->name);
		}

		Stop(ctx, syntax->pos, "ERROR: unknown expression type");
		// isReference/isArray/isFunction/arraySize/hasMember(x)/class constant/class typedef
	}

	ExprBase* value = AnalyzeExpression(ctx, syntax->value);

	if(TypeArray *node = getType<TypeArray>(value->type))
	{
		if(syntax->member == InplaceStr("size"))
			return new ExprIntegerLiteral(syntax, ctx.typeInt, node->length);

		Stop(ctx, syntax->pos, "ERROR: array doesn't have member with this name");
	}

	if(TypeStruct *node = getType<TypeStruct>(value->type))
	{
		for(VariableHandle *el = node->members.head; el; el = el->next)
		{
			if(el->variable->name == syntax->member)
				return new ExprMemberAccess(syntax, el->variable->type, value, el->variable); 
		}
	}

	Stop(ctx, syntax->pos, "ERROR: member variable or function '%.*s' is not defined in class '%.*s'", FMT_ISTR(syntax->member), FMT_ISTR(value->type->name));

	return NULL;
}

ExprArrayIndex* AnalyzeArrayIndex(ExpressionContext &ctx, SynTypeArray *syntax)
{
	ExprBase *value = AnalyzeExpression(ctx, syntax->type);
	ExprBase *index = AnalyzeExpression(ctx, syntax->size);

	if(TypeArray *type = getType<TypeArray>(value->type))
		return new ExprArrayIndex(syntax, type->subType, value, index);

	if(TypeUnsizedArray *type = getType<TypeUnsizedArray>(value->type))
		return new ExprArrayIndex(syntax, type->subType, value, index);

	Stop(ctx, syntax->pos, "ERROR: type '%.*s' is not an array", FMT_ISTR(value->type->name));

	return NULL;
}

ExprArrayIndex* AnalyzeArrayIndex(ExpressionContext &ctx, SynArrayIndex *syntax)
{
	if(syntax->arguments.size() > 1)
		Stop(ctx, syntax->pos, "ERROR: multiple array indexes are not supported");

	if(syntax->arguments.empty())
		Stop(ctx, syntax->pos, "ERROR: array index is missing");

	ExprBase *value = AnalyzeExpression(ctx, syntax->value);

	ExprBase *index = AnalyzeExpression(ctx, syntax->arguments.head);

	if(TypeArray *type = getType<TypeArray>(value->type))
		return new ExprArrayIndex(syntax, type->subType, value, index);

	if(TypeUnsizedArray *type = getType<TypeUnsizedArray>(value->type))
		return new ExprArrayIndex(syntax, type->subType, value, index);

	Stop(ctx, syntax->pos, "ERROR: type '%.*s' is not an array", FMT_ISTR(value->type->name));

	return NULL;
}

ExprFunctionCall* AnalyzeFunctionCall(ExpressionContext &ctx, SynFunctionCall *syntax)
{
	ExprBase *function = AnalyzeExpression(ctx, syntax->value);

	if(!syntax->aliases.empty())
		Stop(ctx, syntax->pos, "ERROR: function call with explicit generic arguments is not supported");

	IntrusiveList<ExprBase> arguments;

	for(SynCallArgument *el = syntax->arguments.head; el; el = getType<SynCallArgument>(el->next))
	{
		if(!el->name.empty())
			Stop(ctx, syntax->pos, "ERROR: named function arguments are not supported");

		arguments.push_back(AnalyzeExpression(ctx, el->value));
	}

	if(TypeFunction *type = getType<TypeFunction>(function->type))
	{
		return new ExprFunctionCall(syntax, type->returnType, function, arguments);
	}

	Stop(ctx, syntax->pos, "ERROR: unknown call");

	return NULL;
}

ExprFunctionCall* AnalyzeNew(ExpressionContext &ctx, SynNew *syntax)
{
	if(!syntax->constructor.empty())
		Stop(ctx, syntax->pos, "ERROR: custom constructors are not supported");

	if(!syntax->arguments.empty())
		Stop(ctx, syntax->pos, "ERROR: constructor call is not supported");

	TypeBase *type = NULL;
	SynBase *synCount = NULL;

	if(syntax->count == NULL && isType<SynTypeArray>(syntax->type))
	{
		SynTypeArray *node = getType<SynTypeArray>(syntax->type);

		type = AnalyzeType(ctx, node->type);
		synCount = node->size;
	}
	else
	{
		type = AnalyzeType(ctx, syntax->type);
		synCount = syntax->count;
	}

	ExprFunctionCall *call = NULL;

	if(synCount)
	{
		// TODO: implicit cast for manual function call
		ExprBase *count = AnalyzeExpression(ctx, synCount);

		FunctionData **function = ctx.functionMap.find(GetStringHash("__newA"));

		if(!function || (*function)->name != InplaceStr("__newA"))
			Stop(ctx, syntax->pos, "ERROR: internal error, __newA not found");

		IntrusiveList<ExprBase> arguments;

		arguments.push_back(new ExprIntegerLiteral(syntax, ctx.typeInt, type->size));
		arguments.push_back(count);
		arguments.push_back(new ExprTypeLiteral(syntax, ctx.typeTypeID, type));

{'fraction_non_alphanumeric': 0.14877172459893048, 'fraction_numerical': 0.003425802139037433, 'mean_word_length': 4.5765318008852995, 'pattern_counts': {'":': 0, '<': 143, '<?xml version=': 0, '>': 841, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4788890', 'n_tokens_mistral': 27154, 'n_tokens_neox': 24333, 'n_words': 5691}