From 45c899bd7b17a8adf6aeb39af5db33a45453e8a2 Mon Sep 17 00:00:00 2001 From: arlen22 Date: Wed, 13 Nov 2024 16:12:20 -0500 Subject: [PATCH 01/12] implement example try-expression-proposal syntax parser --- README.md | 62 +++++++-- package.json | 5 +- src/compiler/checker.ts | 35 +++++ src/compiler/emitter.ts | 8 ++ src/compiler/factory/nodeFactory.ts | 19 +++ src/compiler/factory/nodeTests.ts | 6 + src/compiler/parser.ts | 208 +++++++++++++++++----------- src/compiler/types.ts | 10 ++ src/compiler/visitorPublic.ts | 7 + src/lib/es5.d.ts | 13 ++ src/services/formatting/rules.ts | 5 + src/services/utilities.ts | 4 +- 12 files changed, 288 insertions(+), 94 deletions(-) diff --git a/README.md b/README.md index 3314c58f49221..5b8321b645026 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,45 @@ +# Fork of Typescript + +This fork contains a few modifications to Typescript to preview the try-expression-proposal syntax. It is not intended to be used in production. + +## To play around with it for yourself + +- Create a new npm project folder using `npm init`. +- `npm install` the built tgz file from the releases page of this repository. +- In VSCode, create a new typescript file and open it to enable the Typescript engine, then run the command "Typescript: Select Typescript Version" and select the version from your node_modules folder. +- Create a tsconfig.json file with `strictNullChecks` enabled. +- If you run `tsc` it will just pass the syntax through to the Javascript file. + +## The following code should be valid + +```ts +const [error, data]: TryResult<1> = try 1; +if (error) { + const e: TryError = error; + const r: undefined = data; +} else { + const e: undefined = error; + const r: 1 = data; +} +// try at the beginning of an arrow function body +const [error1, data1] = (() => try someExpression() ?? otherexpression() ? true : false)(); +// regular assignment, terminated by comma, semicolon, or other statement. +const [error2, data2] = try someExpression() ?? otherexpression() ? true : false; +// used as an array value, terminated by comma or end of array +const [[error3, data3]] = [try someExpression() ?? otherexpression() ? true : false]; +// used as an object value, terminated by comma or end of object +const { res: [error4, data4] } = { res: try someExpression() ?? otherexpression() ? true : false } +``` + +## Changelog + +### 0.2 + +- Added the try-expression-proposal syntax to the parser basically copying the yield syntax. + +### 0.1 + +- Failed attempt that only partly worked. # TypeScript @@ -6,7 +48,6 @@ [![Downloads](https://img.shields.io/npm/dm/typescript.svg)](https://www.npmjs.com/package/typescript) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/microsoft/TypeScript/badge)](https://securityscorecards.dev/viewer/?uri=github.com/microsoft/TypeScript) - [TypeScript](https://www.typescriptlang.org/) is a language for application-scale JavaScript. TypeScript adds optional types to JavaScript that support tools for large-scale JavaScript applications for any browser, for any host, on any OS. TypeScript compiles to readable, standards-based JavaScript. Try it out at the [playground](https://www.typescriptlang.org/play/), and stay up to date via [our blog](https://blogs.msdn.microsoft.com/typescript) and [Twitter account](https://twitter.com/typescript). Find others who are using TypeScript at [our community page](https://www.typescriptlang.org/community/). @@ -28,12 +69,13 @@ npm install -D typescript@next ## Contribute There are many ways to [contribute](https://github.com/microsoft/TypeScript/blob/main/CONTRIBUTING.md) to TypeScript. -* [Submit bugs](https://github.com/microsoft/TypeScript/issues) and help us verify fixes as they are checked in. -* Review the [source code changes](https://github.com/microsoft/TypeScript/pulls). -* Engage with other TypeScript users and developers on [StackOverflow](https://stackoverflow.com/questions/tagged/typescript). -* Help each other in the [TypeScript Community Discord](https://discord.gg/typescript). -* Join the [#typescript](https://twitter.com/search?q=%23TypeScript) discussion on Twitter. -* [Contribute bug fixes](https://github.com/microsoft/TypeScript/blob/main/CONTRIBUTING.md). + +- [Submit bugs](https://github.com/microsoft/TypeScript/issues) and help us verify fixes as they are checked in. +- Review the [source code changes](https://github.com/microsoft/TypeScript/pulls). +- Engage with other TypeScript users and developers on [StackOverflow](https://stackoverflow.com/questions/tagged/typescript). +- Help each other in the [TypeScript Community Discord](https://discord.gg/typescript). +- Join the [#typescript](https://twitter.com/search?q=%23TypeScript) discussion on Twitter. +- [Contribute bug fixes](https://github.com/microsoft/TypeScript/blob/main/CONTRIBUTING.md). This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) @@ -41,9 +83,9 @@ with any additional questions or comments. ## Documentation -* [TypeScript in 5 minutes](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html) -* [Programming handbook](https://www.typescriptlang.org/docs/handbook/intro.html) -* [Homepage](https://www.typescriptlang.org/) +- [TypeScript in 5 minutes](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html) +- [Programming handbook](https://www.typescriptlang.org/docs/handbook/intro.html) +- [Homepage](https://www.typescriptlang.org/) ## Roadmap diff --git a/package.json b/package.json index 4ddb7a53a7146..1886c2ad5b53f 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "typescript", "author": "Microsoft Corp.", "homepage": "https://www.typescriptlang.org/", - "version": "5.8.0", + "version": "5.8.0-arlen22-0.2", "license": "Apache-2.0", "description": "TypeScript is a language for application scale JavaScript development", "keywords": [ @@ -99,7 +99,8 @@ "lint": "hereby lint", "knip": "hereby knip", "format": "dprint fmt", - "setup-hooks": "node scripts/link-hooks.mjs" + "setup-hooks": "node scripts/link-hooks.mjs", + "pack": "hereby local && hereby LKG && hereby clean" }, "browser": { "fs": false, diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cc8a7f7428fcf..c4deab9cf4672 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1132,6 +1132,8 @@ import { WithStatement, WriterContextOut, YieldExpression, + TryExpression, + isTryExpression, } from "./_namespaces/ts.js"; import * as moduleSpecifiers from "./_namespaces/ts.moduleSpecifiers.js"; import * as performance from "./_namespaces/ts.performance.js"; @@ -1651,6 +1653,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { getParameterIdentifierInfoAtPosition, getPromisedTypeOfPromise, getAwaitedType: type => getAwaitedType(type), + getTryResultType: type => getTryResultType(type), getReturnTypeOfSignature, isNullableType, getNullableType, @@ -2274,6 +2277,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { var deferredGlobalExtractSymbol: Symbol | undefined; var deferredGlobalOmitSymbol: Symbol | undefined; var deferredGlobalAwaitedSymbol: Symbol | undefined; + var deferredGlobalTryResultSymbol: Symbol | undefined; var deferredGlobalBigIntType: ObjectType | undefined; var deferredGlobalNaNSymbol: Symbol | undefined; var deferredGlobalRecordSymbol: Symbol | undefined; @@ -17014,6 +17018,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return deferredGlobalAwaitedSymbol === unknownSymbol ? undefined : deferredGlobalAwaitedSymbol; } + function getGlobalTryResultSymbol(reportErrors: boolean): Symbol | undefined { + // Only cache `unknownSymbol` if we are reporting errors so that we don't report the error more than once. + deferredGlobalTryResultSymbol ||= getGlobalTypeAliasSymbol("TryResult" as __String, /*arity*/ 1, reportErrors) || (reportErrors ? unknownSymbol : undefined); + return deferredGlobalTryResultSymbol === unknownSymbol ? undefined : deferredGlobalTryResultSymbol; + } + function getGlobalBigIntType() { return (deferredGlobalBigIntType ||= getGlobalType("BigInt" as __String, /*arity*/ 0, /*reportErrors*/ false)) || emptyObjectType; } @@ -31386,6 +31396,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return undefined; } + + function getContextualTypeForTryOperand(node: TryExpression, contextFlags: ContextFlags | undefined): Type | undefined { + const contextualType = getContextualType(node, contextFlags); + return contextualType && getTryResultType(contextualType); + } + function getContextualTypeForAwaitOperand(node: AwaitExpression, contextFlags: ContextFlags | undefined): Type | undefined { const contextualType = getContextualType(node, contextFlags); if (contextualType) { @@ -32133,6 +32149,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return getContextualTypeForYieldOperand(parent as YieldExpression, contextFlags); case SyntaxKind.AwaitExpression: return getContextualTypeForAwaitOperand(parent as AwaitExpression, contextFlags); + case SyntaxKind.TryExpression: + return getContextualTypeForTryOperand(parent as TryExpression, contextFlags); case SyntaxKind.CallExpression: case SyntaxKind.NewExpression: return getContextualTypeForArgument(parent as CallExpression | NewExpression | Decorator, node); @@ -39236,6 +39254,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return hasError; } + function checkTryExpression(node: TryExpression): Type { + const operandType = checkExpression(node.expression); + return getTryResultType(operandType) ?? unknownType; + } + function checkAwaitExpression(node: AwaitExpression): Type { addLazyDiagnostic(() => checkAwaitGrammar(node)); @@ -40973,6 +40996,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const type = getQuickTypeOfExpression(expr.expression); return type ? getAwaitedType(type) : undefined; } + if (isTryExpression(expr)) { + const type = getQuickTypeOfExpression(expr.expression); + return type ? getTryResultType(type) : undefined; + } // Optimize for the common case of a call to a function with a single non-generic call // signature where we can just fetch the return type without checking the arguments. if (isCallExpression(expr) && expr.expression.kind !== SyntaxKind.SuperKeyword && !isRequireCall(expr, /*requireStringLiteralLikeArgument*/ true) && !isSymbolOrSymbolForCall(expr)) { @@ -41163,6 +41190,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return checkVoidExpression(node as VoidExpression); case SyntaxKind.AwaitExpression: return checkAwaitExpression(node as AwaitExpression); + case SyntaxKind.TryExpression: + return checkTryExpression(node as TryExpression); case SyntaxKind.PrefixUnaryExpression: return checkPrefixUnaryExpression(node as PrefixUnaryExpression); case SyntaxKind.PostfixUnaryExpression: @@ -42645,6 +42674,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } + function getTryResultType(type: Type, errorNode?: Node, diagnosticMessage?: DiagnosticMessage, ...args: DiagnosticArguments) { + // Nothing to do if `TryResult` doesn't exist + const tryresultSymbol = getGlobalTryResultSymbol(/*reportErrors*/ true); + return tryresultSymbol && getTypeAliasInstantiation(tryresultSymbol, [type]) + } + function getAwaitedTypeOfPromise(type: Type, errorNode?: Node, diagnosticMessage?: DiagnosticMessage, ...args: DiagnosticArguments): Type | undefined { const promisedType = getPromisedTypeOfPromise(type, errorNode); return promisedType && getAwaitedType(promisedType, errorNode, diagnosticMessage, ...args); diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 01a96579861f4..fa4fbacbaf7ad 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -422,6 +422,7 @@ import { writeFile, WriteFileCallbackData, YieldExpression, + TryExpression, } from "./_namespaces/ts.js"; import * as performance from "./_namespaces/ts.performance.js"; @@ -1962,6 +1963,8 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri return emitTemplateExpression(node as TemplateExpression); case SyntaxKind.YieldExpression: return emitYieldExpression(node as YieldExpression); + case SyntaxKind.TryExpression: + return emitTryExpression(node as TryExpression); case SyntaxKind.SpreadElement: return emitSpreadElement(node as SpreadElement); case SyntaxKind.ClassExpression: @@ -2956,6 +2959,11 @@ export function createPrinter(printerOptions: PrinterOptions = {}, handlers: Pri emitExpressionWithLeadingSpace(node.expression && parenthesizeExpressionForNoAsi(node.expression), parenthesizeExpressionForNoAsiAndDisallowedComma); } + function emitTryExpression(node: TryExpression) { + emitTokenWithComment(SyntaxKind.TryKeyword, node.pos, writeKeyword, node); + emitExpressionWithLeadingSpace(node.expression && parenthesizeExpressionForNoAsi(node.expression), parenthesizeExpressionForNoAsiAndDisallowedComma); + } + function emitSpreadElement(node: SpreadElement) { emitTokenWithComment(SyntaxKind.DotDotDotToken, node.pos, writePunctuation, node); emitExpression(node.expression, parenthesizer.parenthesizeExpressionForDisallowedComma); diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index 694262eeeb54a..3b9ffa2401239 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -431,6 +431,7 @@ import { TokenFlags, TransformFlags, TrueLiteral, + TryExpression, TryStatement, TupleTypeNode, Type, @@ -684,6 +685,8 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode createTemplateLiteralLikeNode, createYieldExpression, updateYieldExpression, + createTryExpression, + updateTryExpression, createSpreadElement, updateSpreadElement, createClassExpression, @@ -3629,6 +3632,22 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode : node; } + + // @api + function createTryExpression(expression: Expression): TryExpression { + const node = createBaseNode(SyntaxKind.TryExpression); + node.expression = expression && parenthesizerRules().parenthesizeExpressionForDisallowedComma(expression); + node.transformFlags |= propagateChildFlags(node.expression); + return node; + } + + // @api + function updateTryExpression(node: TryExpression, expression: Expression) { + return node.expression !== expression + ? update(createTryExpression(expression), node) + : node; + } + // @api function createSpreadElement(expression: Expression) { const node = createBaseNode(SyntaxKind.SpreadElement); diff --git a/src/compiler/factory/nodeTests.ts b/src/compiler/factory/nodeTests.ts index 8aa4bb02e83d2..4e999d6159358 100644 --- a/src/compiler/factory/nodeTests.ts +++ b/src/compiler/factory/nodeTests.ts @@ -209,6 +209,7 @@ import { ThisTypeNode, ThrowStatement, Token, + TryExpression, TryStatement, TupleTypeNode, TypeAliasDeclaration, @@ -657,6 +658,11 @@ export function isYieldExpression(node: Node): node is YieldExpression { return node.kind === SyntaxKind.YieldExpression; } +export function isTryExpression(node: Node): node is TryExpression { + return node.kind === SyntaxKind.TryExpression; +} + + export function isSpreadElement(node: Node): node is SpreadElement { return node.kind === SyntaxKind.SpreadElement; } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 59ad1f030220f..f29d4cb070247 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -63,6 +63,7 @@ import { DeleteExpression, Diagnostic, DiagnosticArguments, + DiagnosticCategory, DiagnosticMessage, Diagnostics, DiagnosticWithDetachedLocation, @@ -372,6 +373,7 @@ import { tracing, transferSourceFileChildren, TransformFlags, + TryExpression, TryStatement, TupleTypeNode, TypeAliasDeclaration, @@ -474,10 +476,10 @@ export function isFileProbablyExternalModule(sourceFile: SourceFile): Node | und function isAnExternalModuleIndicatorNode(node: Node) { return canHaveModifiers(node) && hasModifierOfKind(node, SyntaxKind.ExportKeyword) - || isImportEqualsDeclaration(node) && isExternalModuleReference(node.moduleReference) - || isImportDeclaration(node) - || isExportAssignment(node) - || isExportDeclaration(node) ? node : undefined; + || isImportEqualsDeclaration(node) && isExternalModuleReference(node.moduleReference) + || isImportDeclaration(node) + || isExportAssignment(node) + || isExportDeclaration(node) ? node : undefined; } function getImportMetaIfNecessary(sourceFile: SourceFile) { @@ -773,6 +775,9 @@ const forEachChildTable: ForEachChildTable = { return visitNode(cbNode, node.asteriskToken) || visitNode(cbNode, node.expression); }, + [SyntaxKind.TryExpression]: function forEachChildInTryExpression(node: TryExpression, cbNode: (node: Node) => T | undefined, _cbNodes?: (nodes: NodeArray) => T | undefined): T | undefined { + return visitNode(cbNode, node.expression); + }, [SyntaxKind.AwaitExpression]: function forEachChildInAwaitExpression(node: AwaitExpression, cbNode: (node: Node) => T | undefined, _cbNodes?: (nodes: NodeArray) => T | undefined): T | undefined { return visitNode(cbNode, node.expression); }, @@ -1090,13 +1095,13 @@ const forEachChildTable: ForEachChildTable = { [SyntaxKind.JSDocTypedefTag]: function forEachChildInJSDocTypedefTag(node: JSDocTypedefTag, cbNode: (node: Node) => T | undefined, cbNodes?: (nodes: NodeArray) => T | undefined): T | undefined { return visitNode(cbNode, node.tagName) || (node.typeExpression && - node.typeExpression.kind === SyntaxKind.JSDocTypeExpression + node.typeExpression.kind === SyntaxKind.JSDocTypeExpression ? visitNode(cbNode, node.typeExpression) || - visitNode(cbNode, node.fullName) || - (typeof node.comment === "string" ? undefined : visitNodes(cbNode, cbNodes, node.comment)) + visitNode(cbNode, node.fullName) || + (typeof node.comment === "string" ? undefined : visitNodes(cbNode, cbNodes, node.comment)) : visitNode(cbNode, node.fullName) || - visitNode(cbNode, node.typeExpression) || - (typeof node.comment === "string" ? undefined : visitNodes(cbNode, cbNodes, node.comment))); + visitNode(cbNode, node.typeExpression) || + (typeof node.comment === "string" ? undefined : visitNodes(cbNode, cbNodes, node.comment))); }, [SyntaxKind.JSDocCallbackTag]: function forEachChildInJSDocCallbackTag(node: JSDocCallbackTag, cbNode: (node: Node) => T | undefined, cbNodes?: (nodes: NodeArray) => T | undefined): T | undefined { return visitNode(cbNode, node.tagName) || @@ -1683,7 +1688,7 @@ namespace Parser { expression = parseLiteralNode() as StringLiteral | NumericLiteral; break; } - // falls through + // falls through default: expression = parseObjectLiteralExpression(); break; @@ -2626,10 +2631,10 @@ namespace Parser { const pos = getNodePos(); const result = kind === SyntaxKind.Identifier ? factoryCreateIdentifier("", /*originalKeywordKind*/ undefined) : isTemplateLiteralKind(kind) ? factory.createTemplateLiteralLikeNode(kind, "", "", /*templateFlags*/ undefined) : - kind === SyntaxKind.NumericLiteral ? factoryCreateNumericLiteral("", /*numericLiteralFlags*/ undefined) : - kind === SyntaxKind.StringLiteral ? factoryCreateStringLiteral("", /*isSingleQuote*/ undefined) : - kind === SyntaxKind.MissingDeclaration ? factory.createMissingDeclaration() : - factoryCreateToken(kind); + kind === SyntaxKind.NumericLiteral ? factoryCreateNumericLiteral("", /*numericLiteralFlags*/ undefined) : + kind === SyntaxKind.StringLiteral ? factoryCreateStringLiteral("", /*isSingleQuote*/ undefined) : + kind === SyntaxKind.MissingDeclaration ? factory.createMissingDeclaration() : + factoryCreateToken(kind); return finishNode(result, pos) as T; } @@ -2793,9 +2798,9 @@ namespace Parser { function canFollowExportModifier(): boolean { return token() === SyntaxKind.AtToken || token() !== SyntaxKind.AsteriskToken - && token() !== SyntaxKind.AsKeyword - && token() !== SyntaxKind.OpenBraceToken - && canFollowModifier(); + && token() !== SyntaxKind.AsKeyword + && token() !== SyntaxKind.OpenBraceToken + && canFollowModifier(); } function nextTokenCanFollowExportModifier(): boolean { @@ -2906,7 +2911,7 @@ namespace Parser { case SyntaxKind.DotToken: // Not an array literal member, but don't want to close the array (see `tests/cases/fourslash/completionsDotInArrayLiteralInObjectLiteral.ts`) return true; } - // falls through + // falls through case ParsingContext.ArgumentExpressions: return token() === SyntaxKind.DotDotDotToken || isStartOfExpression(); case ParsingContext.Parameters: @@ -3225,53 +3230,53 @@ namespace Parser { case ParsingContext.Parameters: return isReusableParameter(node); - // Any other lists we do not care about reusing nodes in. But feel free to add if - // you can do so safely. Danger areas involve nodes that may involve speculative - // parsing. If speculative parsing is involved with the node, then the range the - // parser reached while looking ahead might be in the edited range (see the example - // in canReuseVariableDeclaratorNode for a good case of this). - - // case ParsingContext.HeritageClauses: - // This would probably be safe to reuse. There is no speculative parsing with - // heritage clauses. - - // case ParsingContext.TypeParameters: - // This would probably be safe to reuse. There is no speculative parsing with - // type parameters. Note that that's because type *parameters* only occur in - // unambiguous *type* contexts. While type *arguments* occur in very ambiguous - // *expression* contexts. - - // case ParsingContext.TupleElementTypes: - // This would probably be safe to reuse. There is no speculative parsing with - // tuple types. - - // Technically, type argument list types are probably safe to reuse. While - // speculative parsing is involved with them (since type argument lists are only - // produced from speculative parsing a < as a type argument list), we only have - // the types because speculative parsing succeeded. Thus, the lookahead never - // went past the end of the list and rewound. - // case ParsingContext.TypeArguments: - - // Note: these are almost certainly not safe to ever reuse. Expressions commonly - // need a large amount of lookahead, and we should not reuse them as they may - // have actually intersected the edit. - // case ParsingContext.ArgumentExpressions: - - // This is not safe to reuse for the same reason as the 'AssignmentExpression' - // cases. i.e. a property assignment may end with an expression, and thus might - // have lookahead far beyond it's old node. - // case ParsingContext.ObjectLiteralMembers: - - // This is probably not safe to reuse. There can be speculative parsing with - // type names in a heritage clause. There can be generic names in the type - // name list, and there can be left hand side expressions (which can have type - // arguments.) - // case ParsingContext.HeritageClauseElement: - - // Perhaps safe to reuse, but it's unlikely we'd see more than a dozen attributes - // on any given element. Same for children. - // case ParsingContext.JsxAttributes: - // case ParsingContext.JsxChildren: + // Any other lists we do not care about reusing nodes in. But feel free to add if + // you can do so safely. Danger areas involve nodes that may involve speculative + // parsing. If speculative parsing is involved with the node, then the range the + // parser reached while looking ahead might be in the edited range (see the example + // in canReuseVariableDeclaratorNode for a good case of this). + + // case ParsingContext.HeritageClauses: + // This would probably be safe to reuse. There is no speculative parsing with + // heritage clauses. + + // case ParsingContext.TypeParameters: + // This would probably be safe to reuse. There is no speculative parsing with + // type parameters. Note that that's because type *parameters* only occur in + // unambiguous *type* contexts. While type *arguments* occur in very ambiguous + // *expression* contexts. + + // case ParsingContext.TupleElementTypes: + // This would probably be safe to reuse. There is no speculative parsing with + // tuple types. + + // Technically, type argument list types are probably safe to reuse. While + // speculative parsing is involved with them (since type argument lists are only + // produced from speculative parsing a < as a type argument list), we only have + // the types because speculative parsing succeeded. Thus, the lookahead never + // went past the end of the list and rewound. + // case ParsingContext.TypeArguments: + + // Note: these are almost certainly not safe to ever reuse. Expressions commonly + // need a large amount of lookahead, and we should not reuse them as they may + // have actually intersected the edit. + // case ParsingContext.ArgumentExpressions: + + // This is not safe to reuse for the same reason as the 'AssignmentExpression' + // cases. i.e. a property assignment may end with an expression, and thus might + // have lookahead far beyond it's old node. + // case ParsingContext.ObjectLiteralMembers: + + // This is probably not safe to reuse. There can be speculative parsing with + // type names in a heritage clause. There can be generic names in the type + // name list, and there can be left hand side expressions (which can have type + // arguments.) + // case ParsingContext.HeritageClauseElement: + + // Perhaps safe to reuse, but it's unlikely we'd see more than a dozen attributes + // on any given element. Same for children. + // case ParsingContext.JsxAttributes: + // case ParsingContext.JsxChildren: } return false; @@ -3765,9 +3770,9 @@ namespace Parser { // We also do not need to check for negatives because any prefix operator would be part of a // parent unary expression. kind === SyntaxKind.NumericLiteral ? factoryCreateNumericLiteral(scanner.getTokenValue(), scanner.getNumericLiteralFlags()) : - kind === SyntaxKind.StringLiteral ? factoryCreateStringLiteral(scanner.getTokenValue(), /*isSingleQuote*/ undefined, scanner.hasExtendedUnicodeEscape()) : - isLiteralKind(kind) ? factoryCreateLiteralLikeNode(kind, scanner.getTokenValue()) : - Debug.fail(); + kind === SyntaxKind.StringLiteral ? factoryCreateStringLiteral(scanner.getTokenValue(), /*isSingleQuote*/ undefined, scanner.hasExtendedUnicodeEscape()) : + isLiteralKind(kind) ? factoryCreateLiteralLikeNode(kind, scanner.getTokenValue()) : + Debug.fail(); if (scanner.hasExtendedUnicodeEscape()) { node.hasExtendedUnicodeEscape = true; @@ -4601,13 +4606,13 @@ namespace Parser { case SyntaxKind.AsteriskEqualsToken: // If there is '*=', treat it as * followed by postfix = scanner.reScanAsteriskEqualsToken(); - // falls through + // falls through case SyntaxKind.AsteriskToken: return parseJSDocAllType(); case SyntaxKind.QuestionQuestionToken: // If there is '??', treat it as prefix-'?' in JSDoc type. scanner.reScanQuestionToken(); - // falls through + // falls through case SyntaxKind.QuestionToken: return parseJSDocUnknownOrNullableType(); case SyntaxKind.FunctionKeyword: @@ -5014,6 +5019,16 @@ namespace Parser { // it is definitely an expression). Or it's a keyword (either because we're in // a generator or async function, or in strict mode (or both)) and it started a yield or await expression. return true; + case SyntaxKind.TryKeyword: + // if we encounter the try keyword and the next token is not an open brace, + // this is probably the try expression, but since the try statement takes priority + // wherever it is valid, there is no point in a lookahead here. + // however, this does not allow the try expression to be used as a statement. + // i.e. it must be encountered somewhere where an expression is already expected. + // but this makes sense because we don't want to allow the try to just get rid of + // errors without explicitly dealing with them somehow. In a context where an expression + // is already expected it would then have to be handled explicitly somehow. + return true; default: // Error tolerance. If we see the start of some binary operator, we consider // that the start of an expression. That way we'll parse out a missing identifier, @@ -5080,6 +5095,9 @@ namespace Parser { if (isYieldExpression()) { return parseYieldExpression(); } + if (isTryExpression()) { + return parseTryExpression(); + } // Then, check if we have an arrow function (production '4' and '5') that starts with a parenthesized // parameter list or is an async arrow function. @@ -5131,6 +5149,31 @@ namespace Parser { return parseConditionalExpressionRest(expr, pos, allowReturnTypeInArrowFunction); } + function isTryExpression() { + return token() === SyntaxKind.TryKeyword; + } + function parseTryExpression(): TryExpression { + const pos = getNodePos(); + + nextToken(); + + if(token() === SyntaxKind.OpenBraceToken) { + parseErrorAtCurrentToken({ + message: "Object literal following the try keyword must be wrapped in parentheses.", + category: DiagnosticCategory.Error, + code: 99999, + key: "Object_literal_following_the_try_keyword_must_be_wrapped_in_parentheses", + }); + } + + return finishNode( + factory.createTryExpression( + parseAssignmentExpressionOrHigher(/*allowReturnTypeInArrowFunction*/ true), + ), + pos, + ); + } + function isYieldExpression(): boolean { if (token() === SyntaxKind.YieldKeyword) { // If we have a 'yield' keyword, and this is a context where yield expressions are @@ -5164,6 +5207,7 @@ namespace Parser { return !scanner.hasPrecedingLineBreak() && isIdentifier(); } + function parseYieldExpression(): YieldExpression { const pos = getNodePos(); @@ -5815,7 +5859,7 @@ namespace Parser { if (isAwaitExpression()) { return parseAwaitExpression(); } - // falls through + // falls through default: return parseUpdateExpression(); } @@ -5849,8 +5893,8 @@ namespace Parser { if (languageVariant !== LanguageVariant.JSX) { return false; } - // We are in JSX context and the token is part of JSXElement. - // falls through + // We are in JSX context and the token is part of JSXElement. + // falls through default: return true; } @@ -6666,7 +6710,7 @@ namespace Parser { function parseArgumentOrArrayLiteralElement(): Expression { return token() === SyntaxKind.DotDotDotToken ? parseSpreadElement() : token() === SyntaxKind.CommaToken ? finishNode(factory.createOmittedExpression(), getNodePos()) : - parseAssignmentExpressionOrHigher(/*allowReturnTypeInArrowFunction*/ true); + parseAssignmentExpressionOrHigher(/*allowReturnTypeInArrowFunction*/ true); } function parseArgumentExpression(): Expression { @@ -6767,8 +6811,8 @@ namespace Parser { const isAsync = some(modifiers, isAsyncModifier) ? SignatureFlags.Await : SignatureFlags.None; const name = isGenerator && isAsync ? doInYieldAndAwaitContext(parseOptionalBindingIdentifier) : isGenerator ? doInYieldContext(parseOptionalBindingIdentifier) : - isAsync ? doInAwaitContext(parseOptionalBindingIdentifier) : - parseOptionalBindingIdentifier(); + isAsync ? doInAwaitContext(parseOptionalBindingIdentifier) : + parseOptionalBindingIdentifier(); const typeParameters = parseTypeParameters(); const parameters = parseParameters(isGenerator | isAsync); @@ -7272,8 +7316,10 @@ namespace Parser { case SyntaxKind.WithKeyword: case SyntaxKind.SwitchKeyword: case SyntaxKind.ThrowKeyword: - case SyntaxKind.TryKeyword: case SyntaxKind.DebuggerKeyword: + // 'try' applies to both try statement and try expression statement + // falls through + case SyntaxKind.TryKeyword: // 'catch' and 'finally' do not actually indicate that the code is part of a statement, // however, we say they are here so that we may gracefully parse them and error later. // falls through @@ -8957,7 +9003,7 @@ namespace Parser { linkEnd = scanner.getTokenEnd(); break; } - // fallthrough if it's not a {@link sequence + // fallthrough if it's not a {@link sequence default: // Anything else is doc comment text. We just save it. Because it // wasn't a tag, we can no longer parse a tag on this line until we hit the next @@ -9237,8 +9283,8 @@ namespace Parser { indent += 1; break; } - // record the * as a comment - // falls through + // record the * as a comment + // falls through default: if (state !== JSDocState.SavingBackticks) { state = JSDocState.SavingComments; // leading identifiers start recording as well @@ -9282,7 +9328,7 @@ namespace Parser { } const create = linkType === "link" ? factory.createJSDocLink : linkType === "linkcode" ? factory.createJSDocLinkCode - : factory.createJSDocLinkPlain; + : factory.createJSDocLinkPlain; return finishNode(create(name, text.join("")), start, scanner.getTokenEnd()); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 9b4915f1cc293..5a4ca7033a3f6 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -295,6 +295,7 @@ export const enum SyntaxKind { ConditionalExpression, TemplateExpression, YieldExpression, + TryExpression, SpreadElement, ClassExpression, OmittedExpression, @@ -1119,6 +1120,7 @@ export type HasChildren = | ConditionalExpression | TemplateExpression | YieldExpression + | TryExpression | SpreadElement | ClassExpression | ExpressionWithTypeArguments @@ -2505,6 +2507,11 @@ export interface YieldExpression extends Expression { readonly expression?: Expression; } +export interface TryExpression extends Expression { + readonly kind: SyntaxKind.TryExpression; + readonly expression: Expression; +} + export interface SyntheticExpression extends Expression { readonly kind: SyntaxKind.SyntheticExpression; readonly isSpread: boolean; @@ -5072,6 +5079,7 @@ export interface TypeChecker { getWidenedLiteralType(type: Type): Type; /** @internal */ getPromisedTypeOfPromise(promise: Type, errorNode?: Node): Type | undefined; + getTryResultType(type: Type): Type | undefined; /** * Gets the "awaited type" of a type. * @@ -8930,6 +8938,8 @@ export interface NodeFactory { createYieldExpression(asteriskToken: undefined, expression: Expression | undefined): YieldExpression; /** @internal */ createYieldExpression(asteriskToken: AsteriskToken | undefined, expression: Expression | undefined): YieldExpression; // eslint-disable-line @typescript-eslint/unified-signatures updateYieldExpression(node: YieldExpression, asteriskToken: AsteriskToken | undefined, expression: Expression | undefined): YieldExpression; + createTryExpression(expression: Expression): TryExpression; + updateTryExpression(node: TryExpression, expression: Expression): TryExpression; createSpreadElement(expression: Expression): SpreadElement; updateSpreadElement(node: SpreadElement, expression: Expression): SpreadElement; createClassExpression(modifiers: readonly ModifierLike[] | undefined, name: string | Identifier | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, heritageClauses: readonly HeritageClause[] | undefined, members: readonly ClassElement[]): ClassExpression; diff --git a/src/compiler/visitorPublic.ts b/src/compiler/visitorPublic.ts index dbd49379e5750..ac196e6ed56df 100644 --- a/src/compiler/visitorPublic.ts +++ b/src/compiler/visitorPublic.ts @@ -1201,6 +1201,13 @@ const visitEachChildTable: VisitEachChildTable = { ); }, + [SyntaxKind.TryExpression]: function visitEachChildOfTryExpression(node, visitor, context, _nodesVisitor, nodeVisitor, _tokenVisitor) { + return context.factory.updateTryExpression( + node, + Debug.checkDefined(nodeVisitor(node.expression, visitor, isExpression)), + ); + }, + [SyntaxKind.SpreadElement]: function visitEachChildOfSpreadElement(node, visitor, context, _nodesVisitor, nodeVisitor, _tokenVisitor) { return context.factory.updateSpreadElement( node, diff --git a/src/lib/es5.d.ts b/src/lib/es5.d.ts index ab484e1812e7b..6919c8aecb309 100644 --- a/src/lib/es5.d.ts +++ b/src/lib/es5.d.ts @@ -1,6 +1,19 @@ /// /// +///////////////////////////// +/// Arlen22 Fork Additions +///////////////////////////// + + +type TryResult = [undefined, T] | [TryError, undefined] +declare class TryError extends Error { + inner: T; + constructor(inner: T); +} + + + ///////////////////////////// /// ECMAScript APIs ///////////////////////////// diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index 45d80841d1894..d36413e973f06 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -164,6 +164,7 @@ export function getAllRules(): RuleSpec[] { rule("NoSpaceBetweenYieldKeywordAndStar", SyntaxKind.YieldKeyword, SyntaxKind.AsteriskToken, [isNonJsxSameLineTokenContext, isYieldOrYieldStarWithOperand], RuleAction.DeleteSpace), rule("SpaceBetweenYieldOrYieldStarAndOperand", [SyntaxKind.YieldKeyword, SyntaxKind.AsteriskToken], anyToken, [isNonJsxSameLineTokenContext, isYieldOrYieldStarWithOperand], RuleAction.InsertSpace), + rule("SpaceBetweenTryAndOperand", [SyntaxKind.TryKeyword], anyToken, [isNonJsxSameLineTokenContext, isTryExpression], RuleAction.InsertSpace), rule("NoSpaceBetweenReturnAndSemicolon", SyntaxKind.ReturnKeyword, SyntaxKind.SemicolonToken, [isNonJsxSameLineTokenContext], RuleAction.DeleteSpace), rule("SpaceAfterCertainKeywords", [SyntaxKind.VarKeyword, SyntaxKind.ThrowKeyword, SyntaxKind.NewKeyword, SyntaxKind.DeleteKeyword, SyntaxKind.ReturnKeyword, SyntaxKind.TypeOfKeyword, SyntaxKind.AwaitKeyword], anyToken, [isNonJsxSameLineTokenContext], RuleAction.InsertSpace), @@ -885,6 +886,10 @@ function isYieldOrYieldStarWithOperand(context: FormattingContext): boolean { return context.contextNode.kind === SyntaxKind.YieldExpression && (context.contextNode as YieldExpression).expression !== undefined; } +function isTryExpression(context: FormattingContext): boolean { + return context.contextNode.kind === SyntaxKind.TryExpression; +} + function isNonNullAssertionContext(context: FormattingContext): boolean { return context.contextNode.kind === SyntaxKind.NonNullExpression; } diff --git a/src/services/utilities.ts b/src/services/utilities.ts index fbbcb67a106b9..36bbcb9d7d7a3 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -253,6 +253,7 @@ import { isTemplateLiteralKind, isToken, isTransientSymbol, + isTryExpression, isTypeAliasDeclaration, isTypeElement, isTypeNode, @@ -1500,7 +1501,8 @@ function getAdjustedLocation(node: Node, forRename: boolean): Node { node.kind === SyntaxKind.TypeOfKeyword && isTypeOfExpression(parent) || node.kind === SyntaxKind.AwaitKeyword && isAwaitExpression(parent) || node.kind === SyntaxKind.YieldKeyword && isYieldExpression(parent) || - node.kind === SyntaxKind.DeleteKeyword && isDeleteExpression(parent) + node.kind === SyntaxKind.DeleteKeyword && isDeleteExpression(parent) || + node.kind === SyntaxKind.TryKeyword && isTryExpression(parent) ) { if (parent.expression) { return skipOuterExpressions(parent.expression); From ca793d3ee353450df099111f48b17d23da8ff0c6 Mon Sep 17 00:00:00 2001 From: Arlen22 Date: Wed, 13 Nov 2024 16:19:09 -0500 Subject: [PATCH 02/12] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5b8321b645026..3beeb20d3ee12 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ This fork contains a few modifications to Typescript to preview the try-expressi - Create a new npm project folder using `npm init`. - `npm install` the built tgz file from the releases page of this repository. + - https://github.com/Arlen22/TypeScript/releases/download/arlen22-dev-0.2/typescript-5.8.0-arlen22-0.2.tgz - In VSCode, create a new typescript file and open it to enable the Typescript engine, then run the command "Typescript: Select Typescript Version" and select the version from your node_modules folder. - Create a tsconfig.json file with `strictNullChecks` enabled. - If you run `tsc` it will just pass the syntax through to the Javascript file. From 692db3ad1dbdcea7c4662c4e9b287a8d8b392371 Mon Sep 17 00:00:00 2001 From: Arlen22 Date: Thu, 23 Jan 2025 23:15:56 +0000 Subject: [PATCH 03/12] add transformer (I think it works) --- src/compiler/_namespaces/ts.ts | 2 +- src/compiler/transformer.ts | 5 + src/compiler/transformers/arlen22.ts | 469 +++++++++++++++++++++++++++ src/lib/arlen22.d.ts | 62 ++++ src/lib/es5.d.ts | 8 +- 5 files changed, 538 insertions(+), 8 deletions(-) create mode 100644 src/compiler/transformers/arlen22.ts create mode 100644 src/lib/arlen22.d.ts diff --git a/src/compiler/_namespaces/ts.ts b/src/compiler/_namespaces/ts.ts index 94fb16857d38e..ba365a6498668 100644 --- a/src/compiler/_namespaces/ts.ts +++ b/src/compiler/_namespaces/ts.ts @@ -1,5 +1,5 @@ /* Generated file to emulate the ts namespace. */ - +export * from "../transformers/arlen22.js"; export * from "../corePublic.js"; export * from "../core.js"; export * from "../debug.js"; diff --git a/src/compiler/transformer.ts b/src/compiler/transformer.ts index 0d499f3a08298..302e93909ee1d 100644 --- a/src/compiler/transformer.ts +++ b/src/compiler/transformer.ts @@ -72,6 +72,7 @@ import { transformSystemModule, transformTypeScript, VariableDeclaration, + transformTryExpression, } from "./_namespaces/ts.js"; import * as performance from "./_namespaces/ts.performance.js"; @@ -142,6 +143,8 @@ function getScriptTransformers(compilerOptions: CompilerOptions, customTransform transformers.push(transformJsx); } + transformers.push(transformTryExpression); + if (languageVersion < ScriptTarget.ESNext) { transformers.push(transformESNext); } @@ -181,6 +184,8 @@ function getScriptTransformers(compilerOptions: CompilerOptions, customTransform transformers.push(transformGenerators); } + + transformers.push(getModuleTransformer(moduleKind)); addRange(transformers, customTransformers && map(customTransformers.after, wrapScriptTransformerFactory)); diff --git a/src/compiler/transformers/arlen22.ts b/src/compiler/transformers/arlen22.ts new file mode 100644 index 0000000000000..d73b44d77facf --- /dev/null +++ b/src/compiler/transformers/arlen22.ts @@ -0,0 +1,469 @@ +import { + BinaryExpression, + Bundle, + chainBundle, + Debug, + EmitFlags, + Expression, + FunctionExpression, + isAsyncFunction, + isElementAccessExpression, + isExpression, + isFunctionDeclaration, + isFunctionExpression, + isFunctionLike, + isFunctionLikeDeclaration, + isFunctionLikeKind, + isPropertyAccessExpression, + ModifierFlags, + modifierToFlag, + Node, + setEmitFlags, + setTextRange, + SourceFile, + SyntaxKind, + TransformationContext, + TransformFlags, + TryExpression, + UnscopedEmitHelper, + visitEachChild, + visitNode, + VisitResult, +} from "../_namespaces/ts.js"; +const TryResultConstructor: UnscopedEmitHelper = { + name: "typescript:tryresult", + importName: "__TryResultConstructor", + scoped: false, + priority: 3, + text: ` +var TryResult = /** @class */ (function () { + function TryResult(ok, error, value) { + this.ok = ok; + this.error = error; + this.value = value; + } + TryResult.prototype[Symbol.iterator] = function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.ok]; + case 1: + _a.sent(); + return [4 /*yield*/, this.error]; + case 2: + _a.sent(); + return [4 /*yield*/, this.value]; + case 3: + _a.sent(); + return [2 /*return*/]; + } + }); + }; + TryResult.ok = function (value) { + return new TryResult(true, undefined, value); + }; + TryResult.error = function (error) { + return new TryResult(false, error, undefined); + }; + return TryResult; +}()); +`, +}; +/** @internal */ +export function transformTryExpression(context: TransformationContext): (x: SourceFile | Bundle) => SourceFile | Bundle { + const { + factory, + hoistVariableDeclaration, + requestEmitHelper, + + } = context; + + return chainBundle(context, transformSourceFile); + + function transformSourceFile(node: SourceFile) { + if (node.isDeclarationFile) { + return node; + } + + return visitEachChild(node, visitor, context); + } + + function visitor(node: Node): VisitResult { + switch (node.kind) { + case SyntaxKind.TryExpression: + return visitTryExpression(node as TryExpression); + default: + return visitEachChild(node, visitor, context); + } + } + + function visitTryExpression(node: TryExpression): VisitResult { + let hasAwait = false, hasYield = false;; + function checkForAwait(node: Node) { + if (node.kind === SyntaxKind.AwaitExpression) hasAwait = true; + if (node.kind === SyntaxKind.YieldExpression) hasYield = true; + if (isFunctionLikeDeclaration(node)) return node; + return visitEachChild(node, checkForAwait, context); + } + checkForAwait(node); + const expression = visitEachChild(node.expression, visitor, context); + const catchVar = factory.createIdentifier("e"); + requestEmitHelper(TryResultConstructor); + + const callExpression = factory.createCallExpression( + factory.createFunctionBindCall( + factory.createFunctionExpression( + factory.createModifiersFromModifierFlags(+hasAwait && ModifierFlags.Async), + hasYield ? factory.createToken(SyntaxKind.AsteriskToken) : undefined, + undefined, + undefined, + [], + undefined, + factory.createBlock([ + setEmitFlags(factory.createTryStatement( + setEmitFlags( + factory.createBlock([ + factory.createReturnStatement( + factory.createGlobalMethodCall("TryResult", "ok", [ + expression + ]) + ) + ]), + EmitFlags.SingleLine + ), + factory.createCatchClause( + catchVar, + setEmitFlags( + factory.createBlock([ + factory.createReturnStatement( + factory.createGlobalMethodCall("TryResult", "error", [ + catchVar + ]) + ) + ]), + EmitFlags.SingleLine + ), + ), + undefined + ), EmitFlags.SingleLine) + ]) + ), + factory.createThis(), + [] + ), + undefined, + [] + ); + // if(hasYield){ + // const _1 = factory.createPropertyAccessExpression(callExpression, "next"); + // const _2 = factory.createCallExpression(_1, undefined, undefined); + // const _3 = factory.createPropertyAccessExpression(_2, "value"); + // return _3; + // } + // yield handles both sync and async + if (hasYield) { + return factory.createYieldExpression( + factory.createToken(SyntaxKind.AsteriskToken), + callExpression + ) + } else if (hasAwait) { + return factory.createAwaitExpression(callExpression); + } else { + return callExpression; + } + + } + function create_try_await_helper(node: TryExpression, hasAwait: boolean) { + + + } + + /** + * Gets an identifier for the name of an *unscoped* emit helper. + */ + function getUnscopedHelperName(name: string) { + return setEmitFlags(factory.createIdentifier(name), EmitFlags.HelperName | EmitFlags.AdviseOnEmitNode); + } + // function createTryStatement(){ + // return factory.createTryStatement( + // factory.createBlock([ + // factory.restoreEnclosingLabel( + // forStatement, + // outermostLabeledStatement, + // ), + // ]), + // factory.createCatchClause( + // factory.createVariableDeclaration(catchVariable), + // setEmitFlags( + // factory.createBlock([ + // factory.createExpressionStatement( + // factory.createAssignment( + // errorRecord, + // factory.createObjectLiteralExpression([ + // factory.createPropertyAssignment("error", catchVariable), + // ]), + // ), + // ), + // ]), + // EmitFlags.SingleLine, + // ), + // ), + // factory.createBlock([ + // factory.createTryStatement( + // /*tryBlock*/ factory.createBlock([ + // setEmitFlags( + // factory.createIfStatement( + // factory.createLogicalAnd( + // factory.createLogicalAnd( + // factory.createLogicalNot(nonUserCode), + // factory.createLogicalNot(done), + // ), + // factory.createAssignment( + // returnMethod, + // factory.createPropertyAccessExpression(iterator, "return"), + // ), + // ), + // factory.createExpressionStatement(createDownlevelAwait(callReturn)), + // ), + // EmitFlags.SingleLine, + // ), + // ]), + // /*catchClause*/ undefined, + // /*finallyBlock*/ setEmitFlags( + // factory.createBlock([ + // setEmitFlags( + // factory.createIfStatement( + // errorRecord, + // factory.createThrowStatement( + // factory.createPropertyAccessExpression(errorRecord, "error"), + // ), + // ), + // EmitFlags.SingleLine, + // ), + // ]), + // EmitFlags.SingleLine, + // ), + // ), + // ]), + // ); + // } + // function visitExponentiationAssignmentExpression(node: BinaryExpression) { + // let target: Expression; + // let value: Expression; + // const left = visitNode(node.left, visitor, isExpression); + // const right = visitNode(node.right, visitor, isExpression); + // if (isElementAccessExpression(left)) { + // // Transforms `a[x] **= b` into `(_a = a)[_x = x] = Math.pow(_a[_x], b)` + // const expressionTemp = factory.createTempVariable(hoistVariableDeclaration); + // const argumentExpressionTemp = factory.createTempVariable(hoistVariableDeclaration); + // target = setTextRange( + // factory.createElementAccessExpression( + // setTextRange(factory.createAssignment(expressionTemp, left.expression), left.expression), + // setTextRange(factory.createAssignment(argumentExpressionTemp, left.argumentExpression), left.argumentExpression), + // ), + // left, + // ); + // value = setTextRange( + // factory.createElementAccessExpression( + // expressionTemp, + // argumentExpressionTemp, + // ), + // left, + // ); + // } + // else if (isPropertyAccessExpression(left)) { + // // Transforms `a.x **= b` into `(_a = a).x = Math.pow(_a.x, b)` + // const expressionTemp = factory.createTempVariable(hoistVariableDeclaration); + // target = setTextRange( + // factory.createPropertyAccessExpression( + // setTextRange(factory.createAssignment(expressionTemp, left.expression), left.expression), + // left.name, + // ), + // left, + // ); + // value = setTextRange( + // factory.createPropertyAccessExpression( + // expressionTemp, + // left.name, + // ), + // left, + // ); + // } + // else { + // // Transforms `a **= b` into `a = Math.pow(a, b)` + // target = left; + // value = left; + // } + // return setTextRange( + // factory.createAssignment( + // target, + // setTextRange(factory.createGlobalMethodCall("Math", "pow", [value, right]), node), + // ), + // node, + // ); + // } + + // function visitExponentiationExpression(node: BinaryExpression) { + // // Transforms `a ** b` into `Math.pow(a, b)` + // const left = visitNode(node.left, visitor, isExpression); + // const right = visitNode(node.right, visitor, isExpression); + // return setTextRange(factory.createGlobalMethodCall("Math", "pow", [left, right]), node); + // } +} + + + + +// import { +// NodeFactory, +// TransformationContext, +// Node, +// VisitResult, +// Identifier, +// Expression, +// SyntaxKind, +// VariableDeclaration, +// EmitFlags, +// factory as tsFactory, +// chainBundle, +// } from "../_namespaces/ts.js"; + +// /** +// * Transforms the following syntax: +// * ```js +// * const result = try expr1; +// * ``` +// * Into: +// * ```js +// * let _result; +// * try { +// * _result = TryResult.ok(expr1); +// * } catch (error) { +// * _result = TryResult.error(error); +// * } +// * const result = _result; +// * ``` +// */ +// export function transformTryExpression(context: TransformationContext): (node: Node) => VisitResult { +// const { factory } = context; + + +// return chainBundle(context, transformSourceFile); +// } + +// function transformSourceFile(node: SourceFile) { +// if (node.isDeclarationFile) { +// return node; +// } + +// currentSourceFile = node; +// const visited = visitSourceFile(node); +// addEmitHelpers(visited, context.readEmitHelpers()); + +// currentSourceFile = undefined!; +// taggedTemplateStringDeclarations = undefined!; +// return visited; +// return (node: Node): VisitResult => { +// if ( +// node.kind === SyntaxKind.VariableDeclaration && +// isTryExpressionDeclaration(node as VariableDeclaration) +// ) { +// return transformTryExpressionDeclaration(node as VariableDeclaration, factory); +// } +// return node; +// }; +// } + +// function isTryExpressionDeclaration(node: VariableDeclaration): boolean { +// if ( +// node.initializer && +// node.initializer.kind === SyntaxKind.TryExpression && +// node.name.kind === SyntaxKind.Identifier +// ) { +// return true; +// } +// return false; +// } + +// function transformTryExpressionDeclaration( +// node: VariableDeclaration, +// factory: NodeFactory +// ): Node { +// const resultVarName = factory.createUniqueName("_result"); +// const resultConstName = node.name as Identifier; +// const expr = node.initializer as Expression; + +// // Create `let _result;` +// const tempResultDecl = factory.createVariableStatement( +// /*modifiers*/ undefined, +// factory.createVariableDeclarationList([ +// factory.createVariableDeclaration( +// resultVarName, +// /*exclamationToken*/ undefined, +// /*type*/ undefined, +// /*initializer*/ undefined +// ), +// ]) +// ); + +// // Create `try { _result = TryResult.ok(expr); }` +// const tryBlock = factory.createBlock([ +// factory.createExpressionStatement( +// factory.createBinaryExpression( +// resultVarName, +// SyntaxKind.EqualsToken, +// factory.createCallExpression( +// factory.createPropertyAccessExpression( +// factory.createIdentifier("TryResult"), +// "ok" +// ), +// /*typeArguments*/ undefined, +// [expr] +// ) +// ) +// ), +// ], +// /*multiLine*/ true); + +// // Create `catch (error) { _result = TryResult.error(error); }` +// const catchBlock = factory.createCatchClause( +// "error", +// factory.createBlock([ +// factory.createExpressionStatement( +// factory.createBinaryExpression( +// resultVarName, +// SyntaxKind.EqualsToken, +// factory.createCallExpression( +// factory.createPropertyAccessExpression( +// factory.createIdentifier("TryResult"), +// "error" +// ), +// /*typeArguments*/ undefined, +// [factory.createIdentifier("error")] +// ) +// ) +// ) +// ], +// /*multiLine*/ false), +// ); + +// const tryStatement = factory.createTryStatement( +// tryBlock, +// catchBlock, +// /*finallyBlock*/ undefined +// ); + +// // Create `const result = _result;` +// const resultConstDecl = factory.createVariableStatement( +// /*modifiers*/ undefined, +// factory.createVariableDeclarationList([ +// factory.createVariableDeclaration( +// resultConstName, +// /*exclamationToken*/ undefined, +// /*type*/ undefined, +// resultVarName +// ), +// ]) +// ); + +// return [tempResultDecl, tryStatement, resultConstDecl]; +// } diff --git a/src/lib/arlen22.d.ts b/src/lib/arlen22.d.ts new file mode 100644 index 0000000000000..8b5cb869cdac6 --- /dev/null +++ b/src/lib/arlen22.d.ts @@ -0,0 +1,62 @@ + + +/** + * Error result type expressed as object + */ +type TryResultErrorObject = { ok: false; error: unknown; value: undefined } + +/** + * Error result type expressed as tuple. + * + * - `error` type depends on `useUnknownInCatchVariables` tsconfig option + */ +type TryResultErrorTuple = [ok: false, error: unknown, value: undefined] + +/** + * An error result is a object that can be either destructured {@link TryResultErrorObject} or accessed by index {@link TryResultErrorTuple} + */ +type TryResultError = TryResultErrorObject & TryResultErrorTuple + +/** + * Value result type expressed as object + */ +type TryResultValueObject = { ok: true; error: undefined; value: V } + +/** + * Value result type expressed as tuple + */ +type TryResultValueTuple = [ok: true, error: undefined, value: V] + +/** + * A value result is a object that can be either destructured {@link TryResultValueObject} or accessed by index {@link TryResultValueTuple} + */ +type TryResultValue = TryResultValueObject & TryResultValueTuple + +/** + * A result is a object that can represent the result of either a failed or successful operation. + */ +type TryResult = TryResultError | TryResultValue + +interface ResultConstructor { + /** + * Creates a result from a tuple + * + * @example + * + * new Result(true, undefined, 42) + * new Result(false, new Error('Something went wrong')) + */ + new (...args: TryResultValueTuple | TryResultErrorTuple): TryResult + + /** + * Creates a result for a successful operation + */ + ok(value: V): TryResult + + /** + * Creates a result for a failed operation + */ + error(error: unknown): TryResult +} + +declare const TryResult: ResultConstructor diff --git a/src/lib/es5.d.ts b/src/lib/es5.d.ts index 6919c8aecb309..628bf6ebded50 100644 --- a/src/lib/es5.d.ts +++ b/src/lib/es5.d.ts @@ -1,18 +1,12 @@ /// /// +/// ///////////////////////////// /// Arlen22 Fork Additions ///////////////////////////// -type TryResult = [undefined, T] | [TryError, undefined] -declare class TryError extends Error { - inner: T; - constructor(inner: T); -} - - ///////////////////////////// /// ECMAScript APIs From 88e5f39a0890286385ddc0757176885be30fa541 Mon Sep 17 00:00:00 2001 From: Arlen22 Date: Thu, 23 Jan 2025 23:41:05 +0000 Subject: [PATCH 04/12] example files --- example/package.json | 12 +++++++ example/test.js | 84 ++++++++++++++++++++++++++++++++++++++++++++ example/test.ts | 57 ++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+) create mode 100644 example/package.json create mode 100644 example/test.js create mode 100644 example/test.ts diff --git a/example/package.json b/example/package.json new file mode 100644 index 0000000000000..1ae3db1565d1d --- /dev/null +++ b/example/package.json @@ -0,0 +1,12 @@ +{ + "name": "example", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/example/test.js b/example/test.js new file mode 100644 index 0000000000000..cf70421cc5fa8 --- /dev/null +++ b/example/test.js @@ -0,0 +1,84 @@ +"use strict"; +var TryResult = /** @class */ (function () { + function TryResult(ok, error, value) { + this.ok = ok; + this.error = error; + this.value = value; + } + TryResult.prototype[Symbol.iterator] = function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.ok]; + case 1: + _a.sent(); + return [4 /*yield*/, this.error]; + case 2: + _a.sent(); + return [4 /*yield*/, this.value]; + case 3: + _a.sent(); + return [2 /*return*/]; + } + }); + }; + TryResult.ok = function (value) { + return new TryResult(true, undefined, value); + }; + TryResult.error = function (error) { + return new TryResult(false, error, undefined); + }; + return TryResult; +}()); +Object.defineProperty(exports, "__esModule", { value: true }); +function* test() { + try { + const test2 = yield (yield fetch("url")).json(); + console.log("success"); + } + catch (e) { + console.log("failed"); + } + console.log("continue"); +} +async function tester(arg) { + try { + // const test2 = try await (await fetch("")).json(); + // const test3 = try JSON.parse(); + // const test2 = await (async () => { try { return await (await fetch("")).json(); } catch (e) { return e; } })(); + // const test3 = (() => { try { return JSON.parse(); } catch (e) { return e; } })(); + console.log("it worked"); + } + catch (e) { + console.log("it didn't"); + } + console.log("keep going"); +} +// async function examples() { +// array.map((fn) => try fn()).filter((result) => result.ok); +// let result; +// result = try expr1; // literally any expression +// result = try data?.someProperty.anotherFunction?.(await someData()).andAnotherOne() +// result = try await fetch("https://api.example.com/data", { headers: {} }) +// result = try expression1 || expression2; // the try covers both, same as an arrow function body +// result = try check ? expression1 : expression2; // try covers the entire expression +// result = try expression1 ?? expresssion2; // again covers the entire expression +// result = try (expression1, expression2); // covers all, returning the last one +// result = try ({ "my": await fetch() }); // convers everything inside the object +// result = try [await (await fetch()).json(), expression1, anythingelse]; // covers everything inside the array +// result = try this.test = this.test2 = await fetch(); // covers everything +// } +async function* examples2() { + // function test2() { console.log(arguments) } + const expression = "hello"; + console.log(await async function () { try { return TryResult.ok(await expression); } catch (e) { return TryResult.error(e); } }.bind(this)()); + console.log(yield* function* () { try { return TryResult.ok(yield expression); } catch (e) { return TryResult.error(e); } }.bind(this)()); + console.log(yield* async function* () { try { return TryResult.ok(yield await expression); } catch (e) { return TryResult.error(e); } }.bind(this)()); + console.log(yield function () { try { return TryResult.ok(expression); } catch (e) { return TryResult.error(e); } }.bind(this)()); + console.log(yield await async function () { try { return TryResult.ok(await expression); } catch (e) { return TryResult.error(e); } }.bind(this)()); + console.log(yield yield* async function* () { try { return TryResult.ok(yield await expression); } catch (e) { return TryResult.error(e); } }.bind(this)()); + await Promise.resolve("hello"); +} +(async () => { + for await (const t of examples2()) + console.log(t); +})(); diff --git a/example/test.ts b/example/test.ts new file mode 100644 index 0000000000000..f61fc2fe3c8cd --- /dev/null +++ b/example/test.ts @@ -0,0 +1,57 @@ +export { }; + +function* test() { + try { + const test2 = yield (yield fetch("url")).json() + console.log("success") + } catch (e) { + console.log("failed"); + } + console.log("continue"); +} + +async function tester(arg: any) { + try { + // const test2 = try await (await fetch("")).json(); + // const test3 = try JSON.parse(); + // const test2 = await (async () => { try { return await (await fetch("")).json(); } catch (e) { return e; } })(); + // const test3 = (() => { try { return JSON.parse(); } catch (e) { return e; } })(); + console.log("it worked"); + } catch (e) { + console.log("it didn't") + } + console.log("keep going"); +} + + + +// async function examples() { +// array.map((fn) => try fn()).filter((result) => result.ok); +// let result; +// result = try expr1; // literally any expression +// result = try data?.someProperty.anotherFunction?.(await someData()).andAnotherOne() +// result = try await fetch("https://api.example.com/data", { headers: {} }) +// result = try expression1 || expression2; // the try covers both, same as an arrow function body +// result = try check ? expression1 : expression2; // try covers the entire expression +// result = try expression1 ?? expresssion2; // again covers the entire expression +// result = try (expression1, expression2); // covers all, returning the last one +// result = try ({ "my": await fetch() }); // convers everything inside the object +// result = try [await (await fetch()).json(), expression1, anythingelse]; // covers everything inside the array +// result = try this.test = this.test2 = await fetch(); // covers everything +// } + +async function* examples2() { + // function test2() { console.log(arguments) } + const expression = "hello"; + console.log(try await expression); + console.log(try yield expression); + console.log(try yield await expression); + console.log(yield try expression); + console.log(yield try await expression); + console.log(yield try yield await expression); + await Promise.resolve("hello"); +} + +(async () => { + for await (const t of examples2()) console.log(t); +})(); From 3f4df5a8a53ded0cfe120f24c71eecff2d59c4e5 Mon Sep 17 00:00:00 2001 From: Arlen22 Date: Thu, 23 Jan 2025 23:54:37 +0000 Subject: [PATCH 05/12] a few fixes --- src/compiler/transformers/arlen22.ts | 340 +-------------------------- 1 file changed, 6 insertions(+), 334 deletions(-) diff --git a/src/compiler/transformers/arlen22.ts b/src/compiler/transformers/arlen22.ts index d73b44d77facf..ecb5d6deb430f 100644 --- a/src/compiler/transformers/arlen22.ts +++ b/src/compiler/transformers/arlen22.ts @@ -1,33 +1,17 @@ import { - BinaryExpression, Bundle, chainBundle, - Debug, EmitFlags, - Expression, - FunctionExpression, - isAsyncFunction, - isElementAccessExpression, - isExpression, - isFunctionDeclaration, - isFunctionExpression, - isFunctionLike, isFunctionLikeDeclaration, - isFunctionLikeKind, - isPropertyAccessExpression, ModifierFlags, - modifierToFlag, Node, setEmitFlags, - setTextRange, SourceFile, SyntaxKind, TransformationContext, - TransformFlags, TryExpression, UnscopedEmitHelper, visitEachChild, - visitNode, VisitResult, } from "../_namespaces/ts.js"; const TryResultConstructor: UnscopedEmitHelper = { @@ -43,20 +27,7 @@ var TryResult = /** @class */ (function () { this.value = value; } TryResult.prototype[Symbol.iterator] = function () { - return __generator(this, function (_a) { - switch (_a.label) { - case 0: return [4 /*yield*/, this.ok]; - case 1: - _a.sent(); - return [4 /*yield*/, this.error]; - case 2: - _a.sent(); - return [4 /*yield*/, this.value]; - case 3: - _a.sent(); - return [2 /*return*/]; - } - }); + return [this.ok, this.error, this.value].values(); }; TryResult.ok = function (value) { return new TryResult(true, undefined, value); @@ -114,10 +85,10 @@ export function transformTryExpression(context: TransformationContext): (x: Sour factory.createFunctionExpression( factory.createModifiersFromModifierFlags(+hasAwait && ModifierFlags.Async), hasYield ? factory.createToken(SyntaxKind.AsteriskToken) : undefined, - undefined, - undefined, + /*name*/ undefined, + /*typeParameters*/ undefined, [], - undefined, + /*type*/ undefined, factory.createBlock([ setEmitFlags(factory.createTryStatement( setEmitFlags( @@ -143,22 +114,16 @@ export function transformTryExpression(context: TransformationContext): (x: Sour EmitFlags.SingleLine ), ), - undefined + /*finallyBlock*/ undefined ), EmitFlags.SingleLine) ]) ), factory.createThis(), [] ), - undefined, + /*typeArguments*/ undefined, [] ); - // if(hasYield){ - // const _1 = factory.createPropertyAccessExpression(callExpression, "next"); - // const _2 = factory.createCallExpression(_1, undefined, undefined); - // const _3 = factory.createPropertyAccessExpression(_2, "value"); - // return _3; - // } // yield handles both sync and async if (hasYield) { return factory.createYieldExpression( @@ -172,298 +137,5 @@ export function transformTryExpression(context: TransformationContext): (x: Sour } } - function create_try_await_helper(node: TryExpression, hasAwait: boolean) { - - } - - /** - * Gets an identifier for the name of an *unscoped* emit helper. - */ - function getUnscopedHelperName(name: string) { - return setEmitFlags(factory.createIdentifier(name), EmitFlags.HelperName | EmitFlags.AdviseOnEmitNode); - } - // function createTryStatement(){ - // return factory.createTryStatement( - // factory.createBlock([ - // factory.restoreEnclosingLabel( - // forStatement, - // outermostLabeledStatement, - // ), - // ]), - // factory.createCatchClause( - // factory.createVariableDeclaration(catchVariable), - // setEmitFlags( - // factory.createBlock([ - // factory.createExpressionStatement( - // factory.createAssignment( - // errorRecord, - // factory.createObjectLiteralExpression([ - // factory.createPropertyAssignment("error", catchVariable), - // ]), - // ), - // ), - // ]), - // EmitFlags.SingleLine, - // ), - // ), - // factory.createBlock([ - // factory.createTryStatement( - // /*tryBlock*/ factory.createBlock([ - // setEmitFlags( - // factory.createIfStatement( - // factory.createLogicalAnd( - // factory.createLogicalAnd( - // factory.createLogicalNot(nonUserCode), - // factory.createLogicalNot(done), - // ), - // factory.createAssignment( - // returnMethod, - // factory.createPropertyAccessExpression(iterator, "return"), - // ), - // ), - // factory.createExpressionStatement(createDownlevelAwait(callReturn)), - // ), - // EmitFlags.SingleLine, - // ), - // ]), - // /*catchClause*/ undefined, - // /*finallyBlock*/ setEmitFlags( - // factory.createBlock([ - // setEmitFlags( - // factory.createIfStatement( - // errorRecord, - // factory.createThrowStatement( - // factory.createPropertyAccessExpression(errorRecord, "error"), - // ), - // ), - // EmitFlags.SingleLine, - // ), - // ]), - // EmitFlags.SingleLine, - // ), - // ), - // ]), - // ); - // } - // function visitExponentiationAssignmentExpression(node: BinaryExpression) { - // let target: Expression; - // let value: Expression; - // const left = visitNode(node.left, visitor, isExpression); - // const right = visitNode(node.right, visitor, isExpression); - // if (isElementAccessExpression(left)) { - // // Transforms `a[x] **= b` into `(_a = a)[_x = x] = Math.pow(_a[_x], b)` - // const expressionTemp = factory.createTempVariable(hoistVariableDeclaration); - // const argumentExpressionTemp = factory.createTempVariable(hoistVariableDeclaration); - // target = setTextRange( - // factory.createElementAccessExpression( - // setTextRange(factory.createAssignment(expressionTemp, left.expression), left.expression), - // setTextRange(factory.createAssignment(argumentExpressionTemp, left.argumentExpression), left.argumentExpression), - // ), - // left, - // ); - // value = setTextRange( - // factory.createElementAccessExpression( - // expressionTemp, - // argumentExpressionTemp, - // ), - // left, - // ); - // } - // else if (isPropertyAccessExpression(left)) { - // // Transforms `a.x **= b` into `(_a = a).x = Math.pow(_a.x, b)` - // const expressionTemp = factory.createTempVariable(hoistVariableDeclaration); - // target = setTextRange( - // factory.createPropertyAccessExpression( - // setTextRange(factory.createAssignment(expressionTemp, left.expression), left.expression), - // left.name, - // ), - // left, - // ); - // value = setTextRange( - // factory.createPropertyAccessExpression( - // expressionTemp, - // left.name, - // ), - // left, - // ); - // } - // else { - // // Transforms `a **= b` into `a = Math.pow(a, b)` - // target = left; - // value = left; - // } - // return setTextRange( - // factory.createAssignment( - // target, - // setTextRange(factory.createGlobalMethodCall("Math", "pow", [value, right]), node), - // ), - // node, - // ); - // } - - // function visitExponentiationExpression(node: BinaryExpression) { - // // Transforms `a ** b` into `Math.pow(a, b)` - // const left = visitNode(node.left, visitor, isExpression); - // const right = visitNode(node.right, visitor, isExpression); - // return setTextRange(factory.createGlobalMethodCall("Math", "pow", [left, right]), node); - // } } - - - - -// import { -// NodeFactory, -// TransformationContext, -// Node, -// VisitResult, -// Identifier, -// Expression, -// SyntaxKind, -// VariableDeclaration, -// EmitFlags, -// factory as tsFactory, -// chainBundle, -// } from "../_namespaces/ts.js"; - -// /** -// * Transforms the following syntax: -// * ```js -// * const result = try expr1; -// * ``` -// * Into: -// * ```js -// * let _result; -// * try { -// * _result = TryResult.ok(expr1); -// * } catch (error) { -// * _result = TryResult.error(error); -// * } -// * const result = _result; -// * ``` -// */ -// export function transformTryExpression(context: TransformationContext): (node: Node) => VisitResult { -// const { factory } = context; - - -// return chainBundle(context, transformSourceFile); -// } - -// function transformSourceFile(node: SourceFile) { -// if (node.isDeclarationFile) { -// return node; -// } - -// currentSourceFile = node; -// const visited = visitSourceFile(node); -// addEmitHelpers(visited, context.readEmitHelpers()); - -// currentSourceFile = undefined!; -// taggedTemplateStringDeclarations = undefined!; -// return visited; -// return (node: Node): VisitResult => { -// if ( -// node.kind === SyntaxKind.VariableDeclaration && -// isTryExpressionDeclaration(node as VariableDeclaration) -// ) { -// return transformTryExpressionDeclaration(node as VariableDeclaration, factory); -// } -// return node; -// }; -// } - -// function isTryExpressionDeclaration(node: VariableDeclaration): boolean { -// if ( -// node.initializer && -// node.initializer.kind === SyntaxKind.TryExpression && -// node.name.kind === SyntaxKind.Identifier -// ) { -// return true; -// } -// return false; -// } - -// function transformTryExpressionDeclaration( -// node: VariableDeclaration, -// factory: NodeFactory -// ): Node { -// const resultVarName = factory.createUniqueName("_result"); -// const resultConstName = node.name as Identifier; -// const expr = node.initializer as Expression; - -// // Create `let _result;` -// const tempResultDecl = factory.createVariableStatement( -// /*modifiers*/ undefined, -// factory.createVariableDeclarationList([ -// factory.createVariableDeclaration( -// resultVarName, -// /*exclamationToken*/ undefined, -// /*type*/ undefined, -// /*initializer*/ undefined -// ), -// ]) -// ); - -// // Create `try { _result = TryResult.ok(expr); }` -// const tryBlock = factory.createBlock([ -// factory.createExpressionStatement( -// factory.createBinaryExpression( -// resultVarName, -// SyntaxKind.EqualsToken, -// factory.createCallExpression( -// factory.createPropertyAccessExpression( -// factory.createIdentifier("TryResult"), -// "ok" -// ), -// /*typeArguments*/ undefined, -// [expr] -// ) -// ) -// ), -// ], -// /*multiLine*/ true); - -// // Create `catch (error) { _result = TryResult.error(error); }` -// const catchBlock = factory.createCatchClause( -// "error", -// factory.createBlock([ -// factory.createExpressionStatement( -// factory.createBinaryExpression( -// resultVarName, -// SyntaxKind.EqualsToken, -// factory.createCallExpression( -// factory.createPropertyAccessExpression( -// factory.createIdentifier("TryResult"), -// "error" -// ), -// /*typeArguments*/ undefined, -// [factory.createIdentifier("error")] -// ) -// ) -// ) -// ], -// /*multiLine*/ false), -// ); - -// const tryStatement = factory.createTryStatement( -// tryBlock, -// catchBlock, -// /*finallyBlock*/ undefined -// ); - -// // Create `const result = _result;` -// const resultConstDecl = factory.createVariableStatement( -// /*modifiers*/ undefined, -// factory.createVariableDeclarationList([ -// factory.createVariableDeclaration( -// resultConstName, -// /*exclamationToken*/ undefined, -// /*type*/ undefined, -// resultVarName -// ), -// ]) -// ); - -// return [tempResultDecl, tryStatement, resultConstDecl]; -// } From 97b3b8b950abb98905111e195fd7a4e5aeb46f13 Mon Sep 17 00:00:00 2001 From: Arlen22 Date: Fri, 24 Jan 2025 00:25:42 +0000 Subject: [PATCH 06/12] fix some missing type stuff --- src/compiler/utilities.ts | 16 ++++++++- src/compiler/utilitiesPublic.ts | 1 + src/lib/arlen22.d.ts | 62 --------------------------------- src/lib/es5.d.ts | 61 +++++++++++++++++++++++++++++++- 4 files changed, 76 insertions(+), 64 deletions(-) delete mode 100644 src/lib/arlen22.d.ts diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index d12ae297b81ad..b09aa04893f4f 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -559,6 +559,7 @@ import { TransientSymbol, TriviaSyntaxKind, tryCast, + TryExpression, tryRemovePrefix, TryStatement, TsConfigSourceFile, @@ -2501,9 +2502,10 @@ export function getErrorSpanForNode(sourceFile: SourceFile, node: Node): TextSpa const end = (node as CaseOrDefaultClause).statements.length > 0 ? (node as CaseOrDefaultClause).statements[0].pos : (node as CaseOrDefaultClause).end; return createTextSpanFromBounds(start, end); } + case SyntaxKind.TryExpression: case SyntaxKind.ReturnStatement: case SyntaxKind.YieldExpression: { - const pos = skipTrivia(sourceFile.text, (node as ReturnStatement | YieldExpression).pos); + const pos = skipTrivia(sourceFile.text, (node as ReturnStatement | YieldExpression | TryExpression).pos); return getSpanOfTokenAtPosition(sourceFile, pos); } case SyntaxKind.SatisfiesExpression: { @@ -3587,6 +3589,7 @@ export function isExpressionNode(node: Node): boolean { case SyntaxKind.JsxFragment: case SyntaxKind.YieldExpression: case SyntaxKind.AwaitExpression: + case SyntaxKind.TryExpression: case SyntaxKind.MetaProperty: return true; case SyntaxKind.ExpressionWithTypeArguments: @@ -5558,6 +5561,7 @@ export function getOperatorAssociativity(kind: SyntaxKind, operator: SyntaxKind, case SyntaxKind.DeleteExpression: case SyntaxKind.AwaitExpression: case SyntaxKind.ConditionalExpression: + case SyntaxKind.TryExpression: case SyntaxKind.YieldExpression: return Associativity.Right; @@ -5635,6 +5639,13 @@ export const enum OperatorPrecedence { // `yield` `*` AssignmentExpression Yield, + // NOTE: `Yield` and `Try` should have the same precedence + // AssignmentExpression: TryExpression + // TryExpression: + // `try` AssignmentExpression + TryExpression, + + // AssignmentExpression: LeftHandSideExpression `=` AssignmentExpression // AssignmentExpression: LeftHandSideExpression AssignmentOperator AssignmentExpression // AssignmentOperator: one of @@ -5810,6 +5821,9 @@ export function getOperatorPrecedence(nodeKind: SyntaxKind, operatorKind: Syntax case SyntaxKind.YieldExpression: return OperatorPrecedence.Yield; + case SyntaxKind.TryExpression: + return OperatorPrecedence.TryExpression; + case SyntaxKind.ConditionalExpression: return OperatorPrecedence.Conditional; diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index 02e22b94c895e..25dd96162510b 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -2082,6 +2082,7 @@ function isExpressionKind(kind: SyntaxKind): boolean { switch (kind) { case SyntaxKind.ConditionalExpression: case SyntaxKind.YieldExpression: + case SyntaxKind.TryExpression: case SyntaxKind.ArrowFunction: case SyntaxKind.BinaryExpression: case SyntaxKind.SpreadElement: diff --git a/src/lib/arlen22.d.ts b/src/lib/arlen22.d.ts deleted file mode 100644 index 8b5cb869cdac6..0000000000000 --- a/src/lib/arlen22.d.ts +++ /dev/null @@ -1,62 +0,0 @@ - - -/** - * Error result type expressed as object - */ -type TryResultErrorObject = { ok: false; error: unknown; value: undefined } - -/** - * Error result type expressed as tuple. - * - * - `error` type depends on `useUnknownInCatchVariables` tsconfig option - */ -type TryResultErrorTuple = [ok: false, error: unknown, value: undefined] - -/** - * An error result is a object that can be either destructured {@link TryResultErrorObject} or accessed by index {@link TryResultErrorTuple} - */ -type TryResultError = TryResultErrorObject & TryResultErrorTuple - -/** - * Value result type expressed as object - */ -type TryResultValueObject = { ok: true; error: undefined; value: V } - -/** - * Value result type expressed as tuple - */ -type TryResultValueTuple = [ok: true, error: undefined, value: V] - -/** - * A value result is a object that can be either destructured {@link TryResultValueObject} or accessed by index {@link TryResultValueTuple} - */ -type TryResultValue = TryResultValueObject & TryResultValueTuple - -/** - * A result is a object that can represent the result of either a failed or successful operation. - */ -type TryResult = TryResultError | TryResultValue - -interface ResultConstructor { - /** - * Creates a result from a tuple - * - * @example - * - * new Result(true, undefined, 42) - * new Result(false, new Error('Something went wrong')) - */ - new (...args: TryResultValueTuple | TryResultErrorTuple): TryResult - - /** - * Creates a result for a successful operation - */ - ok(value: V): TryResult - - /** - * Creates a result for a failed operation - */ - error(error: unknown): TryResult -} - -declare const TryResult: ResultConstructor diff --git a/src/lib/es5.d.ts b/src/lib/es5.d.ts index 628bf6ebded50..c50539892f05a 100644 --- a/src/lib/es5.d.ts +++ b/src/lib/es5.d.ts @@ -1,11 +1,70 @@ /// /// -/// ///////////////////////////// /// Arlen22 Fork Additions ///////////////////////////// +/** + * Error result type expressed as object + */ +type TryResultErrorObject = { ok: false; error: unknown; value: undefined } + +/** + * Error result type expressed as tuple. + * + * - `error` type depends on `useUnknownInCatchVariables` tsconfig option + */ +type TryResultErrorTuple = [ok: false, error: unknown, value: undefined] + +/** + * An error result is a object that can be either destructured {@link TryResultErrorObject} or accessed by index {@link TryResultErrorTuple} + */ +type TryResultError = TryResultErrorObject & TryResultErrorTuple + +/** + * Value result type expressed as object + */ +type TryResultValueObject = { ok: true; error: undefined; value: V } + +/** + * Value result type expressed as tuple + */ +type TryResultValueTuple = [ok: true, error: undefined, value: V] + +/** + * A value result is a object that can be either destructured {@link TryResultValueObject} or accessed by index {@link TryResultValueTuple} + */ +type TryResultValue = TryResultValueObject & TryResultValueTuple + +/** + * A result is a object that can represent the result of either a failed or successful operation. + */ +type TryResult = TryResultError | TryResultValue + +interface ResultConstructor { + /** + * Creates a result from a tuple + * + * @example + * + * new Result(true, undefined, 42) + * new Result(false, new Error('Something went wrong')) + */ + new (...args: TryResultValueTuple | TryResultErrorTuple): TryResult + + /** + * Creates a result for a successful operation + */ + ok(value: V): TryResult + + /** + * Creates a result for a failed operation + */ + error(error: unknown): TryResult +} + +declare const TryResult: ResultConstructor ///////////////////////////// From 60cd21f9d817715ea41bede6f6126213fb1706fd Mon Sep 17 00:00:00 2001 From: Arlen22 Date: Fri, 24 Jan 2025 00:32:05 +0000 Subject: [PATCH 07/12] update version --- example/package.json | 5 ++++- package-lock.json | 4 ++-- package.json | 6 ++++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/example/package.json b/example/package.json index 1ae3db1565d1d..7fc4da5cb6a7e 100644 --- a/example/package.json +++ b/example/package.json @@ -8,5 +8,8 @@ }, "keywords": [], "author": "", - "license": "ISC" + "license": "ISC", + "dependencies": { + "typescript": "file:../typescript-5.8.0-arlen22-0.2.tgz" + } } diff --git a/package-lock.json b/package-lock.json index b089dc3b035a7..00118fe615f0e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "typescript", - "version": "5.8.0", + "version": "5.8.0-arlen22-0.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "typescript", - "version": "5.8.0", + "version": "5.8.0-arlen22-0.3", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", diff --git a/package.json b/package.json index 1886c2ad5b53f..e2a16b2730ef8 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "typescript", "author": "Microsoft Corp.", "homepage": "https://www.typescriptlang.org/", - "version": "5.8.0-arlen22-0.2", + "version": "5.8.0-arlen22-0.3", "license": "Apache-2.0", "description": "TypeScript is a language for application scale JavaScript development", "keywords": [ @@ -100,7 +100,9 @@ "knip": "hereby knip", "format": "dprint fmt", "setup-hooks": "node scripts/link-hooks.mjs", - "pack": "hereby local && hereby LKG && hereby clean" + "pack": "hereby local && hereby LKG && hereby clean", + "build:dev": "npm run pack && npm pack && (cd example/ && npm install ../typescript-5.8.0-arlen22-0.2.tgz)", + "install:dev": "mkdir -p example && cd example/ && npm init -y" }, "browser": { "fs": false, From 53888b687781698a74c065d8fe03b75079df7065 Mon Sep 17 00:00:00 2001 From: Arlen22 Date: Thu, 23 Jan 2025 19:42:39 -0500 Subject: [PATCH 08/12] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3beeb20d3ee12..cf95cbe21a7db 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This fork contains a few modifications to Typescript to preview the try-expressi - Create a new npm project folder using `npm init`. - `npm install` the built tgz file from the releases page of this repository. - - https://github.com/Arlen22/TypeScript/releases/download/arlen22-dev-0.2/typescript-5.8.0-arlen22-0.2.tgz + - https://github.com/Arlen22/TypeScript/releases/download/arlen22-dev-0.3/typescript-5.8.0-arlen22-0.3.tgz - In VSCode, create a new typescript file and open it to enable the Typescript engine, then run the command "Typescript: Select Typescript Version" and select the version from your node_modules folder. - Create a tsconfig.json file with `strictNullChecks` enabled. - If you run `tsc` it will just pass the syntax through to the Javascript file. From 8b0811293e4aaec1bbf9dd03a9e965e757a19ed0 Mon Sep 17 00:00:00 2001 From: Arlen22 Date: Fri, 24 Jan 2025 01:05:41 +0000 Subject: [PATCH 09/12] example target es2015 --- example/test.js | 159 +++++++++++++++++++++++++++++------------------- example/test.ts | 58 +++++++----------- 2 files changed, 118 insertions(+), 99 deletions(-) diff --git a/example/test.js b/example/test.js index cf70421cc5fa8..fac18a71375ab 100644 --- a/example/test.js +++ b/example/test.js @@ -6,20 +6,7 @@ var TryResult = /** @class */ (function () { this.value = value; } TryResult.prototype[Symbol.iterator] = function () { - return __generator(this, function (_a) { - switch (_a.label) { - case 0: return [4 /*yield*/, this.ok]; - case 1: - _a.sent(); - return [4 /*yield*/, this.error]; - case 2: - _a.sent(); - return [4 /*yield*/, this.value]; - case 3: - _a.sent(); - return [2 /*return*/]; - } - }); + return [this.ok, this.error, this.value].values(); }; TryResult.ok = function (value) { return new TryResult(true, undefined, value); @@ -29,56 +16,104 @@ var TryResult = /** @class */ (function () { }; return TryResult; }()); +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } +var __asyncValues = (this && this.__asyncValues) || function (o) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var m = o[Symbol.asyncIterator], i; + return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); + function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } + function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } +}; +var __asyncDelegator = (this && this.__asyncDelegator) || function (o) { + var i, p; + return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; + function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v; } : f; } +}; +var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var g = generator.apply(thisArg, _arguments || []), i, q = []; + return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i; + function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; } + function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } } + function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } + function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } + function fulfill(value) { resume("next", value); } + function reject(value) { resume("throw", value); } + function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } +}; Object.defineProperty(exports, "__esModule", { value: true }); -function* test() { - try { - const test2 = yield (yield fetch("url")).json(); - console.log("success"); - } - catch (e) { - console.log("failed"); - } - console.log("continue"); +function examples() { + return __awaiter(this, void 0, void 0, function* () { + // array.map((fn) => try fn()).filter((result) => result.ok); + const data = {}; + const expr1 = Promise.resolve("hello"); + const expr2 = Promise.resolve("world"); + const check = true; + let result; + result = function () { try { return TryResult.ok(expr1); } catch (e) { return TryResult.error(e); } }.bind(this)(); // literally any expression + result = function () { try { return TryResult.ok(expr1 || expr2); } catch (e) { return TryResult.error(e); } }.bind(this)(); // the try covers both, same as an arrow function body + result = function () { try { return TryResult.ok(check ? expr1 : expr2); } catch (e) { return TryResult.error(e); } }.bind(this)(); // try covers the entire expression + result = function () { try { return TryResult.ok(expr1 !== null && expr1 !== void 0 ? expr1 : expr2); } catch (e) { return TryResult.error(e); } }.bind(this)(); // again covers the entire expression + result = yield function () { + return __awaiter(this, void 0, void 0, function* () { var _a, _b; try { return TryResult.ok((_b = data === null || data === void 0 ? void 0 : (_a = data.someProperty).anotherFunction) === null || _b === void 0 ? void 0 : _b.call(_a, yield fetch("")).andAnotherOne()); } catch (e) { return TryResult.error(e); } }); + }.bind(this)(); + result = yield function () { + return __awaiter(this, void 0, void 0, function* () { try { return TryResult.ok(yield fetch("https://api.example.com/data", { headers: {} })); } catch (e) { return TryResult.error(e); } }); + }.bind(this)(); + result = yield function () { + return __awaiter(this, void 0, void 0, function* () { try { return TryResult.ok((yield expr1, yield expr2)); } catch (e) { return TryResult.error(e); } }); + }.bind(this)(); // covers all, returning the last one + result = yield function () { + return __awaiter(this, void 0, void 0, function* () { try { return TryResult.ok(({ "my": yield expr1 })); } catch (e) { return TryResult.error(e); } }); + }.bind(this)(); // convers everything inside the object + result = yield function () { + return __awaiter(this, void 0, void 0, function* () { try { return TryResult.ok([yield (yield fetch("")).json(), expr1, expr2]); } catch (e) { return TryResult.error(e); } }); + }.bind(this)(); // covers everything inside the array + // result = try this.test = this.test2 = await fetch(""); // covers everything + }); } -async function tester(arg) { +function examples2() { + return __asyncGenerator(this, arguments, function* examples2_1() { + const expression = "hello"; + console.log(function () { try { return TryResult.ok(expression); } catch (e) { return TryResult.error(e); } }.bind(this)()); + console.log(yield __await(function () { + return __awaiter(this, void 0, void 0, function* () { try { return TryResult.ok(yield expression); } catch (e) { return TryResult.error(e); } }); + }.bind(this)())); + console.log(yield __await(yield* __asyncDelegator(__asyncValues(function* () { try { return TryResult.ok(yield expression); } catch (e) { return TryResult.error(e); } }.bind(this)())))); + console.log(yield __await(yield* __asyncDelegator(__asyncValues(function () { return __asyncGenerator(this, arguments, function* () { try { return yield __await(TryResult.ok(yield yield __await(yield __await(expression)))); } catch (e) { return yield __await(TryResult.error(e)); } }); }.bind(this)())))); + console.log(yield yield __await(function () { try { return TryResult.ok(expression); } catch (e) { return TryResult.error(e); } }.bind(this)())); + console.log(yield yield __await(yield __await(function () { + return __awaiter(this, void 0, void 0, function* () { try { return TryResult.ok(yield expression); } catch (e) { return TryResult.error(e); } }); + }.bind(this)()))); + console.log(yield yield __await(yield __await(yield* __asyncDelegator(__asyncValues(function () { return __asyncGenerator(this, arguments, function* () { try { return yield __await(TryResult.ok(yield yield __await(yield __await(expression)))); } catch (e) { return yield __await(TryResult.error(e)); } }); }.bind(this)()))))); + const result = function () { try { return TryResult.ok(expression); } catch (e) { return TryResult.error(e); } }.bind(this)(); + yield __await(Promise.resolve("hello")); + }); +} +(() => __awaiter(void 0, void 0, void 0, function* () { + var _a, e_1, _b, _c; try { - // const test2 = try await (await fetch("")).json(); - // const test3 = try JSON.parse(); - // const test2 = await (async () => { try { return await (await fetch("")).json(); } catch (e) { return e; } })(); - // const test3 = (() => { try { return JSON.parse(); } catch (e) { return e; } })(); - console.log("it worked"); + for (var _d = true, _e = __asyncValues(examples2()), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) { + _c = _f.value; + _d = false; + const t = _c; + console.log(t); + } } - catch (e) { - console.log("it didn't"); + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (!_d && !_a && (_b = _e.return)) yield _b.call(_e); + } + finally { if (e_1) throw e_1.error; } } - console.log("keep going"); -} -// async function examples() { -// array.map((fn) => try fn()).filter((result) => result.ok); -// let result; -// result = try expr1; // literally any expression -// result = try data?.someProperty.anotherFunction?.(await someData()).andAnotherOne() -// result = try await fetch("https://api.example.com/data", { headers: {} }) -// result = try expression1 || expression2; // the try covers both, same as an arrow function body -// result = try check ? expression1 : expression2; // try covers the entire expression -// result = try expression1 ?? expresssion2; // again covers the entire expression -// result = try (expression1, expression2); // covers all, returning the last one -// result = try ({ "my": await fetch() }); // convers everything inside the object -// result = try [await (await fetch()).json(), expression1, anythingelse]; // covers everything inside the array -// result = try this.test = this.test2 = await fetch(); // covers everything -// } -async function* examples2() { - // function test2() { console.log(arguments) } - const expression = "hello"; - console.log(await async function () { try { return TryResult.ok(await expression); } catch (e) { return TryResult.error(e); } }.bind(this)()); - console.log(yield* function* () { try { return TryResult.ok(yield expression); } catch (e) { return TryResult.error(e); } }.bind(this)()); - console.log(yield* async function* () { try { return TryResult.ok(yield await expression); } catch (e) { return TryResult.error(e); } }.bind(this)()); - console.log(yield function () { try { return TryResult.ok(expression); } catch (e) { return TryResult.error(e); } }.bind(this)()); - console.log(yield await async function () { try { return TryResult.ok(await expression); } catch (e) { return TryResult.error(e); } }.bind(this)()); - console.log(yield yield* async function* () { try { return TryResult.ok(yield await expression); } catch (e) { return TryResult.error(e); } }.bind(this)()); - await Promise.resolve("hello"); -} -(async () => { - for await (const t of examples2()) - console.log(t); -})(); +}))(); diff --git a/example/test.ts b/example/test.ts index f61fc2fe3c8cd..68e1e252b4b28 100644 --- a/example/test.ts +++ b/example/test.ts @@ -1,54 +1,38 @@ export { }; -function* test() { - try { - const test2 = yield (yield fetch("url")).json() - console.log("success") - } catch (e) { - console.log("failed"); - } - console.log("continue"); -} - -async function tester(arg: any) { - try { - // const test2 = try await (await fetch("")).json(); - // const test3 = try JSON.parse(); - // const test2 = await (async () => { try { return await (await fetch("")).json(); } catch (e) { return e; } })(); - // const test3 = (() => { try { return JSON.parse(); } catch (e) { return e; } })(); - console.log("it worked"); - } catch (e) { - console.log("it didn't") - } - console.log("keep going"); -} -// async function examples() { -// array.map((fn) => try fn()).filter((result) => result.ok); -// let result; -// result = try expr1; // literally any expression -// result = try data?.someProperty.anotherFunction?.(await someData()).andAnotherOne() -// result = try await fetch("https://api.example.com/data", { headers: {} }) -// result = try expression1 || expression2; // the try covers both, same as an arrow function body -// result = try check ? expression1 : expression2; // try covers the entire expression -// result = try expression1 ?? expresssion2; // again covers the entire expression -// result = try (expression1, expression2); // covers all, returning the last one -// result = try ({ "my": await fetch() }); // convers everything inside the object -// result = try [await (await fetch()).json(), expression1, anythingelse]; // covers everything inside the array -// result = try this.test = this.test2 = await fetch(); // covers everything -// } +async function examples() { + // array.map((fn) => try fn()).filter((result) => result.ok); + const data = {} as any; + const expr1 = Promise.resolve("hello"); + const expr2 = Promise.resolve("world"); + const check = true; + let result; + result = try expr1; // literally any expression + result = try expr1 || expr2; // the try covers both, same as an arrow function body + result = try check ? expr1 : expr2; // try covers the entire expression + result = try expr1 ?? expr2; // again covers the entire expression + result = try data?.someProperty.anotherFunction?.(await fetch("")).andAnotherOne() + result = try await fetch("https://api.example.com/data", { headers: {} }) + result = try (await expr1, await expr2); // covers all, returning the last one + result = try ({ "my": await expr1 }); // convers everything inside the object + result = try [await (await fetch("")).json(), expr1, expr2]; // covers everything inside the array + // result = try this.test = this.test2 = await fetch(""); // covers everything +} async function* examples2() { - // function test2() { console.log(arguments) } + const expression = "hello"; + console.log(try expression); console.log(try await expression); console.log(try yield expression); console.log(try yield await expression); console.log(yield try expression); console.log(yield try await expression); console.log(yield try yield await expression); + const result = try expression; await Promise.resolve("hello"); } From 6870388268ad94cd55f5dc40311e965cda9e6ee9 Mon Sep 17 00:00:00 2001 From: Arlen22 Date: Fri, 24 Jan 2025 01:06:31 +0000 Subject: [PATCH 10/12] fix version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e2a16b2730ef8..96fa4d8e16d56 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "format": "dprint fmt", "setup-hooks": "node scripts/link-hooks.mjs", "pack": "hereby local && hereby LKG && hereby clean", - "build:dev": "npm run pack && npm pack && (cd example/ && npm install ../typescript-5.8.0-arlen22-0.2.tgz)", + "build:dev": "npm run pack && npm pack && (cd example/ && npm install ../typescript-5.8.0-arlen22-0.3.tgz)", "install:dev": "mkdir -p example && cd example/ && npm init -y" }, "browser": { From 5d66a13c018373beab57dd2418b8209e2068d20d Mon Sep 17 00:00:00 2001 From: Arlen22 Date: Fri, 24 Jan 2025 01:07:13 +0000 Subject: [PATCH 11/12] extra file --- example/test2.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 example/test2.js diff --git a/example/test2.js b/example/test2.js new file mode 100644 index 0000000000000..956d427c00c94 --- /dev/null +++ b/example/test2.js @@ -0,0 +1,18 @@ + +function* test() { + + return yield* function*(){ + try { + console.log(yield "hello"); + console.log(yield "world"); + return "done"; + } catch (e){ + return e; + } + }.bind(this)(); +} +// const test = (while(!i.done) {i = gen.next(yield i.value);}) +const gen = test(1); +console.log(gen.next()); +console.log(gen.next("welcome")); +console.log(gen.next("finished")); From 18ded0924a723f9fd18a90f098ec6a9f5f2dc3cb Mon Sep 17 00:00:00 2001 From: arlen22 Date: Fri, 28 Feb 2025 15:03:54 -0500 Subject: [PATCH 12/12] update examples folder --- example/package.json | 4 +- example/poly.ts | 59 ++++++++++++++ example/test.js | 180 +++++++++++++++---------------------------- example/test.ts | 18 +++++ 4 files changed, 143 insertions(+), 118 deletions(-) create mode 100644 example/poly.ts diff --git a/example/package.json b/example/package.json index 7fc4da5cb6a7e..22df595305e6b 100644 --- a/example/package.json +++ b/example/package.json @@ -4,12 +4,12 @@ "description": "", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "build": "tsc test.ts --target esnext" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { - "typescript": "file:../typescript-5.8.0-arlen22-0.2.tgz" + "typescript": "https://github.com/Arlen22/TypeScript/releases/download/arlen22-dev-0.3/typescript-5.8.0-arlen22-0.3.tgz" } } diff --git a/example/poly.ts b/example/poly.ts new file mode 100644 index 0000000000000..a110fe65fda0b --- /dev/null +++ b/example/poly.ts @@ -0,0 +1,59 @@ +function isPromise(result) { return result && typeof result.then === 'function'; } +function isIterable(result) { return result && typeof result[Symbol.iterator] === 'function'; } +function isAsyncIterable(result) { return result && typeof result[Symbol.asyncIterator] === 'function'; } + +function try_(callback) { + const syncCall = (function () { + try { + return TryResult.ok(callback()); + } catch (e) { + return TryResult.error(e); + } + }).apply(this) as TryResult; + + if (!syncCall.ok) return syncCall; + + if (isPromise(syncCall.value)) { + return syncCall.value.then( + (value) => TryResult.ok(value), + (e) => TryResult.error(e) + ); + } + // Returns the value returned by that iterator when it's closed (when done is true). + if (isIterable(syncCall.value)) { + return (function* () { + try { + return TryResult.ok(yield* syncCall.value); + } catch (e) { + return TryResult.error(e); + } + }).bind(this)(); + } + + if (isAsyncIterable(syncCall.value)) { + return (async function* () { + try { + return TryResult.ok(yield* syncCall.value); + } catch (e) { + return TryResult.error(e); + } + }).bind(this)(); + } + +} + + +async function* demo() { + // try "hello" + const [ok, error, value] = try_("hello"); + + // try await "hello" + const [ok, error, value] = await try_(async () => await "hello"); + + // try yield "hello" + const [ok, error, value] = yield* try_(function* () { return "hello"; }); + + // try yield await "hello" + const [ok, error, value] = yield* try_(async function* () { return await "hello"; }); + +} diff --git a/example/test.js b/example/test.js index fac18a71375ab..a3fb063f4f144 100644 --- a/example/test.js +++ b/example/test.js @@ -1,119 +1,67 @@ -"use strict"; -var TryResult = /** @class */ (function () { - function TryResult(ok, error, value) { - this.ok = ok; - this.error = error; - this.value = value; - } - TryResult.prototype[Symbol.iterator] = function () { - return [this.ok, this.error, this.value].values(); - }; - TryResult.ok = function (value) { - return new TryResult(true, undefined, value); - }; - TryResult.error = function (error) { - return new TryResult(false, error, undefined); - }; - return TryResult; +var TryResult = /** @class */ (function() { + function TryResult(ok, error, value) { + this.ok = ok; + this.error = error; + this.value = value; + } + TryResult.prototype[Symbol.iterator] = function() { + return [this.ok, this.error, this.value].values(); + }; + TryResult.ok = function(value) { + return new TryResult(true, undefined, value); + }; + TryResult.error = function(error) { + return new TryResult(false, error, undefined); + }; + return TryResult; }()); -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } -var __asyncValues = (this && this.__asyncValues) || function (o) { - if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); - var m = o[Symbol.asyncIterator], i; - return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); - function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } - function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } -}; -var __asyncDelegator = (this && this.__asyncDelegator) || function (o) { - var i, p; - return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; - function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v; } : f; } -}; -var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) { - if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); - var g = generator.apply(thisArg, _arguments || []), i, q = []; - return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i; - function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; } - function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } } - function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } - function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } - function fulfill(value) { resume("next", value); } - function reject(value) { resume("throw", value); } - function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } -}; -Object.defineProperty(exports, "__esModule", { value: true }); -function examples() { - return __awaiter(this, void 0, void 0, function* () { - // array.map((fn) => try fn()).filter((result) => result.ok); - const data = {}; - const expr1 = Promise.resolve("hello"); - const expr2 = Promise.resolve("world"); - const check = true; - let result; - result = function () { try { return TryResult.ok(expr1); } catch (e) { return TryResult.error(e); } }.bind(this)(); // literally any expression - result = function () { try { return TryResult.ok(expr1 || expr2); } catch (e) { return TryResult.error(e); } }.bind(this)(); // the try covers both, same as an arrow function body - result = function () { try { return TryResult.ok(check ? expr1 : expr2); } catch (e) { return TryResult.error(e); } }.bind(this)(); // try covers the entire expression - result = function () { try { return TryResult.ok(expr1 !== null && expr1 !== void 0 ? expr1 : expr2); } catch (e) { return TryResult.error(e); } }.bind(this)(); // again covers the entire expression - result = yield function () { - return __awaiter(this, void 0, void 0, function* () { var _a, _b; try { return TryResult.ok((_b = data === null || data === void 0 ? void 0 : (_a = data.someProperty).anotherFunction) === null || _b === void 0 ? void 0 : _b.call(_a, yield fetch("")).andAnotherOne()); } catch (e) { return TryResult.error(e); } }); - }.bind(this)(); - result = yield function () { - return __awaiter(this, void 0, void 0, function* () { try { return TryResult.ok(yield fetch("https://api.example.com/data", { headers: {} })); } catch (e) { return TryResult.error(e); } }); - }.bind(this)(); - result = yield function () { - return __awaiter(this, void 0, void 0, function* () { try { return TryResult.ok((yield expr1, yield expr2)); } catch (e) { return TryResult.error(e); } }); - }.bind(this)(); // covers all, returning the last one - result = yield function () { - return __awaiter(this, void 0, void 0, function* () { try { return TryResult.ok(({ "my": yield expr1 })); } catch (e) { return TryResult.error(e); } }); - }.bind(this)(); // convers everything inside the object - result = yield function () { - return __awaiter(this, void 0, void 0, function* () { try { return TryResult.ok([yield (yield fetch("")).json(), expr1, expr2]); } catch (e) { return TryResult.error(e); } }); - }.bind(this)(); // covers everything inside the array - // result = try this.test = this.test2 = await fetch(""); // covers everything - }); +async function examples() { + // array.map((fn) => try fn()).filter((result) => result.ok); + const data = {}; + const expr1 = Promise.resolve("hello"); + const expr2 = Promise.resolve("world"); + const check = true; + let result; + result = function() { try { return TryResult.ok(expr1); } catch(e) { return TryResult.error(e); } }.bind(this)(); // literally any expression + result = function() { try { return TryResult.ok(expr1 || expr2); } catch(e) { return TryResult.error(e); } }.bind(this)(); // the try covers both, same as an arrow function body + result = function() { try { return TryResult.ok(check ? expr1 : expr2); } catch(e) { return TryResult.error(e); } }.bind(this)(); // try covers the entire expression + result = function() { try { return TryResult.ok(expr1 ?? expr2); } catch(e) { return TryResult.error(e); } }.bind(this)(); // again covers the entire expression + result = await async function() { try { return TryResult.ok(data?.someProperty.anotherFunction?.(await fetch("")).andAnotherOne()); } catch(e) { return TryResult.error(e); } }.bind(this)(); + result = await async function() { try { return TryResult.ok(await fetch("https://api.example.com/data", { headers: {} })); } catch(e) { return TryResult.error(e); } }.bind(this)(); + result = await async function() { try { return TryResult.ok((await expr1, await expr2)); } catch(e) { return TryResult.error(e); } }.bind(this)(); // covers all, returning the last one + result = await async function() { try { return TryResult.ok(({ "my": await expr1 })); } catch(e) { return TryResult.error(e); } }.bind(this)(); // convers everything inside the object + result = await async function() { try { return TryResult.ok([await (await fetch("")).json(), expr1, expr2]); } catch(e) { return TryResult.error(e); } }.bind(this)(); // covers everything inside the array + // result = try this.test = this.test2 = await fetch(""); // covers everything + const test = await async function() { try { return TryResult.ok(await createUser((await async function() { try { return TryResult.ok(await (await fetch("")).json()); } catch(e) { return TryResult.error(e); } }.bind(this)()).value)); } catch(e) { return TryResult.error(e); } }.bind(this)(); + // const exprsync = try expr1; + const exprsync = function() { try { return TryResult.ok(expr1); } catch(e) { return TryResult.error(e); } }.bind(this)(); + // const exprasync = try await expr1; + const exprasync = await async function() { try { return TryResult.ok(await expr1); } catch(e) { return TryResult.error(e); } }.bind(this)(); + // const expryield = try yield expr1; + const expryield = yield * function*() { try { return TryResult.ok(yield expr1); } catch(e) { return TryResult.error(e); } }.bind(this)(); + // const expryieldasync = try yield await expr1; + const expryieldasync = yield * async function*() { try { return TryResult.ok(yield await expr1); } catch(e) { return TryResult.error(e); } }.bind(this)(); + // const exprsyncyield = yield try expr1; + const exprsyncyield = yield function() { try { return TryResult.ok(expr1); } catch(e) { return TryResult.error(e); } }.bind(this)(); + // const exprsyncyieldasync = yield try await expr1; + const exprsyncyieldasync = yield await async function() { try { return TryResult.ok(await expr1); } catch(e) { return TryResult.error(e); } }.bind(this)(); + // const expryieldyieldasync = yield try yield await expr1; + const expryieldyieldasync = yield yield * async function*() { try { return TryResult.ok(yield await expr1); } catch(e) { return TryResult.error(e); } }.bind(this)(); } -function examples2() { - return __asyncGenerator(this, arguments, function* examples2_1() { - const expression = "hello"; - console.log(function () { try { return TryResult.ok(expression); } catch (e) { return TryResult.error(e); } }.bind(this)()); - console.log(yield __await(function () { - return __awaiter(this, void 0, void 0, function* () { try { return TryResult.ok(yield expression); } catch (e) { return TryResult.error(e); } }); - }.bind(this)())); - console.log(yield __await(yield* __asyncDelegator(__asyncValues(function* () { try { return TryResult.ok(yield expression); } catch (e) { return TryResult.error(e); } }.bind(this)())))); - console.log(yield __await(yield* __asyncDelegator(__asyncValues(function () { return __asyncGenerator(this, arguments, function* () { try { return yield __await(TryResult.ok(yield yield __await(yield __await(expression)))); } catch (e) { return yield __await(TryResult.error(e)); } }); }.bind(this)())))); - console.log(yield yield __await(function () { try { return TryResult.ok(expression); } catch (e) { return TryResult.error(e); } }.bind(this)())); - console.log(yield yield __await(yield __await(function () { - return __awaiter(this, void 0, void 0, function* () { try { return TryResult.ok(yield expression); } catch (e) { return TryResult.error(e); } }); - }.bind(this)()))); - console.log(yield yield __await(yield __await(yield* __asyncDelegator(__asyncValues(function () { return __asyncGenerator(this, arguments, function* () { try { return yield __await(TryResult.ok(yield yield __await(yield __await(expression)))); } catch (e) { return yield __await(TryResult.error(e)); } }); }.bind(this)()))))); - const result = function () { try { return TryResult.ok(expression); } catch (e) { return TryResult.error(e); } }.bind(this)(); - yield __await(Promise.resolve("hello")); - }); +async function* examples2() { + const expression = "hello"; + console.log(function() { try { return TryResult.ok(expression); } catch(e) { return TryResult.error(e); } }.bind(this)()); + console.log(await async function() { try { return TryResult.ok(await expression); } catch(e) { return TryResult.error(e); } }.bind(this)()); + console.log(yield* function*() { try { return TryResult.ok(yield expression); } catch(e) { return TryResult.error(e); } }.bind(this)()); + console.log(yield* async function*() { try { return TryResult.ok(yield await expression); } catch(e) { return TryResult.error(e); } }.bind(this)()); + console.log(yield function() { try { return TryResult.ok(expression); } catch(e) { return TryResult.error(e); } }.bind(this)()); + console.log(yield await async function() { try { return TryResult.ok(await expression); } catch(e) { return TryResult.error(e); } }.bind(this)()); + console.log(yield yield* async function*() { try { return TryResult.ok(yield await expression); } catch(e) { return TryResult.error(e); } }.bind(this)()); + const result = function() { try { return TryResult.ok(expression); } catch(e) { return TryResult.error(e); } }.bind(this)(); + await Promise.resolve("hello"); } -(() => __awaiter(void 0, void 0, void 0, function* () { - var _a, e_1, _b, _c; - try { - for (var _d = true, _e = __asyncValues(examples2()), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) { - _c = _f.value; - _d = false; - const t = _c; - console.log(t); - } - } - catch (e_1_1) { e_1 = { error: e_1_1 }; } - finally { - try { - if (!_d && !_a && (_b = _e.return)) yield _b.call(_e); - } - finally { if (e_1) throw e_1.error; } - } -}))(); +(async () => { + for await(const t of examples2()) + console.log(t); +})(); +export {}; diff --git a/example/test.ts b/example/test.ts index 68e1e252b4b28..5766f7bc607e0 100644 --- a/example/test.ts +++ b/example/test.ts @@ -20,6 +20,24 @@ async function examples() { result = try ({ "my": await expr1 }); // convers everything inside the object result = try [await (await fetch("")).json(), expr1, expr2]; // covers everything inside the array // result = try this.test = this.test2 = await fetch(""); // covers everything + + const test = try await createUser((try await (await fetch("")).json() as {hello: true}).value) + + // const exprsync = try expr1; + const exprsync = try expr1; + // const exprasync = try await expr1; + const exprasync = try await expr1; + // const expryield = try yield expr1; + const expryield = try yield expr1; + // const expryieldasync = try yield await expr1; + const expryieldasync = try yield await expr1; + // const exprsyncyield = yield try expr1; + const exprsyncyield = yield try expr1; + // const exprsyncyieldasync = yield try await expr1; + const exprsyncyieldasync = yield try await expr1; + // const expryieldyieldasync = yield try yield await expr1; + const expryieldyieldasync = yield try yield await expr1; + } async function* examples2() {