Auto-home bare same-package references (#4529)#5425
Conversation
When a program declares a "+package" meta, a bare reference to an object that exists in the same package is now resolved automatically into that package, as if a "+alias" meta had been declared. For example, in package "foo" the reference "bar" is compiled to "Φ.foo.bar" when "Φ.foo.bar" is a known object, so same-package objects no longer need manual "+alias" metas. The decision is existence-based to stay safe: "add-default-package.xsl" takes an "objects" parameter with the fully qualified names of all objects the compiler is aware of. A bare reference is homed into the current package only when such an object exists there; otherwise it stays at the root "Φ" (so bare globals like "seq", which live at "Φ.seq" and not at "Φ.foo.seq", are untouched). The eo-maven-plugin fills this parameter from the registered "foreign" catalog during parsing and includes its digest in the parse cache key so cached XMIRs are invalidated when the set of objects changes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LUBw9ufavQSMDmBxnqJ9Nw
🚀 Performance AnalysisAll benchmarks are within the acceptable range. No critical degradation detected (threshold is 100%). Please refer to the detailed report for more information. Click to see the detailed report
✅ Performance gain: |
- Move the canonical XSL pipeline out of EoSyntax into a new Canonical class implementing Function<XML, XML>, to satisfy qulice (no public static methods, correct declaration order, no trailing dot in Javadoc tags). - Thread the parsing transform and objects-digest through Parsing as parameters instead of mutable fields. - Update MjParseTest.parsesWithCache to account for the objects digest now included in the parse cache key. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LUBw9ufavQSMDmBxnqJ9Nw
- Build the canonical pipeline lazily via a cactoos Scalar so the Canonical constructor contains only instantiations (satisfies ConstructorsCodeFreeCheck). - Suppress PMD UnnecessaryLocalRule where the pipeline/digest locals must be built once and captured by the parsing lambda. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LUBw9ufavQSMDmBxnqJ9Nw
Parsing runs concurrently (Threaded), and the lazily-built pipeline in Canonical was memoized with a plain Sticky scalar, which is not thread-safe: under concurrent first access the scalar could return null, causing a NullPointerException while parsing. Wrap it in a Synced scalar so the pipeline is built once and safely shared across threads. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LUBw9ufavQSMDmBxnqJ9Nw
- add-default-package.xsl now takes the list of local package object NAMES (not all FQNs). A bare name in a program with a "+package" meta is homed into that package when it is one of those names; otherwise it stays at the root "Φ" (treated as a global). Empty list falls back to the root, so bare globals like "seq" and the stand-alone parser are untouched. - The eo-maven-plugin fills the list with the simple names of the local objects that belong to a package. - Use UnaryOperator<XML> instead of Function<XML, XML> (SonarCloud java:S4276). - Remove the PMD UnnecessaryLocalRule suppressions: the parsing transform and digest are passed as method parameters instead of single-use locals, and the test computes the cache version inline via a helper. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LUBw9ufavQSMDmBxnqJ9Nw
With same-package auto-homing in place, a bare reference to an object of the current package no longer needs a manual "+alias". Remove the now-redundant "+alias ms.*" metas across the ms package (abs, pi, e, sine, power, ln, sqrt, arc-sine); those bare references are homed into "Φ.ms.*" automatically, producing the exact same XMIR as before. This both exercises the new feature on real code and delivers its intended benefit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LUBw9ufavQSMDmBxnqJ9Nw
|
@maxonfjvipon take a look |
| final String objects = sources.stream() | ||
| .map(TjForeign::identifier) | ||
| .filter(id -> id.contains(".")) | ||
| .map(id -> id.substring(id.lastIndexOf('.') + 1)) |
There was a problem hiding this comment.
This flattens every object across all packages into one set of simple names, so eo:homed homes a bare name into the current package whenever the name exists in any package. ss and tt already both define contains/index-of/last-index-of, and ss/index-of.eo uses a bare number — the day any package gains a number object, that reference silently re-homes to Φ.ss.number. Keying on the qualified name avoids this: pass the .-containing identifiers as objects unchanged, then home only when concat($package, '.', $name) matches. Same single pipeline, no simple-name collisions.
There was a problem hiding this comment.
Fixed in 501c5d1. add-default-package.xsl now keys on the qualified name: the plugin passes the .-containing identifiers (package.name) unchanged, and a bare name is homed only when concat($package, '.', $name) is in the list. So the simple-name collision is gone — verified against eo-runtime, the bare number in ss/index-of.eo stays Φ.number (33 occurrences, 0 Φ.ss.number).
Generated by Claude Code
| .distinct() | ||
| .collect(Collectors.joining(" ")); | ||
| final int total = this.parsed( | ||
| sources, new Canonical(objects), Integer.toHexString(objects.hashCode()) |
There was a problem hiding this comment.
String.hashCode is a collidable 32-bit hash, and objects is built with distinct() over withSources(), whose order isn't guaranteed. A collision gives a stale cache hit; an order flip gives a needless miss. Sorting the names and using a real digest makes this deterministic.
There was a problem hiding this comment.
Fixed in 501c5d1. The qualified names are now sorted and hashed with SHA-256 (cactoos Sha256DigestOf + HexOf) instead of the order-sensitive, 32-bit String.hashCode, so the cache key is deterministic and collision-resistant.
Generated by Claude Code
Per @maxonfjvipon's review: - add-default-package.xsl now keys on the qualified name. The plugin passes the "." -containing identifiers (package.name) unchanged, and a bare name is homed only when concat($package, '.', $name) matches. This removes the simple-name collision across packages: e.g. a bare "number" in "ss" stays "Φ.number" even though other packages may define objects with colliding simple names. - The parse-cache digest is now deterministic and collision-resistant: the qualified names are sorted and hashed with SHA-256 (cactoos Sha256DigestOf + HexOf) instead of the order-sensitive, 32-bit String.hashCode. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LUBw9ufavQSMDmBxnqJ9Nw
|
@maxonfjvipon take a look again please |
…lt-package.xsl Co-authored-by: Max Trunnikov <mtrunnikov@gmail.com>
|
|
@maxonfjvipon good now? |
|
@maxonfjvipon Thanks for the review! You've earned +7 points for this: +12 as a basis; -5 for very few (2) comments. Your running score is +616; don't forget to check your Zerocracy account too). |
|
@yegor256 Thanks for the contribution! You've earned +4 points for this: +16 as a basis; -4 for too many hits-of-code (475 >= 100); -8 for way too many hits-of-code (475 >= 400). Please, keep them coming. Your running score is +1720; don't forget to check your Zerocracy account too). |



Closes #4529.
What
When a program declares a
+packagemeta, a bare reference to an object that belongs to the same package is now resolved automatically into that package, as if a+aliashad been written by hand. For example, in packagefoo:baris compiled toΦ.foo.bar(previously it defaulted to the rootΦ.bar), so same-package objects no longer need manual+alias foo.barmetas.How
add-default-package.xsltakes anobjectsparameter — the space-separated names of the local objects that belong to a package, filled in by theeo-maven-pluginfrom the registered catalog. The rule for a bare name in a program with a+packagemeta:Φ.<pkg>.<name>);Φ(treated as a global — the default behaviour).This keeps globals correct without any allow/deny gymnastics: a bare
seq(not a local package object) staysΦ.seq, and when the list is empty (e.g. the parser used stand-alone) every bare reference falls back to the rootΦ. Aliased references are untouched (handled earlier in the chain).Changes
add-default-package.xsl— newobjectsparameter and aneo:homed()function;@base/@atombare references are homed into the current package only when the name is a local package object, otherwise intoΦ.Canonical(new, ineo-parser) — wraps the canonical XSL pipeline as aUnaryOperator<XML>, injecting theobjectsparameter intoadd-default-package.xsl; the pipeline is built lazily and once, thread-safely (cactoosSynced/Sticky), and reused across the concurrent parse.EoSyntax—CANONICALdelegates toCanonical; a public(Input, UnaryOperator<XML>)constructor lets the plugin supply the objects-aware pipeline.Parsing— builds the objects-aware pipeline once from the local package-object names and reuses it for every source; the object-set digest is part of the parse-cache key so cached XMIRs are invalidated when the set changes.EoSource— accepts the shared transform.Testing
eo-parser: full suite passes, incl. newEoSyntaxTestcases (bare reference homed into the package when it is a local package object; kept at root otherwise).eo-maven-plugin:MjParseTest(incl. new end-to-end cases across two files in one package),CompilingTest,MjTranspileTest— all green under-Pqulice.eo-runtime: all 113 sources parse concurrently with no error; an exact-match audit confirms no global (seq,malloc,number, …) is wrongly homed into a package, and bareseqstaysΦ.seq.Notes
+alias; bare references to runtime globals are unaffected (they stay at the root).🤖 Generated with Claude Code
https://claude.ai/code/session_01LUBw9ufavQSMDmBxnqJ9Nw