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: 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 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(), 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 Parameters:
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 == 0) {
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: }
266: #if !defined(PETSC_HAVE_YAML)
268: /*
269: #if !defined(PETSC_HAVE_STRDUP)
270: #define strdup(s) (char*)memcpy(malloc(strlen(s)+1),s,strlen(s)+1)
271: #endif
272: */
274: /* Embed LibYAML in this compilation unit */
275: #include <../src/sys/yaml/src/api.c>
276: #include <../src/sys/yaml/src/loader.c>
277: #include <../src/sys/yaml/src/parser.c>
278: #include <../src/sys/yaml/src/reader.c>
279: #include <../src/sys/yaml/src/scanner.c>
281: /* Silence a few unused-function warnings */
282: static PETSC_UNUSED void petsc_yaml_unused(void)
283: {
284: (void)yaml_parser_scan;
285: (void)yaml_document_get_node;
286: (void)yaml_parser_set_encoding;
287: (void)yaml_parser_set_input;
288: (void)yaml_parser_set_input_file;
289: }
291: #endif