Fix expression concatenation in attribute values

- Lexer now emits a single token for the entire expression (e.g., "btn btn-" + type)
- Runtime evaluateExpression now handles + operator for string concatenation
- Added findConcatOperator to safely find operators outside quotes/brackets
This commit is contained in:
2026-01-17 19:35:49 +05:30
parent 8878b630cb
commit 05bbad64a4
4 changed files with 153 additions and 98 deletions

View File

@@ -880,6 +880,22 @@ pub const Runtime = struct {
fn evaluateExpression(self: *Runtime, expr: []const u8) Value {
const trimmed = std.mem.trim(u8, expr, " \t");
// Check for string concatenation with + operator
// e.g., "btn btn-" + type or "hello " + name + "!"
if (self.findConcatOperator(trimmed)) |op_pos| {
const left = std.mem.trim(u8, trimmed[0..op_pos], " \t");
const right = std.mem.trim(u8, trimmed[op_pos + 1 ..], " \t");
const left_val = self.evaluateExpression(left);
const right_val = self.evaluateExpression(right);
const left_str = left_val.toString(self.allocator) catch return Value.null;
const right_str = right_val.toString(self.allocator) catch return Value.null;
const result = std.fmt.allocPrint(self.allocator, "{s}{s}", .{ left_str, right_str }) catch return Value.null;
return Value.str(result);
}
// Check for string literal
if (trimmed.len >= 2) {
if ((trimmed[0] == '"' and trimmed[trimmed.len - 1] == '"') or
@@ -903,6 +919,44 @@ pub const Runtime = struct {
return self.lookupVariable(trimmed);
}
/// Finds the position of a + operator that's not inside quotes or brackets.
/// Returns null if no such operator exists.
fn findConcatOperator(_: *Runtime, expr: []const u8) ?usize {
var in_string: u8 = 0; // 0 = not in string, '"' or '\'' = in that type of string
var bracket_depth: usize = 0;
var paren_depth: usize = 0;
var brace_depth: usize = 0;
for (expr, 0..) |c, i| {
if (in_string != 0) {
if (c == in_string) {
in_string = 0;
} else if (c == '\\' and i + 1 < expr.len) {
// Skip escaped character - we'll handle it in next iteration
continue;
}
} else {
switch (c) {
'"', '\'' => in_string = c,
'[' => bracket_depth += 1,
']' => bracket_depth -|= 1,
'(' => paren_depth += 1,
')' => paren_depth -|= 1,
'{' => brace_depth += 1,
'}' => brace_depth -|= 1,
'+' => {
if (bracket_depth == 0 and paren_depth == 0 and brace_depth == 0) {
return i;
}
},
else => {},
}
}
}
return null;
}
/// Looks up a variable with dot notation support.
fn lookupVariable(self: *Runtime, path: []const u8) Value {
var parts = std.mem.splitScalar(u8, path, '.');