/* assembler.h -- written by Alexis WILKE for Made to Order Software Corp. (c) 2005-2009 */

/*

Copyright (c) 2005-2009 Made to Order Software Corp.

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and
associated documentation files (the "Software"), to
deal in the Software without restriction, including
without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom
the Software is furnished to do so, subject to the
following conditions:

The above copyright notice and this permission notice
shall be included in all copies or substantial
portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

*/


#include	"sswf/libsswf_asas.h"

namespace sswf
{
namespace asas
{



// out internal assembler, this is the real object which does
// the real job! (well... but we need to also have the next
// optimizer rework the output because it really looks ugly
// at times...)
class IntAssembler
{
public:
				IntAssembler(as::ErrorStream& error_stream, as::Options& options);
				~IntAssembler();

	int			AssembleClasses(as::NodePtr& program, TagBase& parent, bool& has_actions);
	int			Assemble(as::NodePtr& program, TagBase& tag, Vectors *actions);

private:
	class LoopData
	{
	public:
		as::String	f_continue;
		as::String	f_break;
	};

	class FuncParam
	{
	public:
		enum mode_t {
			MODE_UNKNOWN,
			MODE_REGISTER,
			MODE_CONSTANT,
			MODE_ARRAY,
			MODE_VARIABLE	// not in a register nor an array
		};

				FuncParam(void)
				{
					f_mode = MODE_UNKNOWN;
					f_reg = 0;
					f_index = 0;
					//f_constant -- auto-init
				}

		mode_t		f_mode;		// see mode_t
		int		f_reg;		// register
		int		f_index;	// array
		as::NodePtr	f_constant;	// direct value
	};

	class NameData
	{
	public:
		enum mode_t {
			MODE_UNKNOWN,
			MODE_VARIABLE,		// use a name
			MODE_REGISTER,
			MODE_FUNCTION = MODE_VARIABLE
		};

				NameData(void)
				{
					f_mode = MODE_UNKNOWN;
					f_reg = 0;
					//f_name -- auto-init
				}

		void		GenerateName(const char *introducer);

		mode_t		f_mode;
		int		f_reg;
		as::String	f_name;

	private:
		static int	g_unique;	// counter to generate unique names
	};

	typedef NameData VariableData;
	typedef NameData FunctionData;

	enum list_flags_t {
		LIST_FLAG_DECLARATIONS = 1,
		LIST_FLAG_STATEMENTS = 2,
		LIST_FLAG_ALL = (LIST_FLAG_DECLARATIONS | LIST_FLAG_STATEMENTS)
	};

	class Registers
	{
	public:
				Registers(void);
				Registers(const Registers& src);
				~Registers();

		void		Push(int max);
		void		Pop(void);
		int		AllocRegister(int start = 0);
		void		Store(int r, TagBase *f_tag, Vectors *f_actions, bool pop = false);
		int		StoreRegister(TagBase *f_tag, Vectors *f_actions, bool pop = false);
		void		LoadRegister(int r, bool to_free, TagBase *f_tag, Vectors *f_actions);
		void		FreeRegister(int r);

		Registers *	f_previous;
		char		f_regs[256];
		int		f_reg;
		int		f_reg_max;
	};

	enum {
		INCLUSIVE_FLOAT     = 0x01,
		INCLUSIVE_INTEGER   = 0x02,
		INCLUSIVE_STRING    = 0x04,
		INCLUSIVE_BOOLEAN   = 0x08,
		INCLUSIVE_UNDEFINED = 0x10,
		INCLUSIVE_NULL      = 0x20,
		INCLUSIVE_ALL       = 0x3F
	};

	struct special_function_t {
		enum setup_t {
			STACK_ORDER_PERFECT,		// stack as is (left to right); it will work
			STACK_ORDER_PERFECT_COUNT,	// stack as is; and put the count
			STACK_ORDER_REVERSED,		// need to reverse the stack order (opposite to function calls order)
			STACK_ORDER_REVERSED_COUNT,	// like reversed with the count
			STACK_ORDER_SPECIAL		// doesn't correspond to normal stacking
		};

		const char *		f_package_name;		// i.e. "Native"
		const char *		f_function_name;	// i.e. "play"
		Action::action_t	f_action;	// i.e. Action::ACTION_PLAY
		setup_t			f_stack_order;	// see STACK_ORDER_...
		bool			f_returns;	// if false, this function doesn't return anything
	};

	struct switch_info_t {
		LoopData *		f_loop_data;
		as::String		f_default_label;
		unsigned long		f_attrs;
		int			f_reg;
	};

	struct try_info_t {
					try_info_t(void)
					{
						f_try = 0;
						//f_end -- auto-init
						f_str = 0;
						//f_regname -- auto-init
						f_regname_str = 0;
						f_reg = -1;
						f_has_number = false;
					}
					~try_info_t()
					{
						delete [] f_str;
						delete [] f_regname_str;
					}

		ActionTry *		f_try;
		as::String		f_end;	// label to exit the catch
		char *			f_str;
		as::String		f_regname;
		char *			f_regname_str;
		int			f_reg;	// all catches share the same register
		bool			f_has_number;
	};

	class PropFlags {
	public:
					PropFlags(void);
		int			SetProp(as::NodePtr& prop);

		// f_prop_flags:
		//
		//	PROP_FLAG_IS_HIDDEN -- set unless "enumeratable" attribute
		//	PROP_FLAG_CAN_DELETE -- never set for now
		//	PROP_FLAG_CAN_OVERWRITE -- set unless it is a function or a const variable
		//
		int			f_flags;	// used as a sort key
		as::NodePtr		f_prop;
	};

	class DefineClass {
	public:
					DefineClass(as::NodePtr& f_class_node, int prototype, int max);
					~DefineClass();
		void			InsertProp(as::NodePtr& prop);

		bool			f_first;
		as::NodePtr&		f_class_node;
		int			f_prototype;	// register
		int			f_count;	// current number of members registered in f_prop_flags
		int			f_max;		// # of f_prop_flags
		PropFlags *		f_prop_flags;
	};

	void			Add(as::NodePtr& expr, bool right_to_left);
	void			Array(as::NodePtr& array, Action::action_t action);
	void			Assignment(as::NodePtr& expr, as::NodePtr instance, bool duplicate);
	void			Call(as::NodePtr& expr);
	void			ClearVariables(as::NodePtr& frame);
	void			Case(as::NodePtr& switch_node, as::NodePtr& case_node, switch_info_t& info, bool no_autobreak);
	void			Catch(try_info_t& info, as::NodePtr& catch_node, bool is_last);
	void			Class(as::NodePtr& class_node);
	void			ClassClass(as::NodePtr& sub_class, int& count);
	int			ConstructorClass(as::NodePtr& class_node, const char *class_name);
	void			Default(switch_info_t& info);
	void			Directive(as::NodePtr& list, int& index, as::NodePtr& child);
	void			DirectiveList(as::NodePtr& list, bool clear = true);
	void			EmptyEnumeration(void);
	void			EnterFrame(as::NodePtr& goto_node);
	bool			ExitFrame(as::NodePtr& goto_node);
	void			Expression(as::NodePtr& expr);
	void			ExpressionArrayLiteral(as::NodePtr& expr);
	void			ExpressionAs(as::NodePtr& expr);
	void			ExpressionAssignment(as::NodePtr& expr, as::node_t type);
	void			ExpressionAssignmentAdd(as::NodePtr& expr);
	void			ExpressionAssignmentPower(as::NodePtr& expr);
	void			ExpressionAssignmentSubtract(as::NodePtr& expr);
	void			ExpressionConditional(as::NodePtr& expr);
	void			ExpressionDelete(as::NodePtr& expr);
	bool			ExpressionHasSideEffects(as::NodePtr& expr);
	void			ExpressionIdentifier(as::NodePtr& id);
	void			ExpressionIdentifierParam(as::NodePtr& instance, as::Data& inst_data);
	void			ExpressionIdentifierType(as::NodePtr& id);
	void			ExpressionIdentifierVariable(as::NodePtr& variable, as::Data& inst_data, bool with_object);
	void			ExpressionIncrement(as::NodePtr& expr, as::node_t type);
	bool			ExpressionIsConstant(as::NodePtr& expr, int inclusive = INCLUSIVE_ALL);
	void			ExpressionList(as::NodePtr& expr);
	void			ExpressionNew(as::NodePtr& expr);
	void			ExpressionObjectLiteral(as::NodePtr& expr);
	void			ExpressionPostIncrement(as::NodePtr& expr, as::node_t type);
	void			ExpressionVoid(as::NodePtr& expr);
	void			ExprIn(as::NodePtr& in_expr);
	void			ExtendsClass(as::NodePtr& class_node, const char *class_name);
	void			Finally(try_info_t& info, as::NodePtr& finally_node);
	void			FlagsClass(DefineClass& define);
	void			For(as::NodePtr& for_node);
	void			Function(as::NodePtr& function, bool use_name);
	void			FunctionClass(DefineClass& define, as::NodePtr& function);
	LoopData *		GetLoopData(as::NodePtr& while_node);
	void			Goto(as::NodePtr& goto_node);
	void			If(as::NodePtr& if_node);
const special_function_t *	IsSpecial(as::NodePtr& function, as::Data& data);
	void			List(as::NodePtr& list, int& from, int to, list_flags_t flags = LIST_FLAG_ALL);
	bool			ListClass(as::NodePtr& list, int level);
	void			Member(as::NodePtr& member, Action::action_t action);
	void			Multiply(as::NodePtr& expr);
	void			Power(as::NodePtr& expr, bool right_to_left);
	void			Return(as::NodePtr& return_node);
	void			StackClass(DefineClass& define, as::NodePtr& list);
	void			SubExtendsClass(as::NodePtr& class_node);
	void			Subtract(as::NodePtr& expr, bool right_to_left);
	void			Switch(as::NodePtr& switch_node);
	void			Throw(as::NodePtr& throw_node);
	void			Try(try_info_t& info, as::NodePtr& try_node);
	void			TryCatchFinally(as::NodePtr& list, int& index, as::NodePtr& try_node);
	int			UserConstructorClass(as::NodePtr& class_node, const char *class_name, int& count, bool& extends);
	void			UserLabel(as::NodePtr& user_label);
	void			Var(as::NodePtr& var);
	void			VarClass(DefineClass& define, as::NodePtr& var);
	void			VoidExpression(as::NodePtr& expr);
	void			With(as::NodePtr& with_node);
	void			While(as::NodePtr& while_node);

	void			Label(as::String& label);

	as::ErrorStream *	f_error_stream;
	as::Options *		f_options;
	TagBase *		f_parent;
	TagBase *		f_tag;
	Vectors *		f_actions;
	try_info_t *		f_try_info;

	Registers		f_registers;

	int			f_label;	// a unique number
};





};	// namespace asas
};	// namespace sswf
