Skip to content

Commit

Permalink
Merge branch 'feature/enhanced-try' into prerelease/3.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
wcjohnson committed Oct 26, 2017
2 parents 7ac8e18 + 9d7ab88 commit 9f1b791
Show file tree
Hide file tree
Showing 20 changed files with 203 additions and 157 deletions.
6 changes: 3 additions & 3 deletions src/config.lsc
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ export getMetadata() -> {
defaultValue: "default"
stage: "1"
}
catchExpression: {
description: "Catch and transform errors while evaluating an expression. (HIGHLY EXPERIMENTAL. DO NOT USE IN PRODUCTION.)"
enhancedTry: {
description: "`try` expressions and pattern matching for `catch`. (HIGHLY EXPERIMENTAL. DO NOT USE IN PRODUCTION.)"
valueType: "boolean"
stage: "-50"
}
Expand Down Expand Up @@ -111,7 +111,7 @@ export getParserOpts(pluginOpts, initialParserOpts) ->
if pluginOpts?.placeholderArgs: plugins.push("syntacticPlaceholder")
if pluginOpts?.placeholder:
parserOpts.placeholder = pluginOpts.placeholder
if pluginOpts.catchExpression: plugins.push("catchExpression")
if pluginOpts.enhancedTry: plugins.push("enhancedTry")

// TODO: watch upstream on pattern matching; default to their syntax when complete
// patternMatchingVersion = pluginOpts?.patternMatching or "v4"
Expand Down
3 changes: 3 additions & 0 deletions src/helpers/tails.lsc
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ export getTails(path, allowLoops) ->
add(path.get("finalizer"))
| ~isa("CatchClause"):
add(path.get("body"))
if node.cases:
for idx i in node.cases:
add(path.get(`cases.${i}.consequent`))
| ~isa("MatchStatement"):
for idx i in node.cases:
add(path.get(`cases.${i}.consequent`))
Expand Down
44 changes: 15 additions & 29 deletions src/lscNodeTypes.lsc
Original file line number Diff line number Diff line change
Expand Up @@ -287,36 +287,22 @@ export registerLightscriptNodeTypes(t): void ->
},
});

if not t.hasType("CatchExpression"):
definePluginType("CatchExpression", {
builder: ["expression", "cases"],
visitor: ["expression", "cases"],
if not t.hasType("TryExpression"):
definePluginType("TryExpression", {
builder: ["block", "handler", "finalizer"],
visitor: ["block", "handler", "finalizer"],
aliases: ["Expression"],
fields: {
expression: {
validate: assertNodeType("Expression")
block: {
validate: assertNodeType("Expression", "BlockStatement"),
},
cases: {
validate: chain(assertValueType("array"), assertEach(assertNodeType("CatchCase")))
}
}
});

if not t.hasType("CatchCase"):
definePluginType("CatchCase", {
builder: ["atoms", "binding", "consequent"],
visitor: ["atoms", "binding", "consequent"],
fields: {
atoms: {
validate: chain(assertValueType("array"), assertEach(assertNodeType("Expression")))
optional: true
}
binding: {
validate: assertNodeType("Identifier", "ArrayPattern", "ObjectPattern")
optional: true
}
consequent: {
validate: assertNodeType("Expression", "Statement")
}
}
handler: {
optional: true,
validate: assertNodeType("CatchClause"),
},
finalizer: {
optional: true,
validate: assertNodeType("BlockStatement"),
},
},
});
109 changes: 0 additions & 109 deletions src/transforms/catchExpression.lsc

This file was deleted.

116 changes: 116 additions & 0 deletions src/transforms/try.lsc
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import t, { isa } from '../types'
import { transformTails } from '../helpers/tails'
import { toBlockStatement } from '../helpers/blocks'

import {
getLoc, placeAtLoc as atLoc, placeAtNode as atNode,
getSurroundingLoc, span, traverse,
placeTreeAtLocWhenUnplaced as allAtLoc
} from 'ast-loc-utils'

import { getMatchInfo, transformMatchCases } from './match'

normalizeTryStatement(node, path): void ->
// Turn stuff into blocks as needed
if node.block: node.block = node.block~toBlockStatement()
if node.handler?.body: node.handler.body = node.handler.body~toBlockStatement()
if node.finalizer: node.finalizer = node.finalizer~toBlockStatement()
// Paramless handler gets a default param
if node.handler and (not node.handler.param):
node.handler.param = path.scope.generateUidIdentifier("err")
// Generate default handler
if (not node.handler) and (not node.finalizer):
errId = path.scope.generateUidIdentifier("err")
node.handler = t.catchClause(
errId,
t.blockStatement([t.expressionStatement(errId)])
)

transformVarDeclTryExpression(path, tryExprPath): void ->
{ node } = tryExprPath

// Starting from `const x = try ...`
// Add `let _val`
// let _val
// const x = try ...
resRef = path.scope.generateUidIdentifier("val")
path.insertBefore! t.variableDeclaration("let", [t.variableDeclarator(resRef)])

// replace `try ...` with `_val`
// let _val
// const x = _val
declaratorPath = path.get("declarations.0")
declaratorPath.node.init = resRef

// Turn the original node into a try statement and add it before the decl
// let _val
// try ...
// const x = _val
node.type = "TryStatement"
normalizeTryStatement(node, tryExprPath)
path.insertBefore! node

// Re-tail the `try` statement so it assigns its final value to `_val`
tryPath = path.getPrevSibling()
transformTails(tryPath, false, false, (node) ->
t.assignmentExpression("=", resRef, node)~atNode(node)
)

transformPessimizedTryExpression(path): void ->
{ node } = path

// If there are any awaits beneath us, we need async.
let needsAsync = false
path.traverse({
noScope: true

Function(path): void -> path.skip()

AwaitExpression(awaitPath) ->
now needsAsync = true
awaitPath.stop()

YieldExpression(yieldPath) ->
throw yieldPath.buildCodeFrameError("`yield` is not allowed in `try` expressions.")

ReturnStatement(returnPath) ->
throw returnPath.buildCodeFrameError("`return` is not allowed in `try` expressions.");
})

node.type = "TryStatement"
normalizeTryStatement(node, path)

iife = t.callExpression(
t.arrowFunctionExpression(
[]
t.blockStatement([node])
needsAsync
)
[]
)

path.replaceWith(iife)

isVarDeclTryExpr(path) ->
path.parent~isa("VariableDeclarator")
and path.parentPath.parent.declarations.length == 1
and path.parentPath.parentPath.listKey == "body"

export transformTryExpression(path, isLinter): void ->
if path~isVarDeclTryExpr!:
transformVarDeclTryExpression(path.parentPath.parentPath, path, isLinter)
else:
transformPessimizedTryExpression(path, isLinter)

export transformTryStatement(path): void ->
{ node } = path
normalizeTryStatement(node, path)
path.replaceWith(node)

export transformCatchClause(path, isLinter): void ->
{ node } = path

if node.cases:
nextBody = getMatchInfo(path, path.node.param, isLinter)~transformMatchCases(path.get("cases"))
node.body = nextBody~toBlockStatement()
path.replaceWith(node)
12 changes: 9 additions & 3 deletions src/visitors/main.lsc
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { maybeTransformArrayWithSpreadLoops, maybeTransformObjectWithSpreadLoops
import { transformExistentialExpression, transformSafeSpreadElement } from "../transforms/safe"
import { maybeReplaceWithInlinedOperator } from "../transforms/inlinedOperators"
import { transformForInArrayStatement, transformForInObjectStatement, lintForInArrayStatement, lintForInObjectStatement } from "../transforms/for"
import { transformCatchExpression } from "../transforms/catchExpression"
import { transformTryExpression, transformTryStatement, transformCatchClause } from "../transforms/try"

import { markIdentifier } from "../state/stdlib"

Expand Down Expand Up @@ -222,8 +222,14 @@ export default mainPass(compilerState, programPath): void ->
MatchStatement(path): void ->
matching.transformMatchStatement(path, opts.__linter)

CatchExpression(path): void ->
transformCatchExpression(path, opts.__linter)
TryExpression(path): void ->
transformTryExpression(path, opts.__linter)

TryStatement(path): void ->
transformTryStatement(path, opts.__linter)

CatchClause(path): void ->
transformCatchClause(path, opts.__linter)

ForOfStatement(path): void ->
// Auto-const
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
f() -/>
<- fetchRemote()
try <- fetchRemote()
catch err: false
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ throwInOne() ->
setTimeout(-> reject(new Error()), 1000)

f() -/>
<- throwInOne()
try <- throwInOne()
catch err: "there was a problem"

g() -/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
a = b()
a = try b()
catch err: panic()

c = d() catch err:
c = try d() catch err:
| SpecificError: specificValue
| Error: generalValue

e = f()
e = try f()
catch err:
| SpecificError: specificValue
| Error: generalValue
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ try {
_val = b();
} catch (err) {
_val = panic();
}

const a = _val;
}const a = _val;
let _val2;

try {
Expand All @@ -18,9 +16,7 @@ try {
} else if (_isMatch(Error, err)) {
_val2 = generalValue;
}
}

const c = _val2;
}const c = _val2;
let _val3;

try {
Expand All @@ -32,5 +28,4 @@ try {
_val3 = generalValue;
}
}

const e = _val3;
2 changes: 2 additions & 0 deletions test/fixtures/enhanced-try/iife-return-illegal/actual.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
wrap() ->
f(try g() catch err: return h)
3 changes: 3 additions & 0 deletions test/fixtures/enhanced-try/iife-return-illegal/options.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"throws": "`return` is not allowed in `try` expressions."
}
Loading

0 comments on commit 9f1b791

Please sign in to comment.