Repository navigation
feat: add Alias option for reusable named grammar fragments - #474
Pastalikek65 wants to merge 1 commit into
Conversation
Alias(name, grammar) registers a named, reusable grammar fragment referenced from struct tags as <name>. Aliases are non-capturing: they match literals and named tokens (and compose via normal tag operators such as ? * + and grouping) but cannot bind values, since captures (@ and @@) always belong to a struct field of the surrounding production — such use is rejected at build time with a clear error. - options.go: Alias option appends aliasDef. - grammar.go: <name> syntax in parseTermNoModifiers via parseAlias, generatorContext.aliases map, addAliasDefs with extraction guard. - struct.go: lexStructFromTag parses a bare grammar string. - alias_test.go: 10 tests (pattern, reuse across fields, multiple aliases, modifiers, capture/struct-capture rejection, unknown ref, duplicate, empty name). Closes alecthomas#22.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: df48c72f89
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if strings.Contains(grammar, "@") { | ||
| return nil, fmt.Errorf("alias %q: aliases cannot capture (@); move captures to a struct field", name) |
There was a problem hiding this comment.
Detect captures lexically instead of rejecting every @ character
When a valid non-capturing alias matches an @ literal, such as Alias("at", "@"), this substring check rejects it as a capture before the grammar is lexed. This prevents aliases from matching any literal containing @ (and even contradicts the literal used in the exported example); inspect the grammar tokens for capture operators rather than treating every raw @ byte as one.
Useful? React with 👍 / 👎.
| n, err := parse(def.name, def.grammar) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| g.aliases[def.name] = n |
There was a problem hiding this comment.
Resolve alias dependencies independently of option order
When one alias references another alias registered later, for example Alias("outer", ), Alias("inner", ":"), parsing outer here fails with unknown grammar alias "inner"; reversing the two otherwise identical options succeeds. Since alias bodies support alias references and the public API discusses transitive references, declaration order should not determine whether an acyclic alias graph is valid; register names first or resolve bodies lazily/topologically.
Useful? React with 👍 / 👎.
| // from struct tags with <name>. It is useful for sharing a common expression | ||
| // shape across multiple fields without repeating the tag text. | ||
| // | ||
| // participle.Alias("jQName", `"@" @Name ":" @Name`), |
There was a problem hiding this comment.
Replace the Alias example that cannot be built
Copying this exported API example always makes Build return aliases cannot capture (@) because both @Name terms are captures, while the implementation explicitly forbids captures inside aliases. Use a genuinely non-capturing fragment and place any captures in the surrounding struct tag so the documented example demonstrates supported behavior.
Useful? React with 👍 / 👎.
Closes #22
Adds
participle.Alias(name, grammar)— named, reusable grammar fragments referenced from struct tags as<name>:Design
? * + !, groups, disjunction), but cannot bind values. Captures (@and@@) always target a struct field of the surrounding production — an alias has no field, so@/@@in an alias body is rejected at build time with a clear message (aliases cannot capture (@); move captures to a struct field). This avoids the index-out-of-range panic that naive attempts hit during development.<name>inparseTermNoModifiers(grammar.go), resolved fromgeneratorContext.aliases.<was otherwise unused in the tag grammar, so no ambiguity with named token references.lexStructFromTag(struct.go): a bare grammar string is lexed without a backing struct type.Tests
10 tests: literal patterns, alias reuse across fields, multiple aliases,
?modifier,@/@@rejection, unknown reference, duplicate, empty name.One open question for maintainer: whether aliases should be capturing by binding to an implicit "value" the referenced field receives (e.g. a special syntax
<name>meaning the fragment's captures flow into the field). This would require threads of field context through alias resolution beyond build time; offered as a follow-up rather than blocking this PR.