DMS-Parser-XS
view release on metacpan or search on metacpan
vendor/dms-c/dms.c view on Meta::CPAN
for (size_t i = 0; i < mods->len; i++) {
h_mod *m = &mods->mods[i];
if (strcmp(m->name, "_fold_paragraphs") == 0) {
if (m->nargs != 0) { free(cur); return set_err(p, "fold_paragraphs() takes no arguments"); }
char *next = fold_paragraphs(cur);
free(cur); cur = next;
} else if (strcmp(m->name, "_trim") == 0) {
if (m->nargs < 2 || m->nargs > 3) {
free(cur); return set_err(p, "trim(chars, where, replacement = \"\") expects 2 or 3 arguments");
}
if (m->args[0]->type != DMS_STRING) {
free(cur); return set_err(p, "trim: first argument (chars) must be a string");
}
if (m->args[1]->type != DMS_STRING) {
free(cur); return set_err(p, "trim: second argument (where) must be a string");
}
const char *replacement = "";
if (m->nargs == 3) {
if (m->args[2]->type != DMS_STRING) {
free(cur); return set_err(p, "trim: third argument (replacement) must be a string");
}
replacement = m->args[2]->u.s;
}
char *next = apply_trim_c(cur, m->args[0]->u.s, m->args[1]->u.s, replacement);
free(cur); cur = next;
} else {
free(cur); return set_err(p, "unknown modifier: %s", m->name);
}
}
*out_str = cur;
return 1;
}
/* Build a dms_string_form descriptor from label+flavor+modifiers. Label
is cloned (unlabeled â NULL); modifier arg values are deep-cloned. */
static dms_string_form *build_heredoc_string_form(
dms_heredoc_flavor flavor, const char *label, const h_mods *mods)
{
dms_string_form *sf = (dms_string_form *)calloc(1, sizeof(dms_string_form));
sf->kind = DMS_STRING_HEREDOC;
sf->heredoc_flavor = flavor;
sf->label = (label && label[0]) ? xstrdup(label) : NULL;
sf->num_modifiers = mods->len;
if (mods->len > 0) {
sf->modifiers = (dms_heredoc_modifier_call *)calloc(mods->len, sizeof(dms_heredoc_modifier_call));
for (size_t i = 0; i < mods->len; i++) {
sf->modifiers[i].name = xstrdup(mods->mods[i].name);
sf->modifiers[i].num_args = mods->mods[i].nargs;
if (mods->mods[i].nargs > 0) {
sf->modifiers[i].args = (dms_value **)calloc(mods->mods[i].nargs, sizeof(dms_value *));
for (size_t j = 0; j < mods->mods[i].nargs; j++) {
sf->modifiers[i].args[j] = dms_value_clone(mods->mods[i].args[j]);
}
}
}
}
return sf;
}
/* SPEC §basic-string escapes: a `\uXXXX` / `\UXXXXXXXX` escape whose
decoded value falls in the surrogate range U+D800..U+DFFF is not a
Unicode scalar and is a parse error. The basic-string lexer
(parse_basic_string_str) already enforces this; basic-heredoc body
lines are collected raw, so we validate the same rule by scanning
the body for surrogate escape sequences. Returns 1 on OK, sets the
parser error and returns 0 on a surrogate escape. */
static int validate_heredoc_basic_surrogates(const h_body *body, parser *p) {
for (size_t li = 0; li < body->len; li++) {
const h_line *ln = &body->lines[li];
const char *text = ln->text;
size_t tl = strlen(text);
size_t i = 0;
while (i < tl) {
if ((unsigned char)text[i] == '\\') {
/* find the run of consecutive backslashes starting at i */
size_t j = i;
while (j < tl && text[j] == '\\') j++;
size_t run = j - i;
/* pairs of `\\` consume themselves; only the last `\` of
an odd-length run is an escape introducer. */
if ((run % 2) == 1 && j < tl) {
char intro = text[j];
int n = 0;
if (intro == 'u') n = 4;
else if (intro == 'U') n = 8;
if (n > 0 && j + 1 + (size_t)n <= tl) {
int all_hex = 1;
unsigned cp = 0;
for (int k = 0; k < n; k++) {
char h = text[j + 1 + k];
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 { all_hex = 0; break; }
cp = (cp << 4) | (unsigned)d;
}
if (all_hex && cp >= 0xD800 && cp <= 0xDFFF) {
/* column points at the leading `\` of the escape
(1-based, byte col). */
size_t esc_off = j - 1;
return set_err_at(p, ln->line, ln->line_start,
ln->line_start + esc_off,
"surrogate codepoint U+%04X in escape", cp);
}
}
}
i = j;
} else {
i++;
}
}
}
return 1;
}
static dms_value *parse_heredoc_basic(parser *p) {
p->pos += 3;
char *label = parse_heredoc_label(p);
h_mods mods;
if (!parse_heredoc_modifiers(p, &mods)) { free(label); return NULL; }
( run in 0.701 second using v1.01-cache-2.11-cpan-995e09ba956 )