diff --git a/src/abstract_tree/as.rs b/src/abstract_tree/as.rs new file mode 100644 index 0000000..41d49a2 --- /dev/null +++ b/src/abstract_tree/as.rs @@ -0,0 +1,107 @@ +use serde::{Deserialize, Serialize}; +use tree_sitter::Node; + +use crate::{ + error::{RuntimeError, SyntaxError, ValidationError}, + AbstractTree, Context, Expression, Format, List, SourcePosition, Type, Value, +}; + +#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] +pub struct As { + expression: Expression, + r#type: Type, + position: SourcePosition, +} + +impl AbstractTree for As { + fn from_syntax(node: Node, source: &str, context: &Context) -> Result { + SyntaxError::expect_syntax_node(source, "as", node)?; + + let expression_node = node.child(0).unwrap(); + let expression = Expression::from_syntax(expression_node, source, context)?; + + let type_node = node.child(2).unwrap(); + let r#type = Type::from_syntax(type_node, source, context)?; + + Ok(As { + expression, + r#type, + position: SourcePosition::from(node.range()), + }) + } + + fn expected_type(&self, _context: &Context) -> Result { + Ok(self.r#type.clone()) + } + + fn validate(&self, source: &str, context: &Context) -> Result<(), ValidationError> { + let expected_type = self.expression.expected_type(context)?; + + if let Type::List(item_type) = &self.r#type { + match &expected_type { + Type::List(expected_item_type) => { + if !item_type.accepts(&expected_item_type) { + return Err(ValidationError::TypeCheck { + expected: self.r#type.clone(), + actual: expected_type.clone(), + position: self.position, + }); + } + } + Type::Any => todo!(), + Type::Boolean => todo!(), + Type::Collection => todo!(), + Type::Custom(_) => todo!(), + Type::Float => todo!(), + Type::Function { + parameter_types, + return_type, + } => todo!(), + Type::Integer => todo!(), + Type::Map(_) => todo!(), + Type::None => todo!(), + Type::Number => todo!(), + Type::String => todo!(), + Type::Range => todo!(), + Type::Option(_) => todo!(), + } + } + + Ok(()) + } + + fn run(&self, source: &str, context: &Context) -> Result { + let value = self.expression.run(source, context)?; + let converted_value = if let Type::List(_) = self.r#type { + match value { + Value::List(list) => Value::List(list), + Value::String(string) => { + let chars = string + .chars() + .map(|char| Value::String(char.to_string())) + .collect(); + + Value::List(List::with_items(chars)) + } + Value::Map(_) => todo!(), + Value::Function(_) => todo!(), + Value::Float(_) => todo!(), + Value::Integer(_) => todo!(), + Value::Boolean(_) => todo!(), + Value::Range(_) => todo!(), + Value::Option(_) => todo!(), + Value::Structure(_) => todo!(), + } + } else { + todo!() + }; + + Ok(converted_value) + } +} + +impl Format for As { + fn format(&self, output: &mut String, indent_level: u8) { + todo!() + } +} diff --git a/src/abstract_tree/expression.rs b/src/abstract_tree/expression.rs index 47e1da9..78fa5fd 100644 --- a/src/abstract_tree/expression.rs +++ b/src/abstract_tree/expression.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use crate::{ error::{RuntimeError, SyntaxError, ValidationError}, value_node::ValueNode, - AbstractTree, Command, Context, Format, FunctionCall, Identifier, Index, Logic, Math, New, + AbstractTree, As, Command, Context, Format, FunctionCall, Identifier, Index, Logic, Math, New, SyntaxNode, Type, Value, }; @@ -22,6 +22,7 @@ pub enum Expression { FunctionCall(Box), New(New), Command(Command), + As(Box), } impl AbstractTree for Expression { @@ -39,6 +40,7 @@ impl AbstractTree for Expression { }; let expression = match child.kind() { + "as" => Expression::As(Box::new(As::from_syntax(child, source, _context)?)), "value" => Expression::Value(ValueNode::from_syntax(child, source, _context)?), "identifier" => { Expression::Identifier(Identifier::from_syntax(child, source, _context)?) @@ -54,7 +56,7 @@ impl AbstractTree for Expression { _ => { return Err(SyntaxError::UnexpectedSyntaxNode { expected: - "value, identifier, index, math, logic, function call, new or command" + "value, identifier, index, math, logic, function call, new, as or command" .to_string(), actual: child.kind().to_string(), location: child.start_position(), @@ -76,6 +78,7 @@ impl AbstractTree for Expression { Expression::Index(index) => index.expected_type(_context), Expression::New(new) => new.expected_type(_context), Expression::Command(command) => command.expected_type(_context), + Expression::As(r#as) => r#as.expected_type(_context), } } @@ -89,6 +92,7 @@ impl AbstractTree for Expression { Expression::Index(index) => index.validate(_source, _context), Expression::New(new) => new.validate(_source, _context), Expression::Command(command) => command.validate(_source, _context), + Expression::As(r#as) => r#as.validate(_source, _context), } } @@ -102,6 +106,7 @@ impl AbstractTree for Expression { Expression::Index(index) => index.run(_source, _context), Expression::New(new) => new.run(_source, _context), Expression::Command(command) => command.run(_source, _context), + Expression::As(r#as) => r#as.run(_source, _context), } } } @@ -117,6 +122,7 @@ impl Format for Expression { Expression::Index(index) => index.format(_output, _indent_level), Expression::New(new) => new.format(_output, _indent_level), Expression::Command(command) => command.format(_output, _indent_level), + Expression::As(r#as) => r#as.format(_output, _indent_level), } } } diff --git a/src/abstract_tree/mod.rs b/src/abstract_tree/mod.rs index ef669de..5dd96da 100644 --- a/src/abstract_tree/mod.rs +++ b/src/abstract_tree/mod.rs @@ -6,6 +6,7 @@ //! syntax nodes. Then add a new AbstractTree type using the existing types as //! examples. +pub mod r#as; pub mod assignment; pub mod assignment_operator; pub mod block; @@ -37,8 +38,8 @@ pub use { assignment::*, assignment_operator::*, block::*, built_in_value::*, command::*, expression::*, function_call::*, function_expression::*, function_node::*, identifier::*, if_else::*, index::*, index_assignment::IndexAssignment, index_expression::*, logic::*, logic_operator::*, - math::*, math_operator::*, new::*, r#for::*, r#match::*, r#type::*, r#while::*, statement::*, - type_specification::*, value_node::*, + math::*, math_operator::*, new::*, r#as::*, r#for::*, r#match::*, r#type::*, r#while::*, + statement::*, type_specification::*, value_node::*, }; use serde::{Deserialize, Serialize}; diff --git a/tests/as.rs b/tests/as.rs new file mode 100644 index 0000000..083393c --- /dev/null +++ b/tests/as.rs @@ -0,0 +1,16 @@ +use dust_lang::*; + +#[test] +fn string_as_list() { + assert_eq!( + interpret("'foobar' as [str]"), + Ok(Value::List(List::with_items(vec![ + Value::String("f".to_string()), + Value::String("o".to_string()), + Value::String("o".to_string()), + Value::String("b".to_string()), + Value::String("a".to_string()), + Value::String("r".to_string()), + ]))) + ) +} diff --git a/tree-sitter-dust/corpus/as.txt b/tree-sitter-dust/corpus/as.txt index c8e8316..467ba42 100644 --- a/tree-sitter-dust/corpus/as.txt +++ b/tree-sitter-dust/corpus/as.txt @@ -37,7 +37,7 @@ foo as (int) -> int As List in For Loop ================================================================================ -for i in foobar as list {} +for i in foobar as [string] {} -------------------------------------------------------------------------------- @@ -50,5 +50,6 @@ for i in foobar as list {} (expression (identifier)) (type - (identifier)))) + (type + (identifier))))) (block))))