use crate::ast::ArgSep;
use crate::ast::ExprType;
use crate::bytecode::TaggedRegisterRef;
use crate::bytecode::VarArgTag;
use crate::mem::HeapOverflowError;
use crate::mem::{ConstantDatum, DatumPtr, Heap, HeapDatum};
use crate::reader::LineCol;
use async_trait::async_trait;
use std::borrow::Cow;
use std::fmt;
use std::io;
use std::ops::RangeInclusive;
use std::rc::Rc;
use std::str::Lines;
#[derive(Debug, thiserror::Error)]
pub enum CallError {
#[error("{0}")]
Argument(String),
#[error("{0}")]
Eval(String),
#[error("{0}")]
IoError(#[from] io::Error),
#[error("{0}")]
Precondition(String),
#[error("{0}: {1}")]
Syntax(LineCol, String),
}
impl From<HeapOverflowError> for CallError {
fn from(value: HeapOverflowError) -> Self {
Self::Eval(value.to_string())
}
}
impl CallError {
pub(crate) fn to_upcall_error(&self, default_pos: LineCol) -> UpcallError {
match self {
CallError::Argument(message) => UpcallError::Argument(default_pos, message.clone()),
CallError::Eval(message) => UpcallError::Eval(default_pos, message.clone()),
CallError::IoError(e) => UpcallError::IoError(default_pos, e.to_string()),
CallError::Precondition(message) => {
UpcallError::Precondition(default_pos, message.clone())
}
CallError::Syntax(pos, message) => UpcallError::Syntax(*pos, message.clone()),
}
}
}
pub type CallResult<T> = Result<T, CallError>;
#[derive(Debug, thiserror::Error)]
pub enum UpcallError {
#[error("{0}: {1}")]
Argument(LineCol, String),
#[error("{0}: {1}")]
Eval(LineCol, String),
#[error("{0}: {1}")]
IoError(LineCol, String),
#[error("{0}: {1}")]
Precondition(LineCol, String),
#[error("{0}: {1}")]
Syntax(LineCol, String),
}
impl UpcallError {
pub fn parts(&self) -> (LineCol, String) {
match self {
UpcallError::Argument(pos, message) => (*pos, message.clone()),
UpcallError::Eval(pos, message) => (*pos, message.clone()),
UpcallError::IoError(pos, message) => (*pos, message.clone()),
UpcallError::Precondition(pos, message) => (*pos, message.clone()),
UpcallError::Syntax(pos, message) => (*pos, message.clone()),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct RequiredValueSyntax {
pub name: Cow<'static, str>,
pub vtype: ExprType,
}
#[derive(Clone, Debug, PartialEq)]
pub struct RequiredRefSyntax {
pub name: Cow<'static, str>,
pub require_array: bool,
pub define_undefined: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub struct OptionalValueSyntax {
pub name: Cow<'static, str>,
pub vtype: ExprType,
}
#[derive(Clone, Debug, PartialEq)]
pub enum RepeatedTypeSyntax {
AnyValue,
TypedValue(ExprType),
VariableRef,
}
#[derive(Clone, Debug, PartialEq)]
pub struct RepeatedSyntax {
pub name: Cow<'static, str>,
pub type_syn: RepeatedTypeSyntax,
pub sep: ArgSepSyntax,
pub require_one: bool,
pub allow_missing: bool,
}
impl RepeatedSyntax {
fn describe(&self, output: &mut String, last_singular_sep: Option<&ArgSepSyntax>) {
if !self.require_one {
output.push('[');
}
if let Some(sep) = last_singular_sep {
sep.describe(output);
}
output.push_str(&self.name);
output.push('1');
if let RepeatedTypeSyntax::TypedValue(vtype) = self.type_syn {
output.push(vtype.annotation());
}
if self.require_one {
output.push('[');
}
self.sep.describe(output);
output.push_str("..");
self.sep.describe(output);
output.push_str(&self.name);
output.push('N');
if let RepeatedTypeSyntax::TypedValue(vtype) = self.type_syn {
output.push(vtype.annotation());
}
output.push(']');
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct AnyValueSyntax {
pub name: Cow<'static, str>,
pub allow_missing: bool,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ArgSepSyntax {
Exactly(ArgSep),
OneOf(&'static [ArgSep]),
End,
}
impl ArgSepSyntax {
fn describe(&self, output: &mut String) {
match self {
ArgSepSyntax::Exactly(sep) => {
let (text, needs_space) = sep.describe();
if !text.is_empty() && needs_space {
output.push(' ');
}
output.push_str(text);
if !text.is_empty() {
output.push(' ');
}
}
ArgSepSyntax::OneOf(seps) => {
output.push_str(" <");
for (i, sep) in seps.iter().enumerate() {
let (text, _needs_space) = sep.describe();
output.push_str(text);
if i < seps.len() - 1 {
output.push('|');
}
}
output.push_str("> ");
}
ArgSepSyntax::End => (),
};
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum SingularArgSyntax {
RequiredValue(RequiredValueSyntax, ArgSepSyntax),
RequiredRef(RequiredRefSyntax, ArgSepSyntax),
OptionalValue(OptionalValueSyntax, ArgSepSyntax),
AnyValue(AnyValueSyntax, ArgSepSyntax),
}
#[derive(Clone, Debug, PartialEq)]
pub(crate) struct CallableSyntax {
pub(crate) singular: Cow<'static, [SingularArgSyntax]>,
pub(crate) repeated: Option<Cow<'static, RepeatedSyntax>>,
}
impl CallableSyntax {
pub(crate) fn new_static(
singular: &'static [SingularArgSyntax],
repeated: Option<&'static RepeatedSyntax>,
) -> Self {
Self { singular: Cow::Borrowed(singular), repeated: repeated.map(Cow::Borrowed) }
}
pub(crate) fn new_dynamic(
singular: Vec<SingularArgSyntax>,
repeated: Option<RepeatedSyntax>,
) -> Self {
Self { singular: Cow::Owned(singular), repeated: repeated.map(Cow::Owned) }
}
pub(crate) fn expected_nargs(&self) -> RangeInclusive<usize> {
let mut min = self.singular.len();
let mut max = self.singular.len();
if let Some(syn) = self.repeated.as_ref() {
if syn.require_one {
min += 1;
}
max = usize::MAX;
}
min..=max
}
pub(crate) fn is_empty(&self) -> bool {
self.singular.is_empty() && self.repeated.is_none()
}
pub(crate) fn describe(&self) -> String {
let mut description = String::new();
let mut last_singular_sep = None;
for (i, s) in self.singular.iter().enumerate() {
let sep = match s {
SingularArgSyntax::RequiredValue(details, sep) => {
description.push_str(&details.name);
description.push(details.vtype.annotation());
sep
}
SingularArgSyntax::RequiredRef(details, sep) => {
description.push_str(&details.name);
sep
}
SingularArgSyntax::OptionalValue(details, sep) => {
description.push('[');
description.push_str(&details.name);
description.push(details.vtype.annotation());
description.push(']');
sep
}
SingularArgSyntax::AnyValue(details, sep) => {
if details.allow_missing {
description.push('[');
}
description.push_str(&details.name);
if details.allow_missing {
description.push(']');
}
sep
}
};
if self.repeated.is_none() || i < self.singular.len() - 1 {
sep.describe(&mut description);
}
if i == self.singular.len() - 1 {
last_singular_sep = Some(sep);
}
}
if let Some(syn) = &self.repeated {
syn.describe(&mut description, last_singular_sep);
}
description
}
}
pub struct CallableMetadataBuilder {
name: Cow<'static, str>,
return_type: Option<ExprType>,
is_async: bool,
category: Option<&'static str>,
syntaxes: Vec<CallableSyntax>,
description: Option<&'static str>,
}
impl CallableMetadataBuilder {
pub fn new(name: &'static str) -> Self {
assert!(name == name.to_ascii_uppercase(), "Callable name must be in uppercase");
Self {
name: Cow::Borrowed(name),
return_type: None,
is_async: false,
syntaxes: vec![],
category: None,
description: None,
}
}
pub fn new_dynamic<S: Into<String>>(name: S) -> Self {
Self {
name: Cow::Owned(name.into().to_ascii_uppercase()),
return_type: None,
is_async: false,
syntaxes: vec![],
category: Some("User defined"),
description: Some("User defined symbol."),
}
}
pub fn with_return_type(mut self, return_type: ExprType) -> Self {
self.return_type = Some(return_type);
self
}
pub fn with_async(mut self, is_async: bool) -> Self {
self.is_async = is_async;
self
}
pub fn with_syntax(
mut self,
syntaxes: &'static [(&'static [SingularArgSyntax], Option<&'static RepeatedSyntax>)],
) -> Self {
self.syntaxes = syntaxes
.iter()
.map(|s| CallableSyntax::new_static(s.0, s.1))
.collect::<Vec<CallableSyntax>>();
self
}
pub(crate) fn with_syntaxes<S: Into<Vec<CallableSyntax>>>(mut self, syntaxes: S) -> Self {
self.syntaxes = syntaxes.into();
self
}
pub(crate) fn with_dynamic_syntax(
self,
syntaxes: Vec<(Vec<SingularArgSyntax>, Option<RepeatedSyntax>)>,
) -> Self {
let syntaxes = syntaxes
.into_iter()
.map(|s| CallableSyntax::new_dynamic(s.0, s.1))
.collect::<Vec<CallableSyntax>>();
self.with_syntaxes(syntaxes)
}
pub fn with_category(mut self, category: &'static str) -> Self {
self.category = Some(category);
self
}
pub fn with_description(mut self, description: &'static str) -> Self {
for l in description.lines() {
assert!(!l.is_empty(), "Description cannot contain empty lines");
}
self.description = Some(description);
self
}
pub fn build(self) -> Rc<CallableMetadata> {
assert!(!self.syntaxes.is_empty(), "All callables must specify a syntax");
Rc::from(CallableMetadata {
name: self.name,
return_type: self.return_type,
is_async: self.is_async,
syntaxes: self.syntaxes,
category: self.category.expect("All callables must specify a category"),
description: self.description.expect("All callables must specify a description"),
})
}
pub fn test_build(mut self) -> Rc<CallableMetadata> {
if self.syntaxes.is_empty() {
self.syntaxes.push(CallableSyntax::new_static(&[], None));
}
Rc::from(CallableMetadata {
name: self.name,
return_type: self.return_type,
is_async: self.is_async,
syntaxes: self.syntaxes,
category: self.category.unwrap_or(""),
description: self.description.unwrap_or(""),
})
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct CallableMetadata {
name: Cow<'static, str>,
return_type: Option<ExprType>,
is_async: bool,
syntaxes: Vec<CallableSyntax>,
category: &'static str,
description: &'static str,
}
impl CallableMetadata {
pub fn name(&self) -> &str {
&self.name
}
pub fn return_type(&self) -> Option<ExprType> {
self.return_type
}
pub fn is_async(&self) -> bool {
self.is_async
}
pub fn syntax(&self) -> String {
fn format_one(cs: &CallableSyntax) -> String {
let mut syntax = cs.describe();
if syntax.is_empty() {
syntax.push_str("no arguments");
}
syntax
}
match self.syntaxes.as_slice() {
[] => panic!("Callables without syntaxes are not allowed at construction time"),
[one] => format_one(one),
many => many
.iter()
.map(|syn| format!("<{}>", syn.describe()))
.collect::<Vec<String>>()
.join(" | "),
}
}
fn is_function_sep(sep: &ArgSepSyntax) -> bool {
match sep {
ArgSepSyntax::Exactly(ArgSep::Long) | ArgSepSyntax::End => true,
ArgSepSyntax::OneOf(seps) => seps.iter().all(|s| *s == ArgSep::Long),
_ => false,
}
}
fn debug_assert_function_seps(&self, syntax: &CallableSyntax) {
if self.return_type().is_none() {
return;
}
for syn in syntax.singular.iter() {
let sep = match syn {
SingularArgSyntax::RequiredValue(_, sep) => sep,
SingularArgSyntax::RequiredRef(_, sep) => sep,
SingularArgSyntax::OptionalValue(_, sep) => sep,
SingularArgSyntax::AnyValue(_, sep) => sep,
};
debug_assert!(
Self::is_function_sep(sep),
"Function {} has a non-comma separator in its singular args syntax",
self.name()
);
}
if let Some(repeated) = syntax.repeated.as_ref() {
debug_assert!(
Self::is_function_sep(&repeated.sep),
"Function {} has a non-comma separator in its repeated args syntax",
self.name()
);
}
}
pub(crate) fn find_syntax(&self, nargs: usize) -> Option<&CallableSyntax> {
let mut matches = self.syntaxes.iter().filter(|s| s.expected_nargs().contains(&nargs));
let syntax = matches.next();
match syntax {
Some(syntax) => {
debug_assert!(matches.next().is_none(), "Ambiguous syntax definitions");
if cfg!(debug_assertions) {
self.debug_assert_function_seps(syntax);
}
Some(syntax)
}
None => None,
}
}
#[allow(unused)]
pub fn category(&self) -> &'static str {
self.category
}
#[allow(unused)]
pub fn description(&self) -> Lines<'static> {
self.description.lines()
}
#[allow(unused)]
pub fn is_argless(&self) -> bool {
self.syntaxes.is_empty() || (self.syntaxes.len() == 1 && self.syntaxes[0].is_empty())
}
#[allow(unused)]
pub(crate) fn is_function(&self) -> bool {
self.return_type.is_some()
}
pub(crate) fn is_user_defined(&self) -> bool {
self.category == "User defined"
}
}
fn deref_boolean(regs: &[u64], index: usize, vtype: ExprType) -> bool {
assert_eq!(ExprType::Boolean, vtype);
regs[index] != 0
}
fn deref_double(regs: &[u64], index: usize, vtype: ExprType) -> f64 {
assert_eq!(ExprType::Double, vtype);
f64::from_bits(regs[index])
}
fn deref_integer(regs: &[u64], index: usize, vtype: ExprType) -> i32 {
assert_eq!(ExprType::Integer, vtype);
regs[index] as i32
}
fn deref_string<'a>(
regs: &[u64],
index: usize,
vtype: ExprType,
constants: &'a [ConstantDatum],
heap: &'a Heap,
) -> &'a str {
assert_eq!(ExprType::Text, vtype);
let ptr = DatumPtr::from(regs[index]);
ptr.resolve_string(constants, heap)
}
fn array_dimensions<'a>(regs: &'a [u64], index: usize, heap: &'a Heap) -> &'a [usize] {
let ptr = DatumPtr::from(regs[index]);
let heap_idx = ptr.heap_index();
let HeapDatum::Array(a) = heap.get(heap_idx) else {
panic!("Scalar variable does not point to an array on the heap");
};
&a.dimensions
}
pub struct RegisterRef<'a, 'vm> {
scope: &'a Scope<'vm>,
index: usize,
pub vtype: ExprType,
}
impl<'a, 'vm> RegisterRef<'a, 'vm> {
pub fn deref_boolean(&self) -> bool {
deref_boolean(self.scope.regs, self.index, self.vtype)
}
pub fn deref_double(&self) -> f64 {
deref_double(self.scope.regs, self.index, self.vtype)
}
pub fn deref_integer(&self) -> i32 {
deref_integer(self.scope.regs, self.index, self.vtype)
}
pub fn deref_string(&self) -> &str {
deref_string(self.scope.regs, self.index, self.vtype, self.scope.constants, self.scope.heap)
}
pub fn array_dimensions(&self) -> &[usize] {
array_dimensions(self.scope.regs, self.index, self.scope.heap)
}
}
impl<'a, 'vm> fmt::Display for RegisterRef<'a, 'vm> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "&[R{}]{}", self.index, self.vtype)
}
}
pub struct RegisterRefMut<'a, 'vm> {
scope: &'a mut Scope<'vm>,
index: usize,
pub vtype: ExprType,
}
impl<'a, 'vm> RegisterRefMut<'a, 'vm> {
pub fn deref_boolean(&self) -> bool {
deref_boolean(self.scope.regs, self.index, self.vtype)
}
pub fn deref_double(&self) -> f64 {
deref_double(self.scope.regs, self.index, self.vtype)
}
pub fn deref_integer(&self) -> i32 {
deref_integer(self.scope.regs, self.index, self.vtype)
}
pub fn deref_string(&self) -> &str {
deref_string(self.scope.regs, self.index, self.vtype, self.scope.constants, self.scope.heap)
}
pub fn array_dimensions(&self) -> &[usize] {
array_dimensions(self.scope.regs, self.index, self.scope.heap)
}
pub fn set_boolean(&mut self, b: bool) {
assert_eq!(ExprType::Boolean, self.vtype);
self.scope.regs[self.index] = if b { 1 } else { 0 };
}
pub fn set_double(&mut self, d: f64) {
assert_eq!(ExprType::Double, self.vtype);
self.scope.regs[self.index] = d.to_bits();
}
pub fn set_integer(&mut self, i: i32) {
assert_eq!(ExprType::Integer, self.vtype);
self.scope.regs[self.index] = i as u64;
}
pub fn set_string<S: Into<String>>(&mut self, s: S) -> CallResult<()> {
assert_eq!(ExprType::Text, self.vtype);
self.scope.regs[self.index] = self.scope.heap.push(HeapDatum::Text(s.into()))?;
Ok(())
}
}
impl<'a, 'vm> fmt::Display for RegisterRefMut<'a, 'vm> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "&[R{}]{}", self.index, self.vtype)
}
}
pub struct Scope<'a> {
pub(crate) regs: &'a mut [u64],
pub(crate) constants: &'a [ConstantDatum],
pub(crate) heap: &'a mut Heap,
pub(crate) fp: usize,
pub(crate) arg_offset: usize,
pub(crate) arg_linecols: &'a [LineCol],
pub(crate) last_error: &'a Option<(LineCol, String)>,
pub(crate) data: &'a [Option<ConstantDatum>],
}
impl<'a> Scope<'a> {
pub fn data(&self) -> &[Option<ConstantDatum>] {
self.data
}
pub fn nargs(&self) -> usize {
self.arg_linecols.len()
}
pub fn get_pos(&self, arg: u8) -> LineCol {
self.arg_linecols[usize::from(arg)]
}
pub fn get_type(&self, arg: u8) -> VarArgTag {
VarArgTag::parse_u64(self.regs[self.fp + self.arg_offset + (arg as usize)]).unwrap()
}
pub fn get_boolean(&self, arg: u8) -> bool {
self.regs[self.fp + self.arg_offset + (arg as usize)] != 0
}
pub fn get_double(&self, arg: u8) -> f64 {
f64::from_bits(self.regs[self.fp + self.arg_offset + (arg as usize)])
}
pub fn get_integer(&self, arg: u8) -> i32 {
self.regs[self.fp + self.arg_offset + (arg as usize)] as i32
}
pub fn get_ref(&self, arg: u8) -> RegisterRef<'_, 'a> {
let tagged_ptr = self.regs[self.fp + self.arg_offset + (arg as usize)];
let (index, vtype) = TaggedRegisterRef::from_u64(tagged_ptr).parse();
RegisterRef { scope: self, index, vtype }
}
pub fn get_mut_ref(&mut self, arg: u8) -> RegisterRefMut<'_, 'a> {
let tagged_ptr = self.regs[self.fp + self.arg_offset + (arg as usize)];
let (index, vtype) = TaggedRegisterRef::from_u64(tagged_ptr).parse();
RegisterRefMut { scope: self, index, vtype }
}
pub fn get_string(&self, arg: u8) -> &str {
let index = self.regs[self.fp + self.arg_offset + (arg as usize)];
let ptr = DatumPtr::from(index);
ptr.resolve_string(self.constants, self.heap)
}
pub fn last_error(&self) -> Option<(LineCol, &str)> {
self.last_error.as_ref().map(|(pos, message)| (*pos, message.as_str()))
}
pub fn return_boolean(self, b: bool) -> CallResult<()> {
self.regs[self.fp] = if b { 1 } else { 0 };
Ok(())
}
pub fn return_double(self, d: f64) -> CallResult<()> {
self.regs[self.fp] = d.to_bits();
Ok(())
}
pub fn return_integer(self, i: i32) -> CallResult<()> {
self.regs[self.fp] = i as u64;
Ok(())
}
pub fn return_string<S: Into<String>>(self, s: S) -> CallResult<()> {
self.regs[self.fp] = self.heap.push(HeapDatum::Text(s.into()))?;
Ok(())
}
}
#[async_trait(?Send)]
pub trait Callable {
fn metadata(&self) -> Rc<CallableMetadata>;
fn exec(&self, _scope: Scope<'_>) -> CallResult<()> {
unimplemented!("Must be implemented for !is_async callables")
}
async fn async_exec(&self, _scope: Scope<'_>) -> CallResult<()> {
unimplemented!("Must be implemented for is_async callables")
}
}