rapidyaml 0.15.1
parse and emit YAML, and do it fast
Loading...
Searching...
No Matches
node_type.cpp
Go to the documentation of this file.
1#ifndef _C4_YML_NODE_TYPE_HPP_
3#endif
4#ifndef _C4_YML_ERROR_HPP_
5#include "c4/yml/error.hpp"
6#endif
7
8
9namespace c4 {
10namespace yml {
11
12const char* NodeType::type_str(NodeType_e ty) noexcept
13{
14 switch(ty & _TYMASK)
15 {
16 case KEYVAL:
17 return "KEYVAL";
18 case KEY:
19 return "KEY";
20 case VAL:
21 return "VAL";
22 case MAP:
23 return "MAP";
24 case SEQ:
25 return "SEQ";
26 case KEYMAP:
27 return "KEYMAP";
28 case KEYSEQ:
29 return "KEYSEQ";
30 case DOCSEQ:
31 return "DOCSEQ";
32 case DOCMAP:
33 return "DOCMAP";
34 case DOCVAL:
35 return "DOCVAL";
36 case DOC:
37 return "DOC";
38 case STREAM:
39 return "STREAM";
40 case NOTYPE:
41 return "NOTYPE";
42 default:
43 if((ty & KEYVAL) == KEYVAL)
44 return "KEYVAL***";
45 if((ty & KEYMAP) == KEYMAP)
46 return "KEYMAP***";
47 if((ty & KEYSEQ) == KEYSEQ)
48 return "KEYSEQ***";
49 if((ty & DOCSEQ) == DOCSEQ)
50 return "DOCSEQ***";
51 if((ty & DOCMAP) == DOCMAP)
52 return "DOCMAP***";
53 if((ty & DOCVAL) == DOCVAL)
54 return "DOCVAL***";
55 if(ty & KEY)
56 return "KEY***";
57 if(ty & VAL)
58 return "VAL***";
59 if(ty & MAP)
60 return "MAP***";
61 if(ty & SEQ)
62 return "SEQ***";
63 if(ty & DOC)
64 return "DOC***";
65 return "(unk)";
66 }
67}
68
69size_t NodeType::type_str(substr buf, NodeType_e flags) noexcept
70{
71 size_t pos = 0;
72 bool gotone = false;
73
74 #define _prflag(fl, txt) \
75 do { \
76 if((flags & (fl)) == (fl)) \
77 { \
78 if(gotone) \
79 { \
80 if(pos + 1 < buf.len) \
81 buf[pos] = '|'; \
82 ++pos; \
83 } \
84 csubstr fltxt = txt; \
85 if(pos + fltxt.len <= buf.len) \
86 memcpy(buf.str + pos, fltxt.str, fltxt.len); \
87 pos += fltxt.len; \
88 gotone = true; \
89 flags = (flags & ~(fl)); /*remove the flag*/ \
90 } \
91 } while(0)
92
93 _prflag(STREAM, "STREAM");
94 _prflag(DOC, "DOC");
95 // key properties
96 _prflag(KEY, "KEY");
97 _prflag(KEYNIL, "KNIL");
98 _prflag(KEYTAG, "KTAG");
99 _prflag(KEYANCH, "KANCH");
100 _prflag(KEYREF, "KREF");
101 _prflag(KEY_LITERAL, "KLITERAL");
102 _prflag(KEY_FOLDED, "KFOLDED");
103 _prflag(KEY_SQUO, "KSQUO");
104 _prflag(KEY_DQUO, "KDQUO");
105 _prflag(KEY_PLAIN, "KPLAIN");
106 _prflag(KEY_UNFILT, "KUNFILT");
107 // val properties
108 _prflag(VAL, "VAL");
109 _prflag(VALNIL, "VNIL");
110 _prflag(VALTAG, "VTAG");
111 _prflag(VALANCH, "VANCH");
112 _prflag(VALREF, "VREF");
113 _prflag(VAL_UNFILT, "VUNFILT");
114 _prflag(VAL_LITERAL, "VLITERAL");
115 _prflag(VAL_FOLDED, "VFOLDED");
116 _prflag(VAL_SQUO, "VSQUO");
117 _prflag(VAL_DQUO, "VDQUO");
118 _prflag(VAL_PLAIN, "VPLAIN");
119 _prflag(VAL_UNFILT, "VUNFILT");
120 // container properties
121 _prflag(MAP, "MAP");
122 _prflag(SEQ, "SEQ");
123 _prflag(FLOW_SL, "FLOWSL");
124 _prflag(FLOW_ML1, "FLOWML1");
125 _prflag(FLOW_MLN, "FLOWMLN");
126 _prflag(FLOW_SPC, "FLOWSPC");
127 _prflag(BLOCK, "BLCK");
128 if(pos == 0)
129 _prflag(NOTYPE, "NOTYPE");
130
131 #undef _prflag
132
133 return pos;
134}
135
136
137//-----------------------------------------------------------------------------
138
139// see https://www.yaml.info/learn/quote.html#noplain
141{
142 // cannot have leading whitespace after a newline
143 for(size_t i = 0; i < s.len; ++i)
144 {
145 if(s.str[i] == '\n' && i + 1 < s.len)
146 {
147 char next = s.str[i + 1];
148 if(next == ' ' || next == '\t')
149 return false;
150 }
151 }
152 return true;
153}
154
155namespace {
156bool _is_wsnl(char c) noexcept
157{
158 return c == ' ' || c == '\n' || c == '\t' || c == '\r';
159}
160bool _is_valid_bulk(csubstr s, size_t i)
161{
162 C4_ASSERT(i >= 1 && i+1 < s.len);
163 C4_ASSERT(s.str[i] == ':' || s.str[i] == '#');
164 switch(s.str[i])
165 {
166 case ':': return !_is_wsnl(s.str[i+1]);
167 case '#': return !_is_wsnl(s.str[i-1]);
168 }
169 C4_UNREACHABLE(); // LCOV_EXCL_LINE
170}
171} // namespace
172// see https://www.yaml.info/learn/quote.html#noplain
174{
175 if(!s.len)
176 return !s.str;
177 // first
178 switch(s.str[0])
179 {
180 case ' ': case '\n': case '\t': case '\r':
181 case '!': case '&': case '*': case ',':
182 case '"': case '\'': case '|': case '>':
183 case '{': case '}': case '[': case ']':
184 case '#': case '`': case '%': case '@':
185 return false;
186 case '-': case ':': case '?':
187 if(s.len == 1 || (s.str[1] == ' ' || s.str[1] == '\t'))
188 return false;
189 break;
190 }
191 // bulk
192 for(size_t i = 1; i + 1 < s.len; ++i)
193 {
194 switch(s.str[i])
195 {
196 case ',': case '{': case '}': case '[': case ']':
197 return false;
198 case ':': case '#':
199 if(!_is_valid_bulk(s, i))
200 return false;
201 break;
202 }
203 }
204 // last
205 if(s.len > 1)
206 {
207 switch(s.back())
208 {
209 case ' ': case '\n': case '\t': case '\r':
210 case ',':
211 case '{': case '}':
212 case '[': case ']':
213 case '#':
214 case ':':
215 return false;
216 }
217 }
218 return true;
219}
220
222{
223 if(!s.len)
224 return !s.str;
225 // first
226 switch(s.str[0])
227 {
228 case ' ': case '\n': case '\t': case '\r':
229 case '!': case '&': case '*': case ',':
230 case '"': case '\'': case '|': case '>':
231 case '{': case '}': case '[': case ']':
232 case '#': case '`': case '%': case '@':
233 return false;
234 case '-': case ':': case '?':
235 if (s.len == 1 || (s.str[1] == ' ' || s.str[1] == '\t'))
236 return false;
237 break;
238 }
239 // bulk
240 for(size_t i = 1; i + 1 < s.len; ++i)
241 {
242 switch(s.str[i])
243 {
244 case ':': case '#':
245 if(!_is_valid_bulk(s, i))
246 return false;
247 break;
248 }
249 }
250 // last
251 if(s.len > 1)
252 {
253 switch(s.back())
254 {
255 case ' ': case '\n': case '\t': case '\r':
256 case '#':
257 case ':':
258 return false;
259 }
260 }
261 return true;
262}
263
265{
266 if(s.len)
267 {
269 return SCALAR_PLAIN;
270 else if(scalar_style_query_squo(s))
271 return SCALAR_SQUO;
272 return SCALAR_DQUO;
273 }
274 return s.str ? SCALAR_SQUO : SCALAR_PLAIN;
275}
276
278{
279 if(s.len)
280 {
282 return SCALAR_PLAIN;
283 _RYML_ASSERT_BASIC(scalar_style_query_squo(s)
284 && "if this assertion fires, please submit an issue!");
285 return SCALAR_SQUO;
286 }
287 return s.str ? SCALAR_SQUO : SCALAR_PLAIN;
288}
289
290
291bool scalar_is_null(csubstr s) noexcept
292{
293 return s.str == nullptr ||
294 (s.len == 1 && (s.str[0] == '~')) ||
295 (s.len == 4 && ((0 == memcmp("null", s.str, 4))
296 || (0 == memcmp("Null", s.str, 4))
297 || (0 == memcmp("NULL", s.str, 4))));
298}
299
300
301//-----------------------------------------------------------------------------
302
303namespace {
304
305#define rest_is(c1, c2) ((s.str[1] == (c1)) && (s.str[2] == (c2)))
306bool is_inf_or_nan(csubstr s) noexcept
307{
308 _RYML_ASSERT_BASIC(!s.begins_with("-."));
309 _RYML_ASSERT_BASIC(!s.begins_with("+."));
310 _RYML_ASSERT_BASIC(!s.begins_with("."));
311 _RYML_ASSERT_BASIC(s.len == 3);
312 switch(s.str[0])
313 {
314 case 'i': return rest_is('n', 'f');
315 case 'I': return rest_is('n', 'f') || rest_is('N', 'F');
316 case 'n': return rest_is('a', 'n');
317 case 'N': return rest_is('a', 'n') || rest_is('A', 'N') || rest_is('a', 'N');
318 }
319 return false;
320}
321bool is_inf(csubstr s) noexcept
322{
323 _RYML_ASSERT_BASIC(!s.begins_with("-."));
324 _RYML_ASSERT_BASIC(!s.begins_with("+."));
325 _RYML_ASSERT_BASIC(!s.begins_with("."));
326 _RYML_ASSERT_BASIC(s.len == 3);
327 switch(s.str[0])
328 {
329 case 'i': return rest_is('n', 'f');
330 case 'I': return rest_is('n', 'f') || rest_is('N', 'F');
331 }
332 return false;
333}
334#undef rest_is
335
336bool json_is_plain_number(csubstr s) noexcept
337{
338 return s.is_number()
339 &&
340 (
341 // quote integral numbers if they have a leading 0
342 // https://github.com/biojppm/rapidyaml/issues/291
343 (!(s.len > 1 && s.begins_with('0')))
344 // do not quote reals with leading 0
345 // https://github.com/biojppm/rapidyaml/issues/313
346 || (s.find('.') != csubstr::npos)
347 );
348}
349bool json_is_special_scalar(csubstr s) noexcept
350{
351 if(s.len == 4)
352 return 0 == memcmp("true", s.str, 4)
353 || 0 == memcmp("null", s.str, 4)
354 || (s[0] == '.' && is_inf_or_nan(s.sub(1)));
355 else if(s.len == 5)
356 return 0 == memcmp("false", s.str, 5)
357 || ((s[0] == '-' || s[0] == '+') && s[1] == '.' && is_inf(s.sub(2)));
358 return false;
359}
360} // namespace
362{
363 // do not quote numbers or special scalars
364 return json_is_plain_number(s) || json_is_special_scalar(s) ? SCALAR_PLAIN : SCALAR_DQUO;
365}
366
367} // namespace yml
368} // namespace c4
Error utilities used by ryml.
bool scalar_style_query_plain_block(csubstr s) noexcept
query whether a scalar can be encoded using plain style while in block mode.
bool scalar_is_null(csubstr s) noexcept
YAML-sense query of nullity.
bool scalar_style_query_squo(csubstr s) noexcept
query whether a scalar can be encoded using single quotes.
NodeType_e scalar_style_choose_flow(csubstr s) noexcept
choose a YAML scalar style based on the scalar's contents, while in flow mode.
NodeType_e scalar_style_choose_block(csubstr s) noexcept
choose a YAML scalar style based on the scalar's contents, while in block mode.
bool scalar_style_query_plain_flow(csubstr s) noexcept
query whether a scalar can be encoded using plain style while in flow mode.
NodeType_e
a bit mask for marking node types and styles
Definition node_type.hpp:34
NodeType_e scalar_style_choose_json(csubstr s) noexcept
choose a json scalar style based on the scalar's contents
@ VALANCH
the val has an &anchor
Definition node_type.hpp:46
@ NOTYPE
no node type or style is set
Definition node_type.hpp:36
@ KEY_DQUO
mark key scalar as double quoted "
@ VALREF
a *reference: the val references an &anchor
Definition node_type.hpp:44
@ VALNIL
the val is null (eg {a : } results in a null val)
Definition node_type.hpp:50
@ MAP
a map: a parent of KEYVAL/KEYSEQ/KEYMAP nodes
Definition node_type.hpp:39
@ STREAM
a stream: a seq of docs
Definition node_type.hpp:42
@ KEY
the scalar to the left of : in a map's member
Definition node_type.hpp:37
@ FLOW_ML1
mark container with multi-line flow style, 1 element per line
Definition node_type.hpp:79
@ VAL_FOLDED
mark val scalar as multiline, block folded >
@ KEYTAG
the key has a tag
Definition node_type.hpp:47
@ SCALAR_SQUO
mask of KEY_SQUO|VAL_SQUO,
@ FLOW_SL
mark container with single-line flow style
Definition node_type.hpp:60
@ VAL_UNFILT
the val scalar was left unfiltered; the parser was set not to filter.
Definition node_type.hpp:56
@ VAL
a scalar: has a scalar (ie string) value, possibly empty. must be a leaf node, and cannot be MAP or S...
Definition node_type.hpp:38
@ KEYMAP
mask of KEY|MAP
@ VALTAG
the val has a tag
Definition node_type.hpp:48
@ KEYSEQ
mask of KEY|SEQ
@ _TYMASK
all the bits up to here
Definition node_type.hpp:51
@ FLOW_MLN
mark container with multi-line flow style, n elements per line, wrapped (as set by EmitOptions::max_c...
Definition node_type.hpp:94
@ SEQ
a seq: a parent of VAL/SEQ/MAP nodes
Definition node_type.hpp:40
@ SCALAR_DQUO
mask of KEY_DQUO|VAL_DQUO,
@ VAL_SQUO
mark val scalar as single quoted '
@ DOCSEQ
mask of DOC|SEQ
@ VAL_PLAIN
mark val scalar as plain scalar (unquoted, even when multiline)
@ KEYREF
a *reference: the key references an &anchor
Definition node_type.hpp:43
@ DOCMAP
mask of DOC|MAP
@ BLOCK
mark container with block style
@ FLOW_SPC
mark container with spaces after comma when in flow mode. Applies to both FLOW_SL and FLOW_MLN (but n...
@ DOCVAL
mask of DOC|VAL
@ KEYVAL
mask of KEY|VAL
@ KEYANCH
the key has an &anchor
Definition node_type.hpp:45
@ VAL_DQUO
mark val scalar as double quoted "
@ KEY_UNFILT
the key scalar was left unfiltered; the parser was set not to filter.
Definition node_type.hpp:55
@ KEY_SQUO
mark key scalar as single quoted '
@ VAL_LITERAL
mark val scalar as multiline, block literal |
@ KEY_LITERAL
mark key scalar as multiline, block literal |
@ KEY_PLAIN
mark key scalar as plain scalar (unquoted, even when multiline)
@ SCALAR_PLAIN
mask of KEY_PLAIN|VAL_PLAIN,
@ KEY_FOLDED
mark key scalar as multiline, block folded >
@ KEYNIL
the key is null (eg { : b} results in a null key)
Definition node_type.hpp:49
@ DOC
a document
Definition node_type.hpp:41
basic_substring< char > substr
a mutable string view
Definition substr.hpp:2356
basic_substring< const char > csubstr
an immutable string view
Definition substr.hpp:2357
(Undefined by default) Use shorter error message from checks/asserts: do not show the check condition...
Definition common.cpp:14
#define rest_is(c1, c2)
#define _prflag(fl, txt)
const char * type_str() const noexcept
return a preset string based on the node type