From f6481b3c0120ff2f5a3abc60b19cd48bb97581ab Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Mon, 23 Feb 2026 21:22:06 +0300 Subject: [PATCH] irc/parser: use explicit IR nodes for patterns and interpolation Replaces lambda pattern desugaring with direct `LambdaPatternNode` generation, which: - Separates required and optional fields in pattern structure - Preserves @-binding and ellipsis information in IR - Remove like 50 lines of Let-binding desugaring logic and replace string interpolation concatenation tree with `StringInterpolationNode` to: - Use `StringPart::make_literal/make_expr` for cleaner representation - Optimize single-literal strings to `ConstStringNode` - Remove `toString` builtin wrapper generation Signed-off-by: NotAShelf Change-Id: I7d3fa038f743d02b9caae0979b79f5086a6a6964 --- src/irc/parser.cpp | 65 ++++++++++++++-------------------------------- 1 file changed, 20 insertions(+), 45 deletions(-) diff --git a/src/irc/parser.cpp b/src/irc/parser.cpp index 2bb31ed..8a37a22 100644 --- a/src/irc/parser.cpp +++ b/src/irc/parser.cpp @@ -1352,45 +1352,22 @@ public: // Parse body auto body = parse_expr(); - // Desugar pattern to lambda with let bindings - // { a, b ? x }: body → arg: let a = arg.a; b = if arg ? a then arg.a else x; in body - - std::string arg_name = named_arg.value_or("_arg"); - auto arg_var = std::make_shared(VarNode(0, arg_name)); - - std::vector>> bindings; + // Create LambdaPatternNode instead of desugaring + auto pattern = LambdaPatternNode(body); + pattern.allow_extra = has_ellipsis; + pattern.at_binding = named_arg; + // Separate required and optional fields for (const auto& field : fields) { - // Create arg.field selection - auto select = std::make_shared( - SelectNode(arg_var, std::make_shared(ConstStringNode(field.name)))); - + PatternField pf(field.name, field.default_val); if (field.default_val) { - // if arg ? field then arg.field else default - auto has_attr = std::make_shared( - HasAttrNode(arg_var, std::make_shared(ConstStringNode(field.name)))); - auto if_node = std::make_shared(IfNode(has_attr, select, *field.default_val)); - bindings.push_back({field.name, if_node}); + pattern.optional_fields.push_back(std::move(pf)); } else { - bindings.push_back({field.name, select}); + pattern.required_fields.push_back(std::move(pf)); } } - // If named pattern, also bind the argument name - if (named_arg) { - bindings.push_back({*named_arg, arg_var}); - } - - // Create let expression - auto let = LetNode(body); - let.bindings = std::move(bindings); - auto let_node = std::make_shared(std::move(let)); - - // Create lambda - auto lambda = LambdaNode(1, let_node); - lambda.param_name = arg_name; - lambda.strict_pattern = !has_ellipsis; - return std::make_shared(std::move(lambda)); + return std::make_shared(std::move(pattern)); } // Not a lambda @@ -1399,7 +1376,7 @@ public: } std::shared_ptr parse_string_interp(const std::string& raw) { - std::vector> parts; + std::vector parts; size_t i = 0; std::string current_str; @@ -1407,7 +1384,7 @@ public: if (raw[i] == '$' && i + 1 < raw.size() && raw[i + 1] == '{') { // Save current string part if any if (!current_str.empty()) { - parts.push_back(std::make_shared(ConstStringNode(current_str))); + parts.push_back(StringPart::make_literal(current_str)); current_str.clear(); } @@ -1463,10 +1440,8 @@ public: tokens = saved_tokens; pos = saved_pos; - // Convert to string using toString builtin - auto to_string = std::make_shared(VarNode(0, "toString")); - auto str_expr = std::make_shared(AppNode(to_string, expr)); - parts.push_back(str_expr); + // Add expression part (will be coerced to string during evaluation) + parts.push_back(StringPart::make_expr(expr)); i++; // Skip } } else { @@ -1477,21 +1452,21 @@ public: // Add remaining string part if (!current_str.empty()) { - parts.push_back(std::make_shared(ConstStringNode(current_str))); + parts.push_back(StringPart::make_literal(current_str)); } - // Build concatenation tree + // Return StringInterpolationNode if (parts.empty()) { return std::make_shared(ConstStringNode("")); } - auto result = parts[0]; - for (size_t j = 1; j < parts.size(); j++) { - // Use ADD (+) for string concatenation; CONCAT (++) is Nix list concatenation - result = std::make_shared(BinaryOpNode(BinaryOp::ADD, result, parts[j])); + // If only one literal part, return it directly as ConstStringNode + if (parts.size() == 1 && parts[0].type == StringPart::Type::LITERAL) { + return std::make_shared(ConstStringNode(parts[0].literal)); } - return result; + // Otherwise return StringInterpolationNode + return std::make_shared(StringInterpolationNode(std::move(parts))); } };