brewery
notashelf /
95baf44a9c4216e7ff7e1243195cf91f25853dd3

nixir

public

Import-resolving Nix IR plugin

Star0tarball
clone
ssh://git@git.notashelf.dev:33/notashelf/nixir.git

Commit 95baf44a9c42

tarball

NotAShelf <raf@notashelf.dev> · 2026-04-24 20:12 UTC

unverified6 files changed+126-9
irc: add Float and URI literal support

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I40c59d94f650e7b9e68f77598492d7ab6a6a6964
diff --git a/src/irc/evaluator.cpp b/src/irc/evaluator.cppindex dfd6eb1..d30862c 100644--- a/src/irc/evaluator.cpp+++ b/src/irc/evaluator.cpp@@ -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()) {diff --git a/src/irc/parser.cpp b/src/irc/parser.cppindex e72d034..bead1e5 100644--- a/src/irc/parser.cpp+++ b/src/irc/parser.cpp@@ -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));diff --git a/src/irc/serializer.cpp b/src/irc/serializer.cppindex fff2208..509735e 100644--- a/src/irc/serializer.cpp+++ b/src/irc/serializer.cpp@@ -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));diff --git a/src/irc/types.h b/src/irc/types.hindex f52db8d..5fb18f9 100644--- a/src/irc/types.h+++ b/src/irc/types.h@@ -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; diff --git a/tests/float_test.nix b/tests/float_test.nixnew file mode 100644index 0000000..c239c60--- /dev/null+++ b/tests/float_test.nix@@ -0,0 +1 @@+1.5diff --git a/tests/uri_test.nix b/tests/uri_test.nixnew file mode 100644index 0000000..28ac1e2--- /dev/null+++ b/tests/uri_test.nix@@ -0,0 +1 @@+https://example.com/path?query=1#frag