string templates

This commit is contained in:
2026-02-05 19:34:06 -06:00
parent 19ba184fec
commit 20f14abd17

View File

@@ -16907,6 +16907,10 @@ static __exception int js_parse_function_decl2 (JSParseState *s,
js_parse_error (s, "invalid number of arguments for getter or setter");
goto fail;
}
if (fd->arg_count > 4) {
js_parse_error (s, "functions cannot have more than 4 parameters");
goto fail;
}
}
/* Explicit arity: defined_arg_count == arg_count always */
fd->defined_arg_count = fd->arg_count;
@@ -28099,28 +28103,39 @@ redo:
goto def_token;
}
break;
case '`':
/* simplified template handling - just find the end */
{
const uint8_t *start = p;
p++;
while (p < s->buf_end && *p != '`') {
if (*p == '\\' && p + 1 < s->buf_end) p++;
if (*p == '$' && p + 1 < s->buf_end && p[1] == '{') {
/* template with expressions - not fully supported in AST yet */
case '`': {
const uint8_t *start = p;
p++;
while (p < s->buf_end && *p != '`') {
if (*p == '\\' && p + 1 < s->buf_end) { p += 2; continue; }
if (*p == '$' && p + 1 < s->buf_end && p[1] == '{') {
p += 2;
int depth = 1;
while (p < s->buf_end && depth > 0) {
if (*p == '{') { depth++; p++; }
else if (*p == '}') { depth--; p++; }
else if (*p == '\'' || *p == '"' || *p == '`') {
int q = *p; p++;
while (p < s->buf_end && *p != q) {
if (*p == '\\' && p + 1 < s->buf_end) p++;
p++;
}
if (p < s->buf_end) p++;
} else { p++; }
}
p++;
}
if (p >= s->buf_end) {
ast_error (s, start, "unterminated template literal");
s->buf_ptr = p;
goto redo;
continue;
}
p++;
s->token_val = TOK_TEMPLATE;
s->token_u.str.str = JS_NewStringLen (s->ctx, (const char *)(start + 1), p - start - 2);
}
break;
if (p >= s->buf_end) {
ast_error (s, start, "unterminated template literal");
s->buf_ptr = p;
goto redo;
}
p++;
s->token_val = TOK_TEMPLATE;
s->token_u.str.str = JS_NewStringLen (s->ctx, (const char *)(start + 1), p - start - 2);
} break;
case '\'':
case '\"': {
const uint8_t *start = p;
@@ -28412,9 +28427,23 @@ static int tokenize_next (ASTParseState *s) {
const uint8_t *start = p;
p++;
while (p < s->buf_end && *p != '`') {
if (*p == '\\' && p + 1 < s->buf_end) p++;
if (*p == '\\' && p + 1 < s->buf_end) { p += 2; continue; }
if (*p == '$' && p + 1 < s->buf_end && p[1] == '{') {
/* template with expressions - not fully supported in AST yet */
p += 2;
int depth = 1;
while (p < s->buf_end && depth > 0) {
if (*p == '{') { depth++; p++; }
else if (*p == '}') { depth--; p++; }
else if (*p == '\'' || *p == '"' || *p == '`') {
int q = *p; p++;
while (p < s->buf_end && *p != q) {
if (*p == '\\' && p + 1 < s->buf_end) p++;
p++;
}
if (p < s->buf_end) p++;
} else { p++; }
}
continue;
}
p++;
}
@@ -28734,12 +28763,78 @@ static cJSON *ast_parse_primary (ASTParseState *s) {
} break;
case TOK_TEMPLATE: {
node = ast_node (s, "text", start);
const char *str = JS_ToCString (s->ctx, s->token_u.str.str);
cJSON_AddStringToObject (node, "value", str ? str : "");
JS_FreeCString (s->ctx, str);
ast_node_end (s, node, s->buf_ptr);
ast_next_token (s);
const uint8_t *tmpl_start = start + 1;
const uint8_t *tmpl_end = s->buf_ptr - 1;
const uint8_t *saved_end = s->buf_ptr;
/* Quick scan for ${ */
BOOL has_expr = FALSE;
for (const uint8_t *sc = tmpl_start; sc < tmpl_end; sc++) {
if (*sc == '\\' && sc + 1 < tmpl_end) { sc++; continue; }
if (*sc == '$' && sc + 1 < tmpl_end && sc[1] == '{') {
has_expr = TRUE; break;
}
}
if (!has_expr) {
/* Simple template — unchanged behavior */
node = ast_node (s, "text", start);
const char *str = JS_ToCString (s->ctx, s->token_u.str.str);
cJSON_AddStringToObject (node, "value", str ? str : "");
JS_FreeCString (s->ctx, str);
ast_node_end (s, node, s->buf_ptr);
ast_next_token (s);
} else {
node = ast_node (s, "text literal", start);
cJSON *list = cJSON_AddArrayToObject (node, "list");
/* Build format string with {N} placeholders */
int cap = 256;
char *fmt = sys_malloc (cap);
int len = 0;
int idx = 0;
const uint8_t *p = tmpl_start;
while (p < tmpl_end) {
if (*p == '\\' && p + 1 < tmpl_end) {
p++; /* skip backslash */
if (len + 1 >= cap) { cap *= 2; fmt = sys_realloc (fmt, cap); }
fmt[len++] = *p++;
continue;
}
if (*p == '$' && p + 1 < tmpl_end && p[1] == '{') {
/* Add {N} placeholder */
if (len + 12 >= cap) { cap *= 2; fmt = sys_realloc (fmt, cap); }
len += snprintf (fmt + len, cap - len, "{%d}", idx++);
p += 2; /* skip ${ */
/* Parse expression: redirect buf_ptr, tokenize, parse */
s->buf_ptr = p;
ast_next_token (s);
cJSON *expr = ast_parse_assign_expr (s);
if (expr) cJSON_AddItemToArray (list, expr);
/* After expression, token should be '}' */
if (s->token_val == '}') {
p = s->buf_ptr;
} else {
ast_error (s, p, "expected '}' after template expression");
p = s->buf_ptr;
}
continue;
}
if (len + 1 >= cap) { cap *= 2; fmt = sys_realloc (fmt, cap); }
fmt[len++] = *p++;
}
fmt[len] = '\0';
cJSON_AddStringToObject (node, "value", fmt);
sys_free (fmt);
s->buf_ptr = saved_end;
ast_node_end (s, node, saved_end);
ast_next_token (s);
}
} break;
case TOK_IDENT: {
@@ -29356,6 +29451,10 @@ static cJSON *ast_parse_function_inner (ASTParseState *s, BOOL is_expr) {
ast_error (s, s->token_ptr, "expected '(' after function name");
}
if (cJSON_GetArraySize (params) > 4) {
ast_error (s, s->token_ptr, "functions cannot have more than 4 parameters");
}
/* Body */
if (s->token_val == '{') {
ast_next_token (s);
@@ -29422,6 +29521,10 @@ static cJSON *ast_parse_arrow_function (ASTParseState *s) {
if (s->token_val == ')') ast_next_token (s);
}
if (cJSON_GetArraySize (params) > 4) {
ast_error (s, s->token_ptr, "functions cannot have more than 4 parameters");
}
/* Arrow token */
if (s->token_val != TOK_ARROW) {
ast_error (s, s->token_ptr, "expected '=>' in arrow function");