DMS-XS-Parser
view release on metacpan or search on metacpan
vendor/dms-c/dms.c view on Meta::CPAN
if (c == 0) { set_err(p, "expected key"); return NULL; }
return parse_bare_key_str(p);
}
static char *parse_bare_key_str(parser *p) {
size_t start = p->pos;
const char *src = p->src;
size_t end = p->len;
size_t pos = p->pos;
/* ASCII fast path: ~6 chars/key à 50k keys is the bench's biggest
* utf8_decode caller. Stepping byte-by-byte here is ~10Ã faster. */
while (pos < end) {
unsigned char b0 = (unsigned char)src[pos];
if (b0 < 0x80) {
if (!is_bare_key_char_ascii(b0)) break;
pos++;
continue;
}
/* Non-ASCII: fall through to the multi-byte decoder. */
size_t clen;
int cp = utf8_decode(src + pos, end - pos, &clen);
if (cp < 0 || !is_bare_key_char_cp(cp)) break;
pos += clen;
}
p->pos = pos;
if (pos == start) { set_err(p, "expected key"); return NULL; }
return xstrndup(src + start, pos - start);
}
/* ---------- string parsers ---------- */
static char *parse_basic_string_str(parser *p) {
int sl = p->line;
size_t sls = p->line_start, sp = p->pos;
p->pos++; /* opening " */
buf b; buf_init(&b);
while (1) {
if (eof(p)) { set_err_at(p, sl, sls, sp, "unterminated string"); free(b.data); return NULL; }
char c = peek(p);
if (c == '\n' || c == '\r') { set_err(p, "strings cannot span lines"); free(b.data); return NULL; }
if (c == '"') { p->pos++; /* SPEC §Unicode normalization: re-NFC after escape decoding. */ if (!b.data) { return nfc_string(xstrdup("")); } return nfc_string(b.data); }
if (c == '\\') {
p->pos++;
if (eof(p)) { set_err(p, "unterminated escape"); free(b.data); return NULL; }
char esc = p->src[p->pos++];
char out[5];
int outlen;
unsigned cp;
switch (esc) {
case '"': buf_push(&b, '"'); break;
case '\\': buf_push(&b, '\\'); break;
case 'n': buf_push(&b, '\n'); break;
case 't': buf_push(&b, '\t'); break;
case 'r': buf_push(&b, '\r'); break;
case 'b': buf_push(&b, '\b'); break;
case 'f': buf_push(&b, '\f'); break;
case 'u':
case 'U': {
int n = (esc == 'u') ? 4 : 8;
if ((int)(p->len - p->pos) < n) {
set_err(p, "expected %d hex digits in unicode escape", n);
free(b.data); return NULL;
}
cp = 0;
for (int i = 0; i < n; i++) {
char h = p->src[p->pos + i];
int d;
if (h >= '0' && h <= '9') d = h - '0';
else if (h >= 'a' && h <= 'f') d = h - 'a' + 10;
else if (h >= 'A' && h <= 'F') d = h - 'A' + 10;
else { set_err(p, "invalid hex in unicode escape"); free(b.data); return NULL; }
cp = (cp << 4) | d;
}
p->pos += n;
/* SPEC: U+0000 is forbidden anywhere in DMS source, including via
escape decoding. `` / `\U00000000` must not slip through. */
if (cp == 0) {
set_err(p, "\\u0000 escape forbidden"); free(b.data); return NULL;
}
/* SPEC §basic-string escapes: surrogate codepoints (U+D800..U+DFFF)
are not valid Unicode scalars and are a parse error in `\uXXXX` /
`\UXXXXXXXX` escapes. */
if (cp >= 0xD800 && cp <= 0xDFFF) {
set_err(p, "surrogate codepoint U+%04X in escape", cp); free(b.data); return NULL;
}
if (cp > 0x10FFFF) {
set_err(p, "unicode escape is not a scalar value"); free(b.data); return NULL;
}
outlen = utf8_encode(cp, out);
buf_push_str(&b, out, outlen);
break;
}
default:
set_err(p, "invalid escape '\\%c'", esc);
free(b.data); return NULL;
}
} else {
/* preserve raw bytes */
buf_push(&b, c);
p->pos++;
}
}
}
static char *parse_literal_string_str(parser *p) {
int sl = p->line;
size_t sls = p->line_start, sp = p->pos;
p->pos++; /* opening ' */
buf b; buf_init(&b);
while (1) {
if (eof(p)) { set_err_at(p, sl, sls, sp, "unterminated string"); free(b.data); return NULL; }
char c = peek(p);
if (c == '\n' || c == '\r') { set_err(p, "strings cannot span lines"); free(b.data); return NULL; }
if (c == '\'') { p->pos++; if (!b.data) return xstrdup(""); return b.data; }
buf_push(&b, c);
p->pos++;
}
}
static dms_value *parse_basic_string(parser *p) {
char *s = parse_basic_string_str(p);
if (!s) return NULL;
dms_value *v = new_value(DMS_STRING);
v->u.s = s;
return v;
}
static dms_value *parse_literal_string(parser *p) {
char *s = parse_literal_string_str(p);
if (!s) return NULL;
dms_value *v = new_value(DMS_STRING);
v->u.s = s;
/* Record original form (Literal). Basic-string form is the emitter
default and not recorded. */
if (p->record_forms) {
dms_string_form *sf = (dms_string_form *)calloc(1, sizeof(dms_string_form));
sf->kind = DMS_STRING_LITERAL;
record_form_string(p, sf);
}
return v;
}
/* ---------- numbers / datetimes ---------- */
static bool valid_underscores(const char *s) {
size_t n = strlen(s);
if (n == 0) return true;
( run in 0.498 second using v1.01-cache-2.11-cpan-9581c071862 )