feat: PowerShell parser — v0.2.0#39
Merged
Merged
Conversation
Translate SPEC.POWERSHELL.md §16 (build order) plus §15/§17 (release and Netclaw integration) into 16 NOW phases for the v0.2.0 PowerShell parser, now that the spec is merged (PR #38). - New "NOW (0.2.0 — PowerShell parser)" section: phases 1–16, each citing its spec section, all unchecked. Phase 11 carries the §16 ordering fix (multi-shell corpus refactor must precede corpus authoring). - Retitle the completed "NOW (0.1.0-alpha shipping path)" section to "SHIPPED (0.1.0-alpha → 0.1.5 — complete)"; fix three stale "in progress" labels on long-finished phases. - Refresh LATER for post-0.2.0 scope: drop the now-elaborated PowerShell bullet, add the SPEC.POWERSHELL.md §18 deferred items. - Intro now names both SPEC.md §16 and SPEC.POWERSHELL.md §16.
The 0.1.0-alpha → 0.1.5 work is shipped; its phase-by-phase record is preserved in git history and RELEASE_NOTES.md. Drop the SHIPPED section so the plan shows only active v0.2.0 work — 464 lines down to 200. The intro bucket list and the NOW spec note no longer reference it.
Implements SPEC.POWERSHELL.md §1-§11: the public-API surface change, verb/binding tables, lexer, command parser, resolver, Set-Location propagation, pwsh -Command/-EncodedCommand recursion, and anomaly safe-fail. Phase 1 — public API: add ShellParserOptions base record; reparent BashParserOptions; add PwshParserOptions + PwshParser; add additive VerbChain.CanonicalVerb / VerbChain.IsDynamic; rename Clause.IsBashCWrapped -> IsCommandStringWrapped (breaking, per SPEC Appendix A). Bash corpus JSON key flipped; PublicApiSnapshotTests and corpus DTOs updated. Phase 2 — Internal/Pwsh/Verbs: PwshApprovedVerbs (Get-Verb set), PwshAliases (complete default alias superset), PwshVerbs (Cwd/File/ control-flow), PwshBindingTables (§6.5), PwshPerVerbRules (§7). Phase 3 — PwshLexer + PwshToken/PwshTokenKind; OpaqueRegionScanner gains a backtick-escape mode (shared). Phases 4-10 — PwshCommandParser: pipeline/statement splitting, verb- chain extraction (cmdlet/alias/native/dynamic), §6.5 parameter binding, PwshResolver (§8), per-verb path rules, Set-Location-in-compound propagation (§9), pwsh recursion with depth-5 cap (§10), and the §11 anomaly contract incl. the 64 KiB input cap. All 550 tests pass (149 bash corpus non-regression + 44 lexer + 71 parser + updated API snapshot).
- Remove dead state: ClassifiedVerb.ArgStart / EffectiveVerb, the dead argStart local, PwshVerbs.CwdVerbs / ScriptBlockVerbs / StatementKeywords, PwshResolver's unused isPsScriptRoot out-param. - Hoist the per-call new[] arrays in PwshResolver.TryMatchHomeVariable to static fields (allocation-inside-a-loop). - Add -PSPath to the Set-Location path-flag check in UpdateAttribution. All 550 tests pass; no behavior change.
The corpus runner and PII audit now enumerate every Corpus/<shell>/ directory and route by directory name (bash/ -> BashParser, powershell/ -> PwshParser). The PII audit gains the SPEC.POWERSHELL.md §14 Windows-path patterns (C:\Users\<user>\, UNC \\<host>\share). Corpus copy-to-output glob widened to Corpus/**/*.json. 550 tests pass (bash corpus non-regression).
- tools/PwshCorpusTool: a dev-only authoring aid. `generate` rebuilds the Corpus/powershell entries from the curated CorpusManifest; `check` prints the parser AST beside the real-pwsh verdict. Registered in TOOLING.md. - 211 hand-curated PowerShell corpus entries under Corpus/powershell/, exceeding every SPEC.POWERSHELL.md §13 category minimum (alias 18, parameter binding 31, recursion 18, unparseable 28, ...). - PwshOracleTests: the §13 validation gate — feeds every corpus input to real pwsh ([Parser]::ParseInput) and enforces the oracle matrix; plus the §6.3 PwshAliases-vs-live-Get-Alias completeness [Fact]. Skips gracefully when pwsh is absent. - PII audit exempts intentional base64 -EncodedCommand payloads and the low-entropy over-cap filler from the long-token heuristic. All 763 tests pass; the oracle gate confirms every corpus input is consistent with real PowerShell 7.6.
- SPEC.md §1/§2/§3/§6.4/§15 updated to the shipped v0.2.0 surface — the Clause rename, the VerbChain additions, PwshParser/PwshParserOptions/ ShellParserOptions, and the v0.2.0 versioning note. - Directory.Build.props: VersionPrefix 0.2.0, VersionSuffix alpha; PackageTags + Description mention PowerShell. dotnet pack produces ShellSyntaxTree.0.2.0-alpha.nupkg. - pr_validation.yml: an explicit step fails the build if pwsh is absent so the §13 oracle gate never silently skips; the existing Linux + Windows matrix already runs both corpora. - RELEASE_NOTES.md: v0.2.0-alpha section with the breaking IsBashCWrapped -> IsCommandStringWrapped old->new mapping. - Samples: the CLI sample gains --shell bash|pwsh; the Web sample gains a shell selector with PowerShell presets. Both route through the shell-neutral IShellParser surface with no logic change. - README.md + IMPLEMENTATION_PLAN.md updated for the shipped v0.2.0. All 763 tests pass on this machine (pwsh 7.6 present).
- PwshOracle: consolidate the three pwsh child-process launches into one TryRunPwsh helper that drains stderr concurrently (a chatty child could otherwise deadlock on a full stderr pipe buffer behind the blocking stdout read) and kills a timed-out child instead of orphaning it. - Delete the unused AstAssert.Dump — PwshCorpusTool's CorpusJson is now the single AST-to-JSON projection; drop the now-unused System.Text.Json import. - PiiAuditTests: fix a misplaced doc-comment that left ShouldScan undocumented and the comment text dead. All 763 tests pass; no behavior change.
The PwshAliases-vs-live-Get-Alias completeness [Fact] fails on Windows CI because Show-Command (and its shcm alias) is a Windows-only host integration that Unix pwsh does not define. Linux remained clean.
Aaronontheweb
marked this pull request as ready for review
May 20, 2026 03:27
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.
Implements the PowerShell parser for ShellSyntaxTree v0.2.0 —
SPEC.POWERSHELL.md§16 build-order phases 1–14.What's in it
A second
IShellParserimplementation —PwshParser— emitting the sameParsedCommandAST a consumer already walks for bash.ShellParserOptionsbase record;BashParserOptionsreparented (source-compatible);
PwshParser/PwshParserOptions;additive
VerbChain.CanonicalVerb/VerbChain.IsDynamic.PwshLexer(quoting, backtick escape, here-strings,stream redirects,
--%, opaque regions),PwshCommandParser(pipeline/statement splitting, verb-chain extraction, the §6.5
parameter-binding model,
PwshResolver, per-verb path rules,Set-Locationpropagation,pwsh -Command/-EncodedCommandrecursion, the §11 anomaly contract + 64 KiB cap).
PwshApprovedVerbs,PwshAliases(complete defaultset),
PwshVerbs,PwshBindingTables,PwshPerVerbRules.Corpus/powershell/,exceeding every
SPEC.POWERSHELL.md§13 category minimum. The corpusrunner and PII audit are directory-routed by shell.
PwshOracleTestsfeeds every corpus input to realpwshand enforces the §13 oracle matrix; aPwshAliases-vs-liveGet-Aliascompleteness[Fact].tools/PwshCorpusToolis thecorpus authoring aid.
SPEC.mdsynced; CI verifiespwshon Linux + Windows;RELEASE_NOTES.mdv0.2.0-alpha section.Clause.IsBashCWrapped→Clause.IsCommandStringWrapped. Permitted on a0.xminor bySPEC.mdAppendix A; the old→new mapping is inRELEASE_NOTES.md. Netclaw must absorb the rename in lockstep.Validation
dotnet build -c Releaseclean (TreatWarningsAsErrors).dotnet test -c Release— 763 tests pass (149 bash corpusnon-regression + 211 PowerShell corpus + 44 lexer + 71 parser + the
oracle gate + API snapshot + PII audit).
dotnet packproducesShellSyntaxTree.0.2.0-alpha.nupkg.Out of scope (needs maintainer / external action)
0.2.0-alphato trigger the NuGet publish.