Skip to content

parse: fix bare-body parallel — block's own parens wrap the parallel#25

Merged
vinodhalaharvi merged 1 commit into
mainfrom
claude/fix-bare-body-parallel
May 25, 2026
Merged

parse: fix bare-body parallel — block's own parens wrap the parallel#25
vinodhalaharvi merged 1 commit into
mainfrom
claude/fix-bare-body-parallel

Conversation

@vinodhalaharvi-claude
Copy link
Copy Markdown
Collaborator

The bug (live in Slack, after #24)

#24 shipped <*> support but required a redundant inner paren — it only accepted ( ( a <*> b ) ), not the bare ( a <*> b ) the original grammar and the LLM actually use. The bare form failed with:

script.Parse: 1:40: unexpected token "<*>" (expected ")")

That's the exact error from @loom in memory: check the SSL for google.com and DNS for github.com at the same time.

Root cause

The block body was a *parsedPipeline (sequential-only); <*> lived only inside an inner group. So the block's own ( ... ) couldn't be a parallel. The original internal/agentscript grammar lets the block's own parens wrap a parallel — this now matches.

Fix (pkg/script/parse.go)

expr     = pipeline ( <*> pipeline )*   // 1 ⇒ sequential, 2+ ⇒ parallel
pipeline = stage ( >=> stage )*
stage    = call | "(" expr ")"
block    = backend mode "(" expr ")"     // body IS an expr → may be parallel

Block body is now a parsedExpr; a group wraps a parsedExpr. exprToAST emits ast.Parallel for multi-branch exprs, wrapped in a one-stage Pipeline so Block.Body stays an ast.Pipeline (invariant preserved — no regression to existing parse tests).

Verified end-to-end (the exact Slack shape)

memory static ( ssl_check "google.com" <*> dns_lookup "github.com" )
→ parse → resolve → RunMemory → BOTH branches execute ✓

Plus 3-way, nested-then-pipe, and ( search >=> analyze <*> search >=> analyze ) >=> merge >=> ask.

Tests

parse_parallel_test.go: bare-body parallel → Parallel stage; 3-way; nested; sequential-is-not-parallel; complex-resolves. Replaced the obsolete reject-parallel assertion. All prior parse tests pass.

Step Result
vet / gofmt / staticcheck
go test -race ./pkg/...
build

Follow-up to #24, which shipped parallel <*> support but with a bug: it
required a REDUNDANT inner paren — accepting only ( ( a <*> b ) ), not the
bare ( a <*> b ) that the original grammar and the LLM actually use. The
bare form failed live in Slack with:
  script.Parse: 1:40: unexpected token "<*>" (expected ")")

Root cause: the block body was a *parsedPipeline (sequential-only), and
<*> was a property of an inner group only. So the block's own ( ... )
could not itself be a parallel. The original internal/agentscript grammar
lets the block's own parens wrap a parallel (Statement = ( "(" Parallel
")" | Command )); this now matches.

Grammar (pkg/script/parse.go):
  expr     = pipeline ( <*> pipeline )*   // 1 ⇒ sequential, 2+ ⇒ parallel
  pipeline = stage ( >=> stage )*
  stage    = call | "(" expr ")"         // group = nesting/precedence
  block    = backend mode "(" expr ")"     // body IS an expr → may be parallel
The block body is now a parsedExpr; a parsedGroup wraps a parsedExpr.

toAST: exprToAST emits ast.Parallel for multi-branch exprs, wrapped in a
one-stage Pipeline so Block.Body stays an ast.Pipeline (the invariant
Lower + the memory bridge rely on); groupToAST unwraps a parallel stage
inside a group. The always-Pipeline body contract is preserved.

Verified end to end through the new pipeline:
  memory static ( ssl_check "google.com" <*> dns_lookup "github.com" )
  → parse → resolve → RunMemory → BOTH branches execute. (Exact Slack shape.)
Plus 3-way, nested-then-pipe, and
  ( search >=> analyze <*> search >=> analyze ) >=> merge >=> ask "..."

Tests: parse_parallel_test.go covers bare-body parallel (→ Parallel
stage), 3-way, nested, sequential-is-not-parallel, complex-resolves.
Replaced obsolete reject-parallel assertion. All prior parse tests pass.

CI: vet, gofmt, staticcheck, go test -race ./pkg/..., build pass.
@vinodhalaharvi vinodhalaharvi merged commit e3a37f6 into main May 25, 2026
1 check passed
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.

2 participants