use indexmap::IndexMap;
use serde::Deserialize;
use super::types::{Grammar, Precedence, PrecedenceEntry, Rule};
#[derive(Debug)]
pub enum GrammarError {
Json(serde_json::Error),
Binary(postcard::Error),
}
impl std::fmt::Display for GrammarError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Json(e) => write!(f, "JSON parse error: {e}"),
Self::Binary(e) => write!(f, "binary decode error: {e}"),
}
}
}
impl std::error::Error for GrammarError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Json(e) => Some(e),
Self::Binary(e) => Some(e),
}
}
}
impl Grammar {
pub fn from_json(json: &str) -> Result<Self, GrammarError> {
let raw: RawGrammar = serde_json::from_str(json).map_err(GrammarError::Json)?;
Ok(raw.into())
}
}
#[derive(Debug, Deserialize)]
struct RawGrammar {
name: String,
rules: IndexMap<String, RawRule>,
#[serde(default)]
extras: Vec<RawRule>,
#[serde(default)]
precedences: Vec<Vec<RawPrecedenceEntry>>,
#[serde(default)]
conflicts: Vec<Vec<String>>,
#[serde(default)]
externals: Vec<RawRule>,
#[serde(default, rename = "inline")]
inline_rules: Vec<String>,
#[serde(default)]
supertypes: Vec<String>,
#[serde(default)]
word: Option<String>,
#[serde(default)]
reserved: IndexMap<String, Vec<RawRule>>,
#[serde(default)]
inherits: Option<String>,
}
impl From<RawGrammar> for Grammar {
fn from(raw: RawGrammar) -> Self {
Self {
name: raw.name,
rules: raw.rules.into_iter().map(|(k, v)| (k, v.into())).collect(),
extras: raw.extras.into_iter().map(Into::into).collect(),
precedences: raw
.precedences
.into_iter()
.map(|v| v.into_iter().map(Into::into).collect())
.collect(),
conflicts: raw.conflicts,
externals: raw.externals.into_iter().map(Into::into).collect(),
inline: raw.inline_rules,
supertypes: raw.supertypes,
word: raw.word,
reserved: raw
.reserved
.into_iter()
.map(|(k, v)| (k, v.into_iter().map(Into::into).collect()))
.collect(),
inherits: raw.inherits,
}
}
}
#[derive(Debug, Deserialize)]
#[serde(tag = "type")]
#[allow(clippy::upper_case_acronyms, non_camel_case_types)]
enum RawRule {
BLANK,
STRING {
value: String,
},
PATTERN {
value: String,
#[serde(default)]
flags: Option<String>,
},
SYMBOL {
name: String,
},
SEQ {
members: Vec<RawRule>,
},
CHOICE {
members: Vec<RawRule>,
},
REPEAT {
content: Box<RawRule>,
},
REPEAT1 {
content: Box<RawRule>,
},
FIELD {
name: String,
content: Box<RawRule>,
},
ALIAS {
content: Box<RawRule>,
value: String,
named: bool,
},
TOKEN {
content: Box<RawRule>,
},
IMMEDIATE_TOKEN {
content: Box<RawRule>,
},
PREC {
value: RawPrecedence,
content: Box<RawRule>,
},
PREC_LEFT {
value: RawPrecedence,
content: Box<RawRule>,
},
PREC_RIGHT {
value: RawPrecedence,
content: Box<RawRule>,
},
PREC_DYNAMIC {
value: i32,
content: Box<RawRule>,
},
RESERVED {
context_name: String,
content: Box<RawRule>,
},
}
impl From<RawRule> for Rule {
fn from(raw: RawRule) -> Self {
#[allow(clippy::boxed_local)] fn conv(content: Box<RawRule>) -> Box<Rule> {
Box::new(Rule::from(*content))
}
match raw {
RawRule::BLANK => Rule::Blank,
RawRule::STRING { value } => Rule::String(value),
RawRule::PATTERN { value, flags } => Rule::Pattern { value, flags },
RawRule::SYMBOL { name } => Rule::Symbol(name),
RawRule::SEQ { members } => Rule::Seq(members.into_iter().map(Into::into).collect()),
RawRule::CHOICE { members } => {
Rule::Choice(members.into_iter().map(Into::into).collect())
}
RawRule::REPEAT { content } => Rule::Repeat(conv(content)),
RawRule::REPEAT1 { content } => Rule::Repeat1(conv(content)),
RawRule::FIELD { name, content } => Rule::Field {
name,
content: conv(content),
},
RawRule::ALIAS {
content,
value,
named,
} => Rule::Alias {
content: conv(content),
value,
named,
},
RawRule::TOKEN { content } => Rule::Token(conv(content)),
RawRule::IMMEDIATE_TOKEN { content } => Rule::ImmediateToken(conv(content)),
RawRule::PREC { value, content } => Rule::Prec {
value: value.into(),
content: conv(content),
},
RawRule::PREC_LEFT { value, content } => Rule::PrecLeft {
value: value.into(),
content: conv(content),
},
RawRule::PREC_RIGHT { value, content } => Rule::PrecRight {
value: value.into(),
content: conv(content),
},
RawRule::PREC_DYNAMIC { value, content } => Rule::PrecDynamic {
value,
content: conv(content),
},
RawRule::RESERVED {
context_name,
content,
} => Rule::Reserved {
context_name,
content: conv(content),
},
}
}
}
#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum RawPrecedence {
Integer(i32),
Name(String),
}
impl From<RawPrecedence> for Precedence {
fn from(raw: RawPrecedence) -> Self {
match raw {
RawPrecedence::Integer(n) => Precedence::Integer(n),
RawPrecedence::Name(s) => Precedence::Name(s),
}
}
}
#[derive(Debug, Deserialize)]
#[serde(tag = "type")]
#[allow(clippy::upper_case_acronyms)]
enum RawPrecedenceEntry {
STRING { value: String },
SYMBOL { name: String },
}
impl From<RawPrecedenceEntry> for PrecedenceEntry {
fn from(raw: RawPrecedenceEntry) -> Self {
match raw {
RawPrecedenceEntry::STRING { value } => PrecedenceEntry::Name(value),
RawPrecedenceEntry::SYMBOL { name } => PrecedenceEntry::Symbol(name),
}
}
}