diff --git a/src/abstract_tree/enum_definition.rs b/src/abstract_tree/enum_definition.rs new file mode 100644 index 0000000..c848a1f --- /dev/null +++ b/src/abstract_tree/enum_definition.rs @@ -0,0 +1,45 @@ +use super::{AbstractTree, Identifier, Type, WithPosition}; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct EnumDefinition { + name: Identifier, + type_parameters: Vec, + variants: Vec<(Identifier, Vec>)>, +} + +impl EnumDefinition { + pub fn new( + name: Identifier, + type_parameters: Vec, + variants: Vec<(Identifier, Vec>)>, + ) -> Self { + Self { + name, + type_parameters, + variants, + } + } +} + +impl AbstractTree for EnumDefinition { + fn expected_type( + &self, + _context: &crate::context::Context, + ) -> Result { + todo!() + } + + fn validate( + &self, + _context: &crate::context::Context, + ) -> Result<(), crate::error::ValidationError> { + todo!() + } + + fn run( + self, + _context: &crate::context::Context, + ) -> Result { + todo!() + } +} diff --git a/src/abstract_tree/mod.rs b/src/abstract_tree/mod.rs index ec99013..db79664 100644 --- a/src/abstract_tree/mod.rs +++ b/src/abstract_tree/mod.rs @@ -1,5 +1,6 @@ pub mod assignment; pub mod block; +pub mod enum_definition; pub mod expression; pub mod function_call; pub mod identifier; @@ -19,6 +20,7 @@ use chumsky::span::{SimpleSpan, Span}; pub use self::{ assignment::{Assignment, AssignmentOperator}, block::Block, + enum_definition::EnumDefinition, expression::Expression, function_call::FunctionCall, identifier::Identifier, diff --git a/src/abstract_tree/statement.rs b/src/abstract_tree/statement.rs index af8afd6..c0569e0 100644 --- a/src/abstract_tree/statement.rs +++ b/src/abstract_tree/statement.rs @@ -3,13 +3,16 @@ use crate::{ error::{RuntimeError, ValidationError}, }; -use super::{AbstractTree, Action, Assignment, Block, Expression, IfElse, Loop, Type, While}; +use super::{ + AbstractTree, Action, Assignment, Block, EnumDefinition, Expression, IfElse, Loop, Type, While, +}; #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum Statement { Assignment(Assignment), Block(Block), Break, + EnumDefinition(EnumDefinition), Expression(Expression), IfElse(IfElse), Loop(Loop), @@ -22,6 +25,7 @@ impl AbstractTree for Statement { Statement::Assignment(assignment) => assignment.expected_type(_context), Statement::Block(block) => block.expected_type(_context), Statement::Break => Ok(Type::None), + Statement::EnumDefinition(_) => todo!(), Statement::Expression(expression) => expression.expected_type(_context), Statement::IfElse(if_else) => if_else.expected_type(_context), Statement::Loop(r#loop) => r#loop.expected_type(_context), @@ -34,6 +38,7 @@ impl AbstractTree for Statement { Statement::Assignment(assignment) => assignment.validate(_context), Statement::Block(block) => block.validate(_context), Statement::Break => Ok(()), + Statement::EnumDefinition(_) => todo!(), Statement::Expression(expression) => expression.validate(_context), Statement::IfElse(if_else) => if_else.validate(_context), Statement::Loop(r#loop) => r#loop.validate(_context), @@ -46,6 +51,7 @@ impl AbstractTree for Statement { Statement::Assignment(assignment) => assignment.run(_context), Statement::Block(block) => block.run(_context), Statement::Break => Ok(Action::Break), + Statement::EnumDefinition(_) => todo!(), Statement::Expression(expression) => expression.run(_context), Statement::IfElse(if_else) => if_else.run(_context), Statement::Loop(r#loop) => r#loop.run(_context), diff --git a/src/lexer.rs b/src/lexer.rs index 3984828..64d7846 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -1,6 +1,6 @@ use std::fmt::{self, Display, Formatter}; -use chumsky::{prelude::*, text::whitespace}; +use chumsky::prelude::*; use crate::error::Error; @@ -217,6 +217,7 @@ pub fn lexer<'src>() -> impl Parser< just("bool").padded(), just("break").padded(), just("else").padded(), + just("enum").padded(), just("float").padded(), just("int").padded(), just("if").padded(), @@ -228,11 +229,10 @@ pub fn lexer<'src>() -> impl Parser< just("loop").padded(), just("while").padded(), )) - .delimited_by(whitespace(), whitespace()) .map(Token::Keyword); choice(( - boolean, float, integer, string, identifier, keyword, control, operator, + boolean, float, integer, string, keyword, identifier, control, operator, )) .map_with(|token, state| (token, state.span())) .padded() @@ -270,7 +270,7 @@ mod tests { #[test] fn keywords() { - assert_eq!(lex("int").unwrap()[0].0, Token::Keyword("int")) + assert_eq!(lex("int ").unwrap()[0].0, Token::Keyword("int")) } #[test] diff --git a/src/parser.rs b/src/parser.rs index 3f3f842..bd62239 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -424,7 +424,48 @@ pub fn parser<'src>() -> DustParser<'src> { .with_position(state.span()) }); + let enum_variant = identifier.clone().then( + r#type + .clone() + .separated_by(just(Token::Control(Control::Comma))) + .allow_trailing() + .collect() + .delimited_by( + just(Token::Control(Control::ParenOpen)), + just(Token::Control(Control::ParenClose)), + ), + ); + + let enum_definition = just(Token::Keyword("enum")) + .ignore_then(identifier.clone()) + .then( + identifier + .clone() + .separated_by(just(Token::Control(Control::Comma))) + .allow_trailing() + .collect() + .delimited_by( + just(Token::Control(Control::ParenOpen)), + just(Token::Control(Control::ParenClose)), + ), + ) + .then( + enum_variant + .separated_by(just(Token::Control(Control::Comma))) + .allow_trailing() + .collect() + .delimited_by( + just(Token::Control(Control::CurlyOpen)), + just(Token::Control(Control::CurlyClose)), + ), + ) + .map_with(|((name, type_parameters), variants), state| { + Statement::EnumDefinition(EnumDefinition::new(name, type_parameters, variants)) + .with_position(state.span()) + }); + choice(( + enum_definition, if_else, assignment, expression_statement, @@ -445,6 +486,37 @@ mod tests { use super::*; + #[test] + fn enum_definition() { + assert_eq!( + parse( + &lex(" + enum FooBar (F, B) { + Foo(F), + Bar(B), + } + ") + .unwrap() + ) + .unwrap()[0] + .node, + Statement::EnumDefinition(EnumDefinition::new( + Identifier::new("FooBar"), + vec![Identifier::new("F"), Identifier::new("B")], + vec![ + ( + Identifier::new("Foo"), + vec![Type::Custom(Identifier::new("F")).with_position((0, 0))], + ), + ( + Identifier::new("Bar"), + vec![Type::Custom(Identifier::new("B")).with_position((0, 0))] + ) + ] + )) + ); + } + #[test] fn map_index() { assert_eq!( diff --git a/src/value.rs b/src/value.rs index 6fff84e..01bd9e9 100644 --- a/src/value.rs +++ b/src/value.rs @@ -3,7 +3,6 @@ use std::{ collections::BTreeMap, fmt::{self, Display, Formatter}, io::stdin, - num::ParseIntError, ops::Range, sync::{Arc, OnceLock}, }; @@ -18,7 +17,7 @@ use stanza::{ use crate::{ abstract_tree::{AbstractTree, Action, Block, Identifier, Type, WithPosition}, context::Context, - error::{RuntimeError, ValidationError}, + error::RuntimeError, }; #[derive(Clone, Debug, PartialEq)] @@ -354,18 +353,15 @@ impl BuiltInFunction { let string = arguments.get(0).unwrap(); if let ValueInner::String(string) = string.inner().as_ref() { - // let integer = string.parse(); + let integer = if let Ok(integer) = string.parse() { + integer + } else { + todo!() + }; - todo!() - - // Ok(Action::Return(Value::integer(integer))) + Ok(Action::Return(Value::integer(integer))) } else { - Err(RuntimeError::ValidationFailure( - ValidationError::WrongArguments { - expected: vec![Type::String], - actual: arguments.iter().map(|value| value.r#type()).collect(), - }, - )) + todo!() } } BuiltInFunction::IntRandomRange => { @@ -409,24 +405,24 @@ impl Display for BuiltInFunction { static INT: OnceLock = OnceLock::new(); static IO: OnceLock = OnceLock::new(); -pub const BUILT_IN_MODULES: [BuiltInModule; 2] = [BuiltInModule::Integer, BuiltInModule::Io]; +pub const BUILT_IN_MODULES: [BuiltInModule; 2] = [BuiltInModule::Int, BuiltInModule::Io]; pub enum BuiltInModule { - Integer, + Int, Io, } impl BuiltInModule { pub fn name(&self) -> &'static str { match self { - BuiltInModule::Integer => "int", + BuiltInModule::Int => "int", BuiltInModule::Io => "io", } } pub fn value(self) -> Value { match self { - BuiltInModule::Integer => { + BuiltInModule::Int => { let mut properties = BTreeMap::new(); properties.insert( diff --git a/tests/functions.rs b/tests/functions.rs index ef54603..393d666 100644 --- a/tests/functions.rs +++ b/tests/functions.rs @@ -47,7 +47,7 @@ fn callback() { #[test] fn built_in_function_call() { - assert_eq!(interpret("output('Hiya')"), Ok(None)); + assert_eq!(interpret("io.write_line('Hiya')"), Ok(None)); } #[test]