Skip to content

[major] Model visibility, constants and entry points in the AST - #32

Merged
matt-edmondson merged 1 commit into
mainfrom
claude/issues-l19cdz
Sep 8, 2026
Merged

[major] Model visibility, constants and entry points in the AST#32
matt-edmondson merged 1 commit into
mainfrom
claude/issues-l19cdz

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Closes #25, closes #26, closes #27.

Three gaps where the AST could not say something every target language can spell. All three go the whole way along the path a node feature has to work: the AST, the schema the editor walks, the inspector, the palette, the four generators and both directions of YAML.

Visibility (#25)

Visibility is an enumeration — Public, Protected, Internal, Private, and Unspecified meaning "however the language would write it anyway". ClassDeclaration, FunctionDeclaration and VariableDeclaration carry it through IHasVisibility.

It replaces the free-text string? AccessModifier that ClassDeclaration and VariableDeclaration carried. That text could only ever be right for whichever language it was typed for — internal means nothing in C++, and Python has no keyword at all — so it could not be spelled per language. Hence the [major] tag.

Language How it is spelled
C# The keyword; a class or function with none stays public
C++ Access labels (public:, protected:, private:) the members are grouped under; Internal becomes public:, since C++ has no assembly to be internal to
JavaScript A private class member takes the # prefix — JavaScript's own private syntax, enforced by the runtime; nothing for the rest
Python Nothing

Python is deliberate rather than unfinished: its convention for a non-public member is a leading underscore on the identifier, and renaming a declaration in the generator would leave every VariableReference to it naming something that no longer exists. It is dropped the same way JavaScriptGenerator already drops the types the AST carries, and the class's remarks say so.

Documents saved with the old accessModifier key still load — the deserializer reads both spellings.

Constants (#26)

A VariableDeclaration marked IsConstant with a literal InitialValue. The AST already had the flag; what was missing was it coming out right where it sits:

  • C++static constexpr for a class member. A plain const member is a per-instance value initialised once per object, which is not what a constant means. A member with no initialiser stays const, since constexpr without one does not compile.
  • JavaScriptstatic for a class member. const declares a binding in a scope and a class body is not one, so the previous output was a syntax error waiting to happen. Combines with # for a private constant: static #MAX = 10;.
  • C# — unchanged, already correct.
  • Python — a plain assignment, for the same reason as visibility: the upper-case naming that stands in for a constant is a convention about the identifier.

The palette now offers a constant that already holds a literal, so it is a node you edit rather than one you have to wire a value into first.

Entry point (#27)

EntryPoint is a node of its own rather than a FunctionDeclaration named main, because only some languages spell the entry point as a function at all and none of them spell it the same way. It carries the two things that actually vary — AcceptsArguments and ReturnsExitCode — and each generator writes what its language looks for:

Language Output
C# public static int Main(string[] args), or a bare void Main()
C++ int main(int argc, char* argv[]); int either way, since that is what the standard names
Python def main(args): plus the if __name__ == "__main__": guard that runs it, and the import sys its arguments and exit code need — emitted only when needed
JavaScript function main(args) plus process.exit(main(process.argv.slice(2)));, since a module that only defines main does nothing

An entry point is accepted as a class member (which is where C# puts Main) but refused as a statement inside a body — a program does not start running part-way through a function.

Testing

dotnet test — 370 passed, 0 failed (41 new). Release build clean, no new warnings. The new tests cover each generator's own spelling rather than asserting one shape four times, both directions of YAML including the old accessModifier key, the schema's slots and what they accept, the inspector's reads and writes, and the palette entries.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FMNNvzNnuLErq96R4NjYUi


Generated by Claude Code

Closes three gaps where the AST could not say something every target
language can spell.

Visibility (#25) becomes an enumeration — Public, Protected, Internal,
Private, and Unspecified for "however the language would write it
anyway" — carried by ClassDeclaration, FunctionDeclaration and
VariableDeclaration through IHasVisibility. It replaces the free-text
AccessModifier that ClassDeclaration and VariableDeclaration carried,
which could only ever be right for whichever language it was typed for.
Each generator spells it its own way: C# writes the keyword, C++ groups
members under access labels, JavaScript gives a private member the #
prefix that is its own private syntax, and Python writes nothing at all,
because its leading-underscore convention renames the declaration and
would leave every reference to it naming something that no longer
exists. Documents written with accessModifier still load.

Constants (#26) are a VariableDeclaration marked IsConstant with a
literal value, and now come out right wherever they sit: C++ writes
static constexpr for a class member rather than a per-instance const,
JavaScript writes static for one rather than a const that is a syntax
error in a class body, and the palette offers a constant already holding
a literal.

EntryPoint (#27) is a node of its own rather than a function named main,
since only some languages spell the entry point as a function at all. It
carries the two things that vary — whether the program reads its
arguments and whether it returns an exit code — and each generator
writes what its language looks for: C#'s static Main, C++'s free int
main, Python's main with the __main__ guard and the import its arguments
need, and JavaScript's main with the call that runs it.

The schema, the inspector, the palette and the YAML round trip carry all
three, with 41 tests over the four generators and both directions of
serialization.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FMNNvzNnuLErq96R4NjYUi
@sonarqubecloud

sonarqubecloud Bot commented Sep 8, 2026

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit 3455af5 into main Sep 8, 2026
12 checks passed
@matt-edmondson
matt-edmondson deleted the claude/issues-l19cdz branch September 8, 2026 13:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants