clone
ssh://git@git.notashelf.dev:33/notashelf/nixir.gitCommit 584d84542ecc
tarballunverified1 files changed+117-0
@@ -74,6 +74,10 @@ struct Serializer::Impl { return NodeType::LETREC; if (node.holds<AssertNode>()) return NodeType::ASSERT;+ if (node.holds<LambdaPatternNode>())+ return NodeType::LAMBDA_PATTERN;+ if (node.holds<StringInterpolationNode>())+ return NodeType::STRING_INTERPOLATION; return NodeType::ERROR; } @@ -199,6 +203,53 @@ struct Serializer::Impl { write_node(*n->cond); if (n->body) write_node(*n->body);+ } else if (auto* n = node.get_if<LambdaPatternNode>()) {+ // Required fields+ write_u32(n->required_fields.size());+ for (const auto& field : n->required_fields) {+ write_string(field.name);+ write_u8(0); // No default+ }++ // Optional fields+ write_u32(n->optional_fields.size());+ for (const auto& field : n->optional_fields) {+ write_string(field.name);+ if (field.default_value && *field.default_value) {+ write_u8(1);+ write_node(**field.default_value);+ } else {+ write_u8(0);+ }+ }++ // At-binding+ if (n->at_binding) {+ write_u8(1);+ write_string(*n->at_binding);+ } else {+ write_u8(0);+ }++ // Allow extra+ write_u8(n->allow_extra ? 1 : 0);++ // Body+ if (n->body)+ write_node(*n->body);+ } else if (auto* n = node.get_if<StringInterpolationNode>()) {+ write_u32(n->parts.size());++ for (const auto& part : n->parts) {+ write_u8(static_cast<uint8_t>(part.type));++ if (part.type == StringPart::Type::LITERAL) {+ write_string(part.literal);+ } else { // EXPR+ if (part.expr)+ write_node(*part.expr);+ }+ } } } };@@ -430,6 +481,72 @@ struct Deserializer::Impl { auto body = read_node(); return std::make_shared<Node>(AssertNode(cond, body, line)); }+ case NodeType::LAMBDA_PATTERN: {+ // Read required fields+ uint32_t num_required = read_u32();+ std::vector<PatternField> required_fields;+ required_fields.reserve(num_required);+ for (uint32_t i = 0; i < num_required; i++) {+ std::string name = read_string();+ read_u8(); // Discard has_default (always 0)+ required_fields.emplace_back(name, std::nullopt);+ }++ // Read optional fields+ uint32_t num_optional = read_u32();+ std::vector<PatternField> optional_fields;+ optional_fields.reserve(num_optional);+ for (uint32_t i = 0; i < num_optional; i++) {+ std::string name = read_string();+ uint8_t has_default = read_u8();+ std::optional<std::shared_ptr<Node>> default_val;+ if (has_default) {+ default_val = read_node();+ }+ optional_fields.emplace_back(name, default_val);+ }++ // Read at-binding+ std::optional<std::string> at_binding;+ if (read_u8()) {+ at_binding = read_string();+ }++ // Read allow_extra+ bool allow_extra = read_u8() != 0;++ // Read body+ auto body = read_node();++ // Construct node+ LambdaPatternNode lambda_pattern(body, line);+ lambda_pattern.required_fields = std::move(required_fields);+ lambda_pattern.optional_fields = std::move(optional_fields);+ lambda_pattern.at_binding = at_binding;+ lambda_pattern.allow_extra = allow_extra;++ return std::make_shared<Node>(std::move(lambda_pattern));+ }+ case NodeType::STRING_INTERPOLATION: {+ uint32_t num_parts = read_u32();+ std::vector<StringPart> parts;+ parts.reserve(num_parts);++ for (uint32_t i = 0; i < num_parts; i++) {+ uint8_t type_byte = read_u8();+ StringPart::Type type = static_cast<StringPart::Type>(type_byte);++ if (type == StringPart::Type::LITERAL) {+ std::string literal = read_string();+ parts.push_back(StringPart::make_literal(std::move(literal)));+ } else { // EXPR+ auto expr = read_node();+ parts.push_back(StringPart::make_expr(expr));+ }+ }++ return std::make_shared<Node>(StringInterpolationNode(std::move(parts), line));+ } default: throw std::runtime_error("Unknown node type in IR"); }