/* libsswf_as.h -- written by Alexis WILKE for Made to Order Software, Ltd. (c) 2005-2006 */
#ifndef LIBSSWF_AS_H
#define	LIBSSWF_AS_H

/*

Copyright (c) 2005-2006 Made to Order Software, Ltd.

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	<stdarg.h>
#include	<stdio.h>
#include	<stdlib.h>
#include	<time.h>
#ifdef _MSVC
#include	<io.h>
#include	<float.h>
#include	<direct.h>
#define	snprintf		_snprintf
#define	vsnprintf		_vsnprintf
inline int			mkdir(const char *filename) { return _mkdir(filename); }
inline int			isnan(double nan) { return _isnan(nan); }
inline int			isinf(double d)
				{
					int c;
					c = _fpclass(d);
					if(c == _FPCLASS_PINF) {
						return 1;
					}
					else if(c == _FPCLASS_NINF) {
						return -1;
					}
					return 0;
				}
typedef __int64			int64_t;
typedef unsigned __int64	uint64_t;
typedef __int32			int32_t;
typedef unsigned __int32	uint32_t;
inline int64_t			strtoll(const char *str, char *end, int base)
				{
					return _atoi64(str);
				}
#else
#include	<inttypes.h>
#include	<unistd.h>
#include	<dirent.h>
#endif
#include	<string.h>
#include	<math.h>
#include	<limits.h>
#include	<sys/stat.h>

#include	"sswf/libsswf-config.h"


#ifdef NEED_FP_NAN
#ifndef FP_NAN
static union __nan_value { uint32_t __l; float __d; } __nan_union = { 0x7fc00000UL };
#define FP_NAN    (__nan_union.__d)
#endif
#endif
#ifdef NEED_ISINF
#if IRIX
// my old version (6.5.7) doesn't have a correct isinf() function
// if you have a newer version and you are sure you have a proper
// version of isinf(), then feel free to remove this declaration
#include	<fp_class.h>
static int isinf(double d)
{
	int c;
	c = fp_class_d(d);
	if(c == FP_POS_INF) {
		return 1;
	}
	else if(c == FP_NEG_INF) {
		return -1;
	}
	return 0;
}
#endif
#endif




namespace sswf
{
namespace as
{



#if defined(_DEBUG) || defined(DEBUG)
#define	AS_ASSERT(test)	{ if(!(test)) printf("FATAL ERROR: AS_ASSERT(" #test ") is false in " __FILE__ " at line %d.\n", __LINE__), abort(); }
#else
#define	AS_ASSERT(test)
#endif


enum err_code_t
{
	AS_ERR_NONE = 0,

	AS_ERR_ABSTRACT,
	AS_ERR_BAD_PRAGMA,
	AS_ERR_CANNOT_MATCH,
	AS_ERR_CANNOT_OVERLOAD,
	AS_ERR_CANNOT_OVERWRITE_CONST,
	AS_ERR_CASE_LABEL,
	AS_ERR_COLON_EXPECTED,
	AS_ERR_CURVLY_BRAKETS_EXPECTED,
	AS_ERR_DEFAULT_LABEL,
	AS_ERR_DIVIDE_BY_ZERO,
	AS_ERR_DUPLICATES,
	AS_ERR_DYNAMIC,
	AS_ERR_FINAL,
	AS_ERR_IMPORPER_STATEMENT,
	AS_ERR_INACCESSIBLE_STATEMENT,
	AS_ERR_INCOMPATIBLE,
	AS_ERR_INCOMPATIBLE_PRAGMA_ARGUMENT,
	AS_ERR_INSTANCE_EXPECTED,
	AS_ERR_INTERNAL_ERROR,
	AS_ERR_INTRINSIC,
	AS_ERR_INVALID_ARRAY_FUNCTION,
	AS_ERR_INVALID_ATTRIBUTES,
	AS_ERR_INVALID_CATCH,
	AS_ERR_INVALID_CLASS,
	AS_ERR_INVALID_CONDITIONAL,
	AS_ERR_INVALID_DEFINITION,
	AS_ERR_INVALID_DO,
	AS_ERR_INVALID_ENUM,
	AS_ERR_INVALID_EXPRESSION,
	AS_ERR_INVALID_FIELD,
	AS_ERR_INVALID_FIELD_NAME,
	AS_ERR_INVALID_FRAME,
	AS_ERR_INVALID_FUNCTION,
	AS_ERR_INVALID_GOTO,
	AS_ERR_INVALID_KEYWORD,
	AS_ERR_INVALID_LABEL,
	AS_ERR_INVALID_NAMESPACE,
	AS_ERR_INVALID_NODE,
	AS_ERR_INVALID_OPERATOR,
	AS_ERR_INVALID_PACKAGE_NAME,
	AS_ERR_INVALID_PARAMETERS,
	AS_ERR_INVALID_REST,
	AS_ERR_INVALID_RETURN_TYPE,
	AS_ERR_INVALID_SCOPE,
	AS_ERR_INVALID_TRY,
	AS_ERR_INVALID_TYPE,
	AS_ERR_INVALID_UNICODE_ESCAPE_SEQUENCE,
	AS_ERR_INVALID_VARIABLE,
	AS_ERR_LABEL_NOT_FOUND,
	AS_ERR_LOOPING_REFERENCE,
	AS_ERR_MISMATCH_FUNC_VAR,
	AS_ERR_NEED_CONST,
	AS_ERR_NOT_FOUND,
	AS_ERR_NOT_SUPPORTED,
	AS_ERR_PARENTHESIS_EXPECTED,
	AS_ERR_PRAGMA_FAILED,
	AS_ERR_SEMICOLON_EXPECTED,
	AS_ERR_SQUARE_BRAKETS_EXPECTED,
	AS_ERR_STATIC,
	AS_ERR_UNKNOWN_ESCAPE_SEQUENCE,
	AS_ERR_UNKNOWN_OPERATOR,
	AS_ERR_UNTERMINTED_STRING,
	AS_ERR_UNEXPECTED_PUNCTUATION,

	AS_ERR_max
};



class Node;

// the node type is often referenced as a token
enum node_t
{
	NODE_EOF = -1,		// when reading after the end of the file
	NODE_UNKNOWN = 0,	// node still uninitialized

	// here are all the punctuation as themselves
	// (i.e. '<', '>', '=', '+', '-', etc.)
	NODE_ADD = '+',
	NODE_BITWISE_AND = '&',
	NODE_BITWISE_NOT = '~',
	NODE_ASSIGNMENT = '=',
	NODE_BITWISE_OR = '|',
	NODE_BITWISE_XOR = '^',
	NODE_CLOSE_CURVLY_BRACKET = '}',
	NODE_CLOSE_PARENTHESIS = ')',
	NODE_CLOSE_SQUARE_BRACKET = ']',
	NODE_COLON = ':',
	NODE_COMMA = ',',
	NODE_CONDITIONAL = '?',
	NODE_DIVIDE = '/',
	NODE_GREATER = '>',
	NODE_LESS = '<',
	NODE_LOGICAL_NOT = '!',
	NODE_MODULO = '%',
	NODE_MULTIPLY = '*',
	NODE_OPEN_CURVLY_BRACKET = '{',
	NODE_OPEN_PARENTHESIS = '(',
	NODE_OPEN_SQUARE_BRACKET = '[',
	NODE_MEMBER = '.',
	NODE_SEMICOLON = ';',
	NODE_SUBTRACT = '-',

	// The following are composed tokens
	// (operators, keywords, strings, numbers...)
	NODE_other = 1000,

	NODE_ARRAY,
	NODE_ARRAY_LITERAL,
	NODE_AS,
	NODE_ASSIGNMENT_ADD,
	NODE_ASSIGNMENT_BITWISE_AND,
	NODE_ASSIGNMENT_BITWISE_OR,
	NODE_ASSIGNMENT_BITWISE_XOR,
	NODE_ASSIGNMENT_DIVIDE,
	NODE_ASSIGNMENT_LOGICAL_AND,
	NODE_ASSIGNMENT_LOGICAL_OR,
	NODE_ASSIGNMENT_LOGICAL_XOR,
	NODE_ASSIGNMENT_MAXIMUM,
	NODE_ASSIGNMENT_MINIMUM,
	NODE_ASSIGNMENT_MODULO,
	NODE_ASSIGNMENT_MULTIPLY,
	NODE_ASSIGNMENT_POWER,
	NODE_ASSIGNMENT_ROTATE_LEFT,
	NODE_ASSIGNMENT_ROTATE_RIGHT,
	NODE_ASSIGNMENT_SHIFT_LEFT,
	NODE_ASSIGNMENT_SHIFT_RIGHT,
	NODE_ASSIGNMENT_SHIFT_RIGHT_UNSIGNED,
	NODE_ASSIGNMENT_SUBTRACT,
	NODE_ATTRIBUTES,
	NODE_AUTO,
	NODE_BREAK,
	NODE_CALL,
	NODE_CASE,
	NODE_CATCH,
	NODE_CLASS,
	NODE_CONST,
	NODE_CONTINUE,
	NODE_DEBUGGER,
	NODE_DECREMENT,
	NODE_DEFAULT,
	NODE_DELETE,
	NODE_DIRECTIVE_LIST,
	NODE_DO,
	NODE_ELSE,
	NODE_EMPTY,
	NODE_ENTRY,
	NODE_ENUM,
	NODE_EQUAL,
	NODE_EXCLUDE,
	NODE_EXTENDS,
	NODE_FALSE,
	NODE_FINALLY,
	NODE_FLOAT64,
	NODE_FOR,
	NODE_FOR_IN,
	NODE_FUNCTION,
	NODE_GOTO,
	NODE_GREATER_EQUAL,
	NODE_IDENTIFIER,
	NODE_IF,
	NODE_IMPLEMENTS,
	NODE_IMPORT,
	NODE_IN,
	NODE_INCLUDE,
	NODE_INCREMENT,
	NODE_INSTANCEOF,
	NODE_INT64,
	NODE_INTERFACE,
	NODE_IS,
	NODE_LABEL,
	NODE_LESS_EQUAL,
	NODE_LIST,
	NODE_LOGICAL_AND,
	NODE_LOGICAL_OR,
	NODE_LOGICAL_XOR,
	NODE_MATCH,
	NODE_MAXIMUM,
	NODE_MINIMUM,
	NODE_NAME,
	NODE_NAMESPACE,
	NODE_NEW,
	NODE_NOT_EQUAL,
	NODE_NULL,
	NODE_OBJECT_LITERAL,
	NODE_PACKAGE,
	NODE_PARAM,
	NODE_PARAMETERS,
	NODE_PARAM_MATCH,
	NODE_POST_DECREMENT,
	NODE_POST_INCREMENT,
	NODE_POWER,
	NODE_PRIVATE,
	NODE_PROGRAM,
	NODE_PUBLIC,
	NODE_RANGE,
	NODE_REGULAR_EXPRESSION,
	NODE_REST,
	NODE_RETURN,
	NODE_ROOT,
	NODE_ROTATE_LEFT,
	NODE_ROTATE_RIGHT,
	NODE_SCOPE,
	NODE_SET,
	NODE_SHIFT_LEFT,
	NODE_SHIFT_RIGHT,
	NODE_SHIFT_RIGHT_UNSIGNED,
	NODE_STRICTLY_EQUAL,
	NODE_STRICTLY_NOT_EQUAL,
	NODE_STRING,
	NODE_SUPER,
	NODE_SWITCH,
	NODE_THIS,
	NODE_THROW,
	NODE_TRUE,
	NODE_TRY,
	NODE_TYPE,
	NODE_TYPEOF,
	NODE_UNDEFINED,
	NODE_USE,
	NODE_VAR,
	NODE_VARIABLE,
	NODE_VAR_ATTRIBUTES,
	NODE_VIDENTIFIER,
	NODE_VOID,
	NODE_WHILE,
	NODE_WITH,

	NODE_max,	// mark the limit

	// used to extract the node type from some integers
	// (used by the SWITCH statement at time of writing)
	NODE_MASK = 0x0FFFF
};





// some nodes use flags as defined below:
enum flags_t
{
	// NODE_CATCH
	NODE_CATCH_FLAG_TYPED = 0x01,

	// NODE_DIRECTIVE_LIST
	NODE_DIRECTIVE_LIST_FLAG_NEW_VARIABLES = 0x01,

	// NODE_FOR
	NODE_FOR_FLAG_FOREACH = 0x01,

	// NODE_FUNCTION
	NODE_FUNCTION_FLAG_GETTER   = 0x01,
	NODE_FUNCTION_FLAG_SETTER   = 0x02,
	NODE_FUNCTION_FLAG_OUT      = 0x04,
	NODE_FUNCTION_FLAG_VOID     = 0x08,
	NODE_FUNCTION_FLAG_NEVER    = 0x10,
	NODE_FUNCTION_FLAG_NOPARAMS = 0x20,
	NODE_FUNCTION_FLAG_OPERATOR = 0x40,

	// NODE_IDENTIFIER, NODE_VIDENTIFIER, NODE_STRING
	NODE_IDENTIFIER_FLAG_WITH  = 0x01,
	NODE_IDENTIFIER_FLAG_TYPED = 0x02,

	// NODE_IMPORT
	NODE_IMPORT_FLAG_IMPLEMENTS = 0x01,

	// NODE_PACKAGE
	NODE_PACKAGE_FLAG_FOUND_LABELS = 0x01,
	NODE_PACKAGE_FLAG_REFERENCED   = 0x02,

	// NODE_PARAM_MATCH
	NODE_PARAM_MATCH_FLAG_UNPROTOTYPED = 0x01,

	// NODE_PARAMETERS
	NODE_PARAMETERS_FLAG_CONST        = 0x001,
	NODE_PARAMETERS_FLAG_IN           = 0x002,
	NODE_PARAMETERS_FLAG_OUT          = 0x004,
	NODE_PARAMETERS_FLAG_NAMED        = 0x008,
	NODE_PARAMETERS_FLAG_REST         = 0x010,
	NODE_PARAMETERS_FLAG_UNCHECKED    = 0x020,
	NODE_PARAMETERS_FLAG_UNPROTOTYPED = 0x040,
	NODE_PARAMETERS_FLAG_REFERENCED   = 0x080,	// referenced from a parameter or a variable
	NODE_PARAMETERS_FLAG_PARAMREF     = 0x100,	// referenced from another parameter
	NODE_PARAMETERS_FLAG_CATCH        = 0x200,	// a parameter defined in a catch()

	// NODE_SWITCH
	// WARNING: the switch() integer is also used for the operator node type
	NODE_SWITCH_FLAG_DEFAULT = 0x80000000,	// we found a 'default:' label in that switch

	// NODE_VARIABLE (and NODE_VAR, NODE_PARAM)
	NODE_VAR_FLAG_CONST      = 0x01,
	NODE_VAR_FLAG_LOCAL      = 0x02,
	NODE_VAR_FLAG_MEMBER     = 0x04,
	NODE_VAR_FLAG_ATTRIBUTES = 0x08,
	NODE_VAR_FLAG_ENUM       = 0x02000000,	// there is a NODE_SET and it somehow needs to be copied
	NODE_VAR_FLAG_COMPILED   = 0x04000000,	// Expression() was called on the NODE_SET
	NODE_VAR_FLAG_INUSE      = 0x08000000,	// this variable was referenced
	NODE_VAR_FLAG_ATTRS      = 0x10000000,	// currently being read for attributes (to avoid loops)
	NODE_VAR_FLAG_DEFINED    = 0x20000000,	// was already parsed
	NODE_VAR_FLAG_DEFINING   = 0x40000000,	// currently defining, can't read
	NODE_VAR_FLAG_TOADD      = 0x80000000,	// to be added in the directive list

	unused_flag	// enables commas on all flags...
};


// the following is a list of all the possible attributes in our system
// (note that the bits may change in the future)
	// member visibility
static const unsigned long	NODE_ATTR_PUBLIC	= 0x00000001;
static const unsigned long	NODE_ATTR_PRIVATE	= 0x00000002;
static const unsigned long	NODE_ATTR_PROTECTED	= 0x00000004;
static const unsigned long	NODE_ATTR_INTERNAL	= 0x00000008;

	// function member type
static const unsigned long	NODE_ATTR_STATIC	= 0x00000010;
static const unsigned long	NODE_ATTR_ABSTRACT	= 0x00000020;
static const unsigned long	NODE_ATTR_VIRTUAL	= 0x00000040;
static const unsigned long	NODE_ATTR_ARRAY		= 0x00000080;

	// function/variable is defined in your system (execution env.)
	// you won't find a body for these functions; the variables
	// will likely be read-only
static const unsigned long	NODE_ATTR_INTRINSIC	= 0x00000100;

	// operator overload (function member)
	// Contructor -> another way to construct this type of objects
static const unsigned long	NODE_ATTR_CONSTRUCTOR	= 0x00000200;

	// function & member constrains
// CONST is not currently available as an attribute (see flags instead)
//static const unsigned long	NODE_ATTR_CONST		= 0x00001000;
static const unsigned long	NODE_ATTR_FINAL		= 0x00002000;
static const unsigned long	NODE_ATTR_ENUMERABLE	= 0x00004000;

	// conditional compilation
static const unsigned long	NODE_ATTR_TRUE		= 0x00010000;
static const unsigned long	NODE_ATTR_FALSE		= 0x00020000;
static const unsigned long	NODE_ATTR_UNUSED	= 0x00040000;	// if definition is used, error!

	// class attribute (whether a class can be enlarged at run time)
static const unsigned long	NODE_ATTR_DYNAMIC	= 0x00100000;

	// switch attributes
static const unsigned long	NODE_ATTR_FOREACH	= 0x01000000;
static const unsigned long	NODE_ATTR_NOBREAK	= 0x02000000;
static const unsigned long	NODE_ATTR_AUTOBREAK	= 0x04000000;

// The following is to make sure we never define the attributes more
// than once.
static const unsigned long	NODE_ATTR_DEFINED	= 0x80000000;




class String
{
public:
			String(void);
			String(const String& string);
			String(const char *str);
			~String();

	String&		operator = (const String& string);

	// Assume ISO-8859-1 as input
	String&		operator = (const char *str);

	bool		operator == (const String& string) const;
	bool		operator != (const String& string) const;
	bool		operator == (const char *str) const;
	bool		operator != (const char *str) const;
	String&		operator += (const String& string);
	// Assume ISO-8859-1 as input
	String&		operator += (const char *str);

	int		Compare(const String& string) const;
	int		Compare(const char *string) const;
	bool		IsEmpty(void) const;
	void		Empty(void);
	long		GetLength(void) const;
	const long *	Get(void) const;	// NOT NULL TERMINATED
	void		Set(const long *str, long size);
	void		AppendChar(long c);
	String&		AppendStr(const long *str, long len);
	int		GetUTF8Length(void) const;
	int		ToUTF8(char *mb, size_t& mb_len) const;		// -1 on error, 0 otherwise
	int		FromUTF8(const char *mb, size_t mb_len);	// -1 on error, 0 otherwise

	// WARNING: you need to 'delete [] str' of returned pointer
	char *		GetUTF8(void) const;

private:
	long		f_len;
	long		f_max;
	long *		f_str;
};

class Int64
{
public:
			Int64(void)
			{
				f_int = 0;
			}
			Int64(const Int64& src_int)
			{
				f_int = src_int.f_int;
			}

	Int64&		operator = (const Int64& src_int)
			{
				f_int = src_int.f_int;
				return *this;
			}

	int64_t		Get(void) const
			{
				return f_int;
			}
	void		Set(int64_t new_int)
			{
				f_int = new_int;
			}

private:
	int64_t		f_int;
};


class Float64
{
public:
			Float64(void)
			{
				f_float = 0;
			}
			Float64(const Float64& src_float)
			{
				f_float = src_float.f_float;
			}

	Float64&	operator = (const Float64& src_float)
			{
				f_float = src_float.f_float;
				return *this;
			}

	double		Get(void) const
			{
				return f_float;
			}
	void		Set(double new_float)
			{
				f_float = new_float;
			}

private:
	double		f_float;
};


class UserData
{
public:
			UserData(void)
			{
				f_size = 0;
				f_data = 0;
			}
			UserData(const UserData& src)
			{
				if(src.f_size > 0) {
					f_data = 0;
					New(src.f_size);
					memcpy(f_data, src.f_data, sizeof(int) * f_size);
				}
				else {
					f_size = 0;
					f_data = 0;
				}
			}
			~UserData()
			{
				delete [] f_data;
			}
	UserData&	operator = (const UserData& src)
			{
				delete [] f_data;
				if(src.f_size > 0) {
					f_data = 0;
					New(src.f_size);
					memcpy(f_data, src.f_data, sizeof(int) * f_size);
				}
				else {
					f_size = 0;
					f_data = 0;
				}
				return *this;
			}
	void		New(int size)
			{
				AS_ASSERT(size >= 0);
				delete [] f_data;
				if(size > 0) {
					f_data = new int[size];
					// by default we clear it
					memset(f_data, 0, sizeof(int) * size);
				}
				else {
					f_data = 0;
				}
				f_size = size;
			}

	int		Size(void) const
			{
				return f_size;
			}
	int *		Buffer(void)
			{
				// we want to make sure that each call
				// to Buffer doesn't require this test
				AS_ASSERT(f_data != 0);
				return f_data;
			}
	const int *	Buffer(void) const
			{
				// we want to make sure that each call
				// to Buffer doesn't require this test
				AS_ASSERT(f_data != 0);
				return f_data;
			}

private:
	int		f_size;
	int *		f_data;
};


struct Data
{
			Data(void)
			{
				f_type = NODE_UNKNOWN;
			}
	void		Clear(void)
			{
				f_type = NODE_UNKNOWN;
				f_int.Set(0);
				f_float.Set(0.0);
				f_str.Empty();
			}
	void		Display(FILE *out) const;
	const char *	GetTypeName(void) const;

	// basic conversions
	bool		ToBoolean(void);
	bool		ToNumber(void);
	bool		ToString(void);

	node_t		f_type;
	Int64		f_int;
	Float64		f_float;
	String		f_str;
	UserData	f_user_data;
};



class Input;
class NodePtr
{
public:
	enum link_t {
		LINK_INSTANCE = 0,
		LINK_TYPE,
		LINK_ATTRIBUTES,	// this is the list of identifiers

		LINK_max,

		LINK_GOTO_EXIT = LINK_INSTANCE,
		LINK_GOTO_ENTER = LINK_TYPE,

		LINK_end
	};

				NodePtr(void)
				{
					f_node = 0;
				}
				NodePtr(const NodePtr& node);
				~NodePtr();

	NodePtr&		operator = (const NodePtr& node);

	void			SetInputInfo(const Input *input);
	void			CopyInputInfo(const NodePtr& node);
	long			GetPage(void) const;
	long			GetPageLine(void) const;
	long			GetParagraph(void) const;
	long			GetLine(void) const;
	const String&		GetFilename(void) const;

	void			SetNode(const Node *node);
	void			SetNode(const NodePtr& node);
	void			CreateNode(node_t type = NODE_UNKNOWN);
	NodePtr&		Clone(const NodePtr& source);
	void			ClearNode(void);
	bool			HasNode(void) const
				{
					return f_node != 0;
				}
	bool			SameAs(const NodePtr& node) const
				{
					return f_node == node.f_node;
				}

	Data&			GetData(void) const;
	void			SetData(const Data& data);
	unsigned long		GetAttrs(void) const;	// see NODE_ATTR_...
	void			SetAttrs(unsigned long attrs);
	bool			HasSideEffects(void) const;

	bool			IsLocked(void) const;
	void			Lock(void);
	void			Unlock(void);
	void			SetOffset(int offset);
	int			GetOffset(void) const;
	void			ReplaceWith(NodePtr& new_node);
	void			DeleteChild(int index);
	void			AddChild(NodePtr& child);
	void			InsertChild(int idx, NodePtr& child);
	void			SetChild(int index, NodePtr& child);
	int			GetChildCount(void) const;
	NodePtr&		GetChild(int index) const;
	void			SetParent(Node *parent);
	NodePtr&		GetParent(void);
	void			SetLink(link_t index, NodePtr& link);
	NodePtr&		GetLink(link_t index);

	void			AddVariable(NodePtr& variable);
	int			GetVariableCount(void) const;
	NodePtr&		GetVariable(int index) const;

	void			AddLabel(NodePtr& label);
	int			GetLabelCount(void) const;
	NodePtr&		GetLabel(int index) const;
	NodePtr&		FindLabel(const String& name) const;

	void			Display(FILE *out, int indent = 0, NodePtr *parent = 0, char c = '-') const;
	void			DisplayPtr(FILE *out) const;

	const char *		OperatorToString(void);
	node_t			StringToOperator(void);

private:
	Node *			f_node;
};


class NodeLock
{
public:
				NodeLock(NodePtr& node)
				{
					f_node = node;
					if(f_node.HasNode()) {
						f_node.Lock();
					}
				}
				~NodeLock()
				{
					if(f_node.HasNode()) {
						f_node.Unlock();
					}
				}

	// premature unlocking
	void			Unlock(void)
				{
					if(f_node.HasNode()) {
						f_node.Unlock();
						f_node.ClearNode();
					}
				}

private:
	NodePtr			f_node;
};



// ErrorStream handling object; you can derive from this
// class to redirect the errors to a different
// output (by default it goes to stderr).
// For that purpose, overload the Error() function.
// Within your function, use the IntGetFilename()
// and IntLine() functions to get the filename
// and line where the error occured.
//
// NOTE: The Input class derives from this class
//	 so it can handle the errors at once.
class ErrorStream
{
public:
				ErrorStream(void);
	virtual			~ErrorStream();

	// by default, write in stderr; overload to send errors in
	// your console, etc.
	virtual void		Error(err_code_t err_code, const char *message);

	// We call one of these functions which transform their
	// parameters (format + params) in a single easy to use
	// string sent to Error().
	// You don't overload these functions.
	void			ErrMsg(err_code_t err_code, const char *format, ...);
	void			ErrMsg(err_code_t err_code, const char *format, va_list ap);

	// the following replaces String pointers by their UTF-8 const char *
	// whenever the format includes a %S -- this makes it work on all
	// systems without jungling with wide characters too much
	// also, we support %N for nodes in which case we print out the
	// filename and line of the given node
	void			ErrStrMsg(err_code_t err_code, const char *format, ...);
	void			ErrStrMsg(err_code_t err_code, const char *format, va_list ap);

	// errors which should use the node information for the
	// filename & line number (instead of GetFilename() and Line())
	void			ErrMsg(err_code_t err_code, const NodePtr& node, const char *format, ...);
	void			ErrStrMsg(err_code_t err_code, const NodePtr& node, const char *format, ...);

	long			ErrCount(void) const
				{
					return f_errcnt;
				}

	virtual const char *	GetFilename(void) const;
	virtual long		Line(void) const;

protected:
	const String&		IntGetFilename(void) const;
	long			IntLine(void) const;

private:
	long			f_errcnt;
	NodePtr			f_node;
	mutable String		f_filename;	// IntGetFilename() must return a string...
};



// I/O interface that YOU have to derive from so the
// parser can read the input data from somewhere
// You need to implement the GetC() function. You can
// also overload the Error() function so it prints
// the errors in a console of your choice.
// The GetFilename() is used by the default Error()
// function. It is used to generate an error like gcc.
// That function returns "asc" by default.
//
// Two examples are available below. One reads a USC-4
// formatted file and the other reads a string.
class Input : public ErrorStream
{
public:
	enum {
		AS_EOF = -1
	};

				Input(void);
	virtual			~Input(void);

	virtual long		GetC(void) = 0;

	void			ResetCounters(long line = 1)
				{
					f_page = 1;
					f_page_line = 1;
					f_paragraph = 1;
					f_line = line;
				}

	void			NewPage(void)
				{
					++f_page;
					f_page_line = 1;
					f_paragraph = 1;
				}
	void			NewParagraph(void)
				{
					++f_paragraph;
				}
	void			NewLine(void)
				{
					++f_page_line;
					++f_line;
				}

	long			Page(void) const
				{
					return f_page;
				}
	long			PageLine(void) const
				{
					return f_page_line;
				}
	long			Paragraph(void) const
				{
					return f_paragraph;
				}
	virtual long		Line(void) const
				{
					return f_line;
				}

private:
	long			f_page;
	long			f_page_line;
	long			f_paragraph;
	long			f_line;
};







class FileInput : public Input
{
public:
				FileInput(void);
	virtual			~FileInput();
	virtual const char *	GetFilename(void) const;
	bool			StandardInput(void);
	bool			Open(const char *filename);
	void			Close(void);
	virtual long		GetC(void);
	void			SetOriginalFilename(const char *original_filename);

protected:
	char *			f_filename;
	char *			f_original_filename;
	FILE *			f_file;
};


class FileUCS32Input : public FileInput
{
public:
	virtual long		GetC(void);
};



// Feel free to derive from this class if you want to
// return a filename to the GetFilename() function.
class StringInput : public Input
{
public:
				StringInput(void);
	virtual			~StringInput();

	void			Set(const long *str, long size, unsigned long line);
	virtual long		GetC(void);

private:
	int			f_pos;
	String			f_str;
};



// Options you can tweak so the compiler reacts in a different
// manner in different situations (for instance, the \e escape
// sequence can be used to generate the escape character whenever
// the extended escape sequences is set to 1).
enum option_t
{
	AS_OPTION_UNKNOWN = 0,
	AS_OPTION_DEBUG,
	AS_OPTION_DEBUG_LEXER,
	AS_OPTION_EXTENDED_ESCAPE_SEQUENCES,
	AS_OPTION_EXTENDED_OPERATORS,
	AS_OPTION_EXTENDED_STATEMENTS,
	AS_OPTION_OCTAL,
	AS_OPTION_STRICT,
	AS_OPTION_TRACE,
	AS_OPTION_TRACE_TO_OBJECT,

	AS_OPTION_max
};

class Options
{
public:
				Options(void)
				{
					memset(f_options, 0, sizeof(f_options));

					// we're always in strict mode
					f_options[AS_OPTION_STRICT] = 1;
				}

	void			SetOption(option_t option, long value)
				{
					AS_ASSERT(option < AS_OPTION_max);

					// we're always in strict mode
					if(option == AS_OPTION_STRICT) {
						value = 1;
					}
					f_options[option] = value;
				}
	long			GetOption(option_t option)
				{
					AS_ASSERT(option < AS_OPTION_max);

					return f_options[option];
				}

private:
	long			f_options[AS_OPTION_max];
};




// The parser class is mostly hidden to you.
// You can't derive from it. You call the CreateParser() to use it.
// Once you are finished with the parser, delete it.
// Note that deleting the parser doesn't delete the nodes and thus
// you can work with the tree even after you deleted the parser.
//
// You use like this:
//
//	using namespace sswf::as;
//	MyInput input;
//	Parser *parser = Parser::CreateParser();
//	parser->SetInput(input);
//	// it is optional to set the options
//	parser->SetOptions(options);
//	NodePtr root = parser->Parse();
//
// NOTE: the input and options are NOT copied, a pointer to these
// object is saved in the parser. Delete the Parser() before you
// delete them. Also, this means you can change the options as the
// parsing goes on (i.e. usually this happens in Input::Error().).
class Parser
{
public:
	static Parser *		CreateParser(void);
	static const char *	Version(void);

	virtual void		SetInput(Input& input) = 0;
	virtual void		SetOptions(Options& options) = 0;
	virtual NodePtr&	Parse(void) = 0;
};



// Once a program was parsed, you need to compile it. This
// mainly means resolving the references (i.e. identifiers)
// which may generate the loading of libraries specified in
// import instructions (note that some import instructions
// are automatic for the global and native environments.)
//
// The code, after you ran the parser looks like this:
//
//	Compiler *compiler = Compiler::CreateCompiler();
//	// this is the same options as for the parser
//	compiler->SetOptions(options);
//	error_count = compiler->Compile(root);
//
// The Compile() function returns the number of errors
// encountered while compiling. The root parameter is
// what was returned by the Parse() function of the
// Parser object.
class Compiler
{
public:
	// NOTE: to be thread safe, this call should be
	//	 protected so only one thread can call it at
	//	 a time (or create a compiler before any thread)
	static Compiler *	CreateCompiler(void);
	static const char *	Version(void);

	virtual void		SetErrorStream(ErrorStream& error_stream) = 0;
	virtual void		SetOptions(Options& options) = 0;
	virtual int		Compile(NodePtr& tree) = 0;
};




// Finally, once the program was parsed and then compiled
// one usually wants to optimize it. This means removing
// all the possible expressions and statements which can
// be removed to make the code more efficient. The
// optimizations applied can be tweaked using the options.
//
// The code, after you ran the compiler looks like this:
//
//	Optimizer *optimizer = Optimizer::CreateOptimizer();
//	// this is the same options as for the parser
//	optimize->SetOptions(options);
//	optimize->Optimize(root);
//
// The Optimize() function goes through the list of
// nodes defined in the root parameter and it tries to
// remove all possible expressions and functions which
// will have no effect in the final output (certain things,
// such as x + 0, are not removed since it has an effect!).
// The root parameter is what was returned by the Parse()
// function of the Parser object.
//
// Note that it is expected that you first Compile()
// the nodes, but it is possible to call the optimizer
// without first running any compilation.
class Optimizer
{
public:
	static Optimizer *	CreateOptimizer(void);
	static const char *	Version(void);

	virtual void		SetErrorStream(ErrorStream& error_stream) = 0;
	virtual void		SetOptions(Options& options) = 0;
	virtual int		Optimize(NodePtr& tree) = 0;
	virtual	int		GetLastLabel(void) const = 0;
	virtual void		SetFirstLabel(int label) = 0;
};




// Now you're on your own! You have a perfect tree
// and you can generate valid code from it...
//
// Each node has a Data structure, a set of children
// node and a set of attribute flags (WARNING:
// the attributes are computed by the compiler; you
// need to call Compile() with a compiler to get
// the attributes).




};	// namespace as
};	// namespace sswf
#endif			/* #ifndef LIBSSWF_AS_H */
