use std::{
cmp::Ordering,
error::Error,
fmt,
ops::{Add, Div, Mul, Sub},
};
use crate::datamodel::SystemVariables;
mod evaluator;
mod lexer;
mod parser;
use evaluator::{Evaluator, EvaluatorError};
use lexer::{Lexer, LexerError};
use parser::{Parser, ParserError};
#[derive(Clone, Debug)]
pub enum EcmaScriptValue {
String(String),
Number(f64),
Boolean(bool),
Null,
}
#[derive(Debug, PartialEq)]
pub enum EcmaScriptEvalError {
IllegalOperation(
ArithmeticOperator,
EcmaScriptValue,
EcmaScriptValue,
),
}
pub struct Interpreter<'s> {
expr_str: &'s str,
}
#[derive(Debug, PartialEq)]
pub enum InterpreterError {
InvalidOperatorConversion(Token),
EvaluatorError(EvaluatorError),
LexerError(LexerError),
ParserError(ParserError),
}
#[derive(Clone, Debug, PartialEq)]
pub enum Token {
LeftParen,
RightParen,
LeftBrace,
RightBrace,
LeftBracket,
RightBracket,
Comma,
Dot,
Minus,
Plus,
Slash,
Star,
Bang,
BangEqual,
Equal,
EqualEqual,
GreaterThan,
GreaterThanOrEqualTo,
LessThan,
LessThanOrEqualTo,
Identifier(String),
String(String),
Number(f64),
}
#[derive(Clone, Debug, PartialEq)]
pub enum Expression {
Literal(Literal),
Identifier(String),
Unary(Unary),
Binary(Box<Self>, Operator, Box<Self>),
Grouping(Box<Self>),
}
#[derive(Clone, Debug, PartialEq)]
pub enum Literal {
Number(f64),
String(String),
True,
False,
Null,
}
#[derive(Clone, Debug, PartialEq)]
pub enum Unary {
Negation(Box<Expression>),
Not(Box<Expression>),
}
#[derive(Clone, Debug, PartialEq)]
pub enum Operator {
Logical(LogicalOperator),
Arithmetic(ArithmeticOperator),
}
#[derive(Clone, Debug, PartialEq)]
pub enum LogicalOperator {
EqualTo,
NotEqualTo,
GreaterThan,
GreaterThanOrEqualTo,
LessThan,
LessThanOrEqualTo,
}
#[derive(Clone, Debug, PartialEq)]
pub enum ArithmeticOperator {
Plus,
Minus,
Star,
Slash,
}
impl<'s> Interpreter<'s> {
pub fn new(expr_str: &'s str) -> Self {
Self { expr_str }
}
pub fn interpret(
&self,
sys_vars: &SystemVariables,
) -> Result<EcmaScriptValue, InterpreterError> {
let tokens = Lexer::new(self.expr_str).scan()?;
if tokens.is_empty() {
return Ok(EcmaScriptValue::String(String::default()));
}
let expr = Parser::new(&tokens).parse()?;
Evaluator::new(&expr, sys_vars)
.evaluate()
.map_err(InterpreterError::EvaluatorError)
}
pub fn interpret_as_bool(&self, sys_vars: &SystemVariables) -> Result<bool, InterpreterError> {
let result = self.interpret(sys_vars)?;
Ok(result == EcmaScriptValue::Boolean(true))
}
}
impl Error for EcmaScriptEvalError {}
impl fmt::Display for EcmaScriptEvalError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::IllegalOperation(op, left, right) => {
write!(
f,
"Attempted operation '{:?} {:?} {:?}' is illegal",
left, op, right
)
}
}
}
}
impl Error for InterpreterError {}
impl fmt::Display for InterpreterError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::InvalidOperatorConversion(token) => {
write!(
f,
"Invalid Token->Operator conversion for token '{:?}'",
token
)
}
Self::EvaluatorError(eval_err) => {
write!(
f,
"EvaluatorError '{:?}' encountered while interpreting",
eval_err
)
}
Self::LexerError(lexer_err) => {
write!(
f,
"LexerError '{:?}' encountered while interpreting",
lexer_err
)
}
Self::ParserError(parse_err) => {
write!(
f,
"ParserError '{:?}' encountered while interpreting",
parse_err
)
}
}
}
}
impl From<EvaluatorError> for InterpreterError {
fn from(src: EvaluatorError) -> Self {
Self::EvaluatorError(src)
}
}
impl From<LexerError> for InterpreterError {
fn from(src: LexerError) -> Self {
Self::LexerError(src)
}
}
impl From<ParserError> for InterpreterError {
fn from(src: ParserError) -> Self {
Self::ParserError(src)
}
}
impl fmt::Display for Expression {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Literal(literal) => write!(f, "{}", literal),
Self::Identifier(name) => write!(f, "var({})", name),
Self::Unary(unary) => write!(f, "({})", unary),
Self::Binary(left, op, right) => write!(f, "({} {} {})", op, left, right),
Self::Grouping(expr) => write!(f, "(group {})", expr),
}
}
}
impl fmt::Display for Literal {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Number(val) => write!(f, "{:?}", val),
Self::String(string) => write!(f, "{:?}", string),
Self::True => write!(f, "true"),
Self::False => write!(f, "false"),
Self::Null => write!(f, "null"),
}
}
}
impl fmt::Display for Unary {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Negation(expr) => write!(f, "- {}", expr),
Self::Not(expr) => write!(f, "! {}", expr),
}
}
}
impl fmt::Display for Operator {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Logical(op) => write!(f, "{}", op),
Self::Arithmetic(op) => write!(f, "{}", op),
}
}
}
impl fmt::Display for LogicalOperator {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::EqualTo => write!(f, "=="),
Self::NotEqualTo => write!(f, "!="),
Self::GreaterThan => write!(f, ">"),
Self::GreaterThanOrEqualTo => write!(f, ">="),
Self::LessThan => write!(f, "<"),
Self::LessThanOrEqualTo => write!(f, "<="),
}
}
}
impl fmt::Display for ArithmeticOperator {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Plus => write!(f, "+"),
Self::Minus => write!(f, "-"),
Self::Star => write!(f, "*"),
Self::Slash => write!(f, "/"),
}
}
}
impl fmt::Display for EcmaScriptValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::String(val) => {
write!(f, "{}", val)
}
Self::Number(val) => {
write!(f, "{}", val)
}
Self::Boolean(val) => {
write!(f, "{}", val)
}
Self::Null => {
write!(f, "null")
}
}
}
}
impl PartialEq for EcmaScriptValue {
fn eq(&self, other: &Self) -> bool {
match self {
Self::String(self_value) => {
match other {
Self::String(other_value) => {
self_value == other_value
}
Self::Number(other_value) => {
if let Ok(parsed_value) = self_value.parse::<f64>() {
&parsed_value == other_value
} else {
false
}
}
Self::Boolean(other_value_is_true) => {
if let Ok(parsed_value) = self_value.parse::<f64>() {
if *other_value_is_true {
(parsed_value - 1.0).abs() < f64::EPSILON
} else {
(parsed_value - 0.0).abs() < f64::EPSILON
}
} else {
false
}
}
Self::Null => {
false
}
}
}
Self::Number(self_value) => {
match other {
Self::String(other_value) => {
Self::String(other_value.clone()) == *self
}
Self::Number(other_value) => {
self_value == other_value
}
Self::Boolean(other_value_is_true) => {
if *other_value_is_true {
(*self_value - 1.0).abs() < f64::EPSILON
} else {
(*self_value - 0.0).abs() < f64::EPSILON
}
}
Self::Null => {
false
}
}
}
Self::Boolean(self_value_is_true) => {
match other {
Self::String(other_value) => {
Self::String(other_value.clone()) == *self
}
Self::Number(other_value) => {
Self::Number(*other_value) == *self
}
Self::Boolean(other_value_is_true) => {
self_value_is_true == other_value_is_true
}
Self::Null => {
false
}
}
}
Self::Null => {
Self::Null == *self
}
}
}
}
impl PartialOrd for EcmaScriptValue {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match self {
Self::String(self_value) => {
match other {
Self::String(other_value) => {
for (self_char, other_char) in self_value.chars().zip(other_value.chars()) {
match self_char.cmp(&other_char) {
Ordering::Equal => continue,
non_equivalence => return Some(non_equivalence),
}
}
Some(Ordering::Equal)
}
Self::Number(other_value) => {
if let Ok(parsed_value) = self_value.parse::<f64>() {
parsed_value.partial_cmp(other_value)
} else {
Some(Ordering::Less)
}
}
Self::Boolean(_other_value_is_true) => {
Some(Ordering::Less)
}
Self::Null => {
if let Ok(parsed_value) = self_value.parse::<f64>() {
parsed_value.partial_cmp(&0.0)
} else {
None
}
}
}
}
Self::Number(self_value) => {
match other {
Self::String(other_value) => {
Self::String(other_value.clone()).partial_cmp(self)
}
Self::Number(other_value) => {
self_value.partial_cmp(other_value)
}
Self::Boolean(other_value_is_true) => {
if *other_value_is_true {
self_value.partial_cmp(&1.0)
} else {
self_value.partial_cmp(&0.0)
}
}
Self::Null => {
self_value.partial_cmp(&0.0)
}
}
}
Self::Boolean(self_value) => {
match other {
Self::String(other_value) => {
Self::String(other_value.clone()).partial_cmp(self)
}
Self::Number(other_value) => {
Self::Number(*other_value).partial_cmp(self)
}
Self::Boolean(other_value) => {
Self::Boolean(*other_value).partial_cmp(self)
}
Self::Null => {
(*self_value as i32 as f64).partial_cmp(&0.0)
}
}
}
Self::Null => {
match other {
Self::String(other_value) => {
Self::String(other_value.clone()).partial_cmp(self)
}
Self::Number(other_value) => {
Self::Number(*other_value).partial_cmp(self)
}
Self::Boolean(other_value) => {
Self::Boolean(*other_value).partial_cmp(self)
}
Self::Null => {
None
}
}
}
}
}
}
impl Add<Self> for EcmaScriptValue {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
match self {
Self::String(self_value) => {
match rhs {
Self::String(rhs_value) => {
Self::String(self_value + &rhs_value)
}
Self::Number(rhs_value) => {
Self::String(self_value + &rhs_value.to_string())
}
Self::Boolean(rhs_value) => {
Self::String(self_value + &rhs_value.to_string())
}
Self::Null => {
Self::String(self_value + "null")
}
}
}
Self::Number(self_value) => {
match rhs {
Self::String(rhs_value) => {
Self::String(rhs_value).add(self)
}
Self::Number(rhs_value) => {
Self::Number(self_value + rhs_value)
}
Self::Boolean(rhs_value) => {
Self::Number(self_value + rhs_value as i32 as f64)
}
Self::Null => {
self
}
}
}
Self::Boolean(self_value) => {
match rhs {
Self::String(rhs_value) => {
Self::String(rhs_value).add(self)
}
Self::Number(rhs_value) => {
Self::Number(rhs_value).add(self)
}
Self::Boolean(rhs_value) => {
Self::Number(self_value as i32 as f64 + rhs_value as i32 as f64)
}
Self::Null => {
Self::Number(self_value as i32 as f64)
}
}
}
Self::Null => {
match rhs {
Self::String(rhs_value) => {
Self::String(rhs_value).add(self)
}
Self::Number(rhs_value) => {
Self::Number(rhs_value).add(self)
}
Self::Boolean(rhs_value) => {
Self::Boolean(rhs_value).add(self)
}
Self::Null => {
Self::Number(0.0)
}
}
}
}
}
}
impl Sub<Self> for EcmaScriptValue {
type Output = Result<Self, EcmaScriptEvalError>;
fn sub(self, rhs: Self) -> Self::Output {
match self {
Self::String(self_value) => {
if let Ok(parsed_self_value_f64) = self_value.parse::<f64>() {
match rhs {
Self::String(rhs_value) => {
if let Ok(parsed_rhs_value_f64) = rhs_value.parse::<f64>() {
Ok(Self::Number(parsed_self_value_f64 - parsed_rhs_value_f64))
} else {
Err(EcmaScriptEvalError::IllegalOperation(
ArithmeticOperator::Minus,
Self::String(self_value),
Self::String(rhs_value),
))
}
}
Self::Number(rhs_value) => {
Ok(Self::Number(parsed_self_value_f64 - rhs_value))
}
Self::Boolean(rhs_value) => {
Ok(Self::Number(
parsed_self_value_f64 - rhs_value as i32 as f64,
))
}
Self::Null => {
Ok(Self::Number(parsed_self_value_f64))
}
}
} else {
Err(EcmaScriptEvalError::IllegalOperation(
ArithmeticOperator::Minus,
Self::String(self_value),
rhs,
))
}
}
Self::Number(self_value) => {
match rhs {
Self::String(rhs_value) => {
if let Ok(parsed_value) = rhs_value.parse::<f64>() {
Ok(Self::Number(self_value - parsed_value))
} else {
Err(EcmaScriptEvalError::IllegalOperation(
ArithmeticOperator::Minus,
self,
Self::String(rhs_value),
))
}
}
Self::Number(rhs_value) => {
Ok(Self::Number(self_value - rhs_value))
}
Self::Boolean(rhs_value) => {
Ok(Self::Number(self_value - rhs_value as i32 as f64))
}
Self::Null => {
Ok(self)
}
}
}
Self::Boolean(self_value) => {
match rhs {
Self::String(rhs_value) => {
if let Ok(parsed_value) = rhs_value.parse::<f64>() {
Ok(Self::Number(self_value as i32 as f64 - parsed_value))
} else {
Err(EcmaScriptEvalError::IllegalOperation(
ArithmeticOperator::Minus,
self,
Self::String(rhs_value),
))
}
}
Self::Number(rhs_value) => {
Ok(Self::Number(self_value as i32 as f64 - rhs_value))
}
Self::Boolean(rhs_value) => {
Ok(Self::Number(
self_value as i32 as f64 - rhs_value as i32 as f64,
))
}
Self::Null => {
Ok(Self::Number(self_value as i32 as f64))
}
}
}
Self::Null => {
match rhs {
Self::String(rhs_value) => {
if let Ok(parsed_value) = rhs_value.parse::<f64>() {
Ok(Self::Number(-parsed_value))
} else {
Err(EcmaScriptEvalError::IllegalOperation(
ArithmeticOperator::Minus,
self,
Self::String(rhs_value),
))
}
}
Self::Number(rhs_value) => {
Ok(Self::Number(-rhs_value))
}
Self::Boolean(rhs_value) => {
Ok(Self::Number(-(rhs_value as i32 as f64)))
}
Self::Null => {
Ok(Self::Number(0.0))
}
}
}
}
}
}
impl Mul<Self> for EcmaScriptValue {
type Output = Result<Self, EcmaScriptEvalError>;
fn mul(self, rhs: Self) -> Self::Output {
match self {
Self::String(self_value) => {
if let Ok(parsed_self_value_f64) = self_value.parse::<f64>() {
match rhs {
Self::String(rhs_value) => {
if let Ok(parsed_rhs_value_f64) = rhs_value.parse::<f64>() {
Ok(Self::Number(parsed_self_value_f64 * parsed_rhs_value_f64))
} else {
Err(EcmaScriptEvalError::IllegalOperation(
ArithmeticOperator::Star,
Self::String(self_value),
Self::String(rhs_value),
))
}
}
Self::Number(rhs_value) => {
Ok(Self::Number(parsed_self_value_f64 * rhs_value))
}
Self::Boolean(rhs_value) => {
Ok(Self::Number(
parsed_self_value_f64 * rhs_value as i32 as f64,
))
}
Self::Null => {
Ok(Self::Number(0.0))
}
}
} else {
Err(EcmaScriptEvalError::IllegalOperation(
ArithmeticOperator::Star,
Self::String(self_value),
rhs,
))
}
}
Self::Number(self_value) => {
match rhs {
Self::String(rhs_value) => {
if let Ok(parsed_value) = rhs_value.parse::<f64>() {
Ok(Self::Number(self_value * parsed_value))
} else {
Err(EcmaScriptEvalError::IllegalOperation(
ArithmeticOperator::Star,
self,
Self::String(rhs_value),
))
}
}
Self::Number(rhs_value) => {
Ok(Self::Number(self_value * rhs_value))
}
Self::Boolean(rhs_value) => {
Ok(Self::Number(self_value * rhs_value as i32 as f64))
}
Self::Null => {
Ok(Self::Number(0.0))
}
}
}
Self::Boolean(self_value) => {
match rhs {
Self::String(rhs_value) => {
if let Ok(parsed_value) = rhs_value.parse::<f64>() {
Ok(Self::Number(self_value as i32 as f64 * parsed_value))
} else {
Err(EcmaScriptEvalError::IllegalOperation(
ArithmeticOperator::Star,
self,
Self::String(rhs_value),
))
}
}
Self::Number(rhs_value) => {
Ok(Self::Number(self_value as i32 as f64 * rhs_value))
}
Self::Boolean(rhs_value) => {
Ok(Self::Number(
self_value as i32 as f64 * rhs_value as i32 as f64,
))
}
Self::Null => {
Ok(Self::Number(0.0))
}
}
}
Self::Null => {
match rhs {
Self::String(rhs_value) => {
if rhs_value.parse::<f64>().is_ok() {
Ok(Self::Number(0.0))
} else {
Err(EcmaScriptEvalError::IllegalOperation(
ArithmeticOperator::Star,
self,
Self::String(rhs_value),
))
}
}
Self::Number(_) => Ok(Self::Number(0.0)),
Self::Boolean(_) => Ok(Self::Number(0.0)),
Self::Null => Ok(Self::Number(0.0)),
}
}
}
}
}
impl Div<Self> for EcmaScriptValue {
type Output = Result<Self, EcmaScriptEvalError>;
fn div(self, rhs: Self) -> Self::Output {
match &rhs {
Self::String(rhs_value) => {
if rhs_value
.parse::<f64>()
.map_or(false, |v| (v - 0.0).abs() < f64::EPSILON)
{
return Ok(Self::Number(f64::INFINITY));
}
}
Self::Number(rhs_value) if (rhs_value - 0.0).abs() < f64::EPSILON => {
return Ok(Self::Number(f64::INFINITY));
}
_ => { }
}
match self {
Self::String(self_value) => {
if let Ok(parsed_self_value) = self_value.parse::<f64>() {
match rhs {
Self::String(rhs_value) => {
if let Ok(parsed_rhs_value) = rhs_value.parse::<f64>() {
Ok(Self::Number(parsed_self_value / parsed_rhs_value))
} else {
Err(EcmaScriptEvalError::IllegalOperation(
ArithmeticOperator::Slash,
Self::String(self_value),
Self::String(rhs_value),
))
}
}
Self::Number(rhs_value) => {
Ok(Self::Number(parsed_self_value / rhs_value))
}
Self::Boolean(rhs_value) => {
Ok(Self::Number(parsed_self_value / rhs_value as i32 as f64))
}
Self::Null => {
Ok(Self::Number(f64::INFINITY))
}
}
} else {
Err(EcmaScriptEvalError::IllegalOperation(
ArithmeticOperator::Slash,
Self::String(self_value),
rhs,
))
}
}
Self::Number(self_value) => {
match rhs {
Self::String(rhs_value) => {
if let Ok(parsed_rhs_value) = rhs_value.parse::<f64>() {
Ok(Self::Number(self_value / parsed_rhs_value))
} else {
Err(EcmaScriptEvalError::IllegalOperation(
ArithmeticOperator::Slash,
self,
Self::String(rhs_value),
))
}
}
Self::Number(rhs_value) => {
Ok(Self::Number(self_value / rhs_value))
}
Self::Boolean(rhs_value) => {
Ok(Self::Number(self_value / rhs_value as i32 as f64))
}
Self::Null => {
Ok(Self::Number(f64::INFINITY))
}
}
}
Self::Boolean(self_value) => {
match rhs {
Self::String(rhs_value) => {
if let Ok(parsed_rhs_value) = rhs_value.parse::<f64>() {
Ok(Self::Number(self_value as i32 as f64 / parsed_rhs_value))
} else {
Err(EcmaScriptEvalError::IllegalOperation(
ArithmeticOperator::Slash,
self,
Self::String(rhs_value),
))
}
}
Self::Number(rhs_value) => {
Ok(Self::Number(self_value as i32 as f64 / rhs_value))
}
Self::Boolean(rhs_value) => {
Ok(Self::Number(
self_value as i32 as f64 / rhs_value as i32 as f64,
))
}
Self::Null => {
if self_value {
Ok(Self::Number(f64::INFINITY))
}
else {
Err(EcmaScriptEvalError::IllegalOperation(
ArithmeticOperator::Slash,
self,
rhs,
))
}
}
}
}
Self::Null => {
match rhs {
Self::String(rhs_value) => {
if rhs_value.parse::<f64>().is_ok() {
Ok(Self::Number(0.0))
} else {
Err(EcmaScriptEvalError::IllegalOperation(
ArithmeticOperator::Slash,
self,
Self::String(rhs_value),
))
}
}
Self::Number(_) => {
Ok(Self::Number(0.0))
}
Self::Boolean(rhs_value) => {
if rhs_value {
Ok(Self::Number(0.0))
}
else {
Err(EcmaScriptEvalError::IllegalOperation(
ArithmeticOperator::Slash,
self,
rhs,
))
}
}
Self::Null => {
Err(EcmaScriptEvalError::IllegalOperation(
ArithmeticOperator::Slash,
self,
rhs,
))
}
}
}
}
}
}
#[cfg(test)]
mod tests {
use std::error::Error;
use crate::interpreter::{lexer::Lexer, parser::Parser};
type TestResult = Result<(), Box<dyn Error>>;
#[test]
fn no_identifiers() -> TestResult {
let cond_str = "-5 != (10 / 2 * (3 + 1)) == true";
eprintln!("Processing string '{}'...", cond_str);
let mut lexer = Lexer::new(cond_str);
let tokens = lexer.scan()?;
let mut parser = Parser::new(&tokens);
let expr = parser.parse()?;
eprintln!("*** Result ***\n{}", expr);
Ok(())
}
}