use crate::core::Function;
use crate::lang::hir::MemberQualifier;
use crate::lang::mir::TypeHandle;
use crate::lang::parser::Located;
use crate::lang::{lir, mir, PrimordialType, SyntacticSugar};
use crate::runtime::rationale::Rationale;
use crate::runtime::{EvaluationResult, ModuleHandle, Output, RuntimeError, TraceResult};
use crate::runtime::{TypeName, World};
use crate::value::{Object, RationaleResult, RuntimeValue};
use serde::Serialize;
use std::any::Any;
use std::borrow::Borrow;
use std::cell::RefCell;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::fmt::{Debug, Display, Formatter};
use std::future::{ready, Future};
use std::hash::{Hash, Hasher};
use std::pin::Pin;
use std::rc::Rc;
use std::sync::Arc;
use std::time::{Duration, Instant};
#[derive(Serialize, Debug, Clone)]
pub enum Expr {
SelfLiteral(),
Value(ValueType),
Function(String, Arc<Expr>),
Add(Arc<Expr>, Arc<Expr>),
Subtract(Arc<Expr>, Arc<Expr>),
Multiply(Arc<Expr>, Arc<Expr>),
Divide(Arc<Expr>, Arc<Expr>),
LessThan(Arc<Expr>, Arc<Expr>),
LessThanEqual(Arc<Expr>, Arc<Expr>),
GreaterThan(Arc<Expr>, Arc<Expr>),
GreaterThanEqual(Arc<Expr>, Arc<Expr>),
Equal(Arc<Expr>, Arc<Expr>),
NotEqual(Arc<Expr>, Arc<Expr>),
Not(Arc<Expr>),
LogicalAnd(Arc<Expr>, Arc<Expr>),
LogicalOr(Arc<Expr>, Arc<Expr>),
}
pub type ExprFuture =
Pin<Box<dyn Future<Output = Result<Rc<RuntimeValue>, RuntimeError>> + 'static>>;
impl Expr {
#[allow(clippy::let_and_return)]
pub fn evaluate(&self, value: Rc<RuntimeValue>) -> ExprFuture {
let this = self.clone();
Box::pin(async move {
match &this {
Expr::SelfLiteral() => Ok(value.clone()),
Expr::Value(ref inner) => Ok(Rc::new(inner.into())),
Expr::Function(_, _) => todo!(),
Expr::Add(_, _) => todo!(),
Expr::Subtract(_, _) => todo!(),
Expr::Multiply(_, _) => todo!(),
Expr::Divide(_, _) => todo!(),
Expr::LessThan(ref lhs, ref rhs) => {
let lhs = lhs.clone().evaluate(value.clone()).await?;
let rhs = rhs.clone().evaluate(value.clone()).await?;
let result = if let Some(Ordering::Less) = (*lhs).partial_cmp(&(*rhs)) {
Ok(Rc::new(true.into()))
} else {
Ok(Rc::new(false.into()))
};
result
}
Expr::LessThanEqual(ref lhs, ref rhs) => {
let lhs = lhs.clone().evaluate(value.clone()).await?;
let rhs = rhs.clone().evaluate(value.clone()).await?;
let result = if let Some(Ordering::Less | Ordering::Equal) =
(*lhs).partial_cmp(&(*rhs))
{
Ok(Rc::new(true.into()))
} else {
Ok(Rc::new(false.into()))
};
result
}
Expr::GreaterThan(ref lhs, ref rhs) => {
let lhs = lhs.clone().evaluate(value.clone()).await?;
let rhs = rhs.clone().evaluate(value.clone()).await?;
let result = if let Some(Ordering::Greater) = (*lhs).partial_cmp(&(*rhs)) {
Ok(Rc::new(true.into()))
} else {
Ok(Rc::new(false.into()))
};
result
}
Expr::GreaterThanEqual(lhs, rhs) => {
let lhs = lhs.clone().evaluate(value.clone()).await?;
let rhs = rhs.clone().evaluate(value.clone()).await?;
let result = if let Some(Ordering::Greater | Ordering::Equal) =
(*lhs).partial_cmp(&(*rhs))
{
Ok(Rc::new(true.into()))
} else {
Ok(Rc::new(false.into()))
};
result
}
Expr::Equal(ref lhs, ref rhs) => {
let lhs = lhs.clone().evaluate(value.clone()).await?;
let rhs = rhs.clone().evaluate(value.clone()).await?;
let result = if let Some(Ordering::Equal) = (*lhs).partial_cmp(&(*rhs)) {
Ok(Rc::new(true.into()))
} else {
Ok(Rc::new(false.into()))
};
result
}
Expr::NotEqual(ref lhs, ref rhs) => {
let lhs = lhs.clone().evaluate(value.clone()).await?;
let rhs = rhs.clone().evaluate(value.clone()).await?;
let result = if let Some(Ordering::Equal) = (*lhs).partial_cmp(&(*rhs)) {
Ok(Rc::new(false.into()))
} else {
Ok(Rc::new(true.into()))
};
result
}
Expr::Not(_) => todo!(),
Expr::LogicalAnd(_, _) => todo!(),
Expr::LogicalOr(_, _) => todo!(),
}
})
}
}
#[derive(Debug, Serialize)]
pub struct Type {
name: Option<TypeName>,
documentation: Option<String>,
parameters: Vec<String>,
inner: InnerType,
}
impl Type {
pub(crate) fn new(
name: Option<TypeName>,
documentation: Option<String>,
parameters: Vec<String>,
inner: InnerType,
) -> Self {
Self {
name,
documentation,
parameters,
inner,
}
}
pub fn order(&self, world: &World) -> u8 {
self.inner.order(world)
}
pub fn name(&self) -> Option<TypeName> {
self.name.clone()
}
pub fn documentation(&self) -> Option<String> {
self.documentation.clone()
}
pub fn inner(&self) -> &InnerType {
&self.inner
}
pub fn parameters(&self) -> Vec<String> {
self.parameters.clone()
}
pub fn try_get_resolved_value(&self) -> Option<ValueType> {
if let InnerType::Const(val) = &self.inner {
Some(val.clone())
} else {
None
}
}
pub fn evaluate<'v>(
self: &'v Arc<Self>,
value: Rc<RuntimeValue>,
ctx: &'v mut EvalContext,
bindings: &'v Bindings,
world: &'v World,
) -> Pin<Box<dyn Future<Output = Result<EvaluationResult, RuntimeError>> + 'v>> {
let trace = ctx.trace();
match &self.inner {
InnerType::Anything => Box::pin(async move {
let mut locked_value = (*value).borrow();
Ok(EvaluationResult::new(
Some(value.clone()),
self.clone(),
Rationale::Anything,
Output::Identity,
trace.done(),
))
}),
InnerType::Ref(sugar, slot, bindings) => Box::pin(async move {
if let Some(ty) = world.get_by_slot(*slot) {
let bindings = (ty.parameters(), bindings, world).into();
let result = ty.evaluate(value.clone(), ctx, &bindings, world).await?;
if let SyntacticSugar::Chain = sugar {
Ok(EvaluationResult::new(
Some(value.clone()),
self.clone(),
result.rationale().clone(),
result.raw_output().clone(),
trace.done(),
))
} else {
Ok(result)
}
} else {
Err(RuntimeError::NoSuchTypeSlot(*slot))
}
}),
InnerType::Bound(ty, bindings) => {
Box::pin(async move { ty.evaluate(value, ctx, bindings, world).await })
}
InnerType::Argument(name) => Box::pin(async move {
if let Some(bound) = bindings.get(name) {
bound.evaluate(value.clone(), ctx, bindings, world).await
} else {
Ok(EvaluationResult::new(
Some(value.clone()),
self.clone(),
Rationale::InvalidArgument(name.clone()),
Output::None,
trace.done(),
))
}
}),
InnerType::Primordial(inner) => match inner {
PrimordialType::Integer => Box::pin(async move {
let mut locked_value = (*value).borrow();
if locked_value.is_integer() {
Ok(EvaluationResult::new(
Some(value.clone()),
self.clone(),
Rationale::Primordial(true),
Output::Identity,
trace.done(),
))
} else {
Ok(EvaluationResult::new(
Some(value.clone()),
self.clone(),
Rationale::Primordial(false),
Output::None,
trace.done(),
))
}
}),
PrimordialType::Decimal => Box::pin(async move {
let mut locked_value = (*value).borrow();
if locked_value.is_decimal() {
Ok(EvaluationResult::new(
Some(value.clone()),
self.clone(),
Rationale::Primordial(true),
Output::Identity,
trace.done(),
))
} else {
Ok(EvaluationResult::new(
Some(value.clone()),
self.clone(),
Rationale::Primordial(false),
Output::None,
trace.done(),
))
}
}),
PrimordialType::Boolean => Box::pin(async move {
let mut locked_value = (*value).borrow();
if locked_value.is_boolean() {
Ok(EvaluationResult::new(
Some(value.clone()),
self.clone(),
Rationale::Primordial(true),
Output::Identity,
trace.done(),
))
} else {
Ok(EvaluationResult::new(
Some(value.clone()),
self.clone(),
Rationale::Primordial(false),
Output::None,
trace.done(),
))
}
}),
PrimordialType::String => Box::pin(async move {
let mut locked_value = (*value).borrow();
if locked_value.is_string() {
Ok(EvaluationResult::new(
Some(value.clone()),
self.clone(),
Rationale::Primordial(true),
Output::Identity,
trace.done(),
))
} else {
Ok(EvaluationResult::new(
Some(value.clone()),
self.clone(),
Rationale::Primordial(false),
Output::None,
trace.done(),
))
}
}),
PrimordialType::Function(sugar, name, func) => Box::pin(async move {
let mut result = func.call(value.clone(), ctx, bindings, world).await?;
Ok(EvaluationResult::new(
Some(value.clone()),
self.clone(),
Rationale::Function(result.output().is_some(), result.supporting()),
result.output(),
trace.done(),
))
}),
},
InnerType::Const(inner) => Box::pin(async move {
let mut locked_value = (*value).borrow();
if inner.is_equal(locked_value).await {
Ok(EvaluationResult::new(
Some(value.clone()),
self.clone(),
Rationale::Const(true),
Output::Identity,
trace.done(),
))
} else {
Ok(EvaluationResult::new(
Some(value.clone()),
self.clone(),
Rationale::Const(false),
Output::Identity,
trace.done(),
))
}
}),
InnerType::Object(inner) => Box::pin(async move {
let mut locked_value = (*value).borrow();
if let Some(obj) = locked_value.try_get_object() {
let mut result = HashMap::new();
for field in &inner.fields {
if let Some(ref field_value) = obj.get(field.name()) {
result.insert(
field.name(),
field
.ty()
.evaluate(field_value.clone(), ctx, bindings, world)
.await?,
);
} else if !field.optional() {
result.insert(
field.name(),
EvaluationResult::new(
None,
field.ty(),
Rationale::MissingField(field.name()),
Output::None,
trace.done(),
),
);
}
}
Ok(EvaluationResult::new(
Some(value.clone()),
self.clone(),
Rationale::Object(result),
Output::Identity,
trace.done(),
))
} else {
Ok(EvaluationResult::new(
Some(value.clone()),
self.clone(),
Rationale::NotAnObject,
Output::None,
trace.done(),
))
}
}),
InnerType::Expr(expr) => Box::pin(async move {
let result = expr.evaluate(value.clone()).await?;
let mut locked_value = (*value).borrow();
let locked_result = (*result).borrow();
if let Some(true) = locked_result.try_get_boolean() {
Ok(EvaluationResult::new(
Some(value.clone()),
self.clone(),
Rationale::Expression(true),
Output::Identity,
trace.done(),
))
} else {
Ok(EvaluationResult::new(
Some(value.clone()),
self.clone(),
Rationale::Expression(false),
Output::None,
trace.done(),
))
}
}),
InnerType::List(terms) => Box::pin(async move {
if let Some(list_value) = value.try_get_list() {
if list_value.len() == terms.len() {
let mut result = Vec::new();
for (term, element) in terms.iter().zip(list_value.iter()) {
result
.push(term.evaluate(element.clone(), ctx, bindings, world).await?);
}
return Ok(EvaluationResult::new(
Some(value.clone()),
self.clone(),
Rationale::List(result),
Output::Identity,
trace.done(),
));
}
}
Ok(EvaluationResult::new(
Some(value.clone()),
self.clone(),
Rationale::NotAList,
Output::None,
trace.done(),
))
}),
InnerType::Nothing => Box::pin(async move {
Ok(EvaluationResult::new(
Some(value.clone()),
self.clone(),
Rationale::Nothing,
Output::None,
trace.done(),
))
}),
}
}
}
#[derive(Serialize)]
pub enum InnerType {
Anything,
Primordial(PrimordialType),
Bound(Arc<Type>, Bindings),
Ref(SyntacticSugar, usize, Vec<Arc<Type>>),
Argument(String),
Const(ValueType),
Object(ObjectType),
Expr(Arc<Expr>),
List(Vec<Arc<Type>>),
Nothing,
}
impl InnerType {
fn order(&self, world: &World) -> u8 {
match self {
Self::Anything => 128,
Self::Primordial(t) => t.order(),
Self::Bound(t, _) => t.order(world),
Self::Ref(_, slot, _) => world
.get_by_slot(*slot)
.map(|t| t.order(world))
.unwrap_or(128),
Self::Argument(s) => 2,
Self::Const(s) => 1,
Self::Object(o) => 64,
Self::Expr(e) => 128,
Self::List(l) => l.iter().map(|e| e.order(world)).max().unwrap_or(128),
Self::Nothing => 0,
}
}
}
#[derive(Serialize, Default, Debug)]
pub struct Bindings {
bindings: HashMap<String, Arc<Type>>,
}
impl Bindings {
pub fn new() -> Self {
Self {
bindings: Default::default(),
}
}
pub fn bind(&mut self, name: String, ty: Arc<Type>) {
self.bindings.insert(name, ty);
}
pub fn get<S: Into<String>>(&self, name: S) -> Option<Arc<Type>> {
self.bindings.get(&name.into()).cloned()
}
pub fn iter(&self) -> impl Iterator<Item = (&String, &Arc<Type>)> {
self.bindings.iter()
}
pub fn len(&self) -> usize {
self.bindings.len()
}
pub fn is_empty(&self) -> bool {
self.bindings.is_empty()
}
}
impl From<(Vec<String>, &Vec<Arc<Type>>, &World)> for Bindings {
fn from((parameters, arguments, world): (Vec<String>, &Vec<Arc<Type>>, &World)) -> Self {
let mut bindings = Self::new();
for (param, arg) in parameters.iter().zip(arguments.iter()) {
if let InnerType::Ref(sugar, slot, unresolved_bindings) = &arg.inner {
if let Some(resolved_type) = world.get_by_slot(*slot) {
if resolved_type.parameters().is_empty() {
bindings.bind(param.clone(), resolved_type.clone())
} else {
let resolved_bindings =
(resolved_type.parameters(), unresolved_bindings, world).into();
bindings.bind(
param.clone(),
Arc::new(Type::new(
resolved_type.name(),
resolved_type.documentation(),
resolved_type.parameters(),
InnerType::Bound(resolved_type, resolved_bindings),
)),
)
}
}
} else {
bindings.bind(param.clone(), arg.clone())
}
}
bindings
}
}
impl Debug for InnerType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
InnerType::Anything => write!(f, "anything"),
InnerType::Primordial(inner) => write!(f, "{:?}", inner),
InnerType::Const(inner) => write!(f, "{:?}", inner),
InnerType::Object(inner) => write!(f, "{:?}", inner),
InnerType::Expr(inner) => write!(f, "$({:?})", inner),
InnerType::List(inner) => write!(f, "[{:?}]", inner),
InnerType::Argument(name) => write!(f, "{:?}", name),
InnerType::Ref(sugar, slot, bindings) => write!(f, "ref {:?}<{:?}>", slot, bindings),
InnerType::Bound(primary, bindings) => write!(f, "bound {:?}<{:?}>", primary, bindings),
InnerType::Nothing => write!(f, "nothing"),
}
}
}
#[derive(Serialize, Debug)]
pub struct Field {
name: String,
ty: Arc<Type>,
optional: bool,
}
impl Display for Field {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name)
}
}
impl Field {
pub fn new(name: String, ty: Arc<Type>, optional: bool) -> Self {
Self { name, ty, optional }
}
pub fn name(&self) -> String {
self.name.clone()
}
pub fn ty(&self) -> Arc<Type> {
self.ty.clone()
}
pub fn optional(&self) -> bool {
self.optional
}
}
#[derive(Debug, Serialize, Clone)]
pub enum ValueType {
Null,
String(String),
Integer(i64),
Decimal(f64),
Boolean(bool),
List(Vec<Arc<ValueType>>),
Octets(Vec<u8>),
}
impl From<&ValueType> for RuntimeValue {
fn from(ty: &ValueType) -> Self {
match ty {
ValueType::Null => RuntimeValue::null(),
ValueType::String(val) => val.clone().into(),
ValueType::Integer(val) => (*val).into(),
ValueType::Decimal(val) => (*val).into(),
ValueType::Boolean(val) => (*val).into(),
ValueType::List(val) => Self::List(
val.iter()
.map(|e| {
let copy = &*e.clone();
Rc::new(RuntimeValue::from(copy))
})
.collect(),
),
ValueType::Octets(val) => Self::Octets(val.clone()),
}
}
}
impl From<Rc<RuntimeValue>> for Type {
fn from(val: Rc<RuntimeValue>) -> Self {
Type::new(
None,
None,
Vec::default(),
match &*val {
RuntimeValue::Null => InnerType::Const(ValueType::Null),
RuntimeValue::String(inner) => InnerType::Const(ValueType::String(inner.clone())),
RuntimeValue::Integer(inner) => InnerType::Const(ValueType::Integer(*inner)),
RuntimeValue::Decimal(inner) => InnerType::Const(ValueType::Decimal(*inner)),
RuntimeValue::Boolean(inner) => InnerType::Const(ValueType::Boolean(*inner)),
RuntimeValue::Object(_) => {
todo!()
}
RuntimeValue::List(_) => {
todo!()
}
RuntimeValue::Octets(inner) => InnerType::Const(ValueType::Octets(inner.clone())),
},
)
}
}
impl ValueType {
pub fn is_equal<'e>(
&'e self,
other: &'e RuntimeValue,
) -> Pin<Box<dyn Future<Output = bool> + 'e>> {
match (self, &other) {
(ValueType::Null, RuntimeValue::Null) => Box::pin(ready(true)),
(ValueType::String(lhs), RuntimeValue::String(rhs)) => {
Box::pin(async move { lhs.eq(rhs) })
}
(ValueType::Integer(lhs), RuntimeValue::Integer(rhs)) => {
Box::pin(async move { lhs.eq(rhs) })
}
(ValueType::Decimal(lhs), RuntimeValue::Decimal(rhs)) => {
Box::pin(async move { lhs.eq(rhs) })
}
(ValueType::Boolean(lhs), RuntimeValue::Boolean(rhs)) => {
Box::pin(async move { lhs.eq(rhs) })
}
(ValueType::List(lhs), RuntimeValue::List(rhs)) => Box::pin(async move {
if lhs.len() != rhs.len() {
false
} else {
for (l, r) in lhs.iter().zip(rhs.iter()) {
if !l.is_equal(r).await {
return false;
}
}
true
}
}),
(ValueType::Octets(lhs), RuntimeValue::Octets(rhs)) => {
Box::pin(async move { lhs.eq(rhs) })
}
_ => Box::pin(ready(false)),
}
}
}
#[derive(Serialize, Debug, Clone)]
pub struct ObjectType {
fields: Vec<Arc<Field>>,
}
impl ObjectType {
pub fn new(fields: Vec<Arc<Field>>) -> Self {
Self { fields }
}
pub fn fields(&self) -> &Vec<Arc<Field>> {
&self.fields
}
}
pub(crate) fn convert(
name: Option<TypeName>,
documentation: Option<String>,
parameters: Vec<String>,
ty: &Arc<Located<mir::Type>>,
) -> Arc<Type> {
match &***ty {
mir::Type::Anything => Arc::new(lir::Type::new(
name,
documentation,
parameters,
lir::InnerType::Anything,
)),
mir::Type::Primordial(primordial) => Arc::new(lir::Type::new(
name,
documentation,
parameters,
lir::InnerType::Primordial(primordial.clone()),
)),
mir::Type::Ref(sugar, slot, bindings) => {
let mut lir_bindings = Vec::default();
for e in bindings {
lir_bindings.push(convert(
e.name(),
e.documentation(),
e.parameters().iter().map(|e| e.inner()).collect(),
&e.ty(),
));
}
Arc::new(lir::Type::new(
name,
documentation,
parameters,
lir::InnerType::Ref(sugar.clone(), *slot, lir_bindings),
))
}
mir::Type::Argument(arg_name) => Arc::new(lir::Type::new(
name,
documentation,
parameters,
lir::InnerType::Argument(arg_name.clone()),
)),
mir::Type::Const(value) => Arc::new(lir::Type::new(
name,
documentation,
parameters,
lir::InnerType::Const(value.clone()),
)),
mir::Type::Object(mir_object) => {
let mut fields: Vec<Arc<Field>> = Default::default();
for f in mir_object.fields().iter() {
let ty = f.ty();
let field = Arc::new(lir::Field::new(
f.name().inner(),
convert(
ty.name(),
ty.documentation(),
ty.parameters().iter().map(|e| e.inner()).collect(),
&ty.ty(),
),
f.optional(),
));
fields.push(field);
}
let object = ObjectType::new(fields);
Arc::new(lir::Type::new(
name,
documentation,
parameters,
lir::InnerType::Object(object),
))
}
mir::Type::Expr(expr) => Arc::new(lir::Type::new(
name,
documentation,
parameters,
lir::InnerType::Expr(Arc::new(expr.lower())),
)),
mir::Type::List(terms) => {
let mut inner = Vec::new();
for e in terms {
inner.push(convert(
e.name(),
e.documentation(),
e.parameters().iter().map(|e| e.inner()).collect(),
&e.ty(),
))
}
Arc::new(lir::Type::new(
name,
documentation,
parameters,
lir::InnerType::List(inner),
))
}
mir::Type::Nothing => Arc::new(lir::Type::new(
name,
documentation,
Vec::default(),
lir::InnerType::Nothing,
)),
}
}
#[derive(Debug)]
pub struct EvalContext {
trace: EvalTrace,
}
impl Default for EvalContext {
fn default() -> Self {
Self {
trace: EvalTrace::Disabled,
}
}
}
impl EvalContext {
pub fn new(trace: EvalTrace) -> Self {
Self { trace }
}
pub fn trace(&self) -> TraceHandle {
match self.trace {
EvalTrace::Enabled => TraceHandle {
start: Some(Instant::now()),
},
EvalTrace::Disabled => TraceHandle { start: None },
}
}
}
#[derive(Clone, Debug, Copy)]
pub enum EvalTrace {
Enabled,
Disabled,
}
#[derive(Clone, Debug, Copy)]
pub struct TraceHandle {
start: Option<Instant>,
}
impl TraceHandle {
fn done(self) -> Option<TraceResult> {
self.start
.map(|start| TraceResult::new(Instant::now() - start))
}
}