Skip to content

Commit

Permalink
Merge pull request #2109 from DennisHeimbigner/ncgenenum.dmh
Browse files Browse the repository at this point in the history
Fix handling of enum constants nested in compound types.
  • Loading branch information
WardF committed Oct 1, 2021
2 parents 437060b + 72d6894 commit e763e6c
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 44 deletions.
2 changes: 1 addition & 1 deletion ncdump/cdl/ref_tst_econst.cdl
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ variables:
data:
test1=pass;
test2=e2.undefined;
test3=/e1.fail;
test3=e1.fail;
}
46 changes: 46 additions & 0 deletions ncdump/cdl/ref_tst_econst2.cdl
Original file line number Diff line number Diff line change
@@ -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 changes: 56 additions & 0 deletions ncdump/expected/ref_tst_econst2.dmp
Original file line number Diff line number Diff line change
@@ -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
}
6 changes: 6 additions & 0 deletions ncdump/tst_ncgen4_cycle.sh
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,15 @@ for x in ${TESTSET} ; do
for t in ${XFAILTESTS} ; do
if test "x${t}" = "x${x}" ; then isxfail=1; fi
done
isnocycle=0
for t in ${NOCYCLE} ; do
if test "x${t}" = "x${x}" ; then isnocycle=1; fi
done
if test "${isxfail}" = "1"; then
echo "xfail test: ${x}: ignored"
xfailcount=`expr $xfailcount + 1`
elif test "${isnocycle}" = "1"; then
echo "test: ${x}: ignored for cycle test"
else
rm -f ${x}_$$.nc ${x}_$$.dmp
# step 1: use original cdl to build the .nc
Expand Down
5 changes: 5 additions & 0 deletions ncdump/tst_ncgen_shared.sh
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,17 @@ unlimtest2 \
ref_niltest \
ref_tst_h_scalar \
ref_tst_nul4 \
ref_tst_econst \
ref_tst_econst2 \
"

if test "x$NC_VLEN_NOTEST" = x ; then
TESTS4="$TESTS4 ref_tst_vlen_data ref_tst_vlen_data2"
fi

# These tests should not be cycle tested because ncdump loses information
NOCYCLE="ref_tst_econst"

SPECIALTESTS3="ref_tst_special_atts3"

SPECIALTESTS="${SPECIALTESTS3} ref_tst_special_atts"
Expand Down
147 changes: 104 additions & 43 deletions ncgen/semantics.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ static void inferattributetype(Symbol* asym);
static void validateNIL(Symbol* sym);
static void checkconsistency(void);
static int tagvlentypes(Symbol* tsym);
static void computefqns(void);
static Symbol* uniquetreelocate(Symbol* refsym, Symbol* root);
static char* createfilename(void);

Expand Down Expand Up @@ -468,61 +467,124 @@ processeconstrefsR(Symbol* avsym, Datalist* data)
if(con->nctype == NC_COMPOUND) {
/* Iterate over the sublists */
processeconstrefsR(avsym,con->value.compoundv);
} else if(con->nctype == NC_ECONST || con->nctype == NC_FILLVALUE) {
} else if(con->nctype == NC_ECONST) {
fixeconstref(avsym,con);
}
}
}

/*
Collect all types in all groups using preorder depth-first traversal.
*/
static void
fixeconstref(Symbol* avsym, NCConstant* con)
typewalk(Symbol* root, nc_type typ, List* list)
{
Symbol* basetype = NULL;
Symbol* refsym = con->value.enumv;
Symbol* varsym = NULL;
int i;

/* Figure out the proper type associated with avsym */
ASSERT(avsym->objectclass == NC_VAR || avsym->objectclass == NC_ATT);
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) {
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;
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);
}
}

/* If this is a non-econst fillvalue, then ignore it */
if(con->nctype == NC_FILLVALUE && basetype->subclass != NC_ENUM)
return;
/* Now do all-tree search */
typewalk(rootgroup,typ,types);
}

/* 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;
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;
}

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;
}
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 */
}
semerror(con->lineno,"Undefined enum or enum constant reference: %s",refsym->name);
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
fixeconstref(Symbol* avsym, NCConstant* con)
{
Symbol* econst = NULL;

econst = findeconstenum(avsym,con);
assert(econst != NULL && econst->subclass == NC_ECONST);
con->value.enumv = econst;
}

/* Compute type sizes and compound offsets*/
Expand Down Expand Up @@ -1131,7 +1193,6 @@ processunlimiteddims(void)
#endif
}


/* Rules for specifying the dataset name:
1. use -o name
2. use the datasetname from the .cdl file
Expand Down

0 comments on commit e763e6c

Please sign in to comment.