KCL parser: Allow .prop or [index] to follow any expression (#7371)
Previously in a member expression like `foo.x` or `foo[3]`, `foo` had to be an identifier. You could not do something like `f().x` (and if you tried, you got a cryptic error). Rather than make the error better, we should just accept any expression to be the LHS of a member expression (aka its 'object'). This does knock our "parse lots of function calls" from 58 to 55 calls before it stack overflows. But I think it's fine, we'll address this in https://github.com/KittyCAD/modeling-app/pull/6226 when I get back to it. Closes https://github.com/KittyCAD/modeling-app/issues/7273
This commit is contained in:
@ -19,8 +19,8 @@ use crate::{
|
||||
parsing::ast::types::{
|
||||
Annotation, ArrayExpression, ArrayRangeExpression, AscribedExpression, BinaryExpression, BinaryOperator,
|
||||
BinaryPart, BodyItem, Expr, IfExpression, ImportPath, ImportSelector, ItemVisibility, LiteralIdentifier,
|
||||
LiteralValue, MemberExpression, MemberObject, Name, Node, NodeRef, ObjectExpression, PipeExpression, Program,
|
||||
TagDeclarator, Type, UnaryExpression, UnaryOperator,
|
||||
LiteralValue, MemberExpression, Name, Node, NodeRef, ObjectExpression, PipeExpression, Program, TagDeclarator,
|
||||
Type, UnaryExpression, UnaryOperator,
|
||||
},
|
||||
source_range::SourceRange,
|
||||
std::args::TyF64,
|
||||
@ -739,7 +739,7 @@ impl ExecutorContext {
|
||||
Expr::ArrayExpression(array_expression) => array_expression.execute(exec_state, self).await?,
|
||||
Expr::ArrayRangeExpression(range_expression) => range_expression.execute(exec_state, self).await?,
|
||||
Expr::ObjectExpression(object_expression) => object_expression.execute(exec_state, self).await?,
|
||||
Expr::MemberExpression(member_expression) => member_expression.get_result(exec_state)?,
|
||||
Expr::MemberExpression(member_expression) => member_expression.get_result(exec_state, self).await?,
|
||||
Expr::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, self).await?,
|
||||
Expr::IfExpression(expr) => expr.get_result(exec_state, self).await?,
|
||||
Expr::LabelledExpression(expr) => {
|
||||
@ -825,7 +825,7 @@ impl BinaryPart {
|
||||
BinaryPart::BinaryExpression(binary_expression) => binary_expression.get_result(exec_state, ctx).await,
|
||||
BinaryPart::CallExpressionKw(call_expression) => call_expression.execute(exec_state, ctx).await,
|
||||
BinaryPart::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, ctx).await,
|
||||
BinaryPart::MemberExpression(member_expression) => member_expression.get_result(exec_state),
|
||||
BinaryPart::MemberExpression(member_expression) => member_expression.get_result(exec_state, ctx).await,
|
||||
BinaryPart::IfExpression(e) => e.get_result(exec_state, ctx).await,
|
||||
BinaryPart::AscribedExpression(e) => e.get_result(exec_state, ctx).await,
|
||||
}
|
||||
@ -942,16 +942,14 @@ impl Node<Name> {
|
||||
}
|
||||
|
||||
impl Node<MemberExpression> {
|
||||
fn get_result(&self, exec_state: &mut ExecState) -> Result<KclValue, KclError> {
|
||||
async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
|
||||
let property = Property::try_from(self.computed, self.property.clone(), exec_state, self.into())?;
|
||||
let object = match &self.object {
|
||||
// TODO: Don't use recursion here, use a loop.
|
||||
MemberObject::MemberExpression(member_expr) => member_expr.get_result(exec_state)?,
|
||||
MemberObject::Identifier(identifier) => {
|
||||
let value = exec_state.stack().get(&identifier.name, identifier.into())?;
|
||||
value.clone()
|
||||
}
|
||||
let meta = Metadata {
|
||||
source_range: SourceRange::from(self),
|
||||
};
|
||||
let object = ctx
|
||||
.execute_expr(&self.object, exec_state, &meta, &[], StatementKind::Expression)
|
||||
.await?;
|
||||
|
||||
// Check the property and object match -- e.g. ints for arrays, strs for objects.
|
||||
match (object, property, self.computed) {
|
||||
|
@ -1949,7 +1949,7 @@ notPipeSub = 1 |> identity(!%))";
|
||||
// a runtime error instead.
|
||||
parse_execute(code11).await.unwrap_err(),
|
||||
KclError::new_syntax(KclErrorDetails::new(
|
||||
"There was an unexpected !. Try removing it.".to_owned(),
|
||||
"There was an unexpected `!`. Try removing it.".to_owned(),
|
||||
vec![SourceRange::new(56, 57, ModuleId::default())],
|
||||
))
|
||||
);
|
||||
|
@ -247,17 +247,6 @@ impl ObjectProperty {
|
||||
}
|
||||
}
|
||||
|
||||
impl MemberObject {
|
||||
fn get_hover_value_for_position(&self, pos: usize, code: &str, opts: &HoverOpts) -> Option<Hover> {
|
||||
match self {
|
||||
MemberObject::MemberExpression(member_expression) => {
|
||||
member_expression.get_hover_value_for_position(pos, code, opts)
|
||||
}
|
||||
MemberObject::Identifier(_identifier) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MemberExpression {
|
||||
fn get_hover_value_for_position(&self, pos: usize, code: &str, opts: &HoverOpts) -> Option<Hover> {
|
||||
let object_source_range: SourceRange = self.object.clone().into();
|
||||
|
@ -4,7 +4,7 @@ use crate::parsing::ast::types::{
|
||||
Annotation, ArrayExpression, ArrayRangeExpression, AscribedExpression, BinaryExpression, BinaryPart, BodyItem,
|
||||
CallExpressionKw, DefaultParamVal, ElseIf, Expr, ExpressionStatement, FunctionExpression, FunctionType, Identifier,
|
||||
IfExpression, ImportItem, ImportSelector, ImportStatement, ItemVisibility, KclNone, LabelledExpression, Literal,
|
||||
LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Name, ObjectExpression, ObjectProperty, Parameter,
|
||||
LiteralIdentifier, LiteralValue, MemberExpression, Name, ObjectExpression, ObjectProperty, Parameter,
|
||||
PipeExpression, PipeSubstitution, PrimitiveType, Program, ReturnStatement, TagDeclarator, Type, TypeDeclaration,
|
||||
UnaryExpression, VariableDeclaration, VariableDeclarator, VariableKind,
|
||||
};
|
||||
@ -167,15 +167,6 @@ impl BinaryPart {
|
||||
}
|
||||
}
|
||||
|
||||
impl MemberObject {
|
||||
pub fn compute_digest(&mut self) -> Digest {
|
||||
match self {
|
||||
MemberObject::MemberExpression(me) => me.compute_digest(),
|
||||
MemberObject::Identifier(id) => id.compute_digest(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LiteralIdentifier {
|
||||
pub fn compute_digest(&mut self) -> Digest {
|
||||
match self {
|
||||
|
@ -2,7 +2,7 @@ pub(crate) mod digest;
|
||||
pub mod types;
|
||||
|
||||
use crate::{
|
||||
parsing::ast::types::{BinaryPart, BodyItem, Expr, LiteralIdentifier, MemberObject},
|
||||
parsing::ast::types::{BinaryPart, BodyItem, Expr, LiteralIdentifier},
|
||||
ModuleId,
|
||||
};
|
||||
|
||||
@ -57,15 +57,6 @@ impl BinaryPart {
|
||||
}
|
||||
}
|
||||
|
||||
impl MemberObject {
|
||||
pub fn module_id(&self) -> ModuleId {
|
||||
match self {
|
||||
MemberObject::MemberExpression(member_expression) => member_expression.module_id,
|
||||
MemberObject::Identifier(identifier) => identifier.module_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LiteralIdentifier {
|
||||
pub fn module_id(&self) -> ModuleId {
|
||||
match self {
|
||||
|
@ -2756,47 +2756,6 @@ impl ObjectProperty {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum MemberObject {
|
||||
MemberExpression(BoxNode<MemberExpression>),
|
||||
Identifier(BoxNode<Identifier>),
|
||||
}
|
||||
|
||||
impl MemberObject {
|
||||
pub fn start(&self) -> usize {
|
||||
match self {
|
||||
MemberObject::MemberExpression(member_expression) => member_expression.start,
|
||||
MemberObject::Identifier(identifier) => identifier.start,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn end(&self) -> usize {
|
||||
match self {
|
||||
MemberObject::MemberExpression(member_expression) => member_expression.end,
|
||||
MemberObject::Identifier(identifier) => identifier.end,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn contains_range(&self, range: &SourceRange) -> bool {
|
||||
let sr = SourceRange::from(self);
|
||||
sr.contains_range(range)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MemberObject> for SourceRange {
|
||||
fn from(obj: MemberObject) -> Self {
|
||||
Self::new(obj.start(), obj.end(), obj.module_id())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&MemberObject> for SourceRange {
|
||||
fn from(obj: &MemberObject) -> Self {
|
||||
Self::new(obj.start(), obj.end(), obj.module_id())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type")]
|
||||
@ -2842,7 +2801,7 @@ impl From<&LiteralIdentifier> for SourceRange {
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type")]
|
||||
pub struct MemberExpression {
|
||||
pub object: MemberObject,
|
||||
pub object: Expr,
|
||||
pub property: LiteralIdentifier,
|
||||
pub computed: bool,
|
||||
|
||||
@ -2864,12 +2823,7 @@ impl Node<MemberExpression> {
|
||||
impl MemberExpression {
|
||||
/// Rename all identifiers that have the old name to the new given name.
|
||||
fn rename_identifiers(&mut self, old_name: &str, new_name: &str) {
|
||||
match &mut self.object {
|
||||
MemberObject::MemberExpression(ref mut member_expression) => {
|
||||
member_expression.rename_identifiers(old_name, new_name)
|
||||
}
|
||||
MemberObject::Identifier(ref mut identifier) => identifier.rename(old_name, new_name),
|
||||
}
|
||||
self.object.rename_identifiers(old_name, new_name);
|
||||
|
||||
match &mut self.property {
|
||||
LiteralIdentifier::Identifier(ref mut identifier) => identifier.rename(old_name, new_name),
|
||||
|
@ -1,6 +1,6 @@
|
||||
use serde::Serialize;
|
||||
|
||||
use super::{BodyItem, Expr, MemberObject, Node, Program};
|
||||
use super::{BodyItem, Expr, Node, Program};
|
||||
use crate::SourceRange;
|
||||
|
||||
/// A traversal path through the AST to a node.
|
||||
@ -248,7 +248,7 @@ impl NodePath {
|
||||
Expr::MemberExpression(node) => {
|
||||
if node.object.contains_range(&range) {
|
||||
path.push(Step::MemberExpressionObject);
|
||||
return Self::from_member_expr_object(&node.object, range, path);
|
||||
return NodePath::from_expr(&node.object, range, path);
|
||||
}
|
||||
if node.property.contains_range(&range) {
|
||||
path.push(Step::MemberExpressionProperty);
|
||||
@ -313,18 +313,6 @@ impl NodePath {
|
||||
Some(path)
|
||||
}
|
||||
|
||||
fn from_member_expr_object(mut expr: &MemberObject, range: SourceRange, mut path: NodePath) -> Option<NodePath> {
|
||||
while let MemberObject::MemberExpression(node) = expr {
|
||||
if !node.object.contains_range(&range) {
|
||||
break;
|
||||
}
|
||||
path.push(Step::MemberExpressionObject);
|
||||
expr = &node.object;
|
||||
}
|
||||
|
||||
Some(path)
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.steps.is_empty()
|
||||
}
|
||||
|
@ -26,8 +26,8 @@ use crate::{
|
||||
Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem,
|
||||
BoxNode, CallExpressionKw, CommentStyle, DefaultParamVal, ElseIf, Expr, ExpressionStatement,
|
||||
FunctionExpression, FunctionType, Identifier, IfExpression, ImportItem, ImportSelector, ImportStatement,
|
||||
ItemVisibility, LabeledArg, Literal, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Name,
|
||||
Node, NodeList, NonCodeMeta, NonCodeNode, NonCodeValue, ObjectExpression, ObjectProperty, Parameter,
|
||||
ItemVisibility, LabeledArg, Literal, LiteralIdentifier, LiteralValue, MemberExpression, Name, Node,
|
||||
NodeList, NonCodeMeta, NonCodeNode, NonCodeValue, ObjectExpression, ObjectProperty, Parameter,
|
||||
PipeExpression, PipeSubstitution, PrimitiveType, Program, ReturnStatement, Shebang, TagDeclarator, Type,
|
||||
TypeDeclaration, UnaryExpression, UnaryOperator, VariableDeclaration, VariableDeclarator, VariableKind,
|
||||
},
|
||||
@ -1326,10 +1326,7 @@ fn member_expression_subscript(i: &mut TokenSlice) -> ModalResult<(LiteralIdenti
|
||||
|
||||
/// Get a property of an object, or an index of an array, or a member of a collection.
|
||||
/// Can be arbitrarily nested, e.g. `people[i]['adam'].age`.
|
||||
fn member_expression(i: &mut TokenSlice) -> ModalResult<Node<MemberExpression>> {
|
||||
// This is an identifier, followed by a sequence of members (aka properties)
|
||||
// First, the identifier.
|
||||
let id = nameable_identifier.context(expected("the identifier of the object whose property you're trying to access, e.g. in 'shape.size.width', 'shape' is the identifier")).parse_next(i)?;
|
||||
fn build_member_expression(object: Expr, i: &mut TokenSlice) -> ModalResult<Node<MemberExpression>> {
|
||||
// Now a sequence of members.
|
||||
let member = alt((member_expression_dot, member_expression_subscript)).context(expected("a member/property, e.g. size.x and size['height'] and size[0] are all different ways to access a member/property of 'size'"));
|
||||
let mut members: Vec<_> = repeat(1.., member)
|
||||
@ -1340,11 +1337,11 @@ fn member_expression(i: &mut TokenSlice) -> ModalResult<Node<MemberExpression>>
|
||||
// It's safe to call remove(0), because the vec is created from repeat(1..),
|
||||
// which is guaranteed to have >=1 elements.
|
||||
let (property, end, computed) = members.remove(0);
|
||||
let start = id.start;
|
||||
let module_id = id.module_id;
|
||||
let start = object.start();
|
||||
let module_id = object.module_id();
|
||||
let initial_member_expression = Node::new(
|
||||
MemberExpression {
|
||||
object: MemberObject::Identifier(Box::new(id)),
|
||||
object,
|
||||
computed,
|
||||
property,
|
||||
digest: None,
|
||||
@ -1362,7 +1359,7 @@ fn member_expression(i: &mut TokenSlice) -> ModalResult<Node<MemberExpression>>
|
||||
.fold(initial_member_expression, |accumulated, (property, end, computed)| {
|
||||
Node::new(
|
||||
MemberExpression {
|
||||
object: MemberObject::MemberExpression(Box::new(accumulated)),
|
||||
object: Expr::MemberExpression(Box::new(accumulated)),
|
||||
computed,
|
||||
property,
|
||||
digest: None,
|
||||
@ -2085,8 +2082,7 @@ fn unnecessarily_bracketed(i: &mut TokenSlice) -> ModalResult<Expr> {
|
||||
}
|
||||
|
||||
fn expr_allowed_in_pipe_expr(i: &mut TokenSlice) -> ModalResult<Expr> {
|
||||
alt((
|
||||
member_expression.map(Box::new).map(Expr::MemberExpression),
|
||||
let parsed_expr = alt((
|
||||
bool_value.map(Box::new).map(Expr::Literal),
|
||||
tag.map(Box::new).map(Expr::TagDeclarator),
|
||||
literal.map(Expr::Literal),
|
||||
@ -2100,14 +2096,19 @@ fn expr_allowed_in_pipe_expr(i: &mut TokenSlice) -> ModalResult<Expr> {
|
||||
unnecessarily_bracketed,
|
||||
))
|
||||
.context(expected("a KCL expression (but not a pipe expression)"))
|
||||
.parse_next(i)
|
||||
.parse_next(i)?;
|
||||
|
||||
let maybe_member = build_member_expression(parsed_expr.clone(), i);
|
||||
if let Ok(mem) = maybe_member {
|
||||
return Ok(Expr::MemberExpression(Box::new(mem)));
|
||||
}
|
||||
Ok(parsed_expr)
|
||||
}
|
||||
|
||||
fn possible_operands(i: &mut TokenSlice) -> ModalResult<Expr> {
|
||||
let mut expr = alt((
|
||||
unary_expression.map(Box::new).map(Expr::UnaryExpression),
|
||||
bool_value.map(Box::new).map(Expr::Literal),
|
||||
member_expression.map(Box::new).map(Expr::MemberExpression),
|
||||
literal.map(Expr::Literal),
|
||||
fn_call_kw.map(Box::new).map(Expr::CallExpressionKw),
|
||||
name.map(Box::new).map(Expr::Name),
|
||||
@ -2118,6 +2119,10 @@ fn possible_operands(i: &mut TokenSlice) -> ModalResult<Expr> {
|
||||
"a KCL value which can be used as an argument/operand to an operator",
|
||||
))
|
||||
.parse_next(i)?;
|
||||
let maybe_member = build_member_expression(expr.clone(), i);
|
||||
if let Ok(mem) = maybe_member {
|
||||
expr = Expr::MemberExpression(Box::new(mem));
|
||||
}
|
||||
|
||||
let ty = opt((colon, opt(whitespace), type_)).parse_next(i)?;
|
||||
if let Some((_, _, ty)) = ty {
|
||||
@ -3258,7 +3263,7 @@ fn fn_call_kw(i: &mut TokenSlice) -> ModalResult<Node<CallExpressionKw>> {
|
||||
return Err(ErrMode::Cut(
|
||||
CompilationError::fatal(
|
||||
SourceRange::from(&tok),
|
||||
format!("There was an unexpected {}. Try removing it.", tok.value),
|
||||
format!("There was an unexpected `{}`. Try removing it.", tok.value),
|
||||
)
|
||||
.into(),
|
||||
));
|
||||
@ -4427,7 +4432,7 @@ z(-[["#,
|
||||
assert_err(
|
||||
r#"z
|
||||
(--#"#,
|
||||
"There was an unexpected -. Try removing it.",
|
||||
"There was an unexpected `-`. Try removing it.",
|
||||
[3, 4],
|
||||
);
|
||||
}
|
||||
@ -4997,6 +5002,17 @@ type foo = fn(fn, f: fn(number(_))): [fn([any]): string]
|
||||
assert_no_err(some_program_string);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_fn_call_then_field() {
|
||||
let some_program_string = "myFunction().field";
|
||||
let module_id = ModuleId::default();
|
||||
let tokens = crate::parsing::token::lex(some_program_string, module_id).unwrap(); // Updated import path
|
||||
let actual = expression.parse(tokens.as_slice()).unwrap();
|
||||
let Expr::MemberExpression(_expr) = actual else {
|
||||
panic!("expected member expression")
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_array_missing_closing_bracket() {
|
||||
let some_program_string = r#"
|
||||
@ -5185,7 +5201,7 @@ bar = 1
|
||||
.cause
|
||||
.as_ref()
|
||||
.expect("Found an error, but there was no cause. Add a cause.");
|
||||
assert_eq!(cause.message, "There was an unexpected }. Try removing it.",);
|
||||
assert_eq!(cause.message, "There was an unexpected `}`. Try removing it.",);
|
||||
assert_eq!(cause.source_range.start(), expected_src_start);
|
||||
}
|
||||
|
||||
|
@ -117,12 +117,20 @@ expression: actual
|
||||
"computed": false,
|
||||
"end": 45,
|
||||
"object": {
|
||||
"abs_path": false,
|
||||
"commentStart": 40,
|
||||
"end": 43,
|
||||
"name": "obj",
|
||||
"name": {
|
||||
"commentStart": 40,
|
||||
"end": 43,
|
||||
"name": "obj",
|
||||
"start": 40,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 40,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
"type": "Name",
|
||||
"type": "Name"
|
||||
},
|
||||
"property": {
|
||||
"commentStart": 44,
|
||||
|
@ -117,12 +117,20 @@ expression: actual
|
||||
"computed": false,
|
||||
"end": 49,
|
||||
"object": {
|
||||
"abs_path": false,
|
||||
"commentStart": 41,
|
||||
"end": 44,
|
||||
"name": "obj",
|
||||
"name": {
|
||||
"commentStart": 41,
|
||||
"end": 44,
|
||||
"name": "obj",
|
||||
"start": 41,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 41,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
"type": "Name",
|
||||
"type": "Name"
|
||||
},
|
||||
"property": {
|
||||
"commentStart": 45,
|
||||
|
@ -104,12 +104,20 @@ expression: actual
|
||||
"computed": false,
|
||||
"end": 44,
|
||||
"object": {
|
||||
"abs_path": false,
|
||||
"commentStart": 36,
|
||||
"end": 39,
|
||||
"name": "obj",
|
||||
"name": {
|
||||
"commentStart": 36,
|
||||
"end": 39,
|
||||
"name": "obj",
|
||||
"start": 36,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 36,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
"type": "Name",
|
||||
"type": "Name"
|
||||
},
|
||||
"property": {
|
||||
"commentStart": 40,
|
||||
|
@ -120,12 +120,20 @@ expression: actual
|
||||
"computed": false,
|
||||
"end": 49,
|
||||
"object": {
|
||||
"abs_path": false,
|
||||
"commentStart": 41,
|
||||
"end": 44,
|
||||
"name": "obj",
|
||||
"name": {
|
||||
"commentStart": 41,
|
||||
"end": 44,
|
||||
"name": "obj",
|
||||
"start": 41,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 41,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
"type": "Name",
|
||||
"type": "Name"
|
||||
},
|
||||
"property": {
|
||||
"commentStart": 45,
|
||||
|
@ -107,12 +107,20 @@ expression: actual
|
||||
"computed": false,
|
||||
"end": 45,
|
||||
"object": {
|
||||
"abs_path": false,
|
||||
"commentStart": 37,
|
||||
"end": 40,
|
||||
"name": "obj",
|
||||
"name": {
|
||||
"commentStart": 37,
|
||||
"end": 40,
|
||||
"name": "obj",
|
||||
"start": 37,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 37,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
"type": "Name",
|
||||
"type": "Name"
|
||||
},
|
||||
"property": {
|
||||
"commentStart": 41,
|
||||
|
@ -107,12 +107,20 @@ expression: actual
|
||||
"computed": false,
|
||||
"end": 44,
|
||||
"object": {
|
||||
"abs_path": false,
|
||||
"commentStart": 36,
|
||||
"end": 39,
|
||||
"name": "obj",
|
||||
"name": {
|
||||
"commentStart": 36,
|
||||
"end": 39,
|
||||
"name": "obj",
|
||||
"start": 36,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 36,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
"type": "Name",
|
||||
"type": "Name"
|
||||
},
|
||||
"property": {
|
||||
"commentStart": 40,
|
||||
|
@ -37,12 +37,20 @@ expression: actual
|
||||
"computed": false,
|
||||
"end": 18,
|
||||
"object": {
|
||||
"abs_path": false,
|
||||
"commentStart": 13,
|
||||
"end": 16,
|
||||
"name": "obj",
|
||||
"name": {
|
||||
"commentStart": 13,
|
||||
"end": 16,
|
||||
"name": "obj",
|
||||
"start": 13,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 13,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
"type": "Name",
|
||||
"type": "Name"
|
||||
},
|
||||
"property": {
|
||||
"commentStart": 17,
|
||||
|
@ -24,12 +24,20 @@ expression: actual
|
||||
"computed": false,
|
||||
"end": 19,
|
||||
"object": {
|
||||
"abs_path": false,
|
||||
"commentStart": 11,
|
||||
"end": 14,
|
||||
"name": "obj",
|
||||
"name": {
|
||||
"commentStart": 11,
|
||||
"end": 14,
|
||||
"name": "obj",
|
||||
"start": 11,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 11,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
"type": "Name",
|
||||
"type": "Name"
|
||||
},
|
||||
"property": {
|
||||
"commentStart": 15,
|
||||
|
@ -101,12 +101,20 @@ expression: actual
|
||||
"computed": false,
|
||||
"end": 44,
|
||||
"object": {
|
||||
"abs_path": false,
|
||||
"commentStart": 36,
|
||||
"end": 39,
|
||||
"name": "obj",
|
||||
"name": {
|
||||
"commentStart": 36,
|
||||
"end": 39,
|
||||
"name": "obj",
|
||||
"start": 36,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 36,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
"type": "Name",
|
||||
"type": "Name"
|
||||
},
|
||||
"property": {
|
||||
"commentStart": 40,
|
||||
|
@ -25,12 +25,20 @@ expression: actual
|
||||
"computed": false,
|
||||
"end": 16,
|
||||
"object": {
|
||||
"abs_path": false,
|
||||
"commentStart": 7,
|
||||
"end": 9,
|
||||
"name": "yo",
|
||||
"name": {
|
||||
"commentStart": 7,
|
||||
"end": 9,
|
||||
"name": "yo",
|
||||
"start": 7,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 7,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
"type": "Name",
|
||||
"type": "Name"
|
||||
},
|
||||
"property": {
|
||||
"commentStart": 10,
|
||||
|
@ -21,12 +21,20 @@ expression: actual
|
||||
"computed": true,
|
||||
"end": 11,
|
||||
"object": {
|
||||
"abs_path": false,
|
||||
"commentStart": 6,
|
||||
"end": 8,
|
||||
"name": "b1",
|
||||
"name": {
|
||||
"commentStart": 6,
|
||||
"end": 8,
|
||||
"name": "b1",
|
||||
"start": 6,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 6,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
"type": "Name",
|
||||
"type": "Name"
|
||||
},
|
||||
"property": {
|
||||
"commentStart": 9,
|
||||
|
@ -33,12 +33,20 @@ expression: actual
|
||||
"computed": false,
|
||||
"end": 13,
|
||||
"object": {
|
||||
"abs_path": false,
|
||||
"commentStart": 7,
|
||||
"end": 9,
|
||||
"name": "yo",
|
||||
"name": {
|
||||
"commentStart": 7,
|
||||
"end": 9,
|
||||
"name": "yo",
|
||||
"start": 7,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 7,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
"type": "Name",
|
||||
"type": "Name"
|
||||
},
|
||||
"property": {
|
||||
"commentStart": 10,
|
||||
|
@ -21,12 +21,20 @@ expression: actual
|
||||
"computed": false,
|
||||
"end": 11,
|
||||
"object": {
|
||||
"abs_path": false,
|
||||
"commentStart": 6,
|
||||
"end": 8,
|
||||
"name": "b1",
|
||||
"name": {
|
||||
"commentStart": 6,
|
||||
"end": 8,
|
||||
"name": "b1",
|
||||
"start": 6,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 6,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
"type": "Name",
|
||||
"type": "Name"
|
||||
},
|
||||
"property": {
|
||||
"commentStart": 9,
|
||||
|
@ -21,12 +21,20 @@ expression: actual
|
||||
"computed": false,
|
||||
"end": 16,
|
||||
"object": {
|
||||
"abs_path": false,
|
||||
"commentStart": 6,
|
||||
"end": 8,
|
||||
"name": "b1",
|
||||
"name": {
|
||||
"commentStart": 6,
|
||||
"end": 8,
|
||||
"name": "b1",
|
||||
"start": 6,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 6,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
"type": "Name",
|
||||
"type": "Name"
|
||||
},
|
||||
"property": {
|
||||
"commentStart": 9,
|
||||
|
@ -21,12 +21,20 @@ expression: actual
|
||||
"computed": false,
|
||||
"end": 13,
|
||||
"object": {
|
||||
"abs_path": false,
|
||||
"commentStart": 6,
|
||||
"end": 8,
|
||||
"name": "b1",
|
||||
"name": {
|
||||
"commentStart": 6,
|
||||
"end": 8,
|
||||
"name": "b1",
|
||||
"start": 6,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"path": [],
|
||||
"start": 6,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
"type": "Name",
|
||||
"type": "Name"
|
||||
},
|
||||
"property": {
|
||||
"commentStart": 9,
|
||||
|
@ -5,8 +5,8 @@ use crate::parsing::{
|
||||
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,
|
||||
LiteralValue, MemberExpression, Node, NonCodeNode, NonCodeValue, ObjectExpression, Parameter, PipeExpression,
|
||||
Program, TagDeclarator, TypeDeclaration, UnaryExpression, VariableDeclaration, VariableKind,
|
||||
},
|
||||
deprecation, DeprecationKind, PIPE_OPERATOR,
|
||||
};
|
||||
@ -278,11 +278,11 @@ impl Expr {
|
||||
ctxt = ExprContext::Other;
|
||||
}
|
||||
match &self {
|
||||
Expr::BinaryExpression(bin_exp) => bin_exp.recast(options),
|
||||
Expr::BinaryExpression(bin_exp) => bin_exp.recast(options, indentation_level, ctxt),
|
||||
Expr::ArrayExpression(array_exp) => array_exp.recast(options, indentation_level, ctxt),
|
||||
Expr::ArrayRangeExpression(range_exp) => range_exp.recast(options, indentation_level, ctxt),
|
||||
Expr::ObjectExpression(ref obj_exp) => obj_exp.recast(options, indentation_level, ctxt),
|
||||
Expr::MemberExpression(mem_exp) => mem_exp.recast(),
|
||||
Expr::MemberExpression(mem_exp) => mem_exp.recast(options, indentation_level, ctxt),
|
||||
Expr::Literal(literal) => literal.recast(),
|
||||
Expr::FunctionExpression(func_exp) => {
|
||||
let mut result = if is_decl { String::new() } else { "fn".to_owned() };
|
||||
@ -299,7 +299,7 @@ impl Expr {
|
||||
}
|
||||
Expr::TagDeclarator(tag) => tag.recast(),
|
||||
Expr::PipeExpression(pipe_exp) => pipe_exp.recast(options, indentation_level),
|
||||
Expr::UnaryExpression(unary_exp) => unary_exp.recast(options),
|
||||
Expr::UnaryExpression(unary_exp) => unary_exp.recast(options, indentation_level, ctxt),
|
||||
Expr::IfExpression(e) => e.recast(options, indentation_level, ctxt),
|
||||
Expr::PipeSubstitution(_) => crate::parsing::PIPE_SUBSTITUTION_OPERATOR.to_string(),
|
||||
Expr::LabelledExpression(e) => {
|
||||
@ -332,7 +332,7 @@ impl AscribedExpression {
|
||||
}
|
||||
|
||||
impl BinaryPart {
|
||||
fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
|
||||
fn recast(&self, options: &FormatOptions, indentation_level: usize, ctxt: ExprContext) -> String {
|
||||
match &self {
|
||||
BinaryPart::Literal(literal) => literal.recast(),
|
||||
BinaryPart::Name(name) => {
|
||||
@ -342,12 +342,16 @@ impl BinaryPart {
|
||||
None => result,
|
||||
}
|
||||
}
|
||||
BinaryPart::BinaryExpression(binary_expression) => binary_expression.recast(options),
|
||||
BinaryPart::BinaryExpression(binary_expression) => {
|
||||
binary_expression.recast(options, indentation_level, ctxt)
|
||||
}
|
||||
BinaryPart::CallExpressionKw(call_expression) => {
|
||||
call_expression.recast(options, indentation_level, ExprContext::Other)
|
||||
}
|
||||
BinaryPart::UnaryExpression(unary_expression) => unary_expression.recast(options),
|
||||
BinaryPart::MemberExpression(member_expression) => member_expression.recast(),
|
||||
BinaryPart::UnaryExpression(unary_expression) => unary_expression.recast(options, indentation_level, ctxt),
|
||||
BinaryPart::MemberExpression(member_expression) => {
|
||||
member_expression.recast(options, indentation_level, ctxt)
|
||||
}
|
||||
BinaryPart::IfExpression(e) => e.recast(options, indentation_level, ExprContext::Other),
|
||||
BinaryPart::AscribedExpression(e) => e.recast(options, indentation_level, ExprContext::Other),
|
||||
}
|
||||
@ -670,7 +674,7 @@ impl ObjectExpression {
|
||||
}
|
||||
|
||||
impl MemberExpression {
|
||||
fn recast(&self) -> String {
|
||||
fn recast(&self, options: &FormatOptions, indentation_level: usize, ctxt: ExprContext) -> String {
|
||||
let key_str = match &self.property {
|
||||
LiteralIdentifier::Identifier(identifier) => {
|
||||
if self.computed {
|
||||
@ -682,15 +686,12 @@ impl MemberExpression {
|
||||
LiteralIdentifier::Literal(lit) => format!("[{}]", &(*lit.raw)),
|
||||
};
|
||||
|
||||
match &self.object {
|
||||
MemberObject::MemberExpression(member_exp) => member_exp.recast() + key_str.as_str(),
|
||||
MemberObject::Identifier(identifier) => identifier.name.to_string() + key_str.as_str(),
|
||||
}
|
||||
self.object.recast(options, indentation_level, ctxt) + key_str.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl BinaryExpression {
|
||||
fn recast(&self, options: &FormatOptions) -> String {
|
||||
fn recast(&self, options: &FormatOptions, _indentation_level: usize, ctxt: ExprContext) -> String {
|
||||
let maybe_wrap_it = |a: String, doit: bool| -> String {
|
||||
if doit {
|
||||
format!("({})", a)
|
||||
@ -715,15 +716,15 @@ impl BinaryExpression {
|
||||
|
||||
format!(
|
||||
"{} {} {}",
|
||||
maybe_wrap_it(self.left.recast(options, 0), should_wrap_left),
|
||||
maybe_wrap_it(self.left.recast(options, 0, ctxt), should_wrap_left),
|
||||
self.operator,
|
||||
maybe_wrap_it(self.right.recast(options, 0), should_wrap_right)
|
||||
maybe_wrap_it(self.right.recast(options, 0, ctxt), should_wrap_right)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl UnaryExpression {
|
||||
fn recast(&self, options: &FormatOptions) -> String {
|
||||
fn recast(&self, options: &FormatOptions, _indentation_level: usize, ctxt: ExprContext) -> String {
|
||||
match self.argument {
|
||||
BinaryPart::Literal(_)
|
||||
| BinaryPart::Name(_)
|
||||
@ -731,10 +732,10 @@ impl UnaryExpression {
|
||||
| BinaryPart::IfExpression(_)
|
||||
| BinaryPart::AscribedExpression(_)
|
||||
| BinaryPart::CallExpressionKw(_) => {
|
||||
format!("{}{}", &self.operator, self.argument.recast(options, 0))
|
||||
format!("{}{}", &self.operator, self.argument.recast(options, 0, ctxt))
|
||||
}
|
||||
BinaryPart::BinaryExpression(_) | BinaryPart::UnaryExpression(_) => {
|
||||
format!("{}({})", &self.operator, self.argument.recast(options, 0))
|
||||
format!("{}({})", &self.operator, self.argument.recast(options, 0, ctxt))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -226,15 +226,6 @@ impl<'tree> From<&'tree types::BinaryPart> for Node<'tree> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tree> From<&'tree types::MemberObject> for Node<'tree> {
|
||||
fn from(node: &'tree types::MemberObject) -> Self {
|
||||
match node {
|
||||
types::MemberObject::MemberExpression(me) => me.as_ref().into(),
|
||||
types::MemberObject::Identifier(id) => id.as_ref().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tree> From<&'tree types::LiteralIdentifier> for Node<'tree> {
|
||||
fn from(node: &'tree types::LiteralIdentifier) -> Self {
|
||||
match node {
|
||||
|
Reference in New Issue
Block a user