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::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,15 +328,28 @@ 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::ExpectedValue {
|
return Err(AnalyzerError::ExpectedInteger {
|
||||||
actual: left.as_ref().clone(),
|
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)?;
|
self.analyze_node(left)?;
|
||||||
self.analyze_node(right)?;
|
self.analyze_node(right)?;
|
||||||
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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(
|
|
||||||
Box::new(Node::new(Statement::Constant(Value::integer(42)), (0, 2))),
|
|
||||||
Box::new(Node::new(
|
|
||||||
Statement::BuiltInFunctionCall {
|
Statement::BuiltInFunctionCall {
|
||||||
function: BuiltInFunction::IsEven,
|
function: BuiltInFunction::IsEven,
|
||||||
type_arguments: None,
|
type_arguments: None,
|
||||||
value_arguments: None
|
value_arguments: Some(vec![Node::new(
|
||||||
|
Statement::Constant(Value::integer(42)),
|
||||||
|
(0, 2)
|
||||||
|
)])
|
||||||
},
|
},
|
||||||
(3, 10)
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
(0, 10),
|
(0, 10),
|
||||||
)]
|
)]
|
||||||
.into()
|
.into()
|
||||||
|
@ -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 {}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user