use std::sync::Arc;
use std::time::Duration;
use crate::{
capability::CapabilityName,
env::Cx,
error::{Diagnostic, Result, Severity},
expr::Expr,
hint::{HintMetadata, diagnostic_hints_value},
id::{CORE_EVAL_REPLY_CLASS_ID, CORE_EVAL_REQUEST_CLASS_ID, ClassId, Symbol},
object::{ClassRef, Object, ShapeRef},
value::Value,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Phase {
Read,
Expand,
Compile,
Eval,
}
pub trait MacroExpander: Send + Sync {
fn expand_expr(&self, cx: &mut Cx, phase: Phase, expr: Expr) -> Result<Expr>;
}
pub type MacroExpanderRef = Arc<dyn MacroExpander>;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum Consistency {
LocalOnly,
#[default]
LocalFirst,
RemoteOnly,
}
impl Consistency {
pub fn as_symbol(self) -> Symbol {
Symbol::new(match self {
Self::LocalOnly => "local-only",
Self::LocalFirst => "local-first",
Self::RemoteOnly => "remote-only",
})
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum EvalMode {
#[default]
Eval,
Logic,
}
impl EvalMode {
pub fn as_symbol(self) -> Symbol {
Symbol::new(match self {
Self::Eval => "eval",
Self::Logic => "logic",
})
}
}
#[derive(Clone)]
pub struct EvalRequest {
pub expr: Expr,
pub result_shape: Option<ShapeRef>,
pub required_capabilities: Vec<CapabilityName>,
pub deadline: Option<Duration>,
pub consistency: Consistency,
pub mode: EvalMode,
pub answer_limit: Option<usize>,
pub stream_buffer: Option<usize>,
pub stream: bool,
pub trace: bool,
}
#[derive(Clone)]
pub struct EvalReply {
pub value: Value,
pub diagnostics: Vec<Diagnostic>,
pub trace: Option<Value>,
}
pub trait EvalFabric: Send + Sync {
fn realize(&self, cx: &mut Cx, request: EvalRequest) -> Result<EvalReply>;
}
pub type EvalFabricRef = Arc<dyn EvalFabric>;
impl Object for EvalRequest {
fn display(&self, _cx: &mut Cx) -> Result<String> {
Ok("#<EvalRequest>".to_owned())
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl crate::ObjectCompat for EvalRequest {
fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
runtime_class(
cx,
CORE_EVAL_REQUEST_CLASS_ID,
Symbol::qualified("core", "EvalRequest"),
)
}
fn as_expr(&self, cx: &mut Cx) -> Result<Expr> {
self.as_table(cx)?.object().as_expr(cx)
}
fn as_table(&self, cx: &mut Cx) -> Result<Value> {
let result_shape = match &self.result_shape {
Some(shape) => shape.clone(),
None => cx.factory().nil()?,
};
let deadline = match self.deadline {
Some(deadline) => cx.factory().string(format_duration(deadline))?,
None => cx.factory().nil()?,
};
let required_capabilities = cx.factory().list(
self.required_capabilities
.iter()
.map(|capability| cx.factory().string(capability.as_str().to_owned()))
.collect::<Result<Vec<_>>>()?,
)?;
cx.factory().table(vec![
(Symbol::new("expr"), cx.factory().expr(self.expr.clone())?),
(Symbol::new("result-shape"), result_shape),
(Symbol::new("requires"), required_capabilities),
(Symbol::new("deadline"), deadline),
(
Symbol::new("consistency"),
cx.factory().symbol(self.consistency.as_symbol())?,
),
(
Symbol::new("mode"),
cx.factory().symbol(self.mode.as_symbol())?,
),
(
Symbol::new("answer-limit"),
match self.answer_limit {
Some(limit) => cx.factory().string(limit.to_string())?,
None => cx.factory().nil()?,
},
),
(
Symbol::new("stream-buffer"),
match self.stream_buffer {
Some(limit) => cx.factory().string(limit.to_string())?,
None => cx.factory().nil()?,
},
),
(Symbol::new("stream"), cx.factory().bool(self.stream)?),
(Symbol::new("trace"), cx.factory().bool(self.trace)?),
])
}
}
impl Object for EvalReply {
fn display(&self, _cx: &mut Cx) -> Result<String> {
Ok("#<EvalReply>".to_owned())
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl crate::ObjectCompat for EvalReply {
fn class(&self, cx: &mut Cx) -> Result<ClassRef> {
runtime_class(
cx,
CORE_EVAL_REPLY_CLASS_ID,
Symbol::qualified("core", "EvalReply"),
)
}
fn as_expr(&self, cx: &mut Cx) -> Result<Expr> {
self.as_table(cx)?.object().as_expr(cx)
}
fn as_table(&self, cx: &mut Cx) -> Result<Value> {
let trace = match &self.trace {
Some(trace) => trace.clone(),
None => cx.factory().nil()?,
};
let diagnostic_values = self
.diagnostics
.iter()
.map(|diagnostic| diagnostic_value(cx, diagnostic))
.collect::<Result<Vec<_>>>()?;
let diagnostics = cx.factory().list(diagnostic_values)?;
cx.factory().table(vec![
(Symbol::new("value"), self.value.clone()),
(Symbol::new("diagnostics"), diagnostics),
(Symbol::new("trace"), trace),
])
}
}
fn runtime_class(cx: &mut Cx, id: ClassId, symbol: Symbol) -> Result<ClassRef> {
if let Some(value) = cx.registry().class_by_symbol(&symbol) {
return Ok(value.clone());
}
cx.factory().class_stub(id, symbol)
}
fn format_duration(duration: Duration) -> String {
if duration.subsec_nanos() == 0 && duration.as_secs() > 0 {
format!("{}s", duration.as_secs())
} else {
format!("{}ms", duration.as_millis())
}
}
fn diagnostic_value(cx: &mut Cx, diagnostic: &Diagnostic) -> Result<Value> {
let hints = diagnostic_hints_value(cx, diagnostic)?;
let severity = cx.factory().symbol(Symbol::new(match diagnostic.severity {
Severity::Error => "error",
Severity::Warning => "warning",
Severity::Info => "info",
Severity::Note => "note",
}))?;
let source = match &diagnostic.source {
Some(source) => cx.factory().string(source.0.to_string())?,
None => cx.factory().nil()?,
};
let span = match &diagnostic.span {
Some(span) => cx.factory().table(vec![
(
Symbol::new("start"),
cx.factory().string(span.start.to_string())?,
),
(
Symbol::new("end"),
cx.factory().string(span.end.to_string())?,
),
])?,
None => cx.factory().nil()?,
};
let code = match &diagnostic.code {
Some(code) => cx.factory().symbol(code.clone())?,
None => cx.factory().nil()?,
};
let related_values = diagnostic
.related
.iter()
.filter(|related| !HintMetadata::is_hint_diagnostic(related))
.map(|related| diagnostic_value(cx, related))
.collect::<Result<Vec<_>>>()?;
let related = cx.factory().list(related_values)?;
cx.factory().table(vec![
(Symbol::new("severity"), severity),
(
Symbol::new("message"),
cx.factory().string(diagnostic.message.clone())?,
),
(Symbol::new("source"), source),
(Symbol::new("span"), span),
(Symbol::new("code"), code),
(Symbol::new("related"), related),
(Symbol::new("hints"), hints),
])
}