From 6983d282d8aea2af856b3267ae8a89bc11c037e4 Mon Sep 17 00:00:00 2001 From: Jeff Date: Mon, 5 Aug 2024 15:54:48 -0400 Subject: [PATCH] Begin adding support for more built-in properties --- dust-lang/src/abstract_tree.rs | 3 +- dust-lang/src/analyzer.rs | 1 + dust-lang/src/lex.rs | 25 +++++++++-- dust-lang/src/lib.rs | 2 +- dust-lang/src/parse.rs | 8 ++++ dust-lang/src/token.rs | 8 ++++ dust-lang/src/value.rs | 54 ------------------------ dust-lang/src/vm.rs | 76 ++++++++++++++++++++++++++++------ 8 files changed, 106 insertions(+), 71 deletions(-) diff --git a/dust-lang/src/abstract_tree.rs b/dust-lang/src/abstract_tree.rs index e47c45b..9a38ebf 100644 --- a/dust-lang/src/abstract_tree.rs +++ b/dust-lang/src/abstract_tree.rs @@ -1,4 +1,4 @@ -use crate::{Identifier, Span, Value}; +use crate::{Identifier, ReservedIdentifier, Span, Value}; #[derive(Debug, PartialEq, Clone)] pub struct Node { @@ -29,4 +29,5 @@ pub enum Statement { // Hard-coded values Constant(Value), Identifier(Identifier), + ReservedIdentifier(ReservedIdentifier), } diff --git a/dust-lang/src/analyzer.rs b/dust-lang/src/analyzer.rs index 184b3ab..a592a64 100644 --- a/dust-lang/src/analyzer.rs +++ b/dust-lang/src/analyzer.rs @@ -66,6 +66,7 @@ impl Analyzer { self.analyze_node(&right)?; } + Statement::ReservedIdentifier(_) => {} } Ok(()) diff --git a/dust-lang/src/lex.rs b/dust-lang/src/lex.rs index b562952..08a3009 100644 --- a/dust-lang/src/lex.rs +++ b/dust-lang/src/lex.rs @@ -5,7 +5,7 @@ //! - [`Lexer`], which lexes the input a token at a time use std::num::{ParseFloatError, ParseIntError}; -use crate::{Identifier, Span, Token}; +use crate::{Identifier, ReservedIdentifier, Span, Token}; /// Lex the input and return a vector of tokens and their positions. pub fn lex(input: &str) -> Result, LexError> { @@ -169,8 +169,11 @@ impl<'a> Lexer<'a> { } } - let identifier = &self.source[start_pos..self.position]; - let token = Token::Identifier(Identifier::new(identifier)); + let string = &self.source[start_pos..self.position]; + let token = match string { + "length" => Token::ReservedIdentifier(ReservedIdentifier::Length), + _ => Token::Identifier(Identifier::new(string)), + }; Ok((token, (start_pos, self.position))) } @@ -198,6 +201,22 @@ impl From for LexError { mod tests { use super::*; + #[test] + fn reserved_identifier() { + let input = "length"; + + assert_eq!( + lex(input), + Ok(vec![ + ( + Token::ReservedIdentifier(ReservedIdentifier::Length), + (0, 6) + ), + (Token::Eof, (6, 6)), + ]) + ) + } + #[test] fn square_braces() { let input = "[]"; diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index 6d7db60..f54bedd 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -21,7 +21,7 @@ pub use identifier::Identifier; pub use lex::{lex, LexError, Lexer}; pub use parse::{parse, ParseError, Parser}; pub use r#type::Type; -pub use token::Token; +pub use token::{ReservedIdentifier, Token}; pub use value::{Value, ValueError}; pub use vm::{run, Vm, VmError}; diff --git a/dust-lang/src/parse.rs b/dust-lang/src/parse.rs index bd194db..e213c27 100644 --- a/dust-lang/src/parse.rs +++ b/dust-lang/src/parse.rs @@ -170,6 +170,14 @@ impl<'src> Parser<'src> { } } } + (Token::ReservedIdentifier(reserved), _) => { + self.next_token()?; + + Ok(Node::new( + Statement::ReservedIdentifier(reserved), + self.current.1, + )) + } _ => Err(ParseError::UnexpectedToken(self.current.0.clone())), } } diff --git a/dust-lang/src/token.rs b/dust-lang/src/token.rs index fb18909..3bbe859 100644 --- a/dust-lang/src/token.rs +++ b/dust-lang/src/token.rs @@ -7,6 +7,7 @@ pub enum Token { Eof, Equal, Identifier(Identifier), + ReservedIdentifier(ReservedIdentifier), Integer(i64), Plus, Star, @@ -16,3 +17,10 @@ pub enum Token { RightSquareBrace, Float(f64), } + +#[derive(Debug, PartialEq, Clone)] +pub enum ReservedIdentifier { + IsEven, + IsOdd, + Length, +} diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index 9bfa14e..623f421 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -88,60 +88,6 @@ impl Value { _ => Err(ValueError::CannotAdd(self.clone(), other.clone())), } } - - pub fn property_access(&self, property: &Identifier) -> Result { - match self.inner().as_ref() { - ValueInner::Map(map) => { - if let Some(value) = map.get(property) { - Ok(value.clone()) - } else { - Err(ValueError::PropertyNotFound { - value: self.clone(), - property: property.clone(), - }) - } - } - ValueInner::Integer(integer) => match property.as_str() { - "is_even" => Ok(Value::boolean(integer % 2 == 0)), - "to_string" => Ok(Value::string(integer.to_string())), - _ => Err(ValueError::PropertyNotFound { - value: self.clone(), - property: property.clone(), - }), - }, - ValueInner::List(values) => match property.as_str() { - "length" => Ok(Value::integer(values.len() as i64)), - _ => Err(ValueError::PropertyNotFound { - value: self.clone(), - property: property.clone(), - }), - }, - _ => todo!(), - } - } - - pub fn list_access(&self, index: i64) -> Result { - match self.inner().as_ref() { - ValueInner::List(list) => { - if index < 0 { - return Err(ValueError::IndexOutOfBounds { - value: self.clone(), - index, - }); - } - - if let Some(value) = list.get(index as usize) { - Ok(value.clone()) - } else { - Err(ValueError::IndexOutOfBounds { - value: self.clone(), - index, - }) - } - } - _ => Err(ValueError::ExpectedList(self.clone())), - } - } } impl Display for Value { diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 1ad4315..d876e9d 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -1,6 +1,8 @@ use std::collections::{HashMap, VecDeque}; -use crate::{parse, Identifier, Node, ParseError, Span, Statement, Value, ValueError}; +use crate::{ + parse, Identifier, Node, ParseError, ReservedIdentifier, Span, Statement, Value, ValueError, +}; pub fn run( input: &str, @@ -40,6 +42,9 @@ impl Vm { variables: &mut HashMap, ) -> Result, VmError> { match node.statement { + Statement::Constant(value) => Ok(Some(value.clone())), + Statement::Identifier(_) => Ok(None), + Statement::ReservedIdentifier(_) => Ok(None), Statement::Add(left, right) => { let left_span = left.span; let left = if let Some(value) = self.run_node(*left, variables)? { @@ -82,8 +87,6 @@ impl Vm { Ok(None) } - Statement::Constant(value) => Ok(Some(value.clone())), - Statement::Identifier(_) => Ok(None), Statement::List(nodes) => { let values = nodes .into_iter() @@ -111,17 +114,44 @@ impl Vm { }; let right_span = right.span; - if let Statement::Identifier(identifier) = &right.statement { - let value = left.property_access(identifier)?; - - return Ok(Some(value)); + if let Statement::ReservedIdentifier(reserved) = &right.statement { + match reserved { + ReservedIdentifier::IsEven => { + if let Some(integer) = left.as_integer() { + return Ok(Some(Value::boolean(integer % 2 == 0))); + } else { + return Err(VmError::ExpectedInteger { + position: right_span, + }); + } + } + ReservedIdentifier::IsOdd => { + if let Some(integer) = left.as_integer() { + return Ok(Some(Value::boolean(integer % 2 != 0))); + } else { + return Err(VmError::ExpectedInteger { + position: right_span, + }); + } + } + ReservedIdentifier::Length => { + if let Some(list) = left.as_list() { + return Ok(Some(Value::integer(list.len() as i64))); + } else { + return Err(VmError::ExpectedList { + position: right_span, + }); + } + } + } } - if let Statement::Constant(value) = &right.statement { + if let (Some(list), Statement::Constant(value)) = (left.as_list(), &right.statement) + { if let Some(index) = value.as_integer() { - let value = left.list_access(index)?; + let value = list.get(index as usize).cloned(); - return Ok(Some(value)); + return Ok(value); } } @@ -141,7 +171,9 @@ pub enum VmError { // Anaylsis Failures // These should be prevented by running the analyzer before the VM ExpectedValue { position: Span }, - ExpectedIdentifierOrInteger { position: (usize, usize) }, + ExpectedIdentifierOrInteger { position: Span }, + ExpectedList { position: Span }, + ExpectedInteger { position: Span }, } impl From for VmError { @@ -160,9 +192,29 @@ impl From for VmError { mod tests { use super::*; + #[test] + fn is_even() { + let input = "42.is_even"; + + assert_eq!( + run(input, &mut HashMap::new()), + Ok(Some(Value::boolean(true))) + ); + } + + #[test] + fn is_odd() { + let input = "42.is_odd"; + + assert_eq!( + run(input, &mut HashMap::new()), + Ok(Some(Value::boolean(false))) + ); + } + #[test] fn list_access() { - let input = "[1, 2, 3][1]"; + let input = "[1, 2, 3].1"; assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(2)))); }