+opt_type_parameter_list
+ : /* empty */ { $$ = null; }
+ | type_parameter_list { $$ = $1; }
+ ;
+
+type_parameter_list
+ : OP_LT type_parameters OP_GT { $$ = $2; }
+ ;
+
+type_parameters
+ : type_parameter {
+ //
+ // Do some profiling to find the optimal size, for now we
+ // assume most people will be generic on one type (saves space
+ //
+ ArrayList type_parameters = new ArrayList (1);
+ type_parameters.Add ($1);
+ $$ = type_parameters;
+ }
+ | type_parameters COMMA type_parameter {
+ ArrayList type_parameters = (ArrayList) $1;
+
+ type_parameters.Add ($3);
+ $$ = type_parameters;
+ }
+ ;
+
+type_parameter
+ : IDENTIFIER
+ ;
+
+opt_type_parameter_constraints_clauses
+ : /* empty */ { $$ = null; }
+ | type_parameter_constraints_clauses
+ { $$ = $1; }
+ ;
+
+type_parameter_constraints_clauses
+ : type_parameter_constraints_clause
+ | type_parameter_constraints_clauses type_parameter_constraints_clause
+ ;
+
+type_parameter_constraint_clause
+ : WHERE type_parameter COLON type_parameter_constraints {
+ $$ = new Constraints ((string) $2, (ArrayList) $4);
+ }
+ ;
+
+type_parameter_constraint
+ //
+ // we merge class and interface constraints, the tree resolution
+ // will split them apart.
+ //
+ | interface_constraints { $$ = $1; }
+ ;
+
+interface_constraints
+ : type {
+ ArrayList constraints = new ArrayList (1);
+ constraints.Add ($1);
+ $$ = constraints;
+ }
+ | NEW OPEN_PARENS CLOSE_PARENS {
+ ArrayList constraints = new ArrayList (1);
+ constraints.Add (true);
+ $$ = constraints;
+ }
+ | interface_constraints COMMA type {
+ ArrayList constraints = (ArrayList) $1;
+
+ constraints.Add ($3);
+ $$ = constraints;
+ }
+ ;
+