You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
A method body can't portably refer to its own instance: field reads and sibling calls need this./self./self-> text in VariableReference, so no such body translates across targets #141
The AST has no node for "the current instance" and no member-access expression. The only way for a method body to read a field, or call another method on its own object, is to put target-language text into VariableReference. The test suite does this itself, with new VariableReference("self.x") and new CallExpression(new VariableReference("self"), "Shift"). CompiledExemplar records the limitation:
a body that does says self->x in C and self.x in the other two, which is a difference no shared declaration can hold.
/// There are two groups, and the line between them is the receiver. Everything down to
/// <see cref="OriginTable"/> is what all three targets are checked against, and none of it contains a
/// body that reads a member — a body that does says <c>self->x</c> in C and <c>self.x</c> in the
/// other two, which is a difference no shared declaration can hold. Everything after it reads a
/// member, so it is shared by the two that spell that the same way and is not offered to C.
/// </para>
The generators already know each language's receiver. Python writes self into the signature, C and Go name the receiver self, and C#/JS write this. for member initialisers. Bodies just have no way to ask for it.
Failure scenario
Take a class Counter with a field count and a method add(n) whose body is count += helper(n), where helper is another instance method. Using a bare VariableReference("count") and CallExpression("helper"), every generator emits count += helper(n):
Python:UnboundLocalError on count, NameError on helper.
C:count and helper are undeclared, since the function is Counter_helper(self, n).
Rust, Go: unresolved names.
Changing the text to self.count fixes Python, Rust and Go, but breaks C# (no self), C++ (this->count), JS (this.count) and C (self->count). No single AST compiles in all seven targets, which undermines the premise of a language-agnostic AST. It is the same kind of gap that #55 closed for calls.
Suggested fix / acceptance criteria
Add a ThisExpression (or SelfReference) node, and a MemberAccessExpression(Receiver, MemberName) for field reads and writes. CallExpression.Receiver already covers calls.
Each generator spells them in its own way:
C# and JS: this.x
C++: this->x
Python, Rust and Go: self.x
C: self->x, with instance calls as Type_method(self, …)
The new nodes get the same support as existing ones: YAML serializer and deserializer, AstSchema/AstFields/AstNodeCatalog, and the node-exhaustiveness test.
Rewrite CompiledExemplar so its member-reading group uses the new nodes and is shared with C.
Priority: Medium. No method body that touches its own instance can be written portably, which works against the language-agnostic premise. It is new surface, not a regression.
Area:Coder/Ast (new ThisExpression / MemberAccessExpression), all seven generators, YAML serializer and deserializer, AstSchema/AstFields/AstNodeCatalog
Activity
matt-edmondson commentedon Oct 5, 2026
Triage
Coder/Ast(newThisExpression/MemberAccessExpression), all seven generators, YAML serializer and deserializer,AstSchema/AstFields/AstNodeCatalog[minor]). Then follow the The expression vocabulary cannot say a call, so every non-trivial body is smuggled through VariableReference as target-language text #55 pattern: add the nodes, a spelling in each generator, serializer support and the exhaustiveness test, and rewriteCompiledExemplar's member-reading group to use them.Generated by Claude Code