Actual source code: optionsyaml.c

  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: 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: static inline MPI_Comm PetscYAMLSetComm(MPI_Comm comm)
 18: {
 19:   MPI_Comm prev   = PetscYAMLGetComm();
 20:   petsc_yaml_comm = comm;
 21:   return prev;
 22: }

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

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

 34:   if (node->type == YAML_SCALAR_NODE && !STR(node)[0]) return 0; /* empty */
 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;


 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);
 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;


 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;

 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);


104:             PetscStrcmp(STR(kn), "<<", &isMergeKey);

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 options database from a string

153:    Logically Collective

155:    Input Parameters:
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()`, `PetscOptionsHeadBegin()`,
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:   int             err;

176:   if (!in_str) in_str = "";
177:   err = !yaml_parser_initialize(&parser);
179:   yaml_parser_set_input_string(&parser, (const unsigned char *)in_str, strlen(in_str));
180:   do {
181:     err = !yaml_parser_load(&parser, &doc);
183:     root = yaml_document_get_root_node(&doc);
184:     if (root) PetscParseLayerYAML(options, &doc, root);
185:     yaml_document_delete(&doc);
186:   } while (root);
187:   yaml_parser_delete(&parser);
188:   return 0;
189: }

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

194:   Collective

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

202:   Notes:
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()`, `PetscOptionsHeadBegin()`,
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;

225:   MPI_Comm_rank(comm, &rank);
226:   if (rank == 0) {
227:     char   fpath[PETSC_MAX_PATH_LEN];
228:     char   fname[PETSC_MAX_PATH_LEN];
229:     FILE  *fd;
230:     size_t rd;

232:     PetscStrreplace(PETSC_COMM_SELF, file, fpath, sizeof(fpath));
233:     PetscFixFilename(fpath, fname);

235:     fd = fopen(fname, "r");
236:     if (fd) {
237:       fseek(fd, 0, SEEK_END);
238:       yamlLength = (int)ftell(fd);
239:       fseek(fd, 0, SEEK_SET);
241:       PetscMalloc1(yamlLength + 1, &yamlString);
242:       rd = fread(yamlString, 1, (size_t)yamlLength, fd);
244:       yamlString[yamlLength] = 0;
245:       fclose(fd);
246:     }
247:   }

249:   MPI_Bcast(&yamlLength, 1, MPI_INT, 0, comm);
251:   if (yamlLength < 0) return 0;

253:   if (rank) PetscMalloc1(yamlLength + 1, &yamlString);
254:   MPI_Bcast(yamlString, yamlLength + 1, MPI_CHAR, 0, comm);

256:   prev = PetscYAMLSetComm(comm);
257:   PetscOptionsInsertStringYAML(options, yamlString);
258:   (void)PetscYAMLSetComm(prev);

260:   PetscFree(yamlString);
261:   return 0;
262: }

264: #if !defined(PETSC_HAVE_YAML)

266:   /*
267: #if !defined(PETSC_HAVE_STRDUP)
268: #define strdup(s) (char*)memcpy(malloc(strlen(s)+1),s,strlen(s)+1)
269: #endif
270: */

272:   /* Embed LibYAML in this compilation unit */
273: #include <../src/sys/yaml/src/api.c>
274: #include <../src/sys/yaml/src/loader.c>
275: #include <../src/sys/yaml/src/parser.c>
276: #include <../src/sys/yaml/src/reader.c>

278:   /*
279:   Avoid compiler warnings like
280:     scanner.c, line 3181: warning: integer conversion resulted in a change of sign
281:                           *(string.pointer++) = '\xC2';

283:   Once yaml fixes them, we can remove the pragmas
284: */
285:   #pragma GCC diagnostic push
286:   #pragma GCC diagnostic ignored "-Wsign-conversion"
287: #include <../src/sys/yaml/src/scanner.c>
288:   #pragma GCC diagnostic pop

290: /* Silence a few unused-function warnings */
291: static PETSC_UNUSED void petsc_yaml_unused(void)
292: {
293:   (void)yaml_parser_scan;
294:   (void)yaml_document_get_node;
295:   (void)yaml_parser_set_encoding;
296:   (void)yaml_parser_set_input;
297:   (void)yaml_parser_set_input_file;
298: }

300: #endif