Refactor function call dot notation; Add better analysis of function calls and property access

This commit is contained in:
Jeff 2024-08-11 21:37:44 -04:00
parent c0254e8a94
commit de30f241a8
4 changed files with 139 additions and 30 deletions

View File

@ -190,14 +190,17 @@ impl<'a> Analyzer<'a> {
} }
} }
Statement::Constant(_) => {} Statement::Constant(_) => {}
Statement::FunctionCall { function, .. } => { Statement::FunctionCall {
if let Statement::Identifier(_) = &function.inner { function,
// Function is in the correct position value_arguments,
} else { ..
return Err(AnalyzerError::ExpectedIdentifier { } => {
actual: function.as_ref().clone(), self.analyze_node(function)?;
position: function.position,
}); if let Some(arguments) = value_arguments {
for argument in arguments {
self.analyze_node(argument)?;
}
} }
} }
Statement::Identifier(identifier) => { Statement::Identifier(identifier) => {
@ -325,14 +328,27 @@ impl<'a> Analyzer<'a> {
self.analyze_node(node)?; self.analyze_node(node)?;
} }
Statement::PropertyAccess(left, right) => { Statement::PropertyAccess(left, right) => {
if let Statement::Identifier(_) | Statement::Constant(_) | Statement::List(_) = if let Some(Type::List { .. }) = left.inner.expected_type(self.context) {
&left.inner if let Some(Type::Integer) = right.inner.expected_type(self.context) {
{ // Allow indexing lists with integers
// Left side is valid } else {
} else { return Err(AnalyzerError::ExpectedInteger {
return Err(AnalyzerError::ExpectedValue { actual: right.as_ref().clone(),
actual: left.as_ref().clone(), position: right.position,
}); });
}
}
if let Some(Type::Map { .. }) = left.inner.expected_type(self.context) {
if let Some(Type::String) = right.inner.expected_type(self.context) {
// Allow indexing maps with strings
} else if let Statement::Identifier(_) = right.inner {
// Allow indexing maps with identifiers
} else {
return Err(AnalyzerError::ExpectedIdentifierOrString {
actual: right.as_ref().clone(),
});
}
} }
self.analyze_node(left)?; self.analyze_node(left)?;
@ -358,11 +374,18 @@ impl<'a> Analyzer<'a> {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum AnalyzerError { pub enum AnalyzerError {
ExpectedBoolean {
actual: Node<Statement>,
position: Span,
},
ExpectedIdentifier { ExpectedIdentifier {
actual: Node<Statement>, actual: Node<Statement>,
position: Span, position: Span,
}, },
ExpectedBoolean { ExpectedIdentifierOrString {
actual: Node<Statement>,
},
ExpectedInteger {
actual: Node<Statement>, actual: Node<Statement>,
position: Span, position: Span,
}, },
@ -395,6 +418,8 @@ impl AnalyzerError {
match self { match self {
AnalyzerError::ExpectedBoolean { position, .. } => *position, AnalyzerError::ExpectedBoolean { position, .. } => *position,
AnalyzerError::ExpectedIdentifier { position, .. } => *position, AnalyzerError::ExpectedIdentifier { position, .. } => *position,
AnalyzerError::ExpectedIdentifierOrString { actual } => actual.position,
AnalyzerError::ExpectedInteger { position, .. } => *position,
AnalyzerError::ExpectedValue { actual } => actual.position, AnalyzerError::ExpectedValue { actual } => actual.position,
AnalyzerError::ExpectedValueArgumentCount { position, .. } => *position, AnalyzerError::ExpectedValueArgumentCount { position, .. } => *position,
AnalyzerError::TypeConflict { AnalyzerError::TypeConflict {
@ -418,6 +443,12 @@ impl Display for AnalyzerError {
AnalyzerError::ExpectedIdentifier { actual, .. } => { AnalyzerError::ExpectedIdentifier { actual, .. } => {
write!(f, "Expected identifier, found {}", actual) write!(f, "Expected identifier, found {}", actual)
} }
AnalyzerError::ExpectedIdentifierOrString { actual } => {
write!(f, "Expected identifier or string, found {}", actual)
}
AnalyzerError::ExpectedInteger { actual, .. } => {
write!(f, "Expected integer, found {}", actual)
}
AnalyzerError::ExpectedValue { actual, .. } => { AnalyzerError::ExpectedValue { actual, .. } => {
write!(f, "Expected value, found {}", actual) write!(f, "Expected value, found {}", actual)
} }

View File

@ -561,6 +561,54 @@ impl<'src> Parser<'src> {
let right = self.parse_statement(Token::Dot.precedence() + 1)?; let right = self.parse_statement(Token::Dot.precedence() + 1)?;
let right_end = right.position.1; let right_end = right.position.1;
if let Statement::BuiltInFunctionCall {
function,
type_arguments,
value_arguments,
} = right.inner
{
let value_arguments = if let Some(mut arguments) = value_arguments {
arguments.insert(0, left);
Some(arguments)
} else {
Some(vec![left])
};
return Ok(Node::new(
Statement::BuiltInFunctionCall {
function,
type_arguments,
value_arguments,
},
(left_start, right_end),
));
}
if let Statement::FunctionCall {
function,
type_arguments,
value_arguments,
} = right.inner
{
let value_arguments = if let Some(mut arguments) = value_arguments {
arguments.insert(0, left);
Some(arguments)
} else {
Some(vec![left])
};
return Ok(Node::new(
Statement::FunctionCall {
function,
type_arguments,
value_arguments,
},
(left_start, right_end),
));
}
return Ok(Node::new( return Ok(Node::new(
Statement::PropertyAccess(Box::new(left), Box::new(right)), Statement::PropertyAccess(Box::new(left), Box::new(right)),
(left_start, right_end), (left_start, right_end),
@ -1408,17 +1456,14 @@ mod tests {
parse(input), parse(input),
Ok(AbstractSyntaxTree { Ok(AbstractSyntaxTree {
nodes: [Node::new( nodes: [Node::new(
Statement::PropertyAccess( Statement::BuiltInFunctionCall {
Box::new(Node::new(Statement::Constant(Value::integer(42)), (0, 2))), function: BuiltInFunction::IsEven,
Box::new(Node::new( type_arguments: None,
Statement::BuiltInFunctionCall { value_arguments: Some(vec![Node::new(
function: BuiltInFunction::IsEven, Statement::Constant(Value::integer(42)),
type_arguments: None, (0, 2)
value_arguments: None )])
}, },
(3, 10)
)),
),
(0, 10), (0, 10),
)] )]
.into() .into()

View File

@ -93,6 +93,10 @@ impl Value {
self.0.r#type(context) self.0.r#type(context)
} }
pub fn get_property(&self, property: &Identifier) -> Option<Value> {
self.0.get_property(property)
}
pub fn as_boolean(&self) -> Option<bool> { pub fn as_boolean(&self) -> Option<bool> {
if let ValueInner::Boolean(boolean) = self.0.as_ref() { if let ValueInner::Boolean(boolean) = self.0.as_ref() {
Some(*boolean) Some(*boolean)
@ -626,7 +630,7 @@ pub enum ValueInner {
} }
impl ValueInner { impl ValueInner {
pub fn r#type(&self, context: &Context) -> Type { fn r#type(&self, context: &Context) -> Type {
match self { match self {
ValueInner::Boolean(_) => Type::Boolean, ValueInner::Boolean(_) => Type::Boolean,
ValueInner::Float(_) => Type::Float, ValueInner::Float(_) => Type::Float,
@ -658,6 +662,20 @@ impl ValueInner {
ValueInner::String(_) => Type::String, ValueInner::String(_) => Type::String,
} }
} }
fn get_property(&self, property: &Identifier) -> Option<Value> {
match self {
ValueInner::List(list) => {
if property.as_str() == "length" {
Some(Value::integer(list.len() as i64))
} else {
None
}
}
ValueInner::Map(value_map) => value_map.get(property).cloned(),
_ => None,
}
}
} }
impl Eq for ValueInner {} impl Eq for ValueInner {}

View File

@ -6,7 +6,8 @@ use std::{
use crate::{ use crate::{
abstract_tree::BinaryOperator, parse, value::ValueInner, AbstractSyntaxTree, Analyzer, abstract_tree::BinaryOperator, parse, value::ValueInner, AbstractSyntaxTree, Analyzer,
BuiltInFunctionError, Context, DustError, Node, ParseError, Span, Statement, Value, ValueError, BuiltInFunctionError, Context, DustError, Identifier, Node, ParseError, Span, Statement, Value,
ValueError,
}; };
pub fn run<'src>( pub fn run<'src>(
@ -536,6 +537,12 @@ pub enum VmError {
UndefinedVariable { UndefinedVariable {
identifier: Node<Statement>, identifier: Node<Statement>,
}, },
UndefinedProperty {
value: Value,
value_position: Span,
property: Identifier,
property_position: Span,
},
} }
impl VmError { impl VmError {
@ -552,6 +559,9 @@ impl VmError {
Self::ExpectedList { position } => *position, Self::ExpectedList { position } => *position,
Self::ExpectedValue { position } => *position, Self::ExpectedValue { position } => *position,
Self::UndefinedVariable { identifier } => identifier.position, Self::UndefinedVariable { identifier } => identifier.position,
Self::UndefinedProperty {
property_position, ..
} => *property_position,
} }
} }
} }
@ -602,6 +612,11 @@ impl Display for VmError {
Self::UndefinedVariable { identifier } => { Self::UndefinedVariable { identifier } => {
write!(f, "Undefined identifier: {}", identifier) write!(f, "Undefined identifier: {}", identifier)
} }
Self::UndefinedProperty {
value, property, ..
} => {
write!(f, "Value {} does not have the property {}", value, property)
}
} }
} }
} }