various: fix string comparison, interpolation and ADD op. for strings

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: Ice1bfb5682ab48a967dc16f1378e23ae6a6a6964
This commit is contained in:
raf 2026-02-21 20:52:30 +03:00
commit 3441853eef
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
2 changed files with 50 additions and 16 deletions

View file

@ -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>& 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<EvalError>("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<EvalError>("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<EvalError>("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<EvalError>("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<EvalError>("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<EvalError>("type error in concatenation").debugThrow();
}
// ++ is list concatenation in Nix; string concat uses ADD (+)
state.error<EvalError>("list concatenation not yet implemented").debugThrow();
break;
default:
state.error<EvalError>("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 {

View file

@ -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_t>(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<Node>(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<Node>(BinaryOpNode(BinaryOp::CONCAT, result, parts[j]));
// Use ADD (+) for string concatenation; CONCAT (++) is Nix list concatenation
result = std::make_shared<Node>(BinaryOpNode(BinaryOp::ADD, result, parts[j]));
}
return result;