diff --git a/CHANGELOG.md b/CHANGELOG.md index cf4ab8ba8..0298d8594 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Generated TS wrappers now use `const` where possible for variable declarations: PR [#1292](https://github.com/tact-lang/tact/pull/1292) - Allow serialization specifiers for trait fields PR: [#1303](https://github.com/tact-lang/tact/pull/1303) - Remove unused typechecker wrapper with the file `check.ts` it is contained in: PR [#1313](https://github.com/tact-lang/tact/pull/1313) +- Unified `StatementTry` and `StatementTryCatch` AST nodes: PR [#1418](https://github.com/tact-lang/tact/pull/1418) ### Fixed diff --git a/src/ast/ast.ts b/src/ast/ast.ts index 6df09670b..0b3da42a0 100644 --- a/src/ast/ast.ts +++ b/src/ast/ast.ts @@ -205,7 +205,6 @@ export type AstStatement = | AstStatementUntil | AstStatementRepeat | AstStatementTry - | AstStatementTryCatch | AstStatementForEach | AstStatementDestruct | AstStatementBlock; @@ -301,15 +300,10 @@ export type AstStatementRepeat = { export type AstStatementTry = { kind: "statement_try"; statements: AstStatement[]; - id: number; - loc: SrcInfo; -}; - -export type AstStatementTryCatch = { - kind: "statement_try_catch"; - statements: AstStatement[]; - catchName: AstId; - catchStatements: AstStatement[]; + catchBlock?: { + catchName: AstId; + catchStatements: AstStatement[]; + }; id: number; loc: SrcInfo; }; diff --git a/src/ast/clone.ts b/src/ast/clone.ts index 57991eb46..127e27bea 100644 --- a/src/ast/clone.ts +++ b/src/ast/clone.ts @@ -125,12 +125,13 @@ export function cloneNode( return cloneNode({ ...src, statements: src.statements.map(recurse), - }); - } else if (src.kind === "statement_try_catch") { - return cloneNode({ - ...src, - statements: src.statements.map(recurse), - catchStatements: src.catchStatements.map(recurse), + catchBlock: src.catchBlock + ? { + catchName: src.catchBlock.catchName, + catchStatements: + src.catchBlock.catchStatements.map(recurse), + } + : undefined, }); } else if (src.kind === "statement_foreach") { return cloneNode({ diff --git a/src/ast/compare.ts b/src/ast/compare.ts index b724d8920..dded77751 100644 --- a/src/ast/compare.ts +++ b/src/ast/compare.ts @@ -25,7 +25,6 @@ import { AstStatementWhile, AstStatementForEach, AstStatementTry, - AstStatementTryCatch, AstCondition, AstStatementAugmentedAssign, AstStatementAssign, @@ -507,30 +506,43 @@ export class AstComparator { } case "statement_try": { - const { statements: tryStatements1 } = node1 as AstStatementTry; - const { statements: tryStatements2 } = node2 as AstStatementTry; - return this.compareArray(tryStatements1, tryStatements2); - } - - case "statement_try_catch": { const { statements: tryCatchStatements1, - catchName: catchName1, - catchStatements: catchStatements1, - } = node1 as AstStatementTryCatch; + catchBlock: catchBlock1, + } = node1 as AstStatementTry; const { statements: tryCatchStatements2, - catchName: catchName2, - catchStatements: catchStatements2, - } = node2 as AstStatementTryCatch; - return ( - this.compareArray( - tryCatchStatements1, - tryCatchStatements2, - ) && - this.compare(catchName1, catchName2) && - this.compareArray(catchStatements1, catchStatements2) - ); + catchBlock: catchBlock2, + } = node2 as AstStatementTry; + + if ( + !this.compareArray(tryCatchStatements1, tryCatchStatements2) + ) { + return false; + } + + if (catchBlock1 === undefined && catchBlock2 === undefined) { + return true; + } + + if (catchBlock1 !== undefined && catchBlock2 !== undefined) { + const { + catchName: catchName1, + catchStatements: catchStatements1, + } = catchBlock1; + + const { + catchName: catchName2, + catchStatements: catchStatements2, + } = catchBlock2; + + return ( + this.compare(catchName1, catchName2) && + this.compareArray(catchStatements1, catchStatements2) + ); + } + + return false; } case "statement_foreach": { diff --git a/src/ast/getAstSchema.ts b/src/ast/getAstSchema.ts index 1de3f6842..7ee0245d6 100644 --- a/src/ast/getAstSchema.ts +++ b/src/ast/getAstSchema.ts @@ -342,23 +342,15 @@ export const getAstSchema = ( StatementTry: ( statements: A.AstStatement[], loc: Loc, + catchBlock?: { + catchName: A.AstId; + catchStatements: A.AstStatement[]; + }, ): A.AstStatementTry => createNode({ kind: "statement_try", statements, - loc: toSrcInfo(loc), - }), - StatementTryCatch: ( - statements: A.AstStatement[], - catchName: A.AstId, - catchStatements: A.AstStatement[], - loc: Loc, - ): A.AstStatementTryCatch => - createNode({ - kind: "statement_try_catch", - statements, - catchName, - catchStatements, + catchBlock: catchBlock, loc: toSrcInfo(loc), }), StatementForEach: ( diff --git a/src/ast/hash.ts b/src/ast/hash.ts index 9dbcb9559..c447b771c 100644 --- a/src/ast/hash.ts +++ b/src/ast/hash.ts @@ -124,9 +124,11 @@ export class AstHasher { case "statement_repeat": return `${node.kind}|${this.hash(node.iterations)}|${this.hashStatements(node.statements)}`; case "statement_try": + if (node.catchBlock !== undefined) { + return `${node.kind}|${this.hashStatements(node.statements)}|${this.hash(node.catchBlock.catchName)}|${this.hashStatements(node.catchBlock.catchStatements)}`; + } + return `${node.kind}|${this.hashStatements(node.statements)}`; - case "statement_try_catch": - return `${node.kind}|${this.hashStatements(node.statements)}|${this.hash(node.catchName)}|${this.hashStatements(node.catchStatements)}`; case "statement_foreach": return `${node.kind}|${this.hash(node.map)}|${this.hashStatements(node.statements)}`; case "statement_destruct": diff --git a/src/ast/iterators.ts b/src/ast/iterators.ts index 3f1f5b9fb..ebdf7a59c 100644 --- a/src/ast/iterators.ts +++ b/src/ast/iterators.ts @@ -189,15 +189,12 @@ export function traverse(node: AstNode, callback: (node: AstNode) => void) { node.statements.forEach((e) => { traverse(e, callback); }); - break; - case "statement_try_catch": - node.statements.forEach((e) => { - traverse(e, callback); - }); - traverse(node.catchName, callback); - node.catchStatements.forEach((e) => { - traverse(e, callback); - }); + if (node.catchBlock !== undefined) { + traverse(node.catchBlock.catchName, callback); + node.catchBlock.catchStatements.forEach((e) => { + traverse(e, callback); + }); + } break; case "statement_foreach": traverse(node.keyName, callback); diff --git a/src/ast/rename.ts b/src/ast/rename.ts index 54e9a36f6..5c3e1df94 100644 --- a/src/ast/rename.ts +++ b/src/ast/rename.ts @@ -407,14 +407,14 @@ export class AstRenamer { return { ...stmt, statements: this.renameStatements(stmt.statements), - }; - case "statement_try_catch": - return { - ...stmt, - statements: this.renameStatements(stmt.statements), - catchStatements: this.renameStatements( - stmt.catchStatements, - ), + catchBlock: stmt.catchBlock + ? { + catchName: stmt.catchBlock.catchName, + catchStatements: this.renameStatements( + stmt.catchBlock.catchStatements, + ), + } + : undefined, }; case "statement_foreach": return { diff --git a/src/generator/writers/writeFunction.ts b/src/generator/writers/writeFunction.ts index dc8cf7026..a0cd860a0 100644 --- a/src/generator/writers/writeFunction.ts +++ b/src/generator/writers/writeFunction.ts @@ -227,26 +227,25 @@ export function writeStatement( writeStatement(s, self, returns, ctx); } }); - ctx.append("} catch (_) { }"); - return; - } - case "statement_try_catch": { - ctx.append(`try {`); - ctx.inIndent(() => { - for (const s of f.statements) { - writeStatement(s, self, returns, ctx); + + const catchBlock = f.catchBlock; + if (catchBlock !== undefined) { + if (isWildcard(catchBlock.catchName)) { + ctx.append(`} catch (_) {`); + } else { + ctx.append( + `} catch (_, ${funcIdOf(catchBlock.catchName)}) {`, + ); } - }); - if (isWildcard(f.catchName)) { - ctx.append(`} catch (_) {`); + ctx.inIndent(() => { + for (const s of catchBlock.catchStatements!) { + writeStatement(s, self, returns, ctx); + } + }); } else { - ctx.append(`} catch (_, ${funcIdOf(f.catchName)}) {`); + ctx.append("} catch (_) { "); } - ctx.inIndent(() => { - for (const s of f.catchStatements) { - writeStatement(s, self, returns, ctx); - } - }); + ctx.append(`}`); return; } diff --git a/src/grammar/next/index.ts b/src/grammar/next/index.ts index 3d42cf160..582673914 100644 --- a/src/grammar/next/index.ts +++ b/src/grammar/next/index.ts @@ -567,23 +567,19 @@ const parseStatementUntil = }; const parseStatementTry = - ({ - body, - handler, - loc, - }: $ast.StatementTry): Handler< - A.AstStatementTry | A.AstStatementTryCatch - > => + ({ body, handler, loc }: $ast.StatementTry): Handler => (ctx) => { if (handler) { - return ctx.ast.StatementTryCatch( + return ctx.ast.StatementTry(parseStatements(body)(ctx), loc, { + catchName: parseId(handler.name)(ctx), + catchStatements: parseStatements(handler.body)(ctx), + }); + } else { + return ctx.ast.StatementTry( parseStatements(body)(ctx), - parseId(handler.name)(ctx), - parseStatements(handler.body)(ctx), loc, + undefined, ); - } else { - return ctx.ast.StatementTry(parseStatements(body)(ctx), loc); } }; diff --git a/src/grammar/prev/grammar.ts b/src/grammar/prev/grammar.ts index f3a847112..ad6dec8e6 100644 --- a/src/grammar/prev/grammar.ts +++ b/src/grammar/prev/grammar.ts @@ -987,10 +987,14 @@ semantics.addOperation("astOfStatement", { _rbraceCatch, ) { return createNode({ - kind: "statement_try_catch", + kind: "statement_try", statements: tryBlock.children.map((s) => s.astOfStatement()), - catchName: exitCodeId.astOfExpression(), - catchStatements: catchBlock.children.map((s) => s.astOfStatement()), + catchBlock: { + catchName: exitCodeId.astOfExpression(), + catchStatements: catchBlock.children.map((s) => + s.astOfStatement(), + ), + }, loc: createRef(this), }); }, diff --git a/src/optimizer/interpreter.ts b/src/optimizer/interpreter.ts index ee923781c..8dfb488b6 100644 --- a/src/optimizer/interpreter.ts +++ b/src/optimizer/interpreter.ts @@ -48,7 +48,6 @@ import { AstStatementRepeat, AstStatementReturn, AstStatementTry, - AstStatementTryCatch, AstStatementUntil, AstStatementWhile, AstStaticCall, @@ -1581,9 +1580,6 @@ export class Interpreter { case "statement_try": this.interpretTryStatement(ast); break; - case "statement_try_catch": - this.interpretTryCatchStatement(ast); - break; case "statement_until": this.interpretUntilStatement(ast); break; @@ -1745,13 +1741,6 @@ export class Interpreter { ); } - public interpretTryCatchStatement(ast: AstStatementTryCatch) { - throwNonFatalErrorConstEval( - "try-catch statements currently not supported", - ast.loc, - ); - } - public interpretUntilStatement(ast: AstStatementUntil) { let condition; let iterCount = 0; diff --git a/src/prettyPrinter.ts b/src/prettyPrinter.ts index a8a724a42..ccea74ba0 100644 --- a/src/prettyPrinter.ts +++ b/src/prettyPrinter.ts @@ -790,19 +790,22 @@ export const ppAstStatementForEach: Printer = ]); export const ppAstStatementTry: Printer = - ({ statements }) => - (c) => - c.concat([c.row(`try `), ppStatementBlock(statements)(c)]); + ({ statements, catchBlock }) => + (c) => { + const catchBlocks = + catchBlock !== undefined + ? [ + c.row(` catch (${ppAstId(catchBlock.catchName)}) `), + ppStatementBlock(catchBlock.catchStatements)(c), + ] + : []; -export const ppAstStatementTryCatch: Printer = - ({ statements, catchName, catchStatements }) => - (c) => - c.concat([ + return c.concat([ c.row(`try `), ppStatementBlock(statements)(c), - c.row(` catch (${ppAstId(catchName)}) `), - ppStatementBlock(catchStatements)(c), + ...catchBlocks, ]); + }; export const ppAstStatementDestruct: Printer = ({ type, identifiers, ignoreUnspecifiedFields, expression }) => @@ -845,7 +848,6 @@ export const ppAstStatement: Printer = statement_repeat: ppAstStatementRepeat, statement_foreach: ppAstStatementForEach, statement_try: ppAstStatementTry, - statement_try_catch: ppAstStatementTryCatch, statement_destruct: ppAstStatementDestruct, statement_block: ppAstStatementBlock, }); @@ -913,7 +915,6 @@ export const ppAstNode: Printer = makeVisitor()({ statement_until: ppAstStatementUntil, statement_repeat: ppAstStatementRepeat, statement_try: ppAstStatementTry, - statement_try_catch: ppAstStatementTryCatch, statement_foreach: ppAstStatementForEach, statement_block: ppAstStatementBlock, import: ppAstImport, diff --git a/src/types/resolveStatements.ts b/src/types/resolveStatements.ts index 281517d27..1ca234ee6 100644 --- a/src/types/resolveStatements.ts +++ b/src/types/resolveStatements.ts @@ -572,16 +572,6 @@ function processStatements( } break; case "statement_try": - { - // Process inner statements - const r = processStatements(s.statements, sctx, ctx); - ctx = r.ctx; - sctx = r.sctx; - // try-statement might not return from the current function - // because the control flow can go to the empty catch block - } - break; - case "statement_try_catch": { let initialSctx = sctx; @@ -589,12 +579,21 @@ function processStatements( const r = processStatements(s.statements, sctx, ctx); ctx = r.ctx; - let catchCtx = sctx; + // try-statement might not return from the current function + // because the control flow can go to the empty catch block + if (s.catchBlock === undefined) { + break; + } + let catchCtx = sctx; // Process catchName variable for exit code - checkVariableExists(ctx, initialSctx, s.catchName); + checkVariableExists( + ctx, + initialSctx, + s.catchBlock.catchName, + ); catchCtx = addVariable( - s.catchName, + s.catchBlock.catchName, { kind: "ref", name: "Int", optional: false }, ctx, initialSctx, @@ -602,7 +601,7 @@ function processStatements( // Process catch statements const rCatch = processStatements( - s.catchStatements, + s.catchBlock.catchStatements, catchCtx, ctx, );