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::FunctionCall { function, .. } => {
if let Statement::Identifier(_) = &function.inner {
// Function is in the correct position
} else {
return Err(AnalyzerError::ExpectedIdentifier {
actual: function.as_ref().clone(),
position: function.position,
});
Statement::FunctionCall {
function,
value_arguments,
..
} => {
self.analyze_node(function)?;
if let Some(arguments) = value_arguments {
for argument in arguments {
self.analyze_node(argument)?;
}
}
}
Statement::Identifier(identifier) => {
@ -325,14 +328,27 @@ impl<'a> Analyzer<'a> {
self.analyze_node(node)?;
}
Statement::PropertyAccess(left, right) => {
if let Statement::Identifier(_) | Statement::Constant(_) | Statement::List(_) =
&left.inner
{
// Left side is valid
} else {
return Err(AnalyzerError::ExpectedValue {
actual: left.as_ref().clone(),
});
if let Some(Type::List { .. }) = left.inner.expected_type(self.context) {
if let Some(Type::Integer) = right.inner.expected_type(self.context) {
// Allow indexing lists with integers
} else {
return Err(AnalyzerError::ExpectedInteger {
actual: right.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)?;
@ -358,11 +374,18 @@ impl<'a> Analyzer<'a> {
#[derive(Clone, Debug, PartialEq)]
pub enum AnalyzerError {
ExpectedBoolean {
actual: Node<Statement>,
position: Span,
},
ExpectedIdentifier {
actual: Node<Statement>,
position: Span,
},
ExpectedBoolean {
ExpectedIdentifierOrString {
actual: Node<Statement>,
},
ExpectedInteger {
actual: Node<Statement>,
position: Span,
},
@ -395,6 +418,8 @@ impl AnalyzerError {
match self {
AnalyzerError::ExpectedBoolean { position, .. } => *position,
AnalyzerError::ExpectedIdentifier { position, .. } => *position,
AnalyzerError::ExpectedIdentifierOrString { actual } => actual.position,
AnalyzerError::ExpectedInteger { position, .. } => *position,
AnalyzerError::ExpectedValue { actual } => actual.position,
AnalyzerError::ExpectedValueArgumentCount { position, .. } => *position,
AnalyzerError::TypeConflict {
@ -418,6 +443,12 @@ impl Display for AnalyzerError {
AnalyzerError::ExpectedIdentifier { 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, .. } => {
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_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(
Statement::PropertyAccess(Box::new(left), Box::new(right)),
(left_start, right_end),
@ -1408,17 +1456,14 @@ mod tests {
parse(input),
Ok(AbstractSyntaxTree {
nodes: [Node::new(
Statement::PropertyAccess(
Box::new(Node::new(Statement::Constant(Value::integer(42)), (0, 2))),
Box::new(Node::new(
Statement::BuiltInFunctionCall {
function: BuiltInFunction::IsEven,
type_arguments: None,
value_arguments: None
},
(3, 10)
)),
),
Statement::BuiltInFunctionCall {
function: BuiltInFunction::IsEven,
type_arguments: None,
value_arguments: Some(vec![Node::new(
Statement::Constant(Value::integer(42)),
(0, 2)
)])
},
(0, 10),
)]
.into()

View File

@ -93,6 +93,10 @@ impl Value {
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> {
if let ValueInner::Boolean(boolean) = self.0.as_ref() {
Some(*boolean)
@ -626,7 +630,7 @@ pub enum ValueInner {
}
impl ValueInner {
pub fn r#type(&self, context: &Context) -> Type {
fn r#type(&self, context: &Context) -> Type {
match self {
ValueInner::Boolean(_) => Type::Boolean,
ValueInner::Float(_) => Type::Float,
@ -658,6 +662,20 @@ impl ValueInner {
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 {}

View File

@ -6,7 +6,8 @@ use std::{
use crate::{
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>(
@ -536,6 +537,12 @@ pub enum VmError {
UndefinedVariable {
identifier: Node<Statement>,
},
UndefinedProperty {
value: Value,
value_position: Span,
property: Identifier,
property_position: Span,
},
}
impl VmError {
@ -552,6 +559,9 @@ impl VmError {
Self::ExpectedList { position } => *position,
Self::ExpectedValue { position } => *position,
Self::UndefinedVariable { identifier } => identifier.position,
Self::UndefinedProperty {
property_position, ..
} => *property_position,
}
}
}
@ -602,6 +612,11 @@ impl Display for VmError {
Self::UndefinedVariable { identifier } => {
write!(f, "Undefined identifier: {}", identifier)
}
Self::UndefinedProperty {
value, property, ..
} => {
write!(f, "Value {} does not have the property {}", value, property)
}
}
}
}