%{
/* UPE compiler - parser
 * (c)2000 Stepan Roh
 * see license.txt for copying
 */
 
#include "upecc.h"
#include "upecc_tree.h"
#include "upe.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
int yylex(void);
int yyerror(char *);
void err_no_mem (void);
int add_include (char *name, FILE *f);
int del_include (void);
extern FILE *yyin;
extern char err_tmp[1024];

%}

%union{
  char *id;
  upe_number_t *num;
  tree_node_t *node;
  int inum;
}

%token          <num> T_NUM
%token		<> T_BYTE_SIZE T_BYTE_ORDER T_NUM_LEN T_INCLUDE
%token		<> T_REGISTER T_INSTRUCTIONS T_IP T_AS T_ARRAY
%token		<> T_INC T_DEC T_PLUS T_MINUS
%token		<> T_ASSIGN_PLUS T_ASSIGN_MINUS
%token		<> T_OPEN_BRACKET T_CLOSE_BRACKET T_OPEN_PARENT T_CLOSE_PARENT
%token		<> T_SEMICOLON T_COLON T_SEP T_QUOTE T_COMMA
%token		<> T_VAR T_BREAK T_FUNCTION T_CONTINUE
%token		<> T_EQUALS T_NON_EQUALS T_LESS_THAN T_LESS_OR_EQUAL T_GREATER_THAN T_GREATER_OR_EQUAL
%token		<> T_ASSIGN T_NEG T_AND T_OR T_BITAND T_BITOR T_BITXOR T_BITNEG
%token		<> T_ASSIGN_BITAND T_ASSIGN_BITOR T_ASSIGN_BITXOR
%token		<> T_CONSTANT T_RETURN T_EXTERNAL
%token		<> T_SHIFT_LEFT T_SHIFT_RIGHT
%token		<> T_ASSIGN_SHIFT_LEFT T_ASSIGN_SHIFT_RIGHT
%token		<> T_WHILE T_DO T_IF T_ELSE T_FOR
%token		<id> T_ID T_STR T_BYTE_ORDER_ID

%right		T_ASSIGN T_ASSIGN_BITAND T_ASSIGN_BITOR T_ASSIGN_BITXOR T_ASSIGN_SHIFT_LEFT T_ASSIGN_SHIFT_RIGHT T_ASSIGN_PLUS T_ASSIGN_MINUS
%right		T_QUOTA T_COLON
%left		T_OR
%left		T_AND
%left		T_BITOR
%left		T_BITXOR
%left		T_BITAND
%left		T_EQUALS T_NON_EQUALS
%left		T_LESS_THAN T_LESS_OR_EQUAL T_GREATER_THAN T_GREATER_OR_EQUAL
%left		T_SHIFT_LEFT T_SHIFT_RIGHT
%left           T_PLUS T_MINUS
%right		T_NEG T_BITNEG T_INC T_DEC
%left		T_OPEN_PARENT T_CLOSE_PARENT

%type		<node> expr
%type		<id> const_name
%type		<id> const_name_decl
%type		<id> var_name
%type		<inum> reg_ip
%type		<node> code
%type		<node> fn_def_args
%type		<node> fn_def_args_next
%type		<node> fn_def_args_n2
%type		<node> fn_def_arg
%type		<node> fn_call
%type		<node> fn_call_args
%type		<node> fn_call_args_next
%type		<node> fn_call_args_n2
%type		<node> fn_call_arg
%type		<id> var_name_decl
%type		<node> statement
%type		<node> bit_range
%type		<node> var_decl
%type		<node> var_decls
%type		<node> var_decls_n
%type		<node> if_branch_else
%type		<node> for_expr
%type		<node> instr_mask instr_masks num_quote num_bit_range
%type		<node> instr_as instr_code instr_code_block instr_code_blocks

%%

input:          /* empty */
	| block input
;

block:	inits
	| constants
	| regs
	| funcs
	| instructions
;

inits:	T_BYTE_SIZE expr	{ upe_number_t *n;
			  if (eval_tree ($2, &n)) {
			    yyerror ("value of byte_size can't be evaluated in compile-time");
			  } else {
			    upe_byte_size = upecc_byte_size = upe_getnum (n);
			    destroy_tree ($2);
			    upe_donenum (n);
			  }
			}
	| T_NUM_LEN expr	{ upe_number_t *n;
			  if (eval_tree ($2, &n)) {
			    yyerror ("value of numbers_len can't be evaluated in compile-time");
			  } else {
			    upecc_num_len = upe_getnum (n);
			    destroy_tree ($2);
			    upe_donenum (n);
			  }
			}
	| T_BYTE_ORDER T_BYTE_ORDER_ID byte_order_next {
			  if (upe_add_byte_order ($2)) {
			    yyerror ("internal compiler error: BYTE_ORDER");
			  }
			  free ($2);
			}
;

byte_order_next:	/* empty */	{}
			| T_COMMA T_BYTE_ORDER_ID byte_order_next {
			  if (upe_add_byte_order ($2)) {
			    yyerror ("internal compiler error: BYTE_ORDER");
			  }
			  free ($2);
			}
;

constants:	T_CONSTANT const_name_decl expr T_COLON expr {
			  int len; id_item_t *i; upe_number_t *n, *val;
			  
			  if (lookup_id_table (global_id_table, $2)) {
			    sprintf (err_tmp, "identifier `%s' already declared", $2);
			    yyerror (err_tmp);
			    break;
			  }
			  if (eval_tree ($3, &val)) {
			    yyerror ("value of constant can't be evaluated in compile-time");
			    break;
			  }
			  destroy_tree ($3);
			  if (eval_tree ($5, &n)) {
			    yyerror ("value of constant can't be evaluated in compile-time");
			    break;
			  }
			  len = upe_getnum (n);
			  upe_donenum (n);
			  destroy_tree ($5);
  
                          if (!(i = malloc (sizeof (id_item_t)))) err_no_mem ();
                          i->type = ID_CONST;
                          i->x.num = val;
                          if (add_id_table (global_id_table, $2, i, destroy_id_item)) err_no_mem ();
                          free ($2);
			}
;

const_name:	T_ID	{ $$ = $1; }
;		

const_name_decl:	const_name	{ $$ = $1; }
;

regs:	T_REGISTER T_ID T_COLON expr reg_ip {
			  int len; id_item_t *i; upe_number_t *n;
			  
			  if (lookup_id_table (global_id_table, $2)) {
			    sprintf (err_tmp, "identifier `%s' already declared in this scope", $2);
			    yyerror (err_tmp);
			    break;
			  }
			  if (eval_tree ($4, &n)) {
			    yyerror ("value can't be evaluated in compile-time");
			    break;
			  }
			  len = upe_getnum (n);
			  upe_donenum (n);
			  destroy_tree ($4);
  
			  if ($5) upecc_ip_reg = strdup ($2);
			  
                          if (!(i = malloc (sizeof (id_item_t)))) err_no_mem ();
                          i->type = ID_REG;
                          i->x.len = len;
                          if (add_id_table (global_id_table, $2, i, destroy_id_item)) err_no_mem ();
                          free ($2);
                          
			}
;

reg_ip:	/* empty */	{ $$ = 0; }
	| T_IP		{ $$ = 1; }
;

funcs:	T_FUNCTION fn_def	{}
	| T_EXTERNAL T_FUNCTION ext_fn_def {}
;

ext_fn_def:	T_ID T_OPEN_PARENT fn_def_args T_CLOSE_PARENT T_SEMICOLON {
			  id_item_t *i, *i2; tree_node_t *n, *nn;
			  
			  if (lookup_id_table (global_id_table, $1)) {
			    sprintf (err_tmp, "identifier `%s' already declared", $1);
			    yyerror (err_tmp);
			    break;
			  }
  
                          if (!(i = malloc (sizeof (id_item_t)))) err_no_mem ();
                          i->type = ID_EXT_FN;
                          i->x.fn.code = NULL;
                          i->x.fn.args_num = 0;
                          if (!(i->x.fn.id_table = create_id_table ())) err_no_mem ();
                          n = $3;
                          while (n) {
                            nn = n->next;
                            if (lookup_id_table (i->x.fn.id_table, n->x.id)) {
			      sprintf (err_tmp, "identifier `%s' already declared in this scope", n->x.id);
			      yyerror (err_tmp);
                              break;
                            }
                            if (!(i2 = malloc (sizeof (id_item_t)))) err_no_mem ();
                            i2->type = ID_VAR;
                            if (add_id_table (i->x.fn.id_table, n->x.id, i2, destroy_id_item)) err_no_mem ();
                            n = nn;
                            i->x.fn.args_num++;
                          }
                          i->x.fn.args = $3;
                          if (add_id_table (global_id_table, $1, i, destroy_id_item)) err_no_mem ();
                          free ($1);
			}
;

fn_def:	T_ID T_OPEN_PARENT fn_def_args T_CLOSE_PARENT T_OPEN_BRACKET var_decls code T_CLOSE_BRACKET {
			  id_item_t *i, *i2; tree_node_t *n, *nn;
			  
			  if (lookup_id_table (global_id_table, $1)) {
			    sprintf (err_tmp, "identifier `%s' already declared", $1);
			    yyerror (err_tmp);
			    break;
			  }
  
                          if (!(i = malloc (sizeof (id_item_t)))) err_no_mem ();
                          i->type = ID_FN;
                          i->x.fn.args_num = 0;
                          if (!(i->x.fn.id_table = create_id_table ())) err_no_mem ();
                          n = $3;
                          while (n) {
                            nn = n->next;
                            if (lookup_id_table (i->x.fn.id_table, n->x.id)) {
			      sprintf (err_tmp, "identifier `%s' already declared in this scope", n->x.id);
			      yyerror (err_tmp);
                              break;
                            }
                            if (!(i2 = malloc (sizeof (id_item_t)))) err_no_mem ();
                            i2->type = ID_VAR;
                            if (add_id_table (i->x.fn.id_table, n->x.id, i2, destroy_id_item)) err_no_mem ();
                            n = nn;
                            i->x.fn.args_num++;
                          }
                          i->x.fn.args = $3;
                          n = $6;
                          while (n) {
                            nn = n->next;
                            if (lookup_id_table (i->x.fn.id_table, n->x.var_decl.id)) {
			      sprintf (err_tmp, "identifier `%s' already declared in this scope", n->x.var_decl.id);
			      yyerror (err_tmp);
                              break;
                            }
                            if (!(i2 = malloc (sizeof (id_item_t)))) err_no_mem ();
                            i2->type = ID_VAR;
                            if (add_id_table (i->x.fn.id_table, n->x.var_decl.id, i2, destroy_id_item)) err_no_mem ();
                            n = nn;
                          }
                          if ($6) {
                            i->x.fn.code = $6;
                            n = $6->next; nn = $6;
                            while (n) {
                              nn = n;
                              n = n->next;
                            }
                            nn->next = $7;
                            if ($7) i->x.fn.code->temp_vars_num = $7->temp_vars_num;
                          } else 
                            i->x.fn.code = $7;
                          if (add_id_table (global_id_table, $1, i, destroy_id_item)) err_no_mem ();
                          free ($1);
			}
;

fn_def_args:	/* empty */	{ $$ = NULL; }
		| fn_def_args_n2	{ $$ = $1; }
;

fn_def_args_next:	/* empty */	{ $$ = NULL; }
			| T_COMMA fn_def_args_n2	{ $$ = $2; }
;

fn_def_args_n2:	fn_def_arg fn_def_args_next {
			  $$ = $1;
			  $1->next = $2;
			}
;

fn_def_arg:	var_name_decl	{
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_VAR;
			  t->x.id = $1;
			  if (!t->x.id) err_no_mem ();
			  $$ = t;
			}
;

instructions:	T_INSTRUCTIONS instr_mask instr_masks instr_code	{
			  tree_node_t *t, *t2, *tt;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_INSTRUCTION;
			  t->temp_vars_num = $2->temp_vars_num + 1;
			  if ($3) t->temp_vars_num += $3->temp_vars_num;
			  if (!(t->x.id_table = create_id_table ())) err_no_mem ();
			  if (!(t2 = create_tree ())) err_no_mem ();
			  t2->line_num = parser_line;
			  t2->file_name = parser_file;
			  t2->sons = $2;
			  $2->next = $3;
			  t->sons = t2;
			  t2->next = $4;
			  t->temp_vars_num += $4->temp_vars_num;
			  
			  tt = $2;
			  while (tt) {
			    tree_node_t *as = tt->sons->next->next;
			    id_item_t *item;
			    
			    t->temp_vars_num++;
			    if (as) {
			      if (lookup_id_table (global_id_table, as->x.id)) {
			        sprintf (err_tmp, "identifier `%s' already declared", as->x.id);
			        yyerror (err_tmp);
			        break;
			      }
			      if (lookup_id_table (t->x.id_table, as->x.id)) {
			        sprintf (err_tmp, "identifier `%s' already declared in this scope", as->x.id);
			        yyerror (err_tmp);
			        break;
			      }
			    
			      if (!(item = malloc (sizeof (id_item_t)))) err_no_mem ();
			      item->type = ID_INSTR_ALIAS;
			      if (add_id_table (t->x.id_table, as->x.id, item, destroy_id_item)) err_no_mem ();
			    }
			    
			    tt = tt->next;
			  }
			  
			  if (!upecc_instructions)
			    upecc_instructions = t;
			  else {
			    tt = upecc_instructions;
			    while (tt->next) tt = tt->next;
			    tt->next = t;
			  }
			}
;

instr_masks:	/* empty */	{ $$ = NULL; }
		| T_COMMA instr_mask instr_masks	{
			  $$ = $2;
			  $2->next = $3;
			  if ($3) $$->temp_vars_num += $3->temp_vars_num;
			}
;		

instr_mask:	num_bit_range T_COLON num_quote instr_as	{
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->sons = $3;
			  $3->next = $1;
			  $1->next = $4;
			  t->temp_vars_num = $3->temp_vars_num;
			  $$ = t;
			}
;

num_quote:	expr	{
			  tree_node_t *t; upe_number_t *num;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_NUM_QUOTE;
			  t->x.num_quote.is_quote = 0;
			  if (eval_tree ($1, &num)) {
			    yyerror ("value can't be evaluated in compile-time");
			    break;
			  }
			  destroy_tree ($1);
			  t->x.num_quote.num = num;
			  t->temp_vars_num = 1;
			  $$ = t;
			}
		| T_QUOTE	{
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_NUM_QUOTE;
			  t->x.num_quote.is_quote = 1;
			  t->temp_vars_num = 0;
			  $$ = t;
			}
;

num_bit_range:	expr T_MINUS expr	{
			  tree_node_t *t; upe_number_t *num;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_NUM_BIT_RANGE;
			  if (eval_tree ($1, &num)) {
			    yyerror ("value can't be evaluated in compile-time");
			    break;
			  }
			  destroy_tree ($1);
			  t->x.num_bit_range.from = upe_getnum (num);
			  upe_donenum (num);
			  if (eval_tree ($3, &num)) {
			    yyerror ("value can't be evaluated in compile-time");
			    break;
			  }
			  destroy_tree ($3);
			  t->x.num_bit_range.to = upe_getnum (num);
			  upe_donenum (num);
			  $$ = t;
			}
		| expr	{
			  tree_node_t *t; upe_number_t *num;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_NUM_BIT_RANGE;
			  t->x.num_bit_range.from = 0;
			  if (eval_tree ($1, &num)) {
			    yyerror ("value can't be evaluated in compile-time");
			    break;
			  }
			  destroy_tree ($1);
			  t->x.num_bit_range.to = upe_getnum (num);
			  upe_donenum (num);
			  $$ = t;
			}
;

instr_as:	/* empty */	{ $$ = NULL; }
		| T_AS var_name	{
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_VAR;
			  t->x.id = $2;
			  if (!t->x.id) err_no_mem ();
			  $$ = t;
			}
;

var_name:	T_ID	{ $$ = $1; }
;		

var_name_decl:	var_name	{ $$ = $1; }
;

var_decls:	/* empty */	{ $$ = NULL; }
		| T_VAR var_decl var_decls_n T_SEMICOLON	{
			  $$ = $2;
			  $2->next = $3;
			}
;

var_decls_n:	/* empty */	{ $$ = NULL; }
		| T_COMMA var_decl var_decls_n	{
			  $$ = $2;
			  $2->next = $3;
			}

var_decl:	var_name_decl T_COLON expr	{
			  tree_node_t *t; upe_number_t *n;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_VAR_DECL;
			  t->x.var_decl.id = $1;
			  if (!t->x.id) err_no_mem ();
			  if (eval_tree ($3, &n)) {
			    yyerror ("value can't be evaluated in compile-time");
			    break;
			  }
			  t->x.var_decl.len = upe_getnum (n);
			  upe_donenum (n);
			  destroy_tree ($3);
			  t->temp_vars_num = 0;
			  $$ = t;
			}
;

instr_code:	T_OPEN_BRACKET instr_code_block instr_code_blocks T_CLOSE_BRACKET	{
			  $$ = $2; $2->next = $3;
			  if ($3 && ($3->temp_vars_num > $2->temp_vars_num))
			    $$->temp_vars_num = $3->temp_vars_num;
			}
;

instr_code_blocks:	/* empty */	{ $$ = NULL; }
			| instr_code_block instr_code_blocks	{
			  $$ = $1; $1->next = $2;
			  if ($2 && ($2->temp_vars_num > $1->temp_vars_num))
			    $$->temp_vars_num = $2->temp_vars_num;
			}
;

instr_code_block:	expr T_OPEN_BRACKET code T_CLOSE_BRACKET	{
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->sons = $1;
			  $1->next = $3;
			  t->temp_vars_num = $1->temp_vars_num;
			  if ($3) t->temp_vars_num += $3->temp_vars_num;
			  $$ = t;
			}
;

code:	/* empty */	{ $$ = NULL; }
	| statement T_SEMICOLON code	{
			  $$ = $1; $1->next = $3;
			  if ($3 && ($3->temp_vars_num > $1->temp_vars_num))
			    $$->temp_vars_num = $3->temp_vars_num;
			}
;

if_branch_else:	/* empty */	{ $$ = NULL; }
		| T_ELSE T_OPEN_BRACKET code T_CLOSE_BRACKET {
			  $$ = $3;
			}
;

for_expr:	/* empty */	{
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("*for-void");
			  if (!t->x.id) err_no_mem ();
			  t->temp_vars_num = 0;
			  $$ = t;
			}
		| expr	{ $$ = $1; }
;
		
statement:	expr	{ $$ = $1; }
		| T_BREAK		{
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("*break");
			  if (!t->x.id) err_no_mem ();
			  t->temp_vars_num = 0;
			  $$ = t;
			}
		| T_CONTINUE	{
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("*continue");
			  if (!t->x.id) err_no_mem ();
			  t->temp_vars_num = 0;
			  $$ = t;
			}
		| T_WHILE T_OPEN_PARENT expr T_CLOSE_PARENT T_OPEN_BRACKET code T_CLOSE_BRACKET	{
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("*while");
			  if (!t->x.id) err_no_mem ();
			  t->sons = $3;
			  $3->next = $6;
			  t->temp_vars_num = $3->temp_vars_num;
			  if ($6) t->temp_vars_num += $6->temp_vars_num;
			  $$ = t;
			}
		| T_FOR T_OPEN_PARENT for_expr T_SEMICOLON for_expr T_SEMICOLON for_expr T_CLOSE_PARENT T_OPEN_BRACKET code T_CLOSE_BRACKET	{
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("*for");
			  if (!t->x.id) err_no_mem ();
			  t->sons = $3;
			  $3->next = $5;
			  $5->next = $7;
			  $7->next = $10;
			  t->temp_vars_num = $3->temp_vars_num + $5->temp_vars_num + $7->temp_vars_num;
			  if ($10) t->temp_vars_num += $10->temp_vars_num;
			  $$ = t;
			}
		| T_DO T_OPEN_BRACKET code T_CLOSE_BRACKET T_WHILE T_OPEN_PARENT expr T_CLOSE_PARENT 	{
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("*do-while");
			  if (!t->x.id) err_no_mem ();
			  t->sons = $7;
			  $7->next = $3;
			  t->temp_vars_num = $7->temp_vars_num;
			  if ($3) t->temp_vars_num += $3->temp_vars_num;
			  $$ = t;
			}
		| T_IF T_OPEN_PARENT expr T_CLOSE_PARENT T_OPEN_BRACKET code T_CLOSE_BRACKET if_branch_else {
			  tree_node_t *t, *t2, *t3; int temp_vars;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("*if");
			  if (!t->x.id) err_no_mem ();
			  t->temp_vars_num = $3->temp_vars_num;
			  if (!(t2 = create_tree ())) err_no_mem ();
			  t2->line_num = parser_line;
			  t2->file_name = parser_file;
			  t2->type = NODE_FN;
			  t2->x.id = strdup ("*if-true");
			  if (!t2->x.id) err_no_mem ();
			  t2->sons = $6;
			  temp_vars = 0;
			  if ($6) temp_vars = $6->temp_vars_num;
			  if ($8) {
			    if (!(t3 = create_tree ())) err_no_mem ();
			    t3->line_num = parser_line;
			    t3->file_name = parser_file;
			    t3->type = NODE_FN;
			    t3->x.id = strdup ("*if-false");
			    if (!t3->x.id) err_no_mem ();
			    t3->sons = $8;
			    if ($8 && ($8->temp_vars_num > temp_vars)) temp_vars = $8->temp_vars_num;
			  } else t3 = NULL;
			  t->temp_vars_num += temp_vars;
			  t->sons = $3;
			  $3->next = t2;
			  t2->next = t3;
			  $$ = t;
			}
		| T_RETURN expr	{
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("*return");
			  if (!t->x.id) err_no_mem ();
			  t->sons = $2;
			  t->temp_vars_num = $2->temp_vars_num;
			  $$ = t;
			}
;

fn_call:	T_ID T_OPEN_PARENT fn_call_args T_CLOSE_PARENT	{
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = $1;
			  if (!t->x.id) err_no_mem ();
			  t->sons = $3;
			  if ($3)
			    t->temp_vars_num = $3->temp_vars_num + 1;
			  else
			    t->temp_vars_num = 1;
			  $$ = t;
			}
;

fn_call_args:	/* empty */		{ $$ = NULL; }
		| fn_call_args_n2	{ $$ = $1; }
;

fn_call_args_next:	/* empty */	{ $$ = NULL; }
			| T_COMMA fn_call_args_n2	{ $$ = $2; }
;

fn_call_args_n2:	fn_call_arg fn_call_args_next {
			  $$ = $1;
			  $1->next = $2;
			  if ($2) $$->temp_vars_num += $2->temp_vars_num;
			}
;

fn_call_arg:	expr	{ $$ = $1; }
;

bit_range:	expr T_MINUS expr	{
			  $1->next = $3;
			  $$ = $1;
			}
		| expr	{
			  tree_node_t *t; upe_number_t *n;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_NUM;
		          if (!(n = upe_initnum (upecc_num_len, 0, UPE_NO_SIGN))) err_no_mem ();
		          upe_zeronum (n);
			  t->x.num = n;
			  t->temp_vars_num = 1;
			  t->next = $1;
			  $$ = t;
			}
;

expr:	T_NUM		{
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_NUM;
			  t->x.num = $1;
			  t->temp_vars_num = 1;
			  $$ = t;
			}
        | var_name T_INC	{
			  tree_node_t *t, *t2;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup (" ++");
			  if (!t->x.id) err_no_mem ();
			  if (!(t2 = create_tree ())) err_no_mem ();
			  t2->line_num = parser_line;
			  t2->file_name = parser_file;
			  t2->type = NODE_VAR;
			  t2->x.id = $1;
			  if (!t2->x.id) err_no_mem ();
			  t->sons = t2;
			  t->temp_vars_num = 1;
			  $$ = t;
			}
        | var_name T_DEC	{
			  tree_node_t *t, *t2;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup (" --");
			  if (!t->x.id) err_no_mem ();
			  if (!(t2 = create_tree ())) err_no_mem ();
			  t2->line_num = parser_line;
			  t2->file_name = parser_file;
			  t2->type = NODE_VAR;
			  t2->x.id = $1;
			  if (!t2->x.id) err_no_mem ();
			  t->sons = t2;
			  t->temp_vars_num = 1;
			  $$ = t;
			}
        | T_INC var_name	{
			  tree_node_t *t, *t2;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("++ ");
			  if (!t->x.id) err_no_mem ();
			  if (!(t2 = create_tree ())) err_no_mem ();
			  t2->line_num = parser_line;
			  t2->file_name = parser_file;
			  t2->type = NODE_VAR;
			  t2->x.id = $2;
			  if (!t2->x.id) err_no_mem ();
			  t->sons = t2;
			  t->temp_vars_num = 0;
			  $$ = t;
			}
        | T_DEC var_name	{
			  tree_node_t *t, *t2;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("-- ");
			  if (!t->x.id) err_no_mem ();
			  if (!(t2 = create_tree ())) err_no_mem ();
			  t2->line_num = parser_line;
			  t2->file_name = parser_file;
			  t2->type = NODE_VAR;
			  t2->x.id = $2;
			  if (!t2->x.id) err_no_mem ();
			  t->sons = t2;
			  t->temp_vars_num = 0;
			  $$ = t;
			 }
	| expr T_PLUS expr {
			  upe_number_t *n;
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("+");
			  if (!t->x.id) err_no_mem ();
			  t->sons = $1;
			  $1->next = $3;
			  t->temp_vars_num = $1->temp_vars_num + $3->temp_vars_num + 1;
			  if (!eval_tree (t, &n)) {
			    destroy_tree (t);
			    if (!(t = create_tree ())) err_no_mem ();
			    t->line_num = parser_line;
			    t->file_name = parser_file;
			    t->type = NODE_NUM;
			    t->x.num = n;
			    t->temp_vars_num = 1;
			  }
			  $$ = t;
                        }
        | expr T_MINUS expr {
			  upe_number_t *n;
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("-");
			  if (!t->x.id) err_no_mem ();
			  t->sons = $1;
			  $1->next = $3;
			  t->temp_vars_num = $1->temp_vars_num + $3->temp_vars_num + 1;
			  if (!eval_tree (t, &n)) {
			    destroy_tree (t);
			    if (!(t = create_tree ())) err_no_mem ();
			    t->line_num = parser_line;
			    t->file_name = parser_file;
			    t->type = NODE_NUM;
			    t->x.num = n;
			    t->temp_vars_num = 1;
			  }
			  $$ = t;
                        }
	| expr T_SHIFT_LEFT expr {
			  upe_number_t *n;
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("<<");
			  if (!t->x.id) err_no_mem ();
			  t->sons = $1;
			  $1->next = $3;
			  t->temp_vars_num = $1->temp_vars_num + $3->temp_vars_num + 1;
			  if (!eval_tree (t, &n)) {
			    destroy_tree (t);
			    if (!(t = create_tree ())) err_no_mem ();
			    t->line_num = parser_line;
			    t->file_name = parser_file;
			    t->type = NODE_NUM;
			    t->x.num = n;
			    t->temp_vars_num = 1;
			  }
			  $$ = t;
                        }
	| expr T_SHIFT_RIGHT expr {
			  upe_number_t *n;
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup (">>");
			  if (!t->x.id) err_no_mem ();
			  t->sons = $1;
			  $1->next = $3;
			  t->temp_vars_num = $1->temp_vars_num + $3->temp_vars_num + 1;
			  if (!eval_tree (t, &n)) {
			    destroy_tree (t);
			    if (!(t = create_tree ())) err_no_mem ();
			    t->line_num = parser_line;
			    t->file_name = parser_file;
			    t->type = NODE_NUM;
			    t->x.num = n;
			    t->temp_vars_num = 1;
			  }
			  $$ = t;
                        }
        | T_NEG expr {
			  upe_number_t *n;
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("!");
			  if (!t->x.id) err_no_mem ();
			  t->sons = $2;
			  t->temp_vars_num = $2->temp_vars_num + 1;
			  if (!eval_tree (t, &n)) {
			    destroy_tree (t);
			    if (!(t = create_tree ())) err_no_mem ();
			    t->line_num = parser_line;
			    t->file_name = parser_file;
			    t->type = NODE_NUM;
			    t->x.num = n;
			    t->temp_vars_num = 1;
			  }
			  $$ = t;
                        }
	| expr T_AND expr {
			  upe_number_t *n;
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("&&");
			  if (!t->x.id) err_no_mem ();
			  t->sons = $1;
			  $1->next = $3;
			  t->temp_vars_num = $1->temp_vars_num + $3->temp_vars_num + 1;
			  if (!eval_tree (t, &n)) {
			    destroy_tree (t);
			    if (!(t = create_tree ())) err_no_mem ();
			    t->line_num = parser_line;
			    t->file_name = parser_file;
			    t->type = NODE_NUM;
			    t->x.num = n;
			    t->temp_vars_num = 1;
			  }
			  $$ = t;
                        }
	| expr T_OR expr {
			  upe_number_t *n;
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("||");
			  if (!t->x.id) err_no_mem ();
			  t->sons = $1;
			  $1->next = $3;
			  t->temp_vars_num = $1->temp_vars_num + $3->temp_vars_num + 1;
			  if (!eval_tree (t, &n)) {
			    destroy_tree (t);
			    if (!(t = create_tree ())) err_no_mem ();
			    t->line_num = parser_line;
			    t->file_name = parser_file;
			    t->type = NODE_NUM;
			    t->x.num = n;
			    t->temp_vars_num = 1;
			  }
			  $$ = t;
                        }
        | T_BITNEG expr {
			  upe_number_t *n;
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("~");
			  if (!t->x.id) err_no_mem ();
			  t->sons = $2;
			  t->temp_vars_num = $2->temp_vars_num + 1;
			  if (!eval_tree (t, &n)) {
			    destroy_tree (t);
			    if (!(t = create_tree ())) err_no_mem ();
			    t->line_num = parser_line;
			    t->file_name = parser_file;
			    t->type = NODE_NUM;
			    t->x.num = n;
			    t->temp_vars_num = 1;
			  }
			  $$ = t;
                        }
	| expr T_BITAND expr {
			  upe_number_t *n;
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("&");
			  if (!t->x.id) err_no_mem ();
			  t->sons = $1;
			  $1->next = $3;
			  t->temp_vars_num = $1->temp_vars_num + $3->temp_vars_num + 1;
			  if (!eval_tree (t, &n)) {
			    destroy_tree (t);
			    if (!(t = create_tree ())) err_no_mem ();
			    t->line_num = parser_line;
			    t->file_name = parser_file;
			    t->type = NODE_NUM;
			    t->x.num = n;
			    t->temp_vars_num = 1;
			  }
			  $$ = t;
                        }
	| expr T_BITOR expr {
			  upe_number_t *n;
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("|");
			  if (!t->x.id) err_no_mem ();
			  t->sons = $1;
			  $1->next = $3;
			  t->temp_vars_num = $1->temp_vars_num + $3->temp_vars_num + 1;
			  if (!eval_tree (t, &n)) {
			    destroy_tree (t);
			    if (!(t = create_tree ())) err_no_mem ();
			    t->line_num = parser_line;
			    t->file_name = parser_file;
			    t->type = NODE_NUM;
			    t->x.num = n;
			    t->temp_vars_num = 1;
			  }
			  $$ = t;
                        }
	| expr T_BITXOR expr {
			  upe_number_t *n;
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("^");
			  if (!t->x.id) err_no_mem ();
			  t->sons = $1;
			  $1->next = $3;
			  t->temp_vars_num = $1->temp_vars_num + $3->temp_vars_num + 1;
			  if (!eval_tree (t, &n)) {
			    destroy_tree (t);
			    if (!(t = create_tree ())) err_no_mem ();
			    t->line_num = parser_line;
			    t->file_name = parser_file;
			    t->type = NODE_NUM;
			    t->x.num = n;
			    t->temp_vars_num = 1;
			  }
			  $$ = t;
                        }
        | T_OPEN_PARENT expr T_CLOSE_PARENT  { $$ = $2; }
        | var_name	{
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_VAR;
			  t->x.id = $1;
			  if (!t->x.id) err_no_mem ();
			  $$ = t;
        		}
        | fn_call	{ $$ = $1; }
        | expr T_EQUALS expr	{
			  upe_number_t *n;
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("==");
			  if (!t->x.id) err_no_mem ();
			  t->sons = $1;
			  $1->next = $3;
			  t->temp_vars_num = $1->temp_vars_num + $3->temp_vars_num + 1;
			  if (!eval_tree (t, &n)) {
			    destroy_tree (t);
			    if (!(t = create_tree ())) err_no_mem ();
			    t->line_num = parser_line;
			    t->file_name = parser_file;
			    t->type = NODE_NUM;
			    t->x.num = n;
			    t->temp_vars_num = 1;
			  }
			  $$ = t;
        		}
        | expr T_NON_EQUALS expr	{
			  upe_number_t *n;
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("!=");
			  if (!t->x.id) err_no_mem ();
			  t->sons = $1;
			  $1->next = $3;
			  t->temp_vars_num = $1->temp_vars_num + $3->temp_vars_num + 1;
			  if (!eval_tree (t, &n)) {
			    destroy_tree (t);
			    if (!(t = create_tree ())) err_no_mem ();
			    t->line_num = parser_line;
			    t->file_name = parser_file;
			    t->type = NODE_NUM;
			    t->x.num = n;
			    t->temp_vars_num = 1;
			  }
			  $$ = t;
			}
        | expr T_LESS_THAN expr	{
			  upe_number_t *n;
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("<");
			  if (!t->x.id) err_no_mem ();
			  t->sons = $1;
			  $1->next = $3;
			  t->temp_vars_num = $1->temp_vars_num + $3->temp_vars_num + 1;
			  if (!eval_tree (t, &n)) {
			    destroy_tree (t);
			    if (!(t = create_tree ())) err_no_mem ();
			    t->line_num = parser_line;
			    t->file_name = parser_file;
			    t->type = NODE_NUM;
			    t->x.num = n;
			    t->temp_vars_num = 1;
			  }
			  $$ = t;
        		}
        | expr T_LESS_OR_EQUAL expr	{
			  upe_number_t *n;
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("<=");
			  if (!t->x.id) err_no_mem ();
			  t->sons = $1;
			  $1->next = $3;
			  t->temp_vars_num = $1->temp_vars_num + $3->temp_vars_num + 1;
			  if (!eval_tree (t, &n)) {
			    destroy_tree (t);
			    if (!(t = create_tree ())) err_no_mem ();
			    t->line_num = parser_line;
			    t->file_name = parser_file;
			    t->type = NODE_NUM;
			    t->x.num = n;
			    t->temp_vars_num = 1;
			  }
			  $$ = t;
			}
        | expr T_GREATER_THAN expr	{
			  upe_number_t *n;
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup (">");
			  if (!t->x.id) err_no_mem ();
			  t->sons = $1;
			  $1->next = $3;
			  t->temp_vars_num = $1->temp_vars_num + $3->temp_vars_num + 1;
			  if (!eval_tree (t, &n)) {
			    destroy_tree (t);
			    if (!(t = create_tree ())) err_no_mem ();
			    t->line_num = parser_line;
			    t->file_name = parser_file;
			    t->type = NODE_NUM;
			    t->x.num = n;
			    t->temp_vars_num = 1;
			  }
			  $$ = t;
        		}
        | expr T_GREATER_OR_EQUAL expr	{
			  upe_number_t *n;
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup (">=");
			  if (!t->x.id) err_no_mem ();
			  t->sons = $1;
			  $1->next = $3;
			  t->temp_vars_num = $1->temp_vars_num + $3->temp_vars_num + 1;
			  if (!eval_tree (t, &n)) {
			    destroy_tree (t);
			    if (!(t = create_tree ())) err_no_mem ();
			    t->line_num = parser_line;
			    t->file_name = parser_file;
			    t->type = NODE_NUM;
			    t->x.num = n;
			    t->temp_vars_num = 1;
			  }
			  $$ = t;
			}
        | expr T_QUOTE expr T_COLON expr	{
			  upe_number_t *n;
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("?:");
			  if (!t->x.id) err_no_mem ();
			  t->sons = $1;
			  $1->next = $3;
			  $3->next = $5;
			  t->temp_vars_num = $1->temp_vars_num + $3->temp_vars_num + $5->temp_vars_num;
			  if (!eval_tree (t, &n)) {
			    destroy_tree (t);
			    if (!(t = create_tree ())) err_no_mem ();
			    t->line_num = parser_line;
			    t->file_name = parser_file;
			    t->type = NODE_NUM;
			    t->x.num = n;
			    t->temp_vars_num = 1;
			  }
			  $$ = t;
			}
        | expr T_COLON T_OPEN_PARENT bit_range T_CLOSE_PARENT	{
			  upe_number_t *n;
			  tree_node_t *t;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup (":()");
			  if (!t->x.id) err_no_mem ();
			  t->sons = $1;
			  $1->next = $4;
			  assert ($4->next);
			  t->temp_vars_num = $1->temp_vars_num + $4->temp_vars_num + $4->next->temp_vars_num + 1;
			  if (!eval_tree (t, &n)) {
			    destroy_tree (t);
			    if (!(t = create_tree ())) err_no_mem ();
			    t->line_num = parser_line;
			    t->file_name = parser_file;
			    t->type = NODE_NUM;
			    t->x.num = n;
			    t->temp_vars_num = 1;
			  }
			  $$ = t;
			}
        | var_name T_ASSIGN expr	{
			  tree_node_t *t, *t2;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("=");
			  if (!t->x.id) err_no_mem ();
			  if (!(t2 = create_tree ())) err_no_mem ();
			  t2->line_num = parser_line;
			  t2->file_name = parser_file;
			  t2->type = NODE_VAR;
			  t2->x.id = $1;
			  if (!t2->x.id) err_no_mem ();
			  t->sons = t2;
			  t2->next = $3;
			  t->temp_vars_num = $3->temp_vars_num;
			  $$ = t;
        		}
        | var_name T_ASSIGN_PLUS expr	{
			  tree_node_t *t, *t2;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("+=");
			  if (!t->x.id) err_no_mem ();
			  if (!(t2 = create_tree ())) err_no_mem ();
			  t2->line_num = parser_line;
			  t2->file_name = parser_file;
			  t2->type = NODE_VAR;
			  t2->x.id = $1;
			  if (!t2->x.id) err_no_mem ();
			  t->sons = t2;
			  t2->next = $3;
			  t->temp_vars_num = $3->temp_vars_num;
			  $$ = t;
        		}
        | var_name T_ASSIGN_MINUS expr	{
			  tree_node_t *t, *t2;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("-=");
			  if (!t->x.id) err_no_mem ();
			  if (!(t2 = create_tree ())) err_no_mem ();
			  t2->line_num = parser_line;
			  t2->file_name = parser_file;
			  t2->type = NODE_VAR;
			  t2->x.id = $1;
			  if (!t2->x.id) err_no_mem ();
			  t->sons = t2;
			  t2->next = $3;
			  t->temp_vars_num = $3->temp_vars_num;
			  $$ = t;
        		}
        | var_name T_ASSIGN_BITAND expr	{
			  tree_node_t *t, *t2;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("&=");
			  if (!t->x.id) err_no_mem ();
			  if (!(t2 = create_tree ())) err_no_mem ();
			  t2->line_num = parser_line;
			  t2->file_name = parser_file;
			  t2->type = NODE_VAR;
			  t2->x.id = $1;
			  if (!t2->x.id) err_no_mem ();
			  t->sons = t2;
			  t2->next = $3;
			  t->temp_vars_num = $3->temp_vars_num;
			  $$ = t;
        		}
        | var_name T_ASSIGN_BITOR expr	{
			  tree_node_t *t, *t2;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("|=");
			  if (!t->x.id) err_no_mem ();
			  if (!(t2 = create_tree ())) err_no_mem ();
			  t2->line_num = parser_line;
			  t2->file_name = parser_file;
			  t2->type = NODE_VAR;
			  t2->x.id = $1;
			  if (!t2->x.id) err_no_mem ();
			  t->sons = t2;
			  t2->next = $3;
			  t->temp_vars_num = $3->temp_vars_num;
			  $$ = t;
        		}
        | var_name T_ASSIGN_BITXOR expr	{
			  tree_node_t *t, *t2;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("^=");
			  if (!t->x.id) err_no_mem ();
			  if (!(t2 = create_tree ())) err_no_mem ();
			  t2->line_num = parser_line;
			  t2->file_name = parser_file;
			  t2->type = NODE_VAR;
			  t2->x.id = $1;
			  if (!t2->x.id) err_no_mem ();
			  t->sons = t2;
			  t2->next = $3;
			  t->temp_vars_num = $3->temp_vars_num;
			  $$ = t;
        		}
        | var_name T_ASSIGN_SHIFT_LEFT expr	{
			  tree_node_t *t, *t2;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup ("<<=");
			  if (!t->x.id) err_no_mem ();
			  if (!(t2 = create_tree ())) err_no_mem ();
			  t2->line_num = parser_line;
			  t2->file_name = parser_file;
			  t2->type = NODE_VAR;
			  t2->x.id = $1;
			  if (!t2->x.id) err_no_mem ();
			  t->sons = t2;
			  t2->next = $3;
			  t->temp_vars_num = $3->temp_vars_num;
			  $$ = t;
        		}
        | var_name T_ASSIGN_SHIFT_RIGHT expr	{
			  tree_node_t *t, *t2;
			  if (!(t = create_tree ())) err_no_mem ();
			  t->line_num = parser_line;
			  t->file_name = parser_file;
			  t->type = NODE_FN;
			  t->x.id = strdup (">>=");
			  if (!t->x.id) err_no_mem ();
			  if (!(t2 = create_tree ())) err_no_mem ();
			  t2->line_num = parser_line;
			  t2->file_name = parser_file;
			  t2->type = NODE_VAR;
			  t2->x.id = $1;
			  if (!t2->x.id) err_no_mem ();
			  t->sons = t2;
			  t2->next = $3;
			  t->temp_vars_num = $3->temp_vars_num;
			  $$ = t;
        		}
;

%%

/* actual parsed line */
int parser_line = -1;

/* actual parsed file */
char *parser_file = NULL;

/* temporary string */
char err_tmp[1024];

/* no mem reporting function */
void err_no_mem (void) {
  yyerror ("Insufficient memory!");
  exit (1);
}

/* error reporting */
int yyerror(char *errstr)
{
  err_num++;
  printf("upecc: file %s line %d: %s\n", parser_file, parser_line, errstr);
  return 0;
}

/* main parser function - parses file fname
 * returns 0 on success, -1 on error
 */
int parse_input (char *fname) {
  FILE *fin;

#if YYDEBUG != 0
  yydebug = 1;
#endif

  if (!(fin = fopen (fname, "r"))) {
    err_num++;
    fprintf (stderr, "upecc: error opening input file %s : %s\n", fname, strerror (errno));
    return -1;
  }

  if (add_include (fname, fin)) return -1;

  if (!(global_id_table = create_id_table ())) err_no_mem ();
  
  { id_item_t *i;

/* registers builtin function
 * xid = identifier
 * xfn = function for compile-time evaluation or NULL
 * xfnid = BIFN_ constant
 */
#define ADD_BUILTIN_FN(xid,xfn,xfnid)\
    { if (!(i = malloc (sizeof (id_item_t)))) err_no_mem ();\
      i->type = ID_BUILTIN_FN;\
      i->x.bifn.builtin_fn = (xfn);\
      i->x.bifn.id = (xfnid);\
      if (add_id_table (global_id_table, (xid), i, destroy_id_item)) err_no_mem ();\
    }

    ADD_BUILTIN_FN ("+", fn_sum_add, BIFN_ADD);
    ADD_BUILTIN_FN ("-", fn_sum_sub, BIFN_SUB);
    ADD_BUILTIN_FN ("==", fn_eq, BIFN_EQ);
    ADD_BUILTIN_FN ("!=", fn_ne, BIFN_NE);
    ADD_BUILTIN_FN (">", fn_gt, BIFN_GT);
    ADD_BUILTIN_FN (">=", fn_ge, BIFN_GE);
    ADD_BUILTIN_FN ("<", fn_lt, BIFN_LT);
    ADD_BUILTIN_FN ("<=", fn_le, BIFN_LE);
    ADD_BUILTIN_FN ("=", NULL, BIFN_ASSIGN);
    ADD_BUILTIN_FN ("+=", NULL, BIFN_ASSIGN_ADD);
    ADD_BUILTIN_FN ("-=", NULL, BIFN_ASSIGN_SUB);
    ADD_BUILTIN_FN ("&=", NULL, BIFN_ASSIGN_BITAND);
    ADD_BUILTIN_FN ("|=", NULL, BIFN_ASSIGN_BITOR);
    ADD_BUILTIN_FN ("^=", NULL, BIFN_ASSIGN_BITXOR);
    ADD_BUILTIN_FN (">>=", NULL, BIFN_ASSIGN_SHR);
    ADD_BUILTIN_FN ("<<=", NULL, BIFN_ASSIGN_SHL);
    ADD_BUILTIN_FN ("!", fn_neg, BIFN_NEG);
    ADD_BUILTIN_FN ("&&", fn_sum_and, BIFN_AND);
    ADD_BUILTIN_FN ("||", fn_sum_or, BIFN_OR);
    ADD_BUILTIN_FN ("~", fn_bitneg, BIFN_NEG);
    ADD_BUILTIN_FN ("&", fn_sum_bitand, BIFN_BITAND);
    ADD_BUILTIN_FN ("|", fn_sum_bitor, BIFN_BITOR);
    ADD_BUILTIN_FN ("^", fn_sum_bitxor, BIFN_BITXOR);
    ADD_BUILTIN_FN ("*return", NULL, BIFN_RETURN);
    ADD_BUILTIN_FN ("overflow", NULL, BIFN_OVERFLOW);
    ADD_BUILTIN_FN ("underflow", NULL, BIFN_UNDERFLOW);
    ADD_BUILTIN_FN ("bit_length", NULL, BIFN_BIT_LENGTH);
    ADD_BUILTIN_FN ("fetch", NULL, BIFN_FETCH);
    ADD_BUILTIN_FN ("store", NULL, BIFN_STORE);
    ADD_BUILTIN_FN ("mem_violation", NULL, BIFN_MEM_VIOLATION);
    ADD_BUILTIN_FN ("?:", fn_cond_expr, BIFN_COND_EXPR);
    ADD_BUILTIN_FN (":()", fn_bit_range, BIFN_BIT_RANGE);
    ADD_BUILTIN_FN (">>", fn_shr, BIFN_SHR);
    ADD_BUILTIN_FN ("<<", fn_shl, BIFN_SHL);
    ADD_BUILTIN_FN (" ++", NULL, BIFN_POSTFIX_INC);
    ADD_BUILTIN_FN (" --", NULL, BIFN_POSTFIX_DEC);
    ADD_BUILTIN_FN ("++ ", NULL, BIFN_PREFIX_INC);
    ADD_BUILTIN_FN ("-- ", NULL, BIFN_PREFIX_DEC);
    ADD_BUILTIN_FN ("*while", NULL, BIFN_WHILE);
    ADD_BUILTIN_FN ("*do-while", NULL, BIFN_DO_WHILE);
    ADD_BUILTIN_FN ("*if", NULL, BIFN_IF);
    ADD_BUILTIN_FN ("*break", NULL, BIFN_BREAK);
    ADD_BUILTIN_FN ("*continue", NULL, BIFN_CONTINUE);
    ADD_BUILTIN_FN ("*for", NULL, BIFN_FOR);
    ADD_BUILTIN_FN ("*for-void", NULL, BIFN_FOR_VOID);
  }

  yyparse ();

  fclose (fin);
  
  return 0;
}
