Actual source code: optionsyaml.c

petsc-main 2021-04-20
Report Typos and Errors
  1: #define PETSC_DESIRE_FEATURE_TEST_MACROS /* for strdup() */
  2: #include <petsc/private/petscimpl.h>

  4: #if defined(PETSC_HAVE_YAML)
  5: #include <yaml.h>  /* use external LibYAML */
  6: #else
  7: #include <../src/sys/yaml/include/yaml.h>
  8: #endif

 10: static MPI_Comm petsc_yaml_comm = MPI_COMM_NULL; /* only used for parallel error handling */

 12: PETSC_STATIC_INLINE MPI_Comm PetscYAMLGetComm(void)
 13: {
 14:   return PetscLikely(petsc_yaml_comm != MPI_COMM_NULL) ? petsc_yaml_comm : (petsc_yaml_comm = PETSC_COMM_SELF);
 15: }

 17: PETSC_STATIC_INLINE MPI_Comm PetscYAMLSetComm(MPI_Comm comm)
 18: {
 19:   MPI_Comm prev = PetscYAMLGetComm(); petsc_yaml_comm = comm; return prev;
 20: }

 22: #define TAG(node) ((const char *)((node)->tag))
 23: #define STR(node) ((const char *)((node)->data.scalar.value))
 24: #define SEQ(node) ((node)->data.sequence.items)
 25: #define MAP(node) ((node)->data.mapping.pairs)

 27: static PetscErrorCode PetscParseLayerYAML(PetscOptions options, yaml_document_t *doc, yaml_node_t *node)
 28: {
 29:   MPI_Comm         comm = PetscYAMLGetComm();
 30:   char             name[PETSC_MAX_OPTION_NAME] = "", prefix[PETSC_MAX_OPTION_NAME] = "";
 31:   PetscErrorCode   ierr;

 34:   if (node->type == YAML_SCALAR_NODE && !STR(node)[0]) return(0); /* empty */
 35:   if (node->type != YAML_MAPPING_NODE) SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected mapping");
 36:   for (yaml_node_pair_t *pair = MAP(node).start; pair < MAP(node).top; pair++) {
 37:     yaml_node_t *keynode = yaml_document_get_node(doc, pair->key);
 38:     yaml_node_t *valnode = yaml_document_get_node(doc, pair->value);
 39:     PetscBool   isMergeKey,isDummyKey,isIncludeTag;

 41:     if (!keynode) SETERRQ(comm, PETSC_ERR_LIB, "Corrupt YAML document");
 42:     if (!valnode) SETERRQ(comm, PETSC_ERR_LIB, "Corrupt YAML document");
 43:     if (keynode->type != YAML_SCALAR_NODE) SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected scalar");

 45:     /* "<<" is the merge key: don't increment the prefix */
 46:     PetscStrcmp(STR(keynode), "<<", &isMergeKey);
 47:     if (isMergeKey) {
 48:       if (valnode->type == YAML_SEQUENCE_NODE) {
 49:         for (yaml_node_item_t *item = SEQ(valnode).start; item < SEQ(valnode).top; item++) {
 50:           yaml_node_t *itemnode = yaml_document_get_node(doc, *item);
 51:           if (!itemnode) SETERRQ(comm, PETSC_ERR_LIB, "Corrupt YAML document");
 52:           if (itemnode->type != YAML_MAPPING_NODE) SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected mapping");
 53:           PetscParseLayerYAML(options, doc, itemnode);
 54:         }
 55:       } else if (valnode->type == YAML_MAPPING_NODE) {
 56:         PetscParseLayerYAML(options, doc, valnode);
 57:       } else SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected sequence or mapping");
 58:       continue; /* to next pair */
 59:     }

 61:     /* "$$*" are treated as dummy keys, we use them for !include tags and to define anchors */
 62:     PetscStrbeginswith(STR(keynode), "$$", &isDummyKey);
 63:     if (isDummyKey) {
 64:       PetscStrendswith(TAG(valnode), "!include", &isIncludeTag);
 65:       if (isIncludeTag) { /* TODO: add proper support relative paths */
 66:         PetscOptionsInsertFileYAML(comm, options, STR(valnode), PETSC_TRUE);
 67:       }
 68:       continue; /* to next pair */
 69:     }

 71:     if (valnode->type == YAML_SCALAR_NODE) {
 72:       PetscSNPrintf(name, sizeof(name), "-%s", STR(keynode));
 73:       PetscOptionsSetValue(options, name, STR(valnode));

 75:     } else if (valnode->type == YAML_SEQUENCE_NODE) {
 76:       PetscSegBuffer seg;
 77:       char           *buf, *strlist;
 78:       PetscBool      addSep = PETSC_FALSE;

 80:       PetscSegBufferCreate(sizeof(char), PETSC_MAX_PATH_LEN, &seg);
 81:       for (yaml_node_item_t *item = SEQ(valnode).start; item < SEQ(valnode).top; item++) {
 82:         yaml_node_t *itemnode = yaml_document_get_node(doc, *item);
 83:         const char  *itemstr = NULL;
 84:         size_t       itemlen;

 86:         if (!itemnode) SETERRQ(comm, PETSC_ERR_LIB, "Corrupt YAML document");

 88:         if (itemnode->type == YAML_SCALAR_NODE) {
 89:           itemstr = STR(itemnode);

 91:         } else if (itemnode->type == YAML_MAPPING_NODE) {
 92:           yaml_node_pair_t *kvn = itemnode->data.mapping.pairs.start;
 93:           yaml_node_pair_t *top = itemnode->data.mapping.pairs.top;

 95:           if (top - kvn > 1) SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node value: expected a single key:value pair");
 96:           if (top - kvn > 0) {
 97:             yaml_node_t *kn = yaml_document_get_node(doc, kvn->key);
 98:             yaml_node_t *vn = yaml_document_get_node(doc, kvn->value);

100:             if (!kn) SETERRQ(comm, PETSC_ERR_LIB, "Corrupt YAML document");
101:             if (!vn) SETERRQ(comm, PETSC_ERR_LIB, "Corrupt YAML document");
102:             if (kn->type != YAML_SCALAR_NODE) SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected scalar");

104:             PetscStrcmp(STR(kn), "<<", &isMergeKey);
105:             if (isMergeKey) SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node value: merge key '<<' not supported here");

107:             PetscStrbeginswith(STR(kn), "$$", &isDummyKey);
108:             if (isDummyKey) continue;
109:             itemstr = STR(kn);
110:           }

112:           PetscSNPrintf(prefix,sizeof(prefix), "%s_", STR(keynode));
113:           PetscOptionsPrefixPush(options, prefix);
114:           PetscParseLayerYAML(options, doc, itemnode);
115:           PetscOptionsPrefixPop(options);

117:         } else SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected scalar or mapping");

119:         PetscStrlen(itemstr, &itemlen);
120:         if (itemlen) {
121:           if (addSep) {
122:             PetscSegBufferGet(seg, 1, &buf);
123:             PetscArraycpy(buf, ",", 1);
124:           }
125:           PetscSegBufferGet(seg, itemlen, &buf);
126:           PetscArraycpy(buf, itemstr, itemlen);
127:           addSep = PETSC_TRUE;
128:         }
129:       }
130:       PetscSegBufferGet(seg, 1, &buf);
131:       PetscArrayzero(buf, 1);
132:       PetscSegBufferExtractAlloc(seg, &strlist);
133:       PetscSegBufferDestroy(&seg);

135:       PetscSNPrintf(name, sizeof(name), "-%s", STR(keynode));
136:       PetscOptionsSetValue(options, name, strlist);
137:       PetscFree(strlist);

139:     } else if (valnode->type == YAML_MAPPING_NODE) {
140:       PetscSNPrintf(prefix,sizeof(prefix), "%s_", STR(keynode));
141:       PetscOptionsPrefixPush(options, prefix);
142:       PetscParseLayerYAML(options, doc, valnode);
143:       PetscOptionsPrefixPop(options);

145:     } else SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected scalar, sequence or mapping");
146:   }
147:   return(0);
148: }

150: /*@C
151:    PetscOptionsInsertStringYAML - Inserts YAML-formatted options into the database from a string

153:    Logically Collective

155:    Input Parameter:
156: +  options - options database, use NULL for default global database
157: -  in_str - YAML-formatted string options

159:    Level: intermediate

161: .seealso: PetscOptionsSetValue(), PetscOptionsView(), PetscOptionsHasName(), PetscOptionsGetInt(),
162:           PetscOptionsGetReal(), PetscOptionsGetString(), PetscOptionsGetIntArray(), PetscOptionsBool(),
163:           PetscOptionsName(), PetscOptionsBegin(), PetscOptionsEnd(), PetscOptionsHead(),
164:           PetscOptionsStringArray(),PetscOptionsRealArray(), PetscOptionsScalar(),
165:           PetscOptionsBoolGroupBegin(), PetscOptionsBoolGroup(), PetscOptionsBoolGroupEnd(),
166:           PetscOptionsFList(), PetscOptionsEList(), PetscOptionsInsertFile(), PetscOptionsInsertFileYAML()
167: @*/
168: PetscErrorCode PetscOptionsInsertStringYAML(PetscOptions options,const char in_str[])
169: {
170:   MPI_Comm        comm = PetscYAMLGetComm();
171:   yaml_parser_t   parser;
172:   yaml_document_t doc;
173:   yaml_node_t     *root;
174:   PetscErrorCode  ierr;

177:   if (!in_str) in_str = "";
178:   !yaml_parser_initialize(&parser); if (ierr) SETERRQ(comm, PETSC_ERR_LIB, "YAML parser initialization error");
179:   yaml_parser_set_input_string(&parser, (const unsigned char *)in_str, strlen(in_str));
180:   do {
181:     !yaml_parser_load(&parser, &doc); if (ierr) SETERRQ(comm, PETSC_ERR_LIB, "YAML parser loading error");
182:     root = yaml_document_get_root_node(&doc);
183:     if (root) {
184:       PetscParseLayerYAML(options, &doc, root);
185:     }
186:     yaml_document_delete(&doc);
187:   } while (root);
188:   yaml_parser_delete(&parser);
189:   return(0);
190: }

192: /*@C
193:   PetscOptionsInsertFileYAML - Insert a YAML-formatted file in the options database

195:   Collective

197:   Input Parameter:
198: +   comm - the processes that will share the options (usually PETSC_COMM_WORLD)
199: .   options - options database, use NULL for default global database
200: .   file - name of file
201: -   require - if PETSC_TRUE will generate an error if the file does not exist

203:   PETSc will generate an error condition that stops the program if a YAML error
204:   is detected, hence the user should check that the YAML file is valid before
205:   supplying it, for instance at http://www.yamllint.com/ .

207:   Uses PetscOptionsInsertStringYAML().

209:   Level: intermediate

211: .seealso: PetscOptionsSetValue(), PetscOptionsView(), PetscOptionsHasName(), PetscOptionsGetInt(),
212:           PetscOptionsGetReal(), PetscOptionsGetString(), PetscOptionsGetIntArray(), PetscOptionsBool(),
213:           PetscOptionsName(), PetscOptionsBegin(), PetscOptionsEnd(), PetscOptionsHead(),
214:           PetscOptionsStringArray(),PetscOptionsRealArray(), PetscOptionsScalar(),
215:           PetscOptionsBoolGroupBegin(), PetscOptionsBoolGroup(), PetscOptionsBoolGroupEnd(),
216:           PetscOptionsFList(), PetscOptionsEList(), PetscOptionsInsertFile(), PetscOptionsInsertStringYAML()
217: @*/
218: PetscErrorCode PetscOptionsInsertFileYAML(MPI_Comm comm,PetscOptions options,const char file[],PetscBool require)
219: {
220:   int            yamlLength = -1;
221:   char          *yamlString = NULL;
222:   MPI_Comm       prev;
223:   PetscMPIInt    rank;

227:   MPI_Comm_rank(comm, &rank);
228:   if (!rank) {
229:     char   fpath[PETSC_MAX_PATH_LEN];
230:     char   fname[PETSC_MAX_PATH_LEN];
231:     FILE  *fd;
232:     size_t rd;

234:     PetscStrreplace(PETSC_COMM_SELF, file, fpath, sizeof(fpath));
235:     PetscFixFilename(fpath, fname);

237:     fd = fopen(fname, "r");
238:     if (fd) {
239:       fseek(fd, 0, SEEK_END);
240:       yamlLength = (int)ftell(fd);
241:       fseek(fd, 0, SEEK_SET);
242:       if (yamlLength < 0) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to query size of YAML file: %s", fname);
243:       PetscMalloc1(yamlLength+1, &yamlString);
244:       rd = fread(yamlString, 1, (size_t)yamlLength, fd);
245:       if (rd != (size_t)yamlLength) SETERRQ1(PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Unable to read entire YAML file: %s", fname);
246:       yamlString[yamlLength] = 0;
247:       fclose(fd);
248:     }
249:   }

251:   MPI_Bcast(&yamlLength, 1, MPI_INT, 0, comm);
252:   if (require && yamlLength < 0) SETERRQ1(comm, PETSC_ERR_FILE_OPEN, "Unable to open YAML option file: %s\n", file);
253:   if (yamlLength < 0) return(0);

255:   if (rank) {PetscMalloc1(yamlLength+1, &yamlString);}
256:   MPI_Bcast(yamlString, yamlLength+1, MPI_CHAR, 0, comm);

258:   prev = PetscYAMLSetComm(comm);
259:   PetscOptionsInsertStringYAML(options, yamlString);
260:   (void) PetscYAMLSetComm(prev);

262:   PetscFree(yamlString);
263:   return(0);
264: }


267: #if !defined(PETSC_HAVE_YAML)

269: /*
270: #if !defined(PETSC_HAVE_STRDUP)
271: #define strdup(s) (char*)memcpy(malloc(strlen(s)+1),s,strlen(s)+1)
272: #endif
273: */

275: /* Embed LibYAML in this compilation unit */
276: #include <../src/sys/yaml/src/api.c>
277: #include <../src/sys/yaml/src/loader.c>
278: #include <../src/sys/yaml/src/parser.c>
279: #include <../src/sys/yaml/src/reader.c>
280: #include <../src/sys/yaml/src/scanner.c>

282: /* Silence a few unused-function warnings */
283: static PETSC_UNUSED void petsc_yaml_unused(void)
284: {
285:   (void)yaml_parser_scan;
286:   (void)yaml_document_get_node;
287:   (void)yaml_parser_set_encoding;
288:   (void)yaml_parser_set_input;
289:   (void)yaml_parser_set_input_file;
290: }

292: #endif