use std::collections::BTreeSet;
use super::{CharClassItem, GrammarExpr};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum FieldType {
Choice(&'static [&'static str]),
Integer,
Name,
Text,
Expression,
Expressions,
Condition,
Value,
Conditions,
Block(&'static str, bool),
}
pub type FormFields = &'static [(&'static str, FieldType)];
pub const FEATURE_EXPRESSION_FORMS: &[(&str, FormFields)] = &[
(
"precedence",
&[
("level", FieldType::Integer),
(
"associativity",
FieldType::Choice(&["left", "right", "none"]),
),
("item", FieldType::Expression),
],
),
(
"namedPrecedence",
&[
("name", FieldType::Name),
(
"associativity",
FieldType::Choice(&["left", "right", "none"]),
),
("item", FieldType::Expression),
],
),
(
"dynamicPrecedence",
&[
("level", FieldType::Integer),
("item", FieldType::Expression),
],
),
(
"lexicalPrecedence",
&[
("level", FieldType::Integer),
("item", FieldType::Expression),
],
),
("longest", &[("items", FieldType::Expressions)]),
("token", &[("item", FieldType::Expression)]),
("immediateToken", &[("item", FieldType::Expression)]),
(
"alias",
&[("name", FieldType::Name), ("item", FieldType::Expression)],
),
("parameter", &[("name", FieldType::Name)]),
(
"predicate",
&[
("item", FieldType::Expression),
("condition", FieldType::Condition),
],
),
(
"recover",
&[
("item", FieldType::Expression),
("synchronize", FieldType::Expression),
],
),
("missing", &[("item", FieldType::Expression)]),
(
"embed",
&[
("language", FieldType::Name),
("item", FieldType::Expression),
],
),
(
"expand",
&[
("name", FieldType::Name),
("arguments", FieldType::Expressions),
],
),
];
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum OperationCategory {
Statement,
Condition,
Value,
}
impl OperationCategory {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Self::Statement => "statement",
Self::Condition => "condition",
Self::Value => "value",
}
}
}
const TWO_VALUES: FormFields = &[("left", FieldType::Value), ("right", FieldType::Value)];
const STATEMENT: OperationCategory = OperationCategory::Statement;
const CONDITION: OperationCategory = OperationCategory::Condition;
const VALUE: OperationCategory = OperationCategory::Value;
pub const OPERATION_FORMS: &[(&str, OperationCategory, FormFields)] = &[
("advance", STATEMENT, &[]),
("consume", STATEMENT, &[("item", FieldType::Expression)]),
("skip", STATEMENT, &[("item", FieldType::Expression)]),
("mark", STATEMENT, &[]),
("emit", STATEMENT, &[("token", FieldType::Name)]),
("fail", STATEMENT, &[]),
(
"if",
STATEMENT,
&[
("condition", FieldType::Condition),
("consequent", FieldType::Block("then", false)),
("alternative", FieldType::Block("else", true)),
],
),
(
"while",
STATEMENT,
&[
("condition", FieldType::Condition),
("body", FieldType::Block("do", false)),
],
),
(
"push",
STATEMENT,
&[("stack", FieldType::Name), ("value", FieldType::Value)],
),
("pop", STATEMENT, &[("stack", FieldType::Name)]),
(
"set",
STATEMENT,
&[("variable", FieldType::Name), ("value", FieldType::Value)],
),
("pushMode", STATEMENT, &[("mode", FieldType::Name)]),
("popMode", STATEMENT, &[]),
("setMode", STATEMENT, &[("mode", FieldType::Name)]),
(
"setAttribute",
STATEMENT,
&[("attribute", FieldType::Name), ("value", FieldType::Value)],
),
("buildNode", STATEMENT, &[("kind", FieldType::Name)]),
("valid", CONDITION, &[("token", FieldType::Name)]),
("expected", CONDITION, &[("item", FieldType::Expression)]),
("next", CONDITION, &[("item", FieldType::Expression)]),
("atEnd", CONDITION, &[]),
("equal", CONDITION, TWO_VALUES),
("less", CONDITION, TWO_VALUES),
("greater", CONDITION, TWO_VALUES),
("all", CONDITION, &[("conditions", FieldType::Conditions)]),
("some", CONDITION, &[("conditions", FieldType::Conditions)]),
("not", CONDITION, &[("condition", FieldType::Condition)]),
("integer", VALUE, &[("value", FieldType::Integer)]),
("text", VALUE, &[("value", FieldType::Text)]),
("variable", VALUE, &[("name", FieldType::Name)]),
("top", VALUE, &[("stack", FieldType::Name)]),
("depth", VALUE, &[("stack", FieldType::Name)]),
("column", VALUE, &[]),
("matched", VALUE, &[]),
("mode", VALUE, &[]),
(
"attribute",
VALUE,
&[("field", FieldType::Name), ("attribute", FieldType::Name)],
),
(
"sumOf",
VALUE,
&[("field", FieldType::Name), ("attribute", FieldType::Name)],
),
("fieldText", VALUE, &[("field", FieldType::Name)]),
("length", VALUE, &[("value", FieldType::Value)]),
("number", VALUE, &[("value", FieldType::Value)]),
("add", VALUE, TWO_VALUES),
("subtract", VALUE, TWO_VALUES),
("multiply", VALUE, TWO_VALUES),
];
pub const MATCHING_MODES: &[&str] = &["generalized", "peg", "longest"];
pub const SETTLING_STEPS: &[&str] = &["tokens", "precedence", "dynamic", "first", "ambiguity"];
#[must_use]
pub fn default_settling(matching: &str) -> &'static [&'static str] {
match matching {
"peg" => &["first"],
"longest" => &["tokens", "precedence", "dynamic", "ambiguity"],
_ => &["dynamic", "ambiguity"],
}
}
#[must_use]
pub fn settling_problem<S: AsRef<str>>(steps: &[S]) -> Option<String> {
if steps.is_empty() {
return Some("the settling names no step".to_owned());
}
for (index, step) in steps.iter().enumerate() {
let step = step.as_ref();
if !SETTLING_STEPS.contains(&step) {
return Some(format!("unknown settling step {step}"));
}
if steps.iter().position(|other| other.as_ref() == step) != Some(index) {
return Some(format!("the settling step {step} is given twice"));
}
let tie = step == "first" || step == "ambiguity";
if tie != (index == steps.len() - 1) {
return Some("the settling ends with exactly one of first and ambiguity".to_owned());
}
}
None
}
pub const MAX_INTEGER_DIGITS: usize = 15;
#[must_use]
pub fn feature_expression_fields(head: &str) -> Option<FormFields> {
FEATURE_EXPRESSION_FORMS
.iter()
.find(|(name, _)| *name == head)
.map(|(_, fields)| *fields)
}
#[must_use]
pub fn operation_form(head: &str) -> Option<(OperationCategory, FormFields)> {
OPERATION_FORMS
.iter()
.find(|(name, _, _)| *name == head)
.map(|(_, category, fields)| (*category, *fields))
}
pub fn check_integer(text: &str) -> Result<i64, String> {
let digits = text.strip_prefix('-').unwrap_or(text);
if digits.is_empty() || !digits.bytes().all(|byte| byte.is_ascii_digit()) {
return Err(format!("expected an integer, not {text}"));
}
if digits.len() > MAX_INTEGER_DIGITS {
return Err(format!("integer {text} is too large"));
}
text.parse()
.map_err(|_| format!("expected an integer, not {text}"))
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum FieldValue {
Word(String),
Integer(i64),
Name(String),
Text(String),
Expression(GrammarExpr),
Expressions(Vec<GrammarExpr>),
Operation(FeatureForm),
Operations(Vec<FeatureForm>),
Block(Option<Vec<FeatureForm>>),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FeatureForm {
pub head: String,
pub fields: Vec<FieldValue>,
}
pub type Operation = FeatureForm;
impl FeatureForm {
#[must_use]
pub fn new(head: impl Into<String>, fields: Vec<FieldValue>) -> Self {
Self {
head: head.into(),
fields,
}
}
#[must_use]
pub fn spec(&self) -> FormFields {
feature_expression_fields(&self.head)
.or_else(|| operation_form(&self.head).map(|(_, fields)| fields))
.unwrap_or(&[])
}
#[must_use]
pub fn field(&self, key: &str) -> Option<&FieldValue> {
let index = self.spec().iter().position(|(name, _)| *name == key)?;
self.fields.get(index)
}
#[must_use]
pub fn expression(&self, key: &str) -> Option<&GrammarExpr> {
match self.field(key)? {
FieldValue::Expression(expr) => Some(expr),
_ => None,
}
}
#[must_use]
pub fn expressions(&self, key: &str) -> &[GrammarExpr] {
match self.field(key) {
Some(FieldValue::Expressions(items)) => items,
_ => &[],
}
}
#[must_use]
pub fn text(&self, key: &str) -> Option<&str> {
match self.field(key)? {
FieldValue::Word(value) | FieldValue::Name(value) | FieldValue::Text(value) => {
Some(value)
}
_ => None,
}
}
#[must_use]
pub fn integer(&self, key: &str) -> Option<i64> {
match self.field(key)? {
FieldValue::Integer(value) => Some(*value),
_ => None,
}
}
#[must_use]
pub fn operation(&self, key: &str) -> Option<&Self> {
match self.field(key)? {
FieldValue::Operation(operation) => Some(operation),
_ => None,
}
}
#[must_use]
pub fn operations(&self, key: &str) -> Option<&[Self]> {
match self.field(key)? {
FieldValue::Operations(items) | FieldValue::Block(Some(items)) => Some(items),
_ => None,
}
}
pub fn for_each_expression<'a>(&'a self, visit: &mut impl FnMut(&'a GrammarExpr)) {
for value in &self.fields {
match value {
FieldValue::Expression(expr) => visit(expr),
FieldValue::Expressions(items) => items.iter().for_each(&mut *visit),
FieldValue::Operation(operation) => operation.for_each_expression(visit),
FieldValue::Operations(items) | FieldValue::Block(Some(items)) => {
for operation in items {
operation.for_each_expression(visit);
}
}
_ => {}
}
}
}
pub fn map_expressions(&mut self, map: &mut impl FnMut(&mut GrammarExpr)) {
for value in &mut self.fields {
match value {
FieldValue::Expression(expr) => map(expr),
FieldValue::Expressions(items) => items.iter_mut().for_each(&mut *map),
FieldValue::Operation(operation) => operation.map_expressions(map),
FieldValue::Operations(items) | FieldValue::Block(Some(items)) => {
for operation in items {
operation.map_expressions(map);
}
}
_ => {}
}
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ByteClassItem {
Byte(u8),
Range(u8, u8),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum UnicodeClassItem {
Char(char),
Range(char, char),
Category(String),
Script(String),
}
#[must_use]
pub fn class_expression(negated: bool, items: Vec<UnicodeClassItem>) -> GrammarExpr {
let plain = items
.iter()
.map(|item| match item {
UnicodeClassItem::Char(value) => Some(CharClassItem::Char(*value)),
UnicodeClassItem::Range(start, end) => Some(CharClassItem::Range(*start, *end)),
UnicodeClassItem::Category(_) | UnicodeClassItem::Script(_) => None,
})
.collect::<Option<Vec<_>>>();
plain.map_or_else(
|| GrammarExpr::feature(FeatureExpr::UnicodeClass { negated, items }),
|items| GrammarExpr::CharClass { negated, items },
)
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum FeatureExpr {
Form(FeatureForm),
ByteClass {
negated: bool,
items: Vec<ByteClassItem>,
},
UnicodeClass {
negated: bool,
items: Vec<UnicodeClassItem>,
},
Call {
name: String,
arguments: Vec<GrammarExpr>,
},
}
impl FeatureExpr {
#[must_use]
pub fn head(&self) -> &str {
match self {
Self::Form(form) => &form.head,
Self::ByteClass { .. } => "byteClass",
Self::UnicodeClass { .. } => "charClass",
Self::Call { .. } => "ref",
}
}
pub fn for_each_expression<'a>(&'a self, visit: &mut impl FnMut(&'a GrammarExpr)) {
match self {
Self::Form(form) => form.for_each_expression(visit),
Self::Call { arguments, .. } => arguments.iter().for_each(visit),
Self::ByteClass { .. } | Self::UnicodeClass { .. } => {}
}
}
pub fn map_expressions(&mut self, map: &mut impl FnMut(&mut GrammarExpr)) {
match self {
Self::Form(form) => form.map_expressions(map),
Self::Call { arguments, .. } => arguments.iter_mut().for_each(map),
Self::ByteClass { .. } | Self::UnicodeClass { .. } => {}
}
}
pub(crate) fn collect_references(&self, names: &mut BTreeSet<String>) {
match self {
Self::Call { name, arguments } => {
names.insert(name.clone());
for argument in arguments {
argument.collect_nonterminals(names);
}
}
Self::Form(form) => {
for value in &form.fields {
match value {
FieldValue::Expression(expr) => expr.collect_nonterminals(names),
FieldValue::Expressions(items) => {
for item in items {
item.collect_nonterminals(names);
}
}
_ => {}
}
}
}
Self::ByteClass { .. } | Self::UnicodeClass { .. } => {}
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct RuleAttributes {
pub parameters: Vec<String>,
pub channel: Option<String>,
pub modes: Option<Vec<String>>,
pub action: Option<Vec<Operation>>,
}
impl RuleAttributes {
#[must_use]
pub fn is_empty(&self) -> bool {
self == &Self::default()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GrammarMacro {
pub name: String,
pub parameters: Vec<String>,
pub expression: GrammarExpr,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GrammarScanner {
pub name: String,
pub tokens: Vec<String>,
pub operations: Vec<Operation>,
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum PrecedenceEntry {
Name(String),
Rule(String),
}
impl PrecedenceEntry {
#[must_use]
pub fn from_kind(kind: &str, value: String) -> Option<Self> {
match kind {
"name" => Some(Self::Name(value)),
"rule" => Some(Self::Rule(value)),
_ => None,
}
}
#[must_use]
pub const fn kind(&self) -> &'static str {
match self {
Self::Name(_) => "name",
Self::Rule(_) => "rule",
}
}
#[must_use]
pub fn value(&self) -> &str {
match self {
Self::Name(value) | Self::Rule(value) => value,
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct GrammarDeclarations {
pub matching: Option<String>,
pub settling: Option<Vec<String>>,
pub imports: Vec<String>,
pub modes: Vec<String>,
pub extras: Vec<GrammarExpr>,
pub conflicts: Vec<Vec<String>>,
pub precedences: Vec<Vec<PrecedenceEntry>>,
pub macros: Vec<GrammarMacro>,
pub scanners: Vec<GrammarScanner>,
}
impl GrammarDeclarations {
#[must_use]
pub fn is_empty(&self) -> bool {
self == &Self::default()
}
#[must_use]
pub fn external_tokens(&self) -> Vec<&str> {
self.scanners
.iter()
.flat_map(|scanner| scanner.tokens.iter().map(String::as_str))
.collect()
}
#[must_use]
pub fn present(&self) -> Vec<&'static str> {
let mut present = Vec::new();
if self.matching.is_some() {
present.push("matching");
}
if self.settling.is_some() {
present.push("settling");
}
for (name, empty) in [
("imports", self.imports.is_empty()),
("modes", self.modes.is_empty()),
("extras", self.extras.is_empty()),
("conflicts", self.conflicts.is_empty()),
("precedences", self.precedences.is_empty()),
("macros", self.macros.is_empty()),
("scanners", self.scanners.is_empty()),
] {
if !empty {
present.push(name);
}
}
present
}
}