use arcstr::literal;
use nom::{branch::alt, bytes::complete::tag, character::complete::{char, multispace0}, combinator::{map, opt, recognize, value}, multi::separated_list1, sequence::{delimited, preceded, terminated}, IResult, Parser};
use crate::{model::SId, parser::{doc::StofParseError, ident::ident_type}, runtime::{NumT, Type, Units}};
pub fn parse_type_complete(input: &str) -> Result<Type, nom::Err<StofParseError>> {
let res = parse_type(input)?;
Ok(res.1)
}
pub fn parse_type(input: &str) -> IResult<&str, Type, StofParseError> {
let (input, mut res_type) = map((
multispace0,
alt((
parse_union,
value(Type::Null, tag("null")),
value(Type::Void, tag("void")),
value(Type::Num(NumT::Int), tag("int")),
value(Type::Num(NumT::Float), tag("float")),
value(Type::Str, tag("str")),
value(Type::Prompt, tag("prompt")),
value(Type::Ver, tag("ver")),
value(Type::Blob, tag("blob")),
value(Type::Bool, tag("bool")),
value(Type::List, tag("list")),
value(Type::Unknown, tag("unknown")),
value(Type::Data(literal!("data")), tag("data")),
value(Type::Fn, tag("fn")),
value(Type::Obj(SId::from("obj")), tag("obj")),
value(Type::Set, tag("set")),
value(Type::Map, tag("map")),
parse_custom_data,
parse_promise,
parse_obj_or_units,
parse_tuple,
))
), |(_, ty)| ty).parse(input)?;
let (input, nn) = opt(char('!')).parse(input)?;
if nn.is_some() { res_type = Type::NotNull(Box::new(res_type)); }
Ok((input, res_type))
}
fn parse_inner_union(input: &str) -> IResult<&str, Type, StofParseError> {
map((
multispace0,
alt((
value(Type::Null, tag("null")),
value(Type::Void, tag("void")),
value(Type::Num(NumT::Int), tag("int")),
value(Type::Num(NumT::Float), tag("float")),
value(Type::Str, tag("str")),
value(Type::Prompt, tag("prompt")),
value(Type::Ver, tag("ver")),
value(Type::Blob, tag("blob")),
value(Type::Bool, tag("bool")),
value(Type::List, tag("list")),
value(Type::Unknown, tag("unknown")),
value(Type::Data(literal!("data")), tag("data")),
value(Type::Fn, tag("fn")),
value(Type::Obj(SId::from("obj")), tag("obj")),
value(Type::Set, tag("set")),
value(Type::Map, tag("map")),
parse_custom_data,
parse_promise,
parse_obj_or_units,
parse_tuple,
))
), |(_, ty)| ty).parse(input)
}
fn parse_obj_or_units(input: &str) -> IResult<&str, Type, StofParseError> {
let (input, parsed) = recognize(separated_list1(char('.'), ident_type)).parse(input)?;
let units = Units::from(parsed);
if units.has_units() && !units.is_undefined() {
Ok((input, Type::Num(NumT::Units(units))))
} else {
Ok((input, Type::Obj(parsed.into())))
}
}
fn parse_tuple(input: &str) -> IResult<&str, Type, StofParseError> {
map(
delimited(
preceded(char('('), multispace0),
separated_list1(
delimited(multispace0, char(','), multispace0),
parse_inner_union
),
terminated(multispace0, char(')'))
),
|list| Type::Tup(list.into_iter().collect())
).parse(input)
}
fn parse_union(input: &str) -> IResult<&str, Type, StofParseError> {
map(
parse_union_list,
|list| Type::Union(list.into_iter().collect())
).parse(input)
}
fn parse_union_list(input: &str) -> IResult<&str, Vec<Type>, StofParseError> {
let mut parser = separated_list1(preceded(multispace0, tag("|")), parse_inner_union);
let (input, vals) = parser.parse(input)?;
if vals.len() < 2 {
Err(nom::Err::Error(StofParseError::from(format!("union type must have at least 2 values: {vals:?}"))))
} else {
Ok((input, vals))
}
}
fn parse_custom_data(input: &str) -> IResult<&str, Type, StofParseError> {
map(
delimited(tag("Data<"), ident_type, char('>')),
|ct| Type::Data(ct.into())
).parse(input)
}
fn parse_promise(input: &str) -> IResult<&str, Type, StofParseError> {
map(
delimited(tag("Promise<"), parse_inner_promise, char('>')),
|ct| Type::Promise(ct.into())
).parse(input)
}
fn parse_inner_promise(input: &str) -> IResult<&str, Type, StofParseError> {
alt((
parse_union,
value(Type::Null, tag("null")),
value(Type::Void, tag("void")),
value(Type::Num(NumT::Int), tag("int")),
value(Type::Num(NumT::Float), tag("float")),
value(Type::Str, tag("str")),
value(Type::Prompt, tag("prompt")),
value(Type::Ver, tag("ver")),
value(Type::Blob, tag("blob")),
value(Type::Bool, tag("bool")),
value(Type::List, tag("list")),
value(Type::Unknown, tag("unknown")),
value(Type::Data(literal!("data")), tag("data")),
value(Type::Fn, tag("fn")),
value(Type::Obj(SId::from("obj")), tag("obj")),
value(Type::Set, tag("set")),
value(Type::Map, tag("map")),
parse_custom_data,
parse_obj_or_units,
parse_tuple,
)).parse(input)
}
#[cfg(test)]
mod tests {
use arcstr::{literal, ArcStr};
use imbl::vector;
use crate::{model::SId, parser::types::parse_type_complete, runtime::{NumT, Type, Units}};
#[test]
fn from_str() {
assert_eq!(Type::from("str"), Type::Str);
assert_eq!(Type::from("\n\t\t\t\tbool\n\t\n\n\r"), Type::Bool);
assert_eq!(Type::from("ms|seconds|ns"), Type::Union(vector![
Type::Num(NumT::Units(Units::Milliseconds)),
Type::Num(NumT::Units(Units::Seconds)),
Type::Num(NumT::Units(Units::Nanoseconds))
]));
assert_eq!(Type::from(literal!("blob")), Type::Blob);
assert_eq!(Type::from(String::from("fn")), Type::Fn);
}
#[test]
fn parse_custom_data() {
assert_eq!(parse_type_complete("Data<PDF>").unwrap(), Type::Data("PDF".into()));
assert_eq!(parse_type_complete("Data<Image>").unwrap(), Type::Data("Image".into()));
}
#[test]
fn parse_tuples() {
assert_eq!(parse_type_complete("(int,str)").unwrap(), Type::Tup(vector![Type::Num(NumT::Int), Type::Str]));
assert_eq!(parse_type_complete("( str \n, \n\tstr )").unwrap(), Type::Tup(vector![Type::Str, Type::Str]));
assert_eq!(parse_type_complete("((bool, (str, str), blob), fn)").unwrap(), Type::Tup(vector![Type::Tup(vector![Type::Bool, Type::Tup(vector![Type::Str, Type::Str]), Type::Blob]), Type::Fn]));
}
#[test]
fn parse_union() {
assert_eq!(parse_type_complete("int | str").unwrap(), Type::Union(vector![Type::Num(NumT::Int), Type::Str]));
assert_eq!(parse_type_complete("(bool, fn, blob) \n\t\t| str \n | fn\n\n").unwrap(), Type::Union(vector![Type::Tup(vector![Type::Bool, Type::Fn, Type::Blob]), Type::Str, Type::Fn]));
assert_eq!(parse_type_complete("bool|blob|fn|str").unwrap(), Type::Union(vector![Type::Bool, Type::Blob, Type::Fn, Type::Str]));
}
#[test]
fn parse_littypes() {
assert_eq!(parse_type_complete("int, ").unwrap(), Type::Num(NumT::Int));
assert_eq!(parse_type_complete(" null ").unwrap(), Type::Null);
assert_eq!(parse_type_complete(" null").unwrap(), Type::Null);
assert_eq!(parse_type_complete("null ").unwrap(), Type::Null);
assert_eq!(parse_type_complete("void").unwrap(), Type::Void);
assert_eq!(parse_type_complete("bool").unwrap(), Type::Bool);
assert_eq!(parse_type_complete("int").unwrap(), Type::Num(NumT::Int));
assert_eq!(parse_type_complete("float").unwrap(), Type::Num(NumT::Float));
assert_eq!(parse_type_complete("ms").unwrap(), Type::Num(NumT::Units(Units::Milliseconds)));
assert_eq!(parse_type_complete("str").unwrap(), Type::Str);
assert_eq!(parse_type_complete("ver").unwrap(), Type::Ver);
assert_eq!(parse_type_complete("obj").unwrap(), Type::Obj("obj".into()));
assert_eq!(parse_type_complete("fn").unwrap(), Type::Fn);
assert_eq!(parse_type_complete("data").unwrap(), Type::Data("data".into()));
assert_eq!(parse_type_complete("blob").unwrap(), Type::Blob);
assert_eq!(parse_type_complete("list").unwrap(), Type::List);
assert_eq!(parse_type_complete("map").unwrap(), Type::Map);
assert_eq!(parse_type_complete("set").unwrap(), Type::Set);
assert_eq!(parse_type_complete("unknown").unwrap(), Type::Unknown);
assert_eq!(parse_type_complete("CustomType").unwrap(), Type::Obj("CustomType".into()));
}
#[test]
fn parse_promise_works() {
assert_eq!(parse_type_complete("Promise<str>").unwrap(), Type::Promise(Box::new(Type::Str)));
assert_eq!(parse_type_complete("Promise<Data<PDF>>").unwrap(), Type::Promise(Box::new(Type::Data(ArcStr::from("PDF")))));
assert_eq!(parse_type_complete("Promise<str | bool | int>").unwrap(), Type::Promise(Box::new(Type::Union(vector![Type::Str, Type::Bool, Type::Num(NumT::Int)]))));
assert_eq!(parse_type_complete("Promise<(str, bool, blob)>").unwrap(), Type::Promise(Box::new(Type::Tup(vector![Type::Str, Type::Bool, Type::Blob]))));
}
#[test]
fn type_equality() {
assert_eq!(Type::Unknown, Type::Null);
assert_eq!(Type::Unknown, Type::Bool);
assert_eq!(Type::Unknown, Type::Obj(SId::from("obj")));
assert_eq!(Type::Null, Type::Unknown);
assert_eq!(Type::Bool, Type::Unknown);
assert_eq!(Type::Obj(SId::from("obj")), Type::Unknown);
assert_eq!(Type::Unknown, Type::Union(vector![Type::Bool, Type::Str, Type::Blob]));
assert_eq!(Type::Bool, Type::Union(vector![Type::Bool, Type::Str, Type::Blob]));
assert_eq!(Type::Str, Type::Union(vector![Type::Bool, Type::Str, Type::Blob]));
assert_ne!(Type::Ver, Type::Union(vector![Type::Bool, Type::Str, Type::Blob]));
assert_eq!(Type::Bool, Type::Promise(Box::new(Type::Bool)));
assert_eq!(Type::Promise(Box::new(Type::Bool)), Type::Promise(Box::new(Type::Bool)));
assert_eq!(Type::Promise(Box::new(Type::Bool)), Type::Bool);
assert_ne!(Type::Promise(Box::new(Type::Bool)), Type::Promise(Box::new(Type::Str)));
assert_ne!(Type::Promise(Box::new(Type::Bool)), Type::Str);
}
}