Skip to content

Commit

Permalink
Merge pull request #357 from koto-lang/impl-try-from-kvalue-for-std-t…
Browse files Browse the repository at this point in the history
…ypes

Implement `TryFrom<KValue>` for some `std` types
  • Loading branch information
irh authored Sep 26, 2024
2 parents a4e2e3b + 770901b commit 8cc8300
Show file tree
Hide file tree
Showing 22 changed files with 319 additions and 203 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ The Koto project adheres to
- The macros in `koto_derive` have been updated to support generics.
- `KotoField` has been added to reduce boilerplate when using the derive
macros.
- `TryFrom<KValue>` has been implemented for some `core` and `std` types,
including `bool`, string, and number types.

### Changed

Expand All @@ -57,6 +59,8 @@ The Koto project adheres to
- `type_error` has been renamed to `unexpected_type`.
- `type_error_with_slice` has been replaced by `unexpected_args` and
`unexpected_args_after_instance`.
- `From` impls for `KNumber` now saturate integer values that are out of the
target type's bounds, instead of wrapping.

### Removed

Expand All @@ -66,6 +70,7 @@ The Koto project adheres to
IDs are now represented as AST nodes.
- `Node::NamedCall` has been removed, with all calls represented by expression
chains.
- `KNumber::as_i64` has been removed in favour of `i64::from`.

### Fixed

Expand Down
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ regex = "1.10.2"
rustc-hash = "1.1.0"
# Rustyline, a readline implementation
rustyline = "13.0.0"
# Library for saturating casts between integer primitives.
saturating_cast = "0.1.0"
# A generic serialization/deserialization framework
serde = "1.0.0"
# JSON support for serde
Expand Down
2 changes: 1 addition & 1 deletion crates/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ fn load_config(config_path: Option<&String>) -> Result<Config> {
None => {}
}
match repl_config.get("max_history") {
Some(KValue::Number(value)) => match value.as_i64() {
Some(KValue::Number(value)) => match i64::from(value) {
value if value > 0 => config.max_history = value as usize,
_ => bail!("expected positive number for max_history setting"),
},
Expand Down
1 change: 1 addition & 0 deletions crates/runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ koto_parser = { path = "../parser", version = "^0.15.0", default-features = fals
downcast-rs = { workspace = true }
indexmap = { workspace = true }
rustc-hash = { workspace = true }
saturating_cast = { workspace = true }
smallvec = { workspace = true }
thiserror = { workspace = true }
unicode-segmentation = { workspace = true }
Expand Down
15 changes: 10 additions & 5 deletions crates/runtime/src/core_lib/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,20 +117,25 @@ pub fn make_module() -> KMap {

result.add_fn("get", |ctx| {
let (list, index, default) = {
use KValue::{List, Null, Number};
let expected_error = "|List, Number|, or |List, Number, Any|";

match ctx.instance_and_args(is_list, expected_error)? {
(KValue::List(list), [KValue::Number(n)]) => (list, n, &KValue::Null),
(KValue::List(list), [KValue::Number(n), default]) => (list, n, default),
(List(list), [Number(n)]) => (list, n, Null),
(List(list), [Number(n), default]) => (list, n, default.clone()),
(instance, args) => {
return unexpected_args_after_instance(expected_error, instance, args)
}
}
};

match list.data().get::<usize>(index.into()) {
Some(value) => Ok(value.clone()),
None => Ok(default.clone()),
if *index >= 0 {
match list.data().get(usize::from(index)) {
Some(value) => Ok(value.clone()),
None => Ok(default),
}
} else {
Ok(default)
}
});

Expand Down
31 changes: 17 additions & 14 deletions crates/runtime/src/core_lib/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,23 +99,26 @@ pub fn make_module() -> KMap {
});

result.add_fn("get_index", |ctx| {
let (map, index, default) = {
let expected_error = "|Map, Number|";

match map_instance_and_args(ctx, expected_error)? {
(KValue::Map(map), [KValue::Number(n)]) => (map, n, &KValue::Null),
(KValue::Map(map), [KValue::Number(n), default]) => (map, n, default),
(instance, args) => {
return unexpected_args_after_instance(expected_error, instance, args)
}
use KValue::{Map, Null, Number};
let expected_error = "|Map, Number|";

let (map, index, default) = match map_instance_and_args(ctx, expected_error)? {
(Map(map), [Number(n)]) => (map, n, Null),
(Map(map), [Number(n), default]) => (map, n, default.clone()),
(instance, args) => {
return unexpected_args_after_instance(expected_error, instance, args)
}
};

match map.data().get_index(index.into()) {
Some((key, value)) => Ok(KValue::Tuple(
vec![key.value().clone(), value.clone()].into(),
)),
None => Ok(default.clone()),
if *index >= 0 {
match map.data().get_index(usize::from(index)) {
Some((key, value)) => Ok(KValue::Tuple(
vec![key.value().clone(), value.clone()].into(),
)),
None => Ok(default),
}
} else {
Ok(default)
}
});

Expand Down
2 changes: 1 addition & 1 deletion crates/runtime/src/core_lib/number.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ pub fn make_module() -> KMap {
let expected_error = "|Number|";

match ctx.instance_and_args(is_number, expected_error)? {
(Number(n), []) => Ok((!n.as_i64()).into()),
(Number(n), []) => Ok((!n.to_bits() as i64).into()),
(instance, args) => unexpected_args_after_instance(expected_error, instance, args),
}
});
Expand Down
2 changes: 1 addition & 1 deletion crates/runtime/src/core_lib/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ pub fn make_module() -> KMap {
for output in iterator.map(collect_pair) {
use KIteratorOutput as Output;
match output {
Output::Value(KValue::Number(n)) => match u8::try_from(n.as_i64()) {
Output::Value(KValue::Number(n)) => match u8::try_from(i64::from(n)) {
Ok(byte) => bytes.push(byte),
Err(_) => return runtime_error!("'{n}' is out of the valid byte range"),
},
Expand Down
27 changes: 15 additions & 12 deletions crates/runtime/src/core_lib/tuple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,21 +49,24 @@ pub fn make_module() -> KMap {
});

result.add_fn("get", |ctx| {
let (tuple, index, default) = {
let expected_error = "|Tuple, Number|, or |Tuple, Number, Any|";

match ctx.instance_and_args(is_tuple, expected_error)? {
(KValue::Tuple(tuple), [KValue::Number(n)]) => (tuple, n, &KValue::Null),
(KValue::Tuple(tuple), [KValue::Number(n), default]) => (tuple, n, default),
(instance, args) => {
return unexpected_args_after_instance(expected_error, instance, args)
}
use KValue::{Null, Number, Tuple};
let expected_error = "|Tuple, Number|, or |Tuple, Number, Any|";

let (tuple, index, default) = match ctx.instance_and_args(is_tuple, expected_error)? {
(Tuple(tuple), [Number(n)]) => (tuple, n, Null),
(Tuple(tuple), [Number(n), default]) => (tuple, n, default.clone()),
(instance, args) => {
return unexpected_args_after_instance(expected_error, instance, args)
}
};

match tuple.get::<usize>(index.into()) {
Some(value) => Ok(value.clone()),
None => Ok(default.clone()),
if *index >= 0 {
match tuple.get(usize::from(index)) {
Some(value) => Ok(value.clone()),
None => Ok(default),
}
} else {
Ok(default)
}
});

Expand Down
18 changes: 11 additions & 7 deletions crates/runtime/src/types/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ use koto_memory::Ptr;
/// A Koto function
///
/// See also:
/// * [KCaptureFunction]
/// * [KNativeFunction](crate::KNativeFunction)
/// * [KValue::Function](crate::KValue::Function)
/// * [`KCaptureFunction`]
/// * [`KNativeFunction`](crate::KNativeFunction)
/// * [`KValue::Function`](crate::KValue::Function)
#[derive(Clone, Debug, PartialEq)]
pub struct KFunction {
/// The [Chunk] in which the function can be found.
Expand All @@ -26,16 +26,20 @@ pub struct KFunction {
pub arg_is_unpacked_tuple: bool,
/// If the function is a generator, then calling the function will yield an iterator that
/// executes the function's body for each iteration step, pausing when a yield instruction is
/// encountered. See Vm::call_generator and Iterable::Generator.
/// encountered.
///
// See also:
// - `Vm::call_generator`
// - `Iterable::Generator`
pub generator: bool,
}

/// A Koto function with captured values
///
/// See also:
/// * [KFunction]
/// * [KNativeFunction](crate::KNativeFunction)
/// * [KValue::CaptureFunction](crate::KValue::CaptureFunction)
/// * [`KFunction`]
/// * [`KNativeFunction`](crate::KNativeFunction)
/// * [`KValue::CaptureFunction`](crate::KValue::CaptureFunction)
#[derive(Clone)]
pub struct KCaptureFunction {
/// The function's properties
Expand Down
4 changes: 2 additions & 2 deletions crates/runtime/src/types/list.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use crate::{prelude::*, Borrow, BorrowMut, PtrMut, Result};

/// The underlying Vec type used by [KList]
/// The underlying `Vec` type used by [KList]
pub type ValueVec = smallvec::SmallVec<[KValue; 4]>;

/// The Koto runtime's List type
/// The List type used by the Koto runtime
#[derive(Clone, Default)]
pub struct KList(PtrMut<ValueVec>);

Expand Down
8 changes: 4 additions & 4 deletions crates/runtime/src/types/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,22 @@ pub type KotoHasher = FxHasher;

type ValueMapType = IndexMap<ValueKey, KValue, BuildHasherDefault<KotoHasher>>;

/// The (ValueKey -> Value) 'data' hashmap used by the Koto runtime
/// The (ValueKey -> Value) 'data' hash map used by the Koto runtime
///
/// See also: [KMap]
#[derive(Clone, Default)]
pub struct ValueMap(ValueMapType);

impl ValueMap {
/// Creates a new DataMap with the given capacity
/// Creates a new map with the given capacity
pub fn with_capacity(capacity: usize) -> Self {
Self(ValueMapType::with_capacity_and_hasher(
capacity,
Default::default(),
))
}

/// Makes a new ValueMap containing a slice of the map's elements
/// Creates a new map containing a slice of the map's elements
pub fn make_data_slice(&self, range: impl RangeBounds<usize>) -> Option<Self> {
self.get_range(range).map(|entries| {
Self::from_iter(
Expand Down Expand Up @@ -58,7 +58,7 @@ impl FromIterator<(ValueKey, KValue)> for ValueMap {
}
}

/// The core hashmap value type used in Koto, containing a [ValueMap] and a [MetaMap]
/// The core hash map value type used in Koto, containing a [ValueMap] and a [MetaMap]
#[derive(Clone, Default)]
pub struct KMap {
data: PtrMut<ValueMap>,
Expand Down
8 changes: 4 additions & 4 deletions crates/runtime/src/types/meta_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type MetaMapType = IndexMap<MetaKey, KValue, BuildHasherDefault<KotoHasher>>;
/// The meta map used by [KMap](crate::KMap)
///
/// Each KMap contains a metamap, which allows for customized value behaviour by implementing
/// [MetaKeys](crate::MetaKey).
/// [`MetaKeys`](crate::MetaKey).
#[derive(Clone, Default)]
pub struct MetaMap(MetaMapType);

Expand Down Expand Up @@ -43,7 +43,7 @@ impl DerefMut for MetaMap {
}
}

/// The key type used by [MetaMaps](crate::MetaMap)
/// The key type used by [`MetaMaps`](crate::MetaMap)
#[derive(Clone, Eq, Hash, PartialEq)]
pub enum MetaKey {
/// A binary operation
Expand All @@ -62,7 +62,7 @@ pub enum MetaKey {
///
/// e.g. `@meta my_named_key`
///
/// Named entries are used in [KMaps][crate::KMap], so that shared named items can be
/// Named entries are used in [`KMaps`][crate::KMap], so that shared named items can be
/// made available without them being inserted into the map's contents.
Named(KString),
/// A test function
Expand Down Expand Up @@ -255,7 +255,7 @@ pub fn meta_id_to_key(id: MetaKeyId, name: Option<KString>) -> Result<MetaKey> {
Ok(result)
}

// Support efficient map accesses with &str
/// Support efficient map accesses with `&str`
impl Equivalent<MetaKey> for str {
fn equivalent(&self, other: &MetaKey) -> bool {
match &other {
Expand Down
2 changes: 1 addition & 1 deletion crates/runtime/src/types/native_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ impl<T> KotoFunction for T where

/// An function that's defined outside of the Koto runtime
///
/// See [KValue::NativeFunction]
/// See [`KValue::NativeFunction`]
pub struct KNativeFunction {
/// The function implementation that should be called when calling the external function
//
Expand Down
Loading

0 comments on commit 8cc8300

Please sign in to comment.