string templates
This commit is contained in:
155
source/quickjs.c
155
source/quickjs.c
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user