#ifndef __PARSER_H__
#define __PARSER_H__

#include "Expr.h"

namespace AScript {

//-----------------------------------------------------------------------------
// Parser
//-----------------------------------------------------------------------------
class Parser {
public:
	enum Stat {
		STAT_Start,
		STAT_DoubleChars, STAT_TripleChars, STAT_Escape,
		STAT_Quote, STAT_Colon, STAT_ColonColon,
		STAT_Error, STAT_ErrorRecovery,
		STAT_AfterLBrace,
		STAT_NumberPre, STAT_NumberHex, STAT_NumberOct,
		STAT_NumberAfterDot, STAT_Number,
		STAT_NumberExpAfterE, STAT_NumberExpAfterSign, STAT_NumberExp,
		STAT_ImagNumber, STAT_Symbol, STAT_SymbolExclamation,
		STAT_CommentLine, STAT_CommentBlock,
		STAT_CommentBlockEnd, STAT_CommentBlockNest,
		STAT_StringPre, STAT_StringFirst, STAT_StringSecond,
		STAT_String, STAT_MString,
		STAT_StringEsc, STAT_StringEscHex, STAT_StringEscOct,
		STAT_StringInCommentBlock, STAT_StringEscInCommentBlock,
		STAT_MStringFirst, STAT_MStringSecond,
	};
	enum Precedence {
		PREC_LT,
		PREC_EQ,
		PREC_GT,
		PREC_Error,
	};
	enum ElemType {
		ETYPE_Begin,
		ETYPE_Number,
		ETYPE_ImagNumber,
		ETYPE_Quote,
		ETYPE_Force,
		ETYPE_Question,
		ETYPE_Plus,
		ETYPE_Minus,
		ETYPE_Multiply,
		ETYPE_Divide,
		ETYPE_Modulo,
		ETYPE_Power,
		ETYPE_Or,
		ETYPE_And,
		ETYPE_Xor,
		ETYPE_Invert,
		ETYPE_Equal,
		ETYPE_NotEqual,
		ETYPE_Less,
		ETYPE_Greater,
		ETYPE_LessEq,
		ETYPE_GreaterEq,
		ETYPE_Compare,
		ETYPE_ContainCheck,
		ETYPE_Assign,
		ETYPE_AssignPlus,
		ETYPE_AssignMinus,
		ETYPE_AssignMultiply,
		ETYPE_AssignDivide,
		ETYPE_AssignModulo,
		ETYPE_AssignPower,
		ETYPE_AssignBitOr,
		ETYPE_AssignBitAnd,
		ETYPE_AssignBitXor,
		ETYPE_DictAssign,
		ETYPE_OrOr,
		ETYPE_AndAnd,
		ETYPE_Not,
		ETYPE_Colon,
		ETYPE_ColonColon,
		ETYPE_Sequence,
		ETYPE_Comma,
		ETYPE_Semicolon,
		ETYPE_Dot,
		ETYPE_LParenthesis,		// open element
		ETYPE_RParenthesis,		// close element
		ETYPE_LBrace,			// open element
		ETYPE_RBrace,			// close element
		ETYPE_LBracket,			// open element
		ETYPE_RBracket,			// close element
		ETYPE_LBlockParam,		// open element
		ETYPE_RBlockParam,		// close element
		ETYPE_EOL,
		ETYPE_EOF,
		ETYPE_Symbol,
		ETYPE_String,
		ETYPE_Expr,
		ETYPE_DoubleChars,		// only used in tokenizing process
		ETYPE_TripleChars,		// only used in tokenizing process
		ETYPE_Unknown,
	};
	struct ElemTypeInfo {
		ElemType elemType;
		const TCHAR *name;
		const TCHAR *symbol;
	};
	class Element {
	private:
		ElemType _elemType;
		String _str;
		int _num;	// _num is only available for 
		// _pExpr is only available for the following eleement types.
		// ETYPE_Expr          (Expr)
		// ETYPE_LParenthesis  (Expr_Lister)
		// ETYPE_LBrace        (Expr_Block)
		// ETYPE_LBracket      (Expr_Lister)
		// ETYPE_LBlockParam   (Expr_BlockParam)
		Expr *_pExpr;
		static const ElemTypeInfo _elemTypeInfoTbl[];
	public:
		inline Element(const Element &elem) :
					_elemType(elem._elemType), _pExpr(elem._pExpr),
					_str(elem._str), _num(elem._num) {}
		inline Element(ElemType elemType, const String &str) :
					_elemType(elemType), _pExpr(NULL), _str(str), _num(0) {}
		inline Element(ElemType elemType, int num) :
					_elemType(elemType), _pExpr(NULL), _num(num) {}
		inline Element(ElemType elemType, Expr *pExpr = NULL) :
					_elemType(elemType), _pExpr(pExpr), _num(0) {}
		inline Element &operator=(const Element &elem) {
			_elemType = elem._elemType, _pExpr = elem._pExpr;
			_str = elem._str, _num = elem._num;
			return *this;
		}
		inline bool IsType(ElemType elemType) const { return _elemType == elemType; }
		inline bool IsOpenElement() const {
			return IsType(ETYPE_LParenthesis) || IsType(ETYPE_LBrace) ||
					IsType(ETYPE_LBracket) || IsType(ETYPE_LBlockParam);
		}
		inline bool IsCloseElement() const {
			return IsType(ETYPE_RParenthesis) || IsType(ETYPE_RBrace) ||
					IsType(ETYPE_RBracket) || IsType(ETYPE_RBlockParam);
		}
		inline bool IsSeparatorElement() const {
			return IsType(ETYPE_EOL) || IsType(ETYPE_EOF) ||
								IsType(ETYPE_Comma) || IsType(ETYPE_Semicolon);
		}
		inline bool IsStrongReducer() const {
			return IsType(ETYPE_Begin) || IsType(ETYPE_Number) ||
				IsType(ETYPE_ImagNumber) || IsType(ETYPE_RParenthesis) ||
				IsType(ETYPE_RBrace) || IsType(ETYPE_RBracket) ||
				IsType(ETYPE_RBlockParam) || IsType(ETYPE_Symbol) ||
				IsType(ETYPE_String) || IsType(ETYPE_Expr);
		}
		inline Expr *GetExpr() const { return _pExpr; }
		inline void SetExpr(Expr *pExpr) { _pExpr = pExpr; }
		inline const TCHAR *GetString() const { return _str.c_str(); }
		inline int GetNum() const { return _num; }
		inline void AddString(const TCHAR *str) { _str.append(str); }
		Number GetNumber() const;
		const TCHAR *GetTypeName() const;
		const TCHAR *GetTypeSymbol() const;
	};
	class ElementStack : public std::vector<Element> {
	public:
		reverse_iterator SeekTerminal(reverse_iterator p);
		Element &Peek(int offset) { return *(rbegin() + offset); }
		void Clear();
		String ToString() const;
	};
private:
	bool _newlineAsSeparatorFlag;
	Stat _stat;
	bool _blockParamFlag;
	bool _quoteFlag;
	bool _shiftFlag;
	int _cntLine;
	int _fieldDepthLevel;
	int _commentNestLevel;
	String _token;
	ElementStack _elemStack;
	struct {
		TCHAR chBorder;
		bool escapeEnableFlag;
		Stat statRtn;
		int cntRest;
		unsigned long accum;
	} _stringInfo;
public:
	Parser();
	void InitStack();
	Expr *ParseChar(Environment &env, Signal sig, int ch);
	Value Exec(Environment &env, Signal sig, const TCHAR *str);
	Value Exec(Environment &env, Signal sig, File &file, bool skipErrorFlag);
	Expr *Parse(Environment &env, Signal sig, const TCHAR *str);
	inline bool IsStackEmpty() const { return _elemStack.size() <= 1; }
	inline bool IsContinued() const { return !IsStackEmpty() || _stat != STAT_Start; }
	inline int GetLineNo() const { return _cntLine + 1; }
private:
	void SetError(Signal sig, const TCHAR *format, ...);
	void SetError_InvalidElement(Signal sig);
	void SetError_InvalidElement(Signal sig, int lineno);
	static Precedence LookupPrec(const Element &elemLeft, const Element &elemRight);
	Expr *FeedElement(Environment &env, Signal sig, const Element &elem);
	bool ReduceOneElem(Environment &env, Signal sig);
	bool ReduceTwoElems(Environment &env, Signal sig);
	bool ReduceThreeElems(Environment &env, Signal sig);
	bool ReduceFourElems(Environment &env, Signal sig);
	bool ReduceFiveElems(Environment &env, Signal sig);
};

}

#endif
