use crate::types::{IntegerConstraint, StringConstraint, TypeValue};
use itertools::Itertools;
use std::borrow::Cow;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
use std::hash::{Hash, Hasher};
use std::iter::Peekable;
use std::rc::Rc;
use std::str::Chars;
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Serialize, Deserialize, Hash)]
pub(crate) struct MangledFnName(String);
impl MangledFnName {
#[inline]
pub fn as_str(&self) -> &str {
self.0.as_str()
}
}
impl MangledFnName {
pub fn unmangle(&self) -> (Vec<(&str, TypeValue)>, TypeValue) {
let (_fn_name, arg_names_and_types, ret_type) =
self.0.split('@').collect_tuple().unwrap_or_else(|| {
panic!("invalid mangled name: `{}`", self.0)
});
let mut args = Vec::new();
if !arg_names_and_types.is_empty() {
for arg_str in arg_names_and_types.split(',') {
let (arg_name, arg_type) =
if let Some((n, t)) = arg_str.split_once(':') {
(n, t)
} else {
panic!(
"argument name missing in mangled name: `{}`",
self.0
)
};
let mut chars = arg_type.chars().peekable();
let type_value =
self.next_type(&mut chars).unwrap_or_else(|| {
panic!(
"invalid argument type in mangled name: `{}`",
self.0
)
});
args.push((arg_name, type_value));
}
}
let mut chars = ret_type.chars().peekable();
let ret = self.next_type(&mut chars).unwrap_or_else(|| {
panic!("expecting return type in mangled name: `{}`", self.0)
});
assert!(!matches!(ret, TypeValue::Regexp(_)));
(args, ret)
}
#[inline]
pub fn result_may_be_undef(&self) -> bool {
self.0.ends_with('u')
}
pub fn method_of(&self) -> Option<&str> {
self.0.split_once("::").map(|(type_name, _)| type_name)
}
fn next_type(&self, chars: &mut Peekable<Chars>) -> Option<TypeValue> {
match chars.next() {
Some('u') => Some(TypeValue::Unknown),
Some('r') => Some(TypeValue::Regexp(None)),
Some('f') => Some(TypeValue::unknown_float()),
Some('b') => Some(TypeValue::unknown_bool()),
Some('i') => {
let mut constraints = Vec::new();
while let Some(':') = chars.peek() {
chars.next(); match chars.next() {
Some('R') => {
let min = self.parse_i64(chars);
assert_eq!(chars.next(), Some(':'));
let max = self.parse_i64(chars);
constraints
.push(IntegerConstraint::Range(min, max));
}
None | Some(_) => {
panic!("invalid mangled name: `{}`", self.0)
}
}
}
Some(if constraints.is_empty() {
TypeValue::unknown_integer()
} else {
TypeValue::unknown_integer_with_constraints(constraints)
})
}
Some('s') => {
let mut constraints = Vec::new();
while let Some(':') = chars.peek() {
chars.next(); match chars.next() {
Some('L') => {
constraints.push(StringConstraint::Lowercase);
}
Some('U') => {
constraints.push(StringConstraint::Uppercase);
}
Some('N') => {
let n = self.parse_i64(chars);
constraints.push(StringConstraint::ExactLength(
n as usize,
));
}
None | Some(_) => {
panic!("invalid mangled name: `{}`", self.0)
}
}
}
Some(if constraints.is_empty() {
TypeValue::unknown_string()
} else {
TypeValue::unknown_string_with_constraints(constraints)
})
}
Some(c) => {
panic!("unknown type `{}` in mangled name: `{}`", c, self.0)
}
None => None,
}
}
fn parse_i64(&self, chars: &mut Peekable<Chars>) -> i64 {
chars
.by_ref()
.peeking_take_while(|&c| c.is_ascii_digit() || c == '-')
.collect::<String>()
.parse::<i64>()
.unwrap_or_else(|_| panic!("invalid mangled name: `{}`", self.0))
}
}
impl<S> From<S> for MangledFnName
where
S: Into<String>,
{
fn from(value: S) -> Self {
Self(value.into())
}
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub(crate) struct FuncSignature {
pub mangled_name: MangledFnName,
pub args: Vec<(String, TypeValue)>,
pub result: TypeValue,
pub doc: Option<Cow<'static, str>>,
}
impl FuncSignature {
#[inline]
pub fn result_may_be_undef(&self) -> bool {
self.mangled_name.result_may_be_undef()
}
#[inline]
pub fn method_of(&self) -> Option<&str> {
self.mangled_name.method_of()
}
}
impl Hash for FuncSignature {
fn hash<H: Hasher>(&self, state: &mut H) {
self.mangled_name.hash(state);
}
}
impl Ord for FuncSignature {
fn cmp(&self, other: &Self) -> Ordering {
self.mangled_name.as_str().cmp(other.mangled_name.as_str())
}
}
impl PartialOrd for FuncSignature {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Eq for FuncSignature {}
impl PartialEq for FuncSignature {
fn eq(&self, other: &Self) -> bool {
self.mangled_name == other.mangled_name
}
}
impl<T: Into<String>> From<T> for FuncSignature {
fn from(value: T) -> Self {
let mangled_name = MangledFnName::from(value.into());
let (args_with_names, result) = mangled_name.unmangle();
let mut args = Vec::with_capacity(args_with_names.len());
for (name, ty) in args_with_names {
args.push((name.to_string(), ty));
}
Self { mangled_name, args, result, doc: None }
}
}
#[derive(Clone, Serialize, Deserialize, Debug, Hash, PartialEq, Eq)]
pub(crate) struct Func {
signatures: Vec<Rc<FuncSignature>>,
method_of: Option<String>,
}
impl<T: Into<String>> From<T> for Func {
fn from(value: T) -> Self {
let signature = FuncSignature::from(value);
let method_of = signature.method_of().map(String::from);
Self { signatures: vec![Rc::new(signature)], method_of }
}
}
impl Func {
pub fn is_method(&self) -> bool {
self.method_of.is_some()
}
pub fn add_signature(&mut self, signature: FuncSignature) {
if let Some(method_of) = &self.method_of {
assert_eq!(signature.method_of(), Some(method_of.as_str()));
}
let signature = Rc::new(signature);
match self.signatures.binary_search(&signature) {
Ok(_) => {
panic!(
"function `{}` is implemented twice",
signature.mangled_name.as_str()
)
}
Err(pos) => self.signatures.insert(pos, signature),
}
}
#[inline]
pub fn signatures(&self) -> &[Rc<FuncSignature>] {
self.signatures.as_slice()
}
#[inline]
pub fn signatures_mut(&mut self) -> &mut [Rc<FuncSignature>] {
self.signatures.as_mut_slice()
}
}
#[cfg(test)]
mod test {
use crate::types::{
IntegerConstraint, MangledFnName, StringConstraint, TypeValue,
};
use pretty_assertions::assert_eq;
#[test]
fn mangled_name() {
assert_eq!(
MangledFnName::from("foo@@i").unmangle(),
(vec![], TypeValue::unknown_integer())
);
assert_eq!(
MangledFnName::from("foo@a:i,b:i@i").unmangle(),
(
vec![
("a", TypeValue::unknown_integer()),
("b", TypeValue::unknown_integer())
],
TypeValue::unknown_integer()
)
);
assert_eq!(
MangledFnName::from("foo@a:f,b:f@f").unmangle(),
(
vec![
("a", TypeValue::unknown_float()),
("b", TypeValue::unknown_float())
],
TypeValue::unknown_float()
)
);
assert_eq!(
MangledFnName::from("foo@a:b,b:b@b").unmangle(),
(
vec![
("a", TypeValue::unknown_bool()),
("b", TypeValue::unknown_bool())
],
TypeValue::unknown_bool()
)
);
assert_eq!(
MangledFnName::from("foo@a:s,b:s@s").unmangle(),
(
vec![
("a", TypeValue::unknown_string()),
("b", TypeValue::unknown_string())
],
TypeValue::unknown_string()
)
);
assert_eq!(
MangledFnName::from("foo@a:s,b:s:L@s:L").unmangle(),
(
vec![
("a", TypeValue::unknown_string()),
(
"b",
TypeValue::unknown_string_with_constraints(vec![
StringConstraint::Lowercase
])
)
],
TypeValue::unknown_string_with_constraints(vec![
StringConstraint::Lowercase
])
)
);
assert_eq!(
MangledFnName::from("foo@a:s,b:s:U@s:U").unmangle(),
(
vec![
("a", TypeValue::unknown_string()),
(
"b",
TypeValue::unknown_string_with_constraints(vec![
StringConstraint::Uppercase
])
)
],
TypeValue::unknown_string_with_constraints(vec![
StringConstraint::Uppercase
])
)
);
assert_eq!(
MangledFnName::from("foo@a:s,b:s:N16@s:N16").unmangle(),
(
vec![
("a", TypeValue::unknown_string()),
(
"b",
TypeValue::unknown_string_with_constraints(vec![
StringConstraint::ExactLength(16),
])
)
],
TypeValue::unknown_string_with_constraints(vec![
StringConstraint::ExactLength(16),
])
)
);
assert_eq!(
MangledFnName::from("foo@a:s,b:s:N16:L@s:N16:L").unmangle(),
(
vec![
("a", TypeValue::unknown_string()),
(
"b",
TypeValue::unknown_string_with_constraints(vec![
StringConstraint::ExactLength(16),
StringConstraint::Lowercase
])
)
],
TypeValue::unknown_string_with_constraints(vec![
StringConstraint::ExactLength(16),
StringConstraint::Lowercase
])
)
);
assert_eq!(
MangledFnName::from("foo@@i:R0:10").unmangle(),
(
vec![],
TypeValue::unknown_integer_with_constraints(vec![
IntegerConstraint::Range(0, 10),
])
)
);
assert_eq!(
MangledFnName::from("foo@@i:R-100:1000").unmangle(),
(
vec![],
TypeValue::unknown_integer_with_constraints(vec![
IntegerConstraint::Range(-100, 1000),
])
)
);
assert_eq!(
MangledFnName::from("Bar::foo@a:i,b:i@iu").method_of(),
Some("Bar")
);
assert_eq!(
MangledFnName::from("bar.Bar::foo@a:i,b:i@iu").method_of(),
Some("bar.Bar")
);
assert_eq!(MangledFnName::from("foo@a:i,b:i@iu").method_of(), None);
assert!(!MangledFnName::from("foo@a:i,b:i@i").result_may_be_undef());
assert!(MangledFnName::from("foo@a:i,b:i@iu").result_may_be_undef());
}
#[test]
#[should_panic]
fn invalid_mangled_name_1() {
MangledFnName::from("foo@a:i").unmangle();
}
#[test]
#[should_panic]
fn invalid_mangled_name_2() {
MangledFnName::from("foo@@x").unmangle();
}
#[test]
#[should_panic]
fn invalid_mangled_name_3() {
MangledFnName::from("foo@a:x@i").unmangle();
}
#[test]
#[should_panic]
fn missing_argument_name() {
MangledFnName::from("foo@i@i").unmangle();
}
}