Convert maps to structures for advanced type checks

This commit is contained in:
Jeff 2024-02-13 12:04:02 -05:00
parent 1f5dacad7d
commit 85419c47be
6 changed files with 66 additions and 17 deletions

View File

@ -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::Collection => todo!(),
Type::Custom(_) => todo!(),
@ -91,14 +95,12 @@ impl AbstractTree for As {
Value::List(List::with_items(chars))
}
Value::Map(_) => todo!(),
Value::Function(_) => todo!(),
Value::Float(_) => todo!(),
Value::Integer(_) => todo!(),
Value::Boolean(_) => todo!(),
Value::Range(_) => todo!(),
Value::Option(_) => todo!(),
Value::Structure(_) => todo!(),
_ => {
return Err(RuntimeError::ConversionImpossible {
value,
target_type: self.r#type.clone(),
});
}
}
} else {
todo!()

View File

@ -6,7 +6,8 @@ use serde::{Deserialize, Serialize};
use crate::{
built_in_functions::{fs::fs_functions, json::json_functions, str::string_functions, Callable},
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();
@ -87,7 +88,9 @@ impl BuiltInValue {
BuiltInValue::Args => Type::list(Type::String),
BuiltInValue::AssertEqual => BuiltInFunction::AssertEqual.r#type(),
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::Output => BuiltInFunction::Output.r#type(),
BuiltInValue::Random => Type::Map(None),

View File

@ -48,7 +48,17 @@ impl AbstractTree for Index {
fn expected_type(&self, context: &Context) -> Result<Type, ValidationError> {
match self.collection.expected_type(context)? {
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),
r#type => Ok(r#type),
}

View File

@ -7,12 +7,18 @@ use std::{
time,
};
use crate::Value;
use crate::{Type, Value};
use super::rw_lock_error::RwLockError;
#[derive(Debug, PartialEq)]
pub enum RuntimeError {
/// The attempted conversion is impossible.
ConversionImpossible {
value: Value,
target_type: Type,
},
Csv(String),
Io(String),

View File

@ -11,7 +11,7 @@ use serde::{
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)]
pub struct Structure(Arc<BTreeMap<String, (Option<Value>, Type)>>);
@ -21,6 +21,16 @@ impl Structure {
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)> {
&self.0
}
@ -42,7 +52,7 @@ impl Display 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
S: Serializer,
{
@ -94,7 +104,7 @@ impl<'de> Visitor<'de> for StructureVisitor {
}
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
D: Deserializer<'de>,
{

View File

@ -1,4 +1,7 @@
use dust_lang::{error::ValidationError, *};
use dust_lang::{
error::{RuntimeError, ValidationError},
*,
};
#[test]
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))
}))
)
}