From fc3dfc0e03f0fe2a827cbc25011e9862f253b19f Mon Sep 17 00:00:00 2001 From: Jeff Date: Thu, 15 Feb 2024 00:53:43 -0500 Subject: [PATCH] Implement structs; Modify tests --- src/abstract_tree/built_in_value.rs | 13 +- src/abstract_tree/index.rs | 14 +- src/abstract_tree/map_node.rs | 91 +++++++++ src/abstract_tree/mod.rs | 5 +- src/abstract_tree/struct_definition.rs | 112 ++++++++++- src/abstract_tree/type.rs | 24 +-- src/abstract_tree/value_node.rs | 246 ++++++------------------ src/built_in_functions/mod.rs | 2 +- src/error/validation_error.rs | 5 +- src/main.rs | 2 +- src/value/{enum.rs => enum_instance.rs} | 0 src/value/map.rs | 61 ++---- src/value/mod.rs | 27 +-- src/value/struct_instance.rs | 45 +++++ src/value/structure.rs | 113 ----------- tests/as.rs | 2 +- tests/for_loop.rs | 6 +- tests/functions.rs | 4 +- tests/structs.rs | 54 ++++++ tests/structure.rs | 43 ----- tests/value.rs | 14 +- 21 files changed, 416 insertions(+), 467 deletions(-) create mode 100644 src/abstract_tree/map_node.rs rename src/value/{enum.rs => enum_instance.rs} (100%) create mode 100644 src/value/struct_instance.rs delete mode 100644 src/value/structure.rs create mode 100644 tests/structs.rs delete mode 100644 tests/structure.rs diff --git a/src/abstract_tree/built_in_value.rs b/src/abstract_tree/built_in_value.rs index a71114f..10c81c0 100644 --- a/src/abstract_tree/built_in_value.rs +++ b/src/abstract_tree/built_in_value.rs @@ -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 = 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, } } diff --git a/src/abstract_tree/index.rs b/src/abstract_tree/index.rs index a401235..fcdd425 100644 --- a/src/abstract_tree/index.rs +++ b/src/abstract_tree/index.rs @@ -48,17 +48,7 @@ impl AbstractTree for Index { fn expected_type(&self, context: &Context) -> Result { 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(); diff --git a/src/abstract_tree/map_node.rs b/src/abstract_tree/map_node.rs new file mode 100644 index 0000000..d32b5f3 --- /dev/null +++ b/src/abstract_tree/map_node.rs @@ -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)>, + position: SourcePosition, +} + +impl MapNode { + pub fn properties(&self) -> &BTreeMap)> { + &self.properties + } +} + +impl AbstractTree for MapNode { + fn from_syntax(node: SyntaxNode, source: &str, context: &Context) -> Result { + 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 { + 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 { + let mut map = Map::new(); + + Ok(Value::Map(map)) + } +} + +impl Format for MapNode { + fn format(&self, _output: &mut String, _indent_level: u8) { + todo!() + } +} diff --git a/src/abstract_tree/mod.rs b/src/abstract_tree/mod.rs index 0f9519c..2d97306 100644 --- a/src/abstract_tree/mod.rs +++ b/src/abstract_tree/mod.rs @@ -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::*, }; diff --git a/src/abstract_tree/struct_definition.rs b/src/abstract_tree/struct_definition.rs index a8c4a9b..b99d540 100644 --- a/src/abstract_tree/struct_definition.rs +++ b/src/abstract_tree/struct_definition.rs @@ -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, Type)>, +} + +impl StructDefinition { + pub fn instantiate( + &self, + new_properties: &MapNode, + source: &str, + context: &Context, + ) -> Result { + 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 { - 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 = None; + let mut current_type: Option = 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 { - todo!() + fn expected_type(&self, _context: &Context) -> Result { + 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 { - todo!() + fn run(&self, _source: &str, context: &Context) -> Result { + 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!() } } diff --git a/src/abstract_tree/type.rs b/src/abstract_tree/type.rs index 88672e2..053d6c9 100644 --- a/src/abstract_tree/type.rs +++ b/src/abstract_tree/type.rs @@ -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), - Map(Option), + 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"), diff --git a/src/abstract_tree/value_node.rs b/src/abstract_tree/value_node.rs index 4539877..fcfa0e1 100644 --- a/src/abstract_tree/value_node.rs +++ b/src/abstract_tree/value_node.rs @@ -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), Option(Option>), - Map(BTreeMap)>, SourcePosition), + Map(MapNode), BuiltInValue(BuiltInValue), Range(RangeInclusive), - Structure(BTreeMap, 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 = None; - let mut current_type: Option = 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 diff --git a/src/built_in_functions/mod.rs b/src/built_in_functions/mod.rs index fd7b575..2f60f94 100644 --- a/src/built_in_functions/mod.rs +++ b/src/built_in_functions/mod.rs @@ -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 { diff --git a/src/error/validation_error.rs b/src/error/validation_error.rs index 133ca0c..0f0bc29 100644 --- a/src/error/validation_error.rs +++ b/src/error/validation_error.rs @@ -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 { diff --git a/src/main.rs b/src/main.rs index f9069b6..953e75c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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}"), diff --git a/src/value/enum.rs b/src/value/enum_instance.rs similarity index 100% rename from src/value/enum.rs rename to src/value/enum_instance.rs diff --git a/src/value/map.rs b/src/value/map.rs index f90ea27..9ca6440 100644 --- a/src/value/map.rs +++ b/src/value/map.rs @@ -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>>, + inner: BTreeMap, } 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) -> Self { - Map { - inner: Arc::new(RwLock::new(variables)), - } + Map { inner: variables } } - pub fn inner(&self) -> Result>, RwLockError> { - Ok(self.inner.read()?) + pub fn inner(&self) -> &BTreeMap { + &self.inner } - pub fn get(&self, key: &str) -> Result, 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 { - Some(self.cmp(other)) - } -} - -impl Ord for Map { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.inner().unwrap().cmp(&other.inner().unwrap()) - } -} diff --git a/src/value/mod.rs b/src/value/mod.rs index c7931ba..9adc5e2 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -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), Option(Option>), - 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!(), } diff --git a/src/value/struct_instance.rs b/src/value/struct_instance.rs new file mode 100644 index 0000000..51c666e --- /dev/null +++ b/src/value/struct_instance.rs @@ -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(&self, serializer: S) -> Result + 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() + } +} diff --git a/src/value/structure.rs b/src/value/structure.rs deleted file mode 100644 index 0f13804..0000000 --- a/src/value/structure.rs +++ /dev/null @@ -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, Type)>>); - -impl Structure { - pub fn new(map: BTreeMap, Type)>) -> Self { - Structure(Arc::new(map)) - } - - pub fn from_map(map: &Map) -> Result { - 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, 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(&self, serializer: S) -> Result - 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 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(self, mut access: M) -> std::result::Result - where - M: MapAccess<'de>, - { - let mut b_tree = BTreeMap::new(); - - { - while let Some((key, value)) = access.next_entry::()? { - 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(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - deserializer.deserialize_any(StructureVisitor::new()) - } -} diff --git a/tests/as.rs b/tests/as.rs index ec31c78..5f6a29d 100644 --- a/tests/as.rs +++ b/tests/as.rs @@ -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)) })) ) } diff --git a/tests/for_loop.rs b/tests/for_loop.rs index 110fb4d..f573c64 100644 --- a/tests/for_loop.rs +++ b/tests/for_loop.rs @@ -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); } diff --git a/tests/functions.rs b/tests/functions.rs index 3734ae6..acb656f 100644 --- a/tests/functions.rs +++ b/tests/functions.rs @@ -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( diff --git a/tests/structs.rs b/tests/structs.rs new file mode 100644 index 0000000..e4b4f36 --- /dev/null +++ b/tests/structs.rs @@ -0,0 +1,54 @@ +use std::collections::BTreeMap; + +use dust_lang::*; + +#[test] +fn simple_struct() { + let result = interpret( + " + struct Foo { + bar = 0 + baz + } + + 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 + } + 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!() +} diff --git a/tests/structure.rs b/tests/structure.rs deleted file mode 100644 index 341ea84..0000000 --- a/tests/structure.rs +++ /dev/null @@ -1,43 +0,0 @@ -use std::collections::BTreeMap; - -use dust_lang::*; - -#[test] -fn simple_structure() { - let result = interpret("struct { x = 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 = 0.0 - x = 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!() -} diff --git a/tests/value.rs b/tests/value.rs index e9bcb4c..8ec6ef0 100644 --- a/tests/value.rs +++ b/tests/value.rs @@ -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 = 1, foo = 'bar' }"),