// Filename: dcParser.yxx
// Created by:  drose (05Oct00)
// 
////////////////////////////////////////////////////////////////////

%{
#include "dcLexerDefs.h"
#include "dcParserDefs.h"
#include "dcFile.h"
#include "dcClass.h"
#include "dcSwitch.h"
#include "dcAtomicField.h"
#include "dcMolecularField.h"
#include "dcClassParameter.h"
#include "dcSwitchParameter.h"
#include "dcArrayParameter.h"
#include "dcSimpleParameter.h"
#include "dcTypedef.h"
#include "dcPacker.h"
#include "dcNumericRange.h"

// Because our token type contains objects of type string, which
// require correct copy construction (and not simply memcpying), we
// cannot use bison's built-in auto-stack-grow feature.  As an easy
// solution, we ensure here that we have enough yacc stack to start
// with, and that it doesn't ever try to grow.
#define YYINITDEPTH 1000
#define YYMAXDEPTH 1000

static DCFile *dc_file = (DCFile *)NULL;
static DCClass *current_class = (DCClass *)NULL;
static DCSwitch *current_switch = (DCSwitch *)NULL;
static DCAtomicField *current_atomic = (DCAtomicField *)NULL;
static DCMolecularField *current_molecular = (DCMolecularField *)NULL;
static DCParameter *current_parameter = (DCParameter *)NULL;
static DCPacker default_packer;
static DCPacker *current_packer;
static DCDoubleRange double_range;
static DCUnsignedIntRange uint_range;
static DCField *parameter_description = (DCField *)NULL;

////////////////////////////////////////////////////////////////////
// Defining the interface to the parser.
////////////////////////////////////////////////////////////////////

void
dc_init_parser(istream &in, const string &filename, DCFile &file) {
  dc_file = &file;
  dc_init_lexer(in, filename);
}

void
dc_init_parser_parameter_value(istream &in, const string &filename, 
                               DCPacker &packer) {
  dc_file = NULL;
  current_packer = &packer;
  dc_init_lexer(in, filename);
  dc_start_parameter_value();
}

void
dc_init_parser_parameter_description(istream &in, const string &filename,
                                     DCFile *file) {
  dc_file = file;
  dc_init_lexer(in, filename);
  parameter_description = NULL;
  dc_start_parameter_description();
}

DCField *
dc_get_parameter_description() {
  return parameter_description;
}

void
dc_cleanup_parser() {
  dc_file = (DCFile *)NULL;
}

%}

%token <u.uint64> UNSIGNED_INTEGER
%token <u.int64> SIGNED_INTEGER
%token <u.real> REAL
%token <str> STRING HEX_STRING IDENTIFIER

%token KW_DCLASS 
%token KW_STRUCT 
%token KW_FROM 
%token KW_IMPORT 
%token KW_TYPEDEF 
%token KW_SWITCH
%token KW_CASE

%token KW_INT8
%token KW_INT16
%token KW_INT32
%token KW_INT64
%token KW_UINT8
%token KW_UINT16
%token KW_UINT32
%token KW_UINT64
%token KW_FLOAT64
%token KW_STRING
%token KW_BLOB
%token KW_BLOB32
%token KW_INT8ARRAY
%token KW_INT16ARRAY
%token KW_INT32ARRAY
%token KW_UINT8ARRAY
%token KW_UINT16ARRAY
%token KW_UINT32ARRAY
%token KW_UINT32UINT8ARRAY
%token KW_CHAR

%token KW_REQUIRED
%token KW_BROADCAST
%token KW_P2P
%token KW_RAM
%token KW_DB
%token KW_CLSEND
%token KW_CLRECV
%token KW_OWNSEND
%token KW_AIRECV

/* These special tokens are used to set the starting state of the
   parser.  The lexer places the appropriate one of these on the head
   of the input stream. */
%token START_DC
%token START_PARAMETER_VALUE
%token START_PARAMETER_DESCRIPTION

%type <u.atomic> atomic_name
%type <u.s_int> server_flags
%type <u.s_int> no_server_flags
%type <u.dclass> dclass_or_struct
%type <u.dclass> dclass_name
%type <u.dclass> dclass
%type <u.field> dclass_field
%type <u.dclass> struct_name
%type <u.dclass> struct
%type <u.field> struct_field
%type <u.field> parameter_description
%type <u.dswitch> switch
%type <u.field> switch_field
%type <u.field> atomic_field
%type <u.field> molecular_field
%type <u.subatomic> type_token
%type <u.parameter> simple_type_name
%type <u.parameter> type_name
%type <u.parameter> type_definition
%type <u.parameter> named_parameter
%type <u.parameter> unnamed_parameter
%type <u.parameter> named_parameter_with_default
%type <u.parameter> unnamed_parameter_with_default
%type <u.parameter> parameter
%type <u.parameter> parameter_with_default
%type <u.parameter> parameter_definition
%type <str> import_identifier
%type <str> slash_identifier
%type <str> optional_name
%type <u.s_uint> char_or_uint
%type <u.s_uint> small_unsigned_integer
%type <u.s_uint> small_negative_integer
%type <u.int64> signed_integer
%type <u.uint64> unsigned_integer
%type <u.real> char_or_number
%type <u.real> number

%%

grammar:
	START_DC dc
	| START_PARAMETER_VALUE	parameter_value
	| START_PARAMETER_DESCRIPTION parameter_description
{
  parameter_description = $2;
}
	;

dc:
        empty
        | dc ';'
        | dc dclass_or_struct
{
  if (!dc_file->add_class($2)) {
    DCClass *old_class = dc_file->get_class_by_name($2->get_name());
    if (old_class != (DCClass *)NULL && old_class->is_bogus_class()) {
      yyerror("Base class defined after its first reference: " + $2->get_name());
    } else {
      yyerror("Duplicate class name: " + $2->get_name());
    }
  }
}
        | dc switch
{
  if (!dc_file->add_switch($2)) {
    yyerror("Duplicate class name: " + $2->get_name());
  }
}
        | dc import
        | dc typedef_decl
        ;

slash_identifier:
        IDENTIFIER
	| slash_identifier '/' IDENTIFIER
{
  $$ = $1 + string("/") + $3;
}
	;

import_identifier:
        slash_identifier
	| import_identifier '.' slash_identifier
{
  $$ = $1 + string(".") + $3;
}
	;

import:
	KW_IMPORT import_identifier
{
  dc_file->add_import_module($2);
}
	| KW_FROM import_identifier KW_IMPORT 
{
  dc_file->add_import_module($2);
}
	import_symbol_list_or_star
	;

import_symbol_list_or_star:
	import_symbol_list
	| '*'
{
  dc_file->add_import_symbol("*");
}
	;

import_symbol_list:
	slash_identifier
{
  dc_file->add_import_symbol($1);
}
	| import_symbol_list ',' slash_identifier
{
  dc_file->add_import_symbol($3);
}
	;

typedef_decl:
        KW_TYPEDEF parameter_with_default
{
  if ($2 != (DCParameter *)NULL) {
    DCTypedef *dtypedef = new DCTypedef($2);
    
    if (!dc_file->add_typedef(dtypedef)) {
      DCTypedef *old_typedef = dc_file->get_typedef_by_name(dtypedef->get_name());
      if (old_typedef->is_bogus_typedef()) {
        yyerror("typedef defined after its first reference: " + dtypedef->get_name());
      } else {
        yyerror("Duplicate typedef name: " + dtypedef->get_name());
      }
    }
  }
}
	;

dclass_or_struct:
        dclass
	| struct
	;

dclass:
        KW_DCLASS optional_name 
{
  $$ = current_class;
  current_class = new DCClass(dc_file, $2, false, false);
}
        dclass_derivation '{' dclass_fields '}'
{
  $$ = current_class;
  current_class = $<u.dclass>3;
}
        ;

dclass_name:
        IDENTIFIER
{
  if (dc_file == (DCFile *)NULL) {
    yyerror("No DCFile available, so no class names are predefined.");
    $$ = NULL;

  } else {
    DCClass *dclass = dc_file->get_class_by_name($1);
    if (dclass == (DCClass *)NULL) {
      // Create a bogus class as a forward reference.
      dclass = new DCClass(dc_file, $1, false, true);
      dc_file->add_class(dclass);
    }
    if (dclass->is_struct()) {
      yyerror("struct name not allowed");
    }
  
    $$ = dclass;
  }
}
	;

dclass_derivation:
        empty
        | ':' dclass_base_list
        ;

dclass_base_list:
        dclass_name
{
  if ($1 != (DCClass *)NULL) {
    current_class->add_parent($1);
  }
}
        | dclass_base_list ',' dclass_name
{
  if (!dc_multiple_inheritance) {
    yyerror("Multiple inheritance is not supported without \"dc-multiple-inheritance 1\" in your Config.prc file.");

  } else {
    if ($3 != (DCClass *)NULL) {
      current_class->add_parent($3);
    }
  }
}
        ;

dclass_fields:
        empty
        | dclass_fields ';'
        | dclass_fields dclass_field
{
  if ($2 == (DCField *)NULL) {
    // Pass this error up.
  } else if (!current_class->add_field($2)) {
    yyerror("Duplicate field name: " + $2->get_name());
  } else if ($2->get_number() < 0) {
    yyerror("A non-network field cannot be stored on a dclass");
  }
}
        ;

dclass_field:
	atomic_field server_flags
{
  if ($1 != (DCField *)NULL) {
    if ($1->get_name().empty()) {
      yyerror("Field name required.");
    }
    $1->set_flags($2);
  }
  $$ = $1;
}
	| molecular_field no_server_flags
	| unnamed_parameter_with_default server_flags ';'
{
  yyerror("Unnamed parameters are not allowed on a dclass");
  if ($1 != (DCField *)NULL) {
    $1->set_flags($2);
  }
  $$ = $1;
}
	| named_parameter_with_default server_flags
{
  if ($1 != (DCField *)NULL) {
    $1->set_flags($2);
  }
  $$ = $1;
}
	;

struct:
        KW_STRUCT optional_name 
{
  $$ = current_class;
  current_class = new DCClass(dc_file, $2, true, false);
}
        struct_derivation '{' struct_fields '}'
{
  $$ = current_class;
  current_class = $<u.dclass>3;
}
        ;

struct_name:
        IDENTIFIER
{
  if (dc_file == (DCFile *)NULL) {
    yyerror("No DCFile available, so no struct names are predefined.");
    $$ = NULL;

  } else {
    DCClass *dstruct = dc_file->get_class_by_name($1);
    if (dstruct == (DCClass *)NULL) {
      // Create a bogus class as a forward reference.
      dstruct = new DCClass(dc_file, $1, false, true);
      dc_file->add_class(dstruct);
    }
    if (!dstruct->is_struct()) {
      yyerror("struct name required");
    }
  
    $$ = dstruct;
  }
}
	;

struct_derivation:
        empty
        | ':' struct_base_list
        ;

struct_base_list:
        struct_name
{
  if ($1 != (DCClass *)NULL) {
    current_class->add_parent($1);
  }
}
        | struct_base_list ',' struct_name
{
  if ($3 != (DCClass *)NULL) {
    current_class->add_parent($3);
  }
}
        ;

struct_fields:
        empty
        | struct_fields ';'
        | struct_fields struct_field
{
  if ($2 == (DCField *)NULL) {
    // Pass this error up.
  } else if (!current_class->add_field($2)) {
    yyerror("Duplicate field name: " + $2->get_name());
  }
}
        ;

struct_field:
	atomic_field no_server_flags
{
  if ($1->get_name().empty()) {
    yyerror("Field name required.");
  }
  $$ = $1;
}
	| molecular_field no_server_flags
	| unnamed_parameter_with_default no_server_flags ';'
{
  $$ = $1;
}
	| named_parameter_with_default no_server_flags
{
  $$ = $1;
}
	;

atomic_field:
        optional_name '('
{
  $$ = current_atomic;
  current_atomic = new DCAtomicField($1, current_class);
}
        parameter_list ')'
{
  $$ = current_atomic;
  current_atomic = $<u.atomic>3;
}
        ;

parameter_list:
        empty
        | nonempty_parameter_list
        ;

nonempty_parameter_list:
        atomic_element
        | nonempty_parameter_list ',' atomic_element
        ;

atomic_element:
        parameter_with_default
{
  if ($1 != (DCParameter *)NULL) {
    current_atomic->add_element($1);
  }
}
	;

named_parameter:
        type_definition
{
  current_parameter = $1;
}
        parameter_definition
{
  $$ = $3;
}
	;

unnamed_parameter:
	type_definition
	;

named_parameter_with_default:
        named_parameter
	| named_parameter '=' 
{
  current_packer = &default_packer;
  current_packer->clear_data();
  if ($1 != (DCField *)NULL) {
    current_packer->begin_pack($1);
  }
}
	parameter_value
{
  bool is_valid = false;
  if ($1 != (DCField *)NULL) {
    is_valid = $1->is_valid();
  }
  if (current_packer->end_pack()) {
    $1->set_default_value(current_packer->get_string());

  } else {
    if (is_valid) {
      yyerror("Invalid default value for type");
    }
    // If the current parameter isn't valid, we don't mind a pack
    // error (there's no way for us to validate the syntax).  So we'll
    // just ignore the default value in this case.
  }
}
	;

unnamed_parameter_with_default:
        unnamed_parameter
	| unnamed_parameter '=' 
{
  current_packer = &default_packer;
  current_packer->clear_data();
  if ($1 != (DCField *)NULL) {
    current_packer->begin_pack($1);
  }
}
	parameter_value
{
  bool is_valid = false;
  if ($1 != (DCField *)NULL) {
    is_valid = $1->is_valid();
  }
  if (current_packer->end_pack()) {
    $1->set_default_value(current_packer->get_string());

  } else {
    if (is_valid) {
      yyerror("Invalid default value for type");
    }
    // If the current parameter isn't valid, we don't mind a pack
    // error (there's no way for us to validate the syntax).  So we'll
    // just ignore the default value in this case.
  }
}
	;

parameter:
        named_parameter
        | unnamed_parameter
        ;

parameter_with_default:
        named_parameter_with_default
        | unnamed_parameter_with_default
        ;

parameter_description:
	atomic_field no_server_flags
{
  $$ = $1;
}
	| unnamed_parameter_with_default no_server_flags
{
  $$ = $1;
}
	| named_parameter_with_default no_server_flags
{
  $$ = $1;
}
	;

simple_type_name:
	type_token
{
  $$ = new DCSimpleParameter($1);
}
	| simple_type_name '(' double_range ')'
{
  DCSimpleParameter *simple_param = $1->as_simple_parameter();
  nassertr(simple_param != (DCSimpleParameter *)NULL, 0);
  if (!simple_param->set_range(double_range)) {
    yyerror("Inappropriate range for type");
  }
  $$ = simple_param;
}
        | simple_type_name '/' small_unsigned_integer
{
  DCSimpleParameter *simple_param = $1->as_simple_parameter();
  nassertr(simple_param != (DCSimpleParameter *)NULL, 0);
  if (!simple_param->is_numeric_type()) {
    yyerror("A divisor is only valid on a numeric type.");

  } else if (!simple_param->set_divisor($3)) {
    yyerror("Invalid divisor.");

  } else if (simple_param->has_modulus() && !simple_param->set_modulus(simple_param->get_modulus())) {
    // Changing the divisor may change the valid range for the modulus.
    yyerror("Invalid modulus.");
  }
  $$ = simple_param;
}
        | simple_type_name '%' number
{ 
  DCSimpleParameter *simple_param = $1->as_simple_parameter();
  nassertr(simple_param != (DCSimpleParameter *)NULL, 0);
  if (!simple_param->is_numeric_type()) {
    yyerror("A divisor is only valid on a numeric type.");

  } else if (!simple_param->set_modulus($3)) {
    yyerror("Invalid modulus.");
  }
  $$ = simple_param;
}
	;

type_name:
        simple_type_name
	| IDENTIFIER
{
  if (dc_file == (DCFile *)NULL) {
    yyerror("Invalid type.");
    $$ = NULL;

  } else {
    DCTypedef *dtypedef = dc_file->get_typedef_by_name($1);
    if (dtypedef == (DCTypedef *)NULL) {
      // Maybe it's a class name.
      DCClass *dclass = dc_file->get_class_by_name($1);
      if (dclass != (DCClass *)NULL) {
        if (!dclass->is_struct()) {
          yyerror("cannot embed a dclass object within a message; use a struct");
        }
        // Create an implicit typedef for this.
        dtypedef = new DCTypedef(new DCClassParameter(dclass), true);
      } else {
        // Maybe it's a switch name.
        DCSwitch *dswitch = dc_file->get_switch_by_name($1);
        if (dswitch != (DCSwitch *)NULL) {
          // This also gets an implicit typedef.
          dtypedef = new DCTypedef(new DCSwitchParameter(dswitch), true);
        } else {
          // It's an undefined typedef.  Create a bogus forward reference.
          dtypedef = new DCTypedef($1);
        }
      }
      
      dc_file->add_typedef(dtypedef);
    }
    
    $$ = dtypedef->make_new_parameter();
  }
}
	| struct
{
  // This is an inline struct definition.
  if ($1 == (DCClass *)NULL) {
    $$ = NULL;
  } else {
    if (dc_file != (DCFile *)NULL) {
      dc_file->add_thing_to_delete($1);
    } else {
      // This is a memory leak--this happens when we put an anonymous
      // struct reference within the string passed to
      // DCPackerInterface::check_match().  Maybe it doesn't really matter.
    }
    $$ = new DCClassParameter($1);
  }
}
	| switch
{
  // This is an inline switch definition.
  if ($1 == (DCSwitch *)NULL) {
    $$ = NULL;
  } else {
    if (dc_file != (DCFile *)NULL) {
      dc_file->add_thing_to_delete($1);
    } else {
      // This is a memory leak--this happens when we put an anonymous
      // switch reference within the string passed to
      // DCPackerInterface::check_match().  Maybe it doesn't really matter.
    }
    $$ = new DCSwitchParameter($1);
  }
}
	;

double_range:
	empty
{
  double_range.clear();
}
	| char_or_number
{
  double_range.clear();
  if (!double_range.add_range($1, $1)) {
    yyerror("Overlapping range");
  }
}
	| char_or_number '-' char_or_number
{
  double_range.clear();
  if (!double_range.add_range($1, $3)) {
    yyerror("Overlapping range");
  }
}
	| char_or_number number
{
  double_range.clear();
  if ($2 >= 0) {
    yyerror("Syntax error");
  } else if (!double_range.add_range($1, -$2)) {
    yyerror("Overlapping range");
  }
}
	| double_range ',' char_or_number
{
  if (!double_range.add_range($3, $3)) {
    yyerror("Overlapping range");
  }
}
	| double_range ',' char_or_number '-' char_or_number
{
  if (!double_range.add_range($3, $5)) {
    yyerror("Overlapping range");
  }
}
	| double_range ',' char_or_number number
{
  if ($4 >= 0) {
    yyerror("Syntax error");
  } else if (!double_range.add_range($3, -$4)) {
    yyerror("Overlapping range");
  }
}
	;

uint_range:
	empty
{
  uint_range.clear();
}
	| char_or_uint
{
  uint_range.clear();
  if (!uint_range.add_range($1, $1)) {
    yyerror("Overlapping range");
  }
}
	| char_or_uint '-' char_or_uint
{
  uint_range.clear();
  if (!uint_range.add_range($1, $3)) {
    yyerror("Overlapping range");
  }
}
	| char_or_uint small_negative_integer
{
  uint_range.clear();
  if (!uint_range.add_range($1, $2)) {
    yyerror("Overlapping range");
  }
}
	| uint_range ',' char_or_uint
{
  if (!uint_range.add_range($3, $3)) {
    yyerror("Overlapping range");
  }
}
	| uint_range ',' char_or_uint '-' char_or_uint
{
  if (!uint_range.add_range($3, $5)) {
    yyerror("Overlapping range");
  }
}
	| uint_range ',' char_or_uint small_negative_integer
{
  if (!uint_range.add_range($3, $4)) {
    yyerror("Overlapping range");
  }
}
	;

type_definition:
        type_name
	| type_definition '[' uint_range ']'
{
  if ($1 == (DCParameter *)NULL) {
    $$ = NULL;
  } else {
    $$ = $1->append_array_specification(uint_range);
  }
}
	;

parameter_definition:
        IDENTIFIER
{
  current_parameter->set_name($1);
  $$ = current_parameter;
}
        | parameter_definition '/' small_unsigned_integer
{
  DCSimpleParameter *simple_param = $1->as_simple_parameter();
  if (simple_param == NULL || simple_param->get_typedef() != (DCTypedef *)NULL) {
    yyerror("A divisor is only allowed on a primitive type.");

  } else if (!simple_param->is_numeric_type()) {
      yyerror("A divisor is only valid on a numeric type.");

  } else {
    if (!simple_param->set_divisor($3)) {
      yyerror("Invalid divisor.");
    }
  }
}
        | parameter_definition '%' number
{
  DCSimpleParameter *simple_param = $1->as_simple_parameter();
  if (simple_param == NULL || simple_param->get_typedef() != (DCTypedef *)NULL) {
    yyerror("A modulus is only allowed on a primitive type.");

  } else if (!simple_param->is_numeric_type()) {
      yyerror("A modulus is only valid on a numeric type.");

  } else {
    if (!simple_param->set_modulus($3)) {
      yyerror("Invalid modulus.");
    }
  }
}
	| parameter_definition '[' uint_range ']'
{
  $$ = $1->append_array_specification(uint_range);
}
	;

char_or_uint:
	STRING
{
  if ($1.length() != 1) {
    yyerror("Single character required.");
    $$ = 0;
  } else {
    $$ = (unsigned char)$1[0];
  }
}
	| small_unsigned_integer
	;

small_unsigned_integer:
	UNSIGNED_INTEGER
{
  $$ = (unsigned int)$1;
  if ($$ != $1) {
    yyerror("Number out of range.");
    $$ = 1;
  }
}
	;

small_negative_integer:
	SIGNED_INTEGER
{
  $$ = (unsigned int)-$1;
  if ($1 >= 0) {
    yyerror("Syntax error.");

  } else if ($$ != -$1) {
    yyerror("Number out of range.");
    $$ = 1;
  }
}
	;

signed_integer:
        SIGNED_INTEGER
	;

unsigned_integer:
	UNSIGNED_INTEGER
	;

number:
	unsigned_integer
{
  $$ = (double)$1;
}
	| signed_integer
{
  $$ = (double)$1;
}
	| REAL
	;

char_or_number:
	STRING
{
  if ($1.length() != 1) {
    yyerror("Single character required.");
    $$ = 0;
  } else {
    $$ = (double)(unsigned char)$1[0];
  }
}
	| number
	;


parameter_value:
	signed_integer
{
  current_packer->pack_int64($1);
}
	| unsigned_integer
{
  current_packer->pack_uint64($1);
}
        | REAL
{
  current_packer->pack_double($1);
}
        | STRING
{
  current_packer->pack_string($1);
}
        | HEX_STRING
{
  current_packer->pack_literal_value($1);
}
        | '{' 
{
  current_packer->push();
}
        array '}'
{
  current_packer->pop();
}
        | '[' 
{
  current_packer->push();
}
        array ']'
{
  current_packer->pop();
}
        | '(' 
{
  current_packer->push();
}
        array ')'
{
  current_packer->pop();
}
        | signed_integer '*' small_unsigned_integer
{
  for (unsigned int i = 0; i < $3; i++) {
    current_packer->pack_int64($1);
  }
}
        | unsigned_integer '*' small_unsigned_integer
{
  for (unsigned int i = 0; i < $3; i++) {
    current_packer->pack_uint64($1);
  }
}
        | REAL '*' small_unsigned_integer
{
  for (unsigned int i = 0; i < $3; i++) {
    current_packer->pack_double($1);
  }
}
        | HEX_STRING '*' small_unsigned_integer
{
  for (unsigned int i = 0; i < $3; i++) {
    current_packer->pack_literal_value($1);
  }
}
        ;

array:
        maybe_comma
        | array_def maybe_comma
        ;

maybe_comma:
        empty
        | ','
        ;

array_def:
        parameter_value
        | array_def ',' parameter_value
        ;

type_token:
        KW_INT8
{
  $$ = ST_int8;
}
        | KW_INT16
{
  $$ = ST_int16;
}
        | KW_INT32
{
  $$ = ST_int32;
}
        | KW_INT64
{
  $$ = ST_int64;
}
        | KW_UINT8
{
  $$ = ST_uint8;
}
        | KW_UINT16
{
  $$ = ST_uint16;
}
        | KW_UINT32
{
  $$ = ST_uint32;
}
        | KW_UINT64
{
  $$ = ST_uint64;
}
        | KW_FLOAT64
{
  $$ = ST_float64;
}
        | KW_STRING
{
  $$ = ST_string;
}
        | KW_BLOB
{
  $$ = ST_blob;
}
        | KW_BLOB32
{
  $$ = ST_blob32;
}
        | KW_INT8ARRAY
{
  $$ = ST_int8array;
}
        | KW_INT16ARRAY
{
  $$ = ST_int16array;
}
        | KW_INT32ARRAY
{
  $$ = ST_int32array;
}
        | KW_UINT8ARRAY
{
  $$ = ST_uint8array;
}
        | KW_UINT16ARRAY
{
  $$ = ST_uint16array;
}
        | KW_UINT32ARRAY
{
  $$ = ST_uint32array;
}
        | KW_UINT32UINT8ARRAY
{
  $$ = ST_uint32uint8array;
}
        | KW_CHAR
{
  $$ = ST_char;
}
        ;

server_flags:
        empty
{
  $$ = 0;
}
        | server_flags KW_REQUIRED
{
  $$ = $1 | DCAtomicField::F_required;
}
        | server_flags KW_BROADCAST
{
  $$ = $1 | DCAtomicField::F_broadcast;
}
        | server_flags KW_P2P
{
  $$ = $1 | DCAtomicField::F_p2p;
}
        | server_flags KW_RAM
{
  $$ = $1 | DCAtomicField::F_ram;
}
        | server_flags KW_DB
{
  $$ = $1 | DCAtomicField::F_db;
}
        | server_flags KW_CLSEND
{
  $$ = $1 | DCAtomicField::F_clsend;
}
        | server_flags KW_CLRECV
{
  $$ = $1 | DCAtomicField::F_clrecv;
}
        | server_flags KW_OWNSEND
{
  $$ = $1 | DCAtomicField::F_ownsend;
}
        | server_flags KW_AIRECV
{
  $$ = $1 | DCAtomicField::F_airecv;
}
        ;

no_server_flags:
        server_flags
{
  if ($1 != 0) {
    yyerror("Server flags are not allowed here.");
  }
  $$ = $1;
}
	;

molecular_field:
        IDENTIFIER ':'
{
  current_molecular = new DCMolecularField($1, current_class);
}
        molecular_atom_list
{
  $$ = current_molecular;
}
        ;

atomic_name:
        IDENTIFIER
{
  DCField *field = current_class->get_field_by_name($1);
  $$ = (DCAtomicField *)NULL;
  if (field == (DCField *)NULL) {
    yyerror("Unknown field: " + $1);
  } else {
    $$ = field->as_atomic_field();
    if ($$ == (DCAtomicField *)NULL) {
      yyerror("Not an atomic field: " + $1);
    }
  }
}
	;

molecular_atom_list:
        atomic_name
{
  if ($1 != (DCAtomicField *)NULL) {
    current_molecular->add_atomic($1);
  }
}
        | molecular_atom_list ',' atomic_name
{
  if ($3 != (DCAtomicField *)NULL) {
    current_molecular->add_atomic($3);
    if (!current_molecular->compare_flags(*$3)) {
      yyerror("Mismatched flags in molecule between " + 
              current_molecular->get_atomic(0)->get_name() + " and " +
              $3->get_name());
    }
  }
}
        ;

optional_name:
        empty
{
  $$ = "";
}
	| IDENTIFIER
	;

switch:
	KW_SWITCH optional_name '(' parameter ')' '{'
{
  $$ = current_switch;
  current_switch = new DCSwitch($2, $4);
}
	switch_fields '}'
{
  $$ = current_switch;
  current_switch = (DCSwitch *)$<u.parameter>7;
}
	;


switch_fields:
        empty
        | switch_fields ';'
        | switch_fields switch_case
        | switch_fields switch_field
{
  if (current_switch->get_num_cases() == 0) {
    yyerror("case declaration required before first element");
  } else if ($2 != (DCField *)NULL) {
    if (!current_switch->add_field($2)) {
      yyerror("Duplicate field name: " + $2->get_name());
    }
  }
}
        ;

switch_case:
	KW_CASE
{
  current_packer = &default_packer;
  current_packer->clear_data();
  current_packer->begin_pack(current_switch->get_key_parameter());
}
	parameter_value ':'
{
  if (!current_packer->end_pack()) {
    yyerror("Invalid value for switch parameter");
  } else {
    current_switch->add_case(current_packer->get_string());
  }
}
	;

switch_field:
	unnamed_parameter_with_default ';'
{
  $$ = $1;
}
	| named_parameter_with_default
{
  $$ = $1;
}
	;

empty:
        ;

