279 lines
6.3 KiB
TypeScript
279 lines
6.3 KiB
TypeScript
import { expect, describe, test } from 'bun:test'
|
|
|
|
import '../shrimp.grammar' // Importing this so changes cause it to retest!
|
|
|
|
describe('try/catch/finally/throw', () => {
|
|
test('parses try with catch', () => {
|
|
expect(`try:
|
|
risky-operation
|
|
catch err:
|
|
handle-error err
|
|
end`).toMatchTree(`
|
|
TryExpr
|
|
keyword try
|
|
colon :
|
|
TryBlock
|
|
FunctionCallOrIdentifier
|
|
Identifier risky-operation
|
|
CatchExpr
|
|
keyword catch
|
|
Identifier err
|
|
colon :
|
|
TryBlock
|
|
FunctionCall
|
|
Identifier handle-error
|
|
PositionalArg
|
|
Identifier err
|
|
keyword end
|
|
`)
|
|
})
|
|
|
|
test('parses try with finally', () => {
|
|
expect(`try:
|
|
do-work
|
|
finally:
|
|
cleanup
|
|
end`).toMatchTree(`
|
|
TryExpr
|
|
keyword try
|
|
colon :
|
|
TryBlock
|
|
FunctionCallOrIdentifier
|
|
Identifier do-work
|
|
FinallyExpr
|
|
keyword finally
|
|
colon :
|
|
TryBlock
|
|
FunctionCallOrIdentifier
|
|
Identifier cleanup
|
|
keyword end
|
|
`)
|
|
})
|
|
|
|
test('parses try with catch and finally', () => {
|
|
expect(`try:
|
|
risky-operation
|
|
catch err:
|
|
handle-error err
|
|
finally:
|
|
cleanup
|
|
end`).toMatchTree(`
|
|
TryExpr
|
|
keyword try
|
|
colon :
|
|
TryBlock
|
|
FunctionCallOrIdentifier
|
|
Identifier risky-operation
|
|
CatchExpr
|
|
keyword catch
|
|
Identifier err
|
|
colon :
|
|
TryBlock
|
|
FunctionCall
|
|
Identifier handle-error
|
|
PositionalArg
|
|
Identifier err
|
|
FinallyExpr
|
|
keyword finally
|
|
colon :
|
|
TryBlock
|
|
FunctionCallOrIdentifier
|
|
Identifier cleanup
|
|
keyword end
|
|
`)
|
|
})
|
|
|
|
test('parses single-line try with catch', () => {
|
|
expect('result = try: parse-number input catch err: 0 end').toMatchTree(`
|
|
Assign
|
|
AssignableIdentifier result
|
|
Eq =
|
|
TryExpr
|
|
keyword try
|
|
colon :
|
|
FunctionCall
|
|
Identifier parse-number
|
|
PositionalArg
|
|
Identifier input
|
|
CatchExpr
|
|
keyword catch
|
|
Identifier err
|
|
colon :
|
|
Number 0
|
|
keyword end
|
|
`)
|
|
})
|
|
|
|
test('parses single-line try with finally', () => {
|
|
expect('try: work catch err: 0 finally: cleanup end').toMatchTree(`
|
|
TryExpr
|
|
keyword try
|
|
colon :
|
|
FunctionCallOrIdentifier
|
|
Identifier work
|
|
CatchExpr
|
|
keyword catch
|
|
Identifier err
|
|
colon :
|
|
Number 0
|
|
FinallyExpr
|
|
keyword finally
|
|
colon :
|
|
FunctionCallOrIdentifier
|
|
Identifier cleanup
|
|
keyword end
|
|
`)
|
|
})
|
|
|
|
test('parses throw statement with string', () => {
|
|
expect("throw 'error message'").toMatchTree(`
|
|
Throw
|
|
keyword throw
|
|
String
|
|
StringFragment error message
|
|
`)
|
|
})
|
|
|
|
test('parses throw statement with identifier', () => {
|
|
expect('throw error-object').toMatchTree(`
|
|
Throw
|
|
keyword throw
|
|
Identifier error-object
|
|
`)
|
|
})
|
|
|
|
test('parses throw statement with dict', () => {
|
|
expect('throw [type=validation-error message=failed]').toMatchTree(`
|
|
Throw
|
|
keyword throw
|
|
Dict
|
|
NamedArg
|
|
NamedArgPrefix type=
|
|
Identifier validation-error
|
|
NamedArg
|
|
NamedArgPrefix message=
|
|
Identifier failed
|
|
`)
|
|
})
|
|
|
|
test('does not parse identifiers that start with try', () => {
|
|
expect('trying = try: work catch err: 0 end').toMatchTree(`
|
|
Assign
|
|
AssignableIdentifier trying
|
|
Eq =
|
|
TryExpr
|
|
keyword try
|
|
colon :
|
|
FunctionCallOrIdentifier
|
|
Identifier work
|
|
CatchExpr
|
|
keyword catch
|
|
Identifier err
|
|
colon :
|
|
Number 0
|
|
keyword end
|
|
`)
|
|
})
|
|
})
|
|
|
|
describe('function-level exception handling', () => {
|
|
test('parses function with catch', () => {
|
|
expect(`read-file = do path:
|
|
read-data path
|
|
catch e:
|
|
empty-string
|
|
end`).toMatchTree(`
|
|
Assign
|
|
AssignableIdentifier read-file
|
|
Eq =
|
|
FunctionDef
|
|
Do do
|
|
Params
|
|
Identifier path
|
|
colon :
|
|
FunctionCall
|
|
Identifier read-data
|
|
PositionalArg
|
|
Identifier path
|
|
CatchExpr
|
|
keyword catch
|
|
Identifier e
|
|
colon :
|
|
TryBlock
|
|
FunctionCallOrIdentifier
|
|
Identifier empty-string
|
|
keyword end
|
|
`)
|
|
})
|
|
|
|
test('parses function with finally', () => {
|
|
expect(`cleanup-task = do x:
|
|
do-work x
|
|
finally:
|
|
close-resources
|
|
end`).toMatchTree(`
|
|
Assign
|
|
AssignableIdentifier cleanup-task
|
|
Eq =
|
|
FunctionDef
|
|
Do do
|
|
Params
|
|
Identifier x
|
|
colon :
|
|
FunctionCall
|
|
Identifier do-work
|
|
PositionalArg
|
|
Identifier x
|
|
FinallyExpr
|
|
keyword finally
|
|
colon :
|
|
TryBlock
|
|
FunctionCallOrIdentifier
|
|
Identifier close-resources
|
|
keyword end
|
|
`)
|
|
})
|
|
|
|
test('parses function with catch and finally', () => {
|
|
expect(`safe-operation = do x:
|
|
risky-work x
|
|
catch err:
|
|
log err
|
|
default-value
|
|
finally:
|
|
cleanup
|
|
end`).toMatchTree(`
|
|
Assign
|
|
AssignableIdentifier safe-operation
|
|
Eq =
|
|
FunctionDef
|
|
Do do
|
|
Params
|
|
Identifier x
|
|
colon :
|
|
FunctionCall
|
|
Identifier risky-work
|
|
PositionalArg
|
|
Identifier x
|
|
CatchExpr
|
|
keyword catch
|
|
Identifier err
|
|
colon :
|
|
TryBlock
|
|
FunctionCall
|
|
Identifier log
|
|
PositionalArg
|
|
Identifier err
|
|
FunctionCallOrIdentifier
|
|
Identifier default-value
|
|
FinallyExpr
|
|
keyword finally
|
|
colon :
|
|
TryBlock
|
|
FunctionCallOrIdentifier
|
|
Identifier cleanup
|
|
keyword end
|
|
`)
|
|
})
|
|
})
|