// SafeC Standard Library — YAML implementation (see yaml.h for the
// supported subset).
#pragma once
#include <std/serial/yaml.h>
#include <std/serial/value.h>
#include <std/collections/string.h>
#include <std/collections/vec.h>
#include <std/mem.h>
#include <std/fmt.h>
#include <std/convert.h>
#include <std/str.h>
namespace std {
// ── Writer ──────────────────────────────────────────────────────────────────
static void yaml_indent_(&String out, int depth) {
int i = 0;
while (i < depth) {
unsafe { out.push(" "); }
i = i + 1;
}
}
static int yaml_scalar_needs_quoting_(const char* s) {
char c0;
unsafe { c0 = s[0]; }
if (c0 == (char)0) { return 1; } // empty string must be quoted ('' ), else it reads as null
if (c0 == ' ' || c0 == '-' || c0 == '?' || c0 == ':' || c0 == '#' ||
c0 == '&' || c0 == '*' || c0 == '!' || c0 == '|' || c0 == '>' ||
c0 == '\'' || c0 == '"' || c0 == '%' || c0 == '@' || c0 == '`' ||
c0 == '[' || c0 == ']' || c0 == '{' || c0 == '}' || c0 == ',') {
return 1;
}
unsigned long i = 0UL;
while (1) {
char c;
unsafe { c = s[i]; }
if (c == (char)0) { break; }
if (c == ':' || c == '#' || c == '\n') { return 1; }
i = i + 1UL;
}
struct String trimmed; unsafe { trimmed = String::from(s); }
struct String trimmedCopy = trimmed.trim();
trimmed.free();
int looksLikeOther = trimmedCopy.eq_cstr("true") || trimmedCopy.eq_cstr("false") ||
trimmedCopy.eq_cstr("null") || trimmedCopy.eq_cstr("~") ||
trimmedCopy.eq_cstr("True") || trimmedCopy.eq_cstr("False") ||
trimmedCopy.eq_cstr("Null");
trimmedCopy.free();
return looksLikeOther;
}
static void yaml_append_scalar_string_(&String out, const char* s) {
if (!yaml_scalar_needs_quoting_(s)) {
unsafe { out.push(s); }
return;
}
// Single-quoted form: only escaping rule is '' for a literal quote —
// simpler than double-quoted, and sufficient since we're only quoting
// for YAML's syntactic reasons above, not to represent control chars
// (a control-char-bearing string is a corner case this writer doesn't
// specially handle, matching the writer's overall "common case" scope).
unsafe { out.push_char('\''); }
unsigned long i = 0UL;
unsigned long run_start = 0UL;
while (1) {
char c;
unsafe { c = s[i]; }
if (c == (char)0) { break; }
if (c == '\'') {
if (i > run_start) { unsafe { out.push_n_(s + run_start, i - run_start); } }
unsafe { out.push("''"); }
i = i + 1UL;
run_start = i;
} else {
i = i + 1UL;
}
}
if (i > run_start) { unsafe { out.push_n_(s + run_start, i - run_start); } }
unsafe { out.push_char('\''); }
}
static void yaml_append_float_(&String out, double v) {
unsafe {
unsigned long before = out.length();
out.push_float(v, 6);
unsigned long end = out.length();
while (end > before) {
int c = out.char_at(end - 1UL);
if (c != (int)'0') { break; }
end = end - 1UL;
}
if (end > before) {
int c = out.char_at(end - 1UL);
if (c == (int)'.') { end = end - 1UL; }
}
out.truncate(end);
}
}
static void yaml_append_scalar_(&String out, const &Value v) {
int kind;
unsafe { kind = v.kind; }
if (kind == VAL_NULL) { unsafe { out.push("null"); } }
else if (kind == VAL_BOOL) {
int b;
unsafe { b = v.bool_val; }
unsafe { out.push(b != 0 ? "true" : "false"); }
} else if (kind == VAL_INT) {
long long n;
unsafe { n = v.int_val; }
unsafe { out.push_int(n); }
} else if (kind == VAL_FLOAT) {
double f;
unsafe { f = v.float_val; }
yaml_append_float_(out, f);
} else if (kind == VAL_STRING) {
const char* s;
unsafe {
if (v.str_val != (char*)0) { s = (const char*)v.str_val; }
else { s = (const char*)""; }
}
yaml_append_scalar_string_(out, s);
}
}
static void yaml_write_block_(const &Value v, &String out, int depth);
static void yaml_write_array_(const &Value v, &String out, int depth) {
unsigned long n;
unsafe { n = v.arr_val.length(); }
if (n == 0UL) { unsafe { out.push("[]\n"); } return; }
unsigned long i = 0UL;
while (i < n) {
yaml_indent_(out, depth);
unsafe { out.push_char('-'); }
&Value elem;
unsafe { elem = v.arr_val.at(i); }
int ekind;
unsafe { ekind = elem->kind; }
if (ekind == VAL_ARRAY || ekind == VAL_OBJECT) {
unsafe { out.push_char('\n'); }
yaml_write_block_(elem, out, depth + 1);
} else {
unsafe { out.push_char(' '); }
yaml_append_scalar_(out, elem);
unsafe { out.push_char('\n'); }
}
i = i + 1UL;
}
}
static void yaml_write_object_(const &Value v, &String out, int depth) {
unsigned long n;
unsafe { n = v.obj_val.length(); }
if (n == 0UL) { unsafe { out.push("{}\n"); } return; }
unsigned long i = 0UL;
while (i < n) {
yaml_indent_(out, depth);
struct ObjectEntry* e;
unsafe { e = v.obj_val.at(i); }
const char* key;
unsafe { key = (const char*)e->key; }
yaml_append_scalar_string_(out, key);
unsafe { out.push_char(':'); }
&Value val;
unsafe { val = e->val; }
int vkind;
unsafe { vkind = val->kind; }
if (vkind == VAL_ARRAY || vkind == VAL_OBJECT) {
unsafe { out.push_char('\n'); }
yaml_write_block_(val, out, depth + 1);
} else {
unsafe { out.push_char(' '); }
yaml_append_scalar_(out, val);
unsafe { out.push_char('\n'); }
}
i = i + 1UL;
}
}
static void yaml_write_block_(const &Value v, &String out, int depth) {
int kind;
unsafe { kind = v.kind; }
if (kind == VAL_ARRAY) { yaml_write_array_(v, out, depth); }
else if (kind == VAL_OBJECT) { yaml_write_object_(v, out, depth); }
else { yaml_append_scalar_(out, v); unsafe { out.push_char('\n'); } }
}
void yaml_write(const &Value v, &String out) {
yaml_write_block_(v, out, 0);
}
inline struct String value_to_yaml(const &Value v) {
struct String out = String::new();
yaml_write(v, &out);
return out;
}
// ── Parser ────────────────────────────────────────────────────────────────
struct YamlLine_ {
unsigned long indent;
struct String content; // comment-stripped, trailing-whitespace-trimmed
const char* doc; // the whole document (block scalars re-read raw lines)
unsigned long end; // offset just past this line's '\n' in 'doc'
};
// Strips a trailing '#...' comment (only when the '#' is outside any
// quoted scalar on the line) and trailing whitespace.
static struct String yaml_strip_comment_(const char* line) {
struct String out = String::new();
unsigned long i = 0UL;
char quote = (char)0; // 0 = not in a quote, else the active quote char
while (1) {
char c;
unsafe { c = line[i]; }
if (c == (char)0) { break; }
if (quote != (char)0) {
unsafe { out.push_char(c); }
if (c == quote) { quote = (char)0; }
i = i + 1UL;
continue;
}
if (c == '\'' || c == '"') {
quote = c;
unsafe { out.push_char(c); }
i = i + 1UL;
continue;
}
if (c == '#') {
// A comment starts at '#' only at the line start or after
// whitespace ('a#b' is a plain scalar).
char prev = (char)0;
unsafe { if (i > 0UL) { prev = line[i - 1UL]; } }
if (i == 0UL || prev == ' ' || prev == '\t') { break; }
}
unsafe { out.push_char(c); }
i = i + 1UL;
}
struct String trimmed = out.trim_right();
out.free();
return trimmed;
}
// Splits 'text' into non-blank, comment-stripped lines with their leading-
// space indent measured. Tabs are not accepted as indentation (matching
// the YAML spec, which forbids tabs for indent) — a tab in leading
// whitespace ends the indent count at that point, generally producing an
// indent that won't line up with anything and surfacing as a structural
// mismatch rather than being silently misinterpreted.
static struct Vec<struct YamlLine_> yaml_split_lines_(const char* text) {
struct Vec<struct YamlLine_> lines = Vec::new();
unsigned long i = 0UL;
while (1) {
char c0;
unsafe { c0 = text[i]; }
if (c0 == (char)0) { break; }
unsigned long lineStart = i;
while (1) {
char c;
unsafe { c = text[i]; }
if (c == (char)0 || c == '\n') { break; }
i = i + 1UL;
}
unsigned long lineEnd = i;
char c;
unsafe { c = text[i]; }
if (c == '\n') { i = i + 1UL; }
unsigned long indent = 0UL;
unsigned long p = lineStart;
while (p < lineEnd) {
char sc;
unsafe { sc = text[p]; }
if (sc != ' ') { break; }
indent = indent + 1UL;
p = p + 1UL;
}
struct String rawLine;
unsafe { rawLine = String::from_n_(text + p, lineEnd - p); }
struct String content = yaml_strip_comment_(rawLine.as_ptr());
rawLine.free();
if (!content.is_empty()) {
struct YamlLine_ yl;
yl.indent = indent;
yl.content = content;
yl.doc = text;
yl.end = i;
lines.push(yl);
} else {
content.free();
}
}
return lines;
}
static struct Value yaml_parse_flow_value_(const char* t);
static struct Value yaml_parse_scalar_(const char* text) {
struct String s; unsafe { s = String::from(text); }
struct String trimmed = s.trim();
s.free();
const char* t = trimmed.as_ptr();
char c0;
unsafe { c0 = t[0]; }
struct Value result;
int handled = 1;
if (trimmed.is_empty() || trimmed.eq_cstr("~") || trimmed.eq_cstr_ignore_case("null")) {
result = Value::null();
} else if (trimmed.eq_cstr_ignore_case("true")) {
result = Value::from_bool(1);
} else if (trimmed.eq_cstr_ignore_case("false")) {
result = Value::from_bool(0);
} else if (c0 == '[' || c0 == '{') {
result = yaml_parse_flow_value_(t);
} else if (c0 == '"') {
struct String out = String::new();
unsigned long i = 1UL;
while (1) {
char c;
unsafe { c = t[i]; }
if (c == (char)0 || c == '"') { break; }
if (c == '\\') {
char esc;
unsafe { esc = t[i + 1UL]; }
if (esc == 'n') { unsafe { out.push_char('\n'); } }
else if (esc == 't') { unsafe { out.push_char('\t'); } }
else if (esc == 'r') { unsafe { out.push_char('\r'); } }
else if (esc == '"') { unsafe { out.push_char('"'); } }
else if (esc == '\\') { unsafe { out.push_char('\\'); } }
else { unsafe { out.push_char(esc); } }
i = i + 2UL;
} else {
unsafe { out.push_char(c); }
i = i + 1UL;
}
}
result = Value::from_str(out.as_ptr());
out.free();
} else if (c0 == '\'') {
struct String out = String::new();
unsigned long i = 1UL;
while (1) {
char c;
unsafe { c = t[i]; }
if (c == (char)0) { break; }
if (c == '\'') {
char next;
unsafe { next = t[i + 1UL]; }
if (next == '\'') { unsafe { out.push_char('\''); } i = i + 2UL; continue; }
break;
}
unsafe { out.push_char(c); }
i = i + 1UL;
}
result = Value::from_str(out.as_ptr());
out.free();
} else {
// Plain scalar: number if it looks like one, else a bare string.
int isNumeric = (c0 == '-' || (c0 >= '0' && c0 <= '9'));
int isFloat = 0;
if (isNumeric) {
unsigned long i = (c0 == '-') ? 1UL : 0UL;
unsigned long len = trimmed.length();
if (i >= len) { isNumeric = 0; }
while (i < len) {
char c;
unsafe { c = t[i]; }
if (c >= '0' && c <= '9') { i = i + 1UL; continue; }
if (c == '.' || c == 'e' || c == 'E' || c == '+' || c == '-') {
isFloat = 1; i = i + 1UL; continue;
}
isNumeric = 0;
break;
}
}
if (isNumeric) {
int pok = 0;
if (isFloat) { result = Value::from_float(trimmed.parse_float(&pok)); }
else { result = Value::from_int(trimmed.parse_int(&pok)); }
unsafe { if (pok == 0) { result = Value::from_str(t); } }
} else {
unsafe { result = Value::from_str(t); }
}
}
(void)handled;
trimmed.free();
return result;
}
// ── Flow collections ('[a, b]', '{k: v}') ───────────────────────────────────
// One line only (a flow collection spanning lines isn't supported). Items
// are flow scalars — quoted, or plain text up to the next ',' ']' '}' —
// or nested flow collections.
static struct Value yaml_parse_flow_(const char* s, unsigned long* i, int* ok);
static void yaml_flow_ws_(const char* s, unsigned long* i) {
unsafe { while (s[*i] == ' ' || s[*i] == '\t') { *i = *i + 1UL; } }
}
// A flow scalar starting at s[*i]; 'is_key' also stops a plain scalar at
// ': ' (or ':' before ',', '}' or the end).
static struct Value yaml_parse_flow_scalar_(const char* s, unsigned long* i, int is_key, int* ok) {
unsigned long start;
unsigned long end;
unsafe {
start = *i;
char q = s[start];
if (q == '"' || q == '\'') {
unsigned long k = start + 1UL;
while (s[k] != (char)0) {
if (q == '"' && s[k] == '\\' && s[k + 1UL] != (char)0) { k = k + 2UL; continue; }
if (s[k] == q) {
if (q == '\'' && s[k + 1UL] == '\'') { k = k + 2UL; continue; }
break;
}
k = k + 1UL;
}
if (s[k] != q) { *ok = 0; return Value::null(); }
end = k + 1UL;
} else {
unsigned long k = start;
while (s[k] != (char)0 && s[k] != ',' && s[k] != ']' && s[k] != '}') {
if (is_key && s[k] == ':' &&
(s[k + 1UL] == ' ' || s[k + 1UL] == ',' || s[k + 1UL] == '}' || s[k + 1UL] == (char)0)) { break; }
k = k + 1UL;
}
end = k;
}
*i = end;
struct String piece = String::from_n_(s + start, end - start);
struct Value v = yaml_parse_scalar_(piece.as_ptr());
piece.free();
return v;
}
}
static struct Value yaml_parse_flow_(const char* s, unsigned long* i, int* ok) {
yaml_flow_ws_(s, i);
char c;
unsafe { c = s[*i]; }
if (c == '[') {
struct Value arr = Value::array();
unsafe { *i = *i + 1UL; }
for (;;) {
yaml_flow_ws_(s, i);
unsafe { c = s[*i]; }
if (c == ']') { unsafe { *i = *i + 1UL; } return arr; }
if (c == (char)0) { unsafe { *ok = 0; } return arr; }
arr.array_push(yaml_parse_flow_(s, i, ok));
int good; unsafe { good = *ok; }
if (!good) { return arr; }
yaml_flow_ws_(s, i);
unsafe { c = s[*i]; }
if (c == ',') { unsafe { *i = *i + 1UL; } }
else if (c != ']') { unsafe { *ok = 0; } return arr; }
}
}
if (c == '{') {
struct Value obj = Value::object();
unsafe { *i = *i + 1UL; }
for (;;) {
yaml_flow_ws_(s, i);
unsafe { c = s[*i]; }
if (c == '}') { unsafe { *i = *i + 1UL; } return obj; }
if (c == (char)0) { unsafe { *ok = 0; } return obj; }
struct Value key = yaml_parse_flow_scalar_(s, i, 1, ok);
struct String keyStr = String::new();
if (key.kind_of() == VAL_STRING) { keyStr.push(key.as_string()); }
else if (key.kind_of() == VAL_INT) { keyStr.push_int(key.as_int()); }
else if (key.kind_of() == VAL_BOOL) { keyStr.push(key.as_bool() ? "true" : "false"); }
key.free();
yaml_flow_ws_(s, i);
unsafe { c = s[*i]; }
struct Value val;
if (c == ':') {
unsafe { *i = *i + 1UL; }
yaml_flow_ws_(s, i);
unsafe { c = s[*i]; }
if (c == ',' || c == '}') { val = Value::null(); }
else { val = yaml_parse_flow_(s, i, ok); }
} else {
val = Value::null(); // '{a, b}': keys with null values
}
obj.object_set(keyStr.as_ptr(), val);
keyStr.free();
int good; unsafe { good = *ok; }
if (!good) { return obj; }
yaml_flow_ws_(s, i);
unsafe { c = s[*i]; }
if (c == ',') { unsafe { *i = *i + 1UL; } }
else if (c != '}') { unsafe { *ok = 0; } return obj; }
}
}
return yaml_parse_flow_scalar_(s, i, 0, ok);
}
// A whole scalar position holding '[...]' / '{...}'. Text that doesn't
// parse as one flow collection stays a plain string (least surprise).
static struct Value yaml_parse_flow_value_(const char* t) {
unsigned long i = 0UL;
int ok = 1;
struct Value v = yaml_parse_flow_(t, &i, &ok);
yaml_flow_ws_(t, &i);
char rest;
unsafe { rest = t[i]; }
if (!ok || rest != (char)0) {
v.free();
unsafe { return Value::from_str(t); }
}
return v;
}
// ── Block scalars ('|' literal, '>' folded) ─────────────────────────────────
// 1 if 'h' is a block scalar header: '|' or '>' plus an optional
// chomping indicator ('-' strip, '+' keep) and/or indentation digit.
static int yaml_is_block_header_(const &String h) {
int c0 = h.char_at(0UL);
if (c0 != (int)'|' && c0 != (int)'>') { return 0; }
for (unsigned long k = 1UL; k < h.len; k = k + 1UL) {
int c = h.char_at(k);
if (c != (int)'-' && c != (int)'+' && !(c >= (int)'1' && c <= (int)'9')) { return 0; }
}
return 1;
}
// Reads the block scalar whose header ends the line lines[*idx - 1]; its
// content is every following raw line indented more than 'parentIndent'
// (blank lines included). Advances *idx past the lines it consumed.
static struct Value yaml_block_scalar_(&Vec<struct YamlLine_> lines, unsigned long* idx,
const &String header, unsigned long parentIndent) {
struct YamlLine_* hl;
unsafe { hl = lines->at(*idx - 1UL); }
const char* doc;
unsigned long pos;
unsafe { doc = hl->doc; pos = hl->end; }
int folded = header.char_at(0UL) == (int)'>';
int chomp = header.contains("-") ? -1 : (header.contains("+") ? 1 : 0);
unsigned long blockIndent = 0UL;
struct String out = String::new();
struct String pendingBreaks = String::new(); // newlines held until more content (or chomping)
int any = 0;
unsigned long endPos = pos;
unsafe {
while (doc[pos] != (char)0) {
unsigned long ls = pos;
unsigned long ind = 0UL;
while (doc[pos] == ' ') { ind = ind + 1UL; pos = pos + 1UL; }
unsigned long cs = pos;
while (doc[pos] != (char)0 && doc[pos] != '\n') { pos = pos + 1UL; }
unsigned long ce = pos;
if (ce > cs && doc[ce - 1UL] == '\r') { ce = ce - 1UL; }
if (doc[pos] == '\n') { pos = pos + 1UL; }
int blank = ce == cs;
if (!blank && ind <= parentIndent) { pos = ls; break; }
if (!blank && blockIndent == 0UL) { blockIndent = ind; }
endPos = pos;
if (blank) { pendingBreaks.push_char('\n'); continue; }
unsigned long from = ls + (blockIndent < ind ? blockIndent : ind);
if (any) {
if (folded && pendingBreaks.len == 0UL && ind == blockIndent) { out.push_char(' '); }
else if (folded) { out.push_str(&pendingBreaks); if (ind != blockIndent) { out.push_char('\n'); } }
else { out.push_char('\n'); out.push_str(&pendingBreaks); }
} else {
out.push_str(&pendingBreaks);
}
pendingBreaks.clear();
out.push_n_(doc + from, ce - from);
any = 1;
}
}
if (any) {
if (chomp == 0) { out.push_char('\n'); }
else if (chomp == 1) { out.push_char('\n'); out.push_str(&pendingBreaks); }
}
pendingBreaks.free();
// Skip the parsed lines that belong to the block.
unsigned long n;
unsafe { n = lines->length(); }
while (1) {
unsigned long cur;
unsafe { cur = *idx; }
if (cur >= n) { break; }
struct YamlLine_* yl;
unsafe { yl = lines->at(cur); }
unsigned long lineEnd;
unsafe { lineEnd = yl->end; }
if (lineEnd > endPos) { break; }
unsafe { *idx = cur + 1UL; }
}
struct Value v = Value::from_str(out.as_ptr());
out.free();
return v;
}
static struct Value yaml_parse_block_(&Vec<struct YamlLine_> lines, unsigned long* idx,
unsigned long minIndent, int* ok);
// Splits "key: value" / "key:" at the first unquoted ':' followed by a
// space or end-of-line (a ':' inside a scalar value like a URL or time-of-
// day is not a key separator unless followed by whitespace/EOL, per the
// YAML spec's own rule for plain scalars).
static long long yaml_find_colon_(const char* s) {
unsigned long i = 0UL;
char quote = (char)0;
int depth = 0; // inside a flow collection: its ':'s aren't ours
while (1) {
char c;
unsafe { c = s[i]; }
if (c == (char)0) { return -1; }
if (quote != (char)0) {
if (c == quote) { quote = (char)0; }
i = i + 1UL;
continue;
}
if (c == '\'' || c == '"') { quote = c; i = i + 1UL; continue; }
if (c == '[' || c == '{') { depth = depth + 1; }
else if ((c == ']' || c == '}') && depth > 0) { depth = depth - 1; }
if (c == ':' && depth == 0) {
char next;
unsafe { next = s[i + 1UL]; }
if (next == (char)0 || next == ' ') { return (long long)i; }
}
i = i + 1UL;
}
}
static struct Value yaml_parse_mapping_(&Vec<struct YamlLine_> lines, unsigned long* idx,
unsigned long indent, int* ok) {
struct Value obj = Value::object();
unsigned long n;
unsafe { n = lines->length(); }
while (1) {
unsigned long curIdx;
unsafe { curIdx = *idx; }
if (curIdx >= n) { break; }
struct YamlLine_* yl;
unsafe { yl = lines->at(curIdx); }
unsigned long ylIndent;
unsafe { ylIndent = yl->indent; }
if (ylIndent != indent) { break; }
const char* content;
unsafe { content = yl->content.as_ptr(); }
long long colonPos = yaml_find_colon_(content);
if (colonPos < 0) { unsafe { *ok = 0; } break; }
struct String keyRaw;
unsafe { keyRaw = String::from_n_(content, (unsigned long)colonPos); }
struct Value keyVal = yaml_parse_scalar_(keyRaw.as_ptr());
keyRaw.free();
// Only string-shaped keys make sense as object keys here (a
// numeric-looking key stays a string key — same 'stringly-keyed'
// convention std::Value's VAL_OBJECT already uses for JSON).
const char* key = keyVal.as_string();
struct String keyStr; unsafe { keyStr = key ? String::from(key) : String::new(); }
keyVal.free();
unsigned long restStart = (unsigned long)colonPos + 1UL;
unsigned long contentLen;
unsafe { contentLen = yl->content.length(); }
struct String restRaw;
unsafe {
restRaw = (restStart <= contentLen)
? yl->content.substr(restStart, contentLen)
: String::new();
}
struct String restTrimmed = restRaw.trim();
restRaw.free();
unsafe { *idx = *idx + 1UL; }
if (restTrimmed.is_empty()) {
struct Value child = yaml_parse_block_(lines, idx, indent + 1UL, ok);
unsafe { obj.object_set(keyStr.as_ptr(), child); }
} else if (yaml_is_block_header_(&restTrimmed)) {
struct Value child = yaml_block_scalar_(lines, idx, &restTrimmed, indent);
unsafe { obj.object_set(keyStr.as_ptr(), child); }
} else {
struct Value scalar = yaml_parse_scalar_(restTrimmed.as_ptr());
unsafe { obj.object_set(keyStr.as_ptr(), scalar); }
}
restTrimmed.free();
keyStr.free();
int stillOk;
unsafe { stillOk = *ok; }
if (stillOk == 0) { break; }
}
return obj;
}
static struct Value yaml_parse_sequence_(&Vec<struct YamlLine_> lines, unsigned long* idx,
unsigned long indent, int* ok) {
struct Value arr = Value::array();
unsigned long n;
unsafe { n = lines->length(); }
while (1) {
unsigned long curIdx;
unsafe { curIdx = *idx; }
if (curIdx >= n) { break; }
struct YamlLine_* yl;
unsafe { yl = lines->at(curIdx); }
unsigned long ylIndent;
unsafe { ylIndent = yl->indent; }
if (ylIndent != indent) { break; }
const char* content;
unsafe { content = yl->content.as_ptr(); }
char c0;
unsafe { c0 = content[0]; }
if (c0 != '-') { break; }
char c1;
unsafe { c1 = content[1]; }
if (c1 != (char)0 && c1 != ' ') { break; } // e.g. "-5" is a scalar, not a list item
struct String restRaw;
unsafe { restRaw = yl->content.substr(1UL, yl->content.length()); }
struct String rest = restRaw.trim_left();
restRaw.free();
if (rest.is_empty()) {
unsafe { *idx = *idx + 1UL; }
struct Value child = yaml_parse_block_(lines, idx, indent + 1UL, ok);
arr.array_push(child);
} else if (yaml_is_block_header_(&rest)) {
unsafe { *idx = *idx + 1UL; }
arr.array_push(yaml_block_scalar_(lines, idx, &rest, indent));
} else if (yaml_find_colon_(rest.as_ptr()) >= 0) {
// "- key: value" compact list-of-mappings shorthand: rewrite
// this line in place as a mapping key at the column right
// after "- ", then let yaml_parse_mapping_ consume it plus any
// further real lines indented to match (see yaml.h's comment).
unsigned long dashIndent;
unsafe { dashIndent = yl->indent; }
unsafe {
yl->content.free();
yl->content = rest.clone();
yl->indent = dashIndent + 2UL;
}
struct Value child = yaml_parse_mapping_(lines, idx, dashIndent + 2UL, ok);
arr.array_push(child);
} else {
struct Value scalar = yaml_parse_scalar_(rest.as_ptr());
arr.array_push(scalar);
unsafe { *idx = *idx + 1UL; }
}
rest.free();
int stillOk;
unsafe { stillOk = *ok; }
if (stillOk == 0) { break; }
}
return arr;
}
static struct Value yaml_parse_block_(&Vec<struct YamlLine_> lines, unsigned long* idx,
unsigned long minIndent, int* ok) {
unsigned long n;
unsafe { n = lines->length(); }
unsigned long curIdx;
unsafe { curIdx = *idx; }
if (curIdx >= n) { return Value::null(); }
struct YamlLine_* yl;
unsafe { yl = lines->at(curIdx); }
unsigned long ylIndent;
unsafe { ylIndent = yl->indent; }
if (ylIndent < minIndent) { return Value::null(); }
unsigned long indent = ylIndent;
const char* content;
unsafe { content = yl->content.as_ptr(); }
char c0;
unsafe { c0 = content[0]; }
char c1;
unsafe { c1 = content[1]; }
if (c0 == '-' && (c1 == (char)0 || c1 == ' ')) {
return yaml_parse_sequence_(lines, idx, indent, ok);
}
if (yaml_find_colon_(content) >= 0) {
return yaml_parse_mapping_(lines, idx, indent, ok);
}
// A bare scalar as the whole document/block value.
struct Value v = yaml_parse_scalar_(content);
unsafe { *idx = *idx + 1UL; }
return v;
}
struct Value yaml_parse(const &char text, ?&int ok) {
struct Vec<struct YamlLine_> lines = yaml_split_lines_(text);
unsigned long idx = 0UL;
int good = 1;
struct Value result = yaml_parse_block_(&lines, &idx, 0UL, &good);
if (good != 0 && idx != lines.length()) {
// Leftover lines the block parser couldn't attach anywhere (e.g. a
// dedent that doesn't match any enclosing indent level).
good = 0;
}
lines.drop(); // and each line's content
if (ok != (int*)0) { unsafe { *ok = good; } }
if (good == 0) {
result.free();
return Value::null();
}
return result;
}
struct Yaml Yaml::new() {
struct Yaml f;
f.unused_ = 0;
return f;
}
void Yaml::write(const &Value v, &String out) const { yaml_write(v, out); }
struct Value Yaml::parse(const &char text, ?&int ok) const { return yaml_parse(text, ok); }
} // namespace std