Implement property access
This commit is contained in:
parent
b81c65629b
commit
80a7700d68
@ -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>),
|
||||
|
||||
|
@ -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(())
|
||||
|
@ -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;
|
||||
|
@ -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)]";
|
||||
|
@ -3,6 +3,7 @@ use crate::Identifier;
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum Token {
|
||||
Comma,
|
||||
Dot,
|
||||
Eof,
|
||||
Equal,
|
||||
Identifier(Identifier),
|
||||
|
@ -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 },
|
||||
}
|
||||
|
@ -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";
|
||||
|
Loading…
Reference in New Issue
Block a user