diff --git a/src/irc/evaluator.cpp b/src/irc/evaluator.cpp index 8aa64c7..977ea85 100644 --- a/src/irc/evaluator.cpp +++ b/src/irc/evaluator.cpp @@ -21,10 +21,6 @@ struct IREnvironment { explicit IREnvironment(IREnvironment* p = nullptr) : parent(p), with_attrs(nullptr) {} - IREnvironment* push() { - return new IREnvironment(this); - } - void bind(Value* val) { bindings.push_back(val); } @@ -104,7 +100,7 @@ struct Evaluator::Impl { thunk->blackholed = true; eval_node(thunk->expr, *v, thunk->env); - thunks.erase(it); + thunks.erase(v); } void eval_node(const std::shared_ptr& node, Value& v, IREnvironment* env) { @@ -231,6 +227,8 @@ struct Evaluator::Impl { case BinaryOp::ADD: if (left->type() == nInt && right->type() == nInt) { v.mkInt((left->integer() + right->integer()).valueWrapping()); + } else if (left->type() == nString && right->type() == nString) { + v.mkString(std::string(left->c_str()) + std::string(right->c_str())); } else { state.error("type error in addition").debugThrow(); } @@ -268,6 +266,8 @@ struct Evaluator::Impl { case BinaryOp::LT: if (left->type() == nInt && right->type() == nInt) { v.mkBool(left->integer() < right->integer()); + } else if (left->type() == nString && right->type() == nString) { + v.mkBool(std::string(left->c_str()) < std::string(right->c_str())); } else { state.error("type error in comparison").debugThrow(); } @@ -275,6 +275,8 @@ struct Evaluator::Impl { case BinaryOp::GT: if (left->type() == nInt && right->type() == nInt) { v.mkBool(left->integer() > right->integer()); + } else if (left->type() == nString && right->type() == nString) { + v.mkBool(std::string(left->c_str()) > std::string(right->c_str())); } else { state.error("type error in comparison").debugThrow(); } @@ -282,6 +284,8 @@ struct Evaluator::Impl { case BinaryOp::LE: if (left->type() == nInt && right->type() == nInt) { v.mkBool(left->integer() <= right->integer()); + } else if (left->type() == nString && right->type() == nString) { + v.mkBool(std::string(left->c_str()) <= std::string(right->c_str())); } else { state.error("type error in comparison").debugThrow(); } @@ -289,16 +293,15 @@ struct Evaluator::Impl { case BinaryOp::GE: if (left->type() == nInt && right->type() == nInt) { v.mkBool(left->integer() >= right->integer()); + } else if (left->type() == nString && right->type() == nString) { + v.mkBool(std::string(left->c_str()) >= std::string(right->c_str())); } else { state.error("type error in comparison").debugThrow(); } break; case BinaryOp::CONCAT: - if (left->type() == nString && right->type() == nString) { - v.mkString(std::string(left->c_str()) + std::string(right->c_str())); - } else { - state.error("type error in concatenation").debugThrow(); - } + // ++ is list concatenation in Nix; string concat uses ADD (+) + state.error("list concatenation not yet implemented").debugThrow(); break; default: state.error("unknown binary operator").debugThrow(); @@ -410,7 +413,9 @@ struct Evaluator::Impl { auto attr = obj->attrs()->get(sym); if (attr) { - v = *attr->value; + Value* val = attr->value; + force(val); + v = *val; } else if (n->default_expr) { eval_node(*n->default_expr, v, env); } else { diff --git a/src/irc/parser.cpp b/src/irc/parser.cpp index 3ec6f86..8a47e2a 100644 --- a/src/irc/parser.cpp +++ b/src/irc/parser.cpp @@ -27,7 +27,10 @@ static std::string read_file(const std::string& path) { long size = ftell(f); fseek(f, 0, SEEK_SET); std::string content(size, '\0'); - fread(content.data(), 1, size, f); + if (fread(content.data(), 1, size, f) != static_cast(size)) { + fclose(f); + throw std::runtime_error("Failed to read file: " + path); + } fclose(f); return content; } @@ -761,7 +764,11 @@ public: } } - expect(Token::RBRACE); + if (!consume(Token::RBRACE)) { + // Not a lambda pattern, restore + pos = saved_pos; + return nullptr; + } if (!consume(Token::COLON)) { // Not a lambda, restore @@ -817,6 +824,7 @@ public: // 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)); } @@ -842,13 +850,33 @@ public: i += 2; // Skip ${ int depth = 1; size_t expr_start = i; + bool in_string = false; + char string_quote = 0; while (i < raw.size() && depth > 0) { - if (raw[i] == '{') depth++; - else if (raw[i] == '}') depth--; + if (!in_string) { + if (raw[i] == '"' || raw[i] == '\'') { + in_string = true; + string_quote = raw[i]; + } else if (raw[i] == '{') { + depth++; + } else if (raw[i] == '}') { + depth--; + } + } else { + if (raw[i] == string_quote && (i == 0 || raw[i-1] != '\\')) { + in_string = false; + } else if (raw[i] == '\\') { + i++; + } + } if (depth > 0) i++; } + if (depth > 0) { + throw std::runtime_error("unterminated ${ in string interpolation"); + } + // Parse the expression std::string expr_str = raw.substr(expr_start, i - expr_start); @@ -893,7 +921,8 @@ public: auto result = parts[0]; for (size_t j = 1; j < parts.size(); j++) { - result = std::make_shared(BinaryOpNode(BinaryOp::CONCAT, result, parts[j])); + // Use ADD (+) for string concatenation; CONCAT (++) is Nix list concatenation + result = std::make_shared(BinaryOpNode(BinaryOp::ADD, result, parts[j])); } return result;