Skip to content

Commit

Permalink
Merge pull request #816 from DanielXMoore/indented-params
Browse files Browse the repository at this point in the history
Indented function parameters
  • Loading branch information
edemaine authored Nov 23, 2023
2 parents 61c8f2d + 0ed88df commit f745e54
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 15 deletions.
102 changes: 90 additions & 12 deletions source/parser.hera
Original file line number Diff line number Diff line change
Expand Up @@ -1230,9 +1230,53 @@ ArrowParameters
Parameters

NonEmptyParameters
# NOTE: BindingElement -> ParameterElement
TypeParameters?:tp OpenParen:open ThisType?:tt ParameterElement*:pes FunctionRestParameter?:rest ParameterElement*:after ( __ CloseParen ):close ->
const names = pes.flatMap(p => p.names)
TypeParameters?:tp OpenParen:open ParameterList:params ( __ CloseParen ):close ->
// Categorize arguments to put any ThisType in front, and split remaining
// arguments into before and after the rest parameter.
let tt, before = [], rest, after = [], errors = []
function append(p) {
(rest ? after : before).push(p)
}
for (const param of params) {
switch (param.type) {
case "ThisType":
if (tt) {
append({
type: "Error",
message: "Only one typed this parameter is allowed",
})
append(param)
} else {
tt = insertTrimmingSpace(param, "")
if (before.length || rest) { // moving ThisType to front
let delim = tt.children.at(-1)
if (Array.isArray(delim)) delim = delim.at(-1)
if (delim?.token !== ",") {
tt = {
...tt,
children: [...tt.children, ", "],
}
}
}
}
break
case "FunctionRestParameter":
if (rest) {
append({
type: "Error",
message: "Only one rest parameter is allowed",
})
append(param)
} else {
rest = param
}
break
default:
append(param)
}
}

const names = before.flatMap(p => p.names)
if (rest) {
const restIdentifier = rest.binding.ref || rest.binding
names.push(...rest.names || [])
Expand All @@ -1251,7 +1295,7 @@ NonEmptyParameters
tp,
open,
tt,
...pes,
...before,
// Remove delimiter
{...rest, children: rest.children.slice(0, -1)},
close,
Expand All @@ -1264,29 +1308,63 @@ NonEmptyParameters

return {
type: "Parameters",
children: [tp, open, tt, ...pes, close],
names: pes.flatMap((p) => p.names),
children: [tp, open, tt, ...before, close],
names,
tp,
}

ParameterList
# Nested case: Allow for one line of parameters followed by a nested list
Parameter* NestedParameterList ->
return [...$1, ...$2]
# Otherwise, try parsing while ignore indentation
( __ Parameter )* ->
return $1.map(([eos, p]) => ({
...p,
children: [eos, ...p.children],
}))

NestedParameterList
PushIndent NestedParameter*:params PopIndent ->
if (!params.length) return $skip
return params

NestedParameter
# Allow one or more parameters on one line
Nested:ws Parameter+:params ->
// Attach whitespace to first parameter
params = [...params]
params[0] = {
...params[0],
children: [ws, ...params[0].children],
}
return params

Parameter
ThisType
ParameterElement
FunctionRestParameter

# https://262.ecma-international.org/#prod-FunctionRestParameter
FunctionRestParameter
__ BindingRestElement:id TypeSuffix? ParameterElementDelimiter ->
# BindingRestElement has a leading _?
# but also sometimes invokes __ via BindingIdentifier
!EOS BindingRestElement:id TypeSuffix? ParameterElementDelimiter ->
return {
type: "FunctionRestParameter",
children: $0,
children: $0.slice(1),
names: id.names,
binding: id.binding,
}

# NOTE: Similar to BindingElement but appears in formal parameters list
ParameterElement
__ AccessModifier? ( BindingIdentifier / BindingPattern ) TypeSuffix? Initializer? ParameterElementDelimiter ->
_? AccessModifier?:accessModifier _? ( NWBindingIdentifier / BindingPattern ):binding TypeSuffix? Initializer? ParameterElementDelimiter ->
return {
type: "Parameter",
children: $0,
names: $3.names,
accessModifier: $2,
names: binding.names,
accessModifier,
}

ParameterElementDelimiter
Expand Down Expand Up @@ -6589,7 +6667,7 @@ TypeParameterDelimiter

# TypeScript's this: T syntax in function parameters
ThisType
( This / AtThis ) Colon Type ParameterElementDelimiter -> {
_? ( This / AtThis ) Colon Type ParameterElementDelimiter -> {
type: "ThisType",
ts: true,
children: $0
Expand Down
40 changes: 37 additions & 3 deletions test/function.civet
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,13 @@ describe "function", ->
})
"""

throws """
double rest parameter
---
(a, ...b, ...c) ->
c
"""

testCase """
non-end rest parameter
---
Expand Down Expand Up @@ -904,7 +911,6 @@ describe "function", ->
) { return null }
"""

// TODO: wrong indentation of }s
testCase """
indented with types
---
Expand All @@ -918,10 +924,38 @@ describe "function", ->
function f<T>(
t: {
item: T
},
},
u: {
item: T
}
}
){}
"""

testCase """
indented with assignments
---
function f(
x = 0
y = 1
)
---
function f(
x = 0,
y = 1
){}
"""

testCase """
indented with assignments and types
---
function f(
x : number = 0
y : number = 1
)
---
function f(
x : number = 0,
y : number = 1
){}
"""

Expand Down
16 changes: 16 additions & 0 deletions test/types/function.civet
Original file line number Diff line number Diff line change
Expand Up @@ -397,3 +397,19 @@ describe "[TS] function", ->
---
(function (this: T) {})
"""

testCase """
move this type to front
---
function (x: T, @: T,) {}
function (x: T, @: T) {}
---
(function (this: T,x: T,) {});
(function (this: T, x: T,) {})
"""

throws """
double this type
---
function (this: T, @: T) {}
"""

0 comments on commit f745e54

Please sign in to comment.