Skip to content

Commit 52ef299

Browse files
committed
feat(generate): properly filter out unused rules
1 parent 9d9c76e commit 52ef299

File tree

1 file changed

+31
-16
lines changed

1 file changed

+31
-16
lines changed

cli/generate/src/parse_grammar.rs

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -125,23 +125,32 @@ impl From<serde_json::Error> for ParseGrammarError {
125125
}
126126
}
127127

128-
fn rule_is_referenced(rule: &Rule, target: &str) -> bool {
128+
/// Check if a rule is referenced by another rule.
129+
///
130+
/// This function is used to determine if a variable is used in a given rule,
131+
/// and `is_other` indicates if the rule is an external, and if it is,
132+
/// to not assume that a named symbol that is equal to itself means it's being referenced.
133+
///
134+
/// For example, if we have an external rule **and** a normal rule both called `foo`,
135+
/// `foo` should not be thought of as directly used unless it's used within another rule.
136+
fn rule_is_referenced(rule: &Rule, target: &str, is_external: bool) -> bool {
129137
match rule {
130-
Rule::NamedSymbol(name) => name == target,
138+
Rule::NamedSymbol(name) => name == target && !is_external,
131139
Rule::Choice(rules) | Rule::Seq(rules) => {
132-
rules.iter().any(|r| rule_is_referenced(r, target))
140+
rules.iter().any(|r| rule_is_referenced(r, target, false))
133141
}
134142
Rule::Metadata { rule, .. } | Rule::Reserved { rule, .. } => {
135-
rule_is_referenced(rule, target)
143+
rule_is_referenced(rule, target, is_external)
136144
}
137-
Rule::Repeat(inner) => rule_is_referenced(inner, target),
145+
Rule::Repeat(inner) => rule_is_referenced(inner, target, false),
138146
Rule::Blank | Rule::String(_) | Rule::Pattern(_, _) | Rule::Symbol(_) => false,
139147
}
140148
}
141149

142150
fn variable_is_used(
143151
grammar_rules: &[(String, Rule)],
144-
other_rules: (&[Rule], &[Rule]),
152+
extras: &[Rule],
153+
externals: &[Rule],
145154
target_name: &str,
146155
in_progress: &mut HashSet<String>,
147156
) -> bool {
@@ -150,11 +159,16 @@ fn variable_is_used(
150159
return true;
151160
}
152161

153-
if other_rules
154-
.0
162+
if extras
155163
.iter()
156-
.chain(other_rules.1.iter())
157-
.any(|rule| rule_is_referenced(rule, target_name))
164+
.any(|rule| rule_is_referenced(rule, target_name, false))
165+
{
166+
return true;
167+
}
168+
169+
if externals
170+
.iter()
171+
.any(|rule| rule_is_referenced(rule, target_name, true))
158172
{
159173
return true;
160174
}
@@ -164,10 +178,10 @@ fn variable_is_used(
164178
.iter()
165179
.filter(|(key, _)| *key != target_name)
166180
.any(|(name, rule)| {
167-
if !rule_is_referenced(rule, target_name) || in_progress.contains(name) {
181+
if !rule_is_referenced(rule, target_name, false) || in_progress.contains(name) {
168182
return false;
169183
}
170-
variable_is_used(grammar_rules, other_rules, name, in_progress)
184+
variable_is_used(grammar_rules, extras, externals, name, in_progress)
171185
});
172186
in_progress.remove(target_name);
173187

@@ -224,16 +238,17 @@ pub(crate) fn parse_grammar(input: &str) -> ParseGrammarResult<InputGrammar> {
224238
for (name, rule) in &rules {
225239
if !variable_is_used(
226240
&rules,
227-
(&extra_symbols, &external_tokens),
241+
&extra_symbols,
242+
&external_tokens,
228243
name,
229244
&mut in_progress,
230-
) && grammar_json.word.as_ref().is_some_and(|w| w != name)
245+
) && grammar_json.word.as_ref().is_none_or(|w| w != name)
231246
{
232247
grammar_json.conflicts.retain(|r| !r.contains(name));
233248
grammar_json.supertypes.retain(|r| r != name);
234249
grammar_json.inline.retain(|r| r != name);
235-
extra_symbols.retain(|r| !rule_is_referenced(r, name));
236-
external_tokens.retain(|r| !rule_is_referenced(r, name));
250+
extra_symbols.retain(|r| !rule_is_referenced(r, name, true));
251+
external_tokens.retain(|r| !rule_is_referenced(r, name, true));
237252
precedence_orderings.retain(|r| {
238253
!r.iter().any(|e| {
239254
let PrecedenceEntry::Symbol(s) = e else {

0 commit comments

Comments
 (0)