Skip to content

Commit bae1ff6

Browse files
authored
Merge pull request #1467 from DanielXMoore/braced-block
One-line braced blocks with single-line statements
2 parents 75bb697 + 21a8abe commit bae1ff6

File tree

6 files changed

+139
-47
lines changed

6 files changed

+139
-47
lines changed

civet.dev/comparison.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,22 @@ if (condition)
168168
By contrast, in JavaScript, the last indented line would have been
169169
treated as if it were at the top level.
170170

171+
## Braced Blocks
172+
173+
Civet allows you to wrap your blocks in braces (or not),
174+
but to avoid conflicts with braced object literals,
175+
there are some restrictions.
176+
In particular, a one-line braced block must be on the same line
177+
as the `if` etc. it's part of:
178+
179+
<Playground>
180+
// block
181+
if (x) { y }
182+
// not a block
183+
if (x)
184+
{ y }
185+
</Playground>
186+
171187
## Automatic Semicolon Insertion
172188

173189
JavaScript has [complicated rules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#automatic_semicolon_insertion)

source/parser.hera

Lines changed: 49 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,11 @@ FatArrowToken
764764
"=>" / "⇒" ->
765765
return { $loc, token: "=>" }
766766

767+
TrailingOperator
768+
_? ( BinaryOp / AssignmentOp / Dot / QuestionMark )
769+
_ OperatorAssignmentOp
770+
TrailingDeclaration
771+
767772
TrailingDeclaration
768773
_? ( ConstAssignment / LetAssignment )
769774

@@ -1093,12 +1098,12 @@ ImplementsTarget
10931098
# https://262.ecma-international.org/#prod-ClassBody
10941099
# NOTE: Nesting and indentation sensitive
10951100
ClassBody
1096-
__ OpenBrace NestedClassElements?:expressions __ CloseBrace ->
1097-
if (!expressions) expressions = $0[2] = []
1101+
__:ws1 OpenBrace:open AllowAll ClassBracedContent?:expressions RestoreAll __:ws2 CloseBrace:close ->
1102+
if (!expressions) expressions = []
10981103
return {
10991104
type: "BlockStatement",
11001105
subtype: "ClassBody",
1101-
children: $0,
1106+
children: [ws1, open, expressions, ws2, close],
11021107
expressions,
11031108
}
11041109
InsertOpenBrace NestedClassElements?:expressions InsertNewline InsertIndent InsertCloseBrace ->
@@ -1110,6 +1115,14 @@ ClassBody
11101115
expressions,
11111116
}
11121117

1118+
# based on BracedContent
1119+
ClassBracedContent
1120+
NestedClassElements
1121+
# based on SingleLineStatements
1122+
ForbidNewlineBinaryOp ( ( _? !EOS ) ClassElement StatementDelimiter )*:stmts RestoreNewlineBinaryOp ->
1123+
if (!stmts) return $skip
1124+
return stmts
1125+
11131126
NestedClassElements
11141127
PushIndent NestedClassElement*:elements PopIndent ->
11151128
if (!elements.length) return $skip
@@ -2473,22 +2486,34 @@ Arrow
24732486
return { $loc, token: "->" }
24742487

24752488
ExplicitBlock
2476-
__ OpenBrace __ CloseBrace ->
2477-
const expressions = []
2489+
# Allow single-line block only when the block starts on the same line
2490+
_?:ws1 OpenBrace:open AllowAll ( NestedBlockStatements / SingleLineStatements / EmptyBracedContent )?:block RestoreAll __:ws2 CloseBrace:close ->
2491+
if (!block) return $skip
24782492
return {
2479-
type: "BlockStatement",
2480-
expressions,
2481-
children: [$1, $2, expressions, $3, $4],
2493+
...block,
2494+
children: [ws1, open, ...block.children, ws2, close],
24822495
bare: false,
2483-
empty: true,
24842496
}
2485-
__ OpenBrace NestedBlockStatements:block __ CloseBrace ->
2497+
# For backward compatibility with JavaScript, allow open brace to start on
2498+
# the next line, as long as it's followed by a newline or close brace
2499+
IndentedAtLeast:ws1 OpenBrace:open AllowAll ( NestedBlockStatements / EmptyBracedContent )?:block RestoreAll __:ws2 CloseBrace:close ->
2500+
if (!block) return $skip
24862501
return {
24872502
...block,
2488-
children: [$1, $2, ...block.children, $4, $5],
2503+
children: [ws1, open, ...block.children, ws2, close],
24892504
bare: false,
24902505
}
24912506

2507+
EmptyBracedContent
2508+
&( __ "}" ) ->
2509+
const expressions = []
2510+
return {
2511+
type: "BlockStatement",
2512+
expressions,
2513+
children: [expressions],
2514+
empty: true,
2515+
}
2516+
24922517
ImplicitNestedBlock
24932518
# NOTE: Check &EOS needed by eventual PushIndent to skip work if not needed
24942519
&EOS InsertOpenBrace:open AllowAll ( NestedBlockStatements InsertNewline InsertIndent InsertCloseBrace )? RestoreAll ->
@@ -2668,16 +2693,7 @@ NoCommaBracedBlock
26682693
}
26692694

26702695
NonSingleBracedBlock
2671-
_?:ws1 OpenBrace:open AllowAll ( BracedContent __ CloseBrace )? RestoreAll ->
2672-
if (!$4) return $skip
2673-
const [block, ws2, close] = $4
2674-
return {
2675-
type: "BlockStatement",
2676-
expressions: block.expressions,
2677-
children: [ws1, open, ...block.children, ws2, close],
2678-
bare: false,
2679-
}
2680-
return block
2696+
!EOS ExplicitBlock !TrailingOperator -> $2
26812697

26822698
# NOTE: Added indentation based implied braces
26832699
ImplicitNestedBlock
@@ -2711,10 +2727,7 @@ SingleLineStatements
27112727
}
27122728

27132729
const children = [expressions]
2714-
2715-
if (hasTrailingComment) {
2716-
children.push("\n")
2717-
}
2730+
if (hasTrailingComment) children.push("\n")
27182731

27192732
return {
27202733
type: "BlockStatement",
@@ -2747,18 +2760,6 @@ PostfixedSingleLineNoCommaStatements
27472760
bare: true,
27482761
}
27492762

2750-
BracedContent
2751-
NestedBlockStatements
2752-
SingleLineStatements
2753-
# Empty content
2754-
&( __ "}" ) ->
2755-
const expressions = []
2756-
return {
2757-
type: "BlockStatement",
2758-
expressions,
2759-
children: [expressions],
2760-
}
2761-
27622763
NestedBlockStatements
27632764
PushIndent NestedBlockStatement*:statements PopIndent ->
27642765
if (!statements.length) return $skip
@@ -4928,7 +4929,7 @@ Condition
49284929
children: [open, ws, expression, close],
49294930
expression,
49304931
}
4931-
ParenthesizedExpression !( _? ( BinaryOp / AssignmentOp / Dot / QuestionMark ) ) !( _ OperatorAssignmentOp ) -> $1
4932+
ParenthesizedExpression !TrailingOperator -> $1
49324933
# NOTE: DeclarationCondition must come before the ExpressionCondition because we need to look ahead to potentially match `:=` or `.=`
49334934
InsertOpenParen:open DeclarationCondition:expression InsertCloseParen:close ->
49344935
return {
@@ -7963,7 +7964,7 @@ TypeBullet
79637964
return list
79647965

79657966
TypeWithPostfix
7966-
TypeConditional:t ( ( _ / IndentedFurther )? TypeIfClause )?:postfix ->
7967+
TypeConditional:t ( SameLineOrIndentedFurther TypeIfClause )?:postfix ->
79677968
if (!postfix) return t
79687969
return prepend(postfix[0],
79697970
expressionizeTypeIf([ ...postfix[1], $1, undefined ]))
@@ -8044,7 +8045,7 @@ TypeLiteral
80448045
return { $loc, token: "[]" }
80458046

80468047
InlineInterfaceLiteral
8047-
InsertInlineOpenBrace InlineBasicInterfaceProperty ( ( IndentedFurther / _? ) InlineBasicInterfaceProperty )* InsertCloseBrace
8048+
InsertInlineOpenBrace InlineBasicInterfaceProperty ( SameLineOrIndentedFurther InlineBasicInterfaceProperty )* InsertCloseBrace
80488049

80498050
InlineBasicInterfaceProperty
80508051
# NOTE: Not using TypeSuffix here to require a Colon, and to forbid spaces
@@ -8053,7 +8054,7 @@ InlineBasicInterfaceProperty
80538054

80548055
InlineInterfacePropertyDelimiter
80558056
( _? Semicolon ) / CommaDelimiter
8056-
&( ( IndentedFurther / _? ) InlineBasicInterfaceProperty ) InsertComma -> $2
8057+
&( SameLineOrIndentedFurther InlineBasicInterfaceProperty ) InsertComma -> $2
80578058
&( __ ( ":" / ")" / "]" / "}" ) )
80588059
&EOS
80598060

@@ -8651,6 +8652,13 @@ NotDedented
86518652
if ($2) ws.push(...$2)
86528653
return ws.flat(Infinity).filter(Boolean)
86538654

8655+
SameLineOrIndentedFurther
8656+
IndentedFurther? _? ->
8657+
const ws = []
8658+
if ($1) ws.push(...$1)
8659+
if ($2) ws.push(...$2)
8660+
return ws.flat(Infinity).filter(Boolean)
8661+
86548662
Dedented
86558663
!IndentedAtLeast EOS -> $2
86568664

test/class.civet

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ describe "class", ->
99
class X {}
1010
"""
1111

12+
testCase """
13+
one-line
14+
---
15+
class X { a = 5; b = 10 }
16+
---
17+
class X { a = 5; b = 10 }
18+
"""
19+
1220
testCase """
1321
empty statement
1422
---

test/do.civet

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,16 @@ describe "do", ->
124124
let ref;{ref = void 0;};x = ref
125125
"""
126126

127+
testCase """
128+
braces in a single line
129+
---
130+
do { x } = y
131+
do { x } := y
132+
---
133+
{ ({ x } = y) }
134+
{ const { x } = y }
135+
"""
136+
127137
testCase """
128138
expression
129139
---

test/if.civet

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,28 @@ describe "if", ->
1313
}
1414
"""
1515

16+
testCase """
17+
if with braces on next line
18+
---
19+
if (true)
20+
{
21+
return false
22+
}
23+
---
24+
if (true)
25+
{
26+
return false
27+
}
28+
"""
29+
30+
testCase """
31+
parenthesized and braced one-line
32+
---
33+
if (true) { return false }
34+
---
35+
if (true) { return false }
36+
"""
37+
1638
testCase """
1739
if with braced body that could be object literal
1840
---
@@ -25,6 +47,17 @@ describe "if", ->
2547
}
2648
"""
2749

50+
testCase """
51+
if with braced body that is an object literal
52+
---
53+
if true
54+
{ x }
55+
---
56+
if (true) {
57+
({ x })
58+
}
59+
"""
60+
2861
testCase """
2962
inline parenthesized expression
3063
---
@@ -510,6 +543,23 @@ describe "if", ->
510543
cond2))) { return }
511544
"""
512545

546+
testCase """
547+
postfix if with braced expression after
548+
---
549+
=>
550+
f(x) if x
551+
{
552+
x
553+
}
554+
---
555+
() => {
556+
if (x) { f(x) }
557+
return ({
558+
x
559+
})
560+
}
561+
"""
562+
513563
testCase """
514564
implied parens parethesized expression
515565
---

test/object.civet

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -269,31 +269,31 @@ describe "object", ->
269269
throws """
270270
doesn't allow bare assignments inside
271271
---
272-
{x=y}
272+
obj = {x=y}
273273
---
274274
ParseError
275275
"""
276276

277277
throws """
278278
doesn't allow update assignments inside
279279
---
280-
{x+=y}
280+
obj = {x+=y}
281281
---
282282
ParseError
283283
"""
284284

285285
throws """
286286
doesn't allow update assignments inside
287287
---
288-
{x-=y}
288+
obj = {x-=y}
289289
---
290290
ParseError
291291
"""
292292

293293
throws """
294294
doesn't allow comparisons inside
295295
---
296-
{x<=y}
296+
obj = {x<=y}
297297
---
298298
ParseError
299299
"""
@@ -1208,15 +1208,15 @@ describe "object", ->
12081208
throws """
12091209
no new properties
12101210
---
1211-
{new.target}
1211+
obj = {new.target}
12121212
---
12131213
ParseError
12141214
"""
12151215

12161216
throws """
12171217
no meta properties
12181218
---
1219-
{import.meta}
1219+
obj = {import.meta}
12201220
---
12211221
ParseError
12221222
"""

0 commit comments

Comments
 (0)