netcdf-c/ncgen/ncgen.y
Dennis Heimbigner 90b912b7e8 Allow use of type keywords as identifier in formats that do not support that type.
Built-in type-name keywords are currently flagged when used as
identifiers in formats that do not support that type.  So if a
user declares a dimension named "string" in a classic .cdl file,
it causes an error.

This PR modifies ncgen to allow those format-specific type keywords
to be used as identifiers when compiling to formats that do not
support that type. Also added a test for this.

Also a couple of misc. changes to conditionalize some debug output.
2020-06-05 17:03:29 -06:00

1607 lines
45 KiB
Plaintext

/*********************************************************************
* Copyright 2018, UCAR/Unidata
* See netcdf/COPYRIGHT file for copying and redistribution conditions.
* $Id: ncgen.y,v 1.42 2010/05/18 21:32:46 dmh Exp $
*********************************************************************/
/* yacc source for "ncgen", a netCDL parser and netCDF generator */
%error-verbose
%{
/*
static char SccsId[] = "$Id: ncgen.y,v 1.42 2010/05/18 21:32:46 dmh Exp $";
*/
#include "includes.h"
#include "netcdf_aux.h"
#include "ncgeny.h"
#include "ncgen.h"
#ifdef USE_NETCDF4
#include "netcdf_filter.h"
#endif
/* Following are in ncdump (for now)*/
/* Need some (unused) definitions to get it to compile */
#define ncatt_t void*
#define ncvar_t void
#include "nctime.h"
/* parser controls */
#define YY_NO_INPUT 1
/* True if string a equals string b*/
#ifndef NCSTREQ
#define NCSTREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
#endif
#define VLENSIZE (sizeof(nc_vlen_t))
#define MAXFLOATDIM 4294967295.0
/* mnemonics */
typedef enum Attrkind {ATTRVAR, ATTRGLOBAL, DONTKNOW} Attrkind;
#define ISCONST 1
#define ISLIST 0
typedef nc_vlen_t vlen_t;
/* We retain the old representation of the symbol list
as a linked list.
*/
List* symlist;
/* Track rootgroup separately*/
Symbol* rootgroup;
/* Track the group sequence */
static List* groupstack;
/* Provide a separate sequence for accumulating values
during the parse.
*/
static List* stack;
/* track homogeneity of types for data lists*/
static nc_type consttype;
/* Misc. */
static int stackbase;
static int stacklen;
static int count;
static int opaqueid; /* counter for opaque constants*/
static int arrayuid; /* counter for pseudo-array types*/
char* primtypenames[PRIMNO] = {
"nat",
"byte", "char", "short",
"int", "float", "double",
"ubyte", "ushort", "uint",
"int64", "uint64",
"string"
};
static int GLOBAL_SPECIAL = _NCPROPS_FLAG
| _ISNETCDF4_FLAG
| _SUPERBLOCK_FLAG
| _FORMAT_FLAG ;
/*Defined in ncgen.l*/
extern int lineno; /* line number for error messages */
extern Bytebuffer* lextext; /* name or string with escapes removed */
extern double double_val; /* last double value read */
extern float float_val; /* last float value read */
extern long long int64_val; /* last int64 value read */
extern int int32_val; /* last int32 value read */
extern short int16_val; /* last short value read */
extern unsigned long long uint64_val; /* last int64 value read */
extern unsigned int uint32_val; /* last int32 value read */
extern unsigned short uint16_val; /* last short value read */
extern char char_val; /* last char value read */
extern signed char byte_val; /* last byte value read */
extern unsigned char ubyte_val; /* last byte value read */
/* Track definitions of dims, types, attributes, and vars*/
List* grpdefs;
List* dimdefs;
List* attdefs; /* variable-specific attributes*/
List* gattdefs; /* global attributes only*/
List* xattdefs; /* unknown attributes*/
List* typdefs;
List* vardefs;
List* condefs; /* non-dimension constants used in type defs*/
List* tmp;
/* Forward */
static NCConstant* makeconstdata(nc_type);
static NCConstant* evaluate(Symbol* fcn, Datalist* arglist);
static NCConstant* makeenumconstref(Symbol*);
static void addtogroup(Symbol*);
static Symbol* currentgroup(void);
static Symbol* createrootgroup(const char*);
static Symbol* creategroup(Symbol*);
static int dupobjectcheck(nc_class,Symbol*);
static void setpathcurrent(Symbol* sym);
static Symbol* makeattribute(Symbol*,Symbol*,Symbol*,Datalist*,Attrkind);
static Symbol* makeprimitivetype(nc_type i);
static Symbol* makespecial(int tag, Symbol* vsym, Symbol* tsym, void* data, int isconst);
static int containsfills(Datalist* list);
static void vercheck(int ncid);
static long long extractint(NCConstant* con);
#ifdef USE_NETCDF4
static int parsefilterflag(const char* sdata0, Specialdata* special);
#ifdef GENDEBUG1
static void printfilters(size_t nfilters, NC_ParsedFilterSpec** filters);
#endif
#endif
int yylex(void);
#ifndef NO_STDARG
static void yyerror(const char *fmt, ...);
#else
static void yyerror(fmt,va_alist) const char* fmt; va_dcl;
#endif
/* Extern */
extern int lex_init(void);
%}
/* DECLARATIONS */
%union {
Symbol* sym;
unsigned long size; /* allow for zero size to indicate e.g. UNLIMITED*/
long mark; /* track indices into the sequence*/
int nctype; /* for tracking attribute list type*/
Datalist* datalist;
NCConstant* constant;
}
%token <sym>
NC_UNLIMITED_K /* keyword for unbounded record dimension */
CHAR_K /* keyword for char datatype */
BYTE_K /* keyword for byte datatype */
SHORT_K /* keyword for short datatype */
INT_K /* keyword for int datatype */
FLOAT_K /* keyword for float datatype */
DOUBLE_K /* keyword for double datatype */
UBYTE_K /* keyword for unsigned byte datatype */
USHORT_K /* keyword for unsigned short datatype */
UINT_K /* keyword for unsigned int datatype */
INT64_K /* keyword for long long datatype */
UINT64_K /* keyword for unsigned long long datatype */
STRING_K /* keyword for string datatype */
IDENT /* name for a dimension, variable, or attribute */
TERMSTRING /* terminal string */
CHAR_CONST /* char constant (not ever generated by ncgen.l) */
BYTE_CONST /* byte constant */
SHORT_CONST /* short constant */
INT_CONST /* int constant */
INT64_CONST /* long long constant */
UBYTE_CONST /* unsigned byte constant */
USHORT_CONST /* unsigned short constant */
UINT_CONST /* unsigned int constant */
UINT64_CONST /* unsigned long long constant */
FLOAT_CONST /* float constant */
DOUBLE_CONST/* double constant */
DIMENSIONS /* keyword starting dimensions section, if any */
VARIABLES /* keyword starting variables section, if any */
NETCDF /* keyword declaring netcdf name */
DATA /* keyword starting data section, if any */
TYPES
COMPOUND
ENUM
OPAQUE_ /* 'OPAQUE' apparently conflicts with HDF4 code */
OPAQUESTRING /* 0x<even number of hexdigits> */
GROUP
PATH /* / or (/IDENT)+(.IDENT)? */
FILLMARKER /* "_" as opposed to the attribute */
NIL /* NIL */
_FILLVALUE
_FORMAT
_STORAGE
_CHUNKSIZES
_DEFLATELEVEL
_SHUFFLE
_ENDIANNESS
_NOFILL
_FLETCHER32
_NCPROPS
_ISNETCDF4
_SUPERBLOCK
_FILTER
DATASETID
%type <sym> ident typename primtype dimd varspec
attrdecl enumid path dimref fielddim fieldspec
%type <sym> typeref
%type <sym> varref
%type <sym> ambiguous_ref
%type <mark> enumidlist fieldlist fields varlist dimspec dimlist field
fielddimspec fielddimlist
%type <constant> dataitem constdata constint conststring constbool
%type <constant> simpleconstant function econstref
%type <datalist> datalist intlist datalist1 datalist0 arglist
%start ncdesc /* start symbol for grammar */
%%
/* RULES */
ncdesc: NETCDF
datasetid
rootgroup
{if (error_count > 0) YYABORT;}
;
datasetid: DATASETID {createrootgroup(datasetname);};
rootgroup: '{'
groupbody
subgrouplist
'}';
/* 2/3/08 - Allow group body with only attributes. (H/T John Storrs). */
groupbody:
attrdecllist
typesection /* Type definitions */
dimsection /* dimension declarations */
vasection /* variable and attribute declarations */
datasection /* data for variables within the group */
;
subgrouplist: /*empty*/ | subgrouplist namedgroup;
namedgroup: GROUP ident '{'
{
Symbol* id = $2;
markcdf4("Group specification");
if(creategroup(id) == NULL)
yyerror("duplicate group declaration within parent group for %s",
id->name);
}
groupbody
subgrouplist
{listpop(groupstack);}
'}'
attrdecllist
;
typesection: /* empty */
| TYPES {}
| TYPES typedecls
{markcdf4("Type specification");}
;
typedecls: type_or_attr_decl | typedecls type_or_attr_decl ;
typename: ident
{ /* Use when defining a type */
$1->objectclass = NC_TYPE;
if(dupobjectcheck(NC_TYPE,$1))
yyerror("duplicate type declaration for %s",
$1->name);
listpush(typdefs,(void*)$1);
}
;
type_or_attr_decl: typedecl {} | attrdecl ';' {} ;
typedecl:
enumdecl optsemicolon
| compounddecl optsemicolon
| vlendecl optsemicolon
| opaquedecl optsemicolon
;
optsemicolon: /*empty*/ | ';' ;
enumdecl: primtype ENUM typename
'{' enumidlist '}'
{
int i;
addtogroup($3); /* sets prefix*/
$3->objectclass=NC_TYPE;
$3->subclass=NC_ENUM;
$3->typ.basetype=$1;
$3->typ.size = $1->typ.size;
$3->typ.alignment = $1->typ.alignment;
stackbase=$5;
stacklen=listlength(stack);
$3->subnodes = listnew();
/* Variety of field fixups*/
/* 1. add in the enum values*/
/* 2. make this type be their container*/
/* 3. make constant names visible in the group*/
/* 4. set field basetype to be same as enum basetype*/
for(i=stackbase;i<stacklen;i++) {
Symbol* eid = (Symbol*)listget(stack,i);
assert(eid->subclass == NC_ECONST);
addtogroup(eid);
listpush($3->subnodes,(void*)eid);
eid->container = $3;
eid->typ.basetype = $3->typ.basetype;
}
listsetlength(stack,stackbase);/* remove stack nodes*/
}
;
enumidlist: enumid
{$$=listlength(stack); listpush(stack,(void*)$1);}
| enumidlist ',' enumid
{
int i;
$$=$1;
/* check for duplicates*/
stackbase=$1;
stacklen=listlength(stack);
for(i=stackbase;i<stacklen;i++) {
Symbol* elem = (Symbol*)listget(stack,i);
if(strcmp($3->name,elem->name)==0)
yyerror("duplicate enum declaration for %s",
elem->name);
}
listpush(stack,(void*)$3);
}
;
enumid: ident '=' constint
{
$1->objectclass=NC_TYPE;
$1->subclass=NC_ECONST;
$1->typ.econst=$3;
$$=$1;
}
;
opaquedecl: OPAQUE_ '(' INT_CONST ')' typename
{
vercheck(NC_OPAQUE);
addtogroup($5); /*sets prefix*/
$5->objectclass=NC_TYPE;
$5->subclass=NC_OPAQUE;
$5->typ.typecode=NC_OPAQUE;
$5->typ.size=int32_val;
$5->typ.alignment=ncaux_class_alignment(NC_OPAQUE);
}
;
vlendecl: typeref '(' '*' ')' typename
{
Symbol* basetype = $1;
vercheck(NC_VLEN);
addtogroup($5); /*sets prefix*/
$5->objectclass=NC_TYPE;
$5->subclass=NC_VLEN;
$5->typ.basetype=basetype;
$5->typ.typecode=NC_VLEN;
$5->typ.size=VLENSIZE;
$5->typ.alignment=ncaux_class_alignment(NC_VLEN);
}
;
compounddecl: COMPOUND typename '{' fields '}'
{
int i,j;
vercheck(NC_COMPOUND);
addtogroup($2);
/* check for duplicate field names*/
stackbase=$4;
stacklen=listlength(stack);
for(i=stackbase;i<stacklen;i++) {
Symbol* elem1 = (Symbol*)listget(stack,i);
for(j=i+1;j<stacklen;j++) {
Symbol* elem2 = (Symbol*)listget(stack,j);
if(strcmp(elem1->name,elem2->name)==0) {
yyerror("duplicate field declaration for %s",elem1->name);
}
}
}
$2->objectclass=NC_TYPE;
$2->subclass=NC_COMPOUND;
$2->typ.basetype=NULL;
$2->typ.typecode=NC_COMPOUND;
$2->subnodes = listnew();
/* Add in the fields*/
for(i=stackbase;i<stacklen;i++) {
Symbol* fsym = (Symbol*)listget(stack,i);
fsym->container = $2;
listpush($2->subnodes,(void*)fsym);
}
listsetlength(stack,stackbase);/* remove stack nodes*/
}
;
fields: field ';' {$$=$1;}
| fields field ';' {$$=$1;}
;
field: typeref fieldlist
{
int i;
$$=$2;
stackbase=$2;
stacklen=listlength(stack);
/* process each field in the fieldlist*/
for(i=stackbase;i<stacklen;i++) {
Symbol* f = (Symbol*)listget(stack,i);
f->typ.basetype = $1;
}
}
;
primtype: CHAR_K { $$ = primsymbols[NC_CHAR]; }
| BYTE_K { $$ = primsymbols[NC_BYTE]; }
| SHORT_K { $$ = primsymbols[NC_SHORT]; }
| INT_K { $$ = primsymbols[NC_INT]; }
| FLOAT_K { $$ = primsymbols[NC_FLOAT]; }
| DOUBLE_K{ $$ = primsymbols[NC_DOUBLE]; }
| UBYTE_K { vercheck(NC_UBYTE); $$ = primsymbols[NC_UBYTE]; }
| USHORT_K { vercheck(NC_USHORT); $$ = primsymbols[NC_USHORT]; }
| UINT_K { vercheck(NC_UINT); $$ = primsymbols[NC_UINT]; }
| INT64_K { vercheck(NC_INT64); $$ = primsymbols[NC_INT64]; }
| UINT64_K { vercheck(NC_UINT64); $$ = primsymbols[NC_UINT64]; }
| STRING_K { vercheck(NC_STRING); $$ = primsymbols[NC_STRING]; }
;
dimsection: /* empty */
| DIMENSIONS {}
| DIMENSIONS dimdecls {}
;
dimdecls: dim_or_attr_decl ';'
| dimdecls dim_or_attr_decl ';'
;
dim_or_attr_decl: dimdeclist {} | attrdecl {} ;
dimdeclist: dimdecl
| dimdeclist ',' dimdecl
;
dimdecl:
dimd '=' constint
{
$1->dim.declsize = (size_t)extractint($3);
#ifdef GENDEBUG1
fprintf(stderr,"dimension: %s = %llu\n",$1->name,(unsigned long long)$1->dim.declsize);
#endif
reclaimconstant($3);
}
| dimd '=' NC_UNLIMITED_K
{
$1->dim.declsize = NC_UNLIMITED;
$1->dim.isunlimited = 1;
#ifdef GENDEBUG1
fprintf(stderr,"dimension: %s = UNLIMITED\n",$1->name);
#endif
}
;
dimd: ident
{
$1->objectclass=NC_DIM;
if(dupobjectcheck(NC_DIM,$1))
yyerror( "Duplicate dimension declaration for %s",
$1->name);
addtogroup($1);
$$=$1;
listpush(dimdefs,(void*)$1);
}
;
vasection: /* empty */
| VARIABLES {}
| VARIABLES vadecls {}
;
vadecls: vadecl_or_attr ';'
| vadecls vadecl_or_attr ';'
;
vadecl_or_attr: vardecl {} | attrdecl {} ;
vardecl: typeref varlist
{
int i;
stackbase=$2;
stacklen=listlength(stack);
/* process each variable in the varlist*/
for(i=stackbase;i<stacklen;i++) {
Symbol* sym = (Symbol*)listget(stack,i);
sym->objectclass = NC_VAR;
if(dupobjectcheck(NC_VAR,sym)) {
yyerror("Duplicate variable declaration for %s",
sym->name);
} else {
sym->typ.basetype = $1;
addtogroup(sym);
listpush(vardefs,(void*)sym);
sym->var.special = ecalloc(sizeof(Specialdata));
if(sym->var.special == NULL)
derror("out of memory");
}
}
listsetlength(stack,stackbase);/* remove stack nodes*/
}
;
varlist: varspec
{$$=listlength(stack);
listpush(stack,(void*)$1);
}
| varlist ',' varspec
{$$=$1; listpush(stack,(void*)$3);}
;
varspec: ident dimspec
{
int i;
Dimset dimset;
Symbol* var = $1; /* for debugging */
stacklen=listlength(stack);
stackbase=$2;
count = stacklen - stackbase;
if(count >= NC_MAX_VAR_DIMS) {
yyerror("%s has too many dimensions",$1->name);
count = NC_MAX_VAR_DIMS - 1;
stacklen = stackbase + count;
}
dimset.ndims = count;
/* extract the actual dimensions*/
if(dimset.ndims > 0) {
for(i=0;i<count;i++) {
Symbol* dsym = (Symbol*)listget(stack,stackbase+i);
dimset.dimsyms[i] = dsym;
}
var->typ.dimset = dimset;
}
var->typ.basetype = NULL; /* not yet known*/
var->objectclass=NC_VAR;
listsetlength(stack,stackbase);/* remove stack nodes*/
$$ = var;
}
;
dimspec: /* empty */ {$$=listlength(stack);}
| '(' dimlist ')' {$$=$2;}
;
dimlist: dimref {$$=listlength(stack); listpush(stack,(void*)$1);}
| dimlist ',' dimref
{$$=$1; listpush(stack,(void*)$3);}
;
dimref: path
{Symbol* dimsym = $1;
dimsym->objectclass = NC_DIM;
/* Find the actual dimension*/
dimsym = locate(dimsym);
if(dimsym == NULL) {
derror("Undefined or forward referenced dimension: %s",$1->name);
YYABORT;
}
$$=dimsym;
}
;
fieldlist:
fieldspec
{$$=listlength(stack);
listpush(stack,(void*)$1);
}
| fieldlist ',' fieldspec
{$$=$1; listpush(stack,(void*)$3);}
;
fieldspec:
ident fielddimspec
{
int i;
Dimset dimset;
stackbase=$2;
stacklen=listlength(stack);
count = stacklen - stackbase;
if(count >= NC_MAX_VAR_DIMS) {
yyerror("%s has too many dimensions",$1->name);
count = NC_MAX_VAR_DIMS - 1;
stacklen = stackbase + count;
}
dimset.ndims = count;
if(count > 0) {
/* extract the actual dimensions*/
for(i=0;i<count;i++) {
Symbol* dsym = (Symbol*)listget(stack,stackbase+i);
dimset.dimsyms[i] = dsym;
}
$1->typ.dimset = dimset;
}
$1->typ.basetype = NULL; /* not yet known*/
$1->objectclass=NC_TYPE;
$1->subclass=NC_FIELD;
listsetlength(stack,stackbase);/* remove stack nodes*/
$$ = $1;
}
;
fielddimspec: /* empty */ {$$=listlength(stack);}
| '(' fielddimlist ')' {$$=$2;}
;
fielddimlist:
fielddim {$$=listlength(stack); listpush(stack,(void*)$1);}
| fielddimlist ',' fielddim
{$$=$1; listpush(stack,(void*)$3);}
;
fielddim:
UINT_CONST
{ /* Anonymous integer dimension.
Can only occur in type definitions*/
char anon[32];
sprintf(anon,"const%u",uint32_val);
$$ = install(anon);
$$->objectclass = NC_DIM;
$$->dim.isconstant = 1;
$$->dim.declsize = uint32_val;
}
| INT_CONST
{ /* Anonymous integer dimension.
Can only occur in type definitions*/
char anon[32];
if(int32_val <= 0) {
derror("field dimension must be positive");
YYABORT;
}
sprintf(anon,"const%d",int32_val);
$$ = install(anon);
$$->objectclass = NC_DIM;
$$->dim.isconstant = 1;
$$->dim.declsize = int32_val;
}
;
/* Use this when referencing defined objects */
varref:
ambiguous_ref
{Symbol* vsym = $1;
if(vsym->objectclass != NC_VAR) {
derror("Undefined or forward referenced variable: %s",vsym->name);
YYABORT;
}
$$=vsym;
}
;
typeref:
ambiguous_ref
{Symbol* tsym = $1;
if(tsym->objectclass != NC_TYPE) {
derror("Undefined or forward referenced type: %s",tsym->name);
YYABORT;
}
$$=tsym;
}
;
ambiguous_ref:
path
{Symbol* tvsym = $1; Symbol* sym;
/* disambiguate*/
tvsym->objectclass = NC_VAR;
sym = locate(tvsym);
if(sym == NULL) {
tvsym->objectclass = NC_TYPE;
sym = locate(tvsym);
if(tvsym == NULL) {
derror("Undefined or forward referenced name: %s",$1->name);
YYABORT;
} else tvsym = sym;
} else tvsym = sym;
if(tvsym == NULL) {
derror("Undefined name (line %d): %s",$1->lineno,$1->name);
YYABORT;
}
$$=tvsym;
}
| primtype {$$=$1;}
;
/* Use this for all attribute decls */
/* Global vs var-specific will be separated in makeattribute */
/* Watch out; this is left recursive */
attrdecllist: /*empty*/ {} | attrdecl ';' attrdecllist {} ;
attrdecl:
':' _NCPROPS '=' conststring
{$$ = makespecial(_NCPROPS_FLAG,NULL,NULL,(void*)$4,ISCONST);}
| ':' _ISNETCDF4 '=' constbool
{$$ = makespecial(_ISNETCDF4_FLAG,NULL,NULL,(void*)$4,ISCONST);}
| ':' _SUPERBLOCK '=' constint
{$$ = makespecial(_SUPERBLOCK_FLAG,NULL,NULL,(void*)$4,ISCONST);}
| ':' ident '=' datalist
{ $$=makeattribute($2,NULL,NULL,$4,ATTRGLOBAL);}
| typeref ambiguous_ref ':' ident '=' datalist
{Symbol* tsym = $1; Symbol* vsym = $2; Symbol* asym = $4;
if(vsym->objectclass == NC_VAR) {
$$=makeattribute(asym,vsym,tsym,$6,ATTRVAR);
} else {
derror("Doubly typed attribute: %s",asym->name);
YYABORT;
}
}
| ambiguous_ref ':' ident '=' datalist
{Symbol* sym = $1; Symbol* asym = $3;
if(sym->objectclass == NC_VAR) {
$$=makeattribute(asym,sym,NULL,$5,ATTRVAR);
} else if(sym->objectclass == NC_TYPE) {
$$=makeattribute(asym,NULL,sym,$5,ATTRGLOBAL);
} else {
derror("Attribute prefix not a variable or type: %s",asym->name);
YYABORT;
}
}
| ambiguous_ref ':' _FILLVALUE '=' datalist
{$$ = makespecial(_FILLVALUE_FLAG,$1,NULL,(void*)$5,ISLIST);}
| typeref ambiguous_ref ':' _FILLVALUE '=' datalist
{$$ = makespecial(_FILLVALUE_FLAG,$2,$1,(void*)$6,ISLIST);}
| ambiguous_ref ':' _STORAGE '=' conststring
{$$ = makespecial(_STORAGE_FLAG,$1,NULL,(void*)$5,ISCONST);}
| ambiguous_ref ':' _CHUNKSIZES '=' intlist
{$$ = makespecial(_CHUNKSIZES_FLAG,$1,NULL,(void*)$5,ISLIST);}
| ambiguous_ref ':' _FLETCHER32 '=' constbool
{$$ = makespecial(_FLETCHER32_FLAG,$1,NULL,(void*)$5,ISCONST);}
| ambiguous_ref ':' _DEFLATELEVEL '=' constint
{$$ = makespecial(_DEFLATE_FLAG,$1,NULL,(void*)$5,ISCONST);}
| ambiguous_ref ':' _SHUFFLE '=' constbool
{$$ = makespecial(_SHUFFLE_FLAG,$1,NULL,(void*)$5,ISCONST);}
| ambiguous_ref ':' _ENDIANNESS '=' conststring
{$$ = makespecial(_ENDIAN_FLAG,$1,NULL,(void*)$5,ISCONST);}
| ambiguous_ref ':' _FILTER '=' conststring
{$$ = makespecial(_FILTER_FLAG,$1,NULL,(void*)$5,ISCONST);}
| ambiguous_ref ':' _NOFILL '=' constbool
{$$ = makespecial(_NOFILL_FLAG,$1,NULL,(void*)$5,ISCONST);}
| ':' _FORMAT '=' conststring
{$$ = makespecial(_FORMAT_FLAG,NULL,NULL,(void*)$4,ISCONST);}
;
path:
ident
{
$$=$1;
$1->ref.is_ref=1;
$1->is_prefixed=0;
setpathcurrent($1);
}
| PATH
{
$$=$1;
$1->ref.is_ref=1;
$1->is_prefixed=1;
/* path is set in ncgen.l*/
}
;
datasection: /* empty */
| DATA {}
| DATA datadecls {}
;
datadecls: datadecl ';'
| datadecls datadecl ';'
;
datadecl: varref '=' datalist
{$1->data = $3;}
;
datalist:
datalist0 {$$ = $1;}
| datalist1 {$$ = $1;}
;
datalist0:
/*empty*/ {$$ = builddatalist(0);}
;
datalist1: /* Must have at least 1 element */
dataitem {$$ = const2list($1);}
| datalist ',' dataitem
{dlappend($1,($3)); $$=$1; }
;
dataitem:
constdata {$$=$1;}
| '{' datalist '}' {$$=builddatasublist($2);}
;
constdata:
simpleconstant {$$=$1;}
| OPAQUESTRING {$$=makeconstdata(NC_OPAQUE);}
| FILLMARKER {$$=makeconstdata(NC_FILLVALUE);}
| NIL {$$=makeconstdata(NC_NIL);}
| econstref {$$=$1;}
| function
;
econstref:
path {$$ = makeenumconstref($1);}
;
function:
ident '(' arglist ')' {$$=evaluate($1,$3);}
;
arglist:
simpleconstant
{$$ = const2list($1);}
| arglist ',' simpleconstant
{dlappend($1,($3)); $$=$1;}
;
simpleconstant:
CHAR_CONST {$$=makeconstdata(NC_CHAR);} /* never used apparently*/
| BYTE_CONST {$$=makeconstdata(NC_BYTE);}
| SHORT_CONST {$$=makeconstdata(NC_SHORT);}
| INT_CONST {$$=makeconstdata(NC_INT);}
| INT64_CONST {$$=makeconstdata(NC_INT64);}
| UBYTE_CONST {$$=makeconstdata(NC_UBYTE);}
| USHORT_CONST {$$=makeconstdata(NC_USHORT);}
| UINT_CONST {$$=makeconstdata(NC_UINT);}
| UINT64_CONST {$$=makeconstdata(NC_UINT64);}
| FLOAT_CONST {$$=makeconstdata(NC_FLOAT);}
| DOUBLE_CONST {$$=makeconstdata(NC_DOUBLE);}
| TERMSTRING {$$=makeconstdata(NC_STRING);}
;
intlist:
constint {$$ = const2list($1);}
| intlist ',' constint {$$=$1; dlappend($1,($3));}
;
constint:
INT_CONST
{$$=makeconstdata(NC_INT);}
| UINT_CONST
{$$=makeconstdata(NC_UINT);}
| INT64_CONST
{$$=makeconstdata(NC_INT64);}
| UINT64_CONST
{$$=makeconstdata(NC_UINT64);}
;
conststring:
TERMSTRING {$$=makeconstdata(NC_STRING);}
;
constbool:
conststring {$$=$1;}
| constint {$$=$1;}
/* End OF RULES */
/* Push all idents thru here*/
ident:
IDENT {$$=$1;}
;
%%
#ifndef NO_STDARG
static void
yyerror(const char *fmt, ...)
#else
static void
yyerror(fmt,va_alist) const char* fmt; va_dcl
#endif
{
va_list argv;
va_start(argv,fmt);
(void)fprintf(stderr,"%s: %s line %d: ", progname, cdlname, lineno);
vderror(fmt,argv);
va_end(argv);
}
/* undefine yywrap macro, in case we are using bison instead of yacc */
#ifdef yywrap
#undef yywrap
#endif
static int
ncgwrap(void) /* returns 1 on EOF if no more input */
{
return 1;
}
/* get lexical input routine generated by lex */
#include "ncgenl.c"
/* Really should init our data within this file */
void
parse_init(void)
{
int i;
opaqueid = 0;
arrayuid = 0;
symlist = listnew();
stack = listnew();
groupstack = listnew();
consttype = NC_NAT;
grpdefs = listnew();
dimdefs = listnew();
attdefs = listnew();
gattdefs = listnew();
xattdefs = listnew();
typdefs = listnew();
vardefs = listnew();
condefs = listnew();
tmp = listnew();
/* Create the primitive types */
for(i=NC_NAT+1;i<=NC_STRING;i++) {
primsymbols[i] = makeprimitivetype(i);
}
lex_init();
}
static Symbol*
makeprimitivetype(nc_type nctype)
{
Symbol* sym = install(primtypenames[nctype]);
sym->objectclass=NC_TYPE;
sym->subclass=NC_PRIM;
sym->nc_id = nctype;
sym->typ.typecode = nctype;
sym->typ.size = ncsize(nctype);
sym->typ.nelems = 1;
sym->typ.alignment = ncaux_class_alignment(nctype);
/* Make the basetype circular so we can always ask for it */
sym->typ.basetype = sym;
sym->prefix = listnew();
return sym;
}
/* Symbol table operations for ncgen tool */
/* install sname in symbol table even if it is already there */
Symbol*
install(const char *sname)
{
Symbol* sp;
sp = (Symbol*) ecalloc (sizeof (struct Symbol));
sp->name = nulldup(sname);
sp->lineno = lineno;
sp->location = currentgroup();
sp->container = currentgroup();
listpush(symlist,sp);
return sp;
}
static Symbol*
currentgroup(void)
{
if(listlength(groupstack) == 0) return rootgroup;
return (Symbol*)listtop(groupstack);
}
static Symbol*
createrootgroup(const char* dataset)
{
Symbol* gsym = install(dataset);
gsym->objectclass = NC_GRP;
gsym->container = NULL;
gsym->subnodes = listnew();
gsym->grp.is_root = 1;
gsym->prefix = listnew();
listpush(grpdefs,(void*)gsym);
rootgroup = gsym;
return gsym;
}
static Symbol*
creategroup(Symbol * gsym)
{
/* See if this group already exists in currentgroup */
gsym->objectclass = NC_GRP;
if(dupobjectcheck(NC_GRP,gsym)) {
derror("Duplicate group name in same scope: %s",gsym->name);
return NULL;
}
addtogroup(gsym);
gsym->subnodes = listnew();
listpush(groupstack,(void*)gsym);
listpush(grpdefs,(void*)gsym);
return gsym;
}
static NCConstant*
makeconstdata(nc_type nctype)
{
NCConstant* con = nullconst();
consttype = nctype;
con->nctype = nctype;
con->lineno = lineno;
con->filled = 0;
switch (nctype) {
case NC_CHAR: con->value.charv = char_val; break;
case NC_BYTE: con->value.int8v = byte_val; break;
case NC_SHORT: con->value.int16v = int16_val; break;
case NC_INT: con->value.int32v = int32_val; break;
case NC_FLOAT:
con->value.floatv = float_val;
break;
case NC_DOUBLE:
con->value.doublev = double_val;
break;
case NC_STRING: { /* convert to a set of chars*/
size_t len;
len = bbLength(lextext);
con->value.stringv.len = len;
con->value.stringv.stringv = bbExtract(lextext);
}
break;
/* Allow these constants even in netcdf-3 */
case NC_UBYTE: con->value.uint8v = ubyte_val; break;
case NC_USHORT: con->value.uint16v = uint16_val; break;
case NC_UINT: con->value.uint32v = uint32_val; break;
case NC_INT64: con->value.int64v = int64_val; break;
case NC_UINT64: con->value.uint64v = uint64_val; break;
#ifdef USE_NETCDF4
case NC_OPAQUE: {
char* s;
int len;
len = bbLength(lextext);
s = (char*)ecalloc(len+1);
strncpy(s,bbContents(lextext),len);
s[len] = '\0';
con->value.opaquev.stringv = s;
con->value.opaquev.len = len;
} break;
case NC_NIL:
break; /* no associated value*/
#endif
case NC_FILLVALUE:
break; /* no associated value*/
default:
yyerror("Data constant: unexpected NC type: %s",
nctypename(nctype));
con->value.stringv.stringv = NULL;
con->value.stringv.len = 0;
}
return con;
}
static NCConstant*
makeenumconstref(Symbol* refsym)
{
NCConstant* con = nullconst();
markcdf4("Enum type");
consttype = NC_ENUM;
con->nctype = NC_ECONST;
con->lineno = lineno;
con->filled = 0;
refsym->objectclass = NC_TYPE;
refsym->subclass = NC_ECONST;
con->value.enumv = refsym;
return con;
}
static void
addtogroup(Symbol* sym)
{
Symbol* grp = currentgroup();
sym->container = grp;
listpush(grp->subnodes,(void*)sym);
setpathcurrent(sym);
}
/* Check for duplicate name of given type within current group*/
static int
dupobjectcheck(nc_class objectclass, Symbol* pattern)
{
int i;
Symbol* grp;
if(pattern == NULL) return 0;
grp = pattern->container;
if(grp == NULL || grp->subnodes == NULL) return 0;
for(i=0;i<listlength(grp->subnodes);i++) {
Symbol* sym = (Symbol*)listget(grp->subnodes,i);
if(!sym->ref.is_ref && sym->objectclass == objectclass
&& strcmp(sym->name,pattern->name)==0) return 1;
}
return 0;
}
static void
setpathcurrent(Symbol* sym)
{
sym->is_prefixed = 0;
sym->prefix = prefixdup(groupstack);
}
/* Convert an nc_type code to the corresponding Symbol*/
Symbol*
basetypefor(nc_type nctype)
{
return primsymbols[nctype];
}
static int
truefalse(NCConstant* con, int tag)
{
if(con->nctype == NC_STRING) {
char* sdata = con->value.stringv.stringv;
if(strncmp(sdata,"false",NC_MAX_NAME) == 0
|| strncmp(sdata,"0",NC_MAX_NAME) == 0)
return 0;
else if(strncmp(sdata,"true",NC_MAX_NAME) == 0
|| strncmp(sdata,"1",NC_MAX_NAME) == 0)
return 1;
else goto fail;
} else if(con->value.int32v < 0 || con->value.int32v > 1)
goto fail;
return con->value.int32v;
fail:
derror("%s: illegal value",specialname(tag));
return 0;
}
/* Since this may be affected by the _Format attribute, which
may come last, capture all the special info and sort it out
in semantics.
*/
static Symbol*
makespecial(int tag, Symbol* vsym, Symbol* tsym, void* data, int isconst)
{
Symbol* attr = NULL;
Datalist* list = NULL;
NCConstant* con = NULL;
NCConstant* tmp = NULL;
int tf = 0;
char* sdata = NULL;
int idata = -1;
if((GLOBAL_SPECIAL & tag) != 0) {
if(vsym != NULL) {
derror("_Format: must be global attribute");
vsym = NULL;
}
} else {
if(vsym == NULL) {
derror("%s: must have non-NULL vsym", specialname(tag));
return NULL;
}
}
if(tag != _FILLVALUE_FLAG && tag != _FORMAT_FLAG)
/*Main.*/specials_flag++;
if(isconst)
con = (NCConstant*)data;
else
list = (Datalist*)data;
switch (tag) {
case _FLETCHER32_FLAG:
case _SHUFFLE_FLAG:
case _ISNETCDF4_FLAG:
case _NOFILL_FLAG:
tmp = nullconst();
tmp->nctype = (con->nctype == NC_STRING?NC_STRING:NC_INT);
convert1(con,tmp);
tf = truefalse(tmp,tag);
reclaimconstant(tmp);
break;
case _FORMAT_FLAG:
case _STORAGE_FLAG:
case _NCPROPS_FLAG:
case _ENDIAN_FLAG:
case _FILTER_FLAG:
tmp = nullconst();
tmp->nctype = NC_STRING;
convert1(con,tmp);
if(tmp->nctype == NC_STRING) {
sdata = tmp->value.stringv.stringv;
tmp->value.stringv.stringv = NULL;
tmp->value.stringv.len = 0;
} else
derror("%s: illegal value",specialname(tag));
reclaimconstant(tmp);
break;
case _SUPERBLOCK_FLAG:
case _DEFLATE_FLAG:
tmp = nullconst();
tmp->nctype = NC_INT;
convert1(con,tmp);
if(tmp->nctype == NC_INT)
idata = tmp->value.int32v;
else
derror("%s: illegal value",specialname(tag));
reclaimconstant(tmp);
break;
case _CHUNKSIZES_FLAG:
case _FILLVALUE_FLAG:
/* Handle below */
break;
default: PANIC1("unexpected special tag: %d",tag);
}
if(tag == _FORMAT_FLAG) {
/* Watch out: this is a global attribute */
struct Kvalues* kvalue;
int found = 0;
/* Use the table in main.c */
for(kvalue = legalkinds; kvalue->name; kvalue++) {
if(sdata) {
if(strcmp(sdata, kvalue->name) == 0) {
globalspecials._Format = kvalue->k_flag;
/*Main.*/format_attribute = 1;
found = 1;
break;
}
}
}
if(!found)
derror("_Format: illegal value: %s",sdata);
} else if((GLOBAL_SPECIAL & tag) != 0) {
if(tag == _ISNETCDF4_FLAG)
globalspecials._IsNetcdf4 = tf;
else if(tag == _SUPERBLOCK_FLAG)
globalspecials._Superblock = idata;
else if(tag == _NCPROPS_FLAG) {
globalspecials._NCProperties = sdata;
sdata = NULL;
}
} else {
Specialdata* special;
/* Set up special info */
if(vsym->var.special == NULL) {
vsym->var.special = ecalloc(sizeof(Specialdata));
if(vsym->var.special == NULL)
derror("Out of memory");
}
special = vsym->var.special;
if(tag == _FILLVALUE_FLAG) {
/* fillvalue must be a single value*/
if(!isconst && datalistlen(list) != 1)
derror("_FillValue: must be a single (possibly compound) value",
vsym->name);
if(isconst) {
list = const2list(con);
con = NULL;
}
/* check that the attribute value contains no fill values*/
if(containsfills(list)) {
derror("Attribute data may not contain fill values (i.e. _ )");
}
/* _FillValue is also a real attribute*/
if(vsym->objectclass != NC_VAR) {
derror("_FillValue attribute not associated with variable: %s",vsym->name);
}
if(tsym == NULL) tsym = vsym->typ.basetype;
else if(vsym->typ.basetype != tsym) {
derror("_FillValue attribute type does not match variable type: %s",vsym->name);
}
special->_Fillvalue = clonedatalist(list);
/* Create the corresponding attribute */
attr = makeattribute(install("_FillValue"),vsym,tsym,list,ATTRVAR);
list = NULL;
} else switch (tag) {
/* These will be output as attributes later */
case _STORAGE_FLAG:
if(!sdata)
derror("_Storage: illegal NULL value");
else if(strcmp(sdata,"contiguous") == 0)
special->_Storage = NC_CONTIGUOUS;
else if(strcmp(sdata,"compact") == 0)
special->_Storage = NC_COMPACT;
else if(strcmp(sdata,"chunked") == 0)
special->_Storage = NC_CHUNKED;
else
derror("_Storage: illegal value: %s",sdata);
special->flags |= _STORAGE_FLAG;
break;
case _FLETCHER32_FLAG:
special->_Fletcher32 = tf;
special->flags |= _FLETCHER32_FLAG;
break;
case _DEFLATE_FLAG:
special->_DeflateLevel = idata;
special->flags |= _DEFLATE_FLAG;
break;
case _SHUFFLE_FLAG:
special->_Shuffle = tf;
special->flags |= _SHUFFLE_FLAG;
break;
case _ENDIAN_FLAG:
if(!sdata)
derror("_Endianness: illegal NULL value");
else if(strcmp(sdata,"little") == 0)
special->_Endianness = 1;
else if(strcmp(sdata,"big") == 0)
special->_Endianness = 2;
else
derror("_Endianness: illegal value: %s",sdata);
special->flags |= _ENDIAN_FLAG;
break;
case _NOFILL_FLAG:
special->_Fill = (1 - tf); /* negate */
special->flags |= _NOFILL_FLAG;
break;
case _CHUNKSIZES_FLAG: {
int i;
list = (isconst ? const2list(con) : list);
special->nchunks = list->length;
special->_ChunkSizes = (size_t*)ecalloc(sizeof(size_t)*special->nchunks);
for(i=0;i<special->nchunks;i++) {
tmp = nullconst();
tmp->nctype = NC_INT;
convert1(list->data[i],tmp);
if(tmp->nctype == NC_INT) {
special->_ChunkSizes[i] = (size_t)tmp->value.int32v;
} else {
efree(special->_ChunkSizes);
derror("%s: illegal value",specialname(tag));
}
reclaimconstant(tmp);
}
special->flags |= _CHUNKSIZES_FLAG;
/* Chunksizes => storage == chunked */
special->flags |= _STORAGE_FLAG;
special->_Storage = NC_CHUNKED;
} break;
case _FILTER_FLAG:
#ifdef USE_NETCDF4
/* Parse the filter spec */
if(parsefilterflag(sdata,special) == NC_NOERR)
special->flags |= _FILTER_FLAG;
else {
derror("_Filter: unparsable filter spec: %s",sdata);
}
#else
derror("%s: the filter attribute requires netcdf-4 to be enabled",specialname(tag));
#endif
break;
default: PANIC1("makespecial: illegal token: %d",tag);
}
}
if(sdata) efree(sdata);
if(con) reclaimconstant(con);
if(list) reclaimdatalist(list);
return attr;
}
static Symbol*
makeattribute(Symbol* asym,
Symbol* vsym,
Symbol* tsym,
Datalist* data,
Attrkind kind) /* global var or unknown*/
{
asym->objectclass = NC_ATT;
asym->data = data;
switch (kind) {
case ATTRVAR:
asym->att.var = vsym;
asym->typ.basetype = tsym;
listpush(attdefs,(void*)asym);
addtogroup(asym);
break;
case ATTRGLOBAL:
asym->att.var = NULL; /* NULL => NC_GLOBAL*/
asym->typ.basetype = tsym;
listpush(gattdefs,(void*)asym);
addtogroup(asym);
break;
default: PANIC1("unexpected attribute type: %d",kind);
}
/* finally; check that the attribute value contains no fill values*/
if(containsfills(data)) {
derror("Attribute data may not contain fill values (i.e. _ ): %s",asym->name);
}
return asym;
}
static long long
extractint(NCConstant* con)
{
switch (con->nctype) {
case NC_BYTE: return (long long)(con->value.int8v);
case NC_SHORT: return (long long)(con->value.int16v);
case NC_INT: return (long long)(con->value.int32v);
case NC_UBYTE: return (long long)(con->value.uint8v);
case NC_USHORT: return (long long)(con->value.uint16v);
case NC_UINT: return (long long)(con->value.uint32v);
case NC_INT64: return (long long)(con->value.int64v);
default:
derror("Not a signed integer type: %d",con->nctype);
break;
}
return 0;
}
static int
containsfills(Datalist* list)
{
if(list != NULL) {
int i;
NCConstant** cons = list->data;
for(i=0;i<list->length;i++) {
if(cons[i]->nctype == NC_COMPOUND) {
if(containsfills(cons[i]->value.compoundv)) return 1;
} else if(cons[i]->nctype == NC_FILLVALUE)
return 1;
}
}
return 0;
}
/*
Try to infer the file type from the
kinds of constructs used in the cdl file.
*/
static void
vercheck(int tid)
{
switch (tid) {
case NC_UBYTE: markcdf4("netCDF4/5 type: UBYTE"); break;
case NC_USHORT: markcdf4("netCDF4/5 type: USHORT"); break;
case NC_UINT: markcdf4("netCDF4/5 type: UINT"); break;
case NC_INT64: markcdf4("netCDF4/5 type: INT64"); break;
case NC_UINT64: markcdf4("netCDF4/5 type: UINT64"); break;
case NC_STRING: markcdf4("netCDF4 type: STRING"); break;
case NC_VLEN: markcdf4("netCDF4 type: VLEN"); break;
case NC_OPAQUE: markcdf4("netCDF4 type: OPAQUE"); break;
case NC_ENUM: markcdf4("netCDF4 type: ENUM"); break;
case NC_COMPOUND: markcdf4("netCDF4 type: COMPOUND"); break;
default: break;
}
}
const char*
specialname(int tag)
{
struct Specialtoken* spp = specials;
for(;spp->name;spp++) {
if(spp->tag == tag)
return spp->name;
}
return "<unknown>";
}
#ifdef USE_NETCDF4
/*
Parse a filter spec string and store it in special
*/
static int
parsefilterflag(const char* sdata, Specialdata* special)
{
int stat = NC_NOERR;
int format;
if(sdata == NULL || strlen(sdata) == 0) return NC_EINVAL;
stat = NC_parsefilterlist(sdata, &format, &special->nfilters, (NC_Filterspec***)&special->_Filters);
if(stat)
derror("Malformed filter spec: %s",sdata);
if(format != NC_FILTER_FORMAT_HDF5)
derror("Non HDF5 filter format encountered");
#ifdef GENDEBUG1
printfilters(special->nfilters,special->_Filters);
#endif
return stat;
}
#endif
/*
Since the arguments are all simple constants,
we can evaluate the function immediately
and return its value.
Note that currently, only a single value can
be returned.
*/
static NCConstant*
evaluate(Symbol* fcn, Datalist* arglist)
{
NCConstant* result = nullconst();
/* prepare the result */
result->lineno = fcn->lineno;
if(strcasecmp(fcn->name,"time") == 0) {
char* timekind = NULL;
char* timevalue = NULL;
result->nctype = NC_DOUBLE;
result->value.doublev = 0;
/* int time([string],string) */
switch (arglist->length) {
case 2:
if(arglist->data[1]->nctype != NC_STRING) {
derror("Expected function signature: time([string,]string)");
goto done;
}
/* fall thru */
case 1:
if(arglist->data[0]->nctype != NC_STRING) {
derror("Expected function signature: time([string,]string)");
goto done;
}
break;
case 0:
default:
derror("Expected function signature: time([string,]string)");
goto done;
}
if(arglist->length == 2) {
timekind = arglist->data[0]->value.stringv.stringv;
timevalue = arglist->data[1]->value.stringv.stringv;
} else
timevalue = arglist->data[0]->value.stringv.stringv;
if(timekind == NULL) { /* use cd time as the default */
cdCompTime comptime;
CdTime cdtime;
cdCalenType timetype = cdStandard;
cdChar2Comp(timetype,timevalue,&comptime);
/* convert comptime to cdTime */
cdtime.year = comptime.year;
cdtime.month = comptime.month;
cdtime.day = comptime.day;
cdtime.hour = comptime.hour;
cdtime.baseYear = 1970;
cdtime.timeType = CdChron;
/* convert to double value */
Cdh2e(&cdtime,&result->value.doublev);
} else {
derror("Time conversion '%s' not supported",timekind);
goto done;
}
} else { /* Unknown function */
derror("Unknown function name: %s",fcn->name);
goto done;
}
done:
return result;
}
#ifdef GENDEBUG1
static void
printfilters(size_t nfilters, NC_ParsedFilterSpec** filters)
{
int i;
fprintf(stderr,"xxx: nfilters=%lu: ",(unsigned long)nfilters);
for(i=0;i<nfilters;i++) {
int k;
NC_ParsedFilterSpec* sp = filters[i];
fprintf(stderr,"{");
fprintf(stderr,"filterid=%llu format=%d nparams=%lu params=%p",
sp->filterid,sp->format,(unsigned long)sp->nparams,sp->params);
if(sp->nparams == 0 || sp->params != NULL) {
fprintf(stderr," params={");
for(k=0;k<sp->nparams;k++) {
if(i==0) fprintf(stderr,",");
fprintf(stderr,"%u",sp->params[i]);
}
fprintf(stderr,"}");
} else
fprintf(stderr,"params=NULL");
fprintf(stderr,"}");
}
fprintf(stderr,"\n");
fflush(stderr);
}
#endif