Implement structs; Modify tests
This commit is contained in:
parent
97319d28b2
commit
fc3dfc0e03
@ -6,8 +6,7 @@ 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, Structure, SyntaxNode,
|
||||
Type, Value,
|
||||
AbstractTree, BuiltInFunction, Context, Format, Function, List, Map, SyntaxNode, Type, Value,
|
||||
};
|
||||
|
||||
static ARGS: OnceLock<Value> = OnceLock::new();
|
||||
@ -87,14 +86,12 @@ impl BuiltInValue {
|
||||
match self {
|
||||
BuiltInValue::Args => Type::list(Type::String),
|
||||
BuiltInValue::AssertEqual => BuiltInFunction::AssertEqual.r#type(),
|
||||
BuiltInValue::Fs => Type::Map(None),
|
||||
BuiltInValue::Json => Type::Map(Some(
|
||||
Structure::from_map(self.get().as_map().unwrap()).unwrap(),
|
||||
)),
|
||||
BuiltInValue::Fs => Type::Map,
|
||||
BuiltInValue::Json => Type::Map,
|
||||
BuiltInValue::Length => BuiltInFunction::Length.r#type(),
|
||||
BuiltInValue::Output => BuiltInFunction::Output.r#type(),
|
||||
BuiltInValue::Random => Type::Map(None),
|
||||
BuiltInValue::Str => Type::Map(None),
|
||||
BuiltInValue::Random => Type::Map,
|
||||
BuiltInValue::Str => Type::Map,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,17 +48,7 @@ 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(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::Map => Ok(Type::Any),
|
||||
Type::None => Ok(Type::None),
|
||||
r#type => Ok(r#type),
|
||||
}
|
||||
@ -94,7 +84,7 @@ impl AbstractTree for Index {
|
||||
Ok(item)
|
||||
}
|
||||
Value::Map(map) => {
|
||||
let map = map.inner()?;
|
||||
let map = map.inner();
|
||||
|
||||
let (key, value) = if let IndexExpression::Identifier(identifier) = &self.index {
|
||||
let key = identifier.inner();
|
||||
|
91
src/abstract_tree/map_node.rs
Normal file
91
src/abstract_tree/map_node.rs
Normal file
@ -0,0 +1,91 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tree_sitter::Node as SyntaxNode;
|
||||
|
||||
use crate::{
|
||||
error::{RuntimeError, SyntaxError, ValidationError},
|
||||
AbstractTree, Context, Format, Identifier, Map, SourcePosition, Statement, Type,
|
||||
TypeSpecification, Value,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub struct MapNode {
|
||||
properties: BTreeMap<String, (Statement, Option<Type>)>,
|
||||
position: SourcePosition,
|
||||
}
|
||||
|
||||
impl MapNode {
|
||||
pub fn properties(&self) -> &BTreeMap<String, (Statement, Option<Type>)> {
|
||||
&self.properties
|
||||
}
|
||||
}
|
||||
|
||||
impl AbstractTree for MapNode {
|
||||
fn from_syntax(node: SyntaxNode, source: &str, context: &Context) -> Result<Self, SyntaxError> {
|
||||
SyntaxError::expect_syntax_node(source, "map", node)?;
|
||||
|
||||
let mut properties = BTreeMap::new();
|
||||
let mut current_key = "".to_string();
|
||||
let mut current_type = None;
|
||||
|
||||
for index in 0..node.child_count() - 1 {
|
||||
let child = node.child(index).unwrap();
|
||||
|
||||
if child.kind() == "identifier" {
|
||||
current_key = Identifier::from_syntax(child, source, context)?.take_inner();
|
||||
current_type = None;
|
||||
}
|
||||
|
||||
if child.kind() == "type_specification" {
|
||||
current_type =
|
||||
Some(TypeSpecification::from_syntax(child, source, context)?.take_inner());
|
||||
}
|
||||
|
||||
if child.kind() == "statement" {
|
||||
let statement = Statement::from_syntax(child, source, context)?;
|
||||
|
||||
properties.insert(current_key.clone(), (statement, current_type.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(MapNode {
|
||||
properties,
|
||||
position: SourcePosition::from(node.range()),
|
||||
})
|
||||
}
|
||||
|
||||
fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> {
|
||||
Ok(Type::Map)
|
||||
}
|
||||
|
||||
fn validate(&self, _source: &str, context: &Context) -> Result<(), ValidationError> {
|
||||
for (_key, (statement, r#type)) in &self.properties {
|
||||
if let Some(expected) = r#type {
|
||||
let actual = statement.expected_type(context)?;
|
||||
|
||||
if !expected.accepts(&actual) {
|
||||
return Err(ValidationError::TypeCheck {
|
||||
expected: expected.clone(),
|
||||
actual,
|
||||
position: self.position.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(&self, _source: &str, _context: &Context) -> Result<Value, RuntimeError> {
|
||||
let mut map = Map::new();
|
||||
|
||||
Ok(Value::Map(map))
|
||||
}
|
||||
}
|
||||
|
||||
impl Format for MapNode {
|
||||
fn format(&self, _output: &mut String, _indent_level: u8) {
|
||||
todo!()
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@ pub mod index_assignment;
|
||||
pub mod index_expression;
|
||||
pub mod logic;
|
||||
pub mod logic_operator;
|
||||
pub mod map_node;
|
||||
pub mod r#match;
|
||||
pub mod math;
|
||||
pub mod math_operator;
|
||||
@ -41,8 +42,8 @@ pub use {
|
||||
assignment::*, assignment_operator::*, block::*, built_in_value::*, command::*,
|
||||
enum_defintion::*, expression::*, function_call::*, function_expression::*, function_node::*,
|
||||
identifier::*, if_else::*, index::*, index_assignment::IndexAssignment, index_expression::*,
|
||||
logic::*, logic_operator::*, math::*, math_operator::*, new::*, r#as::*, r#for::*, r#match::*,
|
||||
r#type::*, r#while::*, statement::*, struct_definition::*, type_definition::*,
|
||||
logic::*, logic_operator::*, map_node::*, math::*, math_operator::*, new::*, r#as::*, r#for::*,
|
||||
r#match::*, r#type::*, r#while::*, statement::*, struct_definition::*, type_definition::*,
|
||||
type_specification::*, value_node::*,
|
||||
};
|
||||
|
||||
|
@ -1,34 +1,126 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tree_sitter::Node as SyntaxNode;
|
||||
|
||||
use crate::{
|
||||
error::{RuntimeError, SyntaxError, ValidationError},
|
||||
AbstractTree, Context, Format, Type, Value,
|
||||
AbstractTree, Context, Format, Identifier, Map, MapNode, Statement, StructInstance, Type,
|
||||
TypeDefinition, TypeSpecification, Value,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub struct StructDefinition;
|
||||
pub struct StructDefinition {
|
||||
name: Identifier,
|
||||
properties: BTreeMap<String, (Option<Statement>, Type)>,
|
||||
}
|
||||
|
||||
impl StructDefinition {
|
||||
pub fn instantiate(
|
||||
&self,
|
||||
new_properties: &MapNode,
|
||||
source: &str,
|
||||
context: &Context,
|
||||
) -> Result<StructInstance, RuntimeError> {
|
||||
let mut all_properties = Map::new();
|
||||
|
||||
for (key, (statement_option, _)) in &self.properties {
|
||||
if let Some(statement) = statement_option {
|
||||
let value = statement.run(source, context)?;
|
||||
|
||||
all_properties.set(key.clone(), value);
|
||||
}
|
||||
}
|
||||
|
||||
for (key, (statement, _)) in new_properties.properties() {
|
||||
let value = statement.run(source, context)?;
|
||||
|
||||
all_properties.set(key.clone(), value);
|
||||
}
|
||||
|
||||
Ok(StructInstance::new(
|
||||
self.name.inner().clone(),
|
||||
all_properties,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl AbstractTree for StructDefinition {
|
||||
fn from_syntax(node: SyntaxNode, source: &str, context: &Context) -> Result<Self, SyntaxError> {
|
||||
todo!()
|
||||
SyntaxError::expect_syntax_node(source, "struct_definition", node)?;
|
||||
|
||||
let name_node = node.child(1).unwrap();
|
||||
let name = Identifier::from_syntax(name_node, source, context)?;
|
||||
|
||||
let mut properties = BTreeMap::new();
|
||||
let mut current_identifier: Option<Identifier> = None;
|
||||
let mut current_type: Option<Type> = None;
|
||||
let mut current_statement = None;
|
||||
|
||||
for index in 2..node.child_count() - 1 {
|
||||
let child_syntax_node = node.child(index).unwrap();
|
||||
|
||||
if child_syntax_node.kind() == "identifier" {
|
||||
if current_statement.is_none() {
|
||||
if let (Some(identifier), Some(r#type)) = (¤t_identifier, ¤t_type) {
|
||||
properties.insert(identifier.inner().clone(), (None, r#type.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
current_type = None;
|
||||
current_identifier =
|
||||
Some(Identifier::from_syntax(child_syntax_node, source, context)?);
|
||||
}
|
||||
|
||||
if child_syntax_node.kind() == "type_specification" {
|
||||
current_type = Some(
|
||||
TypeSpecification::from_syntax(child_syntax_node, source, context)?
|
||||
.take_inner(),
|
||||
);
|
||||
}
|
||||
|
||||
if child_syntax_node.kind() == "statement" {
|
||||
current_statement =
|
||||
Some(Statement::from_syntax(child_syntax_node, source, context)?);
|
||||
|
||||
if let Some(identifier) = ¤t_identifier {
|
||||
let r#type = if let Some(r#type) = ¤t_type {
|
||||
r#type.clone()
|
||||
} else {
|
||||
Type::None
|
||||
};
|
||||
|
||||
properties.insert(
|
||||
identifier.inner().clone(),
|
||||
(current_statement.clone(), r#type.clone()),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(StructDefinition { name, properties })
|
||||
}
|
||||
|
||||
fn expected_type(&self, context: &Context) -> Result<Type, ValidationError> {
|
||||
todo!()
|
||||
fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> {
|
||||
Ok(Type::None)
|
||||
}
|
||||
|
||||
fn validate(&self, source: &str, context: &Context) -> Result<(), ValidationError> {
|
||||
todo!()
|
||||
fn validate(&self, _source: &str, _context: &Context) -> Result<(), ValidationError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(&self, source: &str, context: &Context) -> Result<Value, RuntimeError> {
|
||||
todo!()
|
||||
fn run(&self, _source: &str, context: &Context) -> Result<Value, RuntimeError> {
|
||||
context.set_definition(
|
||||
self.name.inner().clone(),
|
||||
TypeDefinition::Struct(self.clone()),
|
||||
)?;
|
||||
|
||||
Ok(Value::none())
|
||||
}
|
||||
}
|
||||
|
||||
impl Format for StructDefinition {
|
||||
fn format(&self, output: &mut String, indent_level: u8) {
|
||||
fn format(&self, _output: &mut String, _indent_level: u8) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
error::{RuntimeError, SyntaxError, ValidationError},
|
||||
AbstractTree, Context, Format, Identifier, Structure, SyntaxNode, Value,
|
||||
AbstractTree, Context, Format, Identifier, SyntaxNode, Value,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||
@ -20,7 +20,7 @@ pub enum Type {
|
||||
},
|
||||
Integer,
|
||||
List(Box<Type>),
|
||||
Map(Option<Structure>),
|
||||
Map,
|
||||
None,
|
||||
Number,
|
||||
String,
|
||||
@ -58,13 +58,13 @@ impl Type {
|
||||
| (Type::Collection, Type::Collection)
|
||||
| (Type::Collection, Type::List(_))
|
||||
| (Type::List(_), Type::Collection)
|
||||
| (Type::Collection, Type::Map(_))
|
||||
| (Type::Map(_), Type::Collection)
|
||||
| (Type::Collection, Type::Map)
|
||||
| (Type::Map, Type::Collection)
|
||||
| (Type::Collection, Type::String)
|
||||
| (Type::String, Type::Collection)
|
||||
| (Type::Float, Type::Float)
|
||||
| (Type::Integer, Type::Integer)
|
||||
| (Type::Map(_), Type::Map(_))
|
||||
| (Type::Map, Type::Map)
|
||||
| (Type::Number, Type::Number)
|
||||
| (Type::Number, Type::Integer)
|
||||
| (Type::Number, Type::Float)
|
||||
@ -121,7 +121,7 @@ impl Type {
|
||||
}
|
||||
|
||||
pub fn is_map(&self) -> bool {
|
||||
matches!(self, Type::Map(_))
|
||||
matches!(self, Type::Map)
|
||||
}
|
||||
}
|
||||
|
||||
@ -174,7 +174,7 @@ impl AbstractTree for Type {
|
||||
}
|
||||
}
|
||||
"int" => Type::Integer,
|
||||
"map" => Type::Map(None),
|
||||
"map" => Type::Map,
|
||||
"num" => Type::Number,
|
||||
"none" => Type::None,
|
||||
"str" => Type::String,
|
||||
@ -243,12 +243,8 @@ impl Format for Type {
|
||||
item_type.format(output, indent_level);
|
||||
output.push(']');
|
||||
}
|
||||
Type::Map(structure_option) => {
|
||||
if let Some(structure) = structure_option {
|
||||
output.push_str(&structure.to_string());
|
||||
} else {
|
||||
output.push_str("map");
|
||||
}
|
||||
Type::Map => {
|
||||
output.push_str("map");
|
||||
}
|
||||
Type::None => output.push_str("none"),
|
||||
Type::Number => output.push_str("num"),
|
||||
@ -290,7 +286,7 @@ impl Display for Type {
|
||||
}
|
||||
Type::Integer => write!(f, "int"),
|
||||
Type::List(item_type) => write!(f, "[{item_type}]"),
|
||||
Type::Map(_) => write!(f, "map"),
|
||||
Type::Map => write!(f, "map"),
|
||||
Type::Number => write!(f, "num"),
|
||||
Type::None => write!(f, "none"),
|
||||
Type::String => write!(f, "str"),
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::{cmp::Ordering, collections::BTreeMap, ops::RangeInclusive};
|
||||
use std::{cmp::Ordering, ops::RangeInclusive};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tree_sitter::Node as SyntaxNode;
|
||||
@ -6,8 +6,7 @@ use tree_sitter::Node as SyntaxNode;
|
||||
use crate::{
|
||||
error::{RuntimeError, SyntaxError, ValidationError},
|
||||
AbstractTree, BuiltInValue, Context, Expression, Format, Function, FunctionNode,
|
||||
Identifier, List, Map, SourcePosition, Statement, Structure, Type,
|
||||
TypeSpecification, Value, TypeDefinition,
|
||||
Identifier, List, Type, Value, TypeDefinition, MapNode,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
|
||||
@ -19,10 +18,13 @@ pub enum ValueNode {
|
||||
String(String),
|
||||
List(Vec<Expression>),
|
||||
Option(Option<Box<Expression>>),
|
||||
Map(BTreeMap<String, (Statement, Option<Type>)>, SourcePosition),
|
||||
Map(MapNode),
|
||||
BuiltInValue(BuiltInValue),
|
||||
Range(RangeInclusive<i64>),
|
||||
Structure(BTreeMap<String, (Option<Statement>, Type)>),
|
||||
Struct {
|
||||
name: Identifier,
|
||||
properties: MapNode,
|
||||
},
|
||||
Enum {
|
||||
name: Identifier,
|
||||
variant: Identifier,
|
||||
@ -65,32 +67,7 @@ impl AbstractTree for ValueNode {
|
||||
ValueNode::List(expressions)
|
||||
}
|
||||
"map" => {
|
||||
let mut child_nodes = BTreeMap::new();
|
||||
let mut current_key = "".to_string();
|
||||
let mut current_type = None;
|
||||
|
||||
for index in 0..child.child_count() - 1 {
|
||||
let child = child.child(index).unwrap();
|
||||
|
||||
if child.kind() == "identifier" {
|
||||
current_key = Identifier::from_syntax(child, source, context)?.take_inner();
|
||||
current_type = None;
|
||||
}
|
||||
|
||||
if child.kind() == "type_specification" {
|
||||
current_type = Some(
|
||||
TypeSpecification::from_syntax(child, source, context)?.take_inner(),
|
||||
);
|
||||
}
|
||||
|
||||
if child.kind() == "statement" {
|
||||
let statement = Statement::from_syntax(child, source, context)?;
|
||||
|
||||
child_nodes.insert(current_key.clone(), (statement, current_type.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
ValueNode::Map(child_nodes, SourcePosition::from(child.range()))
|
||||
ValueNode::Map(MapNode::from_syntax(child, source, context)?)
|
||||
}
|
||||
"option" => {
|
||||
let first_grandchild = child.child(0).unwrap();
|
||||
@ -113,60 +90,6 @@ impl AbstractTree for ValueNode {
|
||||
context,
|
||||
)?)
|
||||
}
|
||||
"structure" => {
|
||||
let mut btree_map = BTreeMap::new();
|
||||
let mut current_identifier: Option<Identifier> = None;
|
||||
let mut current_type: Option<Type> = None;
|
||||
let mut current_statement = None;
|
||||
|
||||
for index in 2..child.child_count() - 1 {
|
||||
let child_syntax_node = child.child(index).unwrap();
|
||||
|
||||
if child_syntax_node.kind() == "identifier" {
|
||||
if current_statement.is_none() {
|
||||
if let (Some(identifier), Some(r#type)) =
|
||||
(¤t_identifier, ¤t_type)
|
||||
{
|
||||
btree_map
|
||||
.insert(identifier.inner().clone(), (None, r#type.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
current_type = None;
|
||||
current_identifier =
|
||||
Some(Identifier::from_syntax(child_syntax_node, source, context)?);
|
||||
}
|
||||
|
||||
if child_syntax_node.kind() == "type_specification" {
|
||||
current_type = Some(
|
||||
TypeSpecification::from_syntax(child_syntax_node, source, context)?
|
||||
.take_inner(),
|
||||
);
|
||||
}
|
||||
|
||||
if child_syntax_node.kind() == "statement" {
|
||||
current_statement =
|
||||
Some(Statement::from_syntax(child_syntax_node, source, context)?);
|
||||
|
||||
// if let Some(identifier) = ¤t_identifier {
|
||||
// let r#type = if let Some(r#type) = ¤t_type {
|
||||
// r#type.clone()
|
||||
// } else if let Some(statement) = ¤t_statement {
|
||||
// statement.expected_type(context)?
|
||||
// } else {
|
||||
// Type::None
|
||||
// };
|
||||
|
||||
// btree_map.insert(
|
||||
// identifier.inner().clone(),
|
||||
// (current_statement.clone(), r#type.clone()),
|
||||
// );
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
ValueNode::Structure(btree_map)
|
||||
}
|
||||
"range" => {
|
||||
let mut split = source[child.byte_range()].split("..");
|
||||
let start = split.next().unwrap().parse().unwrap();
|
||||
@ -189,10 +112,23 @@ impl AbstractTree for ValueNode {
|
||||
|
||||
ValueNode::Enum { name, variant , expression }
|
||||
}
|
||||
"struct_instance" => {
|
||||
let name_node = child.child(1).unwrap();
|
||||
let name = Identifier::from_syntax(name_node, source, context)?;
|
||||
|
||||
let properties_node = child.child(2).unwrap();
|
||||
let properties = MapNode::from_syntax(properties_node, source, context)?;
|
||||
|
||||
ValueNode::Struct
|
||||
{
|
||||
name,
|
||||
properties
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(SyntaxError::UnexpectedSyntaxNode {
|
||||
expected:
|
||||
"string, integer, float, boolean, range, list, map, option, function, structure or enum"
|
||||
"string, integer, float, boolean, range, list, map, option, function, struct or enum"
|
||||
.to_string(),
|
||||
actual: child.kind().to_string(),
|
||||
location: child.start_position(),
|
||||
@ -239,16 +175,10 @@ impl AbstractTree for ValueNode {
|
||||
Type::None
|
||||
}
|
||||
}
|
||||
ValueNode::Map(_, _) => Type::Map(None),
|
||||
ValueNode::Map(_) => Type::Map,
|
||||
ValueNode::BuiltInValue(built_in_value) => built_in_value.expected_type(context)?,
|
||||
ValueNode::Structure(node_map) => {
|
||||
let mut value_map = BTreeMap::new();
|
||||
|
||||
for (key, (_statement_option, r#type)) in node_map {
|
||||
value_map.insert(key.to_string(), (None, r#type.clone()));
|
||||
}
|
||||
|
||||
Type::Map(Some(Structure::new(value_map)))
|
||||
ValueNode::Struct { name, .. } => {
|
||||
Type::Custom(name.clone())
|
||||
}
|
||||
ValueNode::Range(_) => Type::Range,
|
||||
ValueNode::Enum { name, .. } => Type::Custom(name.clone()),
|
||||
@ -264,21 +194,7 @@ impl AbstractTree for ValueNode {
|
||||
function_node.validate(_source, context)?;
|
||||
}
|
||||
}
|
||||
ValueNode::Map(statements, source_position) => {
|
||||
for (_key, (statement, r#type)) in statements {
|
||||
if let Some(expected) = r#type {
|
||||
let actual = statement.expected_type(context)?;
|
||||
|
||||
if !expected.accepts(&actual) {
|
||||
return Err(ValidationError::TypeCheck {
|
||||
expected: expected.clone(),
|
||||
actual,
|
||||
position: source_position.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ValueNode::Map(map_node) => map_node.validate(_source, context)?,
|
||||
_ => {},
|
||||
}
|
||||
|
||||
@ -316,36 +232,25 @@ impl AbstractTree for ValueNode {
|
||||
|
||||
Value::Option(option_value)
|
||||
}
|
||||
ValueNode::Map(key_statement_pairs, _) => {
|
||||
let mut map = BTreeMap::new();
|
||||
|
||||
{
|
||||
for (key, (statement, _)) in key_statement_pairs {
|
||||
let value = statement.run(source, context)?;
|
||||
|
||||
map.insert(key.clone(), value);
|
||||
}
|
||||
}
|
||||
|
||||
Value::Map(Map::with_values(map))
|
||||
}
|
||||
ValueNode::Map(map_node) => map_node.run(source, context)?,
|
||||
ValueNode::BuiltInValue(built_in_value) => built_in_value.run(source, context)?,
|
||||
ValueNode::Structure(node_map) => {
|
||||
let mut value_map = BTreeMap::new();
|
||||
|
||||
for (key, (statement_option, r#type)) in node_map {
|
||||
let value_option = if let Some(statement) = statement_option {
|
||||
Some(statement.run(source, context)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
value_map.insert(key.to_string(), (value_option, r#type.clone()));
|
||||
}
|
||||
|
||||
Value::Structure(Structure::new(value_map))
|
||||
}
|
||||
ValueNode::Range(range) => Value::Range(range.clone()),
|
||||
ValueNode::Struct { name, properties } => {
|
||||
let instance = if let Some(definition) = context.get_definition(name.inner())? {
|
||||
if let TypeDefinition::Struct(struct_definition) = definition {
|
||||
struct_definition.instantiate(properties, source, context)?
|
||||
} else {
|
||||
return Err(RuntimeError::ValidationFailure(ValidationError::ExpectedStructDefintion { actual: definition.clone() }))
|
||||
}
|
||||
} else {
|
||||
return Err(RuntimeError::ValidationFailure(
|
||||
ValidationError::TypeDefinitionNotFound(name.inner().clone())
|
||||
));
|
||||
};
|
||||
|
||||
Value::Struct(instance)
|
||||
|
||||
}
|
||||
ValueNode::Enum { name, variant, expression } => {
|
||||
let value = if let Some(expression) = expression {
|
||||
expression.run(source, context)?
|
||||
@ -406,52 +311,11 @@ impl Format for ValueNode {
|
||||
output.push_str("none");
|
||||
}
|
||||
}
|
||||
ValueNode::Map(nodes, _) => {
|
||||
output.push_str("{\n");
|
||||
|
||||
for (key, (statement, type_option)) in nodes {
|
||||
if let Some(r#type) = type_option {
|
||||
ValueNode::indent(output, indent_level + 1);
|
||||
output.push_str(key);
|
||||
output.push_str(" <");
|
||||
r#type.format(output, 0);
|
||||
output.push_str("> = ");
|
||||
statement.format(output, 0);
|
||||
} else {
|
||||
ValueNode::indent(output, indent_level + 1);
|
||||
output.push_str(key);
|
||||
output.push_str(" = ");
|
||||
statement.format(output, 0);
|
||||
}
|
||||
|
||||
output.push('\n');
|
||||
}
|
||||
|
||||
ValueNode::indent(output, indent_level);
|
||||
output.push('}');
|
||||
}
|
||||
ValueNode::Map(map_node) => map_node.format(output, indent_level),
|
||||
ValueNode::BuiltInValue(built_in_value) => built_in_value.format(output, indent_level),
|
||||
ValueNode::Structure(nodes) => {
|
||||
output.push('{');
|
||||
|
||||
for (key, (value_option, r#type)) in nodes {
|
||||
if let Some(value) = value_option {
|
||||
output.push_str(" ");
|
||||
output.push_str(key);
|
||||
output.push_str(" <");
|
||||
r#type.format(output, indent_level);
|
||||
output.push_str("> = ");
|
||||
value.format(output, indent_level);
|
||||
} else {
|
||||
output.push_str(" ");
|
||||
output.push_str(key);
|
||||
output.push_str(" <");
|
||||
r#type.format(output, indent_level);
|
||||
output.push('>');
|
||||
}
|
||||
}
|
||||
|
||||
output.push('}');
|
||||
ValueNode::Struct { name, properties } => {
|
||||
name.format(output, indent_level);
|
||||
properties.format(output, indent_level);
|
||||
}
|
||||
ValueNode::Range(_) => todo!(),
|
||||
ValueNode::Enum { .. } => todo!(),
|
||||
@ -476,12 +340,20 @@ impl Ord for ValueNode {
|
||||
(ValueNode::List(_), _) => Ordering::Greater,
|
||||
(ValueNode::Option(left), ValueNode::Option(right)) => left.cmp(right),
|
||||
(ValueNode::Option(_), _) => Ordering::Greater,
|
||||
(ValueNode::Map(left, _), ValueNode::Map(right, _)) => left.cmp(right),
|
||||
(ValueNode::Map(_, _), _) => Ordering::Greater,
|
||||
(ValueNode::Map(left), ValueNode::Map(right)) => left.cmp(right),
|
||||
(ValueNode::Map(_), _) => Ordering::Greater,
|
||||
(ValueNode::BuiltInValue(left), ValueNode::BuiltInValue(right)) => left.cmp(right),
|
||||
(ValueNode::BuiltInValue(_), _) => Ordering::Greater,
|
||||
(ValueNode::Structure(left), ValueNode::Structure(right)) => left.cmp(right),
|
||||
(ValueNode::Structure(_), _) => Ordering::Greater,
|
||||
(ValueNode::Struct{ name: left_name, properties: left_properties }, ValueNode::Struct {name: right_name, properties: right_properties} ) => {
|
||||
let name_cmp = left_name.cmp(right_name);
|
||||
|
||||
if name_cmp.is_eq() {
|
||||
left_properties.cmp(right_properties)
|
||||
} else {
|
||||
name_cmp
|
||||
}
|
||||
},
|
||||
(ValueNode::Struct {..}, _) => Ordering::Greater,
|
||||
(
|
||||
ValueNode::Enum {
|
||||
name: left_name, variant: left_variant, expression: left_expression
|
||||
|
@ -111,7 +111,7 @@ impl Callable for BuiltInFunction {
|
||||
let length = if let Ok(list) = value.as_list() {
|
||||
list.items().len()
|
||||
} else if let Ok(map) = value.as_map() {
|
||||
map.inner()?.len()
|
||||
map.inner().len()
|
||||
} else if let Ok(str) = value.as_string() {
|
||||
str.chars().count()
|
||||
} else {
|
||||
|
@ -67,8 +67,11 @@ pub enum ValidationError {
|
||||
/// Failed to find a type definition with this key.
|
||||
TypeDefinitionNotFound(String),
|
||||
|
||||
/// Failed to find a type definition with this key.
|
||||
/// Failed to find an enum definition with this key.
|
||||
ExpectedEnumDefintion { actual: TypeDefinition },
|
||||
|
||||
/// Failed to find a struct definition with this key.
|
||||
ExpectedStructDefintion { actual: TypeDefinition },
|
||||
}
|
||||
|
||||
impl ValidationError {
|
||||
|
@ -292,7 +292,7 @@ impl Completer for DustCompleter {
|
||||
}
|
||||
|
||||
if let Value::Map(map) = built_in_value.get() {
|
||||
for (key, value) in map.inner().unwrap().iter() {
|
||||
for (key, value) in map.inner() {
|
||||
if key.contains(last_word) {
|
||||
suggestions.push(Suggestion {
|
||||
value: format!("{name}:{key}"),
|
||||
|
@ -1,3 +1,4 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use stanza::{
|
||||
renderer::{console::Console, Renderer},
|
||||
style::{HAlign, Styles},
|
||||
@ -6,52 +7,47 @@ use stanza::{
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
fmt::{self, Display, Formatter},
|
||||
sync::{Arc, RwLock, RwLockReadGuard},
|
||||
};
|
||||
|
||||
use crate::{error::rw_lock_error::RwLockError, value::Value};
|
||||
use crate::value::Value;
|
||||
|
||||
/// A collection dust variables comprised of key-value pairs.
|
||||
///
|
||||
/// The inner value is a BTreeMap in order to allow VariableMap instances to be sorted and compared
|
||||
/// to one another.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub struct Map {
|
||||
inner: Arc<RwLock<BTreeMap<String, Value>>>,
|
||||
inner: BTreeMap<String, Value>,
|
||||
}
|
||||
|
||||
impl Map {
|
||||
/// Creates a new instace.
|
||||
pub fn new() -> Self {
|
||||
Map {
|
||||
inner: Arc::new(RwLock::new(BTreeMap::new())),
|
||||
inner: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_values(variables: BTreeMap<String, Value>) -> Self {
|
||||
Map {
|
||||
inner: Arc::new(RwLock::new(variables)),
|
||||
}
|
||||
Map { inner: variables }
|
||||
}
|
||||
|
||||
pub fn inner(&self) -> Result<RwLockReadGuard<BTreeMap<String, Value>>, RwLockError> {
|
||||
Ok(self.inner.read()?)
|
||||
pub fn inner(&self) -> &BTreeMap<String, Value> {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
pub fn get(&self, key: &str) -> Result<Option<Value>, RwLockError> {
|
||||
Ok(self.inner()?.get(key).cloned())
|
||||
pub fn get(&self, key: &str) -> Option<&Value> {
|
||||
self.inner.get(key)
|
||||
}
|
||||
|
||||
pub fn set(&self, key: String, value: Value) -> Result<(), RwLockError> {
|
||||
self.inner.write()?.insert(key, value);
|
||||
|
||||
Ok(())
|
||||
pub fn set(&mut self, key: String, value: Value) {
|
||||
self.inner.insert(key, value);
|
||||
}
|
||||
|
||||
pub fn as_text_table(&self) -> Table {
|
||||
let mut table = Table::with_styles(Styles::default().with(HAlign::Centred));
|
||||
|
||||
for (key, value) in self.inner().unwrap().iter() {
|
||||
for (key, value) in &self.inner {
|
||||
if let Value::Map(map) = value {
|
||||
table.push_row(Row::new(
|
||||
Styles::default(),
|
||||
@ -92,34 +88,3 @@ impl Display for Map {
|
||||
f.write_str(&renderer.render(&self.as_text_table()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Map {}
|
||||
|
||||
impl PartialEq for Map {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
let left = self.inner().unwrap();
|
||||
let right = other.inner().unwrap();
|
||||
|
||||
if left.len() != right.len() {
|
||||
return false;
|
||||
}
|
||||
|
||||
left.iter()
|
||||
.zip(right.iter())
|
||||
.all(|((left_key, left_value), (right_key, right_value))| {
|
||||
left_key == right_key && left_value == right_value
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Map {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Map {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.inner().unwrap().cmp(&other.inner().unwrap())
|
||||
}
|
||||
}
|
||||
|
@ -17,14 +17,15 @@ use std::{
|
||||
};
|
||||
|
||||
pub use self::{
|
||||
function::Function, list::List, map::Map, r#enum::EnumInstance, structure::Structure,
|
||||
enum_instance::EnumInstance, function::Function, list::List, map::Map,
|
||||
struct_instance::StructInstance,
|
||||
};
|
||||
|
||||
pub mod r#enum;
|
||||
pub mod enum_instance;
|
||||
pub mod function;
|
||||
pub mod list;
|
||||
pub mod map;
|
||||
pub mod structure;
|
||||
pub mod struct_instance;
|
||||
|
||||
/// Dust value representation.
|
||||
///
|
||||
@ -42,7 +43,7 @@ pub enum Value {
|
||||
Boolean(bool),
|
||||
Range(RangeInclusive<i64>),
|
||||
Option(Option<Box<Value>>),
|
||||
Structure(Structure),
|
||||
Struct(StructInstance),
|
||||
Enum(EnumInstance),
|
||||
}
|
||||
|
||||
@ -87,14 +88,14 @@ impl Value {
|
||||
Value::Map(map) => {
|
||||
let mut identifier_types = Vec::new();
|
||||
|
||||
for (key, value) in map.inner().unwrap().iter() {
|
||||
for (key, value) in map.inner() {
|
||||
identifier_types.push((
|
||||
Identifier::new(key.clone()),
|
||||
TypeSpecification::new(value.r#type()),
|
||||
));
|
||||
}
|
||||
|
||||
Type::Map(None)
|
||||
Type::Map
|
||||
}
|
||||
Value::Function(function) => function.r#type().clone(),
|
||||
Value::String(_) => Type::String,
|
||||
@ -109,7 +110,7 @@ impl Value {
|
||||
}
|
||||
}
|
||||
Value::Range(_) => todo!(),
|
||||
Value::Structure(_) => todo!(),
|
||||
Value::Struct(_) => todo!(),
|
||||
Value::Enum(_) => todo!(),
|
||||
}
|
||||
}
|
||||
@ -446,7 +447,7 @@ impl PartialEq for Value {
|
||||
(Value::Function(left), Value::Function(right)) => left == right,
|
||||
(Value::Option(left), Value::Option(right)) => left == right,
|
||||
(Value::Range(left), Value::Range(right)) => left == right,
|
||||
(Value::Structure(left), Value::Structure(right)) => left == right,
|
||||
(Value::Struct(left), Value::Struct(right)) => left == right,
|
||||
(Value::Enum(left), Value::Enum(right)) => left == right,
|
||||
_ => false,
|
||||
}
|
||||
@ -484,8 +485,8 @@ impl Ord for Value {
|
||||
(Value::Map(_), _) => Ordering::Greater,
|
||||
(Value::Function(left), Value::Function(right)) => left.cmp(right),
|
||||
(Value::Function(_), _) => Ordering::Greater,
|
||||
(Value::Structure(left), Value::Structure(right)) => left.cmp(right),
|
||||
(Value::Structure(_), _) => Ordering::Greater,
|
||||
(Value::Struct(left), Value::Struct(right)) => left.cmp(right),
|
||||
(Value::Struct(_), _) => Ordering::Greater,
|
||||
(Value::Enum(left), Value::Enum(right)) => left.cmp(right),
|
||||
(Value::Enum(_), _) => Ordering::Greater,
|
||||
(Value::Range(left), Value::Range(right)) => {
|
||||
@ -523,7 +524,7 @@ impl Serialize for Value {
|
||||
}
|
||||
Value::Option(inner) => inner.serialize(serializer),
|
||||
Value::Map(map) => {
|
||||
let entries = map.inner().unwrap();
|
||||
let entries = map.inner();
|
||||
let mut map = serializer.serialize_map(Some(entries.len()))?;
|
||||
|
||||
for (key, value) in entries.iter() {
|
||||
@ -533,7 +534,7 @@ impl Serialize for Value {
|
||||
map.end()
|
||||
}
|
||||
Value::Function(inner) => inner.serialize(serializer),
|
||||
Value::Structure(inner) => inner.serialize(serializer),
|
||||
Value::Struct(inner) => inner.serialize(serializer),
|
||||
Value::Range(range) => range.serialize(serializer),
|
||||
Value::Enum(_) => todo!(),
|
||||
}
|
||||
@ -557,7 +558,7 @@ impl Display for Value {
|
||||
Value::List(list) => write!(f, "{list}"),
|
||||
Value::Map(map) => write!(f, "{map}"),
|
||||
Value::Function(function) => write!(f, "{function}"),
|
||||
Value::Structure(structure) => write!(f, "{structure}"),
|
||||
Value::Struct(structure) => write!(f, "{structure}"),
|
||||
Value::Range(range) => write!(f, "{}..{}", range.start(), range.end()),
|
||||
Value::Enum(_) => todo!(),
|
||||
}
|
||||
|
45
src/value/struct_instance.rs
Normal file
45
src/value/struct_instance.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
|
||||
use serde::{ser::SerializeMap, Serialize, Serializer};
|
||||
|
||||
use crate::Map;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub struct StructInstance {
|
||||
name: String,
|
||||
map: Map,
|
||||
}
|
||||
|
||||
impl StructInstance {
|
||||
pub fn new(name: String, map: Map) -> Self {
|
||||
StructInstance { name, map }
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for StructInstance {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
writeln!(f, "{{")?;
|
||||
|
||||
for (key, value) in self.map.inner() {
|
||||
writeln!(f, " {key} <{}> = {value}", value.r#type())?;
|
||||
}
|
||||
|
||||
write!(f, "}}")
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for StructInstance {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let map = self.map.inner();
|
||||
let mut serde_map = serializer.serialize_map(Some(map.len()))?;
|
||||
|
||||
for (key, value) in map.iter() {
|
||||
serde_map.serialize_entry(key, value)?;
|
||||
}
|
||||
|
||||
serde_map.end()
|
||||
}
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
fmt::{self, Display, Formatter},
|
||||
marker::PhantomData,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use serde::{
|
||||
de::{MapAccess, Visitor},
|
||||
ser::SerializeMap,
|
||||
Deserialize, Deserializer, Serialize, Serializer,
|
||||
};
|
||||
|
||||
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)>>);
|
||||
|
||||
impl Structure {
|
||||
pub fn new(map: BTreeMap<String, (Option<Value>, Type)>) -> Self {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Structure {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
writeln!(f, "{{")?;
|
||||
|
||||
for (key, (value_option, r#type)) in self.0.as_ref() {
|
||||
if let Some(value) = value_option {
|
||||
writeln!(f, " {key} <{}> = {value}", r#type)?;
|
||||
} else {
|
||||
writeln!(f, " {key} <{}>", r#type)?;
|
||||
}
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Structure {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut map = serializer.serialize_map(Some(self.0.len()))?;
|
||||
|
||||
for (key, (value, _type)) in self.0.iter() {
|
||||
map.serialize_entry(key, value)?;
|
||||
}
|
||||
|
||||
map.end()
|
||||
}
|
||||
}
|
||||
|
||||
struct StructureVisitor {
|
||||
marker: PhantomData<fn() -> Structure>,
|
||||
}
|
||||
|
||||
impl StructureVisitor {
|
||||
fn new() -> Self {
|
||||
StructureVisitor {
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Visitor<'de> for StructureVisitor {
|
||||
type Value = Structure;
|
||||
|
||||
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
|
||||
formatter.write_str("key-value pairs")
|
||||
}
|
||||
|
||||
fn visit_map<M>(self, mut access: M) -> std::result::Result<Structure, M::Error>
|
||||
where
|
||||
M: MapAccess<'de>,
|
||||
{
|
||||
let mut b_tree = BTreeMap::new();
|
||||
|
||||
{
|
||||
while let Some((key, value)) = access.next_entry::<String, Value>()? {
|
||||
let r#type = value.r#type();
|
||||
|
||||
b_tree.insert(key, (Some(value), r#type));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Structure::new(b_tree))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Structure {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_any(StructureVisitor::new())
|
||||
}
|
||||
}
|
@ -39,7 +39,7 @@ fn conversion_runtime_error() {
|
||||
interpret(&format!("json:parse('{JSON}') as [map]")),
|
||||
Err(Error::Runtime(RuntimeError::ConversionImpossible {
|
||||
value: json_value,
|
||||
target_type: Type::List(Box::new(Type::Map(None)))
|
||||
target_type: Type::List(Box::new(Type::Map))
|
||||
}))
|
||||
)
|
||||
}
|
||||
|
@ -65,10 +65,10 @@ fn modify_map() {
|
||||
",
|
||||
);
|
||||
|
||||
let map = Map::new();
|
||||
let mut map = Map::new();
|
||||
|
||||
map.set("x".to_string(), Value::Integer(1)).unwrap();
|
||||
map.set("y".to_string(), Value::Integer(2)).unwrap();
|
||||
map.set("x".to_string(), Value::Integer(1));
|
||||
map.set("y".to_string(), Value::Integer(2));
|
||||
|
||||
assert_eq!(Ok(Value::Map(map)), result);
|
||||
}
|
||||
|
@ -89,9 +89,9 @@ fn function_context_captures_functions() {
|
||||
|
||||
#[test]
|
||||
fn function_context_captures_structure_definitions() {
|
||||
let map = Map::new();
|
||||
let mut map = Map::new();
|
||||
|
||||
map.set("name".to_string(), Value::string("bob")).unwrap();
|
||||
map.set("name".to_string(), Value::string("bob"));
|
||||
|
||||
assert_eq!(
|
||||
interpret(
|
||||
|
54
tests/structs.rs
Normal file
54
tests/structs.rs
Normal file
@ -0,0 +1,54 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use dust_lang::*;
|
||||
|
||||
#[test]
|
||||
fn simple_struct() {
|
||||
let result = interpret(
|
||||
"
|
||||
struct Foo {
|
||||
bar <int> = 0
|
||||
baz <str>
|
||||
}
|
||||
|
||||
new Foo {
|
||||
baz = 'hiya'
|
||||
}
|
||||
",
|
||||
);
|
||||
|
||||
let mut map = Map::new();
|
||||
|
||||
map.set("bar".to_string(), Value::Integer(0));
|
||||
map.set("baz".to_string(), Value::String("hiya".to_string()));
|
||||
|
||||
let expected = Ok(Value::Struct(StructInstance::new("Foo".to_string(), map)));
|
||||
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_struct() {
|
||||
let _result = interpret(
|
||||
"
|
||||
struct Foo {
|
||||
bar <Bar>
|
||||
}
|
||||
struct Bar {}
|
||||
|
||||
new Foo {
|
||||
bar = new Bar {}
|
||||
}
|
||||
",
|
||||
);
|
||||
|
||||
let mut map = BTreeMap::new();
|
||||
|
||||
map.insert("x".to_string(), (Some(Value::Integer(0)), Type::Integer));
|
||||
|
||||
// let expected = Value::Map(Map::from_structure(Structure::new(map)));
|
||||
|
||||
// assert_eq!(Ok(expected), result);
|
||||
|
||||
todo!()
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use dust_lang::*;
|
||||
|
||||
#[test]
|
||||
fn simple_structure() {
|
||||
let result = interpret("struct { x <int> = 0 }");
|
||||
|
||||
let mut btree_map = BTreeMap::new();
|
||||
|
||||
btree_map.insert("x".to_string(), (Some(Value::Integer(0)), Type::Integer));
|
||||
|
||||
let expected = Ok(Value::Structure(Structure::new(btree_map)));
|
||||
|
||||
assert_eq!(expected, result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_structure() {
|
||||
let _result = interpret(
|
||||
"
|
||||
Coords = struct {
|
||||
x <float> = 0.0
|
||||
x <float> = 0.0
|
||||
}
|
||||
|
||||
new Coords {
|
||||
x = 1.5
|
||||
y = 4.2
|
||||
}
|
||||
",
|
||||
);
|
||||
|
||||
let mut map = BTreeMap::new();
|
||||
|
||||
map.insert("x".to_string(), (Some(Value::Integer(0)), Type::Integer));
|
||||
|
||||
// let expected = Value::Map(Map::from_structure(Structure::new(map)));
|
||||
|
||||
// assert_eq!(Ok(expected), result);
|
||||
|
||||
todo!()
|
||||
}
|
@ -69,11 +69,10 @@ fn empty_list() {
|
||||
|
||||
#[test]
|
||||
fn map() {
|
||||
let map = Map::new();
|
||||
let mut map = Map::new();
|
||||
|
||||
map.set("x".to_string(), Value::Integer(1)).unwrap();
|
||||
map.set("foo".to_string(), Value::string("bar".to_string()))
|
||||
.unwrap();
|
||||
map.set("x".to_string(), Value::Integer(1));
|
||||
map.set("foo".to_string(), Value::string("bar".to_string()));
|
||||
|
||||
assert_eq!(interpret("{ x = 1, foo = 'bar' }"), Ok(Value::Map(map)));
|
||||
}
|
||||
@ -85,11 +84,10 @@ fn empty_map() {
|
||||
|
||||
#[test]
|
||||
fn map_types() {
|
||||
let map = Map::new();
|
||||
let mut map = Map::new();
|
||||
|
||||
map.set("x".to_string(), Value::Integer(1)).unwrap();
|
||||
map.set("foo".to_string(), Value::string("bar".to_string()))
|
||||
.unwrap();
|
||||
map.set("x".to_string(), Value::Integer(1));
|
||||
map.set("foo".to_string(), Value::string("bar".to_string()));
|
||||
|
||||
assert_eq!(
|
||||
interpret("{ x <int> = 1, foo <str> = 'bar' }"),
|
||||
|
Loading…
Reference in New Issue
Block a user