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 // Expressions
Add(Box<Node>, Box<Node>), Add(Box<Node>, Box<Node>),
PropertyAccess(Box<Node>, Box<Node>),
List(Vec<Node>), List(Vec<Node>),
Multiply(Box<Node>, Box<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> { pub fn analyze(abstract_tree: Vec<Node>) -> Result<(), AnalyzerError> {
let analyzer = Analyzer::new(abstract_tree); let analyzer = Analyzer::new(abstract_tree);
@ -55,6 +55,17 @@ impl Analyzer {
self.analyze_node(&left)?; self.analyze_node(&left)?;
self.analyze_node(&right)?; 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(()) Ok(())

View File

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

View File

@ -85,6 +85,17 @@ impl<'src> Parser<'src> {
(left_start, right_end), (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 { fn current_precedence(&self) -> u8 {
match self.current.0 { match self.current.0 {
Token::Dot => 4,
Token::Equal => 3, Token::Equal => 3,
Token::Plus => 1, Token::Plus => 1,
Token::Star => 2, Token::Star => 2,
@ -193,6 +205,29 @@ mod tests {
use super::*; 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] #[test]
fn complex_list() { fn complex_list() {
let input = "[1, 1 + 1, 2 + (4 * 10)]"; let input = "[1, 1 + 1, 2 + (4 * 10)]";

View File

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

View File

@ -88,6 +88,37 @@ 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!(),
}
}
} }
impl Display for Value { 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 Eq for ValueInner {}
impl PartialOrd for ValueInner { impl PartialOrd for ValueInner {
@ -516,4 +559,5 @@ impl Ord for ValueInner {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum ValueError { pub enum ValueError {
CannotAdd(Value, Value), CannotAdd(Value, Value),
PropertyNotFound { value: Value, property: Identifier },
} }

View File

@ -100,6 +100,28 @@ impl Vm {
Ok(Some(Value::list(values))) Ok(Some(Value::list(values)))
} }
Statement::Multiply(_, _) => todo!(), 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 { mod tests {
use super::*; 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] #[test]
fn add() { fn add() {
let input = "1 + 2"; let input = "1 + 2";