diff --git a/lykiadb-server/src/comm/mod.rs b/lykiadb-server/src/comm/mod.rs index 1adf5db..e71cfd6 100644 --- a/lykiadb-server/src/comm/mod.rs +++ b/lykiadb-server/src/comm/mod.rs @@ -71,8 +71,9 @@ impl ServerSession { Message::Request(req) => match req { Request::Run(command) => { let execution = self.runtime.interpret(command); + let response = if execution.is_ok() { - Response::Value(execution.ok().or_else(|| Some(RV::Undefined)).unwrap()) + Response::Value(execution.ok().unwrap()) } else { Response::Error(execution.err().unwrap()) }; diff --git a/lykiadb-server/src/engine/stdlib/dtype.rs b/lykiadb-server/src/engine/stdlib/dtype.rs new file mode 100644 index 0000000..1391950 --- /dev/null +++ b/lykiadb-server/src/engine/stdlib/dtype.rs @@ -0,0 +1,18 @@ +use crate::{ + engine::interpreter::{HaltReason, InterpretError, Interpreter}, + value::{datatype::Datatype, RV}, +}; + +pub fn nt_of(_interpreter: &mut Interpreter, args: &[RV]) -> Result { + Ok(RV::Datatype(args[0].get_type())) +} + +pub fn nt_array_of(_interpreter: &mut Interpreter, args: &[RV]) -> Result { + match &args[0] { + RV::Datatype(inner) => Ok(RV::Datatype(Datatype::Array(Box::new(inner.clone())))), + _ => Err(HaltReason::Error(InterpretError::Other { + message: format!("array_of: Unexpected argument '{:?}'", args[0]), + } + .into())), + } +} diff --git a/lykiadb-server/src/engine/stdlib/mod.rs b/lykiadb-server/src/engine/stdlib/mod.rs index 21f3384..731c74a 100644 --- a/lykiadb-server/src/engine/stdlib/mod.rs +++ b/lykiadb-server/src/engine/stdlib/mod.rs @@ -1,10 +1,10 @@ +use dtype::nt_array_of; use rustc_hash::FxHashMap; use crate::{ util::{alloc_shared, Shared}, value::{ - callable::{Callable, CallableKind, Function}, - RV, + callable::{Callable, CallableKind, Function}, datatype::Datatype, RV }, }; @@ -13,6 +13,7 @@ use self::{ json::{nt_json_decode, nt_json_encode}, out::nt_print, time::nt_clock, + dtype::nt_of, }; use super::interpreter::Output; @@ -21,6 +22,7 @@ pub mod fib; pub mod json; pub mod out; pub mod time; +pub mod dtype; pub fn stdlib(out: Option>) -> FxHashMap { let mut std = FxHashMap::default(); @@ -29,6 +31,7 @@ pub fn stdlib(out: Option>) -> FxHashMap { let mut json_namespace = FxHashMap::default(); let mut time_namespace = FxHashMap::default(); let mut io_namespace = FxHashMap::default(); + let mut dtype_namespace = FxHashMap::default(); benchmark_namespace.insert( "fib".to_owned(), @@ -79,6 +82,59 @@ pub fn stdlib(out: Option>) -> FxHashMap { )), ); + dtype_namespace.insert( + "of_".to_owned(), + RV::Callable(Callable::new( + Some(1), + CallableKind::Generic, + Function::Lambda { function: nt_of }, + )), + ); + + dtype_namespace.insert( + "str".to_owned(), + RV::Datatype(Datatype::Str), + ); + + dtype_namespace.insert( + "num".to_owned(), + RV::Datatype(Datatype::Num), + ); + + dtype_namespace.insert( + "bool".to_owned(), + RV::Datatype(Datatype::Bool), + ); + + dtype_namespace.insert( + "array".to_owned(), + RV::Callable(Callable::new( + Some(1), + CallableKind::Generic, + Function::Lambda { function: nt_array_of }, + )), + ); + + dtype_namespace.insert( + "document".to_owned(), + RV::Datatype(Datatype::Document(FxHashMap::default())), + ); + + dtype_namespace.insert( + "callable".to_owned(), + RV::Datatype(Datatype::Callable), + ); + + dtype_namespace.insert( + "dtype".to_owned(), + RV::Datatype(Datatype::Datatype), + ); + + dtype_namespace.insert( + "none".to_owned(), + RV::Datatype(Datatype::None), + ); + if out.is_some() { let mut test_namespace = FxHashMap::default(); @@ -104,6 +160,7 @@ pub fn stdlib(out: Option>) -> FxHashMap { std.insert("json".to_owned(), RV::Object(alloc_shared(json_namespace))); std.insert("time".to_owned(), RV::Object(alloc_shared(time_namespace))); std.insert("io".to_owned(), RV::Object(alloc_shared(io_namespace))); + std.insert("dtype".to_owned(), RV::Object(alloc_shared(dtype_namespace))); std } diff --git a/lykiadb-server/src/value/datatype.rs b/lykiadb-server/src/value/datatype.rs index e4815b5..ca19998 100644 --- a/lykiadb-server/src/value/datatype.rs +++ b/lykiadb-server/src/value/datatype.rs @@ -1,11 +1,35 @@ use rustc_hash::FxHashMap; +use std::fmt::Display; +#[derive(Debug, Clone, PartialEq)] pub enum Datatype { Str, Num, Bool, - Composite(FxHashMap), + Document(FxHashMap), Array(Box), Callable, - Undefined + Datatype, + None } + +impl Display for Datatype { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Datatype::Str => write!(f, "dtype::str"), + Datatype::Num => write!(f, "dtype::num"), + Datatype::Bool => write!(f, "dtype::bool"), + Datatype::Document(map) => { + write!(f, "dtype::document({{")?; + for (key, value) in map.iter() { + writeln!(f, "{}: {}, ", key, value)?; + } + write!(f, "}})") + }, + Datatype::Array(inner) => write!(f, "dtype::array({})", inner), + Datatype::Callable => write!(f, "dtype::callable"), + Datatype::Datatype => write!(f, "dtype::dtype"), + Datatype::None => write!(f, "dtype::none") + } + } +} \ No newline at end of file diff --git a/lykiadb-server/src/value/eval.rs b/lykiadb-server/src/value/eval.rs index 4a62df3..e8a0492 100644 --- a/lykiadb-server/src/value/eval.rs +++ b/lykiadb-server/src/value/eval.rs @@ -24,6 +24,8 @@ impl PartialEq for RV { (RV::Num(_), RV::Bool(b)) => self.eq_any_bool(*b), (RV::Bool(a), RV::Num(_)) => other.eq_any_bool(*a), // + (RV::Datatype(a), RV::Datatype(b)) => a == b, + // _ => false, } } diff --git a/lykiadb-server/src/value/mod.rs b/lykiadb-server/src/value/mod.rs index 6963415..39efe87 100644 --- a/lykiadb-server/src/value/mod.rs +++ b/lykiadb-server/src/value/mod.rs @@ -1,3 +1,4 @@ +use datatype::Datatype; use rustc_hash::FxHashMap; use serde::ser::{SerializeMap, SerializeSeq}; use serde::{Deserialize, Serialize}; @@ -21,10 +22,41 @@ pub enum RV { Object(Shared>), Array(Shared>), Callable(Callable), + Datatype(Datatype), Undefined } impl RV { + pub fn get_type(&self) -> Datatype { + match &self { + RV::Str(_) => Datatype::Str, + RV::Num(_) => Datatype::Num, + RV::Bool(_) => Datatype::Bool, + RV::Object(obj) => { + let obj: &FxHashMap = &obj.read().unwrap(); + if obj.is_empty() { + return Datatype::None; + } + let mut document = FxHashMap::default(); + for key in obj.keys() { + let datatype = obj.get(key).unwrap().get_type(); + document.insert(key.to_string(), datatype); + } + Datatype::Document(document) + }, + RV::Array(arr) => { + let arr: &[RV] = &arr.read().unwrap(); + if arr.is_empty() { + return Datatype::Array(Box::from(Datatype::None)); + } + Datatype::Array(Box::from(arr[0].get_type())) + }, + RV::Callable(_) => Datatype::Callable, + RV::Datatype(_) => Datatype::Datatype, + RV::Undefined => Datatype::None, + } + } + pub fn as_bool(&self) -> bool { match &self { RV::Num(value) => !value.is_nan() && value.abs() > 0.0, @@ -117,6 +149,7 @@ impl Display for RV { write!(f, "}}") } RV::Callable(_) => write!(f, ""), + RV::Datatype(dtype) => write!(f, "", dtype), } } } diff --git a/lykiadb-server/tests/interpreter/types/primitives b/lykiadb-server/tests/interpreter/types/primitives new file mode 100644 index 0000000..8cb256c --- /dev/null +++ b/lykiadb-server/tests/interpreter/types/primitives @@ -0,0 +1,95 @@ +#[name=numerical, run=interpreter]> + +test_utils::out(dtype::of_(1) == dtype::num); +test_utils::out(dtype::of_(1.12) == dtype::num); +test_utils::out(dtype::of_(1) == dtype::str); +test_utils::out(dtype::of_(1)); +test_utils::out(dtype::of_(1.12)); +--- + +true +true +false + + + + +#[name=string, run=interpreter]> + +test_utils::out(dtype::of_("1") == dtype::str); +test_utils::out(dtype::of_("1") == dtype::num); +test_utils::out(dtype::of_("1")); + +--- + +true +false + + + +#[name=boolean, run=interpreter]> + +test_utils::out(dtype::of_(true) == dtype::bool); +test_utils::out(dtype::of_(false) == dtype::bool); +test_utils::out(dtype::of_(true) == dtype::str); +test_utils::out(dtype::of_(true)); +test_utils::out(dtype::of_(false)); + +--- + +true +true +false + + + + +#[name=array, run=interpreter]> +test_utils::out(dtype::of_([1, 2, 3]) == dtype::array(dtype::num)); +test_utils::out(dtype::of_([1, 2, 3]) == dtype::array(dtype::str)); +test_utils::out(dtype::of_([1, 2, 3])); + +--- + +true +false + + + +#[name=callable, run=interpreter]> + +test_utils::out(dtype::of_(io::print) == dtype::callable); +test_utils::out(dtype::of_(io::print) == dtype::str); +test_utils::out(dtype::of_(io::print)); + +--- + +true +false + + + +#[name=datatype, run=interpreter]> + +test_utils::out(dtype::of_(dtype::num) == dtype::dtype); +test_utils::out(dtype::of_(dtype::num) == dtype::str); +test_utils::out(dtype::of_(dtype::num)); + +--- + +true +false + + + +#[name=none, run=interpreter]> + +test_utils::out(dtype::of_(undefined) == dtype::none); +test_utils::out(dtype::of_(undefined) == dtype::str); +test_utils::out(dtype::of_(undefined)); + +--- + +true +false + \ No newline at end of file