This archive contains answers to questions sent to Unidata support through mid-2025. Note that the archive is no longer being updated. We provide the archive for reference; many of the answers presented here remain technically correct, even if somewhat outdated. For the most up-to-date information on the use of NSF Unidata software and data services, please consult the Software Documentation first.
I see that there are a number of constant forms missing. I will add them to the code base. Good catch! Chen Zhang wrote: > New Client Reply: NetCDF-4.0 make check error > > Russ, > > Thank you for this "pre-alpha" ncgen4. > > When I used unsigned integers and int64 types in CDL, I met some > problems. For example, put ushort type in CDL, such as "ushort > Angle(size); Angle:_FillValue = 9999s;" didn't work. Where "9999s" > should be "9999us" for ushort, but the suffix "us" wasn't supported by > ncgen4. I modified two files "ncgen.l" and "ncgen.y" (see attached > files) in ncgen4 to solve the above issues, now it works better. > > Thanks, > Chen > > > Unidata netCDF Support said the following on 10/3/2008 5:08 PM: >> Chen, >> >> Here is the "pre-alpha" version of ncgen4. Although our licensing >> says you can redistribute this, we would prefer just referring >> other potential users to us, and we'll make the latest version >> available with all the caveats about alpha level software. >> >> --Russ >> >> Russ Rew UCAR Unidata Program >> address@hidden http://www.unidata.ucar.edu >> >> >> >> Ticket Details >> =================== >> Ticket ID: YRL-141737 >> Department: Support netCDF >> Priority: Normal >> Status: Closed > > %{ > /********************************************************************* > * Copyright 1993, UCAR/Unidata > * See netcdf/COPYRIGHT file for copying and redistribution conditions. > * $Id: ncgen.l,v 1.1.1.1 2008/09/30 15:21:51 dmh Exp $ > *********************************************************************/ > > /* Problems: > 1. Ideally, we assume the input is true ut8. > Unfortunately, we may actually get iso-latin-8859-1. > This means that there will be ambiguity about the characters > in the range 128-255 because they will look like n-byte unicode > when they are 1-byte 8859 characters. Because of our encoding, > 8859 characters above 128 will be handles as n-byte utf8 and so > will probably not lex correctly. > Solution: assume utf8 and note in the documentation that > ISO8859 is specifically unsupported. > 2. The netcdf function NC_check_name in string.c must be modified to > conform to the use of UTF8. > 3. We actually have three tests for UTF8 of increasing correctness > (in the sense that the least correct will allow some sequences that > are technically illegal UTF8). > The tests are derived from the table at > http://www.w3.org/2005/03/23-lex-U > We include lexical definitions for all three, but use the second version. > */ > > /* lex specification for tokens for ncgen */ > > /* Fill value used by ncdump from version 2.4 and later. Should match > definition of FILL_STRING in ../ncdump/vardata.h */ > #define FILL_STRING "_" > #define XDR_INT32_MIN (-2147483647-1) > #define XDR_INT32_MAX 2147483647 > #define XDR_INT64_MIN (-9223372036854775807LL-1) > #define XDR_INT64_MAX (9223372036854775807LL) > > char errstr[100]; /* for short error messages */ > > int lineno; /* line number for error messages */ > char* lextext; /* name or string with escapes removed */ > > #include <stdio.h> > #include <ctype.h> > > #define YY_BREAK /* defining as nothing eliminates unreachable > statement warnings from flex output, > but make sure every action ends with > "return" or "break"! */ > > double double_val; /* last double value read */ > float float_val; /* last float value read */ > long long int64_val; /* last int64 value read */ > int int32_val; /* last int32 value read */ > short int16_val; /* last short value read */ > unsigned long long uint64_val; /* last int64 value read */ > unsigned int uint32_val; /* last int32 value read */ > unsigned short uint16_val; /* last short value read */ > char char_val; /* last char value read */ > char byte_val; /* last byte value read */ > unsigned char ubyte_val; /* last byte value read */ > > static Symbol* makepath(char* text); > > %} > > %p 6000 > > /* The most correct (validating) version of UTF8 character set > (Taken from: http://www.w3.org/2005/03/23-lex-U) > > The lines of the expression cover the UTF8 characters as follows: > 1. non-overlong 2-byte > 2. excluding overlongs > 3. straight 3-byte > 4. excluding surrogates > 5. straight 3-byte > 6. planes 1-3 > 7. planes 4-15 > 8. plane 16 > > UTF8 ([\xC2-\xDF][\x80-\xBF]) \ > | (\xE0[\xA0-\xBF][\x80-\xBF]) \ > | ([\xE1-\xEC][\x80-\xBF][\x80-\xBF]) \ > | (\xED[\x80-\x9F][\x80-\xBF]) \ > | ([\xEE-\xEF][\x80-\xBF][\x80-\xBF]) \ > | (\xF0[\x90-\xBF][\x80-\xBF][\x80-\xBF]) \ > | ([\xF1-\xF3][\x80-\xBF][\x80-\xBF][\x80-\xBF]) \ > | (\xF4[\x80-\x8F][\x80-\xBF][\x80-\xBF]) \ > > */ > > /* Wish there was some way to ifdef lex files */ > > /*The most relaxed version of UTF8 (not used) > UTF8 ([\xC0-\xD6].)|([\xE0-\xEF]..)|([\xF0-\xF7]...) > */ > > /*The partially relaxed version of UTF8, and the one used here */ > UTF8 ([\xC0-\xD6][\x80-\xBF])|([\xE0-\xEF][\x80-\xBF][\x80-\xBF])|([\xF0-\xF7][\x80-\xBF][\x80-\xBF][\x80-\xBF]) > > /* The old definition of ID > ID ([A-Za-z_]|{UTF8})([A-Z.@#\[\]a-z_0-9+-]|{UTF8})* > */ > > /* Don't permit control characters or '/' in names, but other special > chars OK if escaped. Note that to preserve backwards > compatibility, none of the characters _.@+- should be escaped, as > they were previously permitted in names without escaping. */ > > idescaped \\[ !"#$%&'()*,:;<=>?\[\\\]^`{|}~] > numescaped \\[0-9] > > /* New definition to conform to a subset of string.c */ > ID ([a-zA-Z_]|{UTF8}|{numescaped})([a-zA-Z0-9_.@+-]|{UTF8}|{idescaped})* > > escaped \\. > > /* Note: this definition of string will work for utf8 as well, > although it is a very relaxed definition > */ > nonquotes ([^"\\]|{escaped})* > exp ([eE][+-]?[0-9]+) > > OPAQUESTRING (0[xX][0-9A-Fa-f]*) > > PATH ([/]|([/]{ID})([/]{ID})*) > > %% > \/\/.* { /* comment */ > break; > } > > \/\*.*\*\/ { /* comment */ > break; > } > > \"{nonquotes}\" { > /* In netcdf4, this will be used in a variety > of places, so only remove escapes */ > /* > if(yyleng > MAXTRST) { > yyerror("string too long, truncated\n"); > yytext[MAXTRST-1] = '\0'; > } > */ > if(lextext != NULL) free(lextext); > lextext = emalloc(1+4*yyleng); /* should be max > needed */ > /* Assumes expand escapes also does normalization */ > expand_escapes(lextext,(char *)yytext,yyleng); > return (TERMSTRING); > } > > {OPAQUESTRING} { /* drop leading 0x; pad to event number of chars */ > char* p = yytext+2; > int len = yyleng - 2; > int padlen = len; > if((padlen % 2) == 1) padlen++; > if(lextext != NULL) free(lextext); > lextext = emalloc(1+padlen); > strncpy(lextext,p,len+1); //include null > lextext[len] = '0'; // assume padding > lextext[padlen] = '\0'; // make sure null terminated > // convert all chars to lower case > for(p=lextext;*p;p++) *p = tolower(*p); > return (OPAQUESTRING); > } > > [cC]ompound|[sS]truct|[sS]tructure|STRUCT|COMPOUND|STRUCTURE {return (COMPOUND);} > enum|ENUM {return (ENUM);} > opaque|OPAQUE {return (OPAQUE);} > > float|FLOAT|real|REAL {return (FLOAT_K);} > char|CHAR {return (CHAR_K);} > byte|BYTE {return (BYTE_K);} > ubyte|UBYTE {return (UBYTE_K);} > short|SHORT {return (SHORT_K);} > ushort|USHORT {return (USHORT_K);} > long|LONG|int|INT|integer|INTEGER {return (INT_K);} > ulong|ULONG|uint|UINT|uinteger|UINTEGER {return (UINT_K);} > int64|INT64 {return (INT64_K);} > uint64|UINT64 {return (UINT64_K);} > double|DOUBLE {return (DOUBLE_K);} > unlimited|UNLIMITED {int32_val = -1; > return (NC_UNLIMITED_K);} > > group:|GROUP: {return (GROUP);} > types:|TYPES: {return (TYPES);} > dimensions:|DIMENSIONS: {return (DIMENSIONS);} > variables:|VARIABLES: {return (VARIABLES);} > data:|DATA: {return (DATA);} > (netcdf|NETCDF|netCDF) {return (NETCDF);} > > DoubleInf|NaN|-?Infinity { /* missing value (pre-2.4 backward compatibility) */ > if (yytext[0] == '-') { > double_val = -NC_FILL_DOUBLE; > } else { > double_val = NC_FILL_DOUBLE; > } > return (DOUBLE_CONST); > } > FloatInf|NaNf|-?Infinityf|-?Inff {/* missing value (pre-2.4 backward compatibility)*/ > if (yytext[0] == '-') { > float_val = -NC_FILL_FLOAT; > } else { > float_val = NC_FILL_FLOAT; > } > return (FLOAT_CONST); > } > > {PATH} { > if(lextext != NULL) free(lextext); > lextext = emalloc(1+4*yyleng); /* should be max needed */ > strncpy(lextext,(char*)yytext,yyleng+1); // include null > yylval.sym = makepath(lextext); > return (PATH); > } > > > {ID} { > if(lextext != NULL) free(lextext); > lextext = emalloc(1+4*yyleng); /* should be max needed */ > strncpy(lextext,(char*)yytext,yyleng+1); // include null > deescapify(lextext); > if (STREQ((char *)lextext, FILL_STRING)) return (FILLVALUE); > yylval.sym = install(lextext); > return (IDENT); > } > > \n { > lineno++ ; > break; > } > > [+-]?[0-9]*[0-9]([uU]|[dD][uU])[Bb] { > int ii; > if (sscanf((char*)yytext, "%d", &ii) != 1) { > sprintf(errstr,"bad ubyte constant: %s",(char*)yytext); > yyerror(errstr); > } > ubyte_val = ii; > if (ii != (int)ubyte_val) { > sprintf(errstr,"ubyte constant out of range (0,255): %s",(char*)yytext); > yyerror(errstr); > } > return (UBYTE_CONST); > } > > [+-]?[0-9]*[0-9][Bb] { > int ii; > if (sscanf((char*)yytext, "%d", &ii) != 1) { > sprintf(errstr,"bad byte constant: %s",(char*)yytext); > yyerror(errstr); > } > byte_val = ii; > if (ii != (int)byte_val) { > sprintf(errstr,"byte constant out of range (-128,127): %s",(char*)yytext); > yyerror(errstr); > } > return (BYTE_CONST); > } > > [+-]?[0-9]*\.[0-9]*{exp}?[LlDd]?|[+-]?[0-9]*{exp}[LlDd]? { > if (sscanf((char*)yytext, "%le", &double_val) != 1) { > sprintf(errstr,"bad long or double constant: %s",(char*)yytext); > yyerror(errstr); > } > return (DOUBLE_CONST); > } > [+-]?[0-9]*\.[0-9]*{exp}?[Ff]|[+-]?[0-9]*{exp}[Ff] { > if (sscanf((char*)yytext, "%e", &float_val) != 1) { > sprintf(errstr,"bad float constant: %s",(char*)yytext); > yyerror(errstr); > } > return (FLOAT_CONST); > } > [+]?[0-9]+[uU][sS]|0[xX][0-9a-fA-F]+[uU][sS] { > if (sscanf((char*)yytext, "%hd", &uint16_val) != 1) { > sprintf(errstr,"bad ushort constant: %s",(char*)yytext); > yyerror(errstr); > } > return (USHORT_CONST); > } > [+-]?[0-9]+[sS]|0[xX][0-9a-fA-F]+[sS] { > if (sscanf((char*)yytext, "%hd", &int16_val) != 1) { > sprintf(errstr,"bad short constant: %s",(char*)yytext); > yyerror(errstr); > } > return (SHORT_CONST); > } > > > [+-]?([1-9][0-9]*|0)[zZ] { > char *ptr; > errno = 0; > double_val = strtod((char*)yytext, &ptr); > if (errno != 0 && double_val == 0.0) { > sprintf(errstr,"bad numerical constant: %s",(char*)yytext); > yyerror(errstr); > } > int64_val = (long long) double_val; > return INT64_CONST; > } > > [+]?([1-9][0-9]*|0)[uU][zZ] { > char *ptr; > errno = 0; > double_val = strtod((char*)yytext, &ptr); > if (errno != 0 && double_val == 0.0) { > sprintf(errstr,"bad numerical constant: %s",(char*)yytext); > yyerror(errstr); > } > uint64_val = (unsigned long long) double_val; > return UINT64_CONST; > } > > [+]?([1-9][0-9]*|0)[uU][iI] { > char *ptr; > errno = 0; > double_val = strtod((char*)yytext, &ptr); > if (errno != 0 && double_val == 0.0) { > sprintf(errstr,"bad numerical constant: %s",(char*)yytext); > yyerror(errstr); > } > uint32_val = (unsigned int) double_val; > return UINT_CONST; > } > > [+-]?([1-9][0-9]*|0)[lL]? { > char *ptr; > errno = 0; > double_val = strtod((char*)yytext, &ptr); > if (errno != 0 && double_val == 0.0) { > sprintf(errstr,"bad numerical constant: %s",(char*)yytext); > yyerror(errstr); > } > if (double_val < XDR_INT32_MIN ||double_val > XDR_INT32_MAX) { > return DOUBLE_CONST; > } else { > int32_val = (int) double_val; > return INT_CONST; > } > } > 0[xX]?[0-9a-fA-F]+[lL]? { > char *ptr; > long long_val; > errno = 0; > long_val = strtol((char*)yytext, &ptr, 0); > if (errno != 0) { > sprintf(errstr,"bad long constant: %s",(char*)yytext); > yyerror(errstr); > } > if (long_val < XDR_INT32_MIN || long_val > XDR_INT32_MAX) { > double_val = (double) long_val; > return DOUBLE_CONST; > } else { > int32_val = (int) long_val; > return INT_CONST; > } > } > \'[^\\]\' { > (void) sscanf((char*)&yytext[1],"%c",&byte_val); > return (BYTE_CONST); > } > \'\\[0-7][0-7]?[0-7]?\' { > byte_val = (char) strtol((char*)&yytext[2], (char **) 0, 8); > return (BYTE_CONST); > } > \'\\[xX][0-9a-fA-F][0-9a-fA-F]?\' { > byte_val = (char) strtol((char*)&yytext[3], (char **) 0, 16); > return (BYTE_CONST); > } > \'\\.\' { > switch ((char)yytext[2]) { > case 'a': byte_val = '\007'; break; /* not everyone under- > * stands '\a' yet */ > case 'b': byte_val = '\b'; break; > case 'f': byte_val = '\f'; break; > case 'n': byte_val = '\n'; break; > case 'r': byte_val = '\r'; break; > case 't': byte_val = '\t'; break; > case 'v': byte_val = '\v'; break; > case '\\': byte_val = '\\'; break; > case '?': byte_val = '\177'; break; > case '\'': byte_val = '\''; break; > default: byte_val = (char)yytext[2]; > } > return (BYTE_CONST); > } > > [ \r\t\f]+ { /* whitespace */ > break; > } > . {/* Note: this next rule will not work for UTF8 characters */ > return (yytext[0]) ; > } > %% > int > lex_init(void) > { > lineno = 1; > lextext = NULL; > if(0) unput(0); // keep -Wall quiet > return 0; > } > > static Symbol* > makepath(char* text) > { > // Convert path to a sequence of symbols > // use last name as symbol name (with '/' as exception) > Symbol* sym; > Sequence* prefix = sqNew(); > // walk the path converting to a sequence of symbols > if(strcmp(text,"/")==0) { > // special case of root reference > sym = rootgroup; > } else { > // split the text into IDENT chunks, convert to symbols > // and add to prefix, except the last > char *ident, *p; > int c; > ident=text+1; p=ident; // skip leading '/' > while((c=*p)) { > switch (c) { > case '/': > *p='\0'; > deescapify(ident); > sym = install(ident); > sym->objectclass = NC_GRP; // must be a group ref > sym->is_ref = 1; > sqPush(prefix,(elem_t)sym); > ident=p+1; p=ident; > break; > case '\\': p++; if(*p == '/') p++; break; > default: p++; break; > } > } > assert(sqLength(prefix) > 0); > // last symbol is one we return; turn rest into the symbol's prefix > sqPop(prefix); > sym->prefix = prefix; > sym->is_prefixed = 1; > sym->objectclass = NC_NAT; //make caller set correctly > } > free(text); > return sym; > } > > /********************************************************************* > * Copyright 1993, UCAR/Unidata > * See netcdf/COPYRIGHT file for copying and redistribution conditions. > * $Id: ncgen.y,v 1.2 2008/09/30 16:14:22 dmh Exp $ > *********************************************************************/ > > /* > Issues: > 1. when to deescape strings (esp names) > 2. handling of fill values > */ > > > /* yacc source for "ncgen", a netCDL parser and netCDF generator */ > > %{ > /* > static char SccsId[] = "$Id: ncgen.y,v 1.2 2008/09/30 16:14:22 dmh Exp $"; > */ > #include <stdlib.h> > #include <string.h> > #include <assert.h> > #include "netcdf.h" > #include "ncgen.h" > #include "genlib.h" /* for grow_darray() et al */ > #include "util.h" > #include "debug.h" > > /* parser controls */ > #define YY_NO_INPUT 1 > > > /* True if string a equals string b*/ > #define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0) > #define VLENSIZE (sizeof(nc_vlen_t)) > #define MAXFLOATDIM 4294967295.0 > #define PRIMNO (NC_STRING - NC_NAT + 1) > > /* mnemonic */ > typedef enum Attrkind {ATTRVAR, ATTRGLOBAL, DONTKNOW} Attrkind; > > typedef nc_vlen_t vlen_t; > > /* We retain the old representation of the symbol list > as a linked list. > */ > Symbol* symlist; > > // Track rootgroup separately > Symbol* rootgroup; > > /* Track the group sequence */ > static Sequence* groupstack; > > /* Provide a separate sequence for accumulating values > during the parse. > */ > static Sequence* stack; > > static Sequence* datalist; > > static nc_type consttype; // track homogeneity of types for data lists > > /* Misc. */ > static int i,j; // loop counters > static int stackbase; > static int stacklen; > static int count; > static int opaqueid; // counter for opaque constants > > Symbol* primsymbols[PRIMNO]; > > char* primtypenames[PRIMNO] = { > "nat", > "byte", "char", "short", > "int", "float", "double", > "ubyte", "ushort", "uint", > "int64", "uint64", > "string" > }; > > //Defined in ncgen.l > extern int lineno; /* line number for error messages */ > extern char* 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 char byte_val; /* last byte value read */ > extern unsigned char ubyte_val; /* last byte value read */ > > // Track definitions of dims, types, attributes, and vars > Sequence* grpdefs; > Sequence* dimdefs; > Sequence* attdefs; // variable-specific attributes > Sequence* gattdefs; // global attributes only > Sequence* xattdefs; // unknown attributes > Sequence* typdefs; > Sequence* vardefs; > Sequence* condefs; // non-dimension constants used in type defs > > /* Forward */ > //static void mark(Mark); > //static Datalist* makeattrconst(nc_type); > static Datalist* makedataconst(nc_type); > static Datalist* makeenumconst(Symbol*); > static void addtogroup(Symbol*); > static Symbol* getunlimiteddim(void); > static Symbol* currentgroup(void); > static Symbol* createrootgroup(void); > static Symbol* creategroup(Symbol*); > static int dupobjectcheck(nc_class,Symbol*); > static void setpathcurrent(Symbol* sym); > static char* primtypename(nc_type nctype); > static Symbol* makeattribute(Symbol*,Symbol*,Symbol*,Sequence*,Attrkind); > //static int isnesteddatalist(Datalist* dlist); > > 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 > struct Datalist* datalist; > Sequence* dataseq; > } > > %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 */ > IDENT /* name for a dimension, variable, or attribute */ > TERMSTRING /* terminal string */ > BYTE_CONST /* byte constant */ > CHAR_CONST /* char constant (not ever generated by ncgen.l) */ > SHORT_CONST /* short constant */ > INT_CONST /* int constant */ > FLOAT_CONST /* float constant */ > DOUBLE_CONST /* double constant */ > UBYTE_CONST /* ubyte constant */ > USHORT_CONST /* ushort constant */ > UINT_CONST /* uint constant */ > INT64_CONST /* int64 constant */ > UINT64_CONST /* uint64 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 */ > FILLVALUE /* fill value, from _FillValue attribute or default */ > TYPES > COMPOUND > ENUM > OPAQUE > OPAQUESTRING /* 0x<even number of hexdigits> */ > GROUP > PATH /* / or (/IDENT)+ */ > > %type <sym> typename varref typeref type_var_ref primtype dimd varspec > attdecl gattdecl enumid path dimref fielddim fieldspec > %type <mark> enumidlist fieldlist fields varlist dimspec dimlist field > fielddimspec fielddimlist > %type <dataseq> datalist > %type <datalist> dataconst dataset > > %start ncdesc /* start symbol for grammar */ > > %% > > /* RULES */ > > ncdesc: NETCDF > datasetname > rootgroup > {if (derror_count > 0) exit(6);} > ; > > datasetname: IDENT {datasetname = strdup($1->name);} > > rootgroup: '{' > groupbody > subgrouplist > '}'; > > groupbody: > 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 '{' > { > if(usingclassic()) {verror("Group specification");} > if(creategroup($2) == NULL) > yyerror("duplicate group declaration within parent group for %s", > $2->name); > } > groupbody > subgrouplist > {sqPop(groupstack);} > '}'; > > typesection: /* empty */ > | TYPES > | TYPES > { > if(usingclassic()) > verror("Type specification"); > } > typedecls > ; > > typedecls: type_attr_decl | typedecls type_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); > sqPush(typdefs,(elem_t)$1); > } > ; > > type_attr_decl: typedecl | gattdecl ';' ; > > typedecl: enumdecl ';' | compounddecl ';' | vlendecl ';' | opaquedecl ';' ; > > enumdecl: primtype ENUM typename > '{' enumidlist '}' > { > addtogroup($3); // sets prefix > $3->objectclass=NC_TYPE; > $3->subclass=NC_ENUM; > $3->typ.basetype=$1; > $3->typ.size = $1->typ.size; > stackbase=$5; > stacklen=sqLength(stack); > $3->subnodes = sqNew(); > // 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*)sqGet(stack,i); > assert(eid->subclass == NC_ECONST); > addtogroup(eid); > sqPush($3->subnodes,(elem_t)eid); > eid->container = $3; > eid->typ.basetype = $3->typ.basetype; > } > sqSetlength(stack,stackbase);// remove stack nodes > } > ; > > enumidlist: enumid > {$$=sqLength(stack); sqPush(stack,(elem_t)$1);} > | enumidlist ',' enumid > { > $$=$1; > // check for duplicates > stackbase=$1; > stacklen=sqLength(stack); > for(i=stackbase;i<stacklen;i++) { > Symbol* elem = (Symbol*)sqGet(stack,i); > if(strcmp($3->name,elem->name)==0) > yyerror("duplicate enum declaration for %s", > elem->name); > } > sqPush(stack,(elem_t)$3); > } > ; > > enumid: IDENT '=' dataconst > { > $1->objectclass=NC_TYPE; > $1->subclass=NC_ECONST; > $1->typ.econst=$3; > $$=$1; > } > ; > > opaquedecl: OPAQUE '(' INT_CONST ')' typename > { > addtogroup($5); //sets prefix > $5->objectclass=NC_TYPE; > $5->subclass=NC_OPAQUE; > $5->typ.typecode=NC_OPAQUE; > $5->typ.size=int32_val; > } > ; > > vlendecl: typeref '(' '*' ')' typename > { > addtogroup($5); //sets prefix > Symbol* basetype = $1; > $5->objectclass=NC_TYPE; > $5->subclass=NC_VLEN; > $5->typ.basetype=basetype; > $5->typ.typecode=NC_VLEN; > $5->typ.size=VLENSIZE; > } > ; > > compounddecl: COMPOUND typename '{' fields '}' > { > addtogroup($2); > // check for duplicate field names > stackbase=$4; > stacklen=sqLength(stack); > for(i=stackbase;i<stacklen;i++) { > Symbol* elem1 = (Symbol*)sqGet(stack,i); > for(j=i+1;j<stacklen;j++) { > Symbol* elem2 = (Symbol*)sqGet(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 = sqNew(); > // Add in the fields > for(i=stackbase;i<stacklen;i++) { > Symbol* fsym = (Symbol*)sqGet(stack,i); > fsym->container = $2; > sqPush($2->subnodes,(elem_t)fsym); > } > sqSetlength(stack,stackbase);// remove stack nodes > } > ; > > > fields: field ';' {$$=$1;} > | fields field ';' {$$=$1;} > ; > > field: typeref fieldlist > { > $$=$2; > stackbase=$2; > stacklen=sqLength(stack); > //fix: move this check into semantics.c > // 2. check that none of the fields have an unlimited dimension; > // this a consequence of the fact that the netCDF extended > // interface does not allow reading/writing of individual > // fields in a struct:only the whole struct can be read/written. > for(i=stackbase;i<stacklen;i++) { > Symbol* v = (Symbol*)sqGet(stack,i); > v->typ.basetype=$1; > #ifdef IGNORE > if(v->var.dimset.ndims > 0 ) { > int j; > for(j=0;j<v->var.dimset.ndims;j++) { > if(v->var.dimset.dimsyms[j]->dim.size == NC_UNLIMITED) { > yyerror("Compound field dimensions may not be UNLIMITED: %s (dimension %d); Use VLEN type instead.",v->name,j); > break; > } > } > } > #endif > } > } > ; > > 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 { $$ = primsymbols[NC_UBYTE]; } > | USHORT_K { $$ = primsymbols[NC_USHORT]; } > | UINT_K { $$ = primsymbols[NC_UINT]; } > | INT64_K { $$ = primsymbols[NC_INT64]; } > | UINT64_K { $$ = primsymbols[NC_UINT64]; } > ; > > dimsection: /* empty */ > | DIMENSIONS > | DIMENSIONS dimdecls > ; > > dimdecls: dim_attr_decl ';' > | dimdecls dim_attr_decl ';' > ; > > dim_attr_decl: dimdeclist | gattdecl ; > > dimdeclist: dimdecl > | dimdeclist ',' dimdecl > ; > > dimdecl: dimd '=' INT_CONST > { if (int32_val <= 0) > yyerror("dimension length must be positive"); > $1->dim.size = (size_t)int32_val; > $1->dim.datasize = $1->dim.size; > } > | dimd '=' DOUBLE_CONST > { /* for rare case where 2^31 < dimsize < 2^32 */ > if (double_val <= 0) > yyerror("dimension length must be positive"); > if (double_val > MAXFLOATDIM) > yyerror("dimension too large"); > if (double_val - (size_t) double_val > 0) > yyerror("dimension length must be an integer"); > $1->dim.size = (size_t)double_val; > $1->dim.datasize = $1->dim.size; > } > | dimd '=' NC_UNLIMITED_K > { > if(usingclassic()) { > // check for multiple UNLIMITED decls > if(getunlimiteddim() != NULL) > verror("Type specification"); > } > $1->dim.size = NC_UNLIMITED; > } > ; > > dimd: IDENT > { > $1->objectclass=NC_DIM; > if(dupobjectcheck(NC_DIM,$1)) > yyerror( "Duplicate dimension declaration for %s", > $1->name); > addtogroup($1); > $$=$1; > sqPush(dimdefs,(elem_t)$1); > } > ; > > vasection: /* empty */ > | VARIABLES > | VARIABLES vadecls > ; > > vadecls: vadecl ';' > | vadecls vadecl ';' > ; > > vadecl: vardecl | attdecl ; > > vardecl: typeref varlist > { > stackbase=$2; > stacklen=sqLength(stack); > for(i=stackbase;i<stacklen;i++) { > Symbol* sym = (Symbol*)sqGet(stack,i); > sym->objectclass = NC_VAR; > if(dupobjectcheck(NC_VAR,$1)) > yyerror("Duplicate variable declaration for %s", > sym->name); > sym->typ.basetype = $1; > addtogroup(sym); > sqPush(vardefs,(elem_t)sym); > } > sqSetlength(stack,stackbase);// remove stack nodes > } > ; > > varlist: varspec > {$$=sqLength(stack); > sqPush(stack,(elem_t)$1); > } > | varlist ',' varspec > {$$=$1; sqPush(stack,(elem_t)$3);} > ; > > varspec: IDENT dimspec > { > $1->objectclass=NC_VAR; > stackbase=$2; > stacklen=sqLength(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; > } > $1->var.dimset.ndims = count; > // extract the actual dimensions > if($1->var.dimset.ndims > 0) { > for(i=0;i<count;i++) { > Symbol* dsym = (Symbol*)sqGet(stack,stackbase+i); > $1->var.dimset.dimsyms[i] = dsym; > } > } > sqSetlength(stack,stackbase);// remove stack nodes > } > ; > > dimspec: /* empty */ {$$=sqLength(stack);} > | '(' dimlist ')' {$$=$2;} > ; > > dimlist: dimref {$$=sqLength(stack); sqPush(stack,(elem_t)$1);} > | dimlist ',' dimref > {$$=$1; sqPush(stack,(elem_t)$3);} > ; > > dimref: path > { > $1->objectclass = NC_DIM; > $1->is_ref=1; > $1->ref=NULL; > $$=$1; > } > ; > > fieldlist: > fieldspec > {$$=sqLength(stack); > sqPush(stack,(elem_t)$1); > } > | fieldlist ',' fieldspec > {$$=$1; sqPush(stack,(elem_t)$3);} > ; > > fieldspec: > IDENT fielddimspec > { > $1->objectclass=NC_TYPE; > $1->subclass=NC_FIELD; > stackbase=$2; > stacklen=sqLength(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; > } > $1->var.dimset.ndims = count; > // extract the actual dimensions > for(i=0;i<count;i++) { > Symbol* dsym = (Symbol*)sqGet(stack,stackbase+i); > $1->var.dimset.dimsyms[i] = dsym; > } > sqSetlength(stack,stackbase);// remove stack nodes > $$ = $1; > } > ; > > fielddimspec: /* empty */ {$$=sqLength(stack);} > | '(' fielddimlist ')' {$$=$2;} > ; > > fielddimlist: > fielddim {$$=sqLength(stack); sqPush(stack,(elem_t)$1);} > | fielddimlist ',' fielddim > {$$=$1; sqPush(stack,(elem_t)$3);} > ; > > fielddim: > INT_CONST > { /* Anonymous integer dimension. > Can only occur in type definitions*/ > char anon[32]; > sprintf(anon,"const%d",int32_val); > $$ = install(anon); > $$->objectclass = NC_DIM; > $$->dim.isconstant = 1; > $$->dim.size = int32_val; > $$->dim.datasize = $$->dim.size; > } > ; > > > /* Use this when referencing defined objects */ > > varref: path {$$=$1; $$->objectclass=NC_VAR;} ; > > typeref: > path {$$=$1; $$->objectclass=NC_TYPE;} > | primtype {$$=$1;} > ; > > type_var_ref: > path {$$=$1; $$->objectclass=NC_NAT;} /* Unknown at this point*/ > | primtype {$$=$1;} > ; > > /* Use this when the attribute might be global or local */ > attdecl: > typeref varref ':' IDENT '=' datalist > {$$=makeattribute($4,$2,$1,$6,ATTRVAR);} > | type_var_ref ':' IDENT '=' datalist > { $$=makeattribute($3,$1,NULL,$5,DONTKNOW);} > | ':' IDENT '=' datalist > { $$=makeattribute($2,NULL,NULL,$4,ATTRGLOBAL);} > ; > > gattdecl: > typeref ':' IDENT '=' datalist > { $$=makeattribute($3,NULL,$1,$5,ATTRGLOBAL);} > | ':' IDENT '=' datalist > { $$=makeattribute($2,NULL,NULL,$4,ATTRGLOBAL);} > ; > > path: > IDENT > { > $$=$1; > $1->is_ref=1; > setpathcurrent($1); > } > | PATH > { > $$=$1; > $1->is_ref=1; > // path is set in ncgen.l > } > ; > > datasection: /* empty */ > | DATA > | DATA datadecls > ; > > datadecls: datadecl ';' > | datadecls datadecl ';' > ; > > datadecl: varref '=' datalist > {$1->data = makedatalist(NC_VAR,$3);} > ; > > datalist: > dataset {$$=sqNew(); sqPush($$,(elem_t)$1);} > | datalist ',' dataset {$$=$1; sqPush($$,(elem_t)$3);} > ; > > dataset: > dataconst {$$=$1;} > | '{' datalist '}' {$$ = makedatalist(NC_COMPOUND,$2);} > ; > > dataconst: > CHAR_CONST {$$=makedataconst(NC_CHAR);} // never used apparently > | BYTE_CONST {$$=makedataconst(NC_BYTE);} > | SHORT_CONST {$$=makedataconst(NC_SHORT);} > | INT_CONST {$$=makedataconst(NC_INT);} > | FLOAT_CONST {$$=makedataconst(NC_FLOAT);} > | DOUBLE_CONST {$$=makedataconst(NC_DOUBLE);} > | UBYTE_CONST {$$=makedataconst(NC_UBYTE);} > | USHORT_CONST {$$=makedataconst(NC_USHORT);} > | UINT_CONST {$$=makedataconst(NC_UINT);} > | INT64_CONST {$$=makedataconst(NC_INT64);} > | UINT64_CONST {$$=makedataconst(NC_UINT64);} > | TERMSTRING {$$=makedataconst(NC_STRING);} > | OPAQUESTRING {$$=makedataconst(NC_OPAQUE);} > | path {$$=makeenumconst($1);} > | FILLVALUE {$$=makedataconst(NC_FILLVALUE);} > ; > > /* End OF RULES */ > > %% > > #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; > vastart(argv,fmt); > (void)fprintf(stderr,"%s: %s line %d: ", progname, cdlname, lineno); > vderror(fmt,argv); > } > > /* undefine yywrap macro, in case we are using bison instead of yacc */ > #ifdef yywrap > #undef yywrap > #endif > > static int > yywrap(void) /* returns 1 on EOF if no more input */ > { > return 1; > } > > /* get lexical input routine generated by lex */ > #include "ncgenyy.c" > > /* Really should init our data within this file */ > void > parse_init(void) > { > int i; > derror_count=0; > opaqueid = 0; > symlist = NULL; > stack = sqNew(); > datalist = NULL; > groupstack = sqNew(); > consttype = NC_NAT; > grpdefs = sqNew(); > dimdefs = sqNew(); > attdefs = sqNew(); > gattdefs = sqNew(); > xattdefs = sqNew(); > typdefs = sqNew(); > vardefs = sqNew(); > condefs = sqNew(); > createrootgroup(); > /* Create the primitive types */ > for(i=NC_NAT+1;i<=NC_STRING;i++) { > Symbol* sym = install(primtypenames[i]); > sym->objectclass=NC_TYPE; > sym->subclass=NC_PRIM; > sym->ncid = i; > sym->typ.basetype = NULL; > sym->typ.typecode = i; > sym->typ.size = ncsize(i); > sym->prefix = sqNew(); > primsymbols[i] = sym; > } > lex_init(); > } > > /* Symbol table operations for ncgen tool */ > > /* install sname in symbol table */ > Symbol* > install(const char *sname) > { > Symbol* sp; > sp = (Symbol*) emalloc (sizeof (struct Symbol)); > memset((void*)sp,0,sizeof(struct Symbol)); > sp->name = nulldup(sname); > sp->next = symlist; > sp->lineno = lineno; > sp->container = currentgroup(); > symlist = sp; > return sp; > } > > static Symbol* > getunlimiteddim(void) > { > Symbol* sp; > for(sp=symlist;sp;sp=sp->next) { > if(sp->objectclass == NC_DIM && sp->dim.size == -1) return sp; > } > return NULL; /* 0 ==> not found */ > } > > static Symbol* > currentgroup(void) > { > if(sqLength(groupstack) == 0) return rootgroup; > return (Symbol*)sqTop(groupstack); > } > > static Symbol* > createrootgroup(void) > { > Symbol* gsym = install(ROOTGROUPNAME); > gsym->objectclass = NC_GRP; > gsym->container = NULL; > gsym->subnodes = sqNew(); > gsym->grp.is_root = 1; > gsym->prefix = sqNew(); > sqPush(grpdefs,(elem_t)gsym); > rootgroup = gsym; > return gsym; > } > > static Symbol* > creategroup(Symbol * gsym) > { > /* See if this group already exists in currentgroup */ > gsym->objectclass = NC_GRP; > gsym->container = currentgroup(); > if(dupobjectcheck(NC_GRP,gsym)) {return NULL;} > setpathcurrent(gsym); > gsym->subnodes = sqNew(); > sqPush(gsym->container->subnodes,(elem_t)gsym); > sqPush(groupstack,(elem_t)gsym); > sqPush(grpdefs,(elem_t)gsym); > return gsym; > } > > #ifdef IGNORE > static void > mark(Mark beginend) > { > Datalist* ci = makedatalist(); > ci->lineno = lineno; > ci->tag = NC_COMPOUND; > ci->nctype = NC_COMPOUND; > ci->value.mark = beginend; > sqPush(datalist,(elem_t)ci); > } > > #endif > > static Datalist* > makedataconst(nc_type nctype) > { > Datalist* ci; > // Note that although NC_STRING does not exist > // for netcdf classic, string constants exist > // and will be treated as sequences of characters > if(usingclassic() && !isclassicprim(nctype)) { > verror("Illegal type: %s",primtypename(nctype)); > } > consttype = nctype; > ci = makedatalist(NC_PRIM,NULL); > ci->nctype = nctype; > switch (nctype) { > case NC_CHAR: ci->value.charv = char_val; break; > case NC_BYTE: ci->value.int8v = byte_val; break; > case NC_SHORT: ci->value.int16v = int16_val; break; > case NC_INT: ci->value.int32v = int32_val; break; > case NC_FLOAT: ci->value.floatv = float_val; break; > case NC_DOUBLE: ci->value.doublev = double_val; break; > case NC_UBYTE: ci->value.uint8v = ubyte_val; break; > case NC_USHORT: ci->value.uint16v = uint16_val; break; > case NC_UINT: ci->value.uint32v = uint32_val; break; > case NC_INT64: ci->value.int64v = int64_val; break; > case NC_UINT64: ci->value.uint64v = uint64_val; break; > case NC_STRING: { // convert to a set of chars > int len; > ci->tag = NC_PRIM; > ci->nctype = NC_STRING; > len = strlen(lextext); > if(len == 0) len = 1; > ci->value.stringv.len = len; > ci->value.stringv.stringv = strdup(lextext); > } break; > case NC_OPAQUE: { > char* s; > int len,padlen; > len = strlen(lextext); > padlen = len; > if(padlen < 16) padlen = 16; > if((padlen % 2) == 1) padlen++; > s = (char*)emalloc(padlen+1); > memset((void*)s,'0',padlen); > s[padlen]='\0'; > strncpy(s,lextext,len); > ci->value.opaquev.stringv = s; > ci->value.opaquev.len = padlen; > s = (char*)emalloc(16); > sprintf(s,"opaqueconst%03d",++opaqueid); > ci->value.opaquev.lname = s; > } break; > > case NC_FILLVALUE: > break; // no associated value > > default: > yyerror("Data constant: unexpected NC type: %s", > nctypename(nctype)); > ci->value.stringv.stringv = NULL; > ci->value.stringv.len = 0; > } > return ci; > } > > static Datalist* > makeenumconst(Symbol* econst) > { > Datalist* ci; > if(usingclassic()) { > verror("Illegal type: enum"); > } > consttype = NC_ENUM; > ci = makedatalist(NC_PRIM,NULL); > ci->nctype = NC_ECONST; > ci->value.enumv = econst; > // fix up econst to be a ref to an econst > econst->objectclass = NC_TYPE; > econst->subclass = NC_ECONST; > return ci; > } > > static void > addtogroup(Symbol* sym) > { > Symbol* grp = currentgroup(); > sym->container = currentgroup(); > sqPush(grp->subnodes,(elem_t)sym); > setpathcurrent(sym); > } > > // Check for duplicate name of given type within current group > static int > dupobjectcheck(nc_class objectclass, Symbol* pattern) > { > Symbol* grp; > if(pattern == NULL) return 0; > grp = pattern->container; > if(grp == NULL) return 0; > for(i=0;i<sqLength(grp->subnodes);i++) { > Symbol* sym = (Symbol*)sqGet(grp->subnodes,i); > if(!sym->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 char* > primtypename(nc_type nctype) > { > if(isprim(nctype)) return primtypenames[nctype]; > return "nc_<UNKNOWN>"; > } > > #ifdef IGNORE > // Return 1 if the attribute datalist has nested compound elements > static int > isnesteddatalist(Datalist* dlist) > { > int i; > Sequence* set = dlist->datalist; > for(i=0;i<sqLength(set);i++) { > Datalist* d = (Datalist*)sqGet(set,i); > if(d->tag == NC_COMPOUND) return 1; > } > return 0; > } > > static Symbol* > makeintconst(int value) > { > Symbol* sym; > char tmp[256]; > int i; > sprintf(tmp,"int%d",value); > // see if already defined > for(i=0;i<sqLength(condefs);i++) { > Symbol* csym = (Symbol*)sqGet(condefs,i); > if(csym->con.nctype == NC_INT && csym->con.value.int32v == int32_val) > return csym; > } > sym = install(tmp); > sym->objectclass = NC_CONST; > sym->con.nctype = NC_INT; > sym->con.value.int32v = int32_val; > sqPush(condefs,(elem_t)sym); > return sym; > } > #endif > > static Symbol* > makeattribute(Symbol* asym, > Symbol* vsym, > Symbol* tsym, > Sequence* datalist, > Attrkind kind) // global var or unknown > { > asym->objectclass = NC_ATT; > asym->data = makedatalist(NC_ATT,datalist); > // BTW,check for nesting when using classic > // if(usingclassic() && isnesteddatalist(asym->data)) > // verror("{...} within attribute data list"); > addtogroup(asym); > switch (kind) { > case DONTKNOW: > // We won't know until semantic processing > asym->att.unknown = vsym; > sqPush(xattdefs,(elem_t)asym); > break; > case ATTRVAR: > asym->att.var = vsym; > asym->typ.basetype = tsym; > sqPush(attdefs,(elem_t)asym); > break; > case ATTRGLOBAL: > asym->att.var = NULL; // NULL => NC_GLOBAL > asym->typ.basetype = tsym; > sqPush(gattdefs,(elem_t)asym); > break; > } > return asym; > } > > > > > Ticket Details > =================== > Ticket ID: YRL-141737 > Department: Support netCDF > Priority: Normal > Status: Open > Link: https://www.unidata.ucar.edu/esupport/staff/index.php?_m=tickets&_a=viewticket&ticketid=7539 Ticket Details =================== Ticket ID: YRL-141737 Department: Support netCDF Priority: Normal Status: Open