Implement property access
This commit is contained in:
parent
b81c65629b
commit
80a7700d68
@ -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>),
|
||||||
|
|
||||||
|
@ -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(())
|
||||||
|
@ -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;
|
||||||
|
@ -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)]";
|
||||||
|
@ -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),
|
||||||
|
@ -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 },
|
||||||
}
|
}
|
||||||
|
@ -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";
|
||||||
|
Loading…
Reference in New Issue
Block a user