Skip to content

fix: single-quote literalness (B2) + LooksLikePath trailing-backslash (B3) - #23

Merged
Aaronontheweb merged 2 commits into
devfrom
fix/single-quote-literal-and-trailing-backslash
May 11, 2026
Merged

fix: single-quote literalness (B2) + LooksLikePath trailing-backslash (B3)#23
Aaronontheweb merged 2 commits into
devfrom
fix/single-quote-literal-and-trailing-backslash

Conversation

@Aaronontheweb

Copy link
Copy Markdown
Owner

Summary

Two parser correctness fixes pulled from the v0.1.x backlog. Public API surface unchanged.

B2 — single-quote literalness

v0.1.0-alpha resolved $HOME uniformly regardless of quote style, so echo '$HOME' produced Kind=Tilde. Per SPEC §5, single-quoted contents are literal bytes — no variable expansion. The lexer now marks single-quoted QuotedString tokens with an internal IsSingleQuoted flag, and the resolver short-circuits tilde / $HOME / $VAR / glob / filesystem:: handling when set. Matches bash semantics.

  • echo '$HOME'Kind=Literal, Resolved=null (was Kind=Tilde)
  • cat '/etc/passwd' → still resolves to /etc/passwd (literal absolute path inside single quotes)
  • rm '$VAR/file.txt'Kind=Literal, Resolved="/work/$VAR/file.txt" (literal under cwd)

B3 — LooksLikePath trailing-backslash carve-out

A double-quoted token like "foo\\" lexes to Value foo\ (escape collapse). The trailing \ is an artifact, not a path signal, but the heuristic was returning IsPath=true. Tightened: forward slash anywhere still counts; backslash counts only at a non-trailing position.

  • echo "trailing backslash\\"IsPath=false (was IsPath=true, Resolved=/work/trailing backslash)
  • cd dir/ → still IsPath=true, Resolved=/work/dir (regression guard)
  • path\to\file → still IsPath=true (non-trailing backslash)

What changed

File Change
src/ShellSyntaxTree/Internal/Bash/Lexing/BashToken.cs New IsSingleQuoted init-only flag on the internal record (additive).
src/ShellSyntaxTree/Internal/Bash/Lexing/BashLexer.cs ReadSingleQuoted sets IsSingleQuoted=true on its emitted token.
src/ShellSyntaxTree/Internal/Resolving/BashResolver.cs New Resolve overload accepting isLiteralBytes; LooksLikePath updated for backslash carve-out.
src/ShellSyntaxTree/Internal/Bash/Parsing/BashCommandParser.cs Threads token.IsSingleQuoted through to the resolver from the QuotedString case.
SPEC.md §8 New Step 0 (Single-quoted bypass) preamble + LooksLikePath heuristic clarification.
tests/.../Corpus/bash/104, 109 Updated to assert corrected outputs.
tests/.../Corpus/bash/119, 120, 121 New regression-guard entries.
Directory.Build.props + RELEASE_NOTES.md Bump to 0.1.2-alpha.

Public API impact

Zero. No new public types/fields, no signature changes. PublicApiSnapshotTests count unchanged.

Verification

  • pwsh ./scripts/Add-FileHeaders.ps1 -Verify — clean.
  • dotnet build -c Release0 warnings, 0 errors.
  • dotnet test -c Release359 / 359 passing (was 356; +3 new corpus entries).
  • dotnet pack -c Release -o ./bin/nugetShellSyntaxTree.0.1.2-alpha.nupkg + .snupkg produced cleanly.

Test plan

  • Corpus entries that previously pinned the bad behavior (104, 109) now assert the fixed outputs.
  • Regression guards for the correct behaviors that should NOT have shifted (119 single-quoted absolute path resolves, 120 cd trailing-slash still IsPath, 121 single-quoted env-var stays literal).
  • CI green on ubuntu-latest + windows-latest.

What follows this PR

After merge: push 0.1.2-alpha tag to fire trusted-publishing → nuget.org.

… (B3)

Two parser correctness fixes; public API surface unchanged.

B2 — single-quote literalness
  v0.1.0-alpha resolved `$HOME` uniformly regardless of quote style, so
  `echo '$HOME'` produced Kind=Tilde. Per SPEC §5 single-quoted contents
  are literal bytes — no variable expansion. Lexer now marks single-quoted
  QuotedString tokens with an internal IsSingleQuoted flag and the
  resolver short-circuits tilde / $HOME / $VAR / glob / filesystem::
  handling when set. `cat '/etc/passwd'` still resolves a literal absolute
  path; `echo '$HOME'` stays Kind=Literal.

B3 — LooksLikePath trailing-backslash carve-out
  A double-quoted token like `"foo\\"` lexes to Value `foo\` (escape
  collapse). The trailing `\` is an artifact, not a meaningful path
  signal, but the heuristic was returning IsPath=true. Tightened the
  rule: forward slash anywhere still counts, backslash counts only when
  it's at a non-trailing position. `dir/` still classifies as a path
  (trailing `/` is a meaningful bash directory hint).

Changes:

- src/ShellSyntaxTree/Internal/Bash/Lexing/BashToken.cs:
  new IsSingleQuoted init-only flag (additive on the internal record).
- src/ShellSyntaxTree/Internal/Bash/Lexing/BashLexer.cs:
  ReadSingleQuoted sets IsSingleQuoted=true on its emitted token.
- src/ShellSyntaxTree/Internal/Resolving/BashResolver.cs:
  new Resolve overload accepting isLiteralBytes; LooksLikePath updated
  for the backslash carve-out.
- src/ShellSyntaxTree/Internal/Bash/Parsing/BashCommandParser.cs:
  threads token.IsSingleQuoted through to the resolver from the
  QuotedString case.
- SPEC.md §8: new Step 0 (Single-quoted bypass) preamble +
  LooksLikePath heuristic clarification.
- tests/.../Corpus/bash/104, 109: updated to assert corrected outputs.
- tests/.../Corpus/bash/119, 120, 121: new regression guards
  (single-quoted absolute path still resolves, cd dir/ still a path,
  single-quoted $VAR/file stays literal).
- Directory.Build.props + RELEASE_NOTES.md: bump to 0.1.2-alpha.

Public API impact: zero. No PublicApiSnapshotTests delta.

dotnet build -c Release: 0 warnings, 0 errors.
dotnet test  -c Release: 359/359 passing (was 356; +3 entries).
dotnet pack: ShellSyntaxTree.0.1.2-alpha.nupkg + .snupkg clean.
@Aaronontheweb
Aaronontheweb enabled auto-merge (squash) May 11, 2026 15:27
@Aaronontheweb
Aaronontheweb merged commit 2102c33 into dev May 11, 2026
2 checks passed
@Aaronontheweb
Aaronontheweb deleted the fix/single-quote-literal-and-trailing-backslash branch May 11, 2026 15:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant