Skip to content

Commit

Permalink
Fix partial placeholder . in middle of call chain
Browse files Browse the repository at this point in the history
Fixes #1449
  • Loading branch information
edemaine committed Oct 10, 2024
1 parent 71bd48b commit c813bc0
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 2 deletions.
32 changes: 30 additions & 2 deletions source/parser/function.civet
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type {
ArrowFunction
ASTNode
ASTNodeBase
ASTNodeObject
BlockStatement
BreakStatement
Expand All @@ -11,11 +11,11 @@ import type {
IterationExpression
IterationFamily
IterationStatement
NewExpression
Parameter
ParametersNode
StatementTuple
SwitchStatement
TypeIdentifier
TypeNode
Whitespace
} from ./types.civet
Expand All @@ -30,6 +30,10 @@ import {
gatherBindingCode
} from ./binding.civet

import {
replaceNode
} from ./lib.civet

import {
findAncestor
findChildIndex
Expand Down Expand Up @@ -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
Expand All @@ -815,5 +842,6 @@ export {
processFunctions
processReturn
skipImplicitArguments
splitCall
wrapIterationReturningResults
}
7 changes: 7 additions & 0 deletions source/parser/lib.civet
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import {
prepend
stripTrailingImplicitComma
trimFirstSpace
updateParentPointers
wrapIIFE
wrapWithReturn
} from ./util.civet
Expand Down Expand Up @@ -110,6 +111,7 @@ import {
processCoffeeDo
processFunctions
skipImplicitArguments
splitCall
} from ./function.civet
import { processPatternMatching } from ./pattern-matching.civet
import {
Expand Down Expand Up @@ -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
Expand Down
11 changes: 11 additions & 0 deletions source/parser/types.civet
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ export type NewExpression
type: "NewExpression"
children: Children
parent?: Parent
expression: ASTNode

export type Yield
type: "Yield"
Expand Down Expand Up @@ -368,29 +369,34 @@ 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

export type DefaultClause
type: "DefaultClause"
children: Children
parent?: Parent
block: BlockStatement

export type EmptyStatement
Expand Down Expand Up @@ -454,6 +460,7 @@ export type AtBinding =
type: "AtBinding"
ref: ASTRef
children: Children & [ASTRef]
parent?: Parent

export type BlockStatement =
type: "BlockStatement"
Expand All @@ -480,6 +487,7 @@ export type DeclarationStatement =
export type Binding =
type: "Binding"
children: Children
parent?: Parent
names: string[]
pattern: BindingIdentifier | BindingPattern
typeSuffix: TypeSuffix?
Expand Down Expand Up @@ -620,6 +628,7 @@ export type Placeholder =
export type PinPattern =
type: "PinPattern"
children: Children
parent?: Parent
expression: ExpressionNode

// _?, __
Expand Down Expand Up @@ -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?
Expand Down Expand Up @@ -799,6 +809,7 @@ export type TypeSuffix =
nonnull?: ASTNode
t?: ASTNode
children: Children
parent?: Parent

export type ReturnTypeAnnotation =
type: "ReturnTypeAnnotation"
Expand Down
16 changes: 16 additions & 0 deletions test/partial-placeholder.civet
Original file line number Diff line number Diff line change
Expand Up @@ -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
---
Expand Down Expand Up @@ -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
---
Expand Down

0 comments on commit c813bc0

Please sign in to comment.