diff --git a/Cargo.lock b/Cargo.lock index 6f79618..0eda890 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -356,6 +356,8 @@ dependencies = [ "log", "rand", "rayon", + "serde", + "serde_json", ] [[package]] @@ -1023,18 +1025,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.197" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", @@ -1043,9 +1045,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", diff --git a/dust-lang/Cargo.toml b/dust-lang/Cargo.toml index 4584cf7..4813d6c 100644 --- a/dust-lang/Cargo.toml +++ b/dust-lang/Cargo.toml @@ -17,3 +17,5 @@ env_logger = "0.11.3" log = "0.4.21" rand = "0.8.5" rayon = "1.9.0" +serde = { version = "1.0.203", features = ["derive"] } +serde_json = "1.0.117" diff --git a/dust-lang/src/abstract_tree/as.rs b/dust-lang/src/abstract_tree/as.rs index 6bc067e..37483b7 100644 --- a/dust-lang/src/abstract_tree/as.rs +++ b/dust-lang/src/abstract_tree/as.rs @@ -1,5 +1,7 @@ use std::borrow::Borrow; +use serde::{Deserialize, Serialize}; + use crate::{ context::Context, error::{RuntimeError, ValidationError}, @@ -9,7 +11,7 @@ use crate::{ use super::{AbstractNode, Action, Expression, Type, WithPosition}; -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct As { expression: Expression, r#type: WithPosition, diff --git a/dust-lang/src/abstract_tree/assignment.rs b/dust-lang/src/abstract_tree/assignment.rs index d0da851..3a72d56 100644 --- a/dust-lang/src/abstract_tree/assignment.rs +++ b/dust-lang/src/abstract_tree/assignment.rs @@ -1,3 +1,5 @@ +use serde::{Deserialize, Serialize}; + use crate::{ error::{RuntimeError, ValidationError}, identifier::Identifier, @@ -7,7 +9,7 @@ use crate::{ use super::{AbstractNode, Action, Statement, Type, WithPosition}; -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct Assignment { identifier: WithPosition, r#type: Option>, @@ -15,7 +17,7 @@ pub struct Assignment { statement: Box, } -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub enum AssignmentOperator { Assign, AddAssign, diff --git a/dust-lang/src/abstract_tree/async_block.rs b/dust-lang/src/abstract_tree/async_block.rs index fdc0b32..f73815f 100644 --- a/dust-lang/src/abstract_tree/async_block.rs +++ b/dust-lang/src/abstract_tree/async_block.rs @@ -1,6 +1,7 @@ use std::sync::RwLock; use rayon::prelude::*; +use serde::{Deserialize, Serialize}; use crate::{ context::Context, @@ -9,7 +10,7 @@ use crate::{ use super::{AbstractNode, Action, Statement, Type}; -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct AsyncBlock { statements: Vec, } diff --git a/dust-lang/src/abstract_tree/block.rs b/dust-lang/src/abstract_tree/block.rs index 65d850c..1824977 100644 --- a/dust-lang/src/abstract_tree/block.rs +++ b/dust-lang/src/abstract_tree/block.rs @@ -1,3 +1,5 @@ +use serde::{Deserialize, Serialize}; + use crate::{ context::Context, error::{RuntimeError, ValidationError}, @@ -5,7 +7,7 @@ use crate::{ use super::{AbstractNode, Action, Statement, Type}; -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct Block { statements: Vec, } diff --git a/dust-lang/src/abstract_tree/built_in_function_call.rs b/dust-lang/src/abstract_tree/built_in_function_call.rs index 64cb79e..47499a5 100644 --- a/dust-lang/src/abstract_tree/built_in_function_call.rs +++ b/dust-lang/src/abstract_tree/built_in_function_call.rs @@ -5,6 +5,8 @@ use std::{ time::Duration, }; +use serde::{Deserialize, Serialize}; + use crate::{ abstract_tree::{Action, Type}, context::Context, @@ -13,10 +15,12 @@ use crate::{ Value, }; -use super::{AbstractNode, Expression}; +use super::{AbstractNode, Expression, WithPosition}; -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub enum BuiltInFunctionCall { + JsonParse(WithPosition, Expression), + Length(Expression), ReadFile(Expression), ReadLine, Sleep(Expression), @@ -26,6 +30,8 @@ pub enum BuiltInFunctionCall { impl AbstractNode for BuiltInFunctionCall { fn expected_type(&self, _context: &mut Context) -> Result { match self { + BuiltInFunctionCall::JsonParse(r#type, _) => Ok(r#type.item.clone()), + BuiltInFunctionCall::Length(_) => Ok(Type::Integer), BuiltInFunctionCall::ReadFile(_) => Ok(Type::String), BuiltInFunctionCall::ReadLine => Ok(Type::String), BuiltInFunctionCall::Sleep(_) => Ok(Type::None), @@ -39,6 +45,12 @@ impl AbstractNode for BuiltInFunctionCall { _manage_memory: bool, ) -> Result<(), ValidationError> { match self { + BuiltInFunctionCall::JsonParse(_, expression) => { + expression.validate(_context, _manage_memory) + } + BuiltInFunctionCall::Length(expression) => { + expression.validate(_context, _manage_memory) + } BuiltInFunctionCall::ReadFile(expression) => { expression.validate(_context, _manage_memory) } @@ -52,7 +64,7 @@ impl AbstractNode for BuiltInFunctionCall { fn run(self, context: &mut Context, _manage_memory: bool) -> Result { match self { - BuiltInFunctionCall::ReadFile(expression) => { + BuiltInFunctionCall::JsonParse(r#type, expression) => { let action = expression.clone().run(context, _manage_memory)?; let value = if let Action::Return(value) = action { value @@ -62,6 +74,45 @@ impl AbstractNode for BuiltInFunctionCall { )); }; + if let ValueInner::String(string) = value.inner().as_ref() { + let deserialized = serde_json::from_str(string)?; + + Ok(Action::Return(deserialized)) + } else { + Err(RuntimeError::ValidationFailure( + ValidationError::ExpectedString { + actual: value.r#type(context)?, + position: expression.position(), + }, + )) + } + } + BuiltInFunctionCall::Length(expression) => { + let action = expression.clone().run(context, _manage_memory)?; + let value = if let Action::Return(value) = action { + value + } else { + return Err(RuntimeError::ValidationFailure( + ValidationError::InterpreterExpectedReturn(expression.position()), + )); + }; + let length = if let ValueInner::List(list) = value.inner().as_ref() { + list.len() as i64 + } else { + 0 + }; + + Ok(Action::Return(Value::integer(length))) + } + BuiltInFunctionCall::ReadFile(expression) => { + let action = expression.clone().run(context, _manage_memory)?; + let value = if let Action::Return(value) = action { + value + } else { + return Err(RuntimeError::ValidationFailure( + ValidationError::InterpreterExpectedReturn(expression.position()), + )); + }; let file_contents = if let ValueInner::String(path) = value.inner().as_ref() { read_to_string(path)? } else { diff --git a/dust-lang/src/abstract_tree/expression.rs b/dust-lang/src/abstract_tree/expression.rs index e862d59..3322c6b 100644 --- a/dust-lang/src/abstract_tree/expression.rs +++ b/dust-lang/src/abstract_tree/expression.rs @@ -1,3 +1,5 @@ +use serde::{Deserialize, Serialize}; + use crate::{ context::Context, error::{RuntimeError, ValidationError}, @@ -9,7 +11,7 @@ use super::{ SourcePosition, Type, ValueNode, WithPosition, }; -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub enum Expression { As(WithPosition>), BuiltInFunctionCall(WithPosition>), diff --git a/dust-lang/src/abstract_tree/function_call.rs b/dust-lang/src/abstract_tree/function_call.rs index 84c32d9..fd86885 100644 --- a/dust-lang/src/abstract_tree/function_call.rs +++ b/dust-lang/src/abstract_tree/function_call.rs @@ -1,3 +1,5 @@ +use serde::{Deserialize, Serialize}; + use crate::{ context::Context, error::{RuntimeError, ValidationError}, @@ -6,7 +8,7 @@ use crate::{ use super::{AbstractNode, Action, Expression, Type, WithPosition}; -#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct FunctionCall { function: Box, type_arguments: Vec>, diff --git a/dust-lang/src/abstract_tree/if_else.rs b/dust-lang/src/abstract_tree/if_else.rs index 1556ba6..5064c92 100644 --- a/dust-lang/src/abstract_tree/if_else.rs +++ b/dust-lang/src/abstract_tree/if_else.rs @@ -1,3 +1,5 @@ +use serde::{Deserialize, Serialize}; + use crate::{ context::Context, error::{RuntimeError, ValidationError}, @@ -6,7 +8,7 @@ use crate::{ use super::{AbstractNode, Action, Block, Expression, Type, WithPosition}; -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct IfElse { if_expression: Expression, if_block: WithPosition, diff --git a/dust-lang/src/abstract_tree/list_index.rs b/dust-lang/src/abstract_tree/list_index.rs index e104299..a1feb35 100644 --- a/dust-lang/src/abstract_tree/list_index.rs +++ b/dust-lang/src/abstract_tree/list_index.rs @@ -1,3 +1,5 @@ +use serde::{Deserialize, Serialize}; + use crate::{ context::Context, error::{RuntimeError, ValidationError}, @@ -5,7 +7,7 @@ use crate::{ use super::{AbstractNode, Action, Expression, Type, ValueNode, WithPosition}; -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct ListIndex { left: Expression, right: Expression, diff --git a/dust-lang/src/abstract_tree/logic.rs b/dust-lang/src/abstract_tree/logic.rs index 1f1985f..d88215b 100644 --- a/dust-lang/src/abstract_tree/logic.rs +++ b/dust-lang/src/abstract_tree/logic.rs @@ -1,3 +1,5 @@ +use serde::{Deserialize, Serialize}; + use crate::{ context::Context, error::{RuntimeError, ValidationError}, @@ -7,7 +9,7 @@ use crate::{ use super::{AbstractNode, Action, Expression, Type}; -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub enum Logic { Equal(Expression, Expression), NotEqual(Expression, Expression), diff --git a/dust-lang/src/abstract_tree/loop.rs b/dust-lang/src/abstract_tree/loop.rs index 506fa44..69f750a 100644 --- a/dust-lang/src/abstract_tree/loop.rs +++ b/dust-lang/src/abstract_tree/loop.rs @@ -1,3 +1,5 @@ +use serde::{Deserialize, Serialize}; + use crate::{ context::Context, error::{RuntimeError, ValidationError}, @@ -5,7 +7,7 @@ use crate::{ use super::{AbstractNode, Action, Statement, Type}; -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct Loop { statements: Vec, } diff --git a/dust-lang/src/abstract_tree/map_index.rs b/dust-lang/src/abstract_tree/map_index.rs index ce933a3..e4ee6c3 100644 --- a/dust-lang/src/abstract_tree/map_index.rs +++ b/dust-lang/src/abstract_tree/map_index.rs @@ -1,3 +1,5 @@ +use serde::{Deserialize, Serialize}; + use crate::{ context::Context, error::{RuntimeError, ValidationError}, @@ -6,7 +8,7 @@ use crate::{ use super::{AbstractNode, Action, Expression, Type, ValueNode, WithPosition}; -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct MapIndex { collection: Expression, index: Expression, diff --git a/dust-lang/src/abstract_tree/math.rs b/dust-lang/src/abstract_tree/math.rs index dde5079..ccb77a6 100644 --- a/dust-lang/src/abstract_tree/math.rs +++ b/dust-lang/src/abstract_tree/math.rs @@ -1,3 +1,5 @@ +use serde::{Deserialize, Serialize}; + use crate::{ context::Context, error::{RuntimeError, ValidationError}, @@ -7,7 +9,7 @@ use crate::{ use super::{AbstractNode, Action, Expression, SourcePosition, Type}; -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub enum Math { Add(Expression, Expression), Subtract(Expression, Expression), diff --git a/dust-lang/src/abstract_tree/mod.rs b/dust-lang/src/abstract_tree/mod.rs index fcdb0ad..49818a0 100644 --- a/dust-lang/src/abstract_tree/mod.rs +++ b/dust-lang/src/abstract_tree/mod.rs @@ -20,6 +20,7 @@ pub mod r#while; use std::{cmp::Ordering, ops::Index}; use chumsky::span::{SimpleSpan, Span}; +use serde::{Deserialize, Serialize}; pub use self::{ assignment::{Assignment, AssignmentOperator}, @@ -48,7 +49,7 @@ use crate::{ Value, }; -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct WithPosition { pub item: T, pub position: SourcePosition, @@ -65,7 +66,7 @@ pub trait WithPos: Sized { impl WithPos for T {} -#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct SourcePosition(pub usize, pub usize); impl From for SourcePosition { @@ -87,6 +88,7 @@ pub enum Action { None, } +#[derive(Debug, Clone)] pub struct AbstractTree(Vec); impl AbstractTree { diff --git a/dust-lang/src/abstract_tree/statement.rs b/dust-lang/src/abstract_tree/statement.rs index cd23513..49a5b63 100644 --- a/dust-lang/src/abstract_tree/statement.rs +++ b/dust-lang/src/abstract_tree/statement.rs @@ -1,3 +1,5 @@ +use serde::{Deserialize, Serialize}; + use crate::{ context::Context, error::{RuntimeError, ValidationError}, @@ -8,7 +10,7 @@ use super::{ StructureDefinition, Type, While, WithPosition, }; -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub enum Statement { Assignment(WithPosition), AsyncBlock(WithPosition), diff --git a/dust-lang/src/abstract_tree/structure_definition.rs b/dust-lang/src/abstract_tree/structure_definition.rs index 366a454..2c2629c 100644 --- a/dust-lang/src/abstract_tree/structure_definition.rs +++ b/dust-lang/src/abstract_tree/structure_definition.rs @@ -1,3 +1,5 @@ +use serde::{Deserialize, Serialize}; + use crate::{ context::Context, error::{RuntimeError, ValidationError}, @@ -6,7 +8,7 @@ use crate::{ use super::{AbstractNode, Action, Type, WithPosition}; -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct StructureDefinition { name: Identifier, fields: Vec<(Identifier, WithPosition)>, diff --git a/dust-lang/src/abstract_tree/type.rs b/dust-lang/src/abstract_tree/type.rs index a13c342..e737ea3 100644 --- a/dust-lang/src/abstract_tree/type.rs +++ b/dust-lang/src/abstract_tree/type.rs @@ -1,6 +1,7 @@ use std::fmt::{self, Display, Formatter}; use clap::error::Result; +use serde::{Deserialize, Serialize}; use crate::{ context::Context, @@ -10,7 +11,7 @@ use crate::{ use super::{AbstractNode, Action, WithPosition}; -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub enum Type { Any, Argument(Identifier), diff --git a/dust-lang/src/abstract_tree/value_node.rs b/dust-lang/src/abstract_tree/value_node.rs index 760bf86..eef0896 100644 --- a/dust-lang/src/abstract_tree/value_node.rs +++ b/dust-lang/src/abstract_tree/value_node.rs @@ -1,5 +1,7 @@ use std::{cmp::Ordering, collections::BTreeMap, ops::Range}; +use serde::{Deserialize, Serialize}; + use crate::{ context::Context, error::{RuntimeError, ValidationError}, @@ -9,7 +11,7 @@ use crate::{ use super::{AbstractNode, Action, Block, Expression, Type, WithPos, WithPosition}; -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum ValueNode { Boolean(bool), Float(f64), diff --git a/dust-lang/src/abstract_tree/while.rs b/dust-lang/src/abstract_tree/while.rs index 1f9a3e5..439c631 100644 --- a/dust-lang/src/abstract_tree/while.rs +++ b/dust-lang/src/abstract_tree/while.rs @@ -1,3 +1,5 @@ +use serde::{Deserialize, Serialize}; + use crate::{ context::Context, error::{RuntimeError, ValidationError}, @@ -7,7 +9,7 @@ use crate::{ use super::{AbstractNode, Action, Expression, Statement, Type}; -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct While { expression: Expression, statements: Vec, diff --git a/dust-lang/src/error.rs b/dust-lang/src/error.rs index 649944d..c85d175 100644 --- a/dust-lang/src/error.rs +++ b/dust-lang/src/error.rs @@ -55,6 +55,7 @@ pub enum RuntimeError { Io(io::Error), RwLockPoison(RwLockPoisonError), ValidationFailure(ValidationError), + SerdeJson(serde_json::Error), } impl From for RuntimeError { @@ -81,6 +82,12 @@ impl From for RuntimeError { } } +impl From for RuntimeError { + fn from(error: serde_json::Error) -> Self { + RuntimeError::SerdeJson(error) + } +} + impl PartialEq for RuntimeError { fn eq(&self, other: &Self) -> bool { match (self, other) { @@ -106,6 +113,10 @@ pub enum ValidationError { index_type: Type, index_position: SourcePosition, }, + ExpectedString { + actual: Type, + position: SourcePosition, + }, ExpectedBoolean { actual: Type, position: SourcePosition, diff --git a/dust-lang/src/identifier.rs b/dust-lang/src/identifier.rs index fbd52f4..d0eb837 100644 --- a/dust-lang/src/identifier.rs +++ b/dust-lang/src/identifier.rs @@ -3,6 +3,8 @@ use std::{ sync::Arc, }; +use serde::{de::Visitor, Deserialize, Serialize}; + #[derive(Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)] pub struct Identifier(Arc); @@ -21,3 +23,33 @@ impl Display for Identifier { write!(f, "{}", self.0) } } + +impl Serialize for Identifier { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(self.0.as_str()) + } +} + +impl<'de> Deserialize<'de> for Identifier { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + Ok(Identifier(Arc::new( + deserializer.deserialize_string(StringVisitor)?, + ))) + } +} + +struct StringVisitor; + +impl<'de> Visitor<'de> for StringVisitor { + type Value = String; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a UTF-8 string") + } +} diff --git a/dust-lang/src/lexer.rs b/dust-lang/src/lexer.rs index dd4d849..a50e3d9 100644 --- a/dust-lang/src/lexer.rs +++ b/dust-lang/src/lexer.rs @@ -48,6 +48,8 @@ pub enum Keyword { Fn, Int, If, + JsonParse, + Length, List, Map, None, @@ -85,6 +87,8 @@ impl Display for Keyword { Keyword::Loop => write!(f, "loop"), Keyword::While => write!(f, "while"), Keyword::Type => write!(f, "type"), + Keyword::JsonParse => write!(f, "JSON_PARSE"), + Keyword::Length => write!(f, "LENGTH"), Keyword::ReadFile => write!(f, "READ_FILE"), Keyword::ReadLine => write!(f, "READ_LINE"), Keyword::Sleep => write!(f, "SLEEP"), @@ -278,6 +282,8 @@ pub fn lexer<'src>() -> impl Parser< "type" => Token::Keyword(Keyword::Type), "loop" => Token::Keyword(Keyword::Loop), "while" => Token::Keyword(Keyword::While), + "JSON_PARSE" => Token::Keyword(Keyword::JsonParse), + "LENGTH" => Token::Keyword(Keyword::Length), "READ_FILE" => Token::Keyword(Keyword::ReadFile), "READ_LINE" => Token::Keyword(Keyword::ReadLine), "SLEEP" => Token::Keyword(Keyword::Sleep), diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index ac3c5f8..14591db 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -17,7 +17,7 @@ use ariadne::{Color, Fmt, Label, Report, ReportKind}; use chumsky::prelude::*; use context::Context; use error::{Error, RuntimeError, TypeConflict, ValidationError}; -use lexer::lex; +use lexer::{lex, Token}; use parser::{parse, parser}; use rayon::prelude::*; pub use value::Value; @@ -51,15 +51,47 @@ impl<'a> Interpreter<'a> { } } + pub fn lex<'source>( + &mut self, + source_id: Arc, + source: &'source str, + ) -> Result>, InterpreterError> { + let mut sources = self.sources.write().unwrap(); + + sources.clear(); + sources.push((source_id.clone(), Arc::from(source))); + + lex(source.as_ref()) + .map(|tokens| tokens.into_iter().map(|(token, _)| token).collect()) + .map_err(|errors| InterpreterError { source_id, errors }) + } + + pub fn parse<'source>( + &mut self, + source_id: Arc, + source: &'source str, + ) -> Result { + let mut sources = self.sources.write().unwrap(); + + sources.clear(); + sources.push((source_id.clone(), Arc::from(source))); + + parse(&lex(source).map_err(|errors| InterpreterError { + source_id: source_id.clone(), + errors, + })?) + .map_err(|errors| InterpreterError { source_id, errors }) + } + pub fn run( &mut self, source_id: Arc, source: Arc, ) -> Result, InterpreterError> { - self.sources - .write() - .unwrap() - .push((source_id.clone(), source.clone())); + let mut sources = self.sources.write().unwrap(); + + sources.clear(); + sources.push((source_id.clone(), source.clone())); let tokens = lex(source.as_ref()).map_err(|errors| InterpreterError { source_id: source_id.clone(), @@ -77,7 +109,11 @@ impl<'a> Interpreter<'a> { } pub fn load_std(&mut self) -> Result<(), InterpreterError> { - let std_sources: [(Arc, Arc); 3] = [ + let std_sources: [(Arc, Arc); 4] = [ + ( + Arc::from("std/core.ds"), + Arc::from(include_str!("../../std/core.ds")), + ), ( Arc::from("std/fs.ds"), Arc::from(include_str!("../../std/fs.ds")), @@ -363,6 +399,7 @@ impl InterpreterError { .with_message(format!("This has type {}.", actual.fg(type_color),)), ) } + ValidationError::ExpectedString { actual, position } => todo!(), } } let report = builder.finish(); diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index d28f2cb..70bc1f2 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -263,6 +263,14 @@ pub fn parser<'src>( ); let built_in_function_call = choice(( + just(Token::Keyword(Keyword::Length)) + .ignore_then(expression.clone()) + .map_with(|argument, state| { + Expression::BuiltInFunctionCall( + Box::new(BuiltInFunctionCall::Length(argument)) + .with_position(state.span()), + ) + }), just(Token::Keyword(Keyword::ReadFile)) .ignore_then(expression.clone()) .map_with(|argument, state| { @@ -292,6 +300,15 @@ pub fn parser<'src>( .with_position(state.span()), ) }), + just(Token::Keyword(Keyword::JsonParse)) + .ignore_then(r#type.clone()) + .then(expression.clone()) + .map_with(|(r#type, argument), state| { + Expression::BuiltInFunctionCall( + Box::new(BuiltInFunctionCall::JsonParse(r#type, argument)) + .with_position(state.span()), + ) + }), )) .try_map_with(move |expression, state| { if allow_built_ins { diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index 8de39a2..3c21e45 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -6,6 +6,8 @@ use std::{ sync::Arc, }; +use serde::{Deserialize, Serialize}; + use crate::{ abstract_tree::{AbstractNode, Action, Block, Type, WithPos, WithPosition}, context::Context, @@ -181,7 +183,25 @@ impl Ord for Value { } } -#[derive(Clone, Debug, PartialEq)] +impl Serialize for Value { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + todo!() + } +} + +impl<'de> Deserialize<'de> for Value { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + todo!() + } +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum ValueInner { Boolean(bool), Float(f64), @@ -299,7 +319,7 @@ impl Ord for ValueInner { } } -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct Function { type_parameters: Vec>, parameters: Vec<(Identifier, WithPosition)>, diff --git a/dust-shell/src/main.rs b/dust-shell/src/main.rs index 09fdfad..ad3b548 100644 --- a/dust-shell/src/main.rs +++ b/dust-shell/src/main.rs @@ -23,6 +23,14 @@ struct Args { #[arg(short, long)] command: Option, + // Display lexer tokens of the input source. + #[arg(short, long)] + lex: bool, + + // Display abstract tree of the input source. + #[arg(short, long)] + parse: bool, + #[arg(long)] no_std: bool, @@ -68,6 +76,36 @@ fn main() { return; }; + if args.lex { + match interpreter.lex(source_id, source.as_ref()) { + Ok(tokens) => println!("{tokens:?}"), + Err(error) => { + for report in error.build_reports() { + report + .write_for_stdout(sources(interpreter.sources()), stderr()) + .unwrap(); + } + } + } + + return; + } + + if args.parse { + match interpreter.parse(source_id, source.as_ref()) { + Ok(abstract_tree) => println!("{abstract_tree:?}"), + Err(error) => { + for report in error.build_reports() { + report + .write_for_stdout(sources(interpreter.sources()), stderr()) + .unwrap(); + } + } + } + + return; + } + let run_result = interpreter.run(source_id.clone(), source.clone()); match run_result { diff --git a/examples/json_length.ds b/examples/json_length.ds index 15ee421..e82e2c2 100644 --- a/examples/json_length.ds +++ b/examples/json_length.ds @@ -6,4 +6,4 @@ input = fs.read_file('examples/assets/data.json') data = json.parse(input) -list.length(data) +length(data) diff --git a/std/core.ds b/std/core.ds new file mode 100644 index 0000000..e57fd24 --- /dev/null +++ b/std/core.ds @@ -0,0 +1,3 @@ +length = fn (input: list) int { + LENGTH input +} diff --git a/std/json.ds b/std/json.ds new file mode 100644 index 0000000..47cd9e7 --- /dev/null +++ b/std/json.ds @@ -0,0 +1,5 @@ +json = { + parse = fn (T)(input: str) T { + JSON_PARSE T input + } +}