mod parser;
use crate::v1_1::lexer::Token;
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum ElError {
#[error("EL lex error at byte {at}: unrecognised input {text:?}")]
Lex {
at: usize,
text: String,
},
#[error("EL parse error at byte {at}: {message}")]
Parse {
at: usize,
message: String,
},
#[error("EL production unsupported at byte {at}: {message}")]
Unsupported {
at: usize,
message: String,
},
#[error("EL name `{name}` at byte {at} does not resolve: {message}")]
Unresolved {
at: usize,
name: String,
message: String,
},
}
#[derive(Debug, Clone, PartialEq)]
pub enum ElLiteral {
Boolean(bool),
Integer(i64),
Real(f64),
String(String),
Character(char),
Date(String),
Time(String),
DateTime(String),
Duration(String),
TermCode(String),
}
impl ElLiteral {
#[must_use]
pub fn type_name(&self) -> &'static str {
match self {
ElLiteral::Boolean(_) => "Boolean",
ElLiteral::Integer(_) => "Integer",
ElLiteral::Real(_) => "Real",
ElLiteral::String(_) => "String",
ElLiteral::Character(_) => "Character",
ElLiteral::Date(_) => "Date",
ElLiteral::Time(_) => "Time",
ElLiteral::DateTime(_) => "Date_Time",
ElLiteral::Duration(_) => "Duration",
ElLiteral::TermCode(_) => "Terminology_code",
}
}
#[must_use]
pub fn value_literal(&self) -> String {
match self {
ElLiteral::Boolean(value) => value.to_string(),
ElLiteral::Integer(value) => value.to_string(),
ElLiteral::Real(value) => value.to_string(),
ElLiteral::Character(value) => value.to_string(),
ElLiteral::String(text)
| ElLiteral::Date(text)
| ElLiteral::Time(text)
| ElLiteral::DateTime(text)
| ElLiteral::Duration(text)
| ElLiteral::TermCode(text) => text.clone(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ElOperator {
pub symbol: &'static str,
pub function: &'static str,
}
impl ElOperator {
fn of(token: &Token) -> Option<Self> {
let (symbol, function) = match token {
Token::SymAnd => ("and", "conjunction"),
Token::SymOr => ("or", "disjunction"),
Token::SymXor => ("xor", "exclusive_disjunction"),
Token::SymImplies => ("implies", "implication"),
Token::SymIff => ("<=>", "equal"),
Token::SymNot => ("not", "not"),
Token::SymEq => ("=", "equal"),
Token::SymNe => ("!=", "not_equal"),
Token::SymLt => ("<", "less_than"),
Token::SymLe => ("<=", "less_than_or_equal"),
Token::SymGt => (">", "greater_than"),
Token::SymGe => (">=", "greater_than_or_equal"),
Token::SymPlus => ("+", "add"),
Token::SymMinus => ("-", "subtract"),
Token::SymStar => ("*", "multiply"),
Token::SymSlash => ("/", "divide"),
Token::SymPercent => ("%", "modulus"),
Token::SymCarat => ("^", "exponent"),
Token::SymMatches => ("matches", "matches"),
_ => return None,
};
Some(Self { symbol, function })
}
}
pub trait ElBuilder {
type Expr;
fn literal(&mut self, literal: ElLiteral, at: usize) -> Result<Self::Expr, ElError>;
fn self_ref(&mut self, at: usize) -> Result<Self::Expr, ElError>;
fn result_ref(&mut self, at: usize) -> Result<Self::Expr, ElError>;
fn bound_variable(&mut self, name: &str, at: usize) -> Result<Self::Expr, ElError>;
fn type_ref(&mut self, type_id: &str, at: usize) -> Result<Self::Expr, ElError>;
fn feature_ref(
&mut self,
scoper: Option<Self::Expr>,
name: &str,
args: Option<Vec<Self::Expr>>,
at: usize,
) -> Result<Self::Expr, ElError>;
fn binary(
&mut self,
operator: ElOperator,
left: Self::Expr,
right: Self::Expr,
at: usize,
) -> Result<Self::Expr, ElError>;
fn unary(
&mut self,
operator: ElOperator,
operand: Self::Expr,
at: usize,
) -> Result<Self::Expr, ElError>;
fn attached(&mut self, operand: Self::Expr, at: usize) -> Result<Self::Expr, ElError>;
fn quantified(
&mut self,
universal: bool,
variable: &str,
collection: Self::Expr,
condition: Self::Expr,
at: usize,
) -> Result<Self::Expr, ElError>;
fn tuple(&mut self, items: Vec<Self::Expr>, at: usize) -> Result<Self::Expr, ElError>;
fn constraint(&mut self, raw: &str, at: usize) -> Result<Self::Expr, ElError>;
}
pub fn parse_boolean_expression_with<B: ElBuilder>(
src: &str,
builder: &mut B,
) -> Result<B::Expr, ElError> {
let tokens =
crate::v1_1::lexer::lex(crate::v1_1::lexer::Language::El, src).map_err(|failure| {
ElError::Lex {
at: failure.span.start,
text: failure.text,
}
})?;
parser::Parser::new(src, &tokens, builder).parse_whole_boolean_expression()
}