On Thursday, 21 May 2026 at 18:16:00 UTC, mitgedanken wrote:
On Sunday, 3 May 2026 at 14:57:51 UTC, 0xEAB wrote:
On 4/25/26 23:49, Dejan Lekic wrote:
https://rpa.st/HS26E

Please find attached my comment.


It gives me a **404** now.

Apologies, I was too quick paste the code there and forgot to change the expiry setting...

Here is the improved code that should work:

```d
module mitgedanken.parser;

import std.typecons: Tuple;
import std.container: Array;
import std.conv: to;
import std.traits: isSomeString;
import std.string;

struct Symbol {
   string str;

@property ulong length() const @safe pure nothrow => this.str.length; @property bool empty() const @safe pure nothrow => (this.str.length == 0);

   static Symbol asEmpty() @safe pure nothrow
      => Symbol("");

   string toString() const @safe pure nothrow
      => this.str;

   ulong opDollar()
      => this.length;

   T opCast(T : string)() const
      => this.str;

   auto opAssign(T)(T value)
         if (isSomeString!T) {
      this.str = value;
      return this;
   }

   auto opBinary(string op : "~")(string rhs) {
      Symbol symbol = Symbol(this.str ~ rhs);
      return symbol;
   }

   auto opOpAssign(string op = "~=", T)(T value)
         if (isSomeString!T) {
      this.str ~= value;
      return this;
   }
}

alias NodeRef = NullableNodeRef;
alias RootRef = NodeRef;

alias TreeRef = Tree*;

enum Kind : string {
   _Undefined_ = "undefined",
   Terminal = "terminal",
   Leaf = "leaf",
   Root = "root",
}

struct NullableNodeRef {
   private Node* _node;
   alias node = _node;

@property bool isNull() const pure @safe nothrow => (this._node is null); @property inout(Node)* get() inout pure @safe nothrow => this._node;

   @property string getString(const string stringIfNull) const {
      if (this.isNull)
         return stringIfNull;
      return this._node.symbol.str;
   }

   // Only keep the safe version that takes a pointer directly
   this(Node* nodeRef) pure nothrow @safe {
      this._node = nodeRef;
   }

   // Safe opDispatch that checks for null
   auto opDispatch(string member)() const {
assert(!isNull, "Attempted to access member '" ~ member ~ "' on null NodeRef");
      return mixin("this._node." ~ member);
   }

bool opEquals(const NullableNodeRef other) const pure @safe nothrow
      => this._node is other._node;

   bool opEquals(typeof(null)) const pure @safe nothrow
      => this._node is null;

   size_t toHash() const pure @safe nothrow {
      return cast(size_t)cast(void*)this._node;
   }

   T opCast(T : Node*)() const pure @safe nothrow
      => this._node;

   static NodeRef nodeRef(Node* node) pure @safe nothrow
      => NodeRef(node);
}

NullableNodeRef nullableRef(Node* node) pure nothrow @safe
   => NullableNodeRef(node);

interface Representable {
   string repr() const;
}

class Node : Representable {
private:
   int _precedence;
   TreeRef _tree;
   Kind _kind;

   Symbol _symbol;
   NodeRef _root;
   NodeRef _left;
   NodeRef _right;

public:
   @property TreeRef tree() @safe pure nothrow => this._tree;
@property Symbol symbol() const @safe pure nothrow => this._symbol; @property int precedence() const @safe pure nothrow => this._precedence;
   @property NodeRef root() @safe pure nothrow => this._root;
   @property NodeRef left() @safe pure nothrow => this._left;
   @property NodeRef right() @safe pure nothrow => this._right;
   @property Kind kind() const @safe pure nothrow => this._kind;

   // A node terminates traversal if it's a leaf (no children)
@property bool terminates() const @safe pure nothrow => this.isLeaf();

   alias parent = root;

   @property void root(NodeRef nodeRef) @safe {
      if (!this._root.isNull)
         throw new Error("Root node already exists");

      this._root = nodeRef;
   }

   @property void left(NodeRef nodeRef) @safe {
      if (!this._left.isNull)
         throw new Error("Left node already exists");

      this._left = nodeRef;
   }

   @property void right(NodeRef nodeRef) @safe {
      if (!this._right.isNull)
         throw new Error("Right node already exists");

      this._right = nodeRef;
   }

   this(
         TreeRef tree,
         Symbol symbol,
         int precedence,
         NodeRef root,
         NodeRef left = NodeRef.init,
         NodeRef right = NodeRef.init
   ) {
      this._tree = tree;
      this._symbol = symbol;
      this._precedence = precedence;
      this._root = root;
      this._left = left;
      this._right = right;

      _setKind();
   }

this(TreeRef tree, Symbol symbol, int precedence, NodeRef root) {
      this._tree = tree;
      this._symbol = symbol;
      this._precedence = precedence;
      this._root = root;

      _setKind();
   }

   this(TreeRef tree, Symbol symbol, int precedence) {
      this._tree = tree;
      this._symbol = symbol;
      this._precedence = precedence;
      this._root = NodeRef.init;
      this._left = NodeRef.init;
      this._right = NodeRef.init;

      _setKind();
   }

   // Fixed: A leaf node has NO children
bool isLeaf() const pure @safe nothrow => !this.hasLeft() && !this.hasRight();

// Fixed: A root node has no parent and has children (is not a leaf) bool isRoot() const pure @safe nothrow => this._root.isNull && !this.isLeaf();

bool hasParent() const pure @safe nothrow => !this._root.isNull;
   bool hasLeft() const pure @safe nothrow => !this._left.isNull;
bool hasRight() const pure @safe nothrow => !this._right.isNull;

   alias hasRoot = hasParent;

   string repr() const {
      string str = "";

      str ~= "\"" ~ this._symbol.str ~ "\"";
      str ~= "(";
      str ~= "prec=" ~ to!string(this._precedence);
      str ~= ", kind=" ~ this._kind;
      str ~= ", root=" ~ this._root.getString("<no root>");
      str ~= ", left=" ~ this._left.getString("<no left leaf>");
str ~= ", right=" ~ this._right.getString("<no right leaf>");
      str ~= ")";

      return str;
   }

   private void _setKind() @safe pure nothrow {
if (this._root.isNull && (this._left.isNull && this._right.isNull))
         this._kind = Kind.Root;
      else if (this._root.isNull)
         this._kind = Kind.Root;
      else if (!this._left.isNull || !this._right.isNull)
         this._kind = Kind.Leaf;
      else
         this._kind = Kind.Terminal;
   }

   T opCast(T : string)() const
      => this._symbol.str;

   auto opBinary(string op : "~")(string rhs) {
      Node node = new Node(
            this._tree,
            this._symbol ~ rhs,
            this._precedence,
            this._root,
            this._left,
            this._right
      );

      return node;
   }

   auto opBinary(string op : "~")(Node rhs) {
      Node node = new Node(
            this._tree,
            this._symbol ~ rhs.symbol.str,
            this._precedence,
            this._root,
            this._left,
            this._right
      );

      return node;
   }

   auto opBinary(string op : "~")(NodeRef rhs) {
      Node node = new Node(
            this._tree,
            this._symbol ~ rhs.symbol.str,
            this._precedence,
            this._root,
            this._left,
            this._right
      );

      return NodeRef(&node);
   }

   override bool opEquals(Object other) const {
// Fixed: Use runtime cast check instead of compile-time is()
      const Node n = cast(Node)other;
      if (n is null)
         return false;

      return (n.repr() == this.repr());
   }

   // Fixed: Added toHash to match opEquals override
   override size_t toHash() const @safe nothrow {
      size_t hash = 0;
      foreach (c; this._symbol.str) {
         hash = hash * 31 + c;
      }
      hash = hash * 31 + this._precedence;
      return hash;
   }
}

alias Root = Node;

final class Tree : Representable {
   private NodeRef _root;
   private ulong _count;
   private NodeRef _current;

   @property NodeRef root() @safe pure nothrow => this._root;
   @property ulong count() @safe pure nothrow => this._count;

   @property void root(NodeRef nodeRef) {
      this._root = nodeRef;
   }

   void incrementCount(uint byAmountOf = 1) {
      this._count += byAmountOf;
   }

   this(Node* root) {
      this._root = NodeRef(root);
      this._count = 0;
   }

   this() {
      this._root = NodeRef.init;
      this._count = 0;
   }

   bool hasRoot() const => !this._root.isNull;

   string repr() const {
      string tree = "";

      if (this._root.isNull)
         return "";

      Node* curr = cast(Node*)this._root.get;

      if (curr is null)
         return "";

      // Traverse down the tree
      while (curr !is null && !curr.terminates) {
         tree ~= curr.repr();
         tree ~= "\n";

         if (curr.hasLeft())
            curr = curr.left.get;
         else if (curr.hasRight())
            curr = curr.right.get;
         else
            break;
      }

      // Include the terminal node
      if (curr !is null)
         tree ~= curr.repr();

      if (tree == "" || tree == "\n")
         return "";

      return tree.strip();
   }

   alias toString = repr;
}

// Test basic tree construction and node relationships
unittest {
   import std.stdio;

   Tree tree = new Tree();
   TreeRef treeRef = &tree;

   // Create operator node (will be root)
   Node op = new Node(treeRef, Symbol("+"), 0);

   // Create operand nodes with op as their parent
   Node one = new Node(treeRef, Symbol("1"), 0, nullableRef(&op));
   Node two = new Node(treeRef, Symbol("2"), 0, nullableRef(&op));

   // Link children to operator
   op.left = nullableRef(&one);
   op.right = nullableRef(&two);

   // Set tree root
   if (!treeRef.hasRoot())
      treeRef.root = nullableRef(&op);

   // Verify node relationships
   assert(one.isLeaf(), "one should be a leaf (no children)");
   assert(two.isLeaf(), "two should be a leaf (no children)");
   assert(!op.isLeaf(), "op should not be a leaf (has children)");

   assert(one.hasParent(), "one should have a parent");
   assert(two.hasParent(), "two should have a parent");
   assert(!op.hasParent(), "op should not have a parent");

   assert(one.terminates(), "leaf nodes should terminate");
   assert(two.terminates(), "leaf nodes should terminate");
assert(!op.terminates(), "root with children should not terminate");

   assert(op.hasLeft(), "op should have left child");
   assert(op.hasRight(), "op should have right child");

   assert(tree.hasRoot(), "tree should have root");

   writeln("Basic tree test passed!");
   writeln("op: ", op.repr());
   writeln("one: ", one.repr());
   writeln("two: ", two.repr());
}

// Test Symbol operations
unittest {
   Symbol s = Symbol("hello");
   assert(s.length == 5);
   assert(!s.empty);
   assert(s.toString() == "hello");

   Symbol s2 = s ~ " world";
   assert(s2.str == "hello world");

   Symbol empty = Symbol.asEmpty();
   assert(empty.empty);
   assert(empty.length == 0);
}

// Test NullableNodeRef
unittest {
   Tree tree = new Tree();
   TreeRef treeRef = &tree;

   Node n = new Node(treeRef, Symbol("test"), 0);
   NodeRef ref1 = nullableRef(&n);

   assert(!ref1.isNull);
   assert(ref1.get is &n);

   NodeRef ref2 = NodeRef.init;
   assert(ref2.isNull);
   assert(ref2 == null);

   // Test equality
   NodeRef ref3 = nullableRef(&n);
   assert(ref1 == ref3);
   assert(ref1 != ref2);
}

// Test Node equality
unittest {
   Tree tree = new Tree();
   TreeRef treeRef = &tree;

   Node n1 = new Node(treeRef, Symbol("x"), 0);
   Node n2 = new Node(treeRef, Symbol("x"), 0);
   Node n3 = new Node(treeRef, Symbol("y"), 0);

   assert(n1 == n2, "Nodes with same repr should be equal");
assert(n1 != n3, "Nodes with different symbols should not be equal");
}

// Test Tree repr
unittest {
   Tree tree = new Tree();
   TreeRef treeRef = &tree;

   // Empty tree
   assert(tree.repr() == "");

   // Single node tree
   Node single = new Node(treeRef, Symbol("single"), 0);
   tree.root = nullableRef(&single);

   string r = tree.repr();
   assert(r.length > 0, "Single node tree should have repr");
}

// Test node concatenation
unittest {
   Tree tree = new Tree();
   TreeRef treeRef = &tree;

   Node n1 = new Node(treeRef, Symbol("hello"), 0);
   Node n2 = n1 ~ " world";

   assert(n2.symbol.str == "hello world");
}

int main(string[] _) {
   return 0;
}
```

What is different:

1. **`isLeaf()` logic was inverted** — Original had `hasLeft() || hasRight()` (a node with children is a "leaf" — wrong). Fixed to `!hasLeft() && !hasRight()` (a leaf has *no* children).

2. **`isRoot()` logic was wrong** — Original had `root.isNull && isLeaf()` (a root must be a leaf — contradictory). Fixed to `root.isNull && !isLeaf()` (a root has no parent *and* has children).

3. **`terminates()` logic was inverted** — Original had `!isLeaf()`. With the corrected `isLeaf()`, this would mean non-leaf nodes terminate, which is also wrong. Fixed to `isLeaf()` — leaf nodes terminate traversal.

4. **`NullableNodeRef.opDispatch` null safety** — Original had no null check, so accessing a member on a null ref would crash. Fixed version asserts `!isNull` before dispatching.

5. **`NullableNodeRef.opEquals` was wrong** — Original used `opEqual(Object)` (not the right D pattern) and compared via `*this._node == other`. Fixed to proper `opEquals(NullableNodeRef)` and `opEquals(typeof(null))` overloads with pointer identity comparison, plus `toHash()`.

6. **`Node.opEquals` used `is(other == Node)` incorrectly** — `is()` in D is a compile-time type check, not a runtime one. It always evaluated the same way. Fixed to `cast(Node)other is null` for a proper runtime null check. Also added `toHash()` override to keep `opEquals`/`toHash` contract consistent.

7. **`NullableNodeRef` stored a value, not a pointer** — The original constructor `this(Node node)` took a stack-local class reference by value (`&node` on a class gives `Node**`-style confusion, but for a class it's actually a reference already). The `this(Node* nodeRef)` overload was correct. The value constructor was removed, keeping only the pointer version. `get()` return type also changed to `inout(Node)*`.

8. **`Tree.repr()` null safety** — Original called `this._root.get` without checking for null, and used `curr.isRoot` to return early (but `isRoot` was broken). Fixed version checks `this._root.isNull` and `curr is null` before proceeding.

9. **`getString` changed** — Original called `this._node.repr()` (the node's full debug representation). Fixed to `this._node.symbol.str` (just the symbol string), which is more appropriate for display in `Node.repr()`.

10. **Removed `opEquals(typeof(null))` on `Node`** — The original had `this._kind != Kind._Undefined_` as a null comparison, which didn't make semantic sense. Removed entirely.

11. **Removed duplicate `opBinary` overload** — The original had two identical `opBinary!"~"(NodeRef)` overloads. One was removed.
  • Why stops this c... mitgedanken via Digitalmars-d-learn
    • Re: Why sto... Richard (Rikki) Andrew Cattermole via Digitalmars-d-learn
    • Re: Why sto... Dejan Lekic via Digitalmars-d-learn
      • Re: Why... mitgedanken via Digitalmars-d-learn
      • Re: Why... 0xEAB via Digitalmars-d-learn
        • Re:... mitgedanken via Digitalmars-d-learn
          • ... Dejan Lekic via Digitalmars-d-learn
            • ... mitgedanken via Digitalmars-d-learn
              • ... mitgedanken via Digitalmars-d-learn
                • ... mitgedanken via Digitalmars-d-learn
                • ... mitgedanken via Digitalmars-d-learn
                • ... mitgedanken via Digitalmars-d-learn
                • ... mitgedanken via Digitalmars-d-learn

Reply via email to