diff --git a/crates/steel-core/src/primitives.rs b/crates/steel-core/src/primitives.rs index e3d082600..eb04702ca 100644 --- a/crates/steel-core/src/primitives.rs +++ b/crates/steel-core/src/primitives.rs @@ -393,6 +393,13 @@ impl FromSteelVal for SteelString { } } +// impl<'a, T: AsRefSteelVal> PrimitiveAsRef<'a> for &'a T { +// #[inline(always)] +// fn primitive_as_ref(val: &'a SteelVal) -> crate::rvals::Result { +// AsRefSteelVal::as_ref(val, &mut ::Nursery::default()) +// } +// } + impl<'a> PrimitiveAsRef<'a> for &'a Gc> { #[inline(always)] fn primitive_as_ref(val: &'a SteelVal) -> crate::rvals::Result { diff --git a/crates/steel-core/src/primitives/hashmaps.rs b/crates/steel-core/src/primitives/hashmaps.rs index 23af28eb5..f92252b1e 100644 --- a/crates/steel-core/src/primitives/hashmaps.rs +++ b/crates/steel-core/src/primitives/hashmaps.rs @@ -14,9 +14,7 @@ declare_const_ref_functions!( HM_CONSTRUCT => hm_construct, HM_INSERT => steel_hash_insert, HM_GET => steel_hash_ref, - HM_CLEAR => clear, HM_EMPTY => hm_empty, - HM_UNION => hm_union, ); pub(crate) fn hashmap_module() -> BuiltInModule { @@ -34,9 +32,9 @@ pub(crate) fn hashmap_module() -> BuiltInModule { .register_native_fn_definition(KEYS_TO_VECTOR_DEFINITION) .register_native_fn_definition(VALUES_TO_LIST_DEFINITION) .register_native_fn_definition(VALUES_TO_VECTOR_DEFINITION) - .register_value("hash-clear", HM_CLEAR) + .register_native_fn_definition(CLEAR_DEFINITION) .register_value("hash-empty?", HM_EMPTY) - .register_value("hash-union", HM_UNION); + .register_native_fn_definition(HM_UNION_DEFINITION); module } @@ -256,30 +254,21 @@ pub fn values_to_list(hashmap: &Gc>) -> Result>) -> Result { VectorOperations::vec_construct_iter_normal(hashmap.keys().cloned()) } -#[steel_derive::function(name = "hm-values->vector")] +#[steel_derive::function(name = "hash-values->vector")] pub fn values_to_vector(hashmap: &Gc>) -> Result { VectorOperations::vec_construct_iter_normal(hashmap.values().cloned()) } -pub fn clear(args: &[SteelVal]) -> Result { - if args.len() != 1 { - stop!(ArityMismatch => "hm-clear takes 1 argument") - } - - let hashmap = &args[0]; - - if let SteelVal::HashMapV(hm) = hashmap { - let mut hm = hm.0.unwrap(); - hm.clear(); - Ok(SteelVal::HashMapV(Gc::new(hm).into())) - } else { - stop!(TypeMismatch => "hm-clear takes a hashmap") - } +#[steel_derive::function(name = "hash-clear")] +pub fn clear(hashmap: &Gc>) -> Result { + let mut hm = hashmap.unwrap(); + hm.clear(); + Ok(SteelVal::HashMapV(Gc::new(hm).into())) } pub fn hm_empty(args: &[SteelVal]) -> Result { @@ -296,25 +285,14 @@ pub fn hm_empty(args: &[SteelVal]) -> Result { } } -pub fn hm_union(args: &[SteelVal]) -> Result { - if args.len() != 2 { - stop!(ArityMismatch => "hash-union takes 2 arguments") - } - - let left = &args[0]; - let right = &args[1]; - - if let SteelVal::HashMapV(hml) = left { - if let SteelVal::HashMapV(hmr) = right { - let hml = hml.0.unwrap(); - let hmr = hmr.0.unwrap(); - Ok(SteelVal::HashMapV(Gc::new(hml.union(hmr)).into())) - } else { - stop!(TypeMismatch => "hash-union takes a hashmap, found {}", right) - } - } else { - stop!(TypeMismatch => "hash-union takes a hashmap, found: {}", left) - } +#[steel_derive::function(name = "hash-union")] +pub fn hm_union( + hml: &Gc>, + hmr: &Gc>, +) -> Result { + let hml = hml.unwrap(); + let hmr = hmr.unwrap(); + Ok(SteelVal::HashMapV(Gc::new(hml.union(hmr)).into())) } #[cfg(test)] diff --git a/crates/steel-core/src/rvals.rs b/crates/steel-core/src/rvals.rs index 85eb48489..9499aeab6 100644 --- a/crates/steel-core/src/rvals.rs +++ b/crates/steel-core/src/rvals.rs @@ -441,17 +441,17 @@ pub trait AsRefSteelValFromRef: Sized { fn as_ref_from_ref<'a>(val: &'a SteelVal) -> crate::rvals::Result<&'a Self>; } -impl AsRefSteelVal for List { - type Nursery = (); +// impl AsRefSteelVal for List { +// type Nursery = (); - fn as_ref<'b, 'a: 'b>(val: &'a SteelVal, _nursery: &mut ()) -> Result> { - if let SteelVal::ListV(l) = val { - Ok(SRef::Temporary(l)) - } else { - stop!(TypeMismatch => "Value cannot be referenced as a list") - } - } -} +// fn as_ref<'b, 'a: 'b>(val: &'a SteelVal, _nursery: &mut ()) -> Result> { +// if let SteelVal::ListV(l) = val { +// Ok(SRef::Temporary(l)) +// } else { +// stop!(TypeMismatch => "Value cannot be referenced as a list") +// } +// } +// } impl AsRefSteelVal for UserDefinedStruct { type Nursery = (); @@ -1112,6 +1112,21 @@ impl From>> for SteelHashSet { } } +pub enum TypeKind { + Any, + Bool, + Num, + Int, + Char, + Vector(Box), + Void, + String, + Function, + HashMap(Box, Box), + HashSet(Box), + List(Box), +} + /// A value as represented in the runtime. #[derive(Clone)] pub enum SteelVal { diff --git a/crates/steel-core/src/steel_vm/builtin.rs b/crates/steel-core/src/steel_vm/builtin.rs index c7bc3242b..3d6ae50a9 100644 --- a/crates/steel-core/src/steel_vm/builtin.rs +++ b/crates/steel-core/src/steel_vm/builtin.rs @@ -3,7 +3,7 @@ use std::{borrow::Cow, cell::RefCell, rc::Rc, sync::Arc}; use crate::{ containers::RegisterValue, parser::{ast::ExprKind, interner::InternedString, parser::SyntaxObject, tokens::TokenType}, - rvals::{Custom, FromSteelVal, FunctionSignature, IntoSteelVal, Result, SteelVal}, + rvals::{Custom, FromSteelVal, FunctionSignature, IntoSteelVal, Result, SteelVal, TypeKind}, values::functions::BoxedDynFunction, }; use im_rc::HashMap; @@ -125,6 +125,7 @@ pub struct NativeFunctionDefinition { pub arity: Arity, pub doc: Option>, pub is_const: bool, + pub signature: Option<(&'static [TypeKind], TypeKind)>, } impl BuiltInModuleRepr { @@ -138,37 +139,6 @@ impl BuiltInModuleRepr { } } - // pub fn register_native_fn( - // &mut self, - // name: &'static str, - // func: fn(&[SteelVal]) -> Result, - // arity: Arity, - // ) -> &mut Self { - // // Just automatically add it to the function pointer table to help out with searching - // self.add_to_fn_ptr_table(func, FunctionSignatureMetadata::new(name, arity, false)); - // self.register_value(name, SteelVal::FuncV(func)) - // } - - // pub fn register_native_fn_definition( - // &mut self, - // definition: NativeFunctionDefinition, - // ) -> &mut Self { - // self.add_to_fn_ptr_table( - // definition.func, - // FunctionSignatureMetadata::new(definition.name, definition.arity, definition.is_const), - // ); - // if let Some(doc) = definition.doc { - // self.register_doc(definition.name, doc); - // } - // self.register_value(definition.name, SteelVal::FuncV(definition.func)); - // self - // } - - // pub fn check_compatibility(self: &BuiltInModule) -> bool { - // // self.version == env!("CARGO_PKG_VERSION") - // true - // } - pub fn contains(&self, ident: &str) -> bool { self.values.contains_key(ident) } @@ -443,19 +413,6 @@ impl BuiltInModule { value: FunctionSignature, data: FunctionSignatureMetadata, ) -> &mut Self { - // // Store this in a globally accessible place for printing - // FUNCTION_TABLE.with(|table| { - // table - // .borrow_mut() - // .insert(value as *const FunctionSignature, data) - // }); - - // // Probably don't need to store it in both places? - // self.fn_ptr_table - // .insert(value as *const FunctionSignature, data); - - // self - self.module.borrow_mut().add_to_fn_ptr_table(value, data); self diff --git a/crates/steel-core/src/steel_vm/vm/threads.rs b/crates/steel-core/src/steel_vm/vm/threads.rs index 74d6b0074..b6cf91140 100644 --- a/crates/steel-core/src/steel_vm/vm/threads.rs +++ b/crates/steel-core/src/steel_vm/vm/threads.rs @@ -111,11 +111,24 @@ pub fn closure_into_serializable( } } +struct MovableThread { + constants: Vec, + global_env: Vec, + function_interner: MovableFunctionInterner, + runtime_options: RunTimeOptions, +} + +struct MovableFunctionInterner { + closure_interner: fxhash::FxHashMap, + pure_function_interner: fxhash::FxHashMap, + spans: fxhash::FxHashMap>, + instructions: fxhash::FxHashMap>, +} + /// This will naively deep clone the environment, by attempting to translate every value into a `SerializableSteelVal` /// While this does work, it does result in a fairly hefty deep clone of the environment. It does _not_ smartly attempt /// to keep track of what values this function could touch - rather it assumes every value is possible to be touched -/// by the child thread. In addition - closures which capture mutable variables are unable to be moved across threads. -/// Only pure functions and/or functions which capture immutable can be moved. +/// by the child thread. fn spawn_thread_result(ctx: &mut VmCore, args: &[SteelVal]) -> Result { use crate::rvals::SerializableSteelVal; @@ -127,19 +140,6 @@ fn spawn_thread_result(ctx: &mut VmCore, args: &[SteelVal]) -> Result // global env - This we can do (hopefully) lazily. Only clone the values that actually // get referenced. We can also just straight up reject any closures that cannot be moved // across threads - struct MovableThread { - constants: Vec, - global_env: Vec, - function_interner: MovableFunctionInterner, - runtime_options: RunTimeOptions, - } - - struct MovableFunctionInterner { - closure_interner: fxhash::FxHashMap, - pure_function_interner: fxhash::FxHashMap, - spans: fxhash::FxHashMap>, - instructions: fxhash::FxHashMap>, - } if args.len() != 1 { stop!(ArityMismatch => "spawn-thread! accepts one argument, found: {}", args.len()) diff --git a/crates/steel-core/src/tests/mod.rs b/crates/steel-core/src/tests/mod.rs index 21dc30a1f..cde068c7e 100644 --- a/crates/steel-core/src/tests/mod.rs +++ b/crates/steel-core/src/tests/mod.rs @@ -67,6 +67,7 @@ test_harness_success! { define_normal, dfs, dll, + docs, empty, ellipses, fib, diff --git a/crates/steel-core/src/tests/success/docs.scm b/crates/steel-core/src/tests/success/docs.scm new file mode 100644 index 000000000..886659e72 --- /dev/null +++ b/crates/steel-core/src/tests/success/docs.scm @@ -0,0 +1,6 @@ +;;@doc +;; This is a function that does something +(define (foo x) + (list 10 20 30 40 x)) + +(assert! (equal? "This is a function that does something\n" foo__doc__)) diff --git a/crates/steel-derive/src/lib.rs b/crates/steel-derive/src/lib.rs index d85739d78..802e8f3c3 100644 --- a/crates/steel-derive/src/lib.rs +++ b/crates/steel-derive/src/lib.rs @@ -206,6 +206,7 @@ pub fn native( arity: crate::steel_vm::builtin::Arity::#arity_number, doc: Some(crate::steel_vm::builtin::MarkdownDoc(#doc)), is_const: #is_const, + signature: None, }; } } else { @@ -216,6 +217,7 @@ pub fn native( arity: crate::steel_vm::builtin::Arity::#arity_number, doc: None, is_const: #is_const, + signature: None, }; } }; @@ -305,6 +307,9 @@ pub fn function( let mut rest_arg_generic_inner_type = false; + // let mut argument_signatures: Vec<&'static str> = Vec::new(); + // let mut return_type = "void"; + for (i, arg) in sign.inputs.iter().enumerate() { if let FnArg::Typed(pat_ty) = arg.clone() { if let Type::Path(p) = pat_ty.ty.as_ref() { @@ -328,15 +333,30 @@ pub fn function( } } + /* + TODO: Attempt to bake the type information into the native + function definition. This can give a lot more help to the optimizer + and also the LSP if we have the types for every built in definition + when we make it. + + // Attempt to calculate the function signature + match pat_ty.ty.clone().into_token_stream().to_string().as_str() { + "char" => argument_signatures.push("char"), + "bool" => argument_signatures.push("bool"), + "f64" => argument_signatures.push("f64"), + "isize" => argument_signatures.push("isize"), + "SteelString" => argument_signatures.push("string"), + "&SteelString" => argument_signatures.push("string"), + _ => argument_signatures.push("any"), + } + */ + type_vec.push(pat_ty.ty); } } - // panic!("{:?}", type_vec); - let arity_number = type_vec.len(); - // TODO: Might be able to handle types with lifetimes here, both coming in and leaving let conversion_functions = type_vec.clone().into_iter().map(|x| { if let Type::Reference(_) = *x { quote! { primitive_as_ref } @@ -378,6 +398,7 @@ pub fn function( arity: crate::steel_vm::builtin::Arity::#arity_exactness(#arity_number), doc: Some(crate::steel_vm::builtin::MarkdownDoc(#doc)), is_const: #is_const, + signature: None, }; } } else { @@ -388,6 +409,7 @@ pub fn function( arity: crate::steel_vm::builtin::Arity::#arity_exactness(#arity_number), doc: None, is_const: #is_const, + signature: None }; } }; diff --git a/crates/steel-language-server/Cargo.toml b/crates/steel-language-server/Cargo.toml index 565a03588..7c065e594 100644 --- a/crates/steel-language-server/Cargo.toml +++ b/crates/steel-language-server/Cargo.toml @@ -14,5 +14,5 @@ tower-lsp = { version = "0.20.0", features = ["proposed"] } serde = { version = "1.0.152", features = ["derive"] } dashmap = "5.1.0" log = "0.4.17" -steel-core = { path = "../steel-core", version = "0.5.0" } +steel-core = { path = "../steel-core", version = "0.5.0", features = ["modules", "sqlite"] } once_cell = "1.18.0" diff --git a/crates/steel-language-server/src/diagnostics.rs b/crates/steel-language-server/src/diagnostics.rs index 32c761de8..e801e6c54 100644 --- a/crates/steel-language-server/src/diagnostics.rs +++ b/crates/steel-language-server/src/diagnostics.rs @@ -306,7 +306,11 @@ impl<'a, 'b> VisitorMutUnitRef<'a> for StaticCallSiteArityChecker<'a, 'b> { { // If it is something that... doesn't happen in the file we're currently // looking at - we probably shouldn't report it there? - if !l.first_ident().unwrap().resolve().starts_with("mangler") { + if !l + .first_ident() + .map(|x| x.resolve().starts_with("mangler")) + .unwrap_or(false) + { if let Some(info) = self.context.analysis.get_identifier(function_call_ident) { if info.builtin { let found_arity = self diff --git a/steel-examples/sqlite.scm b/steel-examples/sqlite.scm index 00865fa1d..8c0be6f01 100644 --- a/steel-examples/sqlite.scm +++ b/steel-examples/sqlite.scm @@ -2,23 +2,22 @@ (require "steel/result") (require "steel/time/time.scm" - (for-syntax "steel/time/time.scm")) + (for-syntax "steel/time/time.scm")) -(define connection (unwrap-ok (connection/open-in-memory))) +(define connection (connection/open-in-memory)) -(connection/execute! connection - "CREATE TABLE person ( +(connection/execute! + connection + "CREATE TABLE person ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, data TEXT - )" '()) + )" + '()) -(connection/prepare-and-execute! connection - "INSERT INTO person (name, data) VALUES (?1, ?2)" - (list (list "Steven" "likes to eat") - (list "Alex" "likes biking") - (list "Matt" "likes running"))) +(connection/prepare-and-execute! + connection + "INSERT INTO person (name, data) VALUES (?1, ?2)" + (list (list "Steven" "likes to eat") (list "Alex" "likes biking") (list "Matt" "likes running"))) -(dbg! (time! (connection/prepare-and-query! connection - "SELECT id, name, data FROM person" - '()))) +(time! (connection/prepare-and-query! connection "SELECT id, name, data FROM person" '()))