Files
sprocket-formulas/src/compilation/mod.rs
2024-04-17 12:08:08 +02:00

202 lines
5.4 KiB
Rust

use std::sync::Arc;
use bigdecimal::{num_bigint::BigInt, BigDecimal, ParseBigDecimalError};
use crate::{bool_formula::Comparator, types::Type};
use self::{
parser::{BinaryOp, UnaryOp},
typed_formula::TypedFormula,
};
mod compiler;
mod lexer;
mod parser;
mod typed_formula;
#[derive(Debug)]
pub enum CompileError {
UnexpectedToken {
expected: String,
found: String,
},
UnexpectedUnparsedToken {
found: String,
},
UnexpectedEndOfInput,
CannotParseDecimal(ParseBigDecimalError),
IntegerMustBeIntegral(BigDecimal),
IntegerTooLarge(BigInt),
CannotCompileNumberAsType(BigDecimal, Type),
CannotUseStringAsType(String, Type),
WrongNumberOfArguments {
expected: usize,
found: usize,
},
InvalidArgumentType {
expected: Type,
found: Type,
},
InvalidLocalType {
name: String,
expected: Type,
found: Type,
},
VariableNotFound(String),
ConditionMustBeBool,
ConditionalBranchesMustHaveSameType(Type, Type),
InvalidUnaryOperand(UnaryOp, Type),
InvalidBinaryOperand(BinaryOp, Type, Type),
InvalidComparator(Comparator, Type, Type),
IndexMustBeInteger,
ValueNotAnArray,
InvalidMethodTarget,
NotAnObject,
MethodNotFound {
name: String,
},
NoMatchingMethod {
name: String,
args: usize,
},
AmbiguousMethodCall {
name: String,
args: usize,
},
NotABasicType {
found: Type,
},
}
pub type CompileResult<T> = Result<T, CompileError>;
#[derive(Clone)]
pub enum FullType {
Basic(Type),
Array(Box<FullType>),
Object(Arc<dyn ObjectTypeDefinition>),
EnumConstant(Vec<String>),
}
impl FullType {
pub fn from_simple(t: Type) -> CompileResult<Self> {
match t {
Type::Bool | Type::Int | Type::Decimal | Type::String | Type::Date | Type::DateTime => {
Ok(FullType::Basic(t))
}
Type::BoolArray
| Type::IntArray
| Type::DecimalArray
| Type::StringArray
| Type::DateArray
| Type::DateTimeArray => Ok(FullType::Array(Box::new(FullType::Basic(
t.array().unwrap(),
)))),
other => Err(CompileError::NotABasicType { found: other }),
}
}
pub fn matches(&self, other: &Type) -> bool {
match self {
FullType::Basic(t1) => t1 == other,
FullType::Array(t1) => t1.simplified().array() == Some(other.clone()),
FullType::Object(_) => other == &Type::Object,
FullType::EnumConstant(_) => false,
}
}
pub fn simplified(&self) -> Type {
match self {
FullType::Basic(t) => t.clone(),
FullType::Array(t) => t.simplified().array().unwrap_or(Type::ObjectArray),
FullType::Object(_) => Type::Object,
FullType::EnumConstant(_) => Type::String,
}
}
pub fn object(&self) -> Option<Arc<dyn ObjectTypeDefinition>> {
match self {
FullType::Object(t) => Some(t.clone()),
_ => None,
}
}
pub fn array_element_type(&self) -> Option<FullType> {
match self {
FullType::Basic(t) => match &t {
Type::BoolArray => Some(FullType::Basic(Type::Bool)),
Type::IntArray => Some(FullType::Basic(Type::Int)),
Type::DecimalArray => Some(FullType::Basic(Type::Decimal)),
Type::StringArray => Some(FullType::Basic(Type::String)),
Type::DateArray => Some(FullType::Basic(Type::Date)),
Type::DateTimeArray => Some(FullType::Basic(Type::DateTime)),
Type::ObjectArray => Some(FullType::Basic(Type::Object)),
_ => None,
},
FullType::Array(t) => Some(*t.clone()),
_ => None,
}
}
}
pub trait ObjectTypeDefinition {
fn get_field_type(&self, name: &str) -> Option<FullType>;
fn get_method_headers(&self, name: &str) -> Option<Vec<&MethodHeader>>;
fn get_static_method(&self, id: &str) -> Option<&dyn StaticMethod>;
fn get_instance_method(&self, id: &str) -> Option<&dyn InstanceMethod>;
}
pub struct EmptyRoot;
impl ObjectTypeDefinition for EmptyRoot {
fn get_field_type(&self, _name: &str) -> Option<FullType> {
None
}
fn get_method_headers(&self, _name: &str) -> Option<Vec<&MethodHeader>> {
None
}
fn get_static_method(&self, _id: &str) -> Option<&dyn StaticMethod> {
None
}
fn get_instance_method(&self, _id: &str) -> Option<&dyn InstanceMethod> {
None
}
}
pub trait StaticMethod {
fn get_header(&self) -> &MethodHeader;
fn call(&self, args: Vec<TypedFormula>) -> CompileResult<TypedFormula>;
}
pub trait InstanceMethod {
fn get_header(&self) -> &MethodHeader;
fn call(&self, instance: TypedFormula, args: Vec<TypedFormula>) -> CompileResult<TypedFormula>;
}
pub struct MethodHeader {
pub method_id: String,
pub return_type: FullType,
pub argument_types: Vec<FullType>,
}
impl MethodHeader {
pub fn new(method_id: String, return_type: Type, argument_types: Vec<Type>) -> Self {
Self {
method_id,
return_type: FullType::Basic(return_type),
argument_types: argument_types.into_iter().map(FullType::Basic).collect(),
}
}
}
pub use compiler::{compile_formula, compile_formula_as};
#[cfg(test)]
mod tests;