From d45b98c7d994f3ac37a3d17c21116227a95b20bf Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 14 Nov 2019 09:31:25 -0800 Subject: [PATCH] node-types - Fix duplicate when a terminal is aliased with the same name as a non-terminal --- cli/src/generate/node_types.rs | 78 +++++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 5 deletions(-) diff --git a/cli/src/generate/node_types.rs b/cli/src/generate/node_types.rs index a145c93b..af90b900 100644 --- a/cli/src/generate/node_types.rs +++ b/cli/src/generate/node_types.rs @@ -628,7 +628,7 @@ pub(crate) fn generate_node_types_json( } } - let mut result = node_types_json.into_iter().map(|e| e.1).collect::>(); + let mut anonymous_node_types = Vec::new(); for (i, variable) in lexical_grammar.variables.iter().enumerate() { for alias in aliases_by_symbol @@ -645,26 +645,36 @@ pub(crate) fn generate_node_types_json( is_named = variable.kind == VariableType::Named; } - if variable.kind == VariableType::Named { - result.push(NodeInfoJSON { + if is_named { + let node_type_json = node_types_json.entry(kind.clone()).or_insert(NodeInfoJSON { kind: kind.clone(), named: is_named, fields: None, children: None, subtypes: None, }); + if let Some(children) = &mut node_type_json.children { + children.required = false; + } + if let Some(fields) = &mut node_type_json.fields { + for (_, field) in fields.iter_mut() { + field.required = false; + } + } } else if variable.kind == VariableType::Anonymous { - result.push(NodeInfoJSON { + anonymous_node_types.push(NodeInfoJSON { kind: kind.clone(), named: is_named, fields: None, children: None, subtypes: None, - }); + }) } } } + let mut result = node_types_json.into_iter().map(|e| e.1).collect::>(); + result.extend(anonymous_node_types.into_iter()); result.sort_unstable_by(|a, b| { b.subtypes .is_some() @@ -1159,6 +1169,64 @@ mod tests { ); } + #[test] + fn test_node_types_with_tokens_aliased_to_match_rules() { + let node_types = get_node_types(InputGrammar { + name: String::new(), + extra_symbols: Vec::new(), + external_tokens: Vec::new(), + expected_conflicts: Vec::new(), + variables_to_inline: Vec::new(), + word_token: None, + supertype_symbols: vec![], + variables: vec![ + Variable { + name: "a".to_string(), + kind: VariableType::Named, + rule: Rule::seq(vec![Rule::named("b"), Rule::named("c")]), + }, + // Ordinarily, `b` nodes have two named `c` children. + Variable { + name: "b".to_string(), + kind: VariableType::Named, + rule: Rule::seq(vec![Rule::named("c"), Rule::string("B"), Rule::named("c")]), + }, + Variable { + name: "c".to_string(), + kind: VariableType::Named, + rule: Rule::choice(vec![ + Rule::string("C"), + // This token is aliased as a `b`, which will produce a `b` node + // with no children. + Rule::alias(Rule::string("D"), "b".to_string(), true), + ]), + }, + ], + }); + + assert_eq!( + node_types.iter().map(|n| &n.kind).collect::>(), + &["a", "b", "c", "B", "C"] + ); + assert_eq!( + node_types[1], + NodeInfoJSON { + kind: "b".to_string(), + named: true, + subtypes: None, + children: Some(FieldInfoJSON { + multiple: true, + required: false, + types: vec![NodeTypeJSON { + kind: "c".to_string(), + named: true, + }] + }), + fields: Some(BTreeMap::new()), + } + ); + } + #[test] fn test_get_variable_info() { let variable_info = get_variable_info(