DMS-XS-Parser

 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.894 second using v1.01-cache-2.11-cpan-995e09ba956 )