Skip to content

Commit 8df4284

Browse files
Add \F escape code (closes #235)
1 parent f88b8f6 commit 8df4284

13 files changed

Lines changed: 414 additions & 200 deletions

File tree

docs/SPECIFICATION.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,8 @@
248248
249249
- `\R` = raw mode toggle. In raw mode, all escape sequences (with the exception of `\R`) are treated as plain text until the next `\R` or the end of the string literal.
250250
251+
- `\F` = formatted mode toggle. When formatted mode is on, `{` and `}` delimit embedded expressions within the string literal. The embedded expression is parsed, evaluated, coerced to `STR`, and concatenated into the resulting string. Braces inside the embedded expression are matched by depth: the first `}` that closes the outer `{` terminates the interpolation. Nested braces inside the expression (e.g., block bodies, map literals) are consumed as part of the expression. Raw mode supersedes formatted mode: when raw mode is active, `{` and `}` are treated as literal characters even if formatted mode is also active.
252+
251253
If an invalid escape sequence (or a non-Hexadecimal value when one is expected by the escape sequence) is encountered in a `STR` literal, an error MUST be raised.
252254
253255
---

src/ast.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,15 @@ Expr *expr_wildcard(int line, int column) {
147147
return expr;
148148
}
149149

150+
Expr *expr_fmt_str(ExprList parts, int line, int column) {
151+
Expr *expr = ast_alloc(sizeof(Expr));
152+
expr->type = EXPR_FMT_STR;
153+
expr->line = line;
154+
expr->column = column;
155+
expr->as.fmt_str.parts = parts;
156+
return expr;
157+
}
158+
150159
Expr *expr_lambda(ParamList params, DeclType return_type, int return_base, Expr *return_template_expr, Stmt *body,
151160
int line, int column) {
152161
Expr *expr = ast_alloc(sizeof(Expr));
@@ -469,6 +478,9 @@ void free_expr(Expr *expr) {
469478
free_expr(expr->as.lambda.return_template_expr);
470479
free_stmt(expr->as.lambda.body);
471480
break;
481+
case EXPR_FMT_STR:
482+
free_expr_list(&expr->as.fmt_str.parts);
483+
break;
472484
case EXPR_IDENT:
473485
free(expr->as.ident);
474486
break;

src/ast.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ typedef enum {
4848
EXPR_INDEX,
4949
EXPR_RANGE,
5050
EXPR_WILDCARD,
51-
EXPR_LAMBDA
51+
EXPR_LAMBDA,
52+
EXPR_FMT_STR
5253
} ExprType;
5354

5455
typedef struct {
@@ -91,6 +92,9 @@ struct Expr {
9192
struct {
9293
Stmt *block;
9394
} async;
95+
struct {
96+
ExprList parts; // alternating STR parts and embedded expressions
97+
} fmt_str;
9498
struct {
9599
ParamList params;
96100
DeclType return_type;
@@ -244,6 +248,7 @@ Expr *expr_range(Expr *start, Expr *end, int line, int column);
244248
Expr *expr_wildcard(int line, int column);
245249
Expr *expr_lambda(ParamList params, DeclType return_type, int return_base, Expr *return_template_expr, Stmt *body,
246250
int line, int column);
251+
Expr *expr_fmt_str(ExprList parts, int line, int column);
247252
void expr_list_add(ExprList *list, Expr *expr);
248253

249254
Stmt *stmt_block(int line, int column);

src/builtins.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6448,6 +6448,12 @@ static Value builtin_str(Interpreter *interp, Value *args, int argc, Expr **arg_
64486448
RUNTIME_ERROR(interp, "STR expects BOOL, STR, INT, or FLT", line, col);
64496449
}
64506450

6451+
Value builtin_str_value(Interpreter *interp, Value v, int line, int col) {
6452+
Value args[1];
6453+
args[0] = v;
6454+
return builtin_str(interp, args, 1, NULL, NULL, line, col);
6455+
}
6456+
64516457
// BYTES(INT: n, endian = "big"):TNS
64526458
static Value builtin_bytes(Interpreter *interp, Value *args, int argc, Expr **arg_nodes, Env *env, int line, int col) {
64536459
(void)arg_nodes;

src/builtins.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,7 @@ void builtins_reset_dynamic(void);
4141

4242
const char *decl_type_name(DeclType dt);
4343

44+
// Convert any value to STR (BOOL, INT, FLT, STR). Returns a newly allocated STR value.
45+
Value builtin_str_value(Interpreter *interp, Value v, int line, int col);
46+
4447
#endif // BUILTINS_H

src/interpreter.c

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1246,6 +1246,64 @@ Value eval_expr(Interpreter *interp, Expr *expr, Env *env) {
12461246
case EXPR_STR:
12471247
return value_str(expr->as.str_value);
12481248

1249+
case EXPR_FMT_STR: {
1250+
size_t total_len = 0;
1251+
size_t part_count = expr->as.fmt_str.parts.count;
1252+
Value *temp_vals = safe_malloc(sizeof(Value) * part_count);
1253+
for (size_t i = 0; i < part_count; i++) {
1254+
Expr *part = expr->as.fmt_str.parts.items[i];
1255+
if (part->type == EXPR_STR) {
1256+
total_len += strlen(part->as.str_value ? part->as.str_value : "");
1257+
} else {
1258+
Value v = eval_expr(interp, part, env);
1259+
if (interp->error) {
1260+
for (size_t j = 0; j < i; j++) {
1261+
if (expr->as.fmt_str.parts.items[j]->type != EXPR_STR) {
1262+
value_free(temp_vals[j]);
1263+
}
1264+
}
1265+
free(temp_vals);
1266+
return value_null();
1267+
}
1268+
temp_vals[i] = builtin_str_value(interp, v, expr->line, expr->column);
1269+
if (interp->error) {
1270+
for (size_t j = 0; j < i; j++) {
1271+
if (expr->as.fmt_str.parts.items[j]->type != EXPR_STR) {
1272+
value_free(temp_vals[j]);
1273+
}
1274+
}
1275+
free(temp_vals);
1276+
return value_null();
1277+
}
1278+
total_len += strlen(temp_vals[i].as.s ? temp_vals[i].as.s : "");
1279+
}
1280+
}
1281+
1282+
char *result = safe_malloc(total_len + 1);
1283+
result[0] = '\0';
1284+
size_t pos = 0;
1285+
for (size_t i = 0; i < part_count; i++) {
1286+
Expr *part = expr->as.fmt_str.parts.items[i];
1287+
if (part->type == EXPR_STR) {
1288+
const char *s = part->as.str_value ? part->as.str_value : "";
1289+
size_t len = strlen(s);
1290+
memcpy(result + pos, s, len);
1291+
pos += len;
1292+
} else {
1293+
const char *s = temp_vals[i].as.s;
1294+
size_t len = strlen(s ? s : "");
1295+
memcpy(result + pos, s, len);
1296+
pos += len;
1297+
value_free(temp_vals[i]);
1298+
}
1299+
}
1300+
result[pos] = '\0';
1301+
free(temp_vals);
1302+
Value ret = value_str(result);
1303+
free(result);
1304+
return ret;
1305+
}
1306+
12491307
case EXPR_IDENT: {
12501308
Value v;
12511309
DeclType dtype;

0 commit comments

Comments
 (0)