From 38c13de01dfb8d11b08dd844d9bf2ba2ed97ab55 Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Sun, 22 Feb 2026 00:41:19 +0300 Subject: [PATCH] irc: add Float and URI literal support Signed-off-by: NotAShelf Change-Id: I40c59d94f650e7b9e68f77598492d7ab6a6a6964 --- src/irc/evaluator.cpp | 8 +++++ src/irc/parser.cpp | 81 +++++++++++++++++++++++++++++++++++++++--- src/irc/serializer.cpp | 21 +++++++++++ src/irc/types.h | 23 +++++++++--- tests/float_test.nix | 1 + tests/uri_test.nix | 1 + 6 files changed, 126 insertions(+), 9 deletions(-) create mode 100644 tests/float_test.nix create mode 100644 tests/uri_test.nix diff --git a/src/irc/evaluator.cpp b/src/irc/evaluator.cpp index 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 #include @@ -108,6 +109,8 @@ struct Evaluator::Impl { if (auto* n = node->get_if()) { v.mkInt(n->value); + } else if (auto* n = node->get_if()) { + v.mkFloat(n->value); } else if (auto* n = node->get_if()) { v.mkString(n->value); } else if (auto* n = node->get_if()) { @@ -116,6 +119,11 @@ struct Evaluator::Impl { v.mkBool(n->value); } else if (auto* n = node->get_if()) { // NOLINT(bugprone-branch-clone) v.mkNull(); + } else if (auto* n = node->get_if()) { + // 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()) { 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.cpp index 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(ConstIntNode(std::stoll(t.value))); } + if (t.type == Token::FLOAT) { + advance(); + return std::make_shared(ConstFloatNode(std::stod(t.value))); + } + + if (t.type == Token::URI) { + advance(); + return std::make_shared(ConstURINode(t.value)); + } + if (t.type == Token::STRING) { advance(); return std::make_shared(ConstStringNode(t.value)); diff --git a/src/irc/serializer.cpp b/src/irc/serializer.cpp index 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()) return NodeType::CONST_INT; + if (node.holds()) + return NodeType::CONST_FLOAT; if (node.holds()) return NodeType::CONST_STRING; if (node.holds()) @@ -39,6 +41,8 @@ struct Serializer::Impl { return NodeType::CONST_BOOL; if (node.holds()) return NodeType::CONST_NULL; + if (node.holds()) + return NodeType::CONST_URI; if (node.holds()) return NodeType::VAR; if (node.holds()) @@ -78,6 +82,11 @@ struct Serializer::Impl { if (auto* n = node.get_if()) { write_u64(static_cast(n->value)); + } else if (auto* n = node.get_if()) { + double val = n->value; + uint64_t bits = 0; + std::memcpy(&bits, &val, sizeof(bits)); + write_u64(bits); } else if (auto* n = node.get_if()) { write_string(n->value); } else if (auto* n = node.get_if()) { @@ -86,6 +95,8 @@ struct Serializer::Impl { write_u8(n->value ? 1 : 0); } else if (auto* n = node.get_if()) { // No data for null + } else if (auto* n = node.get_if()) { + write_string(n->value); } else if (auto* n = node.get_if()) { write_u32(n->index); } else if (auto* n = node.get_if()) { @@ -254,6 +265,12 @@ struct Deserializer::Impl { int64_t val = static_cast(read_u64()); return std::make_shared(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(ConstFloatNode(val, line)); + } case NodeType::CONST_STRING: { std::string val = read_string(); return std::make_shared(ConstStringNode(val, line)); @@ -268,6 +285,10 @@ struct Deserializer::Impl { } case NodeType::CONST_NULL: return std::make_shared(ConstNullNode(line)); + case NodeType::CONST_URI: { + std::string val = read_string(); + return std::make_shared(ConstURINode(val, line)); + } case NodeType::VAR: { uint32_t index = read_u32(); return std::make_shared(VarNode(index, "", line)); diff --git a/src/irc/types.h b/src/irc/types.h index 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 name; @@ -189,10 +203,11 @@ struct ForceNode { // Node wraps a variant for type-safe AST class Node { public: - using Variant = std::variant; + using Variant = std::variant; Variant data; diff --git a/tests/float_test.nix b/tests/float_test.nix new file mode 100644 index 0000000..c239c60 --- /dev/null +++ b/tests/float_test.nix @@ -0,0 +1 @@ +1.5 diff --git a/tests/uri_test.nix b/tests/uri_test.nix new file mode 100644 index 0000000..28ac1e2 --- /dev/null +++ b/tests/uri_test.nix @@ -0,0 +1 @@ +https://example.com/path?query=1#frag