Why use temporary links?

Dejan Lekic dejan.lekic at gmail.com
Fri May 22 09:45:54 UTC 2026


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.


More information about the Digitalmars-d-learn mailing list