Skip to content

Commit

Permalink
Merge pull request #1651 from DanielXMoore/pin-param
Browse files Browse the repository at this point in the history
Pin parameter `^p` assigns function parameter to outer variable
  • Loading branch information
edemaine authored Dec 20, 2024
2 parents a1ed02f + 039dbee commit d142b63
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 4 deletions.
18 changes: 18 additions & 0 deletions civet.dev/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -1135,6 +1135,24 @@ You can also use Unicode arrows:
curryAdd := (a: number) → (b: number) ⇒ a + b
</Playground>
### Pin Parameters and This Parameters
You can directly assign an argument to an outer variable `foo` by writing
`^foo` (see pins from [pattern matching](#pattern-matching)):
<Playground>
let resolve, reject
promise := new Promise (^resolve, ^reject) =>
</Playground>
Similarly, you can directly assign an argument to `this.foo` by writing `@foo`
(see [`@` shorthand for `this`](#at)).
This is particularly useful within methods.
<Playground>
@promise := new Promise (@resolve, @reject) =>
</Playground>
### `return.value`
Instead of specifying a function's return value when it returns,
Expand Down
5 changes: 4 additions & 1 deletion source/parser.hera
Original file line number Diff line number Diff line change
Expand Up @@ -1994,7 +1994,7 @@ PinPattern
Caret SingleLineExpressionWithIndentedApplicationForbidden:expression ->
return {
type: "PinPattern",
children: $0,
children: [expression],
expression,
}
ActualMemberExpression:expression ->
Expand Down Expand Up @@ -2153,6 +2153,8 @@ BindingProperty
}

if (pin) {
// Note that this has the name but not the value.
// This is what we want when destructuring, but not in function params.
children = [ws, binding]
if (typeSuffix) {
children.push({
Expand All @@ -2172,6 +2174,7 @@ BindingProperty
name: binding,
value: {
type: "PinPattern",
children: [binding],
expression: binding,
},
}
Expand Down
36 changes: 34 additions & 2 deletions source/parser/binding.civet
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,14 @@ import {
gatherRecursiveAll
} from ./traversal.civet

import {
makeRef
} from ./ref.civet

import {
parenthesizeType
trimFirstSpace
updateParentPointers
} from ./util.civet

/**
Expand Down Expand Up @@ -102,19 +107,46 @@ function adjustBindingElements(elements: ASTNodeObject[])
length
}

function gatherBindingCode(statements: ASTNode, opts?: { injectParamProps?: boolean })
function gatherBindingCode(statements: ASTNode, opts?: { injectParamProps?: boolean, assignPins?: boolean })
thisAssignments: ThisAssignments := []
splices: unknown[] := []

function insertRestSplices(s, p: unknown[], thisAssignments: ThisAssignments): void
for each n of gatherRecursiveAll s, (n) => n.blockPrefix or (opts?.injectParamProps and n.accessModifier) or n.type is "AtBinding"
for each let n of gatherRecursiveAll(s, (n) => (or)
n.blockPrefix
opts?.injectParamProps and n.accessModifier
n.type is "AtBinding"
opts?.assignPins and n.type is like "PinPattern", "PinProperty"
)
// Insert `this` assignments
if n.type is "AtBinding"
{ ref } := n
{ id } := ref
thisAssignments.push([`this.${id} = `, ref])
continue

if opts?.assignPins
if n.type is "PinProperty"
n.children = n.children.flatMap
& is n.name ? [ n.name, ": ", n.value ] : &
updateParentPointers n
// Fall through to handle PinPattern
// (It won't be found by gatherRecursiveAll because wasn't a child)
n = n.value
if n.type is "PinPattern"
n.ref = makeRef
n.expression.type is "Identifier" ? n.expression.name : "pin"
n.children = [n.ref]
updateParentPointers n
thisAssignments.push
type: "AssignmentExpression"
children: [n.expression, " = ", n.ref]
names: []
lhs: n.expression as any
assigned: n.expression
expression: n.ref
continue

if opts?.injectParamProps and n.type is "Parameter" and n.accessModifier
for each id of n.names
thisAssignments.push
Expand Down
1 change: 1 addition & 0 deletions source/parser/function.civet
Original file line number Diff line number Diff line change
Expand Up @@ -986,6 +986,7 @@ function processParams(f: FunctionNode): void

[splices, thisAssignments] := gatherBindingCode parameters,
injectParamProps: isConstructor
assignPins: true

// `@(@x: number)` adds `x: number` declaration to class body
if isConstructor
Expand Down
3 changes: 2 additions & 1 deletion source/parser/types.civet
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ export type AssignmentExpression
names: string[] | null
lhs: AssignmentExpressionLHS
assigned: ASTNode
expression: ExpressionNode
expression: ExpressionNode | ASTRef
hoistDec?: ASTNode

export type AssignmentExpressionLHS = [undefined, NonNullable<ASTNode>, [WSNode, [string, WSNode]], ASTLeaf][]
Expand Down Expand Up @@ -835,6 +835,7 @@ export type PinPattern =
children: Children
parent?: Parent
expression: ExpressionNode
ref?: ASTRef

// _?, __
export type Whitespace = (ASTLeaf | ASTString)[]?
Expand Down
56 changes: 56 additions & 0 deletions test/function.civet
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,62 @@ describe "function", ->
(a) => {this.a = a;return 1}
"""

describe "^pin params", ->
testCase """
empty function body
---
(^a, ^b) ->
---
(function(a1, b1) {a = a1;b = b1;})
"""

testCase """
local reference
---
(^a, ^b.c) ->
a++
---
(function(a1, pin) {
a = a1;
b.c = pin;
return a++
})
"""

testCase """
object binding pattern with alias
---
({^a, b: ^b}) ->
a
---
(function({a: a1, b: b1}) {
a = a1;
b = b1;
return a
})
"""

testCase """
array binding pattern with alias
---
([^a, ^b]) ->
a
---
(function([a1, b1]) {
a = a1;
b = b1;
return a
})
"""

testCase """
short fat arrow
---
(^a) => 1
---
(a1) => {a = a1;return 1}
"""

testCase """
longhand
---
Expand Down

0 comments on commit d142b63

Please sign in to comment.