diff --git a/source/parser/function.civet b/source/parser/function.civet index dea0c8a5..d3f7b7f9 100644 --- a/source/parser/function.civet +++ b/source/parser/function.civet @@ -1,6 +1,6 @@ import type { + ArrowFunction ASTNode - ASTNodeBase ASTNodeObject BlockStatement BreakStatement @@ -11,11 +11,11 @@ import type { IterationExpression IterationFamily IterationStatement + NewExpression Parameter ParametersNode StatementTuple SwitchStatement - TypeIdentifier TypeNode Whitespace } from ./types.civet @@ -30,6 +30,10 @@ import { gatherBindingCode } from ./binding.civet +import { + replaceNode +} from ./lib.civet + import { findAncestor findChildIndex @@ -806,6 +810,29 @@ function makeAmpersandFunction(rhs: AmpersandBlockBody): ASTNode fn +/** +Split a CallExpression node in half, where the inner half ends with child +number given by `index`. Returns the inner call. +*/ +function splitCall(call: CallExpression, index: number): CallExpression | NewExpression + subcall: CallExpression := makeNode + type: "CallExpression" + children: call.children[0..index] + call.children[0..index] = [subcall] + subcall.parent = call + { parent } := call + // Pull preceding `new` into inner call to preserve semantics + if parent is like {type: "NewExpression", expression: ^call} + replaceNode parent, call + parent.parent = call + parent.expression = subcall + parent.children = parent.children.map & is call ? subcall : & + subcall.parent = parent + call.children[0] = parent + parent + else + subcall + export { assignResults expressionizeIteration @@ -815,5 +842,6 @@ export { processFunctions processReturn skipImplicitArguments + splitCall wrapIterationReturningResults } diff --git a/source/parser/lib.civet b/source/parser/lib.civet index 1cfba96a..a7f8f681 100644 --- a/source/parser/lib.civet +++ b/source/parser/lib.civet @@ -71,6 +71,7 @@ import { prepend stripTrailingImplicitComma trimFirstSpace + updateParentPointers wrapIIFE wrapWithReturn } from ./util.civet @@ -110,6 +111,7 @@ import { processCoffeeDo processFunctions skipImplicitArguments + splitCall } from ./function.civet import { processPatternMatching } from ./pattern-matching.civet import { @@ -1296,7 +1298,12 @@ function processPlaceholders(statements: StatementTuple[]): void // Partial placeholder . lifts to the nearest call expression, // including the call itself and any surrounding unary operations. { ancestor } = findAncestor exp, .type is "Call" + call := ancestor ancestor = ancestor?.parent + if ancestor is like {type: "CallExpression"} + index := findChildIndex ancestor, call + if index < ancestor.children# - 1 // not the last call + ancestor = splitCall ancestor, index while ancestor?.parent?.type is "UnaryExpression" or ancestor?.parent?.type is "NewExpression" ancestor = ancestor.parent unless ancestor diff --git a/source/parser/types.civet b/source/parser/types.civet index 226cf038..2e33b374 100644 --- a/source/parser/types.civet +++ b/source/parser/types.civet @@ -239,6 +239,7 @@ export type NewExpression type: "NewExpression" children: Children parent?: Parent + expression: ASTNode export type Yield type: "Yield" @@ -368,22 +369,26 @@ export type CaseBlock type: "CaseBlock" clauses: (PatternClause | CaseClause | WhenClause | DefaultClause)[] children: Children + parent?: Parent export type PatternClause type: "PatternClause" children: Children + parent?: Parent patterns: PatternExpression[] block: BlockStatement export type CaseClause type: "CaseClause" children: Children + parent?: Parent cases: ASTNode[] block: BlockStatement export type WhenClause type: "WhenClause" children: Children + parent?: Parent cases: ASTNode[] break: ASTNode block: BlockStatement @@ -391,6 +396,7 @@ export type WhenClause export type DefaultClause type: "DefaultClause" children: Children + parent?: Parent block: BlockStatement export type EmptyStatement @@ -454,6 +460,7 @@ export type AtBinding = type: "AtBinding" ref: ASTRef children: Children & [ASTRef] + parent?: Parent export type BlockStatement = type: "BlockStatement" @@ -480,6 +487,7 @@ export type DeclarationStatement = export type Binding = type: "Binding" children: Children + parent?: Parent names: string[] pattern: BindingIdentifier | BindingPattern typeSuffix: TypeSuffix? @@ -620,6 +628,7 @@ export type Placeholder = export type PinPattern = type: "PinPattern" children: Children + parent?: Parent expression: ExpressionNode // _?, __ @@ -683,6 +692,7 @@ export type ObjectBindingPatternContent = export type ObjectBindingPattern = type: "ObjectBindingPattern", children: Children & [Whitespace, ASTLeaf, ObjectBindingPatternContent, WSNode, ASTLeaf] + parent?: Parent names: string[] properties: ObjectBindingPatternContent typeSuffix?: TypeSuffix? @@ -799,6 +809,7 @@ export type TypeSuffix = nonnull?: ASTNode t?: ASTNode children: Children + parent?: Parent export type ReturnTypeAnnotation = type: "ReturnTypeAnnotation" diff --git a/test/partial-placeholder.civet b/test/partial-placeholder.civet index d904f45a..42599465 100644 --- a/test/partial-placeholder.civet +++ b/test/partial-placeholder.civet @@ -33,6 +33,14 @@ describe "partial placeholder", -> $ => f()?.g?.().h($, x) """ + testCase """ + not last call in chain + --- + f(a)(b)(.)(c)(d) + --- + ($ => f(a)(b)($))(c)(d) + """ + testCase """ object value --- @@ -101,6 +109,14 @@ describe "partial placeholder", -> $ => new X($) """ + testCase """ + not last call in new chain + --- + new T(a)(b)(.)(c)(d) + --- + ($ => new T(a)(b)($))(c)(d) + """ + testCase """ in larger expression ---