671 lines
13 KiB
Plaintext
671 lines
13 KiB
Plaintext
// Filename: dcParser.yxx
|
|
// Created by: drose (05Oct00)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
%{
|
|
#include "dcLexerDefs.h"
|
|
#include "dcParserDefs.h"
|
|
#include "dcFile.h"
|
|
#include "dcClass.h"
|
|
#include "dcAtomicField.h"
|
|
#include "dcMolecularField.h"
|
|
#include "dcClassParameter.h"
|
|
#include "dcArrayParameter.h"
|
|
#include "dcSimpleParameter.h"
|
|
#include "dcTypedef.h"
|
|
#include "dcPacker.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 DCAtomicField *current_atomic = (DCAtomicField *)NULL;
|
|
static DCMolecularField *current_molecular = (DCMolecularField *)NULL;
|
|
static DCAtomicField::ElementType atomic_element(new DCSimpleParameter(ST_invalid));
|
|
static DCParameter *current_parameter = (DCParameter *)NULL;
|
|
static DCPacker default_packer;
|
|
static DCPacker *current_packer;
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// 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_cleanup_parser() {
|
|
dc_file = (DCFile *)NULL;
|
|
}
|
|
|
|
%}
|
|
|
|
%token <u.integer> 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_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_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
|
|
|
|
%type <u.flag> kw_struct_or_kw_dclass
|
|
%type <u.dclass> dclass_name
|
|
%type <u.atomic> atomic_name
|
|
%type <u.subatomic> type_token
|
|
%type <u.parameter> type_name
|
|
%type <u.parameter> type_definition
|
|
%type <u.parameter> named_parameter
|
|
%type <u.parameter> unnamed_parameter
|
|
%type <u.parameter> parameter
|
|
%type <u.parameter> parameter_definition
|
|
%type <str> import_identifier
|
|
%type <str> slash_identifier
|
|
|
|
%%
|
|
|
|
grammar:
|
|
START_DC dc
|
|
| START_PARAMETER_VALUE parameter_value
|
|
;
|
|
|
|
dc:
|
|
empty
|
|
| dc ';'
|
|
| dc dclass
|
|
| dc import
|
|
| dc typedef_decl
|
|
;
|
|
|
|
dclass:
|
|
kw_struct_or_kw_dclass IDENTIFIER
|
|
{
|
|
current_class = new DCClass($2, $1, false);
|
|
if (!dc_file->add_class(current_class)) {
|
|
DCClass *old_class = dc_file->get_class_by_name(current_class->get_name());
|
|
if (old_class->is_bogus_class()) {
|
|
yyerror("Base class defined after its first reference: " + current_class->get_name());
|
|
} else {
|
|
yyerror("Duplicate class name: " + current_class->get_name());
|
|
}
|
|
}
|
|
}
|
|
dclass_derivation '{' dclass_fields '}'
|
|
;
|
|
|
|
kw_struct_or_kw_dclass:
|
|
KW_STRUCT
|
|
{
|
|
$$ = true;
|
|
}
|
|
| KW_DCLASS
|
|
{
|
|
$$ = false;
|
|
}
|
|
;
|
|
|
|
dclass_name:
|
|
IDENTIFIER
|
|
{
|
|
DCClass *dclass = dc_file->get_class_by_name($1);
|
|
if (dclass == (DCClass *)NULL) {
|
|
dclass = new DCClass($1, false, true);
|
|
dc_file->add_class(dclass);
|
|
}
|
|
|
|
$$ = dclass;
|
|
}
|
|
;
|
|
|
|
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
|
|
{
|
|
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_derivation:
|
|
empty
|
|
| ':' base_list
|
|
;
|
|
|
|
base_list:
|
|
dclass_name
|
|
{
|
|
if ($1 != (DCClass *)NULL) {
|
|
current_class->add_parent($1);
|
|
}
|
|
}
|
|
| base_list ',' dclass_name
|
|
{
|
|
if ($3 != (DCClass *)NULL) {
|
|
current_class->add_parent($3);
|
|
}
|
|
}
|
|
;
|
|
|
|
dclass_fields:
|
|
empty
|
|
| dclass_fields ';'
|
|
| dclass_fields atomic_field
|
|
| dclass_fields molecular_field
|
|
| dclass_fields unnamed_parameter ';'
|
|
{
|
|
current_class->add_parameter($2);
|
|
}
|
|
| dclass_fields named_parameter
|
|
{
|
|
if (!current_class->add_parameter($2)) {
|
|
yyerror("Duplicate parameter name: " + $2->get_name());
|
|
}
|
|
}
|
|
;
|
|
|
|
atomic_field:
|
|
IDENTIFIER '('
|
|
{
|
|
current_atomic = new DCAtomicField($1);
|
|
if (!current_class->add_field(current_atomic)) {
|
|
yyerror("Duplicate field name: " + current_atomic->get_name());
|
|
}
|
|
}
|
|
parameter_list ')' atomic_flags
|
|
;
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
;
|
|
|
|
parameter_list:
|
|
empty
|
|
| nonempty_parameter_list
|
|
;
|
|
|
|
nonempty_parameter_list:
|
|
atomic_element
|
|
| nonempty_parameter_list ',' atomic_element
|
|
;
|
|
|
|
atomic_element:
|
|
parameter
|
|
{
|
|
atomic_element = DCAtomicField::ElementType($1);
|
|
current_atomic->add_element(atomic_element);
|
|
}
|
|
| parameter '='
|
|
{
|
|
current_packer = &default_packer;
|
|
current_packer->begin_pack($1);
|
|
}
|
|
parameter_value
|
|
{
|
|
bool is_valid = $1->is_valid();
|
|
atomic_element = DCAtomicField::ElementType($1);
|
|
if (current_packer->end_pack()) {
|
|
atomic_element.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.
|
|
}
|
|
current_atomic->add_element(atomic_element);
|
|
}
|
|
;
|
|
|
|
named_parameter:
|
|
type_definition
|
|
{
|
|
current_parameter = $1;
|
|
}
|
|
parameter_definition
|
|
{
|
|
$$ = $3;
|
|
}
|
|
;
|
|
|
|
unnamed_parameter:
|
|
type_definition
|
|
;
|
|
|
|
parameter:
|
|
named_parameter
|
|
| unnamed_parameter
|
|
;
|
|
|
|
type_name:
|
|
type_token
|
|
{
|
|
$$ = new DCSimpleParameter($1);
|
|
}
|
|
| type_token '/' INTEGER
|
|
{
|
|
DCSimpleParameter *simple_param = new DCSimpleParameter($1);
|
|
if ($3 == 0) {
|
|
yyerror("Invalid divisor.");
|
|
|
|
} else if (!simple_param->set_divisor($3)) {
|
|
yyerror("A divisor is only valid on a numeric type.");
|
|
}
|
|
$$ = simple_param;
|
|
}
|
|
| IDENTIFIER
|
|
{
|
|
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) {
|
|
// Create an implicit typedef for this.
|
|
dtypedef = new DCTypedef(new DCClassParameter(dclass), 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();
|
|
}
|
|
;
|
|
|
|
type_definition:
|
|
type_name
|
|
| type_definition '[' ']'
|
|
{
|
|
$$ = new DCArrayParameter($1);
|
|
}
|
|
| type_definition '[' INTEGER ']'
|
|
{
|
|
$$ = new DCArrayParameter($1, $3);
|
|
}
|
|
;
|
|
|
|
parameter_definition:
|
|
IDENTIFIER
|
|
{
|
|
current_parameter->set_name($1);
|
|
$$ = current_parameter;
|
|
}
|
|
| parameter_definition '/' INTEGER
|
|
{
|
|
if ($3 == 0) {
|
|
yyerror("Invalid divisor.");
|
|
|
|
} else if ($1->as_simple_parameter() == NULL || $1->get_typedef() != (DCTypedef *)NULL) {
|
|
yyerror("A divisor is only allowed on a primitive type.");
|
|
|
|
} else {
|
|
if (!$1->as_simple_parameter()->set_divisor($3)) {
|
|
yyerror("A divisor is only valid on a numeric type.");
|
|
}
|
|
}
|
|
}
|
|
| parameter_definition '[' ']'
|
|
{
|
|
$$ = new DCArrayParameter($1);
|
|
}
|
|
| parameter_definition '[' INTEGER ']'
|
|
{
|
|
$$ = new DCArrayParameter($1, $3);
|
|
}
|
|
;
|
|
|
|
parameter_value:
|
|
INTEGER
|
|
{
|
|
current_packer->pack_int64($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();
|
|
}
|
|
| INTEGER '*' INTEGER
|
|
{
|
|
for (int i = 0; i < $3; i++) {
|
|
current_packer->pack_int64($1);
|
|
}
|
|
}
|
|
| REAL '*' INTEGER
|
|
{
|
|
for (int i = 0; i < $3; i++) {
|
|
current_packer->pack_double($1);
|
|
}
|
|
}
|
|
| HEX_STRING '*' INTEGER
|
|
{
|
|
for (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;
|
|
}
|
|
;
|
|
|
|
atomic_flags:
|
|
empty
|
|
| atomic_flags KW_REQUIRED
|
|
{
|
|
current_atomic->add_flag(DCAtomicField::F_required);
|
|
}
|
|
| atomic_flags KW_BROADCAST
|
|
{
|
|
current_atomic->add_flag(DCAtomicField::F_broadcast);
|
|
}
|
|
| atomic_flags KW_P2P
|
|
{
|
|
current_atomic->add_flag(DCAtomicField::F_p2p);
|
|
}
|
|
| atomic_flags KW_RAM
|
|
{
|
|
current_atomic->add_flag(DCAtomicField::F_ram);
|
|
}
|
|
| atomic_flags KW_DB
|
|
{
|
|
current_atomic->add_flag(DCAtomicField::F_db);
|
|
}
|
|
| atomic_flags KW_CLSEND
|
|
{
|
|
current_atomic->add_flag(DCAtomicField::F_clsend);
|
|
}
|
|
| atomic_flags KW_CLRECV
|
|
{
|
|
current_atomic->add_flag(DCAtomicField::F_clrecv);
|
|
}
|
|
| atomic_flags KW_OWNSEND
|
|
{
|
|
current_atomic->add_flag(DCAtomicField::F_ownsend);
|
|
}
|
|
| atomic_flags KW_AIRECV
|
|
{
|
|
current_atomic->add_flag(DCAtomicField::F_airecv);
|
|
}
|
|
;
|
|
|
|
molecular_field:
|
|
IDENTIFIER ':'
|
|
{
|
|
current_molecular = new DCMolecularField($1);
|
|
if (!current_class->add_field(current_molecular)) {
|
|
yyerror("Duplicate field name: " + current_molecular->get_name());
|
|
}
|
|
}
|
|
molecular_atom_list
|
|
;
|
|
|
|
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->get_atomic(0)->compare_flags(*$3)) {
|
|
yyerror("Mismatched flags in molecule between " +
|
|
current_molecular->get_atomic(0)->get_name() + " and " +
|
|
$3->get_name());
|
|
}
|
|
}
|
|
}
|
|
;
|
|
|
|
empty:
|
|
;
|
|
|