/* Parser grammar for quick source code scan of Java(TM) language programs.
   Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
   Contributed by Alexandre Petit-Bianco (apbianco@cygnus.com)

This file is part of GNU CC.

GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING.  If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.

Java and all Java-based marks are trademarks or registered trademarks
of Sun Microsystems, Inc. in the United States and other countries.
The Free Software Foundation is independent of Sun Microsystems, Inc.  */

/* This file parses Java source code. Action can be further completed
to achieve a desired behavior. This file isn't part of the Java
language gcc front end.

The grammar conforms to the Java grammar described in "The Java(TM)
Language Specification. J. Gosling, B. Joy, G. Steele. Addison Wesley
1996, ISBN 0-201-63451-1"

Some rules have been modified to support JDK1.1 inner classes
definitions and other extensions.  */

%{
#define JC1_LITE

#include "config.h"
#include "system.h"

#include "obstack.h"
#include "toplev.h"

extern char *input_filename;
extern FILE *finput, *out;

/* Obstack for the lexer.  */
struct obstack temporary_obstack;

/* The current parser context.  */
static struct parser_ctxt *ctxp;

/* Error and warning counts, current line number, because they're used
   elsewhere  */
int java_error_count;
int java_warning_count;
int lineno;

/* Tweak default rules when necessary.  */
static int absorber;
#define USE_ABSORBER absorber = 0

/* Keep track of the current class name and package name.  */
static char *current_class;
static const char *package_name;

/* Keep track of the current inner class qualifier. */
static int current_class_length;

/* Keep track of whether things have be listed before.  */
static int previous_output;

/* Record modifier uses  */
static int modifier_value;

/* Record (almost) cyclomatic complexity.  */
static int complexity; 

/* Keeps track of number of bracket pairs after a variable declarator
   id.  */
static int bracket_count; 

/* Numbers anonymous classes */
static int anonymous_count;

/* Record a method declaration  */
struct method_declarator {
  const char *method_name;
  const char *args;
};
#define NEW_METHOD_DECLARATOR(D,N,A)					     \
{									     \
  (D) = 								     \
    (struct method_declarator *)xmalloc (sizeof (struct method_declarator)); \
  (D)->method_name = (N);						     \
  (D)->args = (A);							     \
}

/* Two actions for this grammar */
static void report_class_declaration PARAMS ((const char *));
static void report_main_declaration PARAMS ((struct method_declarator *));
static void push_class_context PARAMS ((const char *));
static void pop_class_context PARAMS ((void));

void report PARAMS ((void)); 

#include "lex.h"
#include "parse.h"
%}

%union {
  char *node;
  struct method_declarator *declarator;
  int value;			/* For modifiers */
}

%{
#include "lex.c"
%}

%pure_parser

/* Things defined here have to match the order of what's in the
   binop_lookup table.  */

%token   PLUS_TK         MINUS_TK        MULT_TK         DIV_TK    REM_TK
%token   LS_TK           SRS_TK          ZRS_TK
%token   AND_TK          XOR_TK          OR_TK
%token   BOOL_AND_TK BOOL_OR_TK 
%token   EQ_TK NEQ_TK GT_TK GTE_TK LT_TK LTE_TK

/* This maps to the same binop_lookup entry than the token above */

%token   PLUS_ASSIGN_TK  MINUS_ASSIGN_TK MULT_ASSIGN_TK DIV_ASSIGN_TK
%token   REM_ASSIGN_TK   
%token   LS_ASSIGN_TK    SRS_ASSIGN_TK   ZRS_ASSIGN_TK
%token   AND_ASSIGN_TK   XOR_ASSIGN_TK   OR_ASSIGN_TK


/* Modifier TOKEN have to be kept in this order. Don't scramble it */

%token   PUBLIC_TK       PRIVATE_TK         PROTECTED_TK
%token   STATIC_TK       FINAL_TK           SYNCHRONIZED_TK
%token   VOLATILE_TK     TRANSIENT_TK       NATIVE_TK
%token   PAD_TK          ABSTRACT_TK        MODIFIER_TK
%token   STRICT_TK

/* Keep those two in order, too */
%token   DECR_TK INCR_TK

/* From now one, things can be in any order */

%token   DEFAULT_TK      IF_TK              THROW_TK
%token   BOOLEAN_TK      DO_TK              IMPLEMENTS_TK
%token   THROWS_TK       BREAK_TK           IMPORT_TK       
%token   ELSE_TK         INSTANCEOF_TK      RETURN_TK
%token   VOID_TK         CATCH_TK           INTERFACE_TK
%token   CASE_TK         EXTENDS_TK         FINALLY_TK
%token   SUPER_TK        WHILE_TK           CLASS_TK
%token   SWITCH_TK       CONST_TK           TRY_TK
%token   FOR_TK          NEW_TK             CONTINUE_TK
%token   GOTO_TK         PACKAGE_TK         THIS_TK

%token   BYTE_TK         SHORT_TK           INT_TK            LONG_TK
%token   CHAR_TK         INTEGRAL_TK

%token   FLOAT_TK        DOUBLE_TK          FP_TK

%token   ID_TK

%token   REL_QM_TK         REL_CL_TK NOT_TK  NEG_TK

%token   ASSIGN_ANY_TK   ASSIGN_TK
%token   OP_TK  CP_TK  OCB_TK  CCB_TK  OSB_TK  CSB_TK  SC_TK  C_TK DOT_TK

%token   STRING_LIT_TK   CHAR_LIT_TK        INT_LIT_TK        FP_LIT_TK
%token   TRUE_TK         FALSE_TK           BOOL_LIT_TK       NULL_TK

%type <node> ID_TK identifier name simple_name qualified_name type
 	     primitive_type reference_type array_type formal_parameter_list
	     formal_parameter class_or_interface_type class_type interface_type
%type <declarator> method_declarator
%type <value>      MODIFIER_TK

%%
/* 19.2 Production from 2.3: The Syntactic Grammar  */
goal:
	compilation_unit
;

/* 19.3 Productions from 3: Lexical structure  */
literal:
	INT_LIT_TK
|	FP_LIT_TK
|	BOOL_LIT_TK
|	CHAR_LIT_TK
|       STRING_LIT_TK
|       NULL_TK
;

/* 19.4 Productions from 4: Types, Values and Variables  */
type:
	primitive_type
|	reference_type
;

primitive_type:
	INTEGRAL_TK
		{
		  /* use preset global here. FIXME */
		  $$ = xstrdup ("int");
		}
|	FP_TK
		{
		  /* use preset global here. FIXME */
		  $$ = xstrdup ("double");
		}
|	BOOLEAN_TK
		{
		  /* use preset global here. FIXME */
		  $$ = xstrdup ("boolean");
		}
;

reference_type:
	class_or_interface_type
|	array_type
;

class_or_interface_type:
	name
;

class_type:
	class_or_interface_type	/* Default rule */
;

interface_type:
	 class_or_interface_type
;

array_type:
	primitive_type dims
		{
	          while (bracket_count-- > 0) 
		    $$ = concat ("[", $1, NULL);
		}
|	name dims
		{
	          while (bracket_count-- > 0) 
		    $$ = concat ("[", $1, NULL);
		}
;

/* 19.5 Productions from 6: Names  */
name:
	simple_name		/* Default rule */
|	qualified_name		/* Default rule */
;

simple_name:
	identifier		/* Default rule */
;

qualified_name:
	name DOT_TK identifier
		{ 
		  $$ = concat ($1, ".", $3, NULL);
		}
;

identifier:
	ID_TK
;

/* 19.6: Production from 7: Packages  */
compilation_unit:
|	package_declaration
|	import_declarations
|	type_declarations
|       package_declaration import_declarations
|       package_declaration type_declarations
|       import_declarations type_declarations
|       package_declaration import_declarations type_declarations
;

import_declarations:
	import_declaration
|	import_declarations import_declaration
;

type_declarations:
	type_declaration
| 	type_declarations type_declaration
;

package_declaration:
	PACKAGE_TK name SC_TK
		{ package_name = $2; }
;

import_declaration:
	single_type_import_declaration
|	type_import_on_demand_declaration
;

single_type_import_declaration:
	IMPORT_TK name SC_TK
;

type_import_on_demand_declaration:
	IMPORT_TK name DOT_TK MULT_TK SC_TK
;

type_declaration:
	class_declaration
|	interface_declaration
|	empty_statement
;

/* 19.7 Shortened from the original:
   modifiers: modifier | modifiers modifier
   modifier: any of public...  */
modifiers:
	MODIFIER_TK
		{ 
		  if ($1 == PUBLIC_TK)
		    modifier_value++;
                  if ($1 == STATIC_TK)
                    modifier_value++;
	          USE_ABSORBER;
		}	
|	modifiers MODIFIER_TK
		{ 
		  if ($2 == PUBLIC_TK)
		    modifier_value++;
                  if ($2 == STATIC_TK)
                    modifier_value++;
		  USE_ABSORBER;
		}	
;

/* 19.8.1 Production from $8.1: Class Declaration */
class_declaration:
	modifiers CLASS_TK identifier super interfaces 
		{ 
		  report_class_declaration($3);
		  modifier_value = 0;
                }
	class_body
|	CLASS_TK identifier super interfaces 
		{ report_class_declaration($2); }
	class_body
;

super:
|	EXTENDS_TK class_type
;

interfaces:
|	IMPLEMENTS_TK interface_type_list
;

interface_type_list:
	interface_type
		{ USE_ABSORBER; }
|	interface_type_list C_TK interface_type
		{ USE_ABSORBER; }
;

class_body:
	OCB_TK CCB_TK
		{ pop_class_context (); }
|	OCB_TK class_body_declarations CCB_TK
		{ pop_class_context (); }
;

class_body_declarations:
	class_body_declaration
|	class_body_declarations class_body_declaration
;

class_body_declaration:
	class_member_declaration
|	static_initializer
|	constructor_declaration
|	block			/* Added, JDK1.1, instance initializer */
;

class_member_declaration:
	field_declaration
|	method_declaration
|	class_declaration	/* Added, JDK1.1 inner classes */
|	interface_declaration	/* Added, JDK1.1 inner classes */
|	empty_statement
;

/* 19.8.2 Productions from 8.3: Field Declarations  */
field_declaration:
	type variable_declarators SC_TK
		{ USE_ABSORBER; }
|	modifiers type variable_declarators SC_TK
		{ modifier_value = 0; }
;

variable_declarators:
	/* Should we use build_decl_list () instead ? FIXME */
	variable_declarator	/* Default rule */
|	variable_declarators C_TK variable_declarator
;

variable_declarator:
	variable_declarator_id
|	variable_declarator_id ASSIGN_TK variable_initializer
;

variable_declarator_id:
	identifier
		{ bracket_count = 0; USE_ABSORBER; }
|	variable_declarator_id OSB_TK CSB_TK
		{ ++bracket_count; }
;

variable_initializer:
	expression
|	array_initializer
;

/* 19.8.3 Productions from 8.4: Method Declarations  */
method_declaration:
	method_header method_body
;

method_header:	
	type method_declarator throws
		{ USE_ABSORBER; }
|	VOID_TK method_declarator throws
|	modifiers type method_declarator throws
		{ modifier_value = 0; }
|	modifiers VOID_TK method_declarator throws
		{ 
                  report_main_declaration ($3);
		  modifier_value = 0;
		}
;

method_declarator:
	identifier OP_TK CP_TK
		{ 
		  struct method_declarator *d;
		  NEW_METHOD_DECLARATOR (d, $1, NULL);
		  $$ = d;
		}
|	identifier OP_TK formal_parameter_list CP_TK
		{ 
		  struct method_declarator *d;
		  NEW_METHOD_DECLARATOR (d, $1, $3);
		  $$ = d;
		}
|	method_declarator OSB_TK CSB_TK
;

formal_parameter_list:
	formal_parameter
|	formal_parameter_list C_TK formal_parameter
		{
		  $$ = concat ($1, ",", $3, NULL);
		}
;

formal_parameter:
	type variable_declarator_id
		{ 
		  USE_ABSORBER;
		  if (bracket_count)
		    {
		      int i;
		      char *n = xmalloc (bracket_count + 1 + strlen ($$));
		      for (i = 0; i < bracket_count; ++i)
			n[i] = '[';
		      strcpy (n + bracket_count, $$);
		      $$ = n;
		    }
		  else
		    $$ = $1;
		}
|	modifiers type variable_declarator_id /* Added, JDK1.1 final locals */
		{
		  if (bracket_count)
		    {
		      int i;
		      char *n = xmalloc (bracket_count + 1 + strlen ($$));
		      for (i = 0; i < bracket_count; ++i)
			n[i] = '[';
		      strcpy (n + bracket_count, $$);
		      $$ = n;
		    }
		  else
		    $$ = $2;
		}
;

throws:
|	THROWS_TK class_type_list
;

class_type_list:
	class_type
		{ USE_ABSORBER; }
|	class_type_list C_TK class_type
		{ USE_ABSORBER; }
;

method_body:
	block
|	SC_TK
;

/* 19.8.4 Productions from 8.5: Static Initializers  */
static_initializer:
	static block
;

static:				/* Test lval.sub_token here */
	MODIFIER_TK
		{ USE_ABSORBER; }
;

/* 19.8.5 Productions from 8.6: Constructor Declarations  */
/* NOTE FOR FURTHER WORK ON CONSTRUCTORS:
   - If a forbidded modifier is found, the the error is either the use of
     a forbidded modifier for a constructor OR bogus attempt to declare a
     method without having specified the return type. FIXME */
constructor_declaration:
	constructor_declarator throws constructor_body
|	modifiers constructor_declarator throws constructor_body
		{ modifier_value = 0; }
/* extra SC_TK, FIXME */
|	constructor_declarator throws constructor_body SC_TK
/* extra SC_TK, FIXME */
|	modifiers constructor_declarator throws constructor_body SC_TK
		{ modifier_value = 0; }
/* I'm not happy with the SC_TK addition. It isn't in the grammer and should
   probably be matched by and empty statement. But it doesn't work. FIXME */
;

constructor_declarator:
	simple_name OP_TK CP_TK
		{ USE_ABSORBER; }
|	simple_name OP_TK formal_parameter_list CP_TK
		{ USE_ABSORBER; }
;

constructor_body:
	OCB_TK CCB_TK
|	OCB_TK explicit_constructor_invocation CCB_TK
|	OCB_TK block_statements CCB_TK
|       OCB_TK explicit_constructor_invocation block_statements CCB_TK
;

/* Error recovery for that rule moved down expression_statement: rule.  */
explicit_constructor_invocation:
	this_or_super OP_TK CP_TK SC_TK
|	this_or_super OP_TK argument_list CP_TK SC_TK
        /* Added, JDK1.1 inner classes. Modified because the rule
	   'primary' couldn't work.  */
|	name DOT_TK SUPER_TK OP_TK argument_list CP_TK SC_TK
		{ USE_ABSORBER; }
|	name DOT_TK SUPER_TK OP_TK CP_TK SC_TK
		{ USE_ABSORBER; }
;

this_or_super:			/* Added, simplifies error diagnostics */
	THIS_TK
|	SUPER_TK
;

/* 19.9 Productions from 9: Interfaces  */
/* 19.9.1 Productions from 9.1: Interfaces Declarations  */
interface_declaration:
	INTERFACE_TK identifier
		{ report_class_declaration ($2); modifier_value = 0; }
	interface_body
|	modifiers INTERFACE_TK identifier
		{ report_class_declaration ($3); modifier_value = 0; }
	interface_body
|	INTERFACE_TK identifier extends_interfaces
		{ report_class_declaration ($2); modifier_value = 0; }
	interface_body
|	modifiers INTERFACE_TK identifier extends_interfaces
		{ report_class_declaration ($3); modifier_value = 0; }
	interface_body
;

extends_interfaces:
	EXTENDS_TK interface_type
|	extends_interfaces C_TK interface_type
;

interface_body:
	OCB_TK CCB_TK
		{ pop_class_context (); }
|	OCB_TK interface_member_declarations CCB_TK
		{ pop_class_context (); }
;

interface_member_declarations:
	interface_member_declaration
|	interface_member_declarations interface_member_declaration
;

interface_member_declaration:
	constant_declaration
|	abstract_method_declaration
|	class_declaration	/* Added, JDK1.1 inner classes */
|	interface_declaration	/* Added, JDK1.1 inner classes */
;

constant_declaration:
	field_declaration
;

abstract_method_declaration:
	method_header SC_TK
;

/* 19.10 Productions from 10: Arrays  */
array_initializer:
	OCB_TK CCB_TK
|	OCB_TK variable_initializers CCB_TK
|	OCB_TK C_TK CCB_TK
|	OCB_TK variable_initializers C_TK CCB_TK
;

variable_initializers:
	variable_initializer
|	variable_initializers C_TK variable_initializer
;

/* 19.11 Production from 14: Blocks and Statements  */
block:
	OCB_TK CCB_TK
|	OCB_TK block_statements CCB_TK
;

block_statements:
	block_statement
|	block_statements block_statement
;

block_statement:
	local_variable_declaration_statement
|	statement
|	class_declaration	/* Added, JDK1.1 inner classes */
;

local_variable_declaration_statement:
	local_variable_declaration SC_TK /* Can't catch missing ';' here */
;

local_variable_declaration:
	type variable_declarators
		{ USE_ABSORBER; }
|	modifiers type variable_declarators /* Added, JDK1.1 final locals */
		{ modifier_value = 0; }
;

statement:
	statement_without_trailing_substatement
|	labeled_statement
|	if_then_statement
|	if_then_else_statement
|	while_statement
|	for_statement
;

statement_nsi:
	statement_without_trailing_substatement
|	labeled_statement_nsi
|	if_then_else_statement_nsi
|	while_statement_nsi
|	for_statement_nsi
;

statement_without_trailing_substatement:
	block
|	empty_statement
|	expression_statement
|	switch_statement
|	do_statement
|	break_statement
|	continue_statement
|	return_statement
|	synchronized_statement
|	throw_statement
|	try_statement
;

empty_statement:
	SC_TK
;

label_decl:
	identifier REL_CL_TK
		{ USE_ABSORBER; }
;

labeled_statement:
	label_decl statement
;

labeled_statement_nsi:
	label_decl statement_nsi
;

/* We concentrate here a bunch of error handling rules that we couldn't write
   earlier, because expression_statement catches a missing ';'.  */
expression_statement:
	statement_expression SC_TK
;

statement_expression: 
	assignment
|	pre_increment_expression
|	pre_decrement_expression
|	post_increment_expression
|	post_decrement_expression
|	method_invocation
|	class_instance_creation_expression
;

if_then_statement:
	IF_TK OP_TK expression CP_TK statement { ++complexity; }
;

if_then_else_statement:
	IF_TK OP_TK expression CP_TK statement_nsi ELSE_TK statement
	{ ++complexity; }
;

if_then_else_statement_nsi:
	IF_TK OP_TK expression CP_TK statement_nsi ELSE_TK statement_nsi
	{ ++complexity; }
;

switch_statement:
	SWITCH_TK OP_TK expression CP_TK switch_block
;

switch_block:
	OCB_TK CCB_TK
|	OCB_TK switch_labels CCB_TK
|	OCB_TK switch_block_statement_groups CCB_TK
|	OCB_TK switch_block_statement_groups switch_labels CCB_TK
;

switch_block_statement_groups: 
	switch_block_statement_group
|	switch_block_statement_groups switch_block_statement_group
;

switch_block_statement_group:
	switch_labels block_statements { ++complexity; }
;


switch_labels:
	switch_label
|	switch_labels switch_label
;

switch_label:
	CASE_TK constant_expression REL_CL_TK
|	DEFAULT_TK REL_CL_TK
;

while_expression:
	WHILE_TK OP_TK expression CP_TK { ++complexity; }
;

while_statement:
	while_expression statement
;

while_statement_nsi:
	while_expression statement_nsi
;

do_statement_begin:
	DO_TK
;

do_statement: 
	do_statement_begin statement WHILE_TK OP_TK expression CP_TK SC_TK
	{ ++complexity; }
;

for_statement:
	for_begin SC_TK expression SC_TK for_update CP_TK statement
|	for_begin SC_TK SC_TK for_update CP_TK statement
;

for_statement_nsi:
	for_begin SC_TK expression SC_TK for_update CP_TK statement_nsi
|	for_begin SC_TK SC_TK for_update CP_TK statement_nsi
;

for_header:
	FOR_TK OP_TK
;

for_begin:
	for_header for_init { ++complexity; }
;
for_init:			/* Can be empty */
|	statement_expression_list
|	local_variable_declaration
;

for_update:			/* Can be empty */
|	statement_expression_list
;

statement_expression_list:
	statement_expression
|	statement_expression_list C_TK statement_expression
;

break_statement:
	BREAK_TK SC_TK
|	BREAK_TK identifier SC_TK
;

/* `continue' with a label is considered for complexity but ordinary
   continue is not.  */
continue_statement:
	CONTINUE_TK SC_TK
	|       CONTINUE_TK identifier SC_TK { ++complexity; }
;

return_statement:
	RETURN_TK SC_TK
|	RETURN_TK expression SC_TK
;

throw_statement:
	THROW_TK expression SC_TK { ++complexity; }
;

synchronized_statement:
	synchronized OP_TK expression CP_TK block
|	synchronized OP_TK expression CP_TK error
;

synchronized:			/* Test lval.sub_token here */
	MODIFIER_TK
		{ USE_ABSORBER; }
;

try_statement:
	TRY_TK block catches
|	TRY_TK block finally
|	TRY_TK block catches finally
;

catches:
	catch_clause
|	catches catch_clause
;

catch_clause:
	CATCH_TK OP_TK formal_parameter CP_TK block { ++complexity; }
;

finally:
	FINALLY_TK block { ++complexity; }
;

/* 19.12 Production from 15: Expressions  */
primary:
	primary_no_new_array
|	array_creation_expression
;

primary_no_new_array:
	literal
|	THIS_TK
|	OP_TK expression CP_TK
|	class_instance_creation_expression
|	field_access
|	method_invocation
|	array_access
|	type_literals
        /* Added, JDK1.1 inner classes. Documentation is wrong
           refering to a 'ClassName' (class_name) rule that doesn't
           exist. Used name instead.  */
|	name DOT_TK THIS_TK
		{ USE_ABSORBER; }
;

type_literals:
	name DOT_TK CLASS_TK
		{ USE_ABSORBER; }
|	array_type DOT_TK CLASS_TK
		{ USE_ABSORBER; }
|	primitive_type DOT_TK CLASS_TK
		{ USE_ABSORBER; }
|	VOID_TK DOT_TK CLASS_TK
		{ USE_ABSORBER; }
;

class_instance_creation_expression:
	NEW_TK class_type OP_TK argument_list CP_TK
|	NEW_TK class_type OP_TK CP_TK
|	anonymous_class_creation
|	something_dot_new identifier OP_TK CP_TK
|	something_dot_new identifier OP_TK CP_TK class_body
|	something_dot_new identifier OP_TK argument_list CP_TK
|	something_dot_new identifier OP_TK argument_list CP_TK class_body
;

anonymous_class_creation:
	NEW_TK class_type OP_TK CP_TK
		{ report_class_declaration (NULL); }
	class_body         
|	NEW_TK class_type OP_TK argument_list CP_TK
		{ report_class_declaration (NULL); }
	class_body
;

something_dot_new:		/* Added, not part of the specs. */
	name DOT_TK NEW_TK
		{ USE_ABSORBER; }
|	primary DOT_TK NEW_TK
;

argument_list:
	expression
|	argument_list C_TK expression
|	argument_list C_TK error
;

array_creation_expression:
	NEW_TK primitive_type dim_exprs
|	NEW_TK class_or_interface_type dim_exprs
|	NEW_TK primitive_type dim_exprs dims
|	NEW_TK class_or_interface_type dim_exprs dims
        /* Added, JDK1.1 anonymous array. Initial documentation rule
           modified */
|	NEW_TK class_or_interface_type dims array_initializer
|	NEW_TK primitive_type dims array_initializer
;

dim_exprs:
	dim_expr
|	dim_exprs dim_expr
;

dim_expr:
	OSB_TK expression CSB_TK
;

dims:				
	OSB_TK CSB_TK
		{ bracket_count = 1; }
|	dims OSB_TK CSB_TK
		{ bracket_count++; }
;

field_access:
	primary DOT_TK identifier
|	SUPER_TK DOT_TK identifier
;

/* We include method invocation in the complexity measure on the
   theory that most method calls are virtual and therefore involve a
   decision point.  */
method_invocation:
	name OP_TK CP_TK
		{ USE_ABSORBER; ++complexity; }
|	name OP_TK argument_list CP_TK
		{ USE_ABSORBER; ++complexity; }
|	primary DOT_TK identifier OP_TK CP_TK { ++complexity; }
|	primary DOT_TK identifier OP_TK argument_list CP_TK { ++complexity; }
|	SUPER_TK DOT_TK identifier OP_TK CP_TK { ++complexity; }
|	SUPER_TK DOT_TK identifier OP_TK argument_list CP_TK { ++complexity; }
;

array_access:
	name OSB_TK expression CSB_TK
		{ USE_ABSORBER; }
|	primary_no_new_array OSB_TK expression CSB_TK
;

postfix_expression:
	primary
|	name
		{ USE_ABSORBER; }
|	post_increment_expression
|	post_decrement_expression
;

post_increment_expression:
	postfix_expression INCR_TK
;

post_decrement_expression:
	postfix_expression DECR_TK
;

unary_expression:
	pre_increment_expression
|	pre_decrement_expression
|	PLUS_TK unary_expression
|	MINUS_TK unary_expression
|	unary_expression_not_plus_minus
;

pre_increment_expression:
	INCR_TK unary_expression
;

pre_decrement_expression:
	DECR_TK unary_expression
;

unary_expression_not_plus_minus:
	postfix_expression
|	NOT_TK unary_expression
|	NEG_TK unary_expression
|	cast_expression
;

cast_expression:		/* Error handling here is potentially weak */
	OP_TK primitive_type dims CP_TK unary_expression
|	OP_TK primitive_type CP_TK unary_expression
|	OP_TK expression CP_TK unary_expression_not_plus_minus
|	OP_TK name dims CP_TK unary_expression_not_plus_minus
;

multiplicative_expression:
	unary_expression
|	multiplicative_expression MULT_TK unary_expression
|	multiplicative_expression DIV_TK unary_expression
|	multiplicative_expression REM_TK unary_expression
;

additive_expression:
	multiplicative_expression
|	additive_expression PLUS_TK multiplicative_expression
|	additive_expression MINUS_TK multiplicative_expression
;

shift_expression:
	additive_expression
|	shift_expression LS_TK additive_expression
|	shift_expression SRS_TK additive_expression
|	shift_expression ZRS_TK additive_expression
;

relational_expression:
	shift_expression
|	relational_expression LT_TK shift_expression
|	relational_expression GT_TK shift_expression
|	relational_expression LTE_TK shift_expression
|	relational_expression GTE_TK shift_expression
|	relational_expression INSTANCEOF_TK reference_type
;

equality_expression:
	relational_expression
|	equality_expression EQ_TK relational_expression
|	equality_expression NEQ_TK relational_expression
;

and_expression:
	equality_expression
|	and_expression AND_TK equality_expression
;

exclusive_or_expression:
	and_expression
|	exclusive_or_expression XOR_TK and_expression
;

inclusive_or_expression:
	exclusive_or_expression
|	inclusive_or_expression OR_TK exclusive_or_expression
;

conditional_and_expression:
	inclusive_or_expression
|	conditional_and_expression BOOL_AND_TK inclusive_or_expression
	{ ++complexity; }
;

conditional_or_expression:
	conditional_and_expression
|	conditional_or_expression BOOL_OR_TK conditional_and_expression
	{ ++complexity; }
;

conditional_expression:		/* Error handling here is weak */
	conditional_or_expression
|	conditional_or_expression REL_QM_TK expression REL_CL_TK conditional_expression
	{ ++complexity; }
;

assignment_expression:
	conditional_expression
|	assignment
;

assignment:
	left_hand_side assignment_operator assignment_expression
;

left_hand_side:
	name
		{ USE_ABSORBER; }
|	field_access
|	array_access
;

assignment_operator:
	ASSIGN_ANY_TK
|	ASSIGN_TK
;

expression:
	assignment_expression
;

constant_expression:
	expression
;

%%

/* Create a new parser context */

void
java_push_parser_context ()
{
  struct parser_ctxt *new = 
    (struct parser_ctxt *) xcalloc (1, sizeof (struct parser_ctxt));

  new->next = ctxp;
  ctxp = new;
}  

static void
push_class_context (name)
    const char *name;
{
  /* If we already have CURRENT_CLASS set, we're in an inter
     class. Mangle its name. */
  if (current_class)
    {
      const char *p;
      char anonymous [3];
      int additional_length;
      
      /* NAME set to NULL indicates an anonymous class, which are named by
	 numbering them. */
      if (!name)
	{
	  sprintf (anonymous, "%d", ++anonymous_count);
	  p = anonymous;
	}
      else
	p = name;
      
      additional_length = strlen (p)+1; /* +1 for `$' */
      current_class = xrealloc (current_class, 
				current_class_length + additional_length + 1);
      current_class [current_class_length] = '$';
      strcpy (&current_class [current_class_length+1], p);
      current_class_length += additional_length;
    }
  else
    {
      if (!name)
	return;
      current_class_length = strlen (name);
      current_class = xmalloc (current_class_length+1);
      strcpy (current_class, name);
    }
}

static void
pop_class_context ()
{
  /* Go back to the last `$' and cut. */
  while (--current_class_length > 0
        && current_class [current_class_length] != '$')
    ;
  if (current_class_length)
    {
      current_class = xrealloc (current_class, current_class_length+1);
      current_class [current_class_length] = '\0';
    }
  else
    {
      current_class = NULL;
      anonymous_count = 0;
    }
}

/* Actions defined here */

static void
report_class_declaration (name)
     const char * name;
{
  extern int flag_dump_class, flag_list_filename;

  push_class_context (name);
  if (flag_dump_class)
    {
      if (!previous_output)
	{
	  if (flag_list_filename)
	    fprintf (out, "%s: ", input_filename);
	  previous_output = 1;
	}
	
      if (package_name)
	fprintf (out, "%s.%s ", package_name, current_class);
      else
	fprintf (out, "%s ", current_class);
    }
}

static void
report_main_declaration (declarator)
     struct method_declarator *declarator;
{
  extern int flag_find_main;

  if (flag_find_main
      && modifier_value == 2
      && !strcmp (declarator->method_name, "main") 
      && declarator->args 
      && declarator->args [0] == '[' 
      && (! strcmp (declarator->args+1, "String")
	  || ! strcmp (declarator->args + 1, "java.lang.String"))
      && current_class)
    {
      if (!previous_output)
	{
	  if (package_name)
	    fprintf (out, "%s.%s ", package_name, current_class);
	  else
	    fprintf (out, "%s", current_class);
	  previous_output = 1;
	}
    }
}

void
report ()
{
  extern int flag_complexity;
  if (flag_complexity)
    fprintf (out, "%s %d\n", input_filename, complexity);
}

/* Reset global status used by the report functions.  */

void reset_report ()
{
  previous_output = 0;
  package_name = NULL;
  current_class = NULL;
  complexity = 0;
}

void
yyerror (msg)
     const char *msg ATTRIBUTE_UNUSED;
{
  fprintf (stderr, "%s: %d: %s\n", input_filename, lineno, msg);
  exit (1);
}
