%{	/* sswf_grammar.y -- written by Alexis WILKE for Made to Order Software Corp. (c) 2002-2009 */
	/* grammar parser for the SWF scripting tool */

/*

Copyright (c) 2002-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.h"

#define	YYPARSE_PARAM		result
static	void			yyerror(const char *msg);



%}



%union
{
	struct node_t		*node;
	enum node_type_t	type;		/* for UNIT */
	int			unused;
};


/* terminals */
%token	<node> STRING VALUE IDENTIFIER OBJECT
%token	<type> UNIT_ANGLE UNIT_COLOR UNIT_RATIO UNIT_SIZE UNIT_SPEED UNIT_TIME
%token	<unused> FOR IF ACTION_SCRIPT ELSE


/* all these yacc expressions are nodes */
%type	<node> start expr_list expr group_expr block_expr
%type	<node> object opt_name opt_else
%type	<node> object_definition definition



/* expressions are like in C/C++ */
%left	'='
%left	'?' ':'
%left	RANGE
%left	LOGICAL_OR
%left	LOGICAL_XOR
%left	LOGICAL_AND
%left	'|'
%left	'^'
%left	'&'
%left	EQUAL NOT_EQUAL '<' LESS_EQUAL '>' GREATER_EQUAL
%right	SHIFT_LEFT SHIFT_RIGHT SHIFT_RIGHT_UNSIGNED ROTATE_LEFT ROTATE_RIGHT
%left	MIN_OP MAX_OP
%left	'+' '-'
%left	'*' '/' '%'
%right	POWER
%right	POS NEG '!' '~'
%left	'['
%right	'.'
%left	UNIT_ANGLE
%left	UNIT_COLOR
%left	UNIT_RATIO
%left	UNIT_SIZE
%left	UNIT_SPEED
%left	UNIT_TIME



%%

/**** START ****/
start:
	  expr_list opt_end
	  {
	  	/* the returned value is the result */
		* (struct node_t **) result = $1;
	  }
	;

opt_end:
	  /* empty */
	| colons
	;

colons:
	  ';'
	| colons ';'
	;

/**** EXPRESSIONS ****/
expr_list:
	  block_expr
	  {
	  	$$ = $1;
	  }
	| expr_list colons block_expr
	  {
		$$ = $1;
		node_link_tail($$, $3);
	  }
	;

block_expr:
	  expr
	  {
	  	$$ = $1;
	  }
	| '{' expr_list '}'
	  {
		/* this is equivalent to a LIST without a name */
		$$ = node_alloc(NODE_TYPE_OBJECT, NODE_SUBTYPE_BLOCK, @1.first_line);
		/* left = NULL -- always unamed object */
		node_link_right($$, $2);
		node_link_object($$);
	  }
	;

group_expr:
	  expr
	  {
		$$ = $1;
	  }
	| group_expr ',' expr
	  {
		$$ = $1;
		node_link_tail($$, $3);
	  }
	;

expr:
	  expr UNIT_ANGLE		%prec UNIT_ANGLE
	  {
		$$ = $1;
		$$->unit[NODE_UNIT_ANGLE] = $2;
	  }
	| expr UNIT_COLOR		%prec UNIT_COLOR
	  {
		$$ = $1;
		$$->unit[NODE_UNIT_COLOR] = $2;
	  }
	| expr UNIT_RATIO		%prec UNIT_RATIO
	  {
		$$ = $1;
		$$->unit[NODE_UNIT_RATIO] = $2;
	  }
	| expr UNIT_SIZE		%prec UNIT_SIZE
	  {
		$$ = $1;
		$$->unit[NODE_UNIT_SIZE] = $2;
	  }
	| expr UNIT_SPEED		%prec UNIT_SPEED
	  {
		$$ = $1;
		$$->unit[NODE_UNIT_SPEED] = $2;
	  }
	| expr UNIT_TIME		%prec UNIT_TIME
	  {
		$$ = $1;
		$$->unit[NODE_UNIT_TIME] = $2;
	  }
	| expr '.' expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_FIELD, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr '[' group_expr ']'
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_SUBSCRIPT, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| '$' '(' group_expr ')'
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_EXPAND, @1.first_line);
		node_link_left($$, $3);
	  }
	| '(' group_expr ')'
	  {
		if($2->next != NULL) {
			/* Check whether the expr returns a list of nodes...
			 * This happens when a comma is used between multiple expressions.
			 * We need to have a 'GROUP' node because all the children are affected
			 * by the following token(s) such as a UNIT.
			 */
			$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_GROUP, @1.first_line);
			node_link_left($$, $2);
		}
		else {
			/* no worry, we don't need to add another node */
		  	$$ = $2;
		}
	  }
	| '+' expr		%prec POS
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_IDENTITY, @1.first_line);
		node_link_left($$, $2);
	  }
	| '-' expr		%prec NEG
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_NEGATE, @1.first_line);
		node_link_left($$, $2);
	  }
	| '!' expr		%prec '!'
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_LOGICAL_NOT, @1.first_line);
		node_link_left($$, $2);
	  }
	| '~' expr		%prec '~'
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_NOT, @1.first_line);
		node_link_left($$, $2);
	  }
	| IDENTIFIER '(' ')'
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_FUNCTION, @1.first_line);
		node_link_left($$, $1);
		/* right = NULL -- no parameters */
	  }
	| IDENTIFIER '(' group_expr ')'
	  {
	  	$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_FUNCTION, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr POWER expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_POWER, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr '*' expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_MULTIPLY, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr '/' expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_DIVIDE, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr '%' expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_MODULO, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr '+' expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_ADD, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr '-' expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_SUBTRACT, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr MIN_OP expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_MINIMUM, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr MAX_OP expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_MAXIMUM, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr SHIFT_LEFT expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_SHIFT_LEFT, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr SHIFT_RIGHT expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_SHIFT_RIGHT, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr SHIFT_RIGHT_UNSIGNED expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_SHIFT_RIGHT_UNSIGNED, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr ROTATE_LEFT expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_ROTATE_LEFT, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr ROTATE_RIGHT expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_ROTATE_RIGHT, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr EQUAL expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_EQUAL, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr NOT_EQUAL expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_NOT_EQUAL, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr '<' expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_LESS, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr LESS_EQUAL expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_LESS_EQUAL, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr '>' expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_GREATER, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr GREATER_EQUAL expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_GREATER_EQUAL, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr '&' expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_AND, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr '^' expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_XOR, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr '|' expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_OR, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr LOGICAL_AND expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_LOGICAL_AND, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr LOGICAL_XOR expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_LOGICAL_XOR, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr LOGICAL_OR expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_LOGICAL_OR, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr RANGE expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_RANGE, @1.first_line);
		if($1->type == NODE_TYPE_OPERATOR && $1->sub_type == NODE_SUBTYPE_RANGE) {
			fprintf(stderr, "ERROR: at line #%d: can't have two ranges defined one after another.\n", @2.first_line);
			return -1;
		}
		if($3->type == NODE_TYPE_OPERATOR && $3->sub_type == NODE_SUBTYPE_RANGE) {
			fprintf(stderr, "ERROR: at line #%d: can't have two ranges defined one after another.\n", @2.first_line);
			return -1;
		}
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| expr '?' group_expr ':' expr
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_SELECT, @1.first_line);
		node_link_select($$, $1);
		node_link_left($$, $3);
		node_link_right($$, $5);
	  }
	| IF '(' group_expr ')' '{' expr_list opt_end '}' opt_else
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_IF, @1.first_line);
		node_link_select($$, $3);
		node_link_left($$, $6);
		node_link_right($$, $9);	/* may be a null pointer */
	  }
	| IDENTIFIER '=' expr
	  {
		$$ = node_alloc(NODE_TYPE_OBJECT, NODE_SUBTYPE_VARIABLE, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
		node_link_object($$);
	  }
	| object
	  {
	  	$$ = $1;
	  }
	| IDENTIFIER
	  {
	  	$$ = $1;
	  }
	| VALUE
	  {
	  	$$ = $1;
	  }
	| STRING
	  {
	  	$$ = $1;
	  }
	;


opt_else:
	  /* empty */
	  {
	  	$$ = NULL;
	  }
	| ELSE '{' expr_list opt_end '}'
	  {
		$$ = $3;
	  }
	| ELSE IF '(' group_expr ')' '{' expr_list opt_end '}' opt_else
	  {
		$$ = node_alloc(NODE_TYPE_OPERATOR, NODE_SUBTYPE_IF, @1.first_line);
		node_link_select($$, $4);
		node_link_left($$, $7);
		node_link_right($$, $10);	/* may be a null pointer */
	  }
	;

/**** OBJECTS ****/
object:
	  OBJECT opt_name '{' object_definition opt_end '}'
	  {
		$$ = $1;
		/* keep only non-empty names, it simplifies some other parts of the code! */
		if($2 != NULL && $2->string != NULL && *$2->string != '\0') {
			node_link_left($$, $2);
		}
		node_link_right($$, $4);
		node_link_object($$);
	  }
	| OBJECT opt_name '{' opt_end '}'
	  {
		$$ = $1;
		if($2 != NULL && $2->string != NULL && *$2->string != '\0') {
			node_link_left($$, $2);
		}
		/* right = NULL -- no data for this object */
		node_link_object($$);
	  }
	| OBJECT opt_name
	  {
		$$ = $1;
		if($2 != NULL && $2->string != NULL && *$2->string != '\0') {
			node_link_left($$, $2);
		}
		/* right = NULL -- no data for this object */
		node_link_object($$);
	  }
	| ACTION_SCRIPT opt_name '{'
		{ sswf_read_actionscript(1); }
		STRING
		{ sswf_read_actionscript(0); }
		'}'
	  {
	  	struct node_t	*n;

		/*
		 * Note: we use the 1st line of the STRING so we can generate
		 * proper errors in the Javascript compiler.
		 */
		$$ = node_alloc(NODE_TYPE_OBJECT, NODE_SUBTYPE_ACTION_SCRIPT, @5.first_line);
		if($2 != NULL && $2->string != NULL && *$2->string != '\0') {
			node_link_left($$, $2);
		}
	  	n = node_alloc(NODE_TYPE_DEFINITION, NODE_SUBTYPE_UNKNOWN, @1.first_line);
		/* left = NULL -- no label */
		node_link_right(n, $5);
		node_link_right($$, n);
		node_link_object($$);
	  }
	;


opt_name:
	  /* empty */
	  {
		$$ = NULL;
	  }
	| IDENTIFIER
	  {
	  	$$ = $1;
	  }
	| STRING
	  {
	  	$$ = $1;
	  }
	;




/* object definitions are specific to the object sub-type but they can all be parsed the same way
   we will generate errors later when we transform the data to the final structures */
object_definition:
	  definition
	  {
	  	$$ = $1;
	  }
	| object_definition colons definition
	  {
		$$ = $1;
		node_link_tail($$, $3);
	  }
	;


definition:
	  IDENTIFIER ':' group_expr
	  {
	  	$$ = node_alloc(NODE_TYPE_DEFINITION, NODE_SUBTYPE_UNKNOWN, @1.first_line);
		node_link_left($$, $1);
		node_link_right($$, $3);
	  }
	| group_expr
	  {
	  	$$ = node_alloc(NODE_TYPE_DEFINITION, NODE_SUBTYPE_UNKNOWN, @1.first_line);
		/* left = NULL -- no label */
		node_link_right($$, $1);
	  }
	| FOR '(' IDENTIFIER '=' expr ';' expr ';' expr ')' '{' object_definition opt_end '}'
	  {
		struct node_t	*for_obj, *var, *result, *block;

		block = node_alloc(NODE_TYPE_OBJECT, NODE_SUBTYPE_BLOCK, @1.first_line);
		/* node_link_left() -- no name/label */
		node_link_right(block, $12);

		var = node_alloc(NODE_TYPE_OBJECT, NODE_SUBTYPE_VARIABLE, @1.first_line);
		result = node_alloc(NODE_TYPE_INTEGER, NODE_SUBTYPE_UNKNOWN, @1.first_line);
		node_link_left(var, $3);
		node_link_right(var, result);	/* the result of different expr's */
		node_link_object(var);

		node_link_tail(var, $5);
		node_link_tail(var, $7);
		node_link_tail(var, $9);
		node_link_tail(var, block);

		for_obj = node_alloc(NODE_TYPE_OBJECT, NODE_SUBTYPE_FOR, @1.first_line);
		/*node_link_left(for_obj, ...) -- no name for a loop - should we use the variable name? */
		node_link_right(for_obj, var);
		/*node_link_object(for_obj);	-- this makes this software crash and it's not necessary (no name!) */

	  	$$ = node_alloc(NODE_TYPE_DEFINITION, NODE_SUBTYPE_UNKNOWN, @1.first_line);
		/* left = NULL -- no label */
		node_link_right($$, for_obj);
	  }
	;


%%


void yyerror(const char *msg)
{
	/* TODO: enhance this message! */
	printf("YACC ERROR at line #%d in file \"%s\": %s.\n", yylloc.first_line, lex_filename, msg);
}




