mirror of
https://github.com/Unidata/netcdf-c.git
synced 2025-01-18 15:55:12 +08:00
Merge pull request #2109 from DennisHeimbigner/ncgenenum.dmh
Fix handling of enum constants nested in compound types.
This commit is contained in:
commit
e763e6caf1
@ -9,5 +9,5 @@ variables:
|
|||||||
data:
|
data:
|
||||||
test1=pass;
|
test1=pass;
|
||||||
test2=e2.undefined;
|
test2=e2.undefined;
|
||||||
test3=/e1.fail;
|
test3=e1.fail;
|
||||||
}
|
}
|
||||||
|
46
ncdump/cdl/ref_tst_econst2.cdl
Normal file
46
ncdump/cdl/ref_tst_econst2.cdl
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
netcdf foo { // an example netCDF specification in CDL
|
||||||
|
|
||||||
|
types:
|
||||||
|
ubyte enum enum_t {Clear = 0, Cumulonimbus = 1, Stratus = 2};
|
||||||
|
opaque(11) opaque_t;
|
||||||
|
int(*) vlen_t;
|
||||||
|
|
||||||
|
dimensions:
|
||||||
|
lat = 10, lon = 5, time = unlimited ;
|
||||||
|
|
||||||
|
variables:
|
||||||
|
long lat(lat), lon(lon), time(time);
|
||||||
|
float Z(time,lat,lon), t(time,lat,lon);
|
||||||
|
double p(time,lat,lon);
|
||||||
|
long rh(time,lat,lon);
|
||||||
|
|
||||||
|
string country(time,lat,lon);
|
||||||
|
ubyte tag;
|
||||||
|
|
||||||
|
// variable attributes
|
||||||
|
lat:long_name = "latitude";
|
||||||
|
lat:units = "degrees_north";
|
||||||
|
lon:long_name = "longitude";
|
||||||
|
lon:units = "degrees_east";
|
||||||
|
time:units = "seconds since 1992-1-1 00:00:00";
|
||||||
|
|
||||||
|
// typed variable attributes
|
||||||
|
string Z:units = "geopotential meters";
|
||||||
|
float Z:valid_range = 0., 5000.;
|
||||||
|
double p:_FillValue = -9999.;
|
||||||
|
long rh:_FillValue = -1;
|
||||||
|
vlen_t :globalatt = {17, 18, 19};
|
||||||
|
data:
|
||||||
|
lat = 0, 10, 20, 30, 40, 50, 60, 70, 80, 90;
|
||||||
|
lon = -140, -118, -96, -84, -52;
|
||||||
|
group: g {
|
||||||
|
types:
|
||||||
|
compound cmpd_t { vlen_t f1; enum_t f2; enum_t f3;};
|
||||||
|
} // group g
|
||||||
|
group: h {
|
||||||
|
variables:
|
||||||
|
/g/cmpd_t compoundvar;
|
||||||
|
data:
|
||||||
|
compoundvar = { {3,4,5}, Stratus, enum_t.Clear } ;
|
||||||
|
} // group h
|
||||||
|
}
|
56
ncdump/expected/ref_tst_econst2.dmp
Normal file
56
ncdump/expected/ref_tst_econst2.dmp
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
netcdf ref_tst_econst2 {
|
||||||
|
types:
|
||||||
|
ubyte enum enum_t {Clear = 0, Cumulonimbus = 1, Stratus = 2} ;
|
||||||
|
opaque(11) opaque_t ;
|
||||||
|
int(*) vlen_t ;
|
||||||
|
dimensions:
|
||||||
|
lat = 10 ;
|
||||||
|
lon = 5 ;
|
||||||
|
time = UNLIMITED ; // (0 currently)
|
||||||
|
variables:
|
||||||
|
int lat(lat) ;
|
||||||
|
lat:long_name = "latitude" ;
|
||||||
|
lat:units = "degrees_north" ;
|
||||||
|
int lon(lon) ;
|
||||||
|
lon:long_name = "longitude" ;
|
||||||
|
lon:units = "degrees_east" ;
|
||||||
|
int time(time) ;
|
||||||
|
time:units = "seconds since 1992-1-1 00:00:00" ;
|
||||||
|
float Z(time, lat, lon) ;
|
||||||
|
string Z:units = "geopotential meters" ;
|
||||||
|
Z:valid_range = 0.f, 5000.f ;
|
||||||
|
float t(time, lat, lon) ;
|
||||||
|
double p(time, lat, lon) ;
|
||||||
|
p:_FillValue = -9999. ;
|
||||||
|
int rh(time, lat, lon) ;
|
||||||
|
rh:_FillValue = -1 ;
|
||||||
|
string country(time, lat, lon) ;
|
||||||
|
ubyte tag ;
|
||||||
|
|
||||||
|
// global attributes:
|
||||||
|
vlen_t :globalatt = {17, 18, 19} ;
|
||||||
|
data:
|
||||||
|
|
||||||
|
lat = 0, 10, 20, 30, 40, 50, 60, 70, 80, 90 ;
|
||||||
|
|
||||||
|
lon = -140, -118, -96, -84, -52 ;
|
||||||
|
|
||||||
|
tag = 255 ;
|
||||||
|
|
||||||
|
group: g {
|
||||||
|
types:
|
||||||
|
compound cmpd_t {
|
||||||
|
vlen_t f1 ;
|
||||||
|
enum_t f2 ;
|
||||||
|
enum_t f3 ;
|
||||||
|
}; // cmpd_t
|
||||||
|
} // group g
|
||||||
|
|
||||||
|
group: h {
|
||||||
|
variables:
|
||||||
|
/g/cmpd_t compoundvar ;
|
||||||
|
data:
|
||||||
|
|
||||||
|
compoundvar = {{3, 4, 5}, Stratus, Clear} ;
|
||||||
|
} // group h
|
||||||
|
}
|
@ -31,9 +31,15 @@ for x in ${TESTSET} ; do
|
|||||||
for t in ${XFAILTESTS} ; do
|
for t in ${XFAILTESTS} ; do
|
||||||
if test "x${t}" = "x${x}" ; then isxfail=1; fi
|
if test "x${t}" = "x${x}" ; then isxfail=1; fi
|
||||||
done
|
done
|
||||||
|
isnocycle=0
|
||||||
|
for t in ${NOCYCLE} ; do
|
||||||
|
if test "x${t}" = "x${x}" ; then isnocycle=1; fi
|
||||||
|
done
|
||||||
if test "${isxfail}" = "1"; then
|
if test "${isxfail}" = "1"; then
|
||||||
echo "xfail test: ${x}: ignored"
|
echo "xfail test: ${x}: ignored"
|
||||||
xfailcount=`expr $xfailcount + 1`
|
xfailcount=`expr $xfailcount + 1`
|
||||||
|
elif test "${isnocycle}" = "1"; then
|
||||||
|
echo "test: ${x}: ignored for cycle test"
|
||||||
else
|
else
|
||||||
rm -f ${x}_$$.nc ${x}_$$.dmp
|
rm -f ${x}_$$.nc ${x}_$$.dmp
|
||||||
# step 1: use original cdl to build the .nc
|
# step 1: use original cdl to build the .nc
|
||||||
|
@ -97,12 +97,17 @@ unlimtest2 \
|
|||||||
ref_niltest \
|
ref_niltest \
|
||||||
ref_tst_h_scalar \
|
ref_tst_h_scalar \
|
||||||
ref_tst_nul4 \
|
ref_tst_nul4 \
|
||||||
|
ref_tst_econst \
|
||||||
|
ref_tst_econst2 \
|
||||||
"
|
"
|
||||||
|
|
||||||
if test "x$NC_VLEN_NOTEST" = x ; then
|
if test "x$NC_VLEN_NOTEST" = x ; then
|
||||||
TESTS4="$TESTS4 ref_tst_vlen_data ref_tst_vlen_data2"
|
TESTS4="$TESTS4 ref_tst_vlen_data ref_tst_vlen_data2"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# These tests should not be cycle tested because ncdump loses information
|
||||||
|
NOCYCLE="ref_tst_econst"
|
||||||
|
|
||||||
SPECIALTESTS3="ref_tst_special_atts3"
|
SPECIALTESTS3="ref_tst_special_atts3"
|
||||||
|
|
||||||
SPECIALTESTS="${SPECIALTESTS3} ref_tst_special_atts"
|
SPECIALTESTS="${SPECIALTESTS3} ref_tst_special_atts"
|
||||||
|
@ -34,7 +34,6 @@ static void inferattributetype(Symbol* asym);
|
|||||||
static void validateNIL(Symbol* sym);
|
static void validateNIL(Symbol* sym);
|
||||||
static void checkconsistency(void);
|
static void checkconsistency(void);
|
||||||
static int tagvlentypes(Symbol* tsym);
|
static int tagvlentypes(Symbol* tsym);
|
||||||
static void computefqns(void);
|
|
||||||
static Symbol* uniquetreelocate(Symbol* refsym, Symbol* root);
|
static Symbol* uniquetreelocate(Symbol* refsym, Symbol* root);
|
||||||
static char* createfilename(void);
|
static char* createfilename(void);
|
||||||
|
|
||||||
@ -468,61 +467,124 @@ processeconstrefsR(Symbol* avsym, Datalist* data)
|
|||||||
if(con->nctype == NC_COMPOUND) {
|
if(con->nctype == NC_COMPOUND) {
|
||||||
/* Iterate over the sublists */
|
/* Iterate over the sublists */
|
||||||
processeconstrefsR(avsym,con->value.compoundv);
|
processeconstrefsR(avsym,con->value.compoundv);
|
||||||
} else if(con->nctype == NC_ECONST || con->nctype == NC_FILLVALUE) {
|
} else if(con->nctype == NC_ECONST) {
|
||||||
fixeconstref(avsym,con);
|
fixeconstref(avsym,con);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Collect all types in all groups using preorder depth-first traversal.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
typewalk(Symbol* root, nc_type typ, List* list)
|
||||||
|
{
|
||||||
|
unsigned long i;
|
||||||
|
for(i=0;i<listlength(root->subnodes);i++) {
|
||||||
|
Symbol* sym = (Symbol*)listget(root->subnodes,i);
|
||||||
|
if(sym->objectclass == NC_GRP) {
|
||||||
|
typewalk(sym,typ,list);
|
||||||
|
} else if(sym->objectclass == NC_TYPE && (typ == NC_NAT || typ == sym->subclass)) {
|
||||||
|
if(!listcontains(list,sym))
|
||||||
|
listpush(list,sym);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find all user-define types of type typ in access order */
|
||||||
|
static void
|
||||||
|
orderedtypes(Symbol* avsym, nc_type typ, List* types)
|
||||||
|
{
|
||||||
|
Symbol* container = NULL;
|
||||||
|
listclear(types);
|
||||||
|
/* find innermost containing group */
|
||||||
|
if(avsym->objectclass == NC_VAR) {
|
||||||
|
container = avsym->container;
|
||||||
|
} else {
|
||||||
|
ASSERT(avsym->objectclass == NC_ATT);
|
||||||
|
container = avsym->container;
|
||||||
|
if(container->objectclass == NC_VAR)
|
||||||
|
container = container->container;
|
||||||
|
}
|
||||||
|
/* walk up the containing groups and collect type */
|
||||||
|
for(;container!= NULL;container = container->container) {
|
||||||
|
int i;
|
||||||
|
/* Walk types in the container */
|
||||||
|
for(i=0;i<listlength(container->subnodes);i++) {
|
||||||
|
Symbol* sym = (Symbol*)listget(container->subnodes,i);
|
||||||
|
if(sym->objectclass == NC_TYPE && (typ == NC_NAT || sym->subclass == typ))
|
||||||
|
listpush(types,sym);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Now do all-tree search */
|
||||||
|
typewalk(rootgroup,typ,types);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Symbol*
|
||||||
|
locateeconst(Symbol* enumt, const char* ecname)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for(i=0;i<listlength(enumt->subnodes);i++) {
|
||||||
|
Symbol* esym = (Symbol*)listget(enumt->subnodes,i);
|
||||||
|
ASSERT(esym->subclass == NC_ECONST);
|
||||||
|
if(strcmp(esym->name,ecname)==0)
|
||||||
|
return esym;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Symbol*
|
||||||
|
findeconstenum(Symbol* avsym, NCConstant* con)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
Symbol* refsym = con->value.enumv;
|
||||||
|
List* typdefs = listnew();
|
||||||
|
Symbol* enumt = NULL;
|
||||||
|
Symbol* candidate = NULL; /* possible enum type */
|
||||||
|
Symbol* econst = NULL;
|
||||||
|
char* path = NULL;
|
||||||
|
char* name = NULL;
|
||||||
|
|
||||||
|
/* get all enum types */
|
||||||
|
orderedtypes(avsym,NC_ENUM,typdefs);
|
||||||
|
|
||||||
|
/* It is possible that the enum const is prefixed with the type name */
|
||||||
|
|
||||||
|
path = strchr(refsym->name,'.');
|
||||||
|
if(path != NULL) {
|
||||||
|
path = strdup(refsym->name);
|
||||||
|
name = strchr(path,'.');
|
||||||
|
*name++ = '\0';
|
||||||
|
} else
|
||||||
|
name = refsym->name;
|
||||||
|
/* See if we can find the enum type */
|
||||||
|
for(i=0;i<listlength(typdefs);i++) {
|
||||||
|
Symbol* sym = (Symbol*)listget(typdefs,i);
|
||||||
|
ASSERT(sym->objectclass == NC_TYPE && sym->subclass == NC_ENUM);
|
||||||
|
if(path != NULL && strcmp(sym->name,path)==0) {enumt = sym; break;}
|
||||||
|
/* See if enum has a matching econst */
|
||||||
|
econst = locateeconst(sym,name);
|
||||||
|
if(candidate == NULL && econst != NULL) candidate = sym; /* remember this */
|
||||||
|
}
|
||||||
|
if(enumt != NULL) goto done;
|
||||||
|
/* otherwise use the candidate */
|
||||||
|
enumt = candidate;
|
||||||
|
done:
|
||||||
|
if(enumt) econst = locateeconst(enumt,name);
|
||||||
|
nullfree(path);
|
||||||
|
if(econst == NULL)
|
||||||
|
semerror(con->lineno,"Undefined enum constant: %s",refsym->name);
|
||||||
|
return econst;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
fixeconstref(Symbol* avsym, NCConstant* con)
|
fixeconstref(Symbol* avsym, NCConstant* con)
|
||||||
{
|
{
|
||||||
Symbol* basetype = NULL;
|
Symbol* econst = NULL;
|
||||||
Symbol* refsym = con->value.enumv;
|
|
||||||
Symbol* varsym = NULL;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* Figure out the proper type associated with avsym */
|
econst = findeconstenum(avsym,con);
|
||||||
ASSERT(avsym->objectclass == NC_VAR || avsym->objectclass == NC_ATT);
|
assert(econst != NULL && econst->subclass == NC_ECONST);
|
||||||
|
con->value.enumv = econst;
|
||||||
if(avsym->objectclass == NC_VAR) {
|
|
||||||
basetype = avsym->typ.basetype;
|
|
||||||
varsym = avsym;
|
|
||||||
} else { /*(avsym->objectclass == NC_ATT)*/
|
|
||||||
basetype = avsym->typ.basetype;
|
|
||||||
varsym = avsym->container;
|
|
||||||
if(varsym->objectclass == NC_GRP)
|
|
||||||
varsym = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If this is a non-econst fillvalue, then ignore it */
|
|
||||||
if(con->nctype == NC_FILLVALUE && basetype->subclass != NC_ENUM)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* If this is an econst then validate against type */
|
|
||||||
if(con->nctype == NC_ECONST && basetype->subclass != NC_ENUM)
|
|
||||||
semerror(con->lineno,"Enumconstant associated with a non-econst type");
|
|
||||||
|
|
||||||
if(con->nctype == NC_FILLVALUE) {
|
|
||||||
Datalist* filllist = NULL;
|
|
||||||
NCConstant* filler = NULL;
|
|
||||||
filllist = getfiller(varsym == NULL?basetype:varsym);
|
|
||||||
if(filllist == NULL)
|
|
||||||
semerror(con->lineno, "Cannot determine enum constant fillvalue");
|
|
||||||
filler = datalistith(filllist,0);
|
|
||||||
con->value.enumv = filler->value.enumv;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(i=0;i<listlength(basetype->subnodes);i++) {
|
|
||||||
Symbol* econst = listget(basetype->subnodes,i);
|
|
||||||
ASSERT(econst->subclass == NC_ECONST);
|
|
||||||
if(strcmp(econst->name,refsym->name)==0) {
|
|
||||||
con->value.enumv = econst;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
semerror(con->lineno,"Undefined enum or enum constant reference: %s",refsym->name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Compute type sizes and compound offsets*/
|
/* Compute type sizes and compound offsets*/
|
||||||
@ -1131,7 +1193,6 @@ processunlimiteddims(void)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Rules for specifying the dataset name:
|
/* Rules for specifying the dataset name:
|
||||||
1. use -o name
|
1. use -o name
|
||||||
2. use the datasetname from the .cdl file
|
2. use the datasetname from the .cdl file
|
||||||
|
Loading…
Reference in New Issue
Block a user