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