UNPKG

aran

Version:
237 lines (227 loc) 7.34 kB
import { every, flatenTree, hasNarrowKey, map } from "../util/index.mjs"; import { makeModuleDeclaration } from "./program-module.mjs"; import { AranTypeError, AranClashError } from "../error.mjs"; import { listProgramParameterDeclaration } from "./program-parameter.mjs"; import { isModuleHeader } from "../lang/index.mjs"; import { listIntrinsicDeclaration } from "./intrinsic.mjs"; import { listDeclaration } from "./declaration.mjs"; import { retroStatement } from "./statement.mjs"; import { retroExpression } from "./expression.mjs"; import { listExternalOptimization } from "./optimize-external.mjs"; import { STRICT_KEYWORD_RECORD } from "estree-sentry"; import { checkClash } from "./clash.mjs"; import { makeSimpleLiteral } from "./literal.mjs"; /** * @type {( * header: import("../lang/header.d.ts").DeclareHeader, * config: import("./config-internal.d.ts").InternalConfig, * ) => import("estree-sentry").Statement<{}>} */ export const declare = ({ kind, variable }, config) => { const clash = checkClash(variable, config); if (clash) { throw new AranClashError(clash); } else { return { type: "VariableDeclaration", kind, declarations: [ { type: "VariableDeclarator", id: { type: "Identifier", name: variable, }, init: null, }, ], }; } }; /** * @type {( * expression: import("estree-sentry").Expression<{}>, * ) => import("estree-sentry").Statement<{}>} */ const makeReturnStatement = (argument) => ({ type: "ReturnStatement", argument, }); /** * @type {( * expression: import("estree-sentry").Expression<{}>, * ) => import("estree-sentry").Statement<{}>} */ const makeExpressionStatement = (expression) => ({ type: "ExpressionStatement", directive: null, expression, }); /** * @type {( * node: import("./atom.d.ts").RoutineBlock, * config: import("./config-internal.d.ts").InternalConfig, * makeCompletion: ( * expression: import("estree-sentry").Expression<{}>, * ) => import("estree-sentry").Statement<{}> * ) => import("../util/tree.d.ts").Tree<import("estree-sentry").Statement<{}>>} */ const retroRoutineBlock = (node, config, makeCompletion) => [ listDeclaration(node.bindings, config), map(node.body, (child) => retroStatement(child, config)), makeCompletion(retroExpression(node.tail, config)), ]; /** * @type {( * node: import("./atom.d.ts").Program, * config: import("./config-internal.d.ts").InternalConfig, * optimization: import("./optimize-external.d.ts").Optimization, * ) => import("estree-sentry").Program<{}>} */ const makeModuleProgram = (node, config, optimization) => ({ type: "Program", sourceType: "module", body: flatenTree([ listIntrinsicDeclaration(node, config), map( /** @type {import("../lang/header.d.ts").Header[]} */ (node.head), (header) => isModuleHeader(header) ? makeModuleDeclaration(header, config) : null, ), listProgramParameterDeclaration(node, config), optimization.strict, optimization.either, retroRoutineBlock(node.body, config, makeExpressionStatement), ]), }); /** * @type {( * node: import("./atom.d.ts").Program, * config: import("./config-internal.d.ts").InternalConfig, * optimization: import("./optimize-external.d.ts").Optimization, * ) => import("estree-sentry").Program<{}>} */ const makeStrictScriptProgram = (node, config, optimizations) => ({ type: "Program", sourceType: "script", body: flatenTree([ { type: "ExpressionStatement", directive: /** @type {import("estree-sentry").Directive} */ ( "use strict" ), expression: makeSimpleLiteral("use strict"), }, map( /** @type {import("../lang/header.d.ts").Header[]} */ (node.head), (header) => (header.type === "declare" ? declare(header, config) : null), ), { type: "BlockStatement", body: flatenTree([ listIntrinsicDeclaration(node, config), listProgramParameterDeclaration(node, config), optimizations.strict, retroRoutineBlock(node.body, config, makeExpressionStatement), ]), }, ]), }); /** * @type {( * node: import("./atom.d.ts").Program, * config: import("./config-internal.d.ts").InternalConfig, * optimization: import("./optimize-external.d.ts").Optimization, * ) => import("estree-sentry").Program<{}>} */ const makeSloppyScriptProgram = (node, config, optimization) => ({ type: "Program", sourceType: "script", body: flatenTree([ map( /** @type {import("../lang/header.d.ts").Header[]} */ (node.head), (header) => (header.type === "declare" ? declare(header, config) : null), ), { type: "BlockStatement", body: flatenTree([ listIntrinsicDeclaration(node, config), listProgramParameterDeclaration(node, config), optimization.sloppy, { type: "ExpressionStatement", directive: null, expression: { type: "CallExpression", optional: false, callee: { type: "ArrowFunctionExpression", id: null, params: [], async: false, expression: false, generator: false, body: { type: "BlockStatement", body: flatenTree([ { type: "ExpressionStatement", directive: /** @type {import("estree-sentry").Directive} */ ( "use strict" ), expression: makeSimpleLiteral("use strict"), }, optimization.strict, optimization.either, retroRoutineBlock(node.body, config, makeReturnStatement), ]), }, }, arguments: [], }, }, ]), }, ]), }); /** * @type {( * header: import("../lang/header.d.ts").DeclareHeader, * ) => boolean} */ const canBeStrictDeclaration = ({ variable }) => variable !== "eval" && variable !== "arguments" && !hasNarrowKey(STRICT_KEYWORD_RECORD, variable); /** * @type {( * node: import("./atom.d.ts").Program, * config: import("./config-internal.d.ts").InternalConfig, * ) => import("estree-sentry").Program<{}>} */ export const retroProgram = (node, config) => { const optimization = listExternalOptimization(node, config); if (node.kind === "module") { return makeModuleProgram(node, config, optimization); } else if (node.kind === "script" || node.kind === "eval") { if (node.situ === "local.root") { return makeSloppyScriptProgram(node, config, optimization); } else if (node.situ === "local.deep") { return makeStrictScriptProgram(node, config, optimization); } else if (node.situ === "global") { if ( optimization.sloppy.length === 0 && every(node.head, canBeStrictDeclaration) ) { return makeStrictScriptProgram(node, config, optimization); } else { return makeSloppyScriptProgram(node, config, optimization); } } else { throw new AranTypeError(node); } } else { throw new AranTypeError(node); } };