Convert maps to structures for advanced type checks
This commit is contained in:
parent
1f5dacad7d
commit
85419c47be
@ -57,7 +57,11 @@ impl AbstractTree for As {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Type::Any => todo!(),
|
Type::Any => {
|
||||||
|
// Do no validation when converting from "any" to a list.
|
||||||
|
// This effectively defers to runtime behavior, potentially
|
||||||
|
// causing a runtime error.
|
||||||
|
}
|
||||||
Type::Boolean => todo!(),
|
Type::Boolean => todo!(),
|
||||||
Type::Collection => todo!(),
|
Type::Collection => todo!(),
|
||||||
Type::Custom(_) => todo!(),
|
Type::Custom(_) => todo!(),
|
||||||
@ -91,14 +95,12 @@ impl AbstractTree for As {
|
|||||||
|
|
||||||
Value::List(List::with_items(chars))
|
Value::List(List::with_items(chars))
|
||||||
}
|
}
|
||||||
Value::Map(_) => todo!(),
|
_ => {
|
||||||
Value::Function(_) => todo!(),
|
return Err(RuntimeError::ConversionImpossible {
|
||||||
Value::Float(_) => todo!(),
|
value,
|
||||||
Value::Integer(_) => todo!(),
|
target_type: self.r#type.clone(),
|
||||||
Value::Boolean(_) => todo!(),
|
});
|
||||||
Value::Range(_) => todo!(),
|
}
|
||||||
Value::Option(_) => todo!(),
|
|
||||||
Value::Structure(_) => todo!(),
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
todo!()
|
todo!()
|
||||||
|
@ -6,7 +6,8 @@ use serde::{Deserialize, Serialize};
|
|||||||
use crate::{
|
use crate::{
|
||||||
built_in_functions::{fs::fs_functions, json::json_functions, str::string_functions, Callable},
|
built_in_functions::{fs::fs_functions, json::json_functions, str::string_functions, Callable},
|
||||||
error::{RuntimeError, SyntaxError, ValidationError},
|
error::{RuntimeError, SyntaxError, ValidationError},
|
||||||
AbstractTree, BuiltInFunction, Context, Format, Function, List, Map, SyntaxNode, Type, Value,
|
AbstractTree, BuiltInFunction, Context, Format, Function, List, Map, Structure, SyntaxNode,
|
||||||
|
Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
static ARGS: OnceLock<Value> = OnceLock::new();
|
static ARGS: OnceLock<Value> = OnceLock::new();
|
||||||
@ -87,7 +88,9 @@ impl BuiltInValue {
|
|||||||
BuiltInValue::Args => Type::list(Type::String),
|
BuiltInValue::Args => Type::list(Type::String),
|
||||||
BuiltInValue::AssertEqual => BuiltInFunction::AssertEqual.r#type(),
|
BuiltInValue::AssertEqual => BuiltInFunction::AssertEqual.r#type(),
|
||||||
BuiltInValue::Fs => Type::Map(None),
|
BuiltInValue::Fs => Type::Map(None),
|
||||||
BuiltInValue::Json => Type::Map(None),
|
BuiltInValue::Json => Type::Map(Some(
|
||||||
|
Structure::from_map(self.get().as_map().unwrap()).unwrap(),
|
||||||
|
)),
|
||||||
BuiltInValue::Length => BuiltInFunction::Length.r#type(),
|
BuiltInValue::Length => BuiltInFunction::Length.r#type(),
|
||||||
BuiltInValue::Output => BuiltInFunction::Output.r#type(),
|
BuiltInValue::Output => BuiltInFunction::Output.r#type(),
|
||||||
BuiltInValue::Random => Type::Map(None),
|
BuiltInValue::Random => Type::Map(None),
|
||||||
|
@ -48,7 +48,17 @@ impl AbstractTree for Index {
|
|||||||
fn expected_type(&self, context: &Context) -> Result<Type, ValidationError> {
|
fn expected_type(&self, context: &Context) -> Result<Type, ValidationError> {
|
||||||
match self.collection.expected_type(context)? {
|
match self.collection.expected_type(context)? {
|
||||||
Type::List(item_type) => Ok(*item_type.clone()),
|
Type::List(item_type) => Ok(*item_type.clone()),
|
||||||
Type::Map(_) => Ok(Type::Any),
|
Type::Map(structure) => {
|
||||||
|
if let Some(structure) = structure {
|
||||||
|
if let IndexExpression::Identifier(identifier) = &self.index {
|
||||||
|
if let Some((_, r#type)) = structure.inner().get(identifier.inner()) {
|
||||||
|
return Ok(r#type.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Type::Any)
|
||||||
|
}
|
||||||
Type::None => Ok(Type::None),
|
Type::None => Ok(Type::None),
|
||||||
r#type => Ok(r#type),
|
r#type => Ok(r#type),
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,18 @@ use std::{
|
|||||||
time,
|
time,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::Value;
|
use crate::{Type, Value};
|
||||||
|
|
||||||
use super::rw_lock_error::RwLockError;
|
use super::rw_lock_error::RwLockError;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub enum RuntimeError {
|
pub enum RuntimeError {
|
||||||
|
/// The attempted conversion is impossible.
|
||||||
|
ConversionImpossible {
|
||||||
|
value: Value,
|
||||||
|
target_type: Type,
|
||||||
|
},
|
||||||
|
|
||||||
Csv(String),
|
Csv(String),
|
||||||
|
|
||||||
Io(String),
|
Io(String),
|
||||||
|
@ -11,7 +11,7 @@ use serde::{
|
|||||||
Deserialize, Deserializer, Serialize, Serializer,
|
Deserialize, Deserializer, Serialize, Serializer,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Type, Value};
|
use crate::{error::rw_lock_error::RwLockError, Map, Type, Value};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub struct Structure(Arc<BTreeMap<String, (Option<Value>, Type)>>);
|
pub struct Structure(Arc<BTreeMap<String, (Option<Value>, Type)>>);
|
||||||
@ -21,6 +21,16 @@ impl Structure {
|
|||||||
Structure(Arc::new(map))
|
Structure(Arc::new(map))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn from_map(map: &Map) -> Result<Self, RwLockError> {
|
||||||
|
let mut structure = BTreeMap::new();
|
||||||
|
|
||||||
|
for (key, value) in map.inner()?.iter() {
|
||||||
|
structure.insert(key.clone(), (Some(value.clone()), value.r#type()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Structure(Arc::new(structure)))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn inner(&self) -> &BTreeMap<String, (Option<Value>, Type)> {
|
pub fn inner(&self) -> &BTreeMap<String, (Option<Value>, Type)> {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
@ -42,7 +52,7 @@ impl Display for Structure {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for Structure {
|
impl Serialize for Structure {
|
||||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
{
|
{
|
||||||
@ -94,7 +104,7 @@ impl<'de> Visitor<'de> for StructureVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for Structure {
|
impl<'de> Deserialize<'de> for Structure {
|
||||||
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
|
20
tests/as.rs
20
tests/as.rs
@ -1,4 +1,7 @@
|
|||||||
use dust_lang::{error::ValidationError, *};
|
use dust_lang::{
|
||||||
|
error::{RuntimeError, ValidationError},
|
||||||
|
*,
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn string_as_list() {
|
fn string_as_list() {
|
||||||
@ -25,3 +28,18 @@ fn string_as_list_conversion_error() {
|
|||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const JSON: &str = "{ \"x\": 1 }";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn conversion_runtime_error() {
|
||||||
|
let json_value = interpret(&format!("json:parse('{JSON}')")).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
interpret(&format!("json:parse('{JSON}') as [map]")),
|
||||||
|
Err(Error::Runtime(RuntimeError::ConversionImpossible {
|
||||||
|
value: json_value,
|
||||||
|
target_type: Type::List(Box::new(Type::Float))
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user