brewery
notashelf /
f6481b3c0120ff2f5a3abc60b19cd48bb97581ab

nixir

public

Import-resolving Nix IR plugin

Star0tarball
clone
ssh://git@git.notashelf.dev:33/notashelf/nixir.git

Commit f6481b3c0120

tarball

NotAShelf <raf@notashelf.dev> · 2026-04-24 20:13 UTC

unverified1 files changed+20-45
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 <raf@notashelf.dev>
Change-Id: I7d3fa038f743d02b9caae0979b79f5086a6a6964
diff --git a/src/irc/parser.cpp b/src/irc/parser.cppindex 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<Node>(VarNode(0, arg_name));--      std::vector<std::pair<std::string, std::shared_ptr<Node>>> 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<Node>(-            SelectNode(arg_var, std::make_shared<Node>(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<Node>(-              HasAttrNode(arg_var, std::make_shared<Node>(ConstStringNode(field.name))));-          auto if_node = std::make_shared<Node>(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<Node>(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<Node>(std::move(lambda));+      return std::make_shared<Node>(std::move(pattern));     }      // Not a lambda@@ -1399,7 +1376,7 @@ public:   }    std::shared_ptr<Node> parse_string_interp(const std::string& raw) {-    std::vector<std::shared_ptr<Node>> parts;+    std::vector<StringPart> 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<Node>(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<Node>(VarNode(0, "toString"));-        auto str_expr = std::make_shared<Node>(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<Node>(ConstStringNode(current_str)));+      parts.push_back(StringPart::make_literal(current_str));     } -    // Build concatenation tree+    // Return StringInterpolationNode     if (parts.empty()) {       return std::make_shared<Node>(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<Node>(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<Node>(ConstStringNode(parts[0].literal));     } -    return result;+    // Otherwise return StringInterpolationNode+    return std::make_shared<Node>(StringInterpolationNode(std::move(parts)));   } };