202 lines
5.4 KiB
Rust
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;
|