Repository navigation
vm: evaluate a Puppet manifest (notice output) — Module.new, extend, setter-RHS + evaluator primitives - #33
Merged
Merged
Conversation
…mitives Advancing the Puppet apply path: `Puppet::Parser::Compiler.new(node).compile` now runs through catalog setup and into the Pops evaluator. Several primitive bugs and missing methods blocked it; each was reduced to a minimal rbgo-vs-MRI repro before fixing. Engine fixes: - Module.new now builds a real anonymous module (isModule, no superclass) and runs its block as the body, so `def self.x` defines a singleton method and the result can be included / have methods defined into it. Previously Module.new fell through to Class#new and allocated a plain instance, dropping the block. - classOf reports a module's class as Module (not Class), matching MRI (`Comparable.class == Module`, `String.class == Class`). - C.extend(M) now mixes M into the class's singleton ancestry instead of copying only M's own methods, so methods from M's transitively-included modules become class methods too (Puppet::Type extends ProviderFeatures which includes Docs — this is how `desc` reaches a type). singleton_methods/public_methods enumeration was updated to walk those extended-module trees as well. New methods (all matched to MRI semantics, reduced from the apply trace): - File.symlink?, File.realpath - Module#public_method_defined? / #private_method_defined? / #protected_method_defined? - Proc#source_location ([file, 0]; the VM has no per-line map yet) - Array#insert (negative index, end-padding with nil) - MatchData#[] Range and (start, length) forms (md[1..] for captures) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… coverage
With this change `Puppet::Parser::Compiler#compile` runs a trivial manifest end
to end and the Pops evaluator emits real `Notice:` output:
Notice: Scope(Class[main]): hi from puppet
Notice: Scope(Class[main]): hi from evaluate_string
Each blocker was reduced to a minimal rbgo-vs-MRI snippet and fixed in the
engine. Building on the earlier Module.new / extend / file-primitive fixes:
- Array#insert (negative index + end-padding + multi-value + error branches).
- MatchData#[] Range and (start, length) forms (md[1..] grabs the captures).
- ObjectSpace module: define_finalizer/undefine_finalizer/each_object/
garbage_collect/start/count_objects (Go GC owns reclamation, so the finalizers
are accepted-and-ignored and the reflective walkers report empty). require
"fiber"/"objspace" now succeed (Fiber is already a built-in class).
- Concurrent::ThreadLocalVar honours a lazy default block (the gem's API), not
just a default value — Puppet's PuppetStack uses `ThreadLocalVar.new { [] }`.
- A Set holds arbitrary objects keyed by identity, like MRI, instead of raising
for non-primitive members — the catalog adds Puppet resources to a Set.
- A syntactic setter expression (`recv.x = v`, `recv[i] = v`) evaluates to the
assigned value, not the setter method's own return value — a Ruby language
guarantee. This is the deepest fix: ThreadLocalVar#default relies on it, and
the frozen prelude is regenerated because its own setter sites recompile.
Tests added/adjusted (asserted against MRI 4.0.5); the now-incorrect Set/Bag
tests that expected a TypeError for Array/object members are replaced with the
MRI-correct positive assertions. Cross-package coverage is 100.0%.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
File.realpath (via fileExpand) used path.IsAbs, which only recognises a
forward-slash root ("/x"). On Windows an already-absolute path like
"C:/Users/.../a.txt" was therefore treated as relative and the working
directory was prepended ("D:/a/ruby/ruby/internal/vm/C:/Users/..."),
so File.realpath raised Errno::ENOENT in the windows-latest CI lane.
Add isAbsPath, which also accepts a drive-letter root via
filepath.IsAbs(filepath.FromSlash(p)). It is identical to path.IsAbs on
POSIX, so behaviour there is unchanged; coverage stays at 100%.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tannevaled
added a commit
that referenced
this pull request
Jun 28, 2026
Update the README and the Rails/Puppet conformance report to reflect the
current runtime reality: `require "puppet"` fully boots Puppet 8.11.0 on a
pure-Go CGO=0 rbgo, and a manifest parses to the Pops AST, compiles to a
catalog, and evaluates (`notice("hi")` => `Notice: Scope(Class[main]): hi`).
- README: new "Running Puppet" section; reframe the honesty note to cover the
boot->evaluate milestone; update the closing status paragraph.
- CONFORMANCE-RAILS-PUPPET.md: new "Puppet boots, compiles, and evaluates a
manifest (2026-06-28)" section documenting what works, what it took (the
PR #20–#33 VM + pure-Go stdlib batches), and the honest frontier.
Honest framing throughout: what works is boot -> parse -> compile -> evaluate;
full `puppet apply` (the transaction / resource-provider layer) is the active
next milestone, not done. Docs only — no code touched.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Milestone: a Puppet manifest now EVALUATES and emits notice output
Building on `require "puppet"` booting and manifests parsing, this PR drives
`Puppet::Parser::Compiler#compile` end to end so the Pops evaluator runs a
trivial manifest and emits real Puppet log output:
Both the full `compiler.compile` of `notice("hi")` and a standalone
`EvaluatingParser#evaluate_string(scope, 'notice("hi")')` produce `Notice:`
output; the catalog compiles with the base resources
`["Stage[main]", "Class[Settings]", "Class[main]"]`.
Facter is a near-empty pure-Ruby shim staged on the probe load path
(`facter.rb`), not the real gem — the real facter needs `hocon`/native gems and
fails to load even under MRI in this environment. A trivial `notice` manifest
needs no real facts, so the shim suffices for boot + apply. (The shim is a
disposable probe file, not shipped in rbgo.)
Engine fixes (each reduced to a minimal rbgo-vs-MRI repro)
and runs its block as the body — so `def self.x` is a singleton method and the
result is includable. Previously it fell through to `Class#new` and allocated a
plain instance, dropping the block. `classOf` also reports a module's class as
`Module` (not `Class`), matching MRI.
class's singleton ancestry instead of copying only the module's own methods —
so `extend ProviderFeatures` (which `include`s `Util::Docs`) makes `desc` a class
method on `Puppet::Type`. `singleton_methods`/`public_methods` enumeration walks
those extended-module trees too.
assigned value, not the setter method's return value — a Ruby language
guarantee. The frozen prelude is regenerated because its own setter sites
recompile. (This unblocked `Concurrent::ThreadLocalVar#default`.)
raising for non-primitive members — the catalog adds Puppet resources to a Set.
New methods / modules (matched to MRI 4.0.5)
Next blocker
The catalog transaction / resource-application layer (turning the compiled
catalog into applied resource state via providers) — a large subsystem,
characterized for a follow-up. The evaluator milestone (a manifest evaluating to
`notice` output) is reached here.
Quality
Set/Bag tests that expected a TypeError for object/array members are replaced
with the MRI-correct positive assertions).
regenerated (drift test green).
🤖 Generated with Claude Code