tree-sitter/cli/generate/src/parse_grammar.rs

334 lines
9.4 KiB
Rust
Raw Normal View History

2024-10-03 14:39:41 -04:00
use std::collections::HashSet;
use anyhow::{anyhow, Result};
2021-11-21 13:39:30 -08:00
use serde::Deserialize;
use serde_json::{Map, Value};
2018-12-05 12:50:12 -08:00
2024-04-09 13:35:08 -04:00
use super::{
grammars::{InputGrammar, PrecedenceEntry, Variable, VariableType},
rules::{Precedence, Rule},
};
2018-12-05 12:50:12 -08:00
#[derive(Deserialize)]
#[serde(tag = "type")]
#[allow(non_camel_case_types)]
2024-02-04 01:30:33 -05:00
#[allow(clippy::upper_case_acronyms)]
2018-12-06 22:11:52 -08:00
enum RuleJSON {
2018-12-20 13:35:13 -08:00
ALIAS {
content: Box<RuleJSON>,
named: bool,
value: String,
},
2018-12-05 12:50:12 -08:00
BLANK,
STRING {
value: String,
},
PATTERN {
value: String,
flags: Option<String>,
2018-12-05 12:50:12 -08:00
},
SYMBOL {
name: String,
},
CHOICE {
members: Vec<RuleJSON>,
},
2019-02-07 12:29:20 -08:00
FIELD {
name: String,
content: Box<RuleJSON>,
},
2018-12-05 12:50:12 -08:00
SEQ {
members: Vec<RuleJSON>,
},
REPEAT {
content: Box<RuleJSON>,
},
2018-12-20 13:35:13 -08:00
REPEAT1 {
content: Box<RuleJSON>,
},
PREC_DYNAMIC {
value: i32,
content: Box<RuleJSON>,
},
2018-12-05 12:50:12 -08:00
PREC_LEFT {
value: PrecedenceValueJSON,
2018-12-05 12:50:12 -08:00
content: Box<RuleJSON>,
},
PREC_RIGHT {
value: PrecedenceValueJSON,
2018-12-05 12:50:12 -08:00
content: Box<RuleJSON>,
},
PREC {
value: PrecedenceValueJSON,
2018-12-05 12:50:12 -08:00
content: Box<RuleJSON>,
},
TOKEN {
content: Box<RuleJSON>,
},
2018-12-20 13:35:13 -08:00
IMMEDIATE_TOKEN {
2018-12-05 12:50:12 -08:00
content: Box<RuleJSON>,
},
}
#[derive(Deserialize)]
#[serde(untagged)]
enum PrecedenceValueJSON {
Integer(i32),
Name(String),
}
2018-12-05 12:50:12 -08:00
#[derive(Deserialize)]
pub struct GrammarJSON {
pub name: String,
2018-12-05 12:50:12 -08:00
rules: Map<String, Value>,
2021-02-24 16:02:56 -08:00
#[serde(default)]
precedences: Vec<Vec<RuleJSON>>,
2021-02-24 16:02:56 -08:00
#[serde(default)]
conflicts: Vec<Vec<String>>,
#[serde(default)]
externals: Vec<RuleJSON>,
#[serde(default)]
extras: Vec<RuleJSON>,
#[serde(default)]
inline: Vec<String>,
#[serde(default)]
supertypes: Vec<String>,
2018-12-05 12:50:12 -08:00
word: Option<String>,
}
2024-10-03 14:39:41 -04:00
fn rule_is_referenced(rule: &Rule, target: &str) -> bool {
match rule {
Rule::NamedSymbol(name) => name == target,
Rule::Choice(rules) | Rule::Seq(rules) => {
rules.iter().any(|r| rule_is_referenced(r, target))
}
Rule::Metadata { rule, .. } => rule_is_referenced(rule, target),
Rule::Repeat(inner) => rule_is_referenced(inner, target),
Rule::Blank | Rule::String(_) | Rule::Pattern(_, _) | Rule::Symbol(_) => false,
}
}
fn variable_is_used(
grammar_rules: &[(String, Rule)],
extras: &[Rule],
externals: &[Rule],
target_name: &str,
in_progress: &mut HashSet<String>,
) -> bool {
let root = &grammar_rules.first().unwrap().0;
if target_name == root {
return true;
}
if extras
.iter()
.chain(externals.iter())
.any(|rule| rule_is_referenced(rule, target_name))
{
return true;
}
in_progress.insert(target_name.to_string());
let result = grammar_rules
.iter()
.filter(|(key, _)| *key != target_name)
.any(|(name, rule)| {
if !rule_is_referenced(rule, target_name) || in_progress.contains(name) {
return false;
}
variable_is_used(grammar_rules, extras, externals, name, in_progress)
});
in_progress.remove(target_name);
result
}
2018-12-06 22:11:52 -08:00
pub(crate) fn parse_grammar(input: &str) -> Result<InputGrammar> {
2024-10-03 14:39:41 -04:00
let mut grammar_json = serde_json::from_str::<GrammarJSON>(input)?;
let mut extra_symbols =
grammar_json
.extras
.into_iter()
.try_fold(Vec::new(), |mut acc, item| {
let rule = parse_rule(item);
if let Rule::String(ref value) = rule {
if value.is_empty() {
return Err(anyhow!(
"Rules in the `extras` array must not contain empty strings"
));
}
}
acc.push(rule);
Ok(acc)
})?;
let mut external_tokens = grammar_json
.externals
.into_iter()
.map(parse_rule)
.collect::<Vec<_>>();
2018-12-05 12:50:12 -08:00
let mut variables = Vec::with_capacity(grammar_json.rules.len());
2024-10-03 14:39:41 -04:00
let rules = grammar_json
.rules
.into_iter()
.map(|(n, r)| Ok((n, parse_rule(serde_json::from_value(r)?))))
.collect::<Result<Vec<_>>>()?;
let mut in_progress = HashSet::new();
for (name, rule) in &rules {
if !variable_is_used(
&rules,
&extra_symbols,
&external_tokens,
name,
&mut in_progress,
) {
extra_symbols.retain(|r| !rule_is_referenced(r, name));
external_tokens.retain(|r| !rule_is_referenced(r, name));
grammar_json.supertypes.retain(|r| r != name);
continue;
}
2018-12-06 22:11:52 -08:00
variables.push(Variable {
2024-02-04 01:30:33 -05:00
name: name.clone(),
2018-12-05 12:50:12 -08:00
kind: VariableType::Named,
2024-10-03 14:39:41 -04:00
rule: rule.clone(),
2024-02-04 01:30:33 -05:00
});
2018-12-05 12:50:12 -08:00
}
let mut precedence_orderings = Vec::with_capacity(grammar_json.precedences.len());
for list in grammar_json.precedences {
let mut ordering = Vec::with_capacity(list.len());
for entry in list {
ordering.push(match entry {
RuleJSON::STRING { value } => PrecedenceEntry::Name(value),
RuleJSON::SYMBOL { name } => PrecedenceEntry::Symbol(name),
_ => {
return Err(anyhow!(
"Invalid rule in precedences array. Only strings and symbols are allowed"
))
}
2024-02-04 01:30:33 -05:00
});
}
precedence_orderings.push(ordering);
}
2018-12-05 12:50:12 -08:00
Ok(InputGrammar {
name: grammar_json.name,
word_token: grammar_json.word,
2021-02-24 16:02:56 -08:00
expected_conflicts: grammar_json.conflicts,
supertype_symbols: grammar_json.supertypes,
variables_to_inline: grammar_json.inline,
precedence_orderings,
2018-12-05 12:50:12 -08:00
variables,
2019-10-21 17:26:01 -07:00
extra_symbols,
2018-12-05 12:50:12 -08:00
external_tokens,
})
}
fn parse_rule(json: RuleJSON) -> Rule {
match json {
RuleJSON::ALIAS {
content,
value,
named,
} => Rule::alias(parse_rule(*content), value, named),
2018-12-05 12:50:12 -08:00
RuleJSON::BLANK => Rule::Blank,
RuleJSON::STRING { value } => Rule::String(value),
RuleJSON::PATTERN { value, flags } => Rule::Pattern(
value,
flags.map_or(String::new(), |f| {
2024-04-09 21:42:59 -04:00
f.matches(|c| {
if c == 'i' {
true
} else {
// silently ignore unicode flags
if c != 'u' && c != 'v' {
eprintln!("Warning: unsupported flag {c}");
}
2024-04-09 21:42:59 -04:00
false
}
})
.collect()
}),
),
2018-12-05 12:50:12 -08:00
RuleJSON::SYMBOL { name } => Rule::NamedSymbol(name),
RuleJSON::CHOICE { members } => Rule::choice(members.into_iter().map(parse_rule).collect()),
2019-02-07 12:29:20 -08:00
RuleJSON::FIELD { content, name } => Rule::field(name, parse_rule(*content)),
2018-12-05 12:50:12 -08:00
RuleJSON::SEQ { members } => Rule::seq(members.into_iter().map(parse_rule).collect()),
2018-12-20 13:35:13 -08:00
RuleJSON::REPEAT1 { content } => Rule::repeat(parse_rule(*content)),
RuleJSON::REPEAT { content } => {
Rule::choice(vec![Rule::repeat(parse_rule(*content)), Rule::Blank])
}
RuleJSON::PREC { value, content } => Rule::prec(value.into(), parse_rule(*content)),
RuleJSON::PREC_LEFT { value, content } => {
Rule::prec_left(value.into(), parse_rule(*content))
}
RuleJSON::PREC_RIGHT { value, content } => {
Rule::prec_right(value.into(), parse_rule(*content))
}
RuleJSON::PREC_DYNAMIC { value, content } => {
Rule::prec_dynamic(value, parse_rule(*content))
}
2018-12-05 12:50:12 -08:00
RuleJSON::TOKEN { content } => Rule::token(parse_rule(*content)),
2018-12-20 13:35:13 -08:00
RuleJSON::IMMEDIATE_TOKEN { content } => Rule::immediate_token(parse_rule(*content)),
2018-12-05 12:50:12 -08:00
}
}
2024-02-04 01:30:33 -05:00
impl From<PrecedenceValueJSON> for Precedence {
fn from(val: PrecedenceValueJSON) -> Self {
match val {
PrecedenceValueJSON::Integer(i) => Self::Integer(i),
PrecedenceValueJSON::Name(i) => Self::Name(i),
}
}
}
2018-12-05 12:50:12 -08:00
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_grammar() {
let grammar = parse_grammar(
r#"{
2018-12-05 12:50:12 -08:00
"name": "my_lang",
"rules": {
"file": {
2018-12-20 13:35:13 -08:00
"type": "REPEAT1",
2018-12-05 12:50:12 -08:00
"content": {
"type": "SYMBOL",
"name": "statement"
}
},
"statement": {
"type": "STRING",
"value": "foo"
}
}
}"#,
)
.unwrap();
2018-12-05 12:50:12 -08:00
assert_eq!(grammar.name, "my_lang");
assert_eq!(
grammar.variables,
vec![
Variable {
name: "file".to_string(),
kind: VariableType::Named,
rule: Rule::repeat(Rule::NamedSymbol("statement".to_string()))
},
Variable {
name: "statement".to_string(),
kind: VariableType::Named,
rule: Rule::String("foo".to_string())
},
]
);
2018-12-05 12:50:12 -08:00
}
}