Implement property access

This commit is contained in:
Jeff 2024-08-05 14:31:08 -04:00
parent b81c65629b
commit 80a7700d68
7 changed files with 128 additions and 3 deletions

View File

@ -22,6 +22,7 @@ pub enum Statement {
// Expressions
Add(Box<Node>, Box<Node>),
PropertyAccess(Box<Node>, Box<Node>),
List(Vec<Node>),
Multiply(Box<Node>, Box<Node>),

View File

@ -1,4 +1,4 @@
use crate::{Node, Span, Statement};
use crate::{Node, Statement};
pub fn analyze(abstract_tree: Vec<Node>) -> Result<(), AnalyzerError> {
let analyzer = Analyzer::new(abstract_tree);
@ -55,6 +55,17 @@ impl Analyzer {
self.analyze_node(&left)?;
self.analyze_node(&right)?;
}
Statement::PropertyAccess(left, right) => {
if let Statement::Identifier(_) = &left.statement {
// Identifier is in the correct position
} else {
return Err(AnalyzerError::ExpectedIdentifier {
actual: left.as_ref().clone(),
});
}
self.analyze_node(&right)?;
}
}
Ok(())

View File

@ -90,6 +90,10 @@ impl<'a> Lexer<'a> {
self.position += 1;
(Token::Comma, (self.position - 1, self.position))
}
'.' => {
self.position += 1;
(Token::Dot, (self.position - 1, self.position))
}
_ => (Token::Eof, (self.position, self.position)),
}
} else {
@ -122,12 +126,12 @@ impl<'a> Lexer<'a> {
while let Some(c) = self.peek_char() {
if c == '.' {
is_float = true;
self.next_char();
while let Some(c) = self.peek_char() {
if c.is_ascii_digit() {
is_float = true;
self.next_char();
} else {
break;

View File

@ -85,6 +85,17 @@ impl<'src> Parser<'src> {
(left_start, right_end),
));
}
(Token::Dot, _) => {
self.next_token()?;
let right_node = self.parse_node(self.current_precedence())?;
let right_end = right_node.span.1;
return Ok(Node::new(
Statement::PropertyAccess(Box::new(left_node), Box::new(right_node)),
(left_start, right_end),
));
}
_ => {}
}
}
@ -165,6 +176,7 @@ impl<'src> Parser<'src> {
fn current_precedence(&self) -> u8 {
match self.current.0 {
Token::Dot => 4,
Token::Equal => 3,
Token::Plus => 1,
Token::Star => 2,
@ -193,6 +205,29 @@ mod tests {
use super::*;
#[test]
fn property_access() {
let input = "a.b";
assert_eq!(
parse(input),
Ok([Node::new(
Statement::PropertyAccess(
Box::new(Node::new(
Statement::Identifier(Identifier::new("a")),
(0, 1)
)),
Box::new(Node::new(
Statement::Identifier(Identifier::new("b")),
(2, 3)
)),
),
(0, 3),
)]
.into())
);
}
#[test]
fn complex_list() {
let input = "[1, 1 + 1, 2 + (4 * 10)]";

View File

@ -3,6 +3,7 @@ use crate::Identifier;
#[derive(Debug, PartialEq, Clone)]
pub enum Token {
Comma,
Dot,
Eof,
Equal,
Identifier(Identifier),

View File

@ -88,6 +88,37 @@ 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!(),
}
}
}
impl Display for Value {
@ -474,6 +505,18 @@ impl ValueInner {
}
}
pub trait ValueProperties<'a> {}
pub struct IntegerProperties<'a>(&'a Value);
impl<'a> IntegerProperties<'a> {
pub fn is_even(&self) -> bool {
self.0.as_integer().unwrap() % 2 == 0
}
}
impl<'a> ValueProperties<'a> for IntegerProperties<'a> {}
impl Eq for ValueInner {}
impl PartialOrd for ValueInner {
@ -516,4 +559,5 @@ impl Ord for ValueInner {
#[derive(Clone, Debug, PartialEq)]
pub enum ValueError {
CannotAdd(Value, Value),
PropertyNotFound { value: Value, property: Identifier },
}

View File

@ -100,6 +100,28 @@ impl Vm {
Ok(Some(Value::list(values)))
}
Statement::Multiply(_, _) => todo!(),
Statement::PropertyAccess(left, right) => {
let left_span = left.span;
let left = if let Some(value) = self.run_node(*left, variables)? {
value
} else {
return Err(VmError::ExpectedValue {
position: left_span,
});
};
let right_span = right.span;
let right = if let Statement::Identifier(identifier) = &right.statement {
identifier
} else {
return Err(VmError::ExpectedValue {
position: right_span,
});
};
let value = left.property_access(right)?;
Ok(Some(value))
}
}
}
}
@ -130,6 +152,13 @@ impl From<ValueError> for VmError {
mod tests {
use super::*;
#[test]
fn property_access() {
let input = "[1, 2, 3].length";
assert_eq!(run(input, &mut HashMap::new()), Ok(Some(Value::integer(3))));
}
#[test]
fn add() {
let input = "1 + 2";