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(); 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] = "";

 32:   if (node->type == YAML_SCALAR_NODE && !STR(node)[0]) return 0; /* empty */
 34:   for (yaml_node_pair_t *pair = MAP(node).start; pair < MAP(node).top; pair++) {
 35:     yaml_node_t *keynode = yaml_document_get_node(doc, pair->key);
 36:     yaml_node_t *valnode = yaml_document_get_node(doc, pair->value);
 37:     PetscBool   isMergeKey,isDummyKey,isIncludeTag;


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

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

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

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

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


 86:         if (itemnode->type == YAML_SCALAR_NODE) {
 87:           itemstr = STR(itemnode);

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

 94:           if (top - kvn > 0) {
 95:             yaml_node_t *kn = yaml_document_get_node(doc, kvn->key);
 96:             yaml_node_t *vn = yaml_document_get_node(doc, kvn->value);


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

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

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

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

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

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

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

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

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

151:    Logically Collective

153:    Input Parameters:
154: +  options - options database, use NULL for default global database
155: -  in_str - YAML-formatted string options

157:    Level: intermediate

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

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

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

192:   Collective

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

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

204:   Uses PetscOptionsInsertStringYAML().

206:   Level: intermediate

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

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

229:     PetscStrreplace(PETSC_COMM_SELF, file, fpath, sizeof(fpath));
230:     PetscFixFilename(fpath, fname);

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

246:   MPI_Bcast(&yamlLength, 1, MPI_INT, 0, comm);
248:   if (yamlLength < 0) return 0;

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

253:   prev = PetscYAMLSetComm(comm);
254:   PetscOptionsInsertStringYAML(options, yamlString);
255:   (void) PetscYAMLSetComm(prev);

257:   PetscFree(yamlString);
258:   return 0;
259: }

261: #if !defined(PETSC_HAVE_YAML)

263: /*
264: #if !defined(PETSC_HAVE_STRDUP)
265: #define strdup(s) (char*)memcpy(malloc(strlen(s)+1),s,strlen(s)+1)
266: #endif
267: */

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

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

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

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

297: #endif