Begin adding support for more built-in properties
This commit is contained in:
parent
8c5ac0b89e
commit
6983d282d8
@ -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),
|
||||
}
|
||||
|
@ -66,6 +66,7 @@ impl Analyzer {
|
||||
|
||||
self.analyze_node(&right)?;
|
||||
}
|
||||
Statement::ReservedIdentifier(_) => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -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<Vec<(Token, Span)>, 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<ParseIntError> 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 = "[]";
|
||||
|
@ -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};
|
||||
|
||||
|
@ -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())),
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -88,60 +88,6 @@ impl Value {
|
||||
_ => 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 {
|
||||
|
@ -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<Identifier, Value>,
|
||||
) -> Result<Option<Value>, 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<ParseError> for VmError {
|
||||
@ -160,9 +192,29 @@ impl From<ValueError> 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))));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user