Refactor function call dot notation; Add better analysis of function calls and property access
This commit is contained in:
parent
c0254e8a94
commit
de30f241a8
@ -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)
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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 {}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user