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:
parent
da9be4b014
commit
3441853eef
2 changed files with 50 additions and 16 deletions
|
|
@ -21,10 +21,6 @@ struct IREnvironment {
|
||||||
|
|
||||||
explicit IREnvironment(IREnvironment* p = nullptr) : parent(p), with_attrs(nullptr) {}
|
explicit IREnvironment(IREnvironment* p = nullptr) : parent(p), with_attrs(nullptr) {}
|
||||||
|
|
||||||
IREnvironment* push() {
|
|
||||||
return new IREnvironment(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void bind(Value* val) {
|
void bind(Value* val) {
|
||||||
bindings.push_back(val);
|
bindings.push_back(val);
|
||||||
}
|
}
|
||||||
|
|
@ -104,7 +100,7 @@ struct Evaluator::Impl {
|
||||||
|
|
||||||
thunk->blackholed = true;
|
thunk->blackholed = true;
|
||||||
eval_node(thunk->expr, *v, thunk->env);
|
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) {
|
void eval_node(const std::shared_ptr<Node>& node, Value& v, IREnvironment* env) {
|
||||||
|
|
@ -231,6 +227,8 @@ struct Evaluator::Impl {
|
||||||
case BinaryOp::ADD:
|
case BinaryOp::ADD:
|
||||||
if (left->type() == nInt && right->type() == nInt) {
|
if (left->type() == nInt && right->type() == nInt) {
|
||||||
v.mkInt((left->integer() + right->integer()).valueWrapping());
|
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 {
|
} else {
|
||||||
state.error<EvalError>("type error in addition").debugThrow();
|
state.error<EvalError>("type error in addition").debugThrow();
|
||||||
}
|
}
|
||||||
|
|
@ -268,6 +266,8 @@ struct Evaluator::Impl {
|
||||||
case BinaryOp::LT:
|
case BinaryOp::LT:
|
||||||
if (left->type() == nInt && right->type() == nInt) {
|
if (left->type() == nInt && right->type() == nInt) {
|
||||||
v.mkBool(left->integer() < right->integer());
|
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 {
|
} else {
|
||||||
state.error<EvalError>("type error in comparison").debugThrow();
|
state.error<EvalError>("type error in comparison").debugThrow();
|
||||||
}
|
}
|
||||||
|
|
@ -275,6 +275,8 @@ struct Evaluator::Impl {
|
||||||
case BinaryOp::GT:
|
case BinaryOp::GT:
|
||||||
if (left->type() == nInt && right->type() == nInt) {
|
if (left->type() == nInt && right->type() == nInt) {
|
||||||
v.mkBool(left->integer() > right->integer());
|
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 {
|
} else {
|
||||||
state.error<EvalError>("type error in comparison").debugThrow();
|
state.error<EvalError>("type error in comparison").debugThrow();
|
||||||
}
|
}
|
||||||
|
|
@ -282,6 +284,8 @@ struct Evaluator::Impl {
|
||||||
case BinaryOp::LE:
|
case BinaryOp::LE:
|
||||||
if (left->type() == nInt && right->type() == nInt) {
|
if (left->type() == nInt && right->type() == nInt) {
|
||||||
v.mkBool(left->integer() <= right->integer());
|
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 {
|
} else {
|
||||||
state.error<EvalError>("type error in comparison").debugThrow();
|
state.error<EvalError>("type error in comparison").debugThrow();
|
||||||
}
|
}
|
||||||
|
|
@ -289,16 +293,15 @@ struct Evaluator::Impl {
|
||||||
case BinaryOp::GE:
|
case BinaryOp::GE:
|
||||||
if (left->type() == nInt && right->type() == nInt) {
|
if (left->type() == nInt && right->type() == nInt) {
|
||||||
v.mkBool(left->integer() >= right->integer());
|
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 {
|
} else {
|
||||||
state.error<EvalError>("type error in comparison").debugThrow();
|
state.error<EvalError>("type error in comparison").debugThrow();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case BinaryOp::CONCAT:
|
case BinaryOp::CONCAT:
|
||||||
if (left->type() == nString && right->type() == nString) {
|
// ++ is list concatenation in Nix; string concat uses ADD (+)
|
||||||
v.mkString(std::string(left->c_str()) + std::string(right->c_str()));
|
state.error<EvalError>("list concatenation not yet implemented").debugThrow();
|
||||||
} else {
|
|
||||||
state.error<EvalError>("type error in concatenation").debugThrow();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
state.error<EvalError>("unknown binary operator").debugThrow();
|
state.error<EvalError>("unknown binary operator").debugThrow();
|
||||||
|
|
@ -410,7 +413,9 @@ struct Evaluator::Impl {
|
||||||
auto attr = obj->attrs()->get(sym);
|
auto attr = obj->attrs()->get(sym);
|
||||||
|
|
||||||
if (attr) {
|
if (attr) {
|
||||||
v = *attr->value;
|
Value* val = attr->value;
|
||||||
|
force(val);
|
||||||
|
v = *val;
|
||||||
} else if (n->default_expr) {
|
} else if (n->default_expr) {
|
||||||
eval_node(*n->default_expr, v, env);
|
eval_node(*n->default_expr, v, env);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,10 @@ static std::string read_file(const std::string& path) {
|
||||||
long size = ftell(f);
|
long size = ftell(f);
|
||||||
fseek(f, 0, SEEK_SET);
|
fseek(f, 0, SEEK_SET);
|
||||||
std::string content(size, '\0');
|
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);
|
fclose(f);
|
||||||
return content;
|
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)) {
|
if (!consume(Token::COLON)) {
|
||||||
// Not a lambda, restore
|
// Not a lambda, restore
|
||||||
|
|
@ -817,6 +824,7 @@ public:
|
||||||
// Create lambda
|
// Create lambda
|
||||||
auto lambda = LambdaNode(1, let_node);
|
auto lambda = LambdaNode(1, let_node);
|
||||||
lambda.param_name = arg_name;
|
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(lambda));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -842,13 +850,33 @@ public:
|
||||||
i += 2; // Skip ${
|
i += 2; // Skip ${
|
||||||
int depth = 1;
|
int depth = 1;
|
||||||
size_t expr_start = i;
|
size_t expr_start = i;
|
||||||
|
bool in_string = false;
|
||||||
|
char string_quote = 0;
|
||||||
|
|
||||||
while (i < raw.size() && depth > 0) {
|
while (i < raw.size() && depth > 0) {
|
||||||
if (raw[i] == '{') depth++;
|
if (!in_string) {
|
||||||
else if (raw[i] == '}') depth--;
|
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) i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (depth > 0) {
|
||||||
|
throw std::runtime_error("unterminated ${ in string interpolation");
|
||||||
|
}
|
||||||
|
|
||||||
// Parse the expression
|
// Parse the expression
|
||||||
std::string expr_str = raw.substr(expr_start, i - expr_start);
|
std::string expr_str = raw.substr(expr_start, i - expr_start);
|
||||||
|
|
||||||
|
|
@ -893,7 +921,8 @@ public:
|
||||||
|
|
||||||
auto result = parts[0];
|
auto result = parts[0];
|
||||||
for (size_t j = 1; j < parts.size(); j++) {
|
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;
|
return result;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue