Accept type ascription within binary expressions (#6849)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
@ -18,10 +18,10 @@ use crate::{
|
||||
},
|
||||
modules::{ModuleId, ModulePath, ModuleRepr},
|
||||
parsing::ast::types::{
|
||||
Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem,
|
||||
CallExpressionKw, Expr, FunctionExpression, IfExpression, ImportPath, ImportSelector, ItemVisibility,
|
||||
LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Name, Node, NodeRef, ObjectExpression,
|
||||
PipeExpression, Program, TagDeclarator, Type, UnaryExpression, UnaryOperator,
|
||||
Annotation, ArrayExpression, ArrayRangeExpression, AscribedExpression, BinaryExpression, BinaryOperator,
|
||||
BinaryPart, BodyItem, CallExpressionKw, Expr, FunctionExpression, IfExpression, ImportPath, ImportSelector,
|
||||
ItemVisibility, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Name, Node, NodeRef,
|
||||
ObjectExpression, PipeExpression, Program, TagDeclarator, Type, UnaryExpression, UnaryOperator,
|
||||
},
|
||||
source_range::SourceRange,
|
||||
std::{
|
||||
@ -707,17 +707,25 @@ impl ExecutorContext {
|
||||
// TODO this lets us use the label as a variable name, but not as a tag in most cases
|
||||
result
|
||||
}
|
||||
Expr::AscribedExpression(expr) => {
|
||||
let result = self
|
||||
.execute_expr(&expr.expr, exec_state, metadata, &[], statement_kind)
|
||||
.await?;
|
||||
apply_ascription(&result, &expr.ty, exec_state, expr.into())?
|
||||
}
|
||||
Expr::AscribedExpression(expr) => expr.get_result(exec_state, self).await?,
|
||||
};
|
||||
Ok(item)
|
||||
}
|
||||
}
|
||||
|
||||
impl Node<AscribedExpression> {
|
||||
#[async_recursion]
|
||||
pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
|
||||
let metadata = Metadata {
|
||||
source_range: SourceRange::from(self),
|
||||
};
|
||||
let result = ctx
|
||||
.execute_expr(&self.expr, exec_state, &metadata, &[], StatementKind::Expression)
|
||||
.await?;
|
||||
apply_ascription(&result, &self.ty, exec_state, self.into())
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_ascription(
|
||||
value: &KclValue,
|
||||
ty: &Node<Type>,
|
||||
@ -758,6 +766,7 @@ impl BinaryPart {
|
||||
BinaryPart::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, ctx).await,
|
||||
BinaryPart::MemberExpression(member_expression) => member_expression.get_result(exec_state),
|
||||
BinaryPart::IfExpression(e) => e.get_result(exec_state, ctx).await,
|
||||
BinaryPart::AscribedExpression(e) => e.get_result(exec_state, ctx).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2638,4 +2647,10 @@ sketch001 = startSketchOn(XY)
|
||||
|
||||
parse_execute(ast).await.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn ascription_in_binop() {
|
||||
let ast = r#"foo = tan(0): number(rad) - 4deg"#;
|
||||
parse_execute(ast).await.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@ -150,6 +150,7 @@ impl BinaryPart {
|
||||
unary_expression.get_hover_value_for_position(pos, code, opts)
|
||||
}
|
||||
BinaryPart::IfExpression(e) => e.get_hover_value_for_position(pos, code, opts),
|
||||
BinaryPart::AscribedExpression(e) => e.expr.get_hover_value_for_position(pos, code, opts),
|
||||
BinaryPart::MemberExpression(member_expression) => {
|
||||
member_expression.get_hover_value_for_position(pos, code, opts)
|
||||
}
|
||||
|
||||
@ -162,6 +162,7 @@ impl BinaryPart {
|
||||
BinaryPart::UnaryExpression(ue) => ue.compute_digest(),
|
||||
BinaryPart::MemberExpression(me) => me.compute_digest(),
|
||||
BinaryPart::IfExpression(e) => e.compute_digest(),
|
||||
BinaryPart::AscribedExpression(e) => e.compute_digest(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,6 +52,7 @@ impl BinaryPart {
|
||||
BinaryPart::UnaryExpression(unary_expression) => unary_expression.module_id,
|
||||
BinaryPart::MemberExpression(member_expression) => member_expression.module_id,
|
||||
BinaryPart::IfExpression(e) => e.module_id,
|
||||
BinaryPart::AscribedExpression(e) => e.module_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1215,6 +1215,7 @@ impl From<&BinaryPart> for Expr {
|
||||
BinaryPart::UnaryExpression(unary_expression) => Expr::UnaryExpression(unary_expression.clone()),
|
||||
BinaryPart::MemberExpression(member_expression) => Expr::MemberExpression(member_expression.clone()),
|
||||
BinaryPart::IfExpression(e) => Expr::IfExpression(e.clone()),
|
||||
BinaryPart::AscribedExpression(e) => Expr::AscribedExpression(e.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1281,6 +1282,7 @@ pub enum BinaryPart {
|
||||
UnaryExpression(BoxNode<UnaryExpression>),
|
||||
MemberExpression(BoxNode<MemberExpression>),
|
||||
IfExpression(BoxNode<IfExpression>),
|
||||
AscribedExpression(BoxNode<AscribedExpression>),
|
||||
}
|
||||
|
||||
impl From<BinaryPart> for SourceRange {
|
||||
@ -1306,6 +1308,7 @@ impl BinaryPart {
|
||||
BinaryPart::UnaryExpression(unary_expression) => unary_expression.get_constraint_level(),
|
||||
BinaryPart::MemberExpression(member_expression) => member_expression.get_constraint_level(),
|
||||
BinaryPart::IfExpression(e) => e.get_constraint_level(),
|
||||
BinaryPart::AscribedExpression(e) => e.expr.get_constraint_level(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1324,6 +1327,7 @@ impl BinaryPart {
|
||||
}
|
||||
BinaryPart::MemberExpression(_) => {}
|
||||
BinaryPart::IfExpression(e) => e.replace_value(source_range, new_value),
|
||||
BinaryPart::AscribedExpression(e) => e.expr.replace_value(source_range, new_value),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1336,6 +1340,7 @@ impl BinaryPart {
|
||||
BinaryPart::UnaryExpression(unary_expression) => unary_expression.start,
|
||||
BinaryPart::MemberExpression(member_expression) => member_expression.start,
|
||||
BinaryPart::IfExpression(e) => e.start,
|
||||
BinaryPart::AscribedExpression(e) => e.start,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1348,6 +1353,7 @@ impl BinaryPart {
|
||||
BinaryPart::UnaryExpression(unary_expression) => unary_expression.end,
|
||||
BinaryPart::MemberExpression(member_expression) => member_expression.end,
|
||||
BinaryPart::IfExpression(e) => e.end,
|
||||
BinaryPart::AscribedExpression(e) => e.end,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1369,6 +1375,7 @@ impl BinaryPart {
|
||||
member_expression.rename_identifiers(old_name, new_name)
|
||||
}
|
||||
BinaryPart::IfExpression(ref mut if_expression) => if_expression.rename_identifiers(old_name, new_name),
|
||||
BinaryPart::AscribedExpression(ref mut e) => e.expr.rename_identifiers(old_name, new_name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -631,8 +631,7 @@ fn operand(i: &mut TokenSlice) -> PResult<BinaryPart> {
|
||||
| Expr::ArrayExpression(_)
|
||||
| Expr::ArrayRangeExpression(_)
|
||||
| Expr::ObjectExpression(_)
|
||||
| Expr::LabelledExpression(..)
|
||||
| Expr::AscribedExpression(..) => return Err(CompilationError::fatal(source_range, TODO_783)),
|
||||
| Expr::LabelledExpression(..) => return Err(CompilationError::fatal(source_range, TODO_783)),
|
||||
Expr::None(_) => {
|
||||
return Err(CompilationError::fatal(
|
||||
source_range,
|
||||
@ -658,6 +657,7 @@ fn operand(i: &mut TokenSlice) -> PResult<BinaryPart> {
|
||||
Expr::CallExpressionKw(x) => BinaryPart::CallExpressionKw(x),
|
||||
Expr::MemberExpression(x) => BinaryPart::MemberExpression(x),
|
||||
Expr::IfExpression(x) => BinaryPart::IfExpression(x),
|
||||
Expr::AscribedExpression(x) => BinaryPart::AscribedExpression(x),
|
||||
};
|
||||
Ok(expr)
|
||||
})
|
||||
@ -2068,7 +2068,7 @@ fn expr_allowed_in_pipe_expr(i: &mut TokenSlice) -> PResult<Expr> {
|
||||
}
|
||||
|
||||
fn possible_operands(i: &mut TokenSlice) -> PResult<Expr> {
|
||||
alt((
|
||||
let mut expr = alt((
|
||||
unary_expression.map(Box::new).map(Expr::UnaryExpression),
|
||||
bool_value.map(Expr::Literal),
|
||||
member_expression.map(Box::new).map(Expr::MemberExpression),
|
||||
@ -2081,7 +2081,14 @@ fn possible_operands(i: &mut TokenSlice) -> PResult<Expr> {
|
||||
.context(expected(
|
||||
"a KCL value which can be used as an argument/operand to an operator",
|
||||
))
|
||||
.parse_next(i)
|
||||
.parse_next(i)?;
|
||||
|
||||
let ty = opt((colon, opt(whitespace), argument_type)).parse_next(i)?;
|
||||
if let Some((_, _, ty)) = ty {
|
||||
expr = Expr::AscribedExpression(Box::new(AscribedExpression::new(expr, ty)))
|
||||
}
|
||||
|
||||
Ok(expr)
|
||||
}
|
||||
|
||||
/// Parse an item visibility specifier, e.g. export.
|
||||
|
||||
@ -2,11 +2,11 @@ use std::fmt::Write;
|
||||
|
||||
use crate::parsing::{
|
||||
ast::types::{
|
||||
Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem,
|
||||
CallExpressionKw, CommentStyle, DefaultParamVal, Expr, FormatOptions, FunctionExpression, IfExpression,
|
||||
ImportSelector, ImportStatement, ItemVisibility, LabeledArg, Literal, LiteralIdentifier, LiteralValue,
|
||||
MemberExpression, MemberObject, Node, NonCodeNode, NonCodeValue, ObjectExpression, Parameter, PipeExpression,
|
||||
Program, TagDeclarator, TypeDeclaration, UnaryExpression, VariableDeclaration, VariableKind,
|
||||
Annotation, ArrayExpression, ArrayRangeExpression, AscribedExpression, BinaryExpression, BinaryOperator,
|
||||
BinaryPart, BodyItem, CallExpressionKw, CommentStyle, DefaultParamVal, Expr, FormatOptions, FunctionExpression,
|
||||
IfExpression, ImportSelector, ImportStatement, ItemVisibility, LabeledArg, Literal, LiteralIdentifier,
|
||||
LiteralValue, MemberExpression, MemberObject, Node, NonCodeNode, NonCodeValue, ObjectExpression, Parameter,
|
||||
PipeExpression, Program, TagDeclarator, TypeDeclaration, UnaryExpression, VariableDeclaration, VariableKind,
|
||||
},
|
||||
deprecation, DeprecationKind, PIPE_OPERATOR,
|
||||
};
|
||||
@ -308,18 +308,7 @@ impl Expr {
|
||||
result += &e.label.name;
|
||||
result
|
||||
}
|
||||
Expr::AscribedExpression(e) => {
|
||||
let mut result = e.expr.recast(options, indentation_level, ctxt);
|
||||
if matches!(
|
||||
e.expr,
|
||||
Expr::BinaryExpression(..) | Expr::PipeExpression(..) | Expr::UnaryExpression(..)
|
||||
) {
|
||||
result = format!("({result})");
|
||||
}
|
||||
result += ": ";
|
||||
result += &e.ty.to_string();
|
||||
result
|
||||
}
|
||||
Expr::AscribedExpression(e) => e.recast(options, indentation_level, ctxt),
|
||||
Expr::None(_) => {
|
||||
unimplemented!("there is no literal None, see https://github.com/KittyCAD/modeling-app/issues/1115")
|
||||
}
|
||||
@ -327,6 +316,21 @@ impl Expr {
|
||||
}
|
||||
}
|
||||
|
||||
impl AscribedExpression {
|
||||
fn recast(&self, options: &FormatOptions, indentation_level: usize, ctxt: ExprContext) -> String {
|
||||
let mut result = self.expr.recast(options, indentation_level, ctxt);
|
||||
if matches!(
|
||||
self.expr,
|
||||
Expr::BinaryExpression(..) | Expr::PipeExpression(..) | Expr::UnaryExpression(..)
|
||||
) {
|
||||
result = format!("({result})");
|
||||
}
|
||||
result += ": ";
|
||||
result += &self.ty.to_string();
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl BinaryPart {
|
||||
fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
|
||||
match &self {
|
||||
@ -345,6 +349,7 @@ impl BinaryPart {
|
||||
BinaryPart::UnaryExpression(unary_expression) => unary_expression.recast(options),
|
||||
BinaryPart::MemberExpression(member_expression) => member_expression.recast(),
|
||||
BinaryPart::IfExpression(e) => e.recast(options, indentation_level, ExprContext::Other),
|
||||
BinaryPart::AscribedExpression(e) => e.recast(options, indentation_level, ExprContext::Other),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -722,6 +727,7 @@ impl UnaryExpression {
|
||||
| BinaryPart::Name(_)
|
||||
| BinaryPart::MemberExpression(_)
|
||||
| BinaryPart::IfExpression(_)
|
||||
| BinaryPart::AscribedExpression(_)
|
||||
| BinaryPart::CallExpressionKw(_) => {
|
||||
format!("{}{}", &self.operator, self.argument.recast(options, 0))
|
||||
}
|
||||
|
||||
@ -221,6 +221,7 @@ impl<'tree> From<&'tree types::BinaryPart> for Node<'tree> {
|
||||
types::BinaryPart::UnaryExpression(ue) => ue.as_ref().into(),
|
||||
types::BinaryPart::MemberExpression(me) => me.as_ref().into(),
|
||||
types::BinaryPart::IfExpression(e) => e.as_ref().into(),
|
||||
types::BinaryPart::AscribedExpression(e) => e.as_ref().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user