use cas_compute::numerical::builtin::{Builtin, ParamKind};
use cas_error::Error;
use cas_parser::parser::ast::{Call, Param as ParserParam};
use crate::{error::{MissingArgument, TooManyArguments}, sym_table::ScopeId};
use std::cmp::Ordering;
enum Signature<'a> {
Builtin(&'static dyn Builtin),
Parser(&'a [ParserParam]),
}
impl Signature<'_> {
pub fn missing_args(&self, num_given: usize) -> Vec<usize> {
match self {
Self::Builtin(builtin) => {
let idx_of_first_default = builtin.sig()
.iter()
.position(|param| param.kind == ParamKind::Optional);
(num_given..idx_of_first_default.unwrap_or(builtin.sig().len()))
.collect()
},
Self::Parser(params) => {
let idx_of_first_default = params.iter()
.position(|param| matches!(param, ParserParam::Default(..)));
(num_given..idx_of_first_default.unwrap_or(params.len()))
.collect()
},
}
}
fn signature(&self) -> String {
match self {
Self::Builtin(builtin) => builtin.sig_str().to_owned(),
Self::Parser(params) => params.iter()
.map(|param| match param {
ParserParam::Symbol(name) => format!("{}", name),
ParserParam::Default(name, value) => format!("{} = {}", name, value),
})
.collect::<Vec<_>>()
.join(", "),
}
}
}
fn check_call(sig: Signature<'_>, sig_len: usize, call: &Call) -> Result<(), Error> {
match call.args.len().cmp(&sig_len) {
Ordering::Greater => {
let mut spans = call.outer_span().to_vec();
spans.push(call.arg_span(sig_len..call.args.len() - 1));
Err(Error::new(
spans,
TooManyArguments {
name: call.name.name.to_string(),
expected: sig_len,
given: call.args.len(),
signature: sig.signature(),
},
))
},
Ordering::Equal => Ok(()),
Ordering::Less => {
let indices = sig.missing_args(call.args.len());
if indices.is_empty() {
Ok(())
} else {
Err(Error::new(
call.outer_span().to_vec(),
MissingArgument {
name: call.name.name.to_string(),
indices,
expected: sig_len,
given: call.args.len(),
signature: sig.signature(),
},
))
}
},
}
}
#[derive(Clone, Debug)]
pub enum Item {
Symbol(SymbolDecl),
Func(FuncDecl),
}
impl Item {
pub fn id(&self) -> usize {
match self {
Self::Symbol(symbol) => symbol.id,
Self::Func(func) => func.id,
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct SymbolDecl {
pub id: usize,
}
#[derive(Clone, Debug)]
pub struct FuncDecl {
pub id: usize,
pub scope_id: ScopeId,
pub chunk: usize,
pub signature: Vec<ParserParam>,
}
impl FuncDecl {
pub fn new(
id: usize,
scope_id: ScopeId,
chunk: usize,
signature: Vec<ParserParam>,
) -> Self {
Self {
id,
scope_id,
chunk,
signature,
}
}
pub fn check_call(&self, call: &Call) -> Result<(), Error> {
check_call(
Signature::Parser(&self.signature),
self.signature.len(),
call
)
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub enum Symbol {
User(usize),
Builtin(&'static str),
}
impl Symbol {
pub fn index(&self) -> Result<usize, &'static str> {
match self {
Self::User(index) => Ok(*index),
Self::Builtin(name) => Err(name),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Func {
User(UserCall),
Builtin(BuiltinCall),
}
impl Func {
pub fn arity(&self) -> usize {
match self {
Self::User(user) => user.signature.len(),
Self::Builtin(builtin) => builtin.builtin.sig().len(),
}
}
pub fn check_call(&self, call: &Call) -> Result<(), Error> {
match self {
Self::User(user) => check_call(
Signature::Parser(&user.signature),
user.signature.len(),
call,
),
Self::Builtin(builtin) => check_call(
Signature::Builtin(builtin.builtin),
builtin.builtin.sig().len(),
call,
),
}
}
pub fn num_defaults_used(&self) -> Option<usize> {
match self {
Self::User(call) => call.signature.len().checked_sub(call.num_given),
Self::Builtin(call) => call.builtin.sig().len().checked_sub(call.num_given),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UserCall {
pub chunk: usize,
pub signature: Vec<ParserParam>,
pub num_given: usize,
}
#[derive(Clone, Debug)]
pub struct BuiltinCall {
pub builtin: &'static dyn Builtin,
pub num_given: usize,
}
impl PartialEq for BuiltinCall {
fn eq(&self, other: &Self) -> bool {
std::ptr::eq(self.builtin, other.builtin) && self.num_given == other.num_given
}
}
impl Eq for BuiltinCall {}