Implement structs; Modify tests
This commit is contained in:
parent
97319d28b2
commit
fc3dfc0e03
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
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 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::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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)) = (¤t_identifier, ¤t_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) = ¤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> {
|
||||||
|
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!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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"),
|
||||||
|
@ -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)) =
|
|
||||||
(¤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" => {
|
"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
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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}"),
|
||||||
|
@ -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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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!(),
|
||||||
}
|
}
|
||||||
|
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]")),
|
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))
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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
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]
|
#[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' }"),
|
||||||
|
Loading…
Reference in New Issue
Block a user