1
0

Implement structs; Modify tests

This commit is contained in:
Jeff 2024-02-15 00:53:43 -05:00
parent 97319d28b2
commit fc3dfc0e03
21 changed files with 416 additions and 467 deletions

View File

@ -6,8 +6,7 @@ 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, Structure, SyntaxNode, AbstractTree, BuiltInFunction, Context, Format, Function, List, Map, SyntaxNode, Type, Value,
Type, Value,
}; };
static ARGS: OnceLock<Value> = OnceLock::new(); static ARGS: OnceLock<Value> = OnceLock::new();
@ -87,14 +86,12 @@ impl BuiltInValue {
match self { match self {
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,
BuiltInValue::Json => Type::Map(Some( BuiltInValue::Json => Type::Map,
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,
BuiltInValue::Str => Type::Map(None), BuiltInValue::Str => Type::Map,
} }
} }

View File

@ -48,17 +48,7 @@ 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(structure) => { Type::Map => Ok(Type::Any),
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),
} }
@ -94,7 +84,7 @@ impl AbstractTree for Index {
Ok(item) Ok(item)
} }
Value::Map(map) => { Value::Map(map) => {
let map = map.inner()?; let map = map.inner();
let (key, value) = if let IndexExpression::Identifier(identifier) = &self.index { let (key, value) = if let IndexExpression::Identifier(identifier) = &self.index {
let key = identifier.inner(); let key = identifier.inner();

View 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!()
}
}

View File

@ -25,6 +25,7 @@ pub mod index_assignment;
pub mod index_expression; pub mod index_expression;
pub mod logic; pub mod logic;
pub mod logic_operator; pub mod logic_operator;
pub mod map_node;
pub mod r#match; pub mod r#match;
pub mod math; pub mod math;
pub mod math_operator; pub mod math_operator;
@ -41,8 +42,8 @@ pub use {
assignment::*, assignment_operator::*, block::*, built_in_value::*, command::*, assignment::*, assignment_operator::*, block::*, built_in_value::*, command::*,
enum_defintion::*, expression::*, function_call::*, function_expression::*, function_node::*, enum_defintion::*, expression::*, function_call::*, function_expression::*, function_node::*,
identifier::*, if_else::*, index::*, index_assignment::IndexAssignment, index_expression::*, identifier::*, if_else::*, index::*, index_assignment::IndexAssignment, index_expression::*,
logic::*, logic_operator::*, math::*, math_operator::*, new::*, r#as::*, r#for::*, r#match::*, logic::*, logic_operator::*, map_node::*, math::*, math_operator::*, new::*, r#as::*, r#for::*,
r#type::*, r#while::*, statement::*, struct_definition::*, type_definition::*, r#match::*, r#type::*, r#while::*, statement::*, struct_definition::*, type_definition::*,
type_specification::*, value_node::*, type_specification::*, value_node::*,
}; };

View File

@ -1,34 +1,126 @@
use std::collections::BTreeMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tree_sitter::Node as SyntaxNode; use tree_sitter::Node as SyntaxNode;
use crate::{ use crate::{
error::{RuntimeError, SyntaxError, ValidationError}, 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)] #[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 { impl AbstractTree for StructDefinition {
fn from_syntax(node: SyntaxNode, source: &str, context: &Context) -> Result<Self, SyntaxError> { 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)) = (&current_identifier, &current_type) {
properties.insert(identifier.inner().clone(), (None, r#type.clone()));
}
} }
fn expected_type(&self, context: &Context) -> Result<Type, ValidationError> { current_type = None;
todo!() current_identifier =
Some(Identifier::from_syntax(child_syntax_node, source, context)?);
} }
fn validate(&self, source: &str, context: &Context) -> Result<(), ValidationError> { if child_syntax_node.kind() == "type_specification" {
todo!() current_type = Some(
TypeSpecification::from_syntax(child_syntax_node, source, context)?
.take_inner(),
);
} }
fn run(&self, source: &str, context: &Context) -> Result<Value, RuntimeError> { if child_syntax_node.kind() == "statement" {
todo!() current_statement =
Some(Statement::from_syntax(child_syntax_node, source, context)?);
if let Some(identifier) = &current_identifier {
let r#type = if let Some(r#type) = &current_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> {
Ok(Type::None)
}
fn validate(&self, _source: &str, _context: &Context) -> Result<(), ValidationError> {
Ok(())
}
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 { impl Format for StructDefinition {
fn format(&self, output: &mut String, indent_level: u8) { fn format(&self, _output: &mut String, _indent_level: u8) {
todo!() todo!()
} }
} }

View File

@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
error::{RuntimeError, SyntaxError, ValidationError}, 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)] #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
@ -20,7 +20,7 @@ pub enum Type {
}, },
Integer, Integer,
List(Box<Type>), List(Box<Type>),
Map(Option<Structure>), Map,
None, None,
Number, Number,
String, String,
@ -58,13 +58,13 @@ impl Type {
| (Type::Collection, Type::Collection) | (Type::Collection, Type::Collection)
| (Type::Collection, Type::List(_)) | (Type::Collection, Type::List(_))
| (Type::List(_), Type::Collection) | (Type::List(_), Type::Collection)
| (Type::Collection, Type::Map(_)) | (Type::Collection, Type::Map)
| (Type::Map(_), Type::Collection) | (Type::Map, Type::Collection)
| (Type::Collection, Type::String) | (Type::Collection, Type::String)
| (Type::String, Type::Collection) | (Type::String, Type::Collection)
| (Type::Float, Type::Float) | (Type::Float, Type::Float)
| (Type::Integer, Type::Integer) | (Type::Integer, Type::Integer)
| (Type::Map(_), Type::Map(_)) | (Type::Map, Type::Map)
| (Type::Number, Type::Number) | (Type::Number, Type::Number)
| (Type::Number, Type::Integer) | (Type::Number, Type::Integer)
| (Type::Number, Type::Float) | (Type::Number, Type::Float)
@ -121,7 +121,7 @@ impl Type {
} }
pub fn is_map(&self) -> bool { 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, "int" => Type::Integer,
"map" => Type::Map(None), "map" => Type::Map,
"num" => Type::Number, "num" => Type::Number,
"none" => Type::None, "none" => Type::None,
"str" => Type::String, "str" => Type::String,
@ -243,13 +243,9 @@ impl Format for Type {
item_type.format(output, indent_level); item_type.format(output, indent_level);
output.push(']'); output.push(']');
} }
Type::Map(structure_option) => { Type::Map => {
if let Some(structure) = structure_option {
output.push_str(&structure.to_string());
} else {
output.push_str("map"); output.push_str("map");
} }
}
Type::None => output.push_str("none"), Type::None => output.push_str("none"),
Type::Number => output.push_str("num"), Type::Number => output.push_str("num"),
Type::String => output.push_str("str"), Type::String => output.push_str("str"),
@ -290,7 +286,7 @@ impl Display for Type {
} }
Type::Integer => write!(f, "int"), Type::Integer => write!(f, "int"),
Type::List(item_type) => write!(f, "[{item_type}]"), Type::List(item_type) => write!(f, "[{item_type}]"),
Type::Map(_) => write!(f, "map"), Type::Map => write!(f, "map"),
Type::Number => write!(f, "num"), Type::Number => write!(f, "num"),
Type::None => write!(f, "none"), Type::None => write!(f, "none"),
Type::String => write!(f, "str"), Type::String => write!(f, "str"),

View File

@ -1,4 +1,4 @@
use std::{cmp::Ordering, collections::BTreeMap, ops::RangeInclusive}; use std::{cmp::Ordering, ops::RangeInclusive};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tree_sitter::Node as SyntaxNode; use tree_sitter::Node as SyntaxNode;
@ -6,8 +6,7 @@ use tree_sitter::Node as SyntaxNode;
use crate::{ use crate::{
error::{RuntimeError, SyntaxError, ValidationError}, error::{RuntimeError, SyntaxError, ValidationError},
AbstractTree, BuiltInValue, Context, Expression, Format, Function, FunctionNode, AbstractTree, BuiltInValue, Context, Expression, Format, Function, FunctionNode,
Identifier, List, Map, SourcePosition, Statement, Structure, Type, Identifier, List, Type, Value, TypeDefinition, MapNode,
TypeSpecification, Value, TypeDefinition,
}; };
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
@ -19,10 +18,13 @@ pub enum ValueNode {
String(String), String(String),
List(Vec<Expression>), List(Vec<Expression>),
Option(Option<Box<Expression>>), Option(Option<Box<Expression>>),
Map(BTreeMap<String, (Statement, Option<Type>)>, SourcePosition), Map(MapNode),
BuiltInValue(BuiltInValue), BuiltInValue(BuiltInValue),
Range(RangeInclusive<i64>), Range(RangeInclusive<i64>),
Structure(BTreeMap<String, (Option<Statement>, Type)>), Struct {
name: Identifier,
properties: MapNode,
},
Enum { Enum {
name: Identifier, name: Identifier,
variant: Identifier, variant: Identifier,
@ -65,32 +67,7 @@ impl AbstractTree for ValueNode {
ValueNode::List(expressions) ValueNode::List(expressions)
} }
"map" => { "map" => {
let mut child_nodes = BTreeMap::new(); ValueNode::Map(MapNode::from_syntax(child, source, context)?)
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()))
} }
"option" => { "option" => {
let first_grandchild = child.child(0).unwrap(); let first_grandchild = child.child(0).unwrap();
@ -113,60 +90,6 @@ impl AbstractTree for ValueNode {
context, 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)) =
(&current_identifier, &current_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) = &current_identifier {
// let r#type = if let Some(r#type) = &current_type {
// r#type.clone()
// } else if let Some(statement) = &current_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" => { "range" => {
let mut split = source[child.byte_range()].split(".."); let mut split = source[child.byte_range()].split("..");
let start = split.next().unwrap().parse().unwrap(); let start = split.next().unwrap().parse().unwrap();
@ -189,10 +112,23 @@ impl AbstractTree for ValueNode {
ValueNode::Enum { name, variant , expression } 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 { return Err(SyntaxError::UnexpectedSyntaxNode {
expected: 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(), .to_string(),
actual: child.kind().to_string(), actual: child.kind().to_string(),
location: child.start_position(), location: child.start_position(),
@ -239,16 +175,10 @@ impl AbstractTree for ValueNode {
Type::None Type::None
} }
} }
ValueNode::Map(_, _) => Type::Map(None), ValueNode::Map(_) => Type::Map,
ValueNode::BuiltInValue(built_in_value) => built_in_value.expected_type(context)?, ValueNode::BuiltInValue(built_in_value) => built_in_value.expected_type(context)?,
ValueNode::Structure(node_map) => { ValueNode::Struct { name, .. } => {
let mut value_map = BTreeMap::new(); Type::Custom(name.clone())
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::Range(_) => Type::Range, ValueNode::Range(_) => Type::Range,
ValueNode::Enum { name, .. } => Type::Custom(name.clone()), ValueNode::Enum { name, .. } => Type::Custom(name.clone()),
@ -264,21 +194,7 @@ impl AbstractTree for ValueNode {
function_node.validate(_source, context)?; function_node.validate(_source, context)?;
} }
} }
ValueNode::Map(statements, source_position) => { ValueNode::Map(map_node) => map_node.validate(_source, context)?,
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(),
});
}
}
}
}
_ => {}, _ => {},
} }
@ -316,36 +232,25 @@ impl AbstractTree for ValueNode {
Value::Option(option_value) Value::Option(option_value)
} }
ValueNode::Map(key_statement_pairs, _) => { ValueNode::Map(map_node) => map_node.run(source, context)?,
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::BuiltInValue(built_in_value) => built_in_value.run(source, context)?, ValueNode::BuiltInValue(built_in_value) => built_in_value.run(source, context)?,
ValueNode::Structure(node_map) => { ValueNode::Range(range) => Value::Range(range.clone()),
let mut value_map = BTreeMap::new(); ValueNode::Struct { name, properties } => {
let instance = if let Some(definition) = context.get_definition(name.inner())? {
for (key, (statement_option, r#type)) in node_map { if let TypeDefinition::Struct(struct_definition) = definition {
let value_option = if let Some(statement) = statement_option { struct_definition.instantiate(properties, source, context)?
Some(statement.run(source, context)?)
} else { } else {
None return Err(RuntimeError::ValidationFailure(ValidationError::ExpectedStructDefintion { actual: definition.clone() }))
}
} else {
return Err(RuntimeError::ValidationFailure(
ValidationError::TypeDefinitionNotFound(name.inner().clone())
));
}; };
value_map.insert(key.to_string(), (value_option, r#type.clone())); Value::Struct(instance)
}
Value::Structure(Structure::new(value_map))
} }
ValueNode::Range(range) => Value::Range(range.clone()),
ValueNode::Enum { name, variant, expression } => { ValueNode::Enum { name, variant, expression } => {
let value = if let Some(expression) = expression { let value = if let Some(expression) = expression {
expression.run(source, context)? expression.run(source, context)?
@ -406,52 +311,11 @@ impl Format for ValueNode {
output.push_str("none"); output.push_str("none");
} }
} }
ValueNode::Map(nodes, _) => { ValueNode::Map(map_node) => map_node.format(output, indent_level),
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::BuiltInValue(built_in_value) => built_in_value.format(output, indent_level), ValueNode::BuiltInValue(built_in_value) => built_in_value.format(output, indent_level),
ValueNode::Structure(nodes) => { ValueNode::Struct { name, properties } => {
output.push('{'); name.format(output, indent_level);
properties.format(output, indent_level);
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::Range(_) => todo!(), ValueNode::Range(_) => todo!(),
ValueNode::Enum { .. } => todo!(), ValueNode::Enum { .. } => todo!(),
@ -476,12 +340,20 @@ impl Ord for ValueNode {
(ValueNode::List(_), _) => Ordering::Greater, (ValueNode::List(_), _) => Ordering::Greater,
(ValueNode::Option(left), ValueNode::Option(right)) => left.cmp(right), (ValueNode::Option(left), ValueNode::Option(right)) => left.cmp(right),
(ValueNode::Option(_), _) => Ordering::Greater, (ValueNode::Option(_), _) => Ordering::Greater,
(ValueNode::Map(left, _), ValueNode::Map(right, _)) => left.cmp(right), (ValueNode::Map(left), ValueNode::Map(right)) => left.cmp(right),
(ValueNode::Map(_, _), _) => Ordering::Greater, (ValueNode::Map(_), _) => Ordering::Greater,
(ValueNode::BuiltInValue(left), ValueNode::BuiltInValue(right)) => left.cmp(right), (ValueNode::BuiltInValue(left), ValueNode::BuiltInValue(right)) => left.cmp(right),
(ValueNode::BuiltInValue(_), _) => Ordering::Greater, (ValueNode::BuiltInValue(_), _) => Ordering::Greater,
(ValueNode::Structure(left), ValueNode::Structure(right)) => left.cmp(right), (ValueNode::Struct{ name: left_name, properties: left_properties }, ValueNode::Struct {name: right_name, properties: right_properties} ) => {
(ValueNode::Structure(_), _) => Ordering::Greater, 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 { ValueNode::Enum {
name: left_name, variant: left_variant, expression: left_expression name: left_name, variant: left_variant, expression: left_expression

View File

@ -111,7 +111,7 @@ impl Callable for BuiltInFunction {
let length = if let Ok(list) = value.as_list() { let length = if let Ok(list) = value.as_list() {
list.items().len() list.items().len()
} else if let Ok(map) = value.as_map() { } else if let Ok(map) = value.as_map() {
map.inner()?.len() map.inner().len()
} else if let Ok(str) = value.as_string() { } else if let Ok(str) = value.as_string() {
str.chars().count() str.chars().count()
} else { } else {

View File

@ -67,8 +67,11 @@ pub enum ValidationError {
/// Failed to find a type definition with this key. /// Failed to find a type definition with this key.
TypeDefinitionNotFound(String), TypeDefinitionNotFound(String),
/// Failed to find a type definition with this key. /// Failed to find an enum definition with this key.
ExpectedEnumDefintion { actual: TypeDefinition }, ExpectedEnumDefintion { actual: TypeDefinition },
/// Failed to find a struct definition with this key.
ExpectedStructDefintion { actual: TypeDefinition },
} }
impl ValidationError { impl ValidationError {

View File

@ -292,7 +292,7 @@ impl Completer for DustCompleter {
} }
if let Value::Map(map) = built_in_value.get() { 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) { if key.contains(last_word) {
suggestions.push(Suggestion { suggestions.push(Suggestion {
value: format!("{name}:{key}"), value: format!("{name}:{key}"),

View File

@ -1,3 +1,4 @@
use serde::{Deserialize, Serialize};
use stanza::{ use stanza::{
renderer::{console::Console, Renderer}, renderer::{console::Console, Renderer},
style::{HAlign, Styles}, style::{HAlign, Styles},
@ -6,52 +7,47 @@ use stanza::{
use std::{ use std::{
collections::BTreeMap, collections::BTreeMap,
fmt::{self, Display, Formatter}, 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. /// 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 /// The inner value is a BTreeMap in order to allow VariableMap instances to be sorted and compared
/// to one another. /// to one another.
#[derive(Clone, Debug)] #[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct Map { pub struct Map {
inner: Arc<RwLock<BTreeMap<String, Value>>>, inner: BTreeMap<String, Value>,
} }
impl Map { impl Map {
/// Creates a new instace. /// Creates a new instace.
pub fn new() -> Self { pub fn new() -> Self {
Map { Map {
inner: Arc::new(RwLock::new(BTreeMap::new())), inner: BTreeMap::new(),
} }
} }
pub fn with_values(variables: BTreeMap<String, Value>) -> Self { pub fn with_values(variables: BTreeMap<String, Value>) -> Self {
Map { Map { inner: variables }
inner: Arc::new(RwLock::new(variables)),
}
} }
pub fn inner(&self) -> Result<RwLockReadGuard<BTreeMap<String, Value>>, RwLockError> { pub fn inner(&self) -> &BTreeMap<String, Value> {
Ok(self.inner.read()?) &self.inner
} }
pub fn get(&self, key: &str) -> Result<Option<Value>, RwLockError> { pub fn get(&self, key: &str) -> Option<&Value> {
Ok(self.inner()?.get(key).cloned()) self.inner.get(key)
} }
pub fn set(&self, key: String, value: Value) -> Result<(), RwLockError> { pub fn set(&mut self, key: String, value: Value) {
self.inner.write()?.insert(key, value); self.inner.insert(key, value);
Ok(())
} }
pub fn as_text_table(&self) -> Table { pub fn as_text_table(&self) -> Table {
let mut table = Table::with_styles(Styles::default().with(HAlign::Centred)); 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 { if let Value::Map(map) = value {
table.push_row(Row::new( table.push_row(Row::new(
Styles::default(), Styles::default(),
@ -92,34 +88,3 @@ impl Display for Map {
f.write_str(&renderer.render(&self.as_text_table())) 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())
}
}

View File

@ -17,14 +17,15 @@ use std::{
}; };
pub use self::{ 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 function;
pub mod list; pub mod list;
pub mod map; pub mod map;
pub mod structure; pub mod struct_instance;
/// Dust value representation. /// Dust value representation.
/// ///
@ -42,7 +43,7 @@ pub enum Value {
Boolean(bool), Boolean(bool),
Range(RangeInclusive<i64>), Range(RangeInclusive<i64>),
Option(Option<Box<Value>>), Option(Option<Box<Value>>),
Structure(Structure), Struct(StructInstance),
Enum(EnumInstance), Enum(EnumInstance),
} }
@ -87,14 +88,14 @@ impl Value {
Value::Map(map) => { Value::Map(map) => {
let mut identifier_types = Vec::new(); let mut identifier_types = Vec::new();
for (key, value) in map.inner().unwrap().iter() { for (key, value) in map.inner() {
identifier_types.push(( identifier_types.push((
Identifier::new(key.clone()), Identifier::new(key.clone()),
TypeSpecification::new(value.r#type()), TypeSpecification::new(value.r#type()),
)); ));
} }
Type::Map(None) Type::Map
} }
Value::Function(function) => function.r#type().clone(), Value::Function(function) => function.r#type().clone(),
Value::String(_) => Type::String, Value::String(_) => Type::String,
@ -109,7 +110,7 @@ impl Value {
} }
} }
Value::Range(_) => todo!(), Value::Range(_) => todo!(),
Value::Structure(_) => todo!(), Value::Struct(_) => todo!(),
Value::Enum(_) => todo!(), Value::Enum(_) => todo!(),
} }
} }
@ -446,7 +447,7 @@ impl PartialEq for Value {
(Value::Function(left), Value::Function(right)) => left == right, (Value::Function(left), Value::Function(right)) => left == right,
(Value::Option(left), Value::Option(right)) => left == right, (Value::Option(left), Value::Option(right)) => left == right,
(Value::Range(left), Value::Range(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, (Value::Enum(left), Value::Enum(right)) => left == right,
_ => false, _ => false,
} }
@ -484,8 +485,8 @@ impl Ord for Value {
(Value::Map(_), _) => Ordering::Greater, (Value::Map(_), _) => Ordering::Greater,
(Value::Function(left), Value::Function(right)) => left.cmp(right), (Value::Function(left), Value::Function(right)) => left.cmp(right),
(Value::Function(_), _) => Ordering::Greater, (Value::Function(_), _) => Ordering::Greater,
(Value::Structure(left), Value::Structure(right)) => left.cmp(right), (Value::Struct(left), Value::Struct(right)) => left.cmp(right),
(Value::Structure(_), _) => Ordering::Greater, (Value::Struct(_), _) => Ordering::Greater,
(Value::Enum(left), Value::Enum(right)) => left.cmp(right), (Value::Enum(left), Value::Enum(right)) => left.cmp(right),
(Value::Enum(_), _) => Ordering::Greater, (Value::Enum(_), _) => Ordering::Greater,
(Value::Range(left), Value::Range(right)) => { (Value::Range(left), Value::Range(right)) => {
@ -523,7 +524,7 @@ impl Serialize for Value {
} }
Value::Option(inner) => inner.serialize(serializer), Value::Option(inner) => inner.serialize(serializer),
Value::Map(map) => { Value::Map(map) => {
let entries = map.inner().unwrap(); let entries = map.inner();
let mut map = serializer.serialize_map(Some(entries.len()))?; let mut map = serializer.serialize_map(Some(entries.len()))?;
for (key, value) in entries.iter() { for (key, value) in entries.iter() {
@ -533,7 +534,7 @@ impl Serialize for Value {
map.end() map.end()
} }
Value::Function(inner) => inner.serialize(serializer), 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::Range(range) => range.serialize(serializer),
Value::Enum(_) => todo!(), Value::Enum(_) => todo!(),
} }
@ -557,7 +558,7 @@ impl Display for Value {
Value::List(list) => write!(f, "{list}"), Value::List(list) => write!(f, "{list}"),
Value::Map(map) => write!(f, "{map}"), Value::Map(map) => write!(f, "{map}"),
Value::Function(function) => write!(f, "{function}"), 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::Range(range) => write!(f, "{}..{}", range.start(), range.end()),
Value::Enum(_) => todo!(), Value::Enum(_) => todo!(),
} }

View 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()
}
}

View File

@ -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())
}
}

View File

@ -39,7 +39,7 @@ fn conversion_runtime_error() {
interpret(&format!("json:parse('{JSON}') as [map]")), interpret(&format!("json:parse('{JSON}') as [map]")),
Err(Error::Runtime(RuntimeError::ConversionImpossible { Err(Error::Runtime(RuntimeError::ConversionImpossible {
value: json_value, value: json_value,
target_type: Type::List(Box::new(Type::Map(None))) target_type: Type::List(Box::new(Type::Map))
})) }))
) )
} }

View File

@ -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("x".to_string(), Value::Integer(1));
map.set("y".to_string(), Value::Integer(2)).unwrap(); map.set("y".to_string(), Value::Integer(2));
assert_eq!(Ok(Value::Map(map)), result); assert_eq!(Ok(Value::Map(map)), result);
} }

View File

@ -89,9 +89,9 @@ fn function_context_captures_functions() {
#[test] #[test]
fn function_context_captures_structure_definitions() { 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!( assert_eq!(
interpret( interpret(

54
tests/structs.rs Normal file
View 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!()
}

View File

@ -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!()
}

View File

@ -69,11 +69,10 @@ fn empty_list() {
#[test] #[test]
fn map() { fn map() {
let map = Map::new(); let mut map = Map::new();
map.set("x".to_string(), Value::Integer(1)).unwrap(); map.set("x".to_string(), Value::Integer(1));
map.set("foo".to_string(), Value::string("bar".to_string())) map.set("foo".to_string(), Value::string("bar".to_string()));
.unwrap();
assert_eq!(interpret("{ x = 1, foo = 'bar' }"), Ok(Value::Map(map))); assert_eq!(interpret("{ x = 1, foo = 'bar' }"), Ok(Value::Map(map)));
} }
@ -85,11 +84,10 @@ fn empty_map() {
#[test] #[test]
fn map_types() { fn map_types() {
let map = Map::new(); let mut map = Map::new();
map.set("x".to_string(), Value::Integer(1)).unwrap(); map.set("x".to_string(), Value::Integer(1));
map.set("foo".to_string(), Value::string("bar".to_string())) map.set("foo".to_string(), Value::string("bar".to_string()));
.unwrap();
assert_eq!( assert_eq!(
interpret("{ x <int> = 1, foo <str> = 'bar' }"), interpret("{ x <int> = 1, foo <str> = 'bar' }"),