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: PETSC_INTERN PetscErrorCode PetscOptionsSetValue_Private(PetscOptions, const char[], const char[], int *, PetscOptionSource);
11: PETSC_INTERN PetscErrorCode PetscOptionsInsertStringYAML_Private(PetscOptions, const char[], PetscOptionSource);
13: static MPI_Comm petsc_yaml_comm = MPI_COMM_NULL; /* only used for parallel error handling */
15: static inline MPI_Comm PetscYAMLGetComm(void)
16: {
17: return PetscLikely(petsc_yaml_comm != MPI_COMM_NULL) ? petsc_yaml_comm : (petsc_yaml_comm = PETSC_COMM_SELF);
18: }
20: static inline MPI_Comm PetscYAMLSetComm(MPI_Comm comm)
21: {
22: MPI_Comm prev = PetscYAMLGetComm();
23: petsc_yaml_comm = comm;
24: return prev;
25: }
27: #define TAG(node) ((const char *)((node)->tag))
28: #define STR(node) ((const char *)((node)->data.scalar.value))
29: #define SEQ(node) ((node)->data.sequence.items)
30: #define MAP(node) ((node)->data.mapping.pairs)
32: static PetscErrorCode PetscParseLayerYAML(PetscOptions options, yaml_document_t *doc, yaml_node_t *node, PetscOptionSource source)
33: {
34: MPI_Comm comm = PetscYAMLGetComm();
35: char name[PETSC_MAX_OPTION_NAME] = "", prefix[PETSC_MAX_OPTION_NAME] = "";
37: PetscFunctionBegin;
38: if (node->type == YAML_SCALAR_NODE && !STR(node)[0]) PetscFunctionReturn(PETSC_SUCCESS); /* empty */
39: PetscCheck(node->type == YAML_MAPPING_NODE, comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected mapping");
40: for (yaml_node_pair_t *pair = MAP(node).start; pair < MAP(node).top; pair++) {
41: yaml_node_t *keynode = yaml_document_get_node(doc, pair->key);
42: yaml_node_t *valnode = yaml_document_get_node(doc, pair->value);
43: PetscBool isMergeKey, isDummyKey, isIncludeTag;
45: PetscCheck(keynode, comm, PETSC_ERR_LIB, "Corrupt YAML document");
46: PetscCheck(valnode, comm, PETSC_ERR_LIB, "Corrupt YAML document");
47: PetscCheck(keynode->type == YAML_SCALAR_NODE, comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected scalar");
49: /* "<<" is the merge key: don't increment the prefix */
50: PetscCall(PetscStrcmp(STR(keynode), "<<", &isMergeKey));
51: if (isMergeKey) {
52: if (valnode->type == YAML_SEQUENCE_NODE) {
53: for (yaml_node_item_t *item = SEQ(valnode).start; item < SEQ(valnode).top; item++) {
54: yaml_node_t *itemnode = yaml_document_get_node(doc, *item);
55: PetscCheck(itemnode, comm, PETSC_ERR_LIB, "Corrupt YAML document");
56: PetscCheck(itemnode->type == YAML_MAPPING_NODE, comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected mapping");
57: PetscCall(PetscParseLayerYAML(options, doc, itemnode, source));
58: }
59: } else if (valnode->type == YAML_MAPPING_NODE) {
60: PetscCall(PetscParseLayerYAML(options, doc, valnode, source));
61: } else SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected sequence or mapping");
62: continue; /* to next pair */
63: }
65: /* "$$*" are treated as dummy keys, we use them for !include tags and to define anchors */
66: PetscCall(PetscStrbeginswith(STR(keynode), "$$", &isDummyKey));
67: if (isDummyKey) {
68: PetscCall(PetscStrendswith(TAG(valnode), "!include", &isIncludeTag));
69: if (isIncludeTag) { /* TODO: add proper support relative paths */
70: PetscCall(PetscOptionsInsertFileYAML(comm, options, STR(valnode), PETSC_TRUE));
71: }
72: continue; /* to next pair */
73: }
75: if (valnode->type == YAML_SCALAR_NODE) {
76: PetscCall(PetscSNPrintf(name, sizeof(name), "-%s", STR(keynode)));
77: PetscCall(PetscOptionsSetValue_Private(options, name, STR(valnode), NULL, source));
79: } else if (valnode->type == YAML_SEQUENCE_NODE) {
80: PetscSegBuffer seg;
81: char *buf, *strlist;
82: PetscBool addSep = PETSC_FALSE;
84: PetscCall(PetscSegBufferCreate(sizeof(char), PETSC_MAX_PATH_LEN, &seg));
85: for (yaml_node_item_t *item = SEQ(valnode).start; item < SEQ(valnode).top; item++) {
86: yaml_node_t *itemnode = yaml_document_get_node(doc, *item);
87: const char *itemstr = NULL;
88: size_t itemlen;
90: PetscCheck(itemnode, comm, PETSC_ERR_LIB, "Corrupt YAML document");
92: if (itemnode->type == YAML_SCALAR_NODE) {
93: itemstr = STR(itemnode);
95: } else if (itemnode->type == YAML_MAPPING_NODE) {
96: yaml_node_pair_t *kvn = itemnode->data.mapping.pairs.start;
97: yaml_node_pair_t *top = itemnode->data.mapping.pairs.top;
99: PetscCheck(top - kvn <= 1, comm, PETSC_ERR_SUP, "Unsupported YAML node value: expected a single key:value pair");
100: if (top - kvn > 0) {
101: yaml_node_t *kn = yaml_document_get_node(doc, kvn->key);
102: yaml_node_t *vn = yaml_document_get_node(doc, kvn->value);
104: PetscCheck(kn, comm, PETSC_ERR_LIB, "Corrupt YAML document");
105: PetscCheck(vn, comm, PETSC_ERR_LIB, "Corrupt YAML document");
106: PetscCheck(kn->type == YAML_SCALAR_NODE, comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected scalar");
108: PetscCall(PetscStrcmp(STR(kn), "<<", &isMergeKey));
109: PetscCheck(!isMergeKey, comm, PETSC_ERR_SUP, "Unsupported YAML node value: merge key '<<' not supported here");
111: PetscCall(PetscStrbeginswith(STR(kn), "$$", &isDummyKey));
112: if (isDummyKey) continue;
113: itemstr = STR(kn);
114: }
116: PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%s_", STR(keynode)));
117: PetscCall(PetscOptionsPrefixPush(options, prefix));
118: PetscCall(PetscParseLayerYAML(options, doc, itemnode, source));
119: PetscCall(PetscOptionsPrefixPop(options));
121: } else SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected scalar or mapping");
123: PetscCall(PetscStrlen(itemstr, &itemlen));
124: if (itemlen) {
125: if (addSep) {
126: PetscCall(PetscSegBufferGet(seg, 1, &buf));
127: PetscCall(PetscArraycpy(buf, ",", 1));
128: }
129: PetscCall(PetscSegBufferGet(seg, itemlen, &buf));
130: PetscCall(PetscArraycpy(buf, itemstr, itemlen));
131: addSep = PETSC_TRUE;
132: }
133: }
134: PetscCall(PetscSegBufferGet(seg, 1, &buf));
135: PetscCall(PetscArrayzero(buf, 1));
136: PetscCall(PetscSegBufferExtractAlloc(seg, &strlist));
137: PetscCall(PetscSegBufferDestroy(&seg));
139: PetscCall(PetscSNPrintf(name, sizeof(name), "-%s", STR(keynode)));
140: PetscCall(PetscOptionsSetValue_Private(options, name, strlist, NULL, source));
141: PetscCall(PetscFree(strlist));
143: } else if (valnode->type == YAML_MAPPING_NODE) {
144: PetscCall(PetscSNPrintf(prefix, sizeof(prefix), "%s_", STR(keynode)));
145: PetscCall(PetscOptionsPrefixPush(options, prefix));
146: PetscCall(PetscParseLayerYAML(options, doc, valnode, source));
147: PetscCall(PetscOptionsPrefixPop(options));
149: } else SETERRQ(comm, PETSC_ERR_SUP, "Unsupported YAML node type: expected scalar, sequence or mapping");
150: }
151: PetscFunctionReturn(PETSC_SUCCESS);
152: }
154: PetscErrorCode PetscOptionsInsertStringYAML_Private(PetscOptions options, const char in_str[], PetscOptionSource source)
155: {
156: MPI_Comm comm = PetscYAMLGetComm();
157: yaml_parser_t parser;
158: yaml_document_t doc;
159: yaml_node_t *root;
160: int err;
162: PetscFunctionBegin;
163: if (!in_str) in_str = "";
164: err = !yaml_parser_initialize(&parser);
165: PetscCheck(!err, comm, PETSC_ERR_LIB, "YAML parser initialization error");
166: yaml_parser_set_input_string(&parser, (const unsigned char *)in_str, strlen(in_str));
167: do {
168: err = !yaml_parser_load(&parser, &doc);
169: PetscCheck(!err, comm, PETSC_ERR_LIB, "YAML parser loading error");
170: root = yaml_document_get_root_node(&doc);
171: if (root) PetscCall(PetscParseLayerYAML(options, &doc, root, source));
172: yaml_document_delete(&doc);
173: } while (root);
174: yaml_parser_delete(&parser);
175: PetscFunctionReturn(PETSC_SUCCESS);
176: }
177: /*@C
178: PetscOptionsInsertStringYAML - Inserts YAML-formatted options into the options database from a string
180: Logically Collective
182: Input Parameters:
183: + options - options database, use `NULL` for default global database
184: - in_str - YAML-formatted string options
186: Level: intermediate
188: .seealso: `PetscOptionsSetValue()`, `PetscOptionsView()`, `PetscOptionsHasName()`, `PetscOptionsGetInt()`,
189: `PetscOptionsGetReal()`, `PetscOptionsGetString()`, `PetscOptionsGetIntArray()`, `PetscOptionsBool()`,
190: `PetscOptionsName()`, `PetscOptionsBegin()`, `PetscOptionsEnd()`, `PetscOptionsHeadBegin()`,
191: `PetscOptionsStringArray()`, `PetscOptionsRealArray()`, `PetscOptionsScalar()`,
192: `PetscOptionsBoolGroupBegin()`, `PetscOptionsBoolGroup()`, `PetscOptionsBoolGroupEnd()`,
193: `PetscOptionsFList()`, `PetscOptionsEList()`, `PetscOptionsInsertFile()`, `PetscOptionsInsertFileYAML()`
194: @*/
195: PetscErrorCode PetscOptionsInsertStringYAML(PetscOptions options, const char in_str[])
196: {
197: PetscFunctionBegin;
198: PetscCall(PetscOptionsInsertStringYAML_Private(options, in_str, PETSC_OPT_CODE));
199: PetscFunctionReturn(PETSC_SUCCESS);
200: }
202: /*@C
203: PetscOptionsInsertFileYAML - Insert a YAML-formatted file in the options database
205: Collective
207: Input Parameters:
208: + comm - the processes that will share the options (usually `PETSC_COMM_WORLD`)
209: . options - options database, use `NULL` for default global database
210: . file - name of file
211: - require - if `PETSC_TRUE` will generate an error if the file does not exist
213: Level: intermediate
215: Notes:
216: PETSc will generate an error condition that stops the program if a YAML error
217: is detected, hence the user should check that the YAML file is valid before
218: supplying it, for instance at <http://www.yamllint.com> .
220: Uses `PetscOptionsInsertStringYAML()`.
222: .seealso: `PetscOptionsSetValue()`, `PetscOptionsView()`, `PetscOptionsHasName()`, `PetscOptionsGetInt()`,
223: `PetscOptionsGetReal()`, `PetscOptionsGetString()`, `PetscOptionsGetIntArray()`, `PetscOptionsBool()`,
224: `PetscOptionsName()`, `PetscOptionsBegin()`, `PetscOptionsEnd()`, `PetscOptionsHeadBegin()`,
225: `PetscOptionsStringArray()`, `PetscOptionsRealArray()`, `PetscOptionsScalar()`,
226: `PetscOptionsBoolGroupBegin()`, `PetscOptionsBoolGroup()`, `PetscOptionsBoolGroupEnd()`,
227: `PetscOptionsFList()`, `PetscOptionsEList()`, `PetscOptionsInsertFile()`, `PetscOptionsInsertStringYAML()`
228: @*/
229: PetscErrorCode PetscOptionsInsertFileYAML(MPI_Comm comm, PetscOptions options, const char file[], PetscBool require)
230: {
231: int yamlLength = -1;
232: char *yamlString = NULL;
233: MPI_Comm prev;
234: PetscMPIInt rank;
236: PetscFunctionBegin;
237: PetscCallMPI(MPI_Comm_rank(comm, &rank));
238: if (rank == 0) {
239: char fpath[PETSC_MAX_PATH_LEN];
240: char fname[PETSC_MAX_PATH_LEN];
241: FILE *fd;
242: size_t rd;
244: PetscCall(PetscStrreplace(PETSC_COMM_SELF, file, fpath, sizeof(fpath)));
245: PetscCall(PetscFixFilename(fpath, fname));
247: fd = fopen(fname, "r");
248: if (fd) {
249: fseek(fd, 0, SEEK_END);
250: yamlLength = (int)ftell(fd);
251: fseek(fd, 0, SEEK_SET);
252: PetscCheck(yamlLength >= 0, PETSC_COMM_SELF, PETSC_ERR_FILE_OPEN, "Unable to query size of YAML file: %s", fname);
253: PetscCall(PetscMalloc1(yamlLength + 1, &yamlString));
254: rd = fread(yamlString, 1, (size_t)yamlLength, fd);
255: PetscCheck(rd == (size_t)yamlLength, PETSC_COMM_SELF, PETSC_ERR_FILE_READ, "Unable to read entire YAML file: %s", fname);
256: yamlString[yamlLength] = 0;
257: fclose(fd);
258: }
259: }
261: PetscCallMPI(MPI_Bcast(&yamlLength, 1, MPI_INT, 0, comm));
262: PetscCheck(!require || yamlLength >= 0, comm, PETSC_ERR_FILE_OPEN, "Unable to open YAML option file: %s", file);
263: if (yamlLength < 0) PetscFunctionReturn(PETSC_SUCCESS);
265: if (rank) PetscCall(PetscMalloc1(yamlLength + 1, &yamlString));
266: PetscCallMPI(MPI_Bcast(yamlString, yamlLength + 1, MPI_CHAR, 0, comm));
268: prev = PetscYAMLSetComm(comm);
269: PetscCall(PetscOptionsInsertStringYAML_Private(options, yamlString, PETSC_OPT_FILE));
270: (void)PetscYAMLSetComm(prev);
272: PetscCall(PetscFree(yamlString));
273: PetscFunctionReturn(PETSC_SUCCESS);
274: }
276: #if !defined(PETSC_HAVE_YAML)
278: /*
279: #if !defined(PETSC_HAVE_STRDUP)
280: #define strdup(s) (char*)memcpy(malloc(strlen(s)+1),s,strlen(s)+1)
281: #endif
282: */
284: /* Embed LibYAML in this compilation unit */
285: #include <../src/sys/yaml/src/api.c>
286: #include <../src/sys/yaml/src/loader.c>
287: #include <../src/sys/yaml/src/parser.c>
288: #include <../src/sys/yaml/src/reader.c>
290: /*
291: Avoid compiler warnings like
292: scanner.c, line 3181: warning: integer conversion resulted in a change of sign
293: *(string.pointer++) = '\xC2';
295: Once yaml fixes them, we can remove the pragmas
296: */
297: #pragma GCC diagnostic push
298: #pragma GCC diagnostic ignored "-Wsign-conversion"
299: #include <../src/sys/yaml/src/scanner.c>
300: #pragma GCC diagnostic pop
302: /* Silence a few unused-function warnings */
303: static PETSC_UNUSED void petsc_yaml_unused(void)
304: {
305: (void)yaml_parser_scan;
306: (void)yaml_document_get_node;
307: (void)yaml_parser_set_encoding;
308: (void)yaml_parser_set_input;
309: (void)yaml_parser_set_input_file;
310: }
312: #endif