Begin adding support for more built-in properties

This commit is contained in:
Jeff 2024-08-05 15:54:48 -04:00
parent 8c5ac0b89e
commit 6983d282d8
8 changed files with 106 additions and 71 deletions

View File

@ -1,4 +1,4 @@
use crate::{Identifier, Span, Value}; use crate::{Identifier, ReservedIdentifier, Span, Value};
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct Node { pub struct Node {
@ -29,4 +29,5 @@ pub enum Statement {
// Hard-coded values // Hard-coded values
Constant(Value), Constant(Value),
Identifier(Identifier), Identifier(Identifier),
ReservedIdentifier(ReservedIdentifier),
} }

View File

@ -66,6 +66,7 @@ impl Analyzer {
self.analyze_node(&right)?; self.analyze_node(&right)?;
} }
Statement::ReservedIdentifier(_) => {}
} }
Ok(()) Ok(())

View File

@ -5,7 +5,7 @@
//! - [`Lexer`], which lexes the input a token at a time //! - [`Lexer`], which lexes the input a token at a time
use std::num::{ParseFloatError, ParseIntError}; 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. /// Lex the input and return a vector of tokens and their positions.
pub fn lex(input: &str) -> Result<Vec<(Token, Span)>, LexError> { pub fn lex(input: &str) -> Result<Vec<(Token, Span)>, LexError> {
@ -169,8 +169,11 @@ impl<'a> Lexer<'a> {
} }
} }
let identifier = &self.source[start_pos..self.position]; let string = &self.source[start_pos..self.position];
let token = Token::Identifier(Identifier::new(identifier)); let token = match string {
"length" => Token::ReservedIdentifier(ReservedIdentifier::Length),
_ => Token::Identifier(Identifier::new(string)),
};
Ok((token, (start_pos, self.position))) Ok((token, (start_pos, self.position)))
} }
@ -198,6 +201,22 @@ impl From<ParseIntError> for LexError {
mod tests { mod tests {
use super::*; 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] #[test]
fn square_braces() { fn square_braces() {
let input = "[]"; let input = "[]";

View File

@ -21,7 +21,7 @@ pub use identifier::Identifier;
pub use lex::{lex, LexError, Lexer}; pub use lex::{lex, LexError, Lexer};
pub use parse::{parse, ParseError, Parser}; pub use parse::{parse, ParseError, Parser};
pub use r#type::Type; pub use r#type::Type;
pub use token::Token; pub use token::{ReservedIdentifier, Token};
pub use value::{Value, ValueError}; pub use value::{Value, ValueError};
pub use vm::{run, Vm, VmError}; pub use vm::{run, Vm, VmError};

View File

@ -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())), _ => Err(ParseError::UnexpectedToken(self.current.0.clone())),
} }
} }

View File

@ -7,6 +7,7 @@ pub enum Token {
Eof, Eof,
Equal, Equal,
Identifier(Identifier), Identifier(Identifier),
ReservedIdentifier(ReservedIdentifier),
Integer(i64), Integer(i64),
Plus, Plus,
Star, Star,
@ -16,3 +17,10 @@ pub enum Token {
RightSquareBrace, RightSquareBrace,
Float(f64), Float(f64),
} }
#[derive(Debug, PartialEq, Clone)]
pub enum ReservedIdentifier {
IsEven,
IsOdd,
Length,
}

View File

@ -88,60 +88,6 @@ impl Value {
_ => Err(ValueError::CannotAdd(self.clone(), other.clone())), _ => Err(ValueError::CannotAdd(self.clone(), other.clone())),
} }
} }
pub fn property_access(&self, property: &Identifier) -> Result<Value, ValueError> {
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<Value, ValueError> {
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 { impl Display for Value {

View File

@ -1,6 +1,8 @@
use std::collections::{HashMap, VecDeque}; 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( pub fn run(
input: &str, input: &str,
@ -40,6 +42,9 @@ impl Vm {
variables: &mut HashMap<Identifier, Value>, variables: &mut HashMap<Identifier, Value>,
) -> Result<Option<Value>, VmError> { ) -> Result<Option<Value>, VmError> {
match node.statement { match node.statement {
Statement::Constant(value) => Ok(Some(value.clone())),
Statement::Identifier(_) => Ok(None),
Statement::ReservedIdentifier(_) => Ok(None),
Statement::Add(left, right) => { Statement::Add(left, right) => {
let left_span = left.span; let left_span = left.span;
let left = if let Some(value) = self.run_node(*left, variables)? { let left = if let Some(value) = self.run_node(*left, variables)? {
@ -82,8 +87,6 @@ impl Vm {
Ok(None) Ok(None)
} }
Statement::Constant(value) => Ok(Some(value.clone())),
Statement::Identifier(_) => Ok(None),
Statement::List(nodes) => { Statement::List(nodes) => {
let values = nodes let values = nodes
.into_iter() .into_iter()
@ -111,17 +114,44 @@ impl Vm {
}; };
let right_span = right.span; let right_span = right.span;
if let Statement::Identifier(identifier) = &right.statement { if let Statement::ReservedIdentifier(reserved) = &right.statement {
let value = left.property_access(identifier)?; match reserved {
ReservedIdentifier::IsEven => {
return Ok(Some(value)); 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() { 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 // Anaylsis Failures
// These should be prevented by running the analyzer before the VM // These should be prevented by running the analyzer before the VM
ExpectedValue { position: Span }, ExpectedValue { position: Span },
ExpectedIdentifierOrInteger { position: (usize, usize) }, ExpectedIdentifierOrInteger { position: Span },
ExpectedList { position: Span },
ExpectedInteger { position: Span },
} }
impl From<ParseError> for VmError { impl From<ParseError> for VmError {
@ -160,9 +192,29 @@ impl From<ValueError> for VmError {
mod tests { mod tests {
use super::*; 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] #[test]
fn list_access() { 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)))); assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(2))));
} }