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.