Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lone finally block provides cleanup for rest of block #1566

Merged
merged 1 commit into from
Nov 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions civet.dev/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -2103,6 +2103,23 @@ try
catch {message: /^EPIPE:/}
</Playground>

Finally, you can specify a `finally` block without a `try` block,
and it automatically wraps the rest of the block (similar to
`defer` in
[Zig](https://ziglang.org/documentation/master/#defer) and
[Swift](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/statements/#Defer-Statement)):

<Playground>
depth .= 0
function recurse(node)
depth++
finally depth--
console.log depth, "entering", node
finally console.log depth, "exiting", node
return unless node?
recurse child for child of node
</Playground>

### Do Blocks

To put multiple lines in a scope and possibly an expression,
Expand Down
21 changes: 18 additions & 3 deletions source/parser.hera
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,17 @@ let filename; // filename currently being parsed
let initialConfig; // input for parser config
let config; // current parser config after directives
let sync; // synchronous mode: as much as possible without await
export const state = {}; // parser state
export const state = { // parser state
// These get (re)set in Reset, but are included here for type inference
forbidClassImplicitCall: [false],
forbidIndentedApplication: [false],
forbidBracedApplication: [false],
forbidTrailingMemberProperty: [false],
forbidNestedBinaryOp: [false],
forbidNewlineBinaryOp: [false],
forbidPipeline: [false],
JSXTagStack: [undefined],
}

export const getState = () => state;
export const getConfig = () => config;
Expand Down Expand Up @@ -4318,6 +4328,7 @@ Statement
return $1
SwitchStatement !ShouldExpressionize -> $1
TryStatement !ShouldExpressionize -> $1
FinallyClause

EmptyStatement

Expand Down Expand Up @@ -5025,7 +5036,7 @@ IgnoreColon

# https://262.ecma-international.org/#prod-TryStatement
TryStatement
Try !":" NoPostfixBracedOrEmptyBlock CatchClause* ElseClause? FinallyClause? ->
Try !":" NoPostfixBracedOrEmptyBlock CatchClause* ElseClause? WFinallyClause? ->
return processTryBlock($0)

# https://262.ecma-international.org/#prod-Catch
Expand Down Expand Up @@ -5055,9 +5066,13 @@ CatchBinding
children: [ ws, open, parameter, close ]
}

WFinallyClause
( Nested / _ ) FinallyClause ->
return prepend($1, $2)

# https://262.ecma-international.org/#prod-Finally
FinallyClause
( Nested / _ ) Finally ( BracedThenClause / BracedOrEmptyBlock ):block ->
Finally ( BracedThenClause / BracedOrEmptyBlock ):block ->
return {
type: "FinallyClause",
children: $0,
Expand Down
4 changes: 2 additions & 2 deletions source/parser/block.civet
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,8 @@ function insertHoistDec(block: BlockStatement, node: ASTNode | StatementTuple, d

function processBlocks(statements: StatementTuple[]): void
insertSemicolon(statements)
gatherRecursive statements, .type is "BlockStatement"
.forEach ({ expressions }) =>

for each { expressions } of gatherRecursive statements, .type is "BlockStatement"
processBlocks expressions

/**
Expand Down
27 changes: 26 additions & 1 deletion source/parser/lib.civet
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import type {
ASTLeaf
ASTNode
ASTNodeObject
ASTNodeParent
ASTRef
BlockStatement
Call
Expand Down Expand Up @@ -499,6 +498,7 @@ function processTryBlock($0: [ASTLeaf, undefined, BlockStatement, CatchClause[],

blocks := [b]
blocks.push c.block if c
// Omitting finally block here, to avoid implicit return from finally block

return {
type: "TryStatement"
Expand Down Expand Up @@ -1334,6 +1334,30 @@ function processNegativeIndexAccess(statements: StatementTuple[]): void
".length"
]

function processFinallyClauses(statements: StatementTuple[]): void
for each let f of gatherRecursiveAll statements, ($): $ is FinallyClause => (
$.type is "FinallyClause" and $.parent?.type is not "TryStatement"
)
unless { block, index } := blockContainingStatement f
throw new Error "finally clause must be inside try statement or block"
indent := block.expressions[index][0]
expressions := block.expressions[>index]
t: BlockStatement := makeNode {
type: "BlockStatement"
expressions
children: [ "{", expressions, "}" ]
bare: false
}
f = prepend(" ", f) as FinallyClause
tuple: StatementTuple := [indent,
makeNode
type: "TryStatement"
blocks: [ t ] // omit f to avoid implicit return
children: [ "try ", t, f ]
parent: block
]
block.expressions[>=index] = [tuple]

function processProgram(root: BlockStatement): void
state := getState()
config := getConfig()
Expand Down Expand Up @@ -1370,6 +1394,7 @@ function processProgram(root: BlockStatement): void
processStatementExpressions(statements)
processPatternMatching(statements)
processIterationExpressions(statements)
processFinallyClauses(statements)

// Hoist hoistDec attributes to actual declarations.
// NOTE: This should come after iteration expressions get processed
Expand Down
7 changes: 6 additions & 1 deletion source/parser/types.civet
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,8 @@ export type TryStatement
type: "TryStatement"
children: Children
parent?: Parent
// `blocks` should list all blocks that need implicit return:
// main and `catch` blocks, but not `finally `block
blocks: BlockStatement[]

export type CatchClause
Expand Down Expand Up @@ -676,7 +678,10 @@ export type CatchPattern

export type FinallyClause
type: "FinallyClause"
children: Children & [ Whitespace | ASTString, FinallyToken, BlockStatement ]
children: Children & (
| [ FinallyToken, BlockStatement ]
| [ Whitespace | ASTString, FinallyToken, BlockStatement ]
)
parent?: Parent
block: BlockStatement

Expand Down
83 changes: 83 additions & 0 deletions test/try.civet
Original file line number Diff line number Diff line change
Expand Up @@ -375,3 +375,86 @@ describe "try", ->
---
ParseErrors: unknown:3:10 Only one catch clause allowed unless using pattern matching
"""

describe "lone finally blocks", ->
testCase """
basic
---
init()
finally cleanup()
process()
process()
---
init()
try {
process()
process()} finally { cleanup() }
"""

testCase """
indented block
---
init()
finally
cleanup1()
cleanup2()
process()
process()
---
init()
try {
process()
process()} finally {
cleanup1()
cleanup2()
}
"""

testCase """
multiple blocks
---
init()
finally cleanup()
loop
start()
finally stop()
process()
---
init()
try {
while(true) {
start()
try {
process()} finally { stop() }
}} finally { cleanup() }
"""

testCase """
multiple consecutive finally
---
init()
finally cleanup1()
finally cleanup2()
process()
---
init()
try {
try {
process()} finally { cleanup2() }} finally { cleanup1() }
"""

testCase """
multiple nonconsecutive finally
---
init1()
finally cleanup1()
init2()
finally cleanup2()
process()
---
init1()
try {
init2()
try {
process()} finally { cleanup2() }} finally { cleanup1() }
"""
Loading