clone
ssh://git@git.notashelf.dev:33/notashelf/nixir.gitCommit 95baf44a9c42
tarballunverified6 files changed+126-9
@@ -6,6 +6,7 @@ #include "nix/expr/eval.hh" #include "nix/expr/value.hh" #include "nix/util/error.hh"+#include "nix/util/url.hh" #include <stdexcept> #include <unordered_map>@@ -108,6 +109,8 @@ struct Evaluator::Impl { if (auto* n = node->get_if<ConstIntNode>()) { v.mkInt(n->value);+ } else if (auto* n = node->get_if<ConstFloatNode>()) {+ v.mkFloat(n->value); } else if (auto* n = node->get_if<ConstStringNode>()) { v.mkString(n->value); } else if (auto* n = node->get_if<ConstPathNode>()) {@@ -116,6 +119,11 @@ struct Evaluator::Impl { v.mkBool(n->value); } else if (auto* n = node->get_if<ConstNullNode>()) { // NOLINT(bugprone-branch-clone) v.mkNull();+ } else if (auto* n = node->get_if<ConstURINode>()) {+ // Parse and validate URI, then create string with URI context+ auto parsed = parseURL(n->value, true);+ // Store URI with context - the parsed URL string+ v.mkString(parsed.to_string(), nix::NixStringContext{}, state.mem); } else if (auto* n = node->get_if<VarNode>()) { Value* bound = env ? env->lookup(n->index) : nullptr; if (!bound && env && n->name.has_value()) {@@ -69,6 +69,8 @@ struct Token { STRING_INTERP, PATH, INT,+ FLOAT,+ URI, BOOL, LET, IN,@@ -216,14 +218,37 @@ public: } else if (c == '-') { // Check if it's a negative number or minus operator if (pos + 1 < input.size() && isdigit(input[pos + 1])) {- tokenize_int();+ // Check for negative float+ if (pos + 2 < input.size() && input[pos + 2] == '.') {+ tokenize_float();+ } else {+ tokenize_int();+ } } else { emit(TOKEN(MINUS)); } } else if (isdigit(c)) {- tokenize_int();- } else if (isalpha(c) || c == '_') {- tokenize_ident();+ // Check if it's a float (digit followed by '.')+ if (pos + 1 < input.size() && input[pos + 1] == '.') {+ tokenize_float();+ } else {+ tokenize_int();+ }+ } else if (isalpha(c)) {+ // Check if it's a URI (contains ://) - look ahead+ size_t lookahead = pos;+ while (lookahead < input.size() && (isalnum(input[lookahead]) || input[lookahead] == '_' ||+ input[lookahead] == '-' || input[lookahead] == '+' ||+ input[lookahead] == '.'))+ lookahead++;+ std::string potential_scheme = input.substr(pos, lookahead - pos);+ if (lookahead + 2 < input.size() && input[lookahead] == ':' && input[lookahead + 1] == '/' &&+ input[lookahead + 2] == '/') {+ // It's a URI, consume the whole thing+ tokenize_uri();+ } else {+ tokenize_ident();+ } } else { pos++; col++;@@ -339,12 +364,48 @@ private: col += num.size(); } + void tokenize_float() {+ size_t start = pos;+ if (input[pos] == '-')+ pos++;+ while (pos < input.size() && isdigit(input[pos]))+ pos++;+ if (pos < input.size() && input[pos] == '.') {+ pos++;+ while (pos < input.size() && isdigit(input[pos]))+ pos++;+ }+ std::string num = input.substr(start, pos - start);+ tokens.push_back({Token::FLOAT, num, line, col});+ col += num.size();+ }++ void tokenize_uri() {+ size_t start = pos;+ while (pos < input.size() && !isspace(input[pos]) && input[pos] != ')' && input[pos] != ']' &&+ input[pos] != ';') {+ pos++;+ }+ std::string uri = input.substr(start, pos - start);+ tokens.push_back({Token::URI, uri, line, col});+ col += uri.size();+ }+ void tokenize_ident() { size_t start = pos;- while (pos < input.size() && (isalnum(input[pos]) || input[pos] == '_' || input[pos] == '-'))+ while (pos < input.size() && (isalnum(input[pos]) || input[pos] == '_' || input[pos] == '-' ||+ input[pos] == '+' || input[pos] == '.')) pos++; std::string ident = input.substr(start, pos - start); + // Check if it's a URI (contains ://)+ size_t scheme_end = ident.find("://");+ if (scheme_end != std::string::npos && scheme_end > 0) {+ tokens.push_back({Token::URI, ident, line, col});+ col += ident.size();+ return;+ }+ Token::Type type = Token::IDENT; if (ident == "let") type = Token::LET;@@ -646,6 +707,16 @@ public: return std::make_shared<Node>(ConstIntNode(std::stoll(t.value))); } + if (t.type == Token::FLOAT) {+ advance();+ return std::make_shared<Node>(ConstFloatNode(std::stod(t.value)));+ }++ if (t.type == Token::URI) {+ advance();+ return std::make_shared<Node>(ConstURINode(t.value));+ }+ if (t.type == Token::STRING) { advance(); return std::make_shared<Node>(ConstStringNode(t.value));@@ -31,6 +31,8 @@ struct Serializer::Impl { NodeType get_node_type(const Node& node) { if (node.holds<ConstIntNode>()) return NodeType::CONST_INT;+ if (node.holds<ConstFloatNode>())+ return NodeType::CONST_FLOAT; if (node.holds<ConstStringNode>()) return NodeType::CONST_STRING; if (node.holds<ConstPathNode>())@@ -39,6 +41,8 @@ struct Serializer::Impl { return NodeType::CONST_BOOL; if (node.holds<ConstNullNode>()) return NodeType::CONST_NULL;+ if (node.holds<ConstURINode>())+ return NodeType::CONST_URI; if (node.holds<VarNode>()) return NodeType::VAR; if (node.holds<LambdaNode>())@@ -78,6 +82,11 @@ struct Serializer::Impl { if (auto* n = node.get_if<ConstIntNode>()) { write_u64(static_cast<uint64_t>(n->value));+ } else if (auto* n = node.get_if<ConstFloatNode>()) {+ double val = n->value;+ uint64_t bits = 0;+ std::memcpy(&bits, &val, sizeof(bits));+ write_u64(bits); } else if (auto* n = node.get_if<ConstStringNode>()) { write_string(n->value); } else if (auto* n = node.get_if<ConstPathNode>()) {@@ -86,6 +95,8 @@ struct Serializer::Impl { write_u8(n->value ? 1 : 0); } else if (auto* n = node.get_if<ConstNullNode>()) { // No data for null+ } else if (auto* n = node.get_if<ConstURINode>()) {+ write_string(n->value); } else if (auto* n = node.get_if<VarNode>()) { write_u32(n->index); } else if (auto* n = node.get_if<LambdaNode>()) {@@ -254,6 +265,12 @@ struct Deserializer::Impl { int64_t val = static_cast<int64_t>(read_u64()); return std::make_shared<Node>(ConstIntNode(val, line)); }+ case NodeType::CONST_FLOAT: {+ uint64_t bits = read_u64();+ double val = 0.0;+ std::memcpy(&val, &bits, sizeof(val));+ return std::make_shared<Node>(ConstFloatNode(val, line));+ } case NodeType::CONST_STRING: { std::string val = read_string(); return std::make_shared<Node>(ConstStringNode(val, line));@@ -268,6 +285,10 @@ struct Deserializer::Impl { } case NodeType::CONST_NULL: return std::make_shared<Node>(ConstNullNode(line));+ case NodeType::CONST_URI: {+ std::string val = read_string();+ return std::make_shared<Node>(ConstURINode(val, line));+ } case NodeType::VAR: { uint32_t index = read_u32(); return std::make_shared<Node>(VarNode(index, "", line));@@ -19,10 +19,12 @@ constexpr uint32_t IR_VERSION = 2; enum class NodeType : uint8_t { CONST_INT = 0x01,+ CONST_FLOAT = 0x06, CONST_STRING = 0x02, CONST_PATH = 0x03, CONST_BOOL = 0x04, CONST_NULL = 0x05,+ CONST_URI = 0x07, VAR = 0x10, LAMBDA = 0x20, APP = 0x21,@@ -77,6 +79,18 @@ struct ConstNullNode { ConstNullNode(uint32_t l = 0) : line(l) {} }; +struct ConstFloatNode {+ double value;+ uint32_t line = 0;+ ConstFloatNode(double v = 0.0, uint32_t l = 0) : value(v), line(l) {}+};++struct ConstURINode {+ std::string value;+ uint32_t line = 0;+ ConstURINode(std::string v = "", uint32_t l = 0) : value(std::move(v)), line(l) {}+};+ struct VarNode { uint32_t index = 0; std::optional<std::string> name;@@ -189,10 +203,11 @@ struct ForceNode { // Node wraps a variant for type-safe AST class Node { public:- using Variant = std::variant<ConstIntNode, ConstStringNode, ConstPathNode, ConstBoolNode,- ConstNullNode, VarNode, LambdaNode, AppNode, BinaryOpNode,- UnaryOpNode, AttrsetNode, SelectNode, HasAttrNode, WithNode, IfNode,- LetNode, LetRecNode, AssertNode, ThunkNode, ForceNode>;+ using Variant = std::variant<ConstIntNode, ConstFloatNode, ConstStringNode, ConstPathNode,+ ConstBoolNode, ConstNullNode, ConstURINode, VarNode, LambdaNode,+ AppNode, BinaryOpNode, UnaryOpNode, AttrsetNode, SelectNode,+ HasAttrNode, WithNode, IfNode, LetNode, LetRecNode, AssertNode,+ ThunkNode, ForceNode>; Variant data; @@ -0,0 +1 @@+1.5@@ -0,0 +1 @@+https://example.com/path?query=1#frag