Merge pull request #2109 from DennisHeimbigner/ncgenenum.dmh

Fix handling of enum constants nested in compound types.
This commit is contained in:
Ward Fisher 2021-10-01 17:08:45 -05:00 committed by GitHub
commit e763e6caf1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 223 additions and 49 deletions

View File

@ -9,5 +9,5 @@ variables:
data: data:
test1=pass; test1=pass;
test2=e2.undefined; test2=e2.undefined;
test3=/e1.fail; test3=e1.fail;
} }

View 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
}

View 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
}

View File

@ -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

View File

@ -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"

View File

@ -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