#pragma once

#include "BeefySysLib/Common.h"
#include "BeefySysLib/util/DLIList.h"
#include "BeefySysLib/util/BumpAllocator.h"
#include "BeefySysLib/util/SizedArray.h"
#include "BfAstAllocator.h"
#include "BfIRBuilder.h"

//#define BF_AST_HAS_PARENT_MEMBER
//#define BF_AST_COMPACT
//#define BF_AST_VTABLE

#ifdef _DEBUG
#define BF_AST_VTABLE
#endif

/*#pragma warning(push)
#pragma warning(disable:4141)
#pragma warning(disable:4624)
#pragma warning(disable:4996)
#pragma warning(disable:4267)
#pragma warning(disable:4291)
#pragma warning(disable:4267)
#pragma warning(disable:4141)
#pragma warning(disable:4146)
#include "llvm/Support/raw_ostream.h"
#include "llvm/ADT/SmallVector.h"
#pragma warning(pop)*/

namespace llvm
{
	class Value;
};

NS_BF_BEGIN

class BfType;
class BfParser;
class BfSource;
class BfAstNode;
class BfTokenNode;
class BfTokenPairNode;
class BfUsingSpecifierNode;
class BfTypeReference;
class BfTypeDef;
class BfMethodDef;
class BfFieldDef;
class BfSystem;
class BfMethodInstance;
class BfPassInstance;

enum BfProtection : uint8
{
	BfProtection_Disable,
	BfProtection_Hidden,	
	BfProtection_Private,
	BfProtection_Internal,
	BfProtection_Protected,
	BfProtection_ProtectedInternal,
	BfProtection_Public,

	BfProtection_COUNT
};

enum BfCheckedKind : int8
{
	BfCheckedKind_NotSet,
	BfCheckedKind_Checked,
	BfCheckedKind_Unchecked
};

static bool CheckProtection(BfProtection protection, bool allowProtected, bool allowPrivate)
{
	return (protection == BfProtection_Public) ||
		((protection == BfProtection_Protected) && (allowProtected)) ||
		((protection == BfProtection_Private) && (allowPrivate));
}

struct BfVariant
{
	struct StructData
	{
		int mSize;
		uint8 mData[1];
	};

	BfTypeCode mTypeCode;
	int mWarnType;
	union
	{
		bool mBool;
		int8 mInt8;
		uint8 mUInt8;
		int16 mInt16;
		uint16 mUInt16;
		int32 mInt32;
		uint32 mUInt32;
		int64 mInt64;
		uint64 mUInt64;
		float mSingle;
		double mDouble;
		String* mString;
		void* mPtr;
	};
	double ToDouble()
	{
		if (mTypeCode == BfTypeCode_Double)
			return mDouble;
		if (mTypeCode == BfTypeCode_Float)
			return mSingle;
		return (double)mInt64;
	}

	BfVariant()
	{
		mTypeCode = BfTypeCode_None;
		mWarnType = 0;
		mUInt64 = 0;
	}

	BfVariant(const BfVariant& variant)
	{
		mTypeCode = BfTypeCode_None;
		mWarnType = 0;
		mUInt64 = 0;
		*this = variant;
	}

	BfVariant(BfVariant&& variant)
	{
		mTypeCode = variant.mTypeCode;
		mWarnType = variant.mWarnType;
		mUInt64 = variant.mUInt64;
		variant.mTypeCode = BfTypeCode_None;
	}

	~BfVariant()
	{
		if (mTypeCode == BfTypeCode_Struct)
			delete [] (uint8*)mPtr;
	}

	BfVariant& operator=(const BfVariant& variant)
	{
		if (mTypeCode == BfTypeCode_Struct)
			delete [] (uint8*)mPtr;
		mTypeCode = variant.mTypeCode;
		mWarnType = variant.mWarnType;
		mUInt64 = variant.mUInt64;

		if (variant.mTypeCode == BfTypeCode_Struct)
		{
			StructData* srcStructData = (StructData*)mPtr;
			StructData* destStructData = (StructData*)(new uint8[srcStructData->mSize + 4]);
			destStructData->mSize = srcStructData->mSize;
			memcpy(destStructData->mData, srcStructData->mData, destStructData->mSize);
			mPtr = destStructData;
		}

		return *this;
	}

	bool operator==(const BfVariant& variant) const
	{
		if (mTypeCode != variant.mTypeCode)
			return false;
		if (mTypeCode == BfTypeCode_Struct)
		{
			StructData* structDataA = (StructData*)mPtr;
			StructData* structDataB = (StructData*)variant.mPtr;
			if (structDataA->mSize != structDataB->mSize)
				return false;
			return memcmp(structDataA->mData, structDataB->mData, structDataA->mSize) == 0;
		}
		return mUInt64 == variant.mUInt64;
	}
};

enum BfToken : uint8
{
	BfToken_None,
	BfToken_Abstract,
	BfToken_AlignOf,
	BfToken_AllocType,
	BfToken_Append,
	BfToken_As,
	BfToken_Asm,
	BfToken_AsmNewline,
	BfToken_Base,
	BfToken_Box,
	BfToken_Break,
	BfToken_Case,
	BfToken_Catch,
	BfToken_Checked,
	BfToken_Class,
	BfToken_Comptype,
	BfToken_Concrete,
	BfToken_Const,
	BfToken_Continue,
	BfToken_Decltype,
	BfToken_Default,
	BfToken_Defer,
	BfToken_Delegate,
	BfToken_Delete,
	BfToken_Do,
	BfToken_Else,
	BfToken_Enum,
	BfToken_Explicit,
	BfToken_Extern,
	BfToken_Extension,
	BfToken_Fallthrough,
	BfToken_Finally,
	BfToken_Fixed,
	BfToken_For,
	BfToken_Function,
	BfToken_Goto,
	BfToken_If,
	BfToken_Implicit,
	BfToken_In,
	BfToken_Inline,
	BfToken_Interface,
	BfToken_Internal,
	BfToken_Is,
	BfToken_IsConst,
	BfToken_Let,
	BfToken_Mixin,
	BfToken_Mut,
	BfToken_NameOf,
	BfToken_Namespace,
	BfToken_New,
	BfToken_Not,
	BfToken_Null,
	BfToken_Nullable,
	BfToken_OffsetOf,
	BfToken_Operator,
	BfToken_Out,
	BfToken_Override,
	BfToken_Params,
	BfToken_Private,
	BfToken_Protected,
	BfToken_Public,
	BfToken_ReadOnly,
	BfToken_Repeat,
	BfToken_Ref,
	BfToken_RetType,
	BfToken_Return,
	BfToken_Scope,
	BfToken_Sealed,
	BfToken_SizeOf,	
	BfToken_Static,
	BfToken_StrideOf,
	BfToken_Struct,
	BfToken_Switch,
	BfToken_This,
	BfToken_Throw,
	BfToken_Try,
	BfToken_TypeAlias,
	BfToken_TypeOf,
	BfToken_Unchecked,
	BfToken_Unsigned,
	BfToken_Using,
	BfToken_Var,
	BfToken_Virtual,
	BfToken_Volatile,
	BfToken_When,
	BfToken_Where,
	BfToken_While,
	BfToken_Yield,
	BfToken_AssignEquals,
	BfToken_CompareEquals,
	BfToken_CompareStrictEquals,
	BfToken_CompareNotEquals,
	BfToken_CompareStrictNotEquals,
	BfToken_LessEquals,
	BfToken_GreaterEquals,
	BfToken_Spaceship,
	BfToken_PlusEquals,
	BfToken_MinusEquals,
	BfToken_MultiplyEquals,
	BfToken_DivideEquals,
	BfToken_ModulusEquals,
	BfToken_ShiftLeftEquals,
	BfToken_ShiftRightEquals,
	BfToken_AndEquals,
	BfToken_AndMinus,
	BfToken_AndPlus,
	BfToken_AndStar,
	BfToken_AndMinusEquals,
	BfToken_AndPlusEquals,
	BfToken_AndStarEquals,
	BfToken_OrEquals,
	BfToken_XorEquals,
	BfToken_NullCoalsceEquals,
	BfToken_LBrace,
	BfToken_RBrace,
	BfToken_LParen,
	BfToken_RParen,
	BfToken_LBracket,
	BfToken_RBracket,
	BfToken_LChevron,
	BfToken_RChevron,
	BfToken_LDblChevron,
	BfToken_RDblChevron,
	BfToken_Semicolon,
	BfToken_Colon,
	BfToken_Comma,
	BfToken_Dot,
	BfToken_DotDot,
	BfToken_DotDotDot,
	BfToken_DotDotLess,
	BfToken_QuestionDot,
	BfToken_QuestionLBracket,
	BfToken_AutocompleteDot,
	BfToken_Plus,
	BfToken_Minus,
	BfToken_DblPlus,
	BfToken_DblMinus,
	BfToken_Star,
	BfToken_ForwardSlash,
	BfToken_Modulus,
	BfToken_Ampersand,
	BfToken_At,
	BfToken_DblAmpersand,
	BfToken_Bar,
	BfToken_DblBar,
	BfToken_Bang,
	BfToken_Carat,
	BfToken_Tilde,
	BfToken_Question,
	BfToken_DblQuestion,
	BfToken_Arrow,
	BfToken_FatArrow,
};

class BfAstNode;
class BfScopeNode;
class BfNewNode;
class BfLabeledBlock;
class BfGenericArgumentsNode;
class BfCtorExplicitNode;
class BfStatement;
class BfLabelableStatement;
class BfExpression;
class BfExpressionStatement;
class BfNamedExpression;
class BfAttributedExpression;
class BfAttributedStatement;
class BfLiteralExpression;
class BfStringInterpolationExpression;
class BfBlock;
class BfUnscopedBlock;
class BfBlockExtension;
class BfRootNode;
class BfErrorNode;
class BfTokenNode;
class BfIdentifierNode;
class BfAttributedIdentifierNode;
class BfQualifiedNameNode;
class BfNamespaceDeclaration;
class BfTypeDeclaration;
class BfInitializerTypeDeclaration;
class BfTypeAliasDeclaration;
class BfMethodDeclaration;
class BfOperatorDeclaration;
class BfFieldDeclaration;
class BfEnumCaseDeclaration;
class BfParameterDeclaration;
class BfForStatement;
class BfUsingStatement;
class BfDoStatement;
class BfRepeatStatement;
class BfWhileStatement;
class BfMemberDeclaration;
class BfTypeReference;
class BfParameterDeclaration;
class BfVariableDeclaration;
class BfLocalMethodDeclaration;
class BfScopedInvocationTarget;
class BfInvocationExpression;
class BfDeferStatement;
class BfReturnStatement;
class BfYieldStatement;
class BfUnaryOperatorExpression;
class BfBinaryOperatorExpression;
class BfInlineTypeReference;
class BfArrayTypeRef;
class BfPointerTypeRef;
class BfDotTypeReference;
class BfVarTypeReference;
class BfVarRefTypeReference;
class BfLetTypeReference;
class BfGenericInstanceTypeRef;
class BfTupleTypeRef;
class BfTagTypeRef;
class BfDelegateTypeRef;
class BfExprModTypeRef;
class BfCommentNode;
class BfIfStatement;
class BfParenthesizedExpression;
class BfTupleExpression;
class BfAssignmentExpression;
class BfNamedTypeReference;
class BfObjectCreateExpression;
class BfBoxExpression;
class BfDelegateBindExpression;
class BfLambdaBindExpression;
class BfCastExpression;
class BfGenericParamsDeclaration;
class BfThisExpression;
class BfBaseExpression;
class BfMixinExpression;
class BfTryStatement;
class BfCatchStatement;
class BfFinallyStatement;
class BfCheckedStatement;
class BfUncheckedStatement;
class BfBreakStatement;
class BfContinueStatement;
class BfFallthroughStatement;
class BfThrowStatement;
class BfDeleteStatement;
class BfIndexerExpression;
class BfMemberReferenceExpression;
class BfDynamicCastExpression;
class BfCheckTypeExpression;
class BfConstructorDeclaration;
class BfAutoConstructorDeclaration;
class BfDestructorDeclaration;
class BfQualifiedTypeReference;
class BfUsingDirective;
class BfUsingModDirective;
class BfPropertyMethodDeclaration;
class BfPropertyBodyExpression;
class BfPropertyDeclaration;
class BfIndexerDeclaration;
class BfPreprocesorIgnoredSectionNode;
class BfPreprocessorNode;
class BfPreprocessorDefinedExpression;
class BfTypeOfExpression;
class BfEnumCaseBindExpression;
class BfSwitchCase;
class BfCaseExpression;
class BfWhenExpression;
class BfSwitchStatement;
class BfForEachStatement;
class BfTypedValueExpression;
class BfTypeAttrExpression;
class BfSizeOfExpression;
class BfAlignOfExpression;
class BfOffsetOfExpression;
class BfNameOfExpression;
class BfStrideOfExpression;
class BfIsConstExpression;
class BfDefaultExpression;
class BfUninitializedExpression;
class BfConditionalExpression;
class BfInitializerExpression;
class BfCollectionInitializerExpression;
class BfSizedArrayCreateExpression;
class BfEmptyStatement;
class BfGenericOperatorConstraint;
class BfGenericConstraintsDeclaration;
class BfAttributeDirective;
class BfNullableTypeRef;
class BfRefTypeRef;
class BfModifiedTypeRef;
class BfConstTypeRef;
class BfConstExprTypeRef;
class BfInlineAsmStatement;
class BfInlineAsmInstruction;
class BfFieldDtorDeclaration;

class BfStructuralVisitor
{
public:
	bool mCapturingChildRef;
	BfAstNode** mCurChildRef;

public:
	void VisitMembers(BfBlock* node);
	void VisitChildNoRef(BfAstNode* nodeRef);
	void DoVisitChild(BfAstNode*& nodeRef);
	void AssertValidChildAddr(BfAstNode** nodeRef);
	template <typename T>
	void VisitChild(T& nodeRef)
	{
		/*if ((BfAstNode*)nodeRef == NULL)
			return;
		nodeRef->Accept(this);*/

		if (nodeRef == NULL)
			return;
		if (mCapturingChildRef)
		{
			mCurChildRef = ((BfAstNode**) &nodeRef);
			//AssertValidChildAddr(mCurChildRef);
		}
		nodeRef->Accept(this);
		mCurChildRef = NULL;
	}
	template <typename T>
	void VisitChildNoRef(const T& nodeRef)
	{
		if ((BfAstNode*)nodeRef == NULL)
			return;
		nodeRef->Accept(this);
	};

public:
	BfStructuralVisitor();

	virtual void Visit(BfAstNode* bfAstNode) {}
	virtual void Visit(BfErrorNode* bfErrorNode);
	virtual void Visit(BfScopeNode* scopeNode);
	virtual void Visit(BfNewNode* newNode);
	virtual void Visit(BfLabeledBlock* labeledBlock);
	virtual void Visit(BfExpression* expr);
	virtual void Visit(BfExpressionStatement* exprStmt);
	virtual void Visit(BfNamedExpression* namedExpr);
	virtual void Visit(BfAttributedExpression* attribExpr);
	virtual void Visit(BfStatement* stmt);
	virtual void Visit(BfAttributedStatement* attribStmt);
	virtual void Visit(BfLabelableStatement* labelableStmt);
	virtual void Visit(BfTypedValueExpression* typedValueExpr);

	virtual void Visit(BfCommentNode* commentNode);
	virtual void Visit(BfPreprocesorIgnoredSectionNode* preprocesorIgnoredSection);
	virtual void Visit(BfPreprocessorNode* preprocessorNode);
	virtual void Visit(BfPreprocessorDefinedExpression* definedExpr);

	virtual void Visit(BfAttributeDirective* attributeDirective);
	virtual void Visit(BfGenericParamsDeclaration* genericParams);
	virtual void Visit(BfGenericOperatorConstraint* genericConstraints);
	virtual void Visit(BfGenericConstraintsDeclaration* genericConstraints);
	virtual void Visit(BfGenericArgumentsNode* genericArgumentsNode);
	virtual void Visit(BfCtorExplicitNode* genericArgumentsNode);

	virtual void Visit(BfEmptyStatement* emptyStmt);
	virtual void Visit(BfTokenNode* tokenNode);
	virtual void Visit(BfTokenPairNode* tokenPairNode);
	virtual void Visit(BfUsingSpecifierNode* usingSpecifier);
	virtual void Visit(BfLiteralExpression* literalExpr);
	virtual void Visit(BfStringInterpolationExpression* stringInterpolationExpression);
	virtual void Visit(BfIdentifierNode* identifierNode);
	virtual void Visit(BfAttributedIdentifierNode* attrIdentifierNode);
	virtual void Visit(BfQualifiedNameNode* nameNode);
	virtual void Visit(BfThisExpression* thisExpr);
	virtual void Visit(BfBaseExpression* baseExpr);
	virtual void Visit(BfMixinExpression* thisExpr);
	virtual void Visit(BfSizedArrayCreateExpression* createExpr);
	virtual void Visit(BfInitializerExpression* collectionInitExpr);
	virtual void Visit(BfCollectionInitializerExpression* collectionInitExpr);
	virtual void Visit(BfTypeReference* typeRef);
	virtual void Visit(BfInlineTypeReference* typeRef);
	virtual void Visit(BfNamedTypeReference* typeRef);
	virtual void Visit(BfQualifiedTypeReference* qualifiedType);
	virtual void Visit(BfDotTypeReference* typeRef);
	virtual void Visit(BfVarTypeReference* typeRef);
	virtual void Visit(BfVarRefTypeReference* typeRef);
	virtual void Visit(BfLetTypeReference* typeRef);
	virtual void Visit(BfConstTypeRef* typeRef);
	virtual void Visit(BfConstExprTypeRef* typeRef);
	virtual void Visit(BfRefTypeRef* typeRef);
	virtual void Visit(BfModifiedTypeRef* typeRef);
	virtual void Visit(BfArrayTypeRef* typeRef);
	virtual void Visit(BfGenericInstanceTypeRef* typeRef);
	virtual void Visit(BfTupleTypeRef* typeRef);
	virtual void Visit(BfTagTypeRef* typeRef);
	virtual void Visit(BfDelegateTypeRef* typeRef);
	virtual void Visit(BfExprModTypeRef* declTypeRef);
	virtual void Visit(BfPointerTypeRef* typeRef);
	virtual void Visit(BfNullableTypeRef* typeRef);
	virtual void Visit(BfVariableDeclaration* varDecl);
	virtual void Visit(BfLocalMethodDeclaration* methodDecl);
	virtual void Visit(BfParameterDeclaration* paramDecl);
	virtual void Visit(BfTypeAttrExpression* typeAttrExpr);
	virtual void Visit(BfTypeOfExpression* typeOfExpr);
	virtual void Visit(BfSizeOfExpression* sizeOfExpr);
	virtual void Visit(BfAlignOfExpression* alignOfExpr);
	virtual void Visit(BfStrideOfExpression* strideOfExpr);
	virtual void Visit(BfOffsetOfExpression* offsetOfExpr);
	virtual void Visit(BfNameOfExpression* nameOfExpr);
	virtual void Visit(BfIsConstExpression* isConstExpr);
	virtual void Visit(BfDefaultExpression* defaultExpr);
	virtual void Visit(BfUninitializedExpression* uninitializedExpr);
	virtual void Visit(BfCheckTypeExpression* checkTypeExpr);
	virtual void Visit(BfDynamicCastExpression* dynCastExpr);
	virtual void Visit(BfCastExpression* castExpr);
	virtual void Visit(BfDelegateBindExpression* delegateBindExpr);
	virtual void Visit(BfLambdaBindExpression* lambdaBindExpr);
	virtual void Visit(BfObjectCreateExpression* objCreateExpr);
	virtual void Visit(BfBoxExpression* boxExpr);
	virtual void Visit(BfScopedInvocationTarget* scopedTarget);
	virtual void Visit(BfInvocationExpression* invocationExpr);
	virtual void Visit(BfEnumCaseBindExpression* caseBindExpr);
	virtual void Visit(BfCaseExpression* caseExpr);
	virtual void Visit(BfSwitchCase* switchCase);
	virtual void Visit(BfWhenExpression* whenExpr);
	virtual void Visit(BfSwitchStatement* switchStmt);
	virtual void Visit(BfTryStatement* tryStmt);
	virtual void Visit(BfCatchStatement* catchStmt);
	virtual void Visit(BfFinallyStatement* finallyStmt);
	virtual void Visit(BfCheckedStatement* checkedStmt);
	virtual void Visit(BfUncheckedStatement* uncheckedStmt);
	virtual void Visit(BfIfStatement* ifStmt);
	virtual void Visit(BfThrowStatement* throwStmt);
	virtual void Visit(BfDeleteStatement* deleteStmt);
	virtual void Visit(BfReturnStatement* returnStmt);
	virtual void Visit(BfYieldStatement* returnStmt);
	virtual void Visit(BfBreakStatement* breakStmt);
	virtual void Visit(BfContinueStatement* continueStmt);
	virtual void Visit(BfFallthroughStatement* fallthroughStmt);
	virtual void Visit(BfUsingStatement* whileStmt);
	virtual void Visit(BfDoStatement* whileStmt);
	virtual void Visit(BfRepeatStatement* repeatStmt);
	virtual void Visit(BfWhileStatement* whileStmt);
	virtual void Visit(BfForStatement* forStmt);
	virtual void Visit(BfForEachStatement* forEachStmt);
	virtual void Visit(BfDeferStatement* deferStmt);
	virtual void Visit(BfConditionalExpression* condExpr);
	virtual void Visit(BfAssignmentExpression* assignExpr);
	virtual void Visit(BfParenthesizedExpression* parenExpr);
	virtual void Visit(BfTupleExpression* parenExpr);
	virtual void Visit(BfMemberReferenceExpression* memberRefExpr);
	virtual void Visit(BfIndexerExpression* indexerExpr);
	virtual void Visit(BfUnaryOperatorExpression* binOpExpr);
	virtual void Visit(BfBinaryOperatorExpression* binOpExpr);
	virtual void Visit(BfConstructorDeclaration* ctorDeclaration);
	virtual void Visit(BfAutoConstructorDeclaration* ctorDeclaration);
	virtual void Visit(BfDestructorDeclaration* dtorDeclaration);
	virtual void Visit(BfMethodDeclaration* methodDeclaration);
	virtual void Visit(BfOperatorDeclaration* operatorDeclaration);
	virtual void Visit(BfPropertyMethodDeclaration* propertyMethodDeclaration);
	virtual void Visit(BfPropertyBodyExpression* propertyBodyExpression);
	virtual void Visit(BfPropertyDeclaration* propertyDeclaration);
	virtual void Visit(BfIndexerDeclaration* indexerDeclaration);
	virtual void Visit(BfFieldDeclaration* fieldDeclaration);
	virtual void Visit(BfEnumCaseDeclaration* enumCaseDeclaration);
	virtual void Visit(BfFieldDtorDeclaration* fieldDtorDeclaration);
	virtual void Visit(BfTypeDeclaration* typeDeclaration);
	virtual void Visit(BfTypeAliasDeclaration* typeDeclaration);
	virtual void Visit(BfUsingDirective* usingDirective);
	virtual void Visit(BfUsingModDirective* usingDirective);
	virtual void Visit(BfNamespaceDeclaration* namespaceDeclaration);
	virtual void Visit(BfBlock* block);
	virtual void Visit(BfUnscopedBlock* block);
	virtual void Visit(BfBlockExtension* block);
	virtual void Visit(BfRootNode* rootNode);
	virtual void Visit(BfInlineAsmStatement* asmStmt);
	virtual void Visit(BfInlineAsmInstruction* asmInst);
};

enum BfTypedValueKind
{
	BfTypedValueKind_Addr,
	BfTypedValueKind_VolatileAddr,
	BfTypedValueKind_CopyOnMutateAddr,
	BfTypedValueKind_CopyOnMutateAddr_Derived,
	BfTypedValueKind_ReadOnlyAddr,
	BfTypedValueKind_TempAddr,
	BfTypedValueKind_RestrictedTempAddr,
	BfTypedValueKind_ReadOnlyTempAddr,
	BfTypedValueKind_ThisAddr,
	BfTypedValueKind_BaseAddr,
	BfTypedValueKind_ReadOnlyThisAddr,
	BfTypedValueKind_ReadOnlyBaseAddr,

	BfTypedValueKind_Value,
	BfTypedValueKind_ThisValue,
	BfTypedValueKind_BaseValue,
	BfTypedValueKind_ReadOnlyThisValue,
	BfTypedValueKind_ReadOnlyBaseValue,
	BfTypedValueKind_MutableValue, // Only applicable for generic params
	BfTypedValueKind_SplatHead,
	BfTypedValueKind_ThisSplatHead,
	BfTypedValueKind_BaseSplatHead,
	BfTypedValueKind_SplatHead_NeedsCasting,
	BfTypedValueKind_ParamsSplat,
	BfTypedValueKind_Params,

	BfTypedValueKind_NoValue,
	BfTypedValueKind_UntypedValue,
	BfTypedValueKind_GenericConstValue
};

class BfTypedValue
{
public:
	//llvm::Value* mValue;
	BfIRValue mValue;
	BfType* mType;
	BfTypedValueKind mKind; // Is address of variable

public:
	BfTypedValue()
	{
		mType = NULL;
		mKind = BfTypedValueKind_NoValue;
	}

	BfTypedValue(BfTypedValueKind kind)
	{
		mType = NULL;
		mKind = kind;
	}

	BfTypedValue(BfType* resolvedType)
	{
		mType = resolvedType;
		mKind = BfTypedValueKind_NoValue;
	}

	BfTypedValue(BfIRValue val, BfType* resolvedType, bool isAddr)
	{
		BF_ASSERT((!val) || (resolvedType != NULL));
		mValue = val;
		mType = resolvedType;
		mKind = isAddr ? BfTypedValueKind_Addr : BfTypedValueKind_Value;
#ifdef _DEBUG
		//DbgCheckType();
#endif
		BF_ASSERT(val);

		/*if ((!val) && (resolvedType != NULL))
		{
			BF_ASSERT(IsValuelessType());
		}
		else if (IsValuelessType())
		{
			//BF_ASSERT(!val || IsAddr());
		}*/
	}

	BfTypedValue(BfIRValue val, BfType* resolvedType, BfTypedValueKind kind = BfTypedValueKind_Value)
	{
		BF_ASSERT((!val) || (resolvedType != NULL));
		mValue = val;
		mType = resolvedType;
		mKind = kind;
#ifdef _DEBUG
		//DbgCheckType();
#endif
		if ((!val) && (resolvedType != NULL))
		{
			BF_ASSERT(IsValuelessType());
		}
		/*else if (IsValuelessType())
		{
			BF_ASSERT(!val || IsAddr());
		}*/
	}

	void DbgCheckType() const;
	bool IsValuelessType() const;

	bool HasType() const
	{
		return mType != NULL;
	}

	bool IsStatic() const
	{
		return (!mValue) && (mKind != BfTypedValueKind_GenericConstValue);
	}

	bool IsAddr() const
	{
		return (mKind < BfTypedValueKind_Value);
	}

	bool IsTempAddr() const
	{
		return ((mKind == BfTypedValueKind_ReadOnlyTempAddr) || (mKind == BfTypedValueKind_RestrictedTempAddr) || (mKind == BfTypedValueKind_TempAddr));
	}

	bool IsCopyOnMutate() const
	{
		return (mKind == BfTypedValueKind_CopyOnMutateAddr) || (mKind == BfTypedValueKind_CopyOnMutateAddr_Derived);
	}

	bool IsReadOnly() const
	{
		switch (mKind)
		{
		case BfTypedValueKind_ReadOnlyAddr:
		case BfTypedValueKind_ReadOnlyTempAddr:
		case BfTypedValueKind_ReadOnlyThisValue:
		case BfTypedValueKind_ReadOnlyBaseValue:
		case BfTypedValueKind_ReadOnlyThisAddr:
		case BfTypedValueKind_ReadOnlyBaseAddr:
		case BfTypedValueKind_MutableValue: // 'mutable' means we can call mut methods, not that we can assign to it
		case BfTypedValueKind_SplatHead:
		case BfTypedValueKind_ThisSplatHead:
		case BfTypedValueKind_BaseSplatHead:
		case BfTypedValueKind_ParamsSplat:
		case BfTypedValueKind_Params:
			return true;
		default:
			return false;
		}

		return false;
	}

	bool IsThis() const
	{
		return (mKind == BfTypedValueKind_ThisValue) || (mKind == BfTypedValueKind_ThisAddr) || (mKind == BfTypedValueKind_ReadOnlyThisValue) ||
			(mKind == BfTypedValueKind_ReadOnlyThisAddr) || (mKind == BfTypedValueKind_ThisSplatHead);
	}

	bool IsBase() const
	{
		return (mKind == BfTypedValueKind_BaseValue) || (mKind == BfTypedValueKind_BaseAddr) || (mKind == BfTypedValueKind_ReadOnlyBaseValue) ||
			(mKind == BfTypedValueKind_ReadOnlyBaseAddr) || (mKind == BfTypedValueKind_BaseSplatHead);
	}

	void ToBase()
	{
		BF_ASSERT(IsThis());
		mKind = (BfTypedValueKind)((int)mKind + 1);
	}

	void ToThis()
	{
		BF_ASSERT(IsBase());
		mKind = (BfTypedValueKind)((int)mKind - 1);
	}

	bool IsVolatile() const
	{
		return mKind == BfTypedValueKind_VolatileAddr;
	}

	bool IsSplat() const
	{
		return (mKind >= BfTypedValueKind_SplatHead) && (mKind <= BfTypedValueKind_ParamsSplat);
	}

	bool IsUntypedValue() const
	{
		return (mKind == BfTypedValueKind_UntypedValue);
	}

	bool IsNoValueType() const
	{
		return (mKind == BfTypedValueKind_NoValue) && (mType != NULL);
	}

	bool IsParams() const
	{
		return (mKind == BfTypedValueKind_ParamsSplat) || (mKind == BfTypedValueKind_Params);
	}

	operator bool() const
	{
		//return (mKind != BfTypedValueKind_NoValue) && ((mValue) || ((mType != NULL) && (IsValuelessType())));
		return (mKind != BfTypedValueKind_NoValue);
	}

	void MakeReadOnly()
	{
		switch (mKind)
		{
		case BfTypedValueKind_Addr:
			mKind = BfTypedValueKind_ReadOnlyAddr;
			break;
		case BfTypedValueKind_TempAddr:
		case BfTypedValueKind_RestrictedTempAddr:
			mKind = BfTypedValueKind_ReadOnlyTempAddr;
			break;
		default:
			break;
		}
	}

	void MakeTemporary(bool restricted = false)
	{
		switch (mKind)
		{
		case BfTypedValueKind_Addr:
			mKind = restricted ? BfTypedValueKind_RestrictedTempAddr : BfTypedValueKind_TempAddr;
			break;
		case BfTypedValueKind_ReadOnlyAddr:
			mKind = BfTypedValueKind_ReadOnlyTempAddr;
			break;
		default:
			break;
		}
	}

	bool CanModify() const;
};

#define BF_AST_TYPE(name, TBase) \
	static BfAstTypeInfo sTypeInfo;\
	static void ClassAccept(BfAstNode* node, BfStructuralVisitor* bfVisitor) { bfVisitor->Visit((name*)node); } \
	TBase* ToBase() { return (TBase*)this; } \
	name() { InitWithTypeId(sTypeInfo.mTypeId); }

#ifdef BF_AST_DO_IMPL
#define BF_AST_DECL(name, TBase) \
	BfAstTypeInfo name::sTypeInfo(#name, &TBase::sTypeInfo, &name::ClassAccept);
#else
#define BF_AST_DECL(name, TBase)
#endif

class BfAstNode;

class BfAstNodeList
{
public:
	SizedArray<BfAstNode*, 4> mList;

public:
	bool operator==(const BfAstNodeList& rhs)
	{
		if (mList.mSize != rhs.mList.mSize)
			return false;
		for (int i = 0; i < mList.mSize; i++)
			if (mList[i] != rhs.mList[i])
				return false;
		return true;
	}
};

template <typename T>
class BfChunkedArray
{
public:
	static const int sLeafSize = 8;

	T** mRoots;
	int mSize;

public:
	BfChunkedArray()
	{
		mSize = 0;
	}

	int GetRootCount()
	{
		return (mSize + sLeafSize - 1) / sLeafSize;
	}

	void Add(T val, BfAstAllocator* bumpAlloc)
	{
		int idx = mSize;
		if ((mSize % sLeafSize) == 0)
		{
			int rootCount = GetRootCount();
			mSize++;
			int newRootCount = GetRootCount();
			if (rootCount != newRootCount)
			{
				T** newRoots = (T**)bumpAlloc->AllocBytes(newRootCount * sizeof(T**), sizeof(T**));
				memcpy(newRoots, mRoots, rootCount * sizeof(T*));
				mRoots = newRoots;
			}
			mRoots[idx / sLeafSize] = (T*)bumpAlloc->AllocBytes(sLeafSize * sizeof(T*), sizeof(T*));
		}
		else
			mSize++;
		mRoots[idx / sLeafSize][idx % sLeafSize] = val;
	}

	bool IsEmpty()
	{
		return mSize == 0;
	}

	void SetSize(int size)
	{
		BF_ASSERT(size <= mSize);
		mSize = size;
	}

	T& operator[](int idx)
	{
		return mRoots[idx / sLeafSize][idx % sLeafSize];
	}

	T Get(int idx)
	{
		if ((idx < 0) || (idx >= mSize))
			return (T)0;
		return mRoots[idx / sLeafSize][idx % sLeafSize];
	}

	T GetLast()
	{
		if (mSize == 0)
			return (T)0;
		return (*this)[mSize - 1];
	}

	T GetFirst()
	{
		if (mSize == 0)
			return (T)0;
		return (*this)[0];
	}
};

template <typename T>
class BfDebugArray
{
public:
	static const int STATIC_SIZE = 1024;

	Array<T> mElements;
	int mSize;

public:
	BfDebugArray()
	{
		mSize = 0;
	}

	void Add(T val, BfAstAllocator* bumpAlloc)
	{
		mElements.push_back(val);
		mSize++;
	}

	bool IsEmpty()
	{
		return mSize == 0;
	}

	void SetSize(int size)
	{
		BF_ASSERT(size <= mSize);
		mSize = size;
	}

	T& operator[](int idx)
	{
		return mElements[idx];
	}

	T Get(int idx)
	{
		if ((idx < 0) || (idx >= mSize))
			return (T)0;
		return mElements[idx];
	}

	T GetLast()
	{
		if (mSize == 0)
			return (T)0;
		return mElements[mSize - 1];
	}

	T GetFirst()
	{
		if (mSize == 0)
			return (T)0;
		return (*this)[0];
	}
};

template <typename T>
class BfDeferredSizedArray : public llvm::SmallVector<T, 8>
{
public:
	BfSizedArray<T>* mSizedArray;
	BfAstAllocator* mAlloc;

public:
	BfDeferredSizedArray(BfSizedArray<T>& arr, BfAstAllocator* alloc)
	{
		mSizedArray = &arr;
		mAlloc = alloc;
	}

	~BfDeferredSizedArray()
	{
		mSizedArray->mSize = (int)this->size();
		if (mSizedArray->mSize > 0)
		{
			mSizedArray->mVals = (T*)mAlloc->AllocBytes(mSizedArray->mSize * sizeof(T), sizeof(T));
			memcpy(mSizedArray->mVals, &(*this)[0], mSizedArray->mSize * sizeof(T));
		}
	}
};

#ifdef BF_USE_NEAR_NODE_REF
#define ASTREF(T) BfNearNodeRef<T>
#else
#define ASTREF(T) T
#endif

template <typename T, typename T2>
static void BfSizedArrayInitIndirect(BfSizedArray<T>& sizedArray, const SizedArrayImpl<T2>& vec, BfAstAllocator* alloc)
{
	sizedArray.mSize = (int)vec.size();
	BF_ASSERT(sizedArray.mSize >= 0);
	if (sizedArray.mSize > 0)
	{
		sizedArray.mVals = (T*)alloc->AllocBytes(sizedArray.mSize * sizeof(T), sizeof(T));
		for (int i = 0; i < sizedArray.mSize; i++)
			sizedArray.mVals[i] = vec[i];
	}
}

template <typename T>
class BfDeferredAstSizedArray : public SizedArray<T, 8>
{
public:
	BfSizedArray<ASTREF(T)>* mSizedArray;
	BfAstAllocator* mAlloc;

public:
	BfDeferredAstSizedArray(BfSizedArray<ASTREF(T)>& arr, BfAstAllocator* alloc)
	{
		mSizedArray = &arr;
		mAlloc = alloc;
	}

	~BfDeferredAstSizedArray()
	{
		BfSizedArrayInitIndirect(*mSizedArray, *this, mAlloc);
	}
};

typedef void(*BfAstAcceptFunc)(BfAstNode* node, BfStructuralVisitor* visitor);

class BfAstTypeInfo
{
public:
	const char* mName;
	BfAstTypeInfo* mBaseType;
	Array<BfAstTypeInfo*> mDerivedTypes;
	uint8 mTypeId;
	uint8 mFullDerivedCount; // Including all ancestors
	BfAstAcceptFunc mAcceptFunc;

	BfAstTypeInfo(const char* name, BfAstTypeInfo* baseType, BfAstAcceptFunc acceptFunc);

public:
	static void Init();
};

#ifdef BF_AST_COMPACT
struct BfAstInfo
{
	int mTriviaStart;
	int mSrcStart;
	int mSrcEnd;
	uint8 mTypeId;
	BfToken mToken;
};
#endif

class BfAstNode
{
public:
	static BfAstTypeInfo sTypeInfo;

#ifndef BF_AST_ALLOCATOR_USE_PAGES
	BfSourceData* mSourceData;
#endif
#ifdef BF_AST_HAS_PARENT_MEMBER
	BfAstNode* mParent;
#endif

#ifdef BF_AST_COMPACT
	union
	{
		struct
		{
			uint8 mCompact_TriviaLen;
			uint8 mCompact_SrcLen;
			uint8 mCompact_TypeId;
			BfToken mCompact_Token;
			int mCompact_SrcStart : 31;
			int mIsCompact : 1;
		};
		BfAstInfo* mAstInfo;
	};
#else
	int mTriviaStart;
	int mSrcStart;
	int mSrcEnd;
	uint8 mTypeId;
	BfToken mToken;
#endif

public:
	BfAstNode()
	{
#ifdef BF_AST_COMPACT
		// Nothing
		mIsCompact = true;
#else
		//mParent = NULL;
		mTriviaStart = -1;
		mSrcStart = 0x7FFFFFFF;
		mSrcEnd = 0;
		//mSrcEnd = 0;
#endif
	}

#ifdef BF_AST_VTABLE
	virtual ~BfAstNode()
	{
	}
#endif

	void RemoveSelf();
	void DeleteSelf();
	void RemoveNextSibling();
	void DeleteNextSibling();
	bool IsTemporary();
	int GetStartCharId();
	BfSourceData* GetSourceData();
	BfParserData* GetParserData();
	BfParser* GetParser();
	bool IsEmitted();
	bool IsFromParser(BfParser* parser);
	String ToString();
	StringView ToStringView();
	void ToString(StringImpl& str);
	bool Equals(const StringImpl& str);
	bool Equals(const StringView& str);
	bool Equals(const char* str);
	void Init(BfParser* bfParser);
	void Accept(BfStructuralVisitor* bfVisitor);
	static void ClassAccept(BfAstNode* node, BfStructuralVisitor* bfVisitor) { bfVisitor->Visit(node); }
	bool LocationEquals(BfAstNode* otherNode);
	bool LocationEndEquals(BfAstNode* otherNode);
	String LocationToString();
	void Add(BfAstNode* bfAstNode);
	bool IsMissingSemicolon();
	bool IsExpression();
	bool WantsWarning(int warningNumber);

	template <typename T>
	bool IsA()
	{
		return (uint)GetTypeId() - (uint)T::sTypeInfo.mTypeId <= (uint)T::sTypeInfo.mFullDerivedCount;
	}

	template <typename T>
	bool IsExact()
	{
		return (uint)GetTypeId() == (uint)T::sTypeInfo.mTypeId;
	}

#ifdef BF_AST_COMPACT
	BfAstInfo* AllocAstInfo();

	void InitEmpty()
	{
		mIsCompact = true;
		mCompact_SrcStart = 0;
		mCompact_SrcLen = 0;
		mCompact_TriviaLen = 0;
	}

	void InitWithTypeId(int typeId)
	{
		mCompact_TypeId = typeId;
	}

	bool IsInitialized()
	{
		return (!mIsCompact) || (mCompact_SrcLen != 0);
	}

	BfToken GetToken()
	{
		if (mIsCompact)
			return mCompact_Token;
		return mAstInfo->mToken;
	}

	void SetToken(BfToken token)
	{
		if (mIsCompact)
			mCompact_Token = token;
		else
			mAstInfo->mToken = token;
	}

	void Init(int triviaStart, int srcStart, int srcEnd)
	{
		int triviaLen = srcStart - triviaStart;
		int srcLen = srcEnd - srcStart;
		if ((triviaLen <= 255) && (srcLen <= 255))
		{
			mCompact_SrcStart = srcStart;
			mIsCompact = 1;
			mCompact_TriviaLen = (uint8)triviaLen;
			mCompact_SrcLen = (uint8)srcLen;
		}
		else
		{
			auto astInfo = AllocAstInfo();
			astInfo->mTypeId = mCompact_TypeId;
			astInfo->mToken = mCompact_Token;
			astInfo->mTriviaStart = triviaStart;
			astInfo->mSrcStart = srcStart;
			astInfo->mSrcEnd = srcEnd;
			mAstInfo = astInfo;
		}
	}

	int GetTypeId()
	{
		if (mIsCompact)
			return mCompact_TypeId;
		return mAstInfo->mTypeId;
	}

	void GetSrcPositions(int& triviaStart, int& srcStart, int& srcEnd)
	{
		if (mIsCompact)
		{
			srcStart = mCompact_SrcStart;
			srcEnd = srcStart + mCompact_SrcLen;
			triviaStart = srcStart - mCompact_TriviaLen;
		}
		else
		{
			triviaStart = mAstInfo->mTriviaStart;
			srcStart = mAstInfo->mSrcStart;
			srcEnd = mAstInfo->mSrcEnd;
		}
	}

	int GetTriviaStart()
	{
		if (mIsCompact)
			return mCompact_SrcStart - mCompact_TriviaLen;
		return mAstInfo->mTriviaStart;
	}

	void SetTriviaStart(int triviaStart)
	{
		if (mIsCompact)
		{
			int triviaLen = mCompact_SrcStart - triviaStart;
			if (triviaLen <= 255)
			{
				mCompact_TriviaLen = (uint8)triviaLen;
				return;
			}

			auto astInfo = AllocAstInfo();
			astInfo->mTypeId = mCompact_TypeId;
			astInfo->mToken = mCompact_Token;
			astInfo->mSrcStart = mCompact_SrcStart;
			astInfo->mSrcEnd = mCompact_SrcStart + mCompact_SrcLen;
			mAstInfo = astInfo;
		}

		mAstInfo->mTriviaStart = triviaStart;
	}

	int GetSrcStart()
	{
		if (mIsCompact)
			return mCompact_SrcStart;
		return mAstInfo->mSrcStart;
	}

	void SetSrcStart(int srcStart)
	{
		if (mIsCompact)
		{
			int startAdjust = srcStart - mCompact_SrcStart;
			uint32 triviaLen = (uint32)((int)mCompact_TriviaLen + startAdjust);
			uint32 srcLen = (uint32)((int)mCompact_SrcLen - startAdjust);

			if ((triviaLen <= 255) && (srcLen <= 255))
			{
				mCompact_SrcStart = srcStart;
				mCompact_TriviaLen = (uint8)triviaLen;
				mCompact_SrcLen = (uint8)srcLen;
			}
			else
			{
				auto astInfo = AllocAstInfo();
				astInfo->mTypeId = mCompact_TypeId;
				astInfo->mSrcStart = srcStart;
				astInfo->mTriviaStart = srcStart - triviaLen;
				astInfo->mSrcEnd = srcStart + srcLen;
				mAstInfo = astInfo;
			}
		}
		else
			mAstInfo->mSrcStart = srcStart;
	}

	int GetSrcEnd()
	{
		if (mIsCompact)
			return mCompact_SrcStart + mCompact_SrcLen;
		return mAstInfo->mSrcEnd;
	}

	void SetSrcEnd(int srcEnd)
	{
		if (mIsCompact)
		{
			int srcLen = srcEnd - mCompact_SrcStart;
			if (srcLen <= 255)
			{
				mCompact_SrcLen = (uint8)srcLen;
				return;
			}

			auto astInfo = AllocAstInfo();
			astInfo->mTypeId = mCompact_TypeId;
			astInfo->mSrcStart = mCompact_SrcStart;
			astInfo->mTriviaStart = mCompact_SrcStart - mCompact_TriviaLen;
			mAstInfo = astInfo;
		}

		mAstInfo->mSrcEnd = srcEnd;
	}

	void AdjustSrcEnd(BfAstNode* srcNode)
	{
		int srcEnd = srcNode->GetSrcEnd();
		if (srcEnd > GetSrcEnd())
			SetSrcEnd(srcEnd);
	}

	int GetSrcLength()
	{
		if (mIsCompact)
			return mCompact_SrcLen;
		return mAstInfo->mSrcEnd - mAstInfo->mSrcStart;
	}

	bool Contains(int srcPos)
	{
		if (mIsCompact)
			return (srcPos >= mCompact_SrcStart) && (srcPos < mCompact_SrcStart + mCompact_SrcLen);
		return (srcPos >= mAstInfo->mSrcStart) && (srcPos < mAstInfo->mSrcEnd);
	}
#else
	void InitEmpty()
	{
		mTriviaStart = 0;
		mSrcStart = 0;
		mSrcEnd = 0;
	}

	void InitWithTypeId(int typeId)
	{
		mTypeId = typeId;
	}

	bool IsInitialized()
	{
		return mSrcStart != 0x7FFFFFFF;
	}

	void Init(int triviaStart, int srcStart, int srcEnd)
	{
		mTriviaStart = triviaStart;
		mSrcStart = srcStart;
		mSrcEnd = srcEnd;
	}

	BfToken GetToken()
	{
		return mToken;
	}

	void SetToken(BfToken token)
	{
		mToken = token;
	}

	int GetTypeId()
	{
		return mTypeId;
	}

	void GetSrcPositions(int& triviaStart, int& srcStart, int& srcEnd)
	{
		triviaStart = mTriviaStart;
		srcStart = mSrcStart;
		srcEnd = mSrcEnd;
	}

	int GetTriviaStart()
	{
		return mTriviaStart;
	}

	void SetTriviaStart(int triviaStart)
	{
		mTriviaStart = triviaStart;
	}

	int GetSrcStart()
	{
		return mSrcStart;
	}

	void SetSrcStart(int srcStart)
	{
		mSrcStart = srcStart;
	}

	int GetSrcEnd()
	{
		return mSrcEnd;
	}

	void SetSrcEnd(int srcEnd)
	{
		mSrcEnd = srcEnd;
	}

	void AdjustSrcEnd(BfAstNode* srcNode)
	{
		int srcEnd = srcNode->GetSrcEnd();
		if (srcEnd > GetSrcEnd())
			SetSrcEnd(srcEnd);
	}

	int GetSrcLength()
	{
		return mSrcEnd - mSrcStart;
	}

	bool Contains(int srcPos)
	{
		return (srcPos >= mSrcStart) && (srcPos < mSrcEnd);
	}

	bool Contains(int srcPos, int lenAdd, int startAdd)
	{
		return (srcPos >= mSrcStart + startAdd) && (srcPos < mSrcEnd + lenAdd);
	}

	bool Contains(BfAstNode* node)
	{
		return (node->mSrcStart >= mSrcStart) && (node->mSrcEnd <= mSrcEnd);
	}
#endif

#ifdef BF_AST_HAS_PARENT_MEMBER
	template <typename T>
	T* FindParentOfType()
	{
		BfAstNode* checkParent = mParent;
		while (checkParent != NULL)
		{
			if (checkParent->IsA<T>())
				return (T*)checkParent;
			checkParent = checkParent->mParent;
		}
		return NULL;
	}
#endif

#ifdef BF_AST_HAS_PARENT_MEMBER
	template <typename T>
	static T* ZeroedAlloc()
	{
		T* val = new T();
		memset((uint8*)val + offsetof(T, mParent), 0, sizeof(T) - offsetof(T, mParent));
		return val;
	}
#else
	template <typename T>
	static T* ZeroedAlloc()
	{
		T* val = new T();
#ifdef BF_AST_COMPACT
		memset((uint8*)val + offsetof(T, mAstInfo), 0, sizeof(T) - offsetof(T, mAstInfo));
#else
		memset((uint8*)val + offsetof(T, mTriviaStart), 0, sizeof(T) - offsetof(T, mTriviaStart));
#endif
		val->InitWithTypeId(T::sTypeInfo.mTypeId);
		return val;
	}

	template <typename T>
	static void Zero(T* val)
	{
#ifdef BF_AST_COMPACT
		memset((uint8*)val + offsetof(T, mAstInfo), 0, sizeof(T) - offsetof(T, mAstInfo));
#else
		memset((uint8*)val + offsetof(T, mTriviaStart), 0, sizeof(T) - offsetof(T, mTriviaStart));
#endif
		val->InitWithTypeId(T::sTypeInfo.mTypeId);
	}
#endif
};
#ifdef BF_AST_DO_IMPL
BfAstTypeInfo BfAstNode::sTypeInfo("BfAstNode", NULL, &BfAstNode::ClassAccept);
#endif

template <typename T>
bool BfNodeIsA(BfAstNode* node)
{
	if (node == NULL)
		return false;

	bool canCast = (uint)node->GetTypeId() - (uint)T::sTypeInfo.mTypeId <= (uint)T::sTypeInfo.mFullDerivedCount;
	return canCast;
}

template <typename T>
bool BfNodeIsExact(BfAstNode* node)
{
	if (node == NULL)
		return false;

	bool canCast = (uint)node->GetTypeId() == (uint)T::sTypeInfo.mTypeId;
	return canCast;
}

template <typename T>
T* BfNodeDynCast(BfAstNode* node)
{
	if (node == NULL)
		return NULL;

	bool canCast = (uint)node->GetTypeId() - (uint)T::sTypeInfo.mTypeId <= (uint)T::sTypeInfo.mFullDerivedCount;
	//BF_ASSERT(canCast == (node->DynCast(T::TypeId) != NULL));
	return canCast ? (T*)node : NULL;
}

template <typename T>
T* BfNodeDynCastExact(BfAstNode* node)
{
	if (node == NULL)
		return NULL;

	bool canCast = node->GetTypeId() == T::sTypeInfo.mTypeId;
	//BF_ASSERT(canCast == (node->GetTypeId() == T::TypeId));
	return canCast ? (T*)node : NULL;
}

struct BfExteriorNode
{
	BfSizedArray<BfNamespaceDeclaration*> mNamespaceNodes;
	BfAstNode* mNode;
};

BfIdentifierNode* BfIdentifierCast(BfAstNode* node);
BfAstNode* BfNodeToNonTemporary(BfAstNode* node);

template <typename T>
class BfDeferredAstNodeSizedArray : public SizedArray<T, 8>
{
public:
	BfAstNode* mParentNode;
	BfSizedArray<ASTREF(T)>* mSizedArray;
	BfAstAllocator* mAlloc;

public:
	BfDeferredAstNodeSizedArray(BfAstNode* parentNode, BfSizedArray<ASTREF(T)>& arr, BfAstAllocator* alloc)
	{
		mParentNode = parentNode;
		mSizedArray = &arr;
		mAlloc = alloc;
	}

	~BfDeferredAstNodeSizedArray()
	{
		BfSizedArrayInitIndirect(*mSizedArray, *this, mAlloc);
		if (!this->mSizedArray->IsEmpty())
		{
			int endPos = this->mSizedArray->back()->mSrcEnd;
			if (endPos > this->mParentNode->mSrcEnd)
				this->mParentNode->mSrcEnd = endPos;
		}
	}
};

class BfStatement : public BfAstNode
{
public:
	BF_AST_TYPE(BfStatement, BfAstNode);

	BfTokenNode* mTrailingSemicolon;

//	bool IsMissingSemicolon();
// 	{
// 		return mTrailingSemicolon == false;
// 	}
};	BF_AST_DECL(BfStatement, BfAstNode);

class BfExpression : public BfAstNode
{
public:
	BF_AST_TYPE(BfExpression, BfAstNode);

// 	bool IsUsedAsStatement()
// 	{
// 		return mTrailingSemicolon != NULL;
// 	}

	bool VerifyIsStatement(BfPassInstance* passInstance, bool ignoreError = false);
};	BF_AST_DECL(BfExpression, BfAstNode);

class BfErrorNode : public BfExpression
{
public:
	BF_AST_TYPE(BfErrorNode, BfExpression);

	BfAstNode* mRefNode;
};	BF_AST_DECL(BfErrorNode, BfExpression);

class BfExpressionStatement : public BfStatement
{
public:
	BF_AST_TYPE(BfExpressionStatement, BfStatement);
	BfExpression* mExpression;
};  BF_AST_DECL(BfExpressionStatement, BfStatement);

class BfBlockExtension : public BfAstNode
{
public:
	BF_AST_TYPE(BfBlockExtension, BfAstNode);

	BfSizedArray<ASTREF(BfAstNode*)> mChildArr;
};	BF_AST_DECL(BfBlockExtension, BfAstNode);

class BfBlock : public BfExpression
{
public:
	struct Iterator
	{
	public:
		ASTREF(BfAstNode*)* mPtr;
		int mValsLeft;

	public:
		Iterator()
		{
			mPtr = NULL;
			mValsLeft = 0;
		}

		Iterator(ASTREF(BfAstNode*)* ptr, int valsLeft)
		{
			mPtr = ptr;
			mValsLeft = valsLeft;
			if (mValsLeft == 0)
				mPtr = NULL;
		}

		Iterator& operator++()
		{
			BF_ASSERT(mValsLeft >= 0);
			mValsLeft--;
			mPtr++;
			if (mValsLeft == 0)
			{
				mPtr = NULL;
			}
			else
			{
				BfAstNode* curNode = *mPtr;
				if (auto blockExpr = BfNodeDynCastExact<BfBlockExtension>(*mPtr))
				{
					BF_ASSERT(mValsLeft == 1);
					mPtr = blockExpr->mChildArr.mVals;
					mValsLeft = blockExpr->mChildArr.mSize;
				}
			}
			return *this;
		}

		bool operator!=(const Iterator& itr) const
		{
			return itr.mPtr != mPtr;
		}

		bool operator==(const Iterator& itr) const
		{
			return itr.mPtr == mPtr;
		}

		ASTREF(BfAstNode*)& operator*()
		{
			return *mPtr;
		}

		BfAstNode* Get()
		{
			if (mValsLeft == 0)
				return NULL;
			return *mPtr;
		}

		bool IsLast()
		{
			return mValsLeft == 1;
		}
	};

public:
	BF_AST_TYPE(BfBlock, BfExpression);

	ASTREF(BfTokenNode*) mOpenBrace;
	ASTREF(BfTokenNode*) mCloseBrace;
	//BfDebugArray<BfAstNode*> mChildArr;
	BfSizedArray<ASTREF(BfAstNode*)> mChildArr;
	int mParserBlockId;

public:
	using BfAstNode::Init;
	void Init(const SizedArrayImpl<BfAstNode*>& vec, BfAstAllocator* alloc);
	BfAstNode* GetFirst();
	BfAstNode* GetLast();
	int GetSize();
	void SetSize(int wantSize);
// 	virtual bool IsMissingSemicolon() override
// 	{
// 		return false;
// 	}

	ASTREF(BfAstNode*)& operator[](int idx)
	{
#ifdef BF_USE_NEAR_NODE_REF
		BfSizedArray<ASTREF(BfAstNode*)>* childArr = &mChildArr;

		while (true)
		{
			if (idx < childArr->mSize - 1)
				return childArr->mVals[idx];
			if (idx == childArr->mSize - 1)
			{
				auto& checkNode = childArr->mVals[childArr->mSize - 1];
				if (!checkNode->IsA<BfBlockExtension>())
					return checkNode;
			}

			idx -= childArr->mSize - 1;
			BfBlockExtension* blockExt = (BfBlockExtension*)(BfAstNode*)childArr->mVals[childArr->mSize - 1];
			BF_ASSERT(blockExt->GetTypeId() == BfBlockExtension::TypeId);
			childArr = &blockExt->mChildArr;
		}
#else
		return mChildArr.mVals[idx];
#endif
	}

	Iterator begin()
	{
		return Iterator(mChildArr.mVals, mChildArr.mSize);
	}

	Iterator end()
	{
		return Iterator(NULL, 0);
	}
};	BF_AST_DECL(BfBlock, BfExpression);

class BfUnscopedBlock : public BfBlock
{
public:
	BF_AST_TYPE(BfUnscopedBlock, BfBlock);
};  BF_AST_DECL(BfUnscopedBlock, BfBlock);

class BfTypedValueExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfTypedValueExpression, BfExpression);

	BfTypedValue mTypedValue;
	BfAstNode* mRefNode;

public:
	void Init(const BfTypedValue& typedValue)
	{
		mTypedValue = typedValue;
		mRefNode = NULL;
#ifdef BF_AST_HAS_PARENT_MEMBER
		mParent = NULL;
#endif
	}
};	BF_AST_DECL(BfTypedValueExpression, BfExpression);

// Compound statements don't require semicolon termination
class BfCompoundStatement : public BfStatement
{
public:
	BF_AST_TYPE(BfCompoundStatement, BfStatement);
};	BF_AST_DECL(BfCompoundStatement, BfStatement);

class BfLabelNode : public BfAstNode
{
public:
	BF_AST_TYPE(BfLabelNode, BfAstNode);

	BfIdentifierNode* mLabel;
	BfTokenNode* mColonToken;
};	BF_AST_DECL(BfLabelNode, BfAstNode);

class BfLabelableStatement : public BfCompoundStatement
{
public:
	BF_AST_TYPE(BfLabelableStatement, BfCompoundStatement);

	BfLabelNode* mLabelNode;
};	BF_AST_DECL(BfLabelableStatement, BfCompoundStatement);

class BfLabeledBlock : public BfLabelableStatement
{
public:
	BF_AST_TYPE(BfLabeledBlock, BfLabelableStatement);

	BfBlock* mBlock;
};	BF_AST_DECL(BfLabeledBlock, BfLabelableStatement);

enum BfBinaryOp
{
	BfBinaryOp_None,
	BfBinaryOp_Add,
	BfBinaryOp_Subtract,
	BfBinaryOp_Multiply,
	BfBinaryOp_OverflowAdd,
	BfBinaryOp_OverflowSubtract,
	BfBinaryOp_OverflowMultiply,
	BfBinaryOp_Divide,
	BfBinaryOp_Modulus,
	BfBinaryOp_BitwiseAnd,
	BfBinaryOp_BitwiseOr,
	BfBinaryOp_ExclusiveOr,
	BfBinaryOp_LeftShift,
	BfBinaryOp_RightShift,
	BfBinaryOp_Equality,
	BfBinaryOp_StrictEquality,
	BfBinaryOp_InEquality,
	BfBinaryOp_StrictInEquality,
	BfBinaryOp_GreaterThan,
	BfBinaryOp_LessThan,
	BfBinaryOp_GreaterThanOrEqual,
	BfBinaryOp_LessThanOrEqual,
	BfBinaryOp_Compare,
	BfBinaryOp_ConditionalAnd,
	BfBinaryOp_ConditionalOr,
	BfBinaryOp_NullCoalesce,
	BfBinaryOp_Is,
	BfBinaryOp_As,
	BfBinaryOp_Range,
	BfBinaryOp_ClosedRange,
};

enum BfAssignmentOp
{
	BfAssignmentOp_None,
	BfAssignmentOp_Assign,
	BfAssignmentOp_Add,
	BfAssignmentOp_Subtract,
	BfAssignmentOp_Multiply,
	BfAssignmentOp_OverflowAdd,
	BfAssignmentOp_OverflowSubtract,
	BfAssignmentOp_OverflowMultiply,
	BfAssignmentOp_Divide,
	BfAssignmentOp_Modulus,
	BfAssignmentOp_ShiftLeft,
	BfAssignmentOp_ShiftRight,
	BfAssignmentOp_BitwiseAnd,
	BfAssignmentOp_BitwiseOr,
	BfAssignmentOp_ExclusiveOr,
	BfAssignmentOp_NullCoalesce
};

enum BfUnaryOp
{
	BfUnaryOp_None,
	BfUnaryOp_AddressOf,
	BfUnaryOp_Arrow,
	BfUnaryOp_Dereference,
	BfUnaryOp_Negate,
	BfUnaryOp_Not,
	BfUnaryOp_Positive,
	BfUnaryOp_InvertBits,
	BfUnaryOp_Increment,
	BfUnaryOp_Decrement,
	BfUnaryOp_PostIncrement,
	BfUnaryOp_PostDecrement,
	BfUnaryOp_NullConditional,
	BfUnaryOp_Ref,
	BfUnaryOp_Out,
	BfUnaryOp_Mut,
	BfUnaryOp_Params,
	BfUnaryOp_Cascade,
	BfUnaryOp_FromEnd,
	BfUnaryOp_PartialRangeUpTo,
	BfUnaryOp_PartialRangeThrough,
	BfUnaryOp_PartialRangeFrom,
};

class BfTokenNode : public BfAstNode
{
public:
	BF_AST_TYPE(BfTokenNode, BfAstNode);
};	BF_AST_DECL(BfTokenNode, BfAstNode);

class BfScopeNode : public BfAstNode
{
public:
	BF_AST_TYPE(BfScopeNode, BfAstNode);

	BfTokenNode* mScopeToken;
	BfTokenNode* mColonToken;
	BfAstNode* mTargetNode; // . : or identifier
	BfAttributeDirective* mAttributes;
};	BF_AST_DECL(BfScopeNode, BfAstNode);

class BfNewNode : public BfAstNode
{
public:
	BF_AST_TYPE(BfNewNode, BfAstNode);

	BfTokenNode* mNewToken;
	BfTokenNode* mColonToken;
	BfAstNode* mAllocNode; // Expression or BfScopedInvocationTarget
	BfAttributeDirective* mAttributes;
};	BF_AST_DECL(BfNewNode, BfAstNode);

enum BfCommentKind
{
	BfCommentKind_Line,
	BfCommentKind_Block,
	BfCommentKind_Documentation_Block_Pre,
	BfCommentKind_Documentation_Line_Pre,
	BfCommentKind_Documentation_Block_Post,
	BfCommentKind_Documentation_Line_Post,
};

class BfCommentNode : public BfAstNode
{
public:
	BF_AST_TYPE(BfCommentNode, BfAstNode);
	BfCommentKind mCommentKind;

	void GetDocString(StringImpl& docString)
	{
		ToString(docString);
		for (int i = 0; i < (int)docString.length(); i++)
		{
			char c = docString[i];
			if (c == '\n')
				docString[i] = '\x3';
		}
	}
};	BF_AST_DECL(BfCommentNode, BfAstNode);

class BfPreprocesorIgnoredSectionNode : public BfAstNode
{
public:
	BF_AST_TYPE(BfPreprocesorIgnoredSectionNode, BfAstNode);
};	BF_AST_DECL(BfPreprocesorIgnoredSectionNode, BfAstNode);

class BfPreprocessorNode : public BfAstNode
{
public:
	BF_AST_TYPE(BfPreprocessorNode, BfAstNode);

	BfIdentifierNode* mCommand;
	BfBlock* mArgument;
};	BF_AST_DECL(BfPreprocessorNode, BfAstNode);

class BfPreprocessorDefinedExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfPreprocessorDefinedExpression, BfExpression);

	BfIdentifierNode*     mIdentifier;
};	BF_AST_DECL(BfPreprocessorDefinedExpression, BfExpression);

class BfReplaceNode : public BfAstNode
{
public:
	BF_AST_TYPE(BfReplaceNode, BfAstNode);
};	BF_AST_DECL(BfReplaceNode, BfAstNode);

//TODO: Should we have a separate BfIdentifierExpression?
class BfIdentifierNode : public BfExpression
{
public:
	BF_AST_TYPE(BfIdentifierNode, BfExpression);
};	BF_AST_DECL(BfIdentifierNode, BfExpression);

class BfAttributedIdentifierNode : public BfExpression
{
public:
	BF_AST_TYPE(BfAttributedIdentifierNode, BfExpression);

	BfIdentifierNode* mIdentifier;
	BfAttributeDirective* mAttributes;
};	BF_AST_DECL(BfAttributedIdentifierNode, BfExpression);

class BfQualifiedNameNode : public BfIdentifierNode
{
public:
	BF_AST_TYPE(BfQualifiedNameNode, BfIdentifierNode);

	ASTREF(BfIdentifierNode*) mLeft;
	ASTREF(BfTokenNode*) mDot;
	ASTREF(BfIdentifierNode*) mRight;
};	BF_AST_DECL(BfQualifiedNameNode, BfIdentifierNode);

class BfUsingDirective : public BfStatement
{
public:
	BF_AST_TYPE(BfUsingDirective, BfStatement);

	BfTokenNode* mUsingToken;
	BfIdentifierNode* mNamespace;
};	BF_AST_DECL(BfUsingDirective, BfStatement);

class BfUsingModDirective : public BfStatement
{
public:
	BF_AST_TYPE(BfUsingModDirective, BfStatement);

	BfTokenNode* mUsingToken;
	BfTokenNode* mModToken;
	BfTypeReference* mTypeRef;
};	BF_AST_DECL(BfUsingModDirective, BfStatement);

class BfAttributeTargetSpecifier : public BfAstNode
{
public:
	BF_AST_TYPE(BfAttributeTargetSpecifier, BfAstNode);

	ASTREF(BfAstNode*) mTargetToken;
	ASTREF(BfTokenNode*) mColonToken;
};	BF_AST_DECL(BfAttributeTargetSpecifier, BfAstNode);

class BfAttributeDirective : public BfAstNode
{
public:
	BF_AST_TYPE(BfAttributeDirective, BfAstNode);

	ASTREF(BfTokenNode*) mAttrOpenToken; // [ @ ,
	ASTREF(BfTokenNode*) mAttrCloseToken;
	ASTREF(BfAstNode*) mAttributeTargetSpecifier;

	ASTREF(BfTypeReference*) mAttributeTypeRef;
	ASTREF(BfTokenNode*) mCtorOpenParen;
	ASTREF(BfTokenNode*) mCtorCloseParen;
	BfSizedArray<ASTREF(BfExpression*)> mArguments;
	BfSizedArray<ASTREF(BfTokenNode*)> mCommas;
	bool mIsMultiUse; // For anonymous types and also another use like a field decl

	ASTREF(BfAttributeDirective*) mNextAttribute;

public:
	bool Contains(const StringImpl& findName);
};	BF_AST_DECL(BfAttributeDirective, BfAstNode);

class BfNamespaceDeclaration : public BfAstNode
{
public:
	BF_AST_TYPE(BfNamespaceDeclaration, BfAstNode);

	BfTokenNode* mNamespaceNode;
	BfIdentifierNode* mNameNode;
	BfAstNode* mBody;
};	BF_AST_DECL(BfNamespaceDeclaration, BfAstNode);

class BfBinaryOperatorExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfBinaryOperatorExpression, BfExpression);

	BfBinaryOp mOp;
	ASTREF(BfTokenNode*) mOpToken;
	ASTREF(BfExpression*) mLeft;
	ASTREF(BfExpression*) mRight;
};	BF_AST_DECL(BfBinaryOperatorExpression, BfExpression);

class BfConditionalExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfConditionalExpression, BfExpression);

	BfExpression* mConditionExpression;
	BfTokenNode* mQuestionToken;
	BfExpression* mTrueExpression;
	BfTokenNode* mColonToken;
	BfExpression* mFalseExpression;
};	BF_AST_DECL(BfConditionalExpression, BfExpression);

class BfAssignmentExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfAssignmentExpression, BfExpression);

	BfAssignmentOp mOp;
	ASTREF(BfTokenNode*) mOpToken;
	ASTREF(BfExpression*) mLeft;
	ASTREF(BfExpression*) mRight;
};	BF_AST_DECL(BfAssignmentExpression, BfExpression);

class BfMethodBoundExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfMethodBoundExpression, BfExpression);
};	BF_AST_DECL(BfMethodBoundExpression, BfExpression);

class BfIndexerExpression : public BfMethodBoundExpression
{
public:
	BF_AST_TYPE(BfIndexerExpression, BfMethodBoundExpression);

	BfExpression* mTarget;
	BfTokenNode* mOpenBracket;
	BfTokenNode* mCloseBracket;
	BfSizedArray<ASTREF(BfExpression*)> mArguments;
	BfSizedArray<ASTREF(BfTokenNode*)> mCommas;
};	BF_AST_DECL(BfIndexerExpression, BfMethodBoundExpression);

class BfMemberReferenceExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfMemberReferenceExpression, BfExpression);

	ASTREF(BfTokenNode*) mDotToken;
	ASTREF(BfAstNode*) mTarget; // Can be expression or typeRef
	ASTREF(BfAstNode*) mMemberName; // Either or BfIdentiferNode or a BfLiteralNode (for tuple "name.0" type lookups)
};	BF_AST_DECL(BfMemberReferenceExpression, BfExpression);

class BfUnaryOperatorExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfUnaryOperatorExpression, BfExpression);

	BfUnaryOp mOp;
	ASTREF(BfTokenNode*) mOpToken;
	ASTREF(BfExpression*) mExpression;
};	BF_AST_DECL(BfUnaryOperatorExpression, BfExpression);

class BfMixinExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfMixinExpression, BfExpression);
};	BF_AST_DECL(BfMixinExpression, BfExpression);

class BfThisExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfThisExpression, BfExpression);
};	BF_AST_DECL(BfThisExpression, BfExpression);

class BfBaseExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfBaseExpression, BfExpression);
};	BF_AST_DECL(BfBaseExpression, BfExpression);

class BfLiteralExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfLiteralExpression, BfExpression);

	BfVariant mValue;
};	BF_AST_DECL(BfLiteralExpression, BfExpression);

class BfStringInterpolationExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfStringInterpolationExpression, BfExpression);

	BfAstNode* mAllocNode;
	String* mString;
	BfSizedArray<ASTREF(BfBlock*)> mExpressions;
};  BF_AST_DECL(BfStringInterpolationExpression, BfExpression);

class BfInitializerExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfInitializerExpression, BfExpression);

	BfAstNode* mTarget;
	BfTokenNode* mOpenBrace;
	BfInlineTypeReference* mInlineTypeRef;
	BfSizedArray<BfExpression*> mValues;
	BfSizedArray<BfTokenNode*> mCommas;
	BfTokenNode* mCloseBrace;
};  BF_AST_DECL(BfInitializerExpression, BfExpression);

class BfCollectionInitializerExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfCollectionInitializerExpression, BfExpression);

	BfTokenNode* mOpenBrace;
	BfSizedArray<ASTREF(BfExpression*)> mValues;
	BfSizedArray<ASTREF(BfTokenNode*)> mCommas;
	BfTokenNode* mCloseBrace;
};	BF_AST_DECL(BfCollectionInitializerExpression, BfExpression);

class BfSizedArrayCreateExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfSizedArrayCreateExpression, BfExpression);

	BfArrayTypeRef* mTypeRef;
	BfCollectionInitializerExpression* mInitializer;
};	BF_AST_DECL(BfSizedArrayCreateExpression, BfExpression);

class BfParenthesizedExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfParenthesizedExpression, BfExpression);

	BfTokenNode* mOpenParen;
	BfExpression* mExpression;
	BfTokenNode* mCloseParen;
};	BF_AST_DECL(BfParenthesizedExpression, BfExpression);

class BfTupleNameNode : public BfAstNode
{
public:
	BF_AST_TYPE(BfTupleNameNode, BfAstNode);

	BfIdentifierNode* mNameNode;
	BfTokenNode* mColonToken;
};	BF_AST_DECL(BfTupleNameNode, BfAstNode);

class BfTupleExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfTupleExpression, BfExpression);

	BfTokenNode* mOpenParen;
	BfSizedArray<ASTREF(BfTupleNameNode*)> mNames;
	BfSizedArray<ASTREF(BfExpression*)> mValues;
	BfSizedArray<ASTREF(BfTokenNode*)> mCommas;
	ASTREF(BfTokenNode*) mCloseParen;
};	BF_AST_DECL(BfTupleExpression, BfExpression);

class BfWhenExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfWhenExpression, BfExpression);

	BfTokenNode* mWhenToken;
	BfExpression* mExpression;
};	BF_AST_DECL(BfWhenExpression, BfExpression);

class BfEnumCaseBindExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfEnumCaseBindExpression, BfExpression);

	BfTokenNode* mBindToken; // Either 'var' or 'let'
	BfAstNode* mEnumMemberExpr; // Either a BfMemberReferenceExpression or a BfIdentifierNode
	BfTupleExpression* mBindNames;
};	BF_AST_DECL(BfEnumCaseBindExpression, BfExpression);

class BfCaseExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfCaseExpression, BfExpression);

	BfAstNode* mNotToken;
	BfTokenNode* mCaseToken;
	BfExpression* mCaseExpression;
	BfTokenNode* mEqualsNode;
	BfExpression* mValueExpression;
};	BF_AST_DECL(BfCaseExpression, BfExpression);

class BfSwitchCase : public BfAstNode
{
public:
	BF_AST_TYPE(BfSwitchCase, BfAstNode);

	BfTokenNode* mCaseToken;
	BfSizedArray<ASTREF(BfExpression*)> mCaseExpressions;
	BfSizedArray<ASTREF(BfTokenNode*)> mCaseCommas;
	BfTokenNode* mColonToken;
	BfBlock* mCodeBlock; // May or may not have braces set
	BfTokenNode* mEndingToken; // Null, Fallthrough, or Break
	BfTokenNode* mEndingSemicolonToken;
};	BF_AST_DECL(BfSwitchCase, BfAstNode);

class BfSwitchStatement : public BfLabelableStatement
{
public:
	BF_AST_TYPE(BfSwitchStatement, BfLabelableStatement);

	BfTokenNode* mSwitchToken;
	BfTokenNode* mOpenParen;
	BfExpression* mSwitchValue;
	BfTokenNode* mCloseParen;

	BfTokenNode* mOpenBrace;
	BfSizedArray<ASTREF(BfSwitchCase*)> mSwitchCases;
	BfSwitchCase* mDefaultCase;
	BfTokenNode* mCloseBrace;
};	BF_AST_DECL(BfSwitchStatement, BfLabelableStatement);

class BfIfStatement : public BfLabelableStatement
{
public:
	BF_AST_TYPE(BfIfStatement, BfLabelableStatement);

	BfTokenNode* mIfToken;
	BfTokenNode* mOpenParen;
	BfExpression* mCondition;
	BfTokenNode* mCloseParen;
	BfAstNode* mTrueStatement;
	BfTokenNode* mElseToken;
	BfAstNode* mFalseStatement;
};	BF_AST_DECL(BfIfStatement, BfLabelableStatement);

class BfEmptyStatement : public BfStatement
{
public:
	BF_AST_TYPE(BfEmptyStatement, BfStatement);
};	BF_AST_DECL(BfEmptyStatement, BfStatement);

class BfRootNode : public BfBlock
{
public:
	BF_AST_TYPE(BfRootNode, BfBlock);
};	BF_AST_DECL(BfRootNode, BfBlock);

class BfGenericConstraintsDeclaration;

class BfTypeDeclaration : public BfAstNode
{
public:
	BF_AST_TYPE(BfTypeDeclaration, BfAstNode);

	BfCommentNode* mDocumentation;
	BfAttributeDirective* mAttributes;
	BfTokenNode* mAbstractSpecifier;
	BfTokenNode* mSealedSpecifier;
	BfAstNode* mProtectionSpecifier;
	BfTokenNode* mStaticSpecifier;
	BfTokenNode* mPartialSpecifier;
	BfTokenNode* mTypeNode;
	BfIdentifierNode* mNameNode;
	BfAstNode* mDefineNode;
	BfAutoConstructorDeclaration* mAutoCtor;
	BfGenericParamsDeclaration* mGenericParams;
	BfGenericConstraintsDeclaration* mGenericConstraintsDeclaration;
	bool mIgnoreDeclaration;
	char* mAnonymousName;

	BfTokenNode* mColonToken;
	BfSizedArray<ASTREF(BfTypeReference*)> mBaseClasses;
	BfSizedArray<ASTREF(BfAstNode*)> mBaseClassCommas;
	BfSizedArray<BfTypeDeclaration*> mAnonymousTypes;
	
	bool IsAnonymous();	
	bool IsAnonymousInitializerType();

};	BF_AST_DECL(BfTypeDeclaration, BfAstNode);

class BfInitializerTypeDeclaration : public BfTypeDeclaration
{
public:
	BF_AST_TYPE(BfInitializerTypeDeclaration, BfTypeDeclaration);
};	BF_AST_DECL(BfInitializerTypeDeclaration, BfTypeDeclaration);

class BfTypeAliasDeclaration : public BfTypeDeclaration
{
public:
	BF_AST_TYPE(BfTypeAliasDeclaration, BfTypeDeclaration);

	BfTokenNode* mEqualsToken;
	BfTypeReference* mAliasToType;
	BfTokenNode* mEndSemicolon;
};	BF_AST_DECL(BfTypeAliasDeclaration, BfTypeDeclaration);

class BfTypeReference : public BfAstNode
{
public:
	BF_AST_TYPE(BfTypeReference, BfAstNode);

	bool IsNamedTypeReference();
	bool IsTypeDefTypeReference();
	String ToCleanAttributeString();
};	BF_AST_DECL(BfTypeReference, BfAstNode);

class BfInlineTypeReference : public BfTypeReference
{
public:
	BF_AST_TYPE(BfInlineTypeReference, BfTypeReference);
	
	BfTypeDeclaration* mTypeDeclaration;
};	BF_AST_DECL(BfInlineTypeReference, BfTypeReference);

class BfDirectTypeReference : public BfTypeReference
{
public:
	BF_AST_TYPE(BfDirectTypeReference, BfAstNode);

	BfType* mType;
	void Init(BfType* type)
	{
		mType = type;
		InitEmpty();
	}
};	BF_AST_DECL(BfDirectTypeReference, BfAstNode);

class BfDirectTypeDefReference : public BfTypeReference
{
public:
	BF_AST_TYPE(BfDirectTypeDefReference, BfTypeReference);

	BfTypeDef* mTypeDef;
	void Init(BfTypeDef* type)
	{
		mTypeDef = type;
		InitEmpty();
	}
};	BF_AST_DECL(BfDirectTypeDefReference, BfTypeReference);

// class BfTypeDefTypeReference : public BfTypeReference
// {
// public:
// 	BF_AST_TYPE(BfTypeDefTypeReference, BfTypeReference);
//
// 	BfTypeDef* mTypeDef;
// };	BF_AST_DECL(BfTypeDefTypeReference, BfTypeReference);

class BfDirectStrTypeReference : public BfTypeReference
{
public:
	BF_AST_TYPE(BfDirectStrTypeReference, BfTypeReference);

	String mTypeName;

	using BfAstNode::Init;

	void Init(const StringImpl& str)
	{
		mTypeName = str;
#ifdef BF_AST_HAS_PARENT_MEMBER
		mParent = NULL;
#endif
		InitEmpty();
		//mTypeDef = NULL;
	}
};	BF_AST_DECL(BfDirectStrTypeReference, BfTypeReference);

class BfDotTypeReference : public BfTypeReference
{
public:
	BF_AST_TYPE(BfDotTypeReference, BfTypeReference);

	BfTokenNode* mDotToken;
};	BF_AST_DECL(BfDotTypeReference, BfTypeReference);

class BfVarTypeReference : public BfTypeReference
{
public:
	BF_AST_TYPE(BfVarTypeReference, BfTypeReference);

	BfTokenNode* mVarToken;
};	BF_AST_DECL(BfVarTypeReference, BfTypeReference);

class BfVarRefTypeReference : public BfTypeReference
{
public:
	BF_AST_TYPE(BfVarRefTypeReference, BfTypeReference);

	BfTokenNode* mVarToken;
	BfTokenNode* mRefToken;
};	BF_AST_DECL(BfVarRefTypeReference, BfTypeReference);

class BfLetTypeReference : public BfTypeReference
{
public:
	BF_AST_TYPE(BfLetTypeReference, BfTypeReference);

	BfTokenNode* mLetToken;
};	BF_AST_DECL(BfLetTypeReference, BfTypeReference);

class BfWildcardTypeReference : public BfTypeReference
{
public:
	BF_AST_TYPE(BfWildcardTypeReference, BfTypeReference);

	BfTokenNode* mWildcardToken;
};	BF_AST_DECL(BfWildcardTypeReference, BfTypeReference);

class BfQualifiedTypeReference : public BfTypeReference
{
public:
	BF_AST_TYPE(BfQualifiedTypeReference, BfTypeReference);

	ASTREF(BfTypeReference*) mLeft;
	ASTREF(BfTokenNode*) mDot;
	ASTREF(BfTypeReference*) mRight;
};	BF_AST_DECL(BfQualifiedTypeReference, BfTypeReference);

class BfResolvedTypeReference : public BfTypeReference
{
public:
	BF_AST_TYPE(BfResolvedTypeReference, BfTypeReference);

	BfType* mType;
};	BF_AST_DECL(BfResolvedTypeReference, BfTypeReference);

// "Named" means no wrapping (ie: not array, not generic instance, not pointer, etc
class BfNamedTypeReference : public BfTypeReference
{
public:
	BF_AST_TYPE(BfNamedTypeReference, BfTypeReference);

	ASTREF(BfIdentifierNode*) mNameNode;
};	BF_AST_DECL(BfNamedTypeReference, BfTypeReference);

class BfElementedTypeRef : public BfTypeReference
{
public:
	BF_AST_TYPE(BfElementedTypeRef, BfTypeReference);

	ASTREF(BfTypeReference*) mElementType;
};	BF_AST_DECL(BfElementedTypeRef, BfTypeReference);

class BfModifiedTypeRef : public BfElementedTypeRef
{
public:
	BF_AST_TYPE(BfModifiedTypeRef, BfElementedTypeRef);

	BfTokenNode* mRetTypeToken;
	BfTokenNode* mOpenParen;
	BfTokenNode* mCloseParen;
};	BF_AST_DECL(BfModifiedTypeRef, BfElementedTypeRef);

class BfArrayTypeRef : public BfElementedTypeRef
{
public:
	BF_AST_TYPE(BfArrayTypeRef, BfElementedTypeRef);

	int mDimensions;
	BfTokenNode* mOpenBracket;
	BfSizedArray<ASTREF(BfAstNode*)> mParams; // Either commas or constant size expression
	BfTokenNode* mCloseBracket;

	bool IsInferredSize()
	{
		if (mParams.mSize > 0)
			return BfNodeIsA<BfUninitializedExpression>(mParams[0]);
		return false;
	}
};	BF_AST_DECL(BfArrayTypeRef, BfElementedTypeRef);

class BfNullableTypeRef : public BfElementedTypeRef
{
public:
	BF_AST_TYPE(BfNullableTypeRef, BfElementedTypeRef);

	BfTokenNode* mQuestionToken;
};	BF_AST_DECL(BfNullableTypeRef, BfElementedTypeRef);

class BfGenericInstanceTypeRef : public BfElementedTypeRef
{
public:
	BF_AST_TYPE(BfGenericInstanceTypeRef, BfElementedTypeRef);

	BfTokenNode* mOpenChevron;
	BfSizedArray<BfAstNode*> mGenericArguments;
	BfSizedArray<ASTREF(BfAstNode*)> mCommas;
	BfTokenNode* mCloseChevron;
	int GetGenericArgCount()
	{
		if (!mCommas.empty())
			return (int)mCommas.size() + 1;
		return std::max(1, (int)mGenericArguments.size());
	}
};	BF_AST_DECL(BfGenericInstanceTypeRef, BfElementedTypeRef);

class BfTupleTypeRef : public BfElementedTypeRef
{
public:
	BF_AST_TYPE(BfTupleTypeRef, BfElementedTypeRef);

	ASTREF(BfTokenNode*) mOpenParen;
	BfSizedArray<ASTREF(BfTypeReference*)> mFieldTypes;
	BfSizedArray<ASTREF(BfIdentifierNode*)> mFieldNames;
	BfSizedArray<ASTREF(BfAstNode*)> mCommas;
	ASTREF(BfTokenNode*) mCloseParen;
	int GetGenericArgCount()
	{
		if (!mCommas.empty())
			return (int)mCommas.size() + 1;
		return std::max(1, (int)mFieldTypes.size());
	}
};	BF_AST_DECL(BfTupleTypeRef, BfElementedTypeRef);

class BfTagTypeRef : public BfTypeReference
{
public:
	BF_AST_TYPE(BfTagTypeRef, BfTypeReference);

	BfIdentifierNode* mTagNode;
	BfIdentifierNode* mNameNode;
};	BF_AST_DECL(BfTagTypeRef, BfTypeReference);

class BfDelegateTypeRef : public BfTypeReference
{
public:
	BF_AST_TYPE(BfDelegateTypeRef, BfTypeReference);

	BfTokenNode* mTypeToken; // Delegate or Function

	BfAttributeDirective* mAttributes;
	BfTypeReference* mReturnType;
	BfAstNode* mOpenParen;
	BfSizedArray<BfParameterDeclaration*> mParams;
	BfSizedArray<BfTokenNode*> mCommas;
	BfAstNode* mCloseParen;
};	BF_AST_DECL(BfDelegateTypeRef, BfTypeReference);

class BfExprModTypeRef : public BfTypeReference
{
public:
	BF_AST_TYPE(BfExprModTypeRef, BfTypeReference);

	BfTokenNode* mToken;
	BfTokenNode* mOpenParen;
	BfExpression* mTarget;
	BfTokenNode* mCloseParen;
};	BF_AST_DECL(BfExprModTypeRef, BfTypeReference);

enum BfGenericParamKind
{
	BfGenericParamKind_Type,
	BfGenericParamKind_Method
};

class BfGenericParamTypeRef : public BfTypeReference
{
public:
	BF_AST_TYPE(BfGenericParamTypeRef, BfTypeReference);

	BfGenericParamKind mGenericParamKind;
	int mGenericParamIdx;
};	BF_AST_DECL(BfGenericParamTypeRef, BfTypeReference);

class BfPointerTypeRef : public BfElementedTypeRef
{
public:
	BF_AST_TYPE(BfPointerTypeRef, BfElementedTypeRef);

	BfTokenNode* mStarNode;
};	BF_AST_DECL(BfPointerTypeRef, BfElementedTypeRef);

class BfConstTypeRef : public BfElementedTypeRef
{
public:
	BF_AST_TYPE(BfConstTypeRef, BfElementedTypeRef);
	BfTokenNode* mConstToken;
};	BF_AST_DECL(BfConstTypeRef, BfElementedTypeRef);

class BfConstExprTypeRef : public BfTypeReference
{
public:
	BF_AST_TYPE(BfConstExprTypeRef, BfTypeReference);
	BfTokenNode* mConstToken;
	BfExpression* mConstExpr;
};	BF_AST_DECL(BfConstExprTypeRef, BfTypeReference);

class BfUnsignedTypeRef : public BfElementedTypeRef
{
public:
	BF_AST_TYPE(BfUnsignedTypeRef, BfElementedTypeRef);
	BfTokenNode* mUnsignedToken;
};	BF_AST_DECL(BfUnsignedTypeRef, BfElementedTypeRef);

class BfRefTypeRef : public BfElementedTypeRef
{
public:
	BF_AST_TYPE(BfRefTypeRef, BfElementedTypeRef);
	BfTokenNode* mRefToken;
};	BF_AST_DECL(BfRefTypeRef, BfElementedTypeRef);

class BfTypeAttrExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfTypeAttrExpression, BfExpression);

	BfTokenNode* mToken;
	BfTokenNode* mOpenParen;
	BfTypeReference* mTypeRef;
	BfTokenNode* mCloseParen;
};	BF_AST_DECL(BfTypeAttrExpression, BfExpression);

class BfTypeOfExpression : public BfTypeAttrExpression
{
public:
	BF_AST_TYPE(BfTypeOfExpression, BfTypeAttrExpression);
};	BF_AST_DECL(BfTypeOfExpression, BfTypeAttrExpression);

class BfSizeOfExpression : public BfTypeAttrExpression
{
public:
	BF_AST_TYPE(BfSizeOfExpression, BfTypeAttrExpression);
};	BF_AST_DECL(BfSizeOfExpression, BfTypeAttrExpression);

class BfAlignOfExpression : public BfTypeAttrExpression
{
public:
	BF_AST_TYPE(BfAlignOfExpression, BfTypeAttrExpression);
};	BF_AST_DECL(BfAlignOfExpression, BfTypeAttrExpression);

class BfStrideOfExpression : public BfTypeAttrExpression
{
public:
	BF_AST_TYPE(BfStrideOfExpression, BfTypeAttrExpression);
};	BF_AST_DECL(BfStrideOfExpression, BfTypeAttrExpression);

class BfOffsetOfExpression : public BfTypeAttrExpression
{
public:
	BF_AST_TYPE(BfOffsetOfExpression, BfTypeAttrExpression);

	BfTokenNode* mCommaToken;
	BfIdentifierNode* mMemberName;
};	BF_AST_DECL(BfOffsetOfExpression, BfTypeAttrExpression);

class BfNameOfExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfNameOfExpression, BfExpression);
	BfTokenNode* mToken;
	BfTokenNode* mOpenParen;
	BfAstNode* mTarget;
	BfTokenNode* mCloseParen;
};	BF_AST_DECL(BfNameOfExpression, BfExpression);

class BfIsConstExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfIsConstExpression, BfTypeAttrExpression);

	BfTokenNode* mIsConstToken;
	BfTokenNode* mOpenParen;
	BfExpression* mExpression;
	BfTokenNode* mCloseParen;
};	BF_AST_DECL(BfIsConstExpression, BfTypeAttrExpression);

class BfDefaultExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfDefaultExpression, BfExpression);

	BfTokenNode* mDefaultToken;
	BfTokenNode* mOpenParen;
	BfTypeReference* mTypeRef;
	BfTokenNode* mCloseParen;
};	BF_AST_DECL(BfDefaultExpression, BfExpression);

class BfUninitializedExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfUninitializedExpression, BfExpression);

	BfTokenNode* mQuestionToken;
};	BF_AST_DECL(BfUninitializedExpression, BfExpression);

class BfCheckTypeExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfCheckTypeExpression, BfExpression);

	BfExpression* mTarget;
	BfTokenNode* mIsToken;
	BfTypeReference* mTypeRef;
};	BF_AST_DECL(BfCheckTypeExpression, BfExpression);

class BfDynamicCastExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfDynamicCastExpression, BfExpression);

	BfExpression* mTarget;
	BfTokenNode* mAsToken;
	BfTypeReference* mTypeRef;
};	BF_AST_DECL(BfDynamicCastExpression, BfExpression);

class BfCastExpression : public BfUnaryOperatorExpression
{
public:
	BF_AST_TYPE(BfCastExpression, BfUnaryOperatorExpression);

	BfTokenNode* mOpenParen;
	BfTypeReference* mTypeRef;
	BfTokenNode* mCloseParen;
};	BF_AST_DECL(BfCastExpression, BfUnaryOperatorExpression);

class BfDelegateBindExpression : public BfMethodBoundExpression
{
public:
	BF_AST_TYPE(BfDelegateBindExpression, BfMethodBoundExpression);

	BfAstNode* mNewToken;
	BfTokenNode* mFatArrowToken;
	BfExpression* mTarget;
	BfGenericArgumentsNode* mGenericArgs;
};	BF_AST_DECL(BfDelegateBindExpression, BfMethodBoundExpression);

class BfLambdaBindExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfLambdaBindExpression, BfExpression);

	BfAstNode* mNewToken;
	BfTokenNode* mOpenParen;
	BfTokenNode* mCloseParen;
	BfSizedArray<ASTREF(BfIdentifierNode*)> mParams;
	BfSizedArray<ASTREF(BfTokenNode*)> mCommas;
	BfTokenNode* mFatArrowToken;
	BfAstNode* mBody; // Either expression or block
	BfFieldDtorDeclaration* mDtor;
};	BF_AST_DECL(BfLambdaBindExpression, BfExpression);

class BfNamedExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfNamedExpression, BfExpression);

	BfIdentifierNode* mNameNode;
	BfTokenNode* mColonToken;
	BfExpression* mExpression;
};	BF_AST_DECL(BfNamedExpression, BfExpression);

class BfAttributedExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfAttributedExpression, BfExpression);

	BfAttributeDirective* mAttributes;
	BfExpression* mExpression;
};	BF_AST_DECL(BfAttributedExpression, BfExpression);

class BfAttributedStatement : public BfStatement
{
public:
	BF_AST_TYPE(BfAttributedStatement, BfStatement);

	BfAttributeDirective* mAttributes;
	BfAstNode* mStatement;
};	BF_AST_DECL(BfAttributedStatement, BfStatement);

class BfCtorExplicitNode : public BfAstNode
{
public:
	BF_AST_TYPE(BfCtorExplicitNode, BfAstNode);

	BfAstNode* mDotToken;
	BfTokenNode* mThisToken;
	BfGenericArgumentsNode* mGenericArgs;
};	BF_AST_DECL(BfCtorExplicitNode, BfAstNode);

class BfObjectCreateExpression : public BfMethodBoundExpression
{
public:
	BF_AST_TYPE(BfObjectCreateExpression, BfMethodBoundExpression);

	BfAstNode* mNewNode;
	BfTokenNode* mStarToken;
	BfTypeReference* mTypeRef;
	BfCtorExplicitNode* mCtorExplicit;
	BfTokenNode* mOpenToken;
	BfTokenNode* mCloseToken;
	BfSizedArray<BfExpression*> mArguments;
	BfSizedArray<BfTokenNode*> mCommas;
};	BF_AST_DECL(BfObjectCreateExpression, BfMethodBoundExpression);

class BfExtendExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfExtendExpression, BfExpression);
	BfAstNode* mTarget;
	BfTypeDeclaration* mTypeDecl;
};	BF_AST_DECL(BfExtendExpression, BfExpression);

class BfBoxExpression : public BfExpression
{
public:
	BF_AST_TYPE(BfBoxExpression, BfExpression);

	BfAstNode* mAllocNode;
	BfTokenNode* mBoxToken;
	BfExpression* mExpression;
};	BF_AST_DECL(BfBoxExpression, BfExpression);

class BfDeleteStatement : public BfStatement
{
public:
	BF_AST_TYPE(BfDeleteStatement, BfStatement);

	BfTokenNode* mDeleteToken;
	BfTokenNode* mTargetTypeToken; // colon token
	BfAstNode* mAllocExpr;
	BfAttributeDirective* mAttributes;
	BfExpression* mExpression;
};	BF_AST_DECL(BfDeleteStatement, BfStatement);

class BfDeferBindNode : public BfAstNode
{
public:
	BF_AST_TYPE(BfDeferBindNode, BfAstNode);

	BfTokenNode* mOpenBracket;
	BfTokenNode* mCloseBracket;
	BfSizedArray<ASTREF(BfIdentifierNode*)> mParams;
	BfSizedArray<ASTREF(BfTokenNode*)> mCommas;
};	BF_AST_DECL(BfDeferBindNode, BfAstNode);

class BfDeferStatement : public BfStatement
{
public:
	BF_AST_TYPE(BfDeferStatement, BfStatement);

	BfTokenNode* mDeferToken;
	BfTokenNode* mColonToken;
	BfAstNode* mScopeName; // :, mixin, or identifier

	BfDeferBindNode* mBind;

	//Legacy compat, remove
	BfTokenNode* mOpenParen;
	BfTokenNode* mScopeToken;
	BfTokenNode* mCloseParen;

	BfAstNode* mTargetNode;

// 	virtual bool IsMissingSemicolon() override
// 	{
// 		return BfNodeDynCastExact<BfBlock>(mTargetNode) == NULL;
// 	}
};	BF_AST_DECL(BfDeferStatement, BfStatement);

class BfThrowStatement : public BfStatement
{
public:
	BF_AST_TYPE(BfThrowStatement, BfStatement);

	BfTokenNode* mThrowToken;
	BfExpression* mExpression;
};	BF_AST_DECL(BfThrowStatement, BfStatement);

class BfScopedInvocationTarget : public BfAstNode
{
public:
	BF_AST_TYPE(BfScopedInvocationTarget, BfAstNode);

	BfAstNode* mTarget;
	BfTokenNode* mColonToken;
	BfAstNode* mScopeName; // :, mixin, or identifier
};	BF_AST_DECL(BfScopedInvocationTarget, BfAstNode);

class BfInvocationExpression : public BfMethodBoundExpression
{
public:
	BF_AST_TYPE(BfInvocationExpression, BfMethodBoundExpression);

	ASTREF(BfAstNode*) mTarget;
	ASTREF(BfTokenNode*) mOpenParen;
	ASTREF(BfTokenNode*) mCloseParen;
	ASTREF(BfGenericArgumentsNode*) mGenericArgs;
	BfSizedArray<ASTREF(BfExpression*)> mArguments;
	BfSizedArray<ASTREF(BfTokenNode*)> mCommas;
};	BF_AST_DECL(BfInvocationExpression, BfMethodBoundExpression);

class BfEnumCaseDeclaration : public BfAstNode
{
public:
	BF_AST_TYPE(BfEnumCaseDeclaration, BfAstNode);

	ASTREF(BfTokenNode*) mCaseToken;
	BfSizedArray<ASTREF(BfFieldDeclaration*)> mEntries;
	BfSizedArray<ASTREF(BfTokenNode*)> mCommas;
};	BF_AST_DECL(BfEnumCaseDeclaration, BfAstNode);

class BfMemberDeclaration : public BfAstNode
{
public:
	BF_AST_TYPE(BfMemberDeclaration, BfAstNode);

	BfAttributeDirective* mAttributes;
	BfAstNode* mProtectionSpecifier;
	BfTokenNode* mStaticSpecifier;
	BfTokenNode* mReadOnlySpecifier; // Also stores 'inline'
};	BF_AST_DECL(BfMemberDeclaration, BfAstNode);

class BfVariableDeclaration : public BfExpression
{
public:
	BF_AST_TYPE(BfVariableDeclaration, BfExpression);

	ASTREF(BfAttributeDirective*) mAttributes;
	ASTREF(BfTokenNode*) mModSpecifier;
	ASTREF(BfTypeReference*) mTypeRef;
	ASTREF(BfTokenNode*) mPrecedingComma;
	BfAstNode* mNameNode; // Either BfIdentifierNode or BfTupleExpression
	ASTREF(BfTokenNode*) mEqualsNode;
	ASTREF(BfExpression*) mInitializer;
};	BF_AST_DECL(BfVariableDeclaration, BfExpression);

class BfLocalMethodDeclaration : public BfCompoundStatement
{
public:
	BF_AST_TYPE(BfLocalMethodDeclaration, BfCompoundStatement);

	BfMethodDeclaration* mMethodDeclaration;
};	BF_AST_DECL(BfLocalMethodDeclaration, BfCompoundStatement);

class BfParameterDeclaration : public BfVariableDeclaration
{
public:
	BF_AST_TYPE(BfParameterDeclaration, BfVariableDeclaration);
	BfTokenNode* mModToken; // 'Params'
};	BF_AST_DECL(BfParameterDeclaration, BfVariableDeclaration);

class BfGenericParamsDeclaration : public BfAstNode
{
public:
	BF_AST_TYPE(BfGenericParamsDeclaration, BfAstNode);

	ASTREF(BfTokenNode*) mOpenChevron;
	BfSizedArray<ASTREF(BfIdentifierNode*)> mGenericParams;
	BfSizedArray<ASTREF(BfAstNode*)> mCommas;
	ASTREF(BfTokenNode*) mCloseChevron;
};	BF_AST_DECL(BfGenericParamsDeclaration, BfAstNode);

class BfGenericArgumentsNode : public BfAstNode
{
public:
	BF_AST_TYPE(BfGenericArgumentsNode, BfAstNode);

	ASTREF(BfTokenNode*) mOpenChevron;
	BfSizedArray<ASTREF(BfAstNode*)> mGenericArgs;
	BfSizedArray<ASTREF(BfAstNode*)> mCommas;
	ASTREF(BfTokenNode*) mCloseChevron;
};	BF_AST_DECL(BfGenericArgumentsNode, BfAstNode);

class BfTokenPairNode : public BfAstNode
{
public:
	BF_AST_TYPE(BfTokenPairNode, BfAstNode);

	BfTokenNode* mLeft;
	BfTokenNode* mRight;
};	BF_AST_DECL(BfTokenPairNode, BfAstNode);

class BfGenericOperatorConstraint : public BfAstNode
{
public:
	BF_AST_TYPE(BfGenericOperatorConstraint, BfAstNode);

	BfTokenNode* mOperatorToken;
	BfTypeReference* mLeftType;
	BfTokenNode* mOpToken;
	BfTypeReference* mRightType;
};  BF_AST_DECL(BfGenericOperatorConstraint, BfAstNode);

class BfGenericConstraint : public BfAstNode
{
public:
	BF_AST_TYPE(BfGenericConstraint, BfAstNode);

	BfTokenNode* mWhereToken;
	BfTypeReference* mTypeRef;
	BfTokenNode* mColonToken;
	BfSizedArray<BfAstNode*> mConstraintTypes;
	BfSizedArray<ASTREF(BfTokenNode*)> mCommas;
};	BF_AST_DECL(BfGenericConstraint, BfAstNode);

class BfGenericConstraintExpression : public BfAstNode
{
public:
	BF_AST_TYPE(BfGenericConstraintExpression, BfAstNode);

	BfTokenNode* mWhereToken;
	BfExpression* mExpression;
};  BF_AST_DECL(BfGenericConstraintExpression, BfAstNode);

class BfGenericConstraintsDeclaration : public BfAstNode
{
public:
	BF_AST_TYPE(BfGenericConstraintsDeclaration, BfAstNode);
	BfSizedArray<BfAstNode*> mGenericConstraints;
	bool mHasExpressions;
};	BF_AST_DECL(BfGenericConstraintsDeclaration, BfAstNode);

class BfMethodDeclaration : public BfMemberDeclaration
{
public:
	BF_AST_TYPE(BfMethodDeclaration, BfMemberDeclaration);

	BfCommentNode* mDocumentation;
	BfTokenNode* mExternSpecifier;
	BfTokenNode* mVirtualSpecifier; // either 'virtual', 'override', or 'abstract'
	BfTokenNode* mNewSpecifier;
	BfTokenNode* mMixinSpecifier;
	BfTokenNode* mPartialSpecifier;
	BfTokenNode* mMutSpecifier;
	BfTypeReference* mReturnType;
	BfTypeReference* mExplicitInterface;
	BfTokenNode* mExplicitInterfaceDotToken;
	BfIdentifierNode* mNameNode;
	BfTokenNode* mOpenParen;
	BfTokenNode* mThisToken;
	BfSizedArray<BfParameterDeclaration*> mParams;
	BfSizedArray<BfTokenNode*> mCommas;
	BfTokenNode* mCloseParen;
	BfGenericParamsDeclaration* mGenericParams;
	BfGenericConstraintsDeclaration* mGenericConstraintsDeclaration;
	BfAstNode* mEndSemicolon;
	BfTokenNode* mFatArrowToken;
	BfAstNode* mBody; // Either expression or block

	//BfMethodDef* mMethodDef;

	bool mHadYield;
};	BF_AST_DECL(BfMethodDeclaration, BfMemberDeclaration);

class BfOperatorDeclaration : public BfMethodDeclaration
{
public:
	BF_AST_TYPE(BfOperatorDeclaration, BfMethodDeclaration);

	BfTokenNode* mExplicitToken; // Explicit or Implicit
	BfTokenNode* mOperatorToken;
	BfTokenNode* mOpTypeToken;
	bool mIsConvOperator;
	BfUnaryOp mUnaryOp;
	BfBinaryOp mBinOp;
	BfAssignmentOp mAssignOp;
};	BF_AST_DECL(BfOperatorDeclaration, BfMethodDeclaration);

class BfConstructorDeclaration : public BfMethodDeclaration
{
public:
	BF_AST_TYPE(BfConstructorDeclaration, BfMethodDeclaration);

	BfTokenNode* mThisToken;

	BfTokenNode* mInitializerColonToken;
	BfExpression* mInitializer;
};	BF_AST_DECL(BfConstructorDeclaration, BfMethodDeclaration);

class BfAutoConstructorDeclaration : public BfConstructorDeclaration
{
public:
	BF_AST_TYPE(BfAutoConstructorDeclaration, BfConstructorDeclaration);

	BfAstNode* mPrefix;
};	BF_AST_DECL(BfAutoConstructorDeclaration, BfConstructorDeclaration);

class BfDestructorDeclaration : public BfMethodDeclaration
{
public:
	BF_AST_TYPE(BfDestructorDeclaration, BfMethodDeclaration);

	BfTokenNode* mTildeToken;
	BfTokenNode* mThisToken;
};	BF_AST_DECL(BfDestructorDeclaration, BfMethodDeclaration);

class BfFieldDtorDeclaration : public BfAstNode
{
public:
	BF_AST_TYPE(BfFieldDtorDeclaration, BfAstNode);

	BfTokenNode* mTildeToken;

	BfAstNode* mBody;
	BfFieldDtorDeclaration* mNextFieldDtor;
};	BF_AST_DECL(BfFieldDtorDeclaration, BfAstNode);

class BfUsingSpecifierNode : public BfAstNode
{
public:
	BF_AST_TYPE(BfUsingSpecifierNode, BfAstNode);

	BfAstNode* mProtection;
	BfTokenNode* mUsingToken;
};  BF_AST_DECL(BfUsingSpecifierNode, BfAstNode);

class BfFieldDeclaration : public BfMemberDeclaration
{
public:
	BF_AST_TYPE(BfFieldDeclaration, BfMemberDeclaration);

	BfCommentNode* mDocumentation;
	BfTokenNode* mPrecedingComma;
	BfAstNode* mConstSpecifier; // Could be 'const' or 'using'
	BfTokenNode* mVolatileSpecifier;
	BfTokenNode* mNewSpecifier;
	BfTokenNode* mExternSpecifier; // Could be 'extern' or 'append'
	BfTypeReference* mTypeRef;
	BfIdentifierNode* mNameNode;
	BfTokenNode* mEqualsNode;
	BfExpression* mInitializer;
	BfFieldDtorDeclaration* mFieldDtor;

	BfFieldDef* mFieldDef;
};	BF_AST_DECL(BfFieldDeclaration, BfMemberDeclaration);

class BfEnumEntryDeclaration : public BfFieldDeclaration
{
public:
	BF_AST_TYPE(BfEnumEntryDeclaration, BfFieldDeclaration);
};	BF_AST_DECL(BfEnumEntryDeclaration, BfFieldDeclaration);

class BfPropertyMethodDeclaration : public BfAstNode
{
public:
	BF_AST_TYPE(BfPropertyMethodDeclaration, BfAstNode);
	BfPropertyDeclaration* mPropertyDeclaration;
	BfAttributeDirective* mAttributes;
	BfAstNode* mProtectionSpecifier;
	BfTokenNode* mSetRefSpecifier;
	BfTokenNode* mMutSpecifier;
	BfIdentifierNode* mNameNode;
	BfTokenNode* mFatArrowToken;
	BfAstNode* mBody;
	BfAstNode* mEndSemicolon;
};	BF_AST_DECL(BfPropertyMethodDeclaration, BfAstNode);

class BfPropertyBodyExpression : public BfAstNode
{
public:
	BF_AST_TYPE(BfPropertyBodyExpression, BfAstNode);
	BfTokenNode* mMutSpecifier;
	BfTokenNode* mFatTokenArrow;
};  BF_AST_DECL(BfPropertyBodyExpression, BfAstNode);

class BfPropertyDeclaration : public BfFieldDeclaration
{
public:
	BF_AST_TYPE(BfPropertyDeclaration, BfFieldDeclaration);

	BfTokenNode* mVirtualSpecifier; // either 'virtual', 'override', or 'abstract'
	BfTypeReference* mExplicitInterface;
	BfTokenNode* mExplicitInterfaceDotToken;
	BfAstNode* mDefinitionBlock;

	BfSizedArray<BfPropertyMethodDeclaration*> mMethods;

	BfPropertyMethodDeclaration* GetMethod(const StringImpl& name);
};	BF_AST_DECL(BfPropertyDeclaration, BfFieldDeclaration);

class BfIndexerDeclaration : public BfPropertyDeclaration
{
public:
	BF_AST_TYPE(BfIndexerDeclaration, BfPropertyDeclaration);

	BfTokenNode* mThisToken;
	BfTokenNode* mOpenBracket;
	BfSizedArray<ASTREF(BfParameterDeclaration*)> mParams;
	BfSizedArray<ASTREF(BfTokenNode*)> mCommas;
	BfTokenNode* mCloseBracket;
};	BF_AST_DECL(BfIndexerDeclaration, BfPropertyDeclaration);

class BfBreakStatement : public BfStatement
{
public:
	BF_AST_TYPE(BfBreakStatement, BfStatement);

	BfTokenNode* mBreakNode;
	BfAstNode* mLabel;
};	BF_AST_DECL(BfBreakStatement, BfStatement);

class BfTryStatement : public BfCompoundStatement
{
public:
	BF_AST_TYPE(BfTryStatement, BfCompoundStatement);

	BfTokenNode* mTryToken;
	BfAstNode* mStatement;
};	BF_AST_DECL(BfTryStatement, BfCompoundStatement);

class BfCatchStatement : public BfCompoundStatement
{
public:
	BF_AST_TYPE(BfCatchStatement, BfCompoundStatement);

	BfTokenNode* mCatchToken;
	BfAstNode* mStatement;
};	BF_AST_DECL(BfCatchStatement, BfCompoundStatement);

class BfFinallyStatement : public BfCompoundStatement
{
public:
	BF_AST_TYPE(BfFinallyStatement, BfCompoundStatement);

	BfTokenNode* mFinallyToken;
	BfAstNode* mStatement;
};	BF_AST_DECL(BfFinallyStatement, BfCompoundStatement);

class BfCheckedStatement : public BfCompoundStatement
{
public:
	BF_AST_TYPE(BfCheckedStatement, BfCompoundStatement);

	BfTokenNode* mCheckedToken;
	BfAstNode* mStatement;
};	BF_AST_DECL(BfCheckedStatement, BfCompoundStatement);

class BfUncheckedStatement : public BfCompoundStatement
{
public:
	BF_AST_TYPE(BfUncheckedStatement, BfCompoundStatement);

	BfTokenNode* mUncheckedToken;
	BfAstNode* mStatement;
};	BF_AST_DECL(BfUncheckedStatement, BfCompoundStatement);

class BfContinueStatement : public BfStatement
{
public:
	BF_AST_TYPE(BfContinueStatement, BfStatement);

	BfTokenNode* mContinueNode;
	BfAstNode* mLabel;
};	BF_AST_DECL(BfContinueStatement, BfStatement);

class BfFallthroughStatement : public BfStatement
{
public:
	BF_AST_TYPE(BfFallthroughStatement, BfStatement);

	BfTokenNode* mFallthroughToken;
	BfAstNode* mLabel;
};	BF_AST_DECL(BfFallthroughStatement, BfStatement);

class BfForEachStatement : public BfLabelableStatement
{
public:
	BF_AST_TYPE(BfForEachStatement, BfLabelableStatement);

	BfTokenNode* mForToken;
	BfTokenNode* mOpenParen;
	BfTokenNode* mReadOnlyToken;
	BfTypeReference* mVariableTypeRef;
	BfAstNode* mVariableName; // Either BfIdentifierNode or BfTupleExpression
	BfTokenNode* mInToken;
	BfExpression* mCollectionExpression;
	BfTokenNode* mCloseParen;
	BfAstNode* mEmbeddedStatement;
};	BF_AST_DECL(BfForEachStatement, BfLabelableStatement);

class BfForStatement : public BfLabelableStatement
{
public:
	BF_AST_TYPE(BfForStatement, BfLabelableStatement);

	BfTokenNode* mForToken;
	BfTokenNode* mOpenParen;
	BfSizedArray<ASTREF(BfAstNode*)> mInitializers;
	BfSizedArray<ASTREF(BfTokenNode*)> mInitializerCommas;
	BfTokenNode* mInitializerSemicolon;
	BfExpression* mCondition;
	BfTokenNode* mConditionSemicolon;
	BfSizedArray<ASTREF(BfAstNode*)> mIterators;
	BfSizedArray<ASTREF(BfTokenNode*)> mIteratorCommas;
	BfTokenNode* mCloseParen;
	BfAstNode* mEmbeddedStatement;
};	BF_AST_DECL(BfForStatement, BfLabelableStatement);

class BfUsingStatement : public BfCompoundStatement
{
public:
	BF_AST_TYPE(BfUsingStatement, BfCompoundStatement);

	BfTokenNode* mUsingToken;
	BfTokenNode* mOpenParen;
	BfVariableDeclaration* mVariableDeclaration;
	BfTokenNode* mCloseParen;
	BfAstNode* mEmbeddedStatement;
};	BF_AST_DECL(BfUsingStatement, BfCompoundStatement);

class BfDoStatement : public BfLabelableStatement
{
public:
	BF_AST_TYPE(BfDoStatement, BfLabelableStatement);

	BfTokenNode* mDoToken;
	BfAstNode* mEmbeddedStatement;
};	BF_AST_DECL(BfDoStatement, BfLabelableStatement);

class BfRepeatStatement : public BfLabelableStatement
{
public:
	BF_AST_TYPE(BfRepeatStatement, BfLabelableStatement);

	BfTokenNode* mRepeatToken;
	BfAstNode* mEmbeddedStatement;
	BfTokenNode* mWhileToken;
	BfTokenNode* mOpenParen;
	BfExpression* mCondition;
	BfTokenNode* mCloseParen;
};	BF_AST_DECL(BfRepeatStatement, BfLabelableStatement);

class BfWhileStatement : public BfLabelableStatement
{
public:
	BF_AST_TYPE(BfWhileStatement, BfLabelableStatement);

	BfTokenNode* mWhileToken;
	BfTokenNode* mOpenParen;
	BfExpression* mCondition;
	BfTokenNode* mCloseParen;
	BfAstNode* mEmbeddedStatement;
};	BF_AST_DECL(BfWhileStatement, BfLabelableStatement);

class BfReturnStatement : public BfStatement
{
public:
	BF_AST_TYPE(BfReturnStatement, BfStatement);

	BfTokenNode* mReturnToken;
	BfExpression* mExpression;
};	BF_AST_DECL(BfReturnStatement, BfStatement);

class BfYieldStatement : public BfStatement
{
public:
	BF_AST_TYPE(BfYieldStatement, BfStatement);

	BfTokenNode* mReturnOrBreakToken;
	BfExpression* mExpression;
};	BF_AST_DECL(BfYieldStatement, BfStatement);

class BfInlineAsmStatement : public BfCompoundStatement
{
public:
	BF_AST_TYPE(BfInlineAsmStatement, BfCompoundStatement);

	BfTokenNode* mOpenBrace;
	BfTokenNode* mCloseBrace;

	Array<BfInlineAsmInstruction*> mInstructions;
	//TODO: Make a block here
};	BF_AST_DECL(BfInlineAsmStatement, BfCompoundStatement);

class BfInlineAsmInstruction : public BfAstNode
{
public:
	class AsmArg
	{
	public:
		enum EArgType
		{
			ARGTYPE_Immediate,		// immediate integer; uses mInt only
			ARGTYPE_FloatReg,		// float point st register; mInt is st reg index
			ARGTYPE_IntReg,			// general integer register; mReg is register name
			ARGTYPE_Memory,			// memory access; arg mem flags indicate additive permutation, [baseReg + adjReg*scalar + immDisplacement]
		};
		enum EArgMemFlags
		{
			ARGMEMF_ImmediateDisp	= (1 << 0),		// uses immediate displacement constant
			ARGMEMF_BaseReg			= (1 << 1),		// uses unscaled base register
			ARGMEMF_AdjReg			= (1 << 2),		// uses scaled adjustment register and scalar value
		};

		EArgType mType;
		unsigned long mMemFlags;
		String mSegPrefix; // if non-empty, cs|ds|es|fs|gs|ss
		String mSizePrefix; // if non-empty, size prefix for memory accesses (e.g. byte|word|dword), syntactically followed by "ptr" although that's omitted here
		int mInt; // memory displacement, immediate integer, fp st register index, etc.
		String mReg; // general register, or unscaled base register for memory accesses
		String mAdjReg; // scaled adjustment register for memory accesses
		int mAdjRegScalar; // adjustment scalar (e.g. 2, 4, or 8).
		String mMemberSuffix; // if non-empty, struct member suffix following memory access, e.g. [ebx].mFoo

		AsmArg();

		String ToString();
	};

	class AsmInst
	{
	public:
		String mLabel;
		Array<String> mOpPrefixes;
		String mOpCode;
		Array<AsmArg> mArgs;
		int mDebugLine;

		AsmInst();

		String ToString();
	};

public:
	BF_AST_TYPE(BfInlineAsmInstruction, BfAstNode);

	AsmInst mAsmInst;
};	BF_AST_DECL(BfInlineAsmInstruction, BfAstNode);

const char* BfTokenToString(BfToken token);
bool BfTokenIsKeyword(BfToken token);
bool BfTokenIsTypeDecl(BfToken token);
BfBinaryOp BfAssignOpToBinaryOp(BfAssignmentOp assignmentOp);
BfBinaryOp BfGetOppositeBinaryOp(BfBinaryOp origOp);
BfBinaryOp BfGetFlippedBinaryOp(BfBinaryOp origOp);
bool BfBinOpEqualityCheck(BfBinaryOp binOp);
int BfGetBinaryOpPrecendence(BfBinaryOp binOp);
const char* BfGetOpName(BfBinaryOp binOp);
const char* BfGetOpName(BfUnaryOp unaryOp);
bool BfCanOverloadOperator(BfUnaryOp unaryOp);
BfBinaryOp BfTokenToBinaryOp(BfToken token);
BfUnaryOp BfTokenToUnaryOp(BfToken token);
BfAssignmentOp BfTokenToAssignmentOp(BfToken token);
bool BfIsCommentBlock(BfCommentKind commentKind);

NS_BF_END

template<>
struct BeefHash<Beefy::BfVariant>
{
	size_t operator()(const Beefy::BfVariant& val) const
	{
		if (val.mTypeCode == Beefy::BfTypeCode_Struct)
		{
			Beefy::BfVariant::StructData* structData = (Beefy::BfVariant::StructData*)val.mPtr;
			return HashBytes(structData->mData, structData->mSize);
		}
		return (size_t)val.mUInt64;
	}
};

namespace std
{
	template<>
	struct hash<Beefy::BfAstNodeList>
	{
		size_t operator()(const Beefy::BfAstNodeList& val) const
		{
			if (val.mList.mSize == 0)
				return 0;
			if (val.mList.mSize == 0)
				return (size_t)val.mList.mVals[0];
			return HashBytes((uint8*)val.mList.mVals, sizeof(Beefy::BfAstNode*) * val.mList.mSize);
		}
	};
}
