From 811ff79ecee4be3c58bec68a2cda61617d3df27c Mon Sep 17 00:00:00 2001 From: i582 <51853996+i582@users.noreply.github.com> Date: Tue, 21 Jan 2025 15:40:09 +0400 Subject: [PATCH] more changes --- src/ast/util.ts | 106 ++++----- src/generator/writers/writeExpression.ts | 71 +++--- src/optimizer/interpreter.ts | 276 ++++++++++------------- src/optimizer/test/partial-eval.spec.ts | 33 ++- src/types/resolveABITypeRef.ts | 81 +++---- src/types/resolveStatements.ts | 65 +++--- 6 files changed, 270 insertions(+), 362 deletions(-) diff --git a/src/ast/util.ts b/src/ast/util.ts index 0a2f7af34..962938d41 100644 --- a/src/ast/util.ts +++ b/src/ast/util.ts @@ -1,44 +1,26 @@ +import * as A from "./ast"; import { Address, Cell, Slice } from "@ton/core"; -import { - AstExpression, - AstUnaryOperation, - AstBinaryOperation, - isLiteral, - AstNumber, - AstBoolean, - AstSimplifiedString, - AstNull, - AstCell, - AstSlice, - AstAddress, - AstLiteral, - AstStructValue, - AstStructFieldValue, - AstId, - AstCommentValue, - FactoryAst, -} from "./ast"; import { dummySrcInfo, SrcInfo } from "../grammar"; -export const getAstUtil = ({ createNode }: FactoryAst) => { +export const getAstUtil = ({ createNode }: A.FactoryAst) => { function makeUnaryExpression( - op: AstUnaryOperation, - operand: AstExpression, - ): AstExpression { + op: A.AstUnaryOperation, + operand: A.AstExpression, + ): A.AstExpression { const result = createNode({ kind: "op_unary", op: op, operand: operand, loc: dummySrcInfo, }); - return result as AstExpression; + return result as A.AstExpression; } function makeBinaryExpression( - op: AstBinaryOperation, - left: AstExpression, - right: AstExpression, - ): AstExpression { + op: A.AstBinaryOperation, + left: A.AstExpression, + right: A.AstExpression, + ): A.AstExpression { const result = createNode({ kind: "op_binary", op: op, @@ -46,114 +28,114 @@ export const getAstUtil = ({ createNode }: FactoryAst) => { right: right, loc: dummySrcInfo, }); - return result as AstExpression; + return result as A.AstExpression; } - function makeNumberLiteral(n: bigint, loc: SrcInfo): AstNumber { + function makeNumberLiteral(n: bigint, loc: SrcInfo): A.AstNumber { const result = createNode({ kind: "number", base: 10, value: n, loc: loc, }); - return result as AstNumber; + return result as A.AstNumber; } - function makeBooleanLiteral(b: boolean, loc: SrcInfo): AstBoolean { + function makeBooleanLiteral(b: boolean, loc: SrcInfo): A.AstBoolean { const result = createNode({ kind: "boolean", value: b, loc: loc, }); - return result as AstBoolean; + return result as A.AstBoolean; } function makeSimplifiedStringLiteral( s: string, loc: SrcInfo, - ): AstSimplifiedString { + ): A.AstSimplifiedString { const result = createNode({ kind: "simplified_string", value: s, loc: loc, }); - return result as AstSimplifiedString; + return result as A.AstSimplifiedString; } - function makeCommentLiteral(s: string, loc: SrcInfo): AstCommentValue { + function makeCommentLiteral(s: string, loc: SrcInfo): A.AstCommentValue { const result = createNode({ kind: "comment_value", value: s, loc: loc, }); - return result as AstCommentValue; + return result as A.AstCommentValue; } - function makeNullLiteral(loc: SrcInfo): AstNull { + function makeNullLiteral(loc: SrcInfo): A.AstNull { const result = createNode({ kind: "null", loc: loc, }); - return result as AstNull; + return result as A.AstNull; } - function makeCellLiteral(c: Cell, loc: SrcInfo): AstCell { + function makeCellLiteral(c: Cell, loc: SrcInfo): A.AstCell { const result = createNode({ kind: "cell", value: c, loc: loc, }); - return result as AstCell; + return result as A.AstCell; } - function makeSliceLiteral(s: Slice, loc: SrcInfo): AstSlice { + function makeSliceLiteral(s: Slice, loc: SrcInfo): A.AstSlice { const result = createNode({ kind: "slice", value: s, loc: loc, }); - return result as AstSlice; + return result as A.AstSlice; } - function makeAddressLiteral(a: Address, loc: SrcInfo): AstAddress { + function makeAddressLiteral(a: Address, loc: SrcInfo): A.AstAddress { const result = createNode({ kind: "address", value: a, loc: loc, }); - return result as AstAddress; + return result as A.AstAddress; } function makeStructFieldValue( fieldName: string, - val: AstLiteral, + val: A.AstLiteral, loc: SrcInfo, - ): AstStructFieldValue { + ): A.AstStructFieldValue { const result = createNode({ kind: "struct_field_value", field: createNode({ kind: "id", text: fieldName, loc: loc, - }) as AstId, + }) as A.AstId, initializer: val, loc: loc, }); - return result as AstStructFieldValue; + return result as A.AstStructFieldValue; } function makeStructValue( - fields: AstStructFieldValue[], - type: AstId, + fields: A.AstStructFieldValue[], + type: A.AstId, loc: SrcInfo, - ): AstStructValue { + ): A.AstStructValue { const result = createNode({ kind: "struct_value", args: fields, loc: loc, type: type, }); - return result as AstStructValue; + return result as A.AstStructValue; } return { @@ -175,38 +157,38 @@ export const getAstUtil = ({ createNode }: FactoryAst) => { export type AstUtil = ReturnType; // Checks if the top level node is an unary op node -export function checkIsUnaryOpNode(ast: AstExpression): boolean { +export function checkIsUnaryOpNode(ast: A.AstExpression): boolean { return ast.kind === "op_unary"; } // Checks if the top level node is a binary op node -export function checkIsBinaryOpNode(ast: AstExpression): boolean { +export function checkIsBinaryOpNode(ast: A.AstExpression): boolean { return ast.kind === "op_binary"; } // Checks if top level node is a binary op node // with a value node on the right -export function checkIsBinaryOp_With_RightValue(ast: AstExpression): boolean { - return ast.kind === "op_binary" ? isLiteral(ast.right) : false; +export function checkIsBinaryOp_With_RightValue(ast: A.AstExpression): boolean { + return ast.kind === "op_binary" ? A.isLiteral(ast.right) : false; } // Checks if top level node is a binary op node // with a value node on the left -export function checkIsBinaryOp_With_LeftValue(ast: AstExpression): boolean { - return ast.kind === "op_binary" ? isLiteral(ast.left) : false; +export function checkIsBinaryOp_With_LeftValue(ast: A.AstExpression): boolean { + return ast.kind === "op_binary" ? A.isLiteral(ast.left) : false; } // Checks if the top level node is the specified number -export function checkIsNumber(ast: AstExpression, n: bigint): boolean { +export function checkIsNumber(ast: A.AstExpression, n: bigint): boolean { return ast.kind === "number" ? ast.value == n : false; } -export function checkIsName(ast: AstExpression): boolean { +export function checkIsName(ast: A.AstExpression): boolean { return ast.kind === "id"; } // Checks if the top level node is the specified boolean -export function checkIsBoolean(ast: AstExpression, b: boolean): boolean { +export function checkIsBoolean(ast: A.AstExpression, b: boolean): boolean { return ast.kind === "boolean" ? ast.value == b : false; } diff --git a/src/generator/writers/writeExpression.ts b/src/generator/writers/writeExpression.ts index fea2743c0..9665b89bc 100644 --- a/src/generator/writers/writeExpression.ts +++ b/src/generator/writers/writeExpression.ts @@ -1,12 +1,4 @@ -import { - AstExpression, - AstId, - AstLiteral, - eqNames, - getAstFactory, - idText, - tryExtractPath, -} from "../../ast/ast"; +import * as A from "../../ast/ast"; import { idTextErr, TactConstEvalError, @@ -45,7 +37,7 @@ import { isLvalue } from "../../types/resolveStatements"; import { evalConstantExpression } from "../../optimizer/constEval"; import { getAstUtil } from "../../ast/util"; -function isNull(wCtx: WriterContext, expr: AstExpression): boolean { +function isNull(wCtx: WriterContext, expr: A.AstExpression): boolean { return getExpType(wCtx.ctx, expr).kind === "null"; } @@ -99,7 +91,7 @@ function writeStructConstructor( return name; } -export function writeValue(val: AstLiteral, wCtx: WriterContext): string { +export function writeValue(val: A.AstLiteral, wCtx: WriterContext): string { switch (val.kind) { case "number": return val.value.toString(10); @@ -134,9 +126,9 @@ export function writeValue(val: AstLiteral, wCtx: WriterContext): string { } case "struct_value": { // Transform the struct fields into a map for lookup - const valMap: Map = new Map(); + const valMap: Map = new Map(); for (const f of val.args) { - valMap.set(idText(f.field), f.initializer); + valMap.set(A.idText(f.field), f.initializer); } const structDescription = getType(wCtx.ctx, val.type); @@ -167,11 +159,16 @@ export function writeValue(val: AstLiteral, wCtx: WriterContext): string { } } -export function writePathExpression(path: AstId[]): string { - return [funcIdOf(idText(path[0]!)), ...path.slice(1).map(idText)].join(`'`); +export function writePathExpression(path: A.AstId[]): string { + return [funcIdOf(A.idText(path[0]!)), ...path.slice(1).map(A.idText)].join( + `'`, + ); } -export function writeExpression(f: AstExpression, wCtx: WriterContext): string { +export function writeExpression( + f: A.AstExpression, + wCtx: WriterContext, +): string { // literals and constant expressions are covered here // FIXME: Once optimization step is added, remove this try and replace it with this @@ -180,7 +177,7 @@ export function writeExpression(f: AstExpression, wCtx: WriterContext): string { // return writeValue(f, wCtx); // } try { - const util = getAstUtil(getAstFactory()); + const util = getAstUtil(A.getAstFactory()); // Let us put a limit of 2 ^ 12 = 4096 iterations on loops to increase compiler responsiveness. // If a loop takes more than such number of iterations, the interpreter will fail evaluation. // I think maxLoopIterations should be a command line option in case a user wants to wait more @@ -466,8 +463,8 @@ export function writeExpression(f: AstExpression, wCtx: WriterContext): string { fields = fields.slice(0, srcT.partialFieldCount); } - const field = fields.find((v) => eqNames(v.name, f.field)); - const cst = srcT.constants.find((v) => eqNames(v.name, f.field)); + const field = fields.find((v) => A.eqNames(v.name, f.field)); + const cst = srcT.constants.find((v) => A.eqNames(v.name, f.field)); if (!field && !cst) { throwCompilationError( `Cannot find field ${idTextErr(f.field)} in struct ${idTextErr(srcT.name)}`, @@ -477,7 +474,7 @@ export function writeExpression(f: AstExpression, wCtx: WriterContext): string { if (field) { // Trying to resolve field as a path - const path = tryExtractPath(f); + const path = A.tryExtractPath(f); if (path) { // Prepare path const idd = writePathExpression(path); @@ -506,8 +503,8 @@ export function writeExpression(f: AstExpression, wCtx: WriterContext): string { if (f.kind === "static_call") { // Check global functions - if (GlobalFunctions.has(idText(f.function))) { - return GlobalFunctions.get(idText(f.function))!.generate( + if (GlobalFunctions.has(A.idText(f.function))) { + return GlobalFunctions.get(A.idText(f.function))!.generate( wCtx, f.args.map((v) => getExpType(wCtx.ctx, v)), f.args, @@ -515,10 +512,10 @@ export function writeExpression(f: AstExpression, wCtx: WriterContext): string { ); } - const sf = getStaticFunction(wCtx.ctx, idText(f.function)); - let n = ops.global(idText(f.function)); + const sf = getStaticFunction(wCtx.ctx, A.idText(f.function)); + let n = ops.global(A.idText(f.function)); if (sf.ast.kind === "native_function_decl") { - n = idText(sf.ast.nativeName); + n = A.idText(sf.ast.nativeName); if (n.startsWith("__tact")) { wCtx.used(n); } @@ -547,7 +544,7 @@ export function writeExpression(f: AstExpression, wCtx: WriterContext): string { // Write a constructor const id = writeStructConstructor( src, - f.args.map((v) => idText(v.field)), + f.args.map((v) => A.idText(v.field)), wCtx, ); wCtx.used(id); @@ -557,7 +554,7 @@ export function writeExpression(f: AstExpression, wCtx: WriterContext): string { (v) => writeCastedExpression( v.initializer, - src.fields.find((v2) => eqNames(v2.name, v.field))!.type, + src.fields.find((v2) => A.eqNames(v2.name, v.field))!.type, wCtx, ), wCtx, @@ -580,8 +577,8 @@ export function writeExpression(f: AstExpression, wCtx: WriterContext): string { // Check struct ABI if (selfTy.kind === "struct") { - if (StructFunctions.has(idText(f.method))) { - const abi = StructFunctions.get(idText(f.method))!; + if (StructFunctions.has(A.idText(f.method))) { + const abi = StructFunctions.get(A.idText(f.method))!; return abi.generate( wCtx, [ @@ -595,8 +592,8 @@ export function writeExpression(f: AstExpression, wCtx: WriterContext): string { } // Resolve function - const methodDescr = selfTy.functions.get(idText(f.method))!; - let name = ops.extension(selfTyRef.name, idText(f.method)); + const methodDescr = selfTy.functions.get(A.idText(f.method))!; + let name = ops.extension(selfTyRef.name, A.idText(f.method)); if ( methodDescr.ast.kind === "function_def" || methodDescr.ast.kind === "function_decl" || @@ -604,7 +601,7 @@ export function writeExpression(f: AstExpression, wCtx: WriterContext): string { ) { wCtx.used(name); } else { - name = idText(methodDescr.ast.nativeName); + name = A.idText(methodDescr.ast.nativeName); if (name.startsWith("__tact")) { wCtx.used(name); } @@ -638,7 +635,7 @@ export function writeExpression(f: AstExpression, wCtx: WriterContext): string { const s = writeCastedExpression(f.self, methodDescr.self!, wCtx); if (methodDescr.isMutating) { // check if it's an l-value - const path = tryExtractPath(f.self); + const path = A.tryExtractPath(f.self); if (path !== null && isLvalue(path, wCtx.ctx)) { return `${s}~${name}(${renderedArguments.join(", ")})`; } else { @@ -651,13 +648,13 @@ export function writeExpression(f: AstExpression, wCtx: WriterContext): string { // Map types if (selfTyRef.kind === "map") { - if (!MapFunctions.has(idText(f.method))) { + if (!MapFunctions.has(A.idText(f.method))) { throwCompilationError( - `Map function "${idText(f.method)}" not found`, + `Map function "${A.idText(f.method)}" not found`, f.loc, ); } - const abf = MapFunctions.get(idText(f.method))!; + const abf = MapFunctions.get(A.idText(f.method))!; return abf.generate( wCtx, [selfTyRef, ...f.args.map((v) => getExpType(wCtx.ctx, v))], @@ -685,7 +682,7 @@ export function writeExpression(f: AstExpression, wCtx: WriterContext): string { const initArgs = f.args.map((a, i) => writeCastedExpression(a, type.init!.params[i]!.type, wCtx), ); - return `${ops.contractInitChild(idText(f.contract), wCtx)}(${initArgs.join(", ")})`; + return `${ops.contractInitChild(A.idText(f.contract), wCtx)}(${initArgs.join(", ")})`; } // diff --git a/src/optimizer/interpreter.ts b/src/optimizer/interpreter.ts index 12dcbf42d..c0ab3bb1d 100644 --- a/src/optimizer/interpreter.ts +++ b/src/optimizer/interpreter.ts @@ -1,6 +1,7 @@ import { Address, beginCell, BitString, Cell, toNano } from "@ton/core"; import { paddedBufferToBits } from "@ton/core/dist/boc/utils/paddedBits"; import * as crc32 from "crc-32"; +import * as A from "../ast/ast"; import { evalConstantExpression } from "./constEval"; import { CompilerContext } from "../context/context"; import { @@ -10,61 +11,6 @@ import { throwConstEvalError, throwInternalCompilerError, } from "../error/errors"; -import { - AstAddress, - AstBinaryOperation, - AstBoolean, - AstCell, - AstCommentValue, - AstCondition, - AstConditional, - AstConstantDef, - AstContract, - AstExpression, - AstFieldAccess, - AstFunctionDef, - AstId, - AstInitOf, - AstLiteral, - AstMessageDecl, - AstMethodCall, - AstModuleItem, - AstNativeFunctionDecl, - AstNull, - AstNumber, - AstOpBinary, - AstOpUnary, - AstPrimitiveTypeDecl, - AstSimplifiedString, - AstSlice, - FactoryAst, - AstStatement, - AstStatementAssign, - AstStatementAugmentedAssign, - AstStatementDestruct, - AstStatementExpression, - AstStatementForEach, - AstStatementLet, - AstStatementRepeat, - AstStatementReturn, - AstStatementTry, - AstStatementUntil, - AstStatementWhile, - AstStaticCall, - AstString, - AstStructDecl, - AstStructFieldValue, - AstStructInstance, - AstStructValue, - AstTrait, - AstUnaryOperation, - eqExpressions, - eqNames, - getAstFactory, - idText, - isSelfId, - AstStatementBlock, -} from "../ast/ast"; import { AstUtil, divFloor, getAstUtil, modFloor } from "../ast/util"; import { getStaticConstant, @@ -115,10 +61,10 @@ function throwErrorConstEval(msg: string, source: SrcInfo): never { ); } type EvalResult = - | { kind: "ok"; value: AstLiteral } + | { kind: "ok"; value: A.AstLiteral } | { kind: "error"; message: string }; -export function ensureInt(val: AstExpression): AstNumber { +export function ensureInt(val: A.AstExpression): A.AstNumber { if (val.kind !== "number") { throwErrorConstEval( `integer expected, but got expression of kind '${val.kind}'`, @@ -135,7 +81,7 @@ export function ensureInt(val: AstExpression): AstNumber { } } -function ensureArgumentForEquality(val: AstLiteral): AstLiteral { +function ensureArgumentForEquality(val: A.AstLiteral): A.AstLiteral { switch (val.kind) { case "address": case "boolean": @@ -157,7 +103,7 @@ function ensureArgumentForEquality(val: AstLiteral): AstLiteral { } } -function ensureRepeatInt(val: AstExpression): AstNumber { +function ensureRepeatInt(val: A.AstExpression): A.AstNumber { if (val.kind !== "number") { throwErrorConstEval( `integer expected, but got expression of kind '${val.kind}'`, @@ -174,7 +120,7 @@ function ensureRepeatInt(val: AstExpression): AstNumber { } } -export function ensureBoolean(val: AstExpression): AstBoolean { +export function ensureBoolean(val: A.AstExpression): A.AstBoolean { if (val.kind !== "boolean") { throwErrorConstEval( `boolean expected, but got expression of kind '${val.kind}'`, @@ -184,7 +130,7 @@ export function ensureBoolean(val: AstExpression): AstBoolean { return val; } -export function ensureString(val: AstExpression): AstString { +export function ensureString(val: A.AstExpression): A.AstString { if (val.kind !== "string") { throwErrorConstEval( `string expected, but got expression of kind '${val.kind}'`, @@ -195,8 +141,8 @@ export function ensureString(val: AstExpression): AstString { } export function ensureSimplifiedString( - val: AstExpression, -): AstSimplifiedString { + val: A.AstExpression, +): A.AstSimplifiedString { if (val.kind !== "simplified_string") { throwErrorConstEval( `simplified string expected, but got expression of kind '${val.kind}'`, @@ -206,7 +152,11 @@ export function ensureSimplifiedString( return val; } -function ensureFunArity(arity: number, args: AstExpression[], source: SrcInfo) { +function ensureFunArity( + arity: number, + args: A.AstExpression[], + source: SrcInfo, +) { if (args.length !== arity) { throwErrorConstEval( `function expects ${arity} argument(s), but got ${args.length}`, @@ -217,7 +167,7 @@ function ensureFunArity(arity: number, args: AstExpression[], source: SrcInfo) { function ensureMethodArity( arity: number, - args: AstExpression[], + args: A.AstExpression[], source: SrcInfo, ) { if (args.length !== arity) { @@ -229,11 +179,11 @@ function ensureMethodArity( } export function evalUnaryOp( - op: AstUnaryOperation, - valOperand: AstLiteral, + op: A.AstUnaryOperation, + valOperand: A.AstLiteral, source: SrcInfo, util: AstUtil, -): AstLiteral { +): A.AstLiteral { switch (op) { case "+": return ensureInt(valOperand); @@ -266,12 +216,12 @@ export function evalUnaryOp( } export function evalBinaryOp( - op: AstBinaryOperation, - valLeft: AstLiteral, - valRightContinuation: () => AstLiteral, // It needs to be a continuation, because some binary operators short-circuit + op: A.AstBinaryOperation, + valLeft: A.AstLiteral, + valRightContinuation: () => A.AstLiteral, // It needs to be a continuation, because some binary operators short-circuit source: SrcInfo, util: AstUtil, -): AstLiteral { +): A.AstLiteral { switch (op) { case "+": { const astLeft = ensureInt(valLeft); @@ -425,7 +375,7 @@ export function evalBinaryOp( const valR_ = ensureArgumentForEquality(valR); // Changed to equality testing (instead of ===) because cells, slices, address are equal by hashing - const result = eqExpressions(valLeft_, valR_); + const result = A.eqExpressions(valLeft_, valR_); return util.makeBooleanLiteral(result, source); } case "!=": { @@ -447,7 +397,7 @@ export function evalBinaryOp( const valR_ = ensureArgumentForEquality(valR); // Changed to equality testing (instead of ===) because cells, slices are equal by hashing - const result = !eqExpressions(valLeft_, valR_); + const result = !A.eqExpressions(valLeft_, valR_); return util.makeBooleanLiteral(result, source); } case "&&": { @@ -518,14 +468,14 @@ export function interpretEscapeSequences( } class ReturnSignal extends Error { - private value?: AstLiteral; + private value?: A.AstLiteral; - constructor(value?: AstLiteral) { + constructor(value?: A.AstLiteral) { super(); this.value = value; } - public getValue(): AstLiteral | undefined { + public getValue(): A.AstLiteral | undefined { return this.value; } } @@ -540,7 +490,7 @@ export type InterpreterConfig = { const WILDCARD_NAME: string = "_"; -type Environment = { values: Map; parent?: Environment }; +type Environment = { values: Map; parent?: Environment }; class EnvironmentStack { private currentEnv: Environment; @@ -549,7 +499,9 @@ class EnvironmentStack { this.currentEnv = { values: new Map() }; } - private findBindingMap(name: string): Map | undefined { + private findBindingMap( + name: string, + ): Map | undefined { let env: Environment | undefined = this.currentEnv; while (env !== undefined) { if (env.values.has(name)) { @@ -607,7 +559,7 @@ class EnvironmentStack { so that the return at line 5 (now in the environment a = 3) will produce 3 * 2 = 6, and so on. */ - public setNewBinding(name: string, val: AstLiteral) { + public setNewBinding(name: string, val: A.AstLiteral) { if (name !== WILDCARD_NAME) { this.currentEnv.values.set(name, val); } @@ -620,7 +572,7 @@ class EnvironmentStack { to "val". If it does not find "name", the stack is unchanged. As a special case, name "_" is always ignored. */ - public updateBinding(name: string, val: AstLiteral) { + public updateBinding(name: string, val: A.AstLiteral) { if (name !== WILDCARD_NAME) { const bindings = this.findBindingMap(name); if (bindings !== undefined) { @@ -636,7 +588,7 @@ class EnvironmentStack { If it does not find "name", it returns undefined. As a special case, name "_" always returns undefined. */ - public getBinding(name: string): AstLiteral | undefined { + public getBinding(name: string): A.AstLiteral | undefined { if (name === WILDCARD_NAME) { return undefined; } @@ -664,7 +616,7 @@ class EnvironmentStack { */ public executeInNewEnvironment( code: () => T, - initialBindings: { names: string[]; values: AstLiteral[] } = { + initialBindings: { names: string[]; values: A.AstLiteral[] } = { names: [], values: [], }, @@ -689,7 +641,7 @@ class EnvironmentStack { export function parseAndEvalExpression( sourceCode: string, - ast: FactoryAst = getAstFactory(), + ast: A.FactoryAst = A.getAstFactory(), parser: Parser = getParser(ast, defaultParser), util: AstUtil = getAstUtil(ast), ): EvalResult { @@ -772,7 +724,7 @@ export class Interpreter { this.util = util; } - public interpretModuleItem(ast: AstModuleItem): void { + public interpretModuleItem(ast: A.AstModuleItem): void { switch (ast.kind) { case "constant_def": this.interpretConstantDef(ast); @@ -807,63 +759,63 @@ export class Interpreter { } } - public interpretConstantDef(ast: AstConstantDef) { + public interpretConstantDef(ast: A.AstConstantDef) { throwNonFatalErrorConstEval( "Constant definitions are currently not supported.", ast.loc, ); } - public interpretFunctionDef(ast: AstFunctionDef) { + public interpretFunctionDef(ast: A.AstFunctionDef) { throwNonFatalErrorConstEval( "Function definitions are currently not supported.", ast.loc, ); } - public interpretStructDecl(ast: AstStructDecl) { + public interpretStructDecl(ast: A.AstStructDecl) { throwNonFatalErrorConstEval( "Struct declarations are currently not supported.", ast.loc, ); } - public interpretMessageDecl(ast: AstMessageDecl) { + public interpretMessageDecl(ast: A.AstMessageDecl) { throwNonFatalErrorConstEval( "Message declarations are currently not supported.", ast.loc, ); } - public interpretPrimitiveTypeDecl(ast: AstPrimitiveTypeDecl) { + public interpretPrimitiveTypeDecl(ast: A.AstPrimitiveTypeDecl) { throwNonFatalErrorConstEval( "Primitive type declarations are currently not supported.", ast.loc, ); } - public interpretFunctionDecl(ast: AstNativeFunctionDecl) { + public interpretFunctionDecl(ast: A.AstNativeFunctionDecl) { throwNonFatalErrorConstEval( "Native function declarations are currently not supported.", ast.loc, ); } - public interpretContract(ast: AstContract) { + public interpretContract(ast: A.AstContract) { throwNonFatalErrorConstEval( "Contract declarations are currently not supported.", ast.loc, ); } - public interpretTrait(ast: AstTrait) { + public interpretTrait(ast: A.AstTrait) { throwNonFatalErrorConstEval( "Trait declarations are currently not supported.", ast.loc, ); } - public interpretExpression(ast: AstExpression): AstLiteral { + public interpretExpression(ast: A.AstExpression): A.AstLiteral { switch (ast.kind) { case "id": return this.interpretName(ast); @@ -908,9 +860,9 @@ export class Interpreter { } } - public interpretName(ast: AstId): AstLiteral { - if (hasStaticConstant(this.context, idText(ast))) { - const constant = getStaticConstant(this.context, idText(ast)); + public interpretName(ast: A.AstId): A.AstLiteral { + if (hasStaticConstant(this.context, A.idText(ast))) { + const constant = getStaticConstant(this.context, A.idText(ast)); if (constant.value !== undefined) { return constant.value; } else { @@ -920,15 +872,15 @@ export class Interpreter { ); } } - const variableBinding = this.envStack.getBinding(idText(ast)); + const variableBinding = this.envStack.getBinding(A.idText(ast)); if (variableBinding !== undefined) { return variableBinding; } throwNonFatalErrorConstEval("cannot evaluate a variable", ast.loc); } - public interpretMethodCall(ast: AstMethodCall): AstLiteral { - switch (idText(ast.method)) { + public interpretMethodCall(ast: A.AstMethodCall): A.AstLiteral { + switch (A.idText(ast.method)) { case "asComment": { ensureMethodArity(0, ast.args, ast.loc); const comment = ensureSimplifiedString( @@ -944,55 +896,55 @@ export class Interpreter { } } - public interpretInitOf(ast: AstInitOf): AstLiteral { + public interpretInitOf(ast: A.AstInitOf): A.AstLiteral { throwNonFatalErrorConstEval( "initOf is not supported at this moment", ast.loc, ); } - public interpretNull(ast: AstNull): AstNull { + public interpretNull(ast: A.AstNull): A.AstNull { return ast; } - public interpretBoolean(ast: AstBoolean): AstBoolean { + public interpretBoolean(ast: A.AstBoolean): A.AstBoolean { return ast; } - public interpretNumber(ast: AstNumber): AstNumber { + public interpretNumber(ast: A.AstNumber): A.AstNumber { return ensureInt(ast); } - public interpretString(ast: AstString): AstSimplifiedString { + public interpretString(ast: A.AstString): A.AstSimplifiedString { return this.util.makeSimplifiedStringLiteral( interpretEscapeSequences(ast.value, ast.loc), ast.loc, ); } - public interpretCommentValue(ast: AstCommentValue): AstCommentValue { + public interpretCommentValue(ast: A.AstCommentValue): A.AstCommentValue { return ast; } public interpretSimplifiedString( - ast: AstSimplifiedString, - ): AstSimplifiedString { + ast: A.AstSimplifiedString, + ): A.AstSimplifiedString { return ast; } - public interpretAddress(ast: AstAddress): AstAddress { + public interpretAddress(ast: A.AstAddress): A.AstAddress { return ast; } - public interpretCell(ast: AstCell): AstCell { + public interpretCell(ast: A.AstCell): A.AstCell { return ast; } - public interpretSlice(ast: AstSlice): AstSlice { + public interpretSlice(ast: A.AstSlice): A.AstSlice { return ast; } - public interpretUnaryOp(ast: AstOpUnary): AstLiteral { + public interpretUnaryOp(ast: A.AstOpUnary): A.AstLiteral { // Tact grammar does not have negative integer literals, // so in order to avoid errors for `-115792089237316195423570985008687907853269984665640564039457584007913129639936` // which is `-(2**256)` we need to have a special case for it @@ -1009,7 +961,7 @@ export class Interpreter { return evalUnaryOp(ast.op, valOperand, ast.loc, this.util); } - public interpretBinaryOp(ast: AstOpBinary): AstLiteral { + public interpretBinaryOp(ast: A.AstOpBinary): A.AstLiteral { const valLeft = this.interpretExpression(ast.left); const valRightContinuation = () => this.interpretExpression(ast.right); @@ -1022,7 +974,7 @@ export class Interpreter { ); } - public interpretConditional(ast: AstConditional): AstLiteral { + public interpretConditional(ast: A.AstConditional): A.AstLiteral { // here we rely on the typechecker that both branches have the same type const valCond = ensureBoolean(this.interpretExpression(ast.condition)); if (valCond.value) { @@ -1032,13 +984,13 @@ export class Interpreter { } } - public interpretStructInstance(ast: AstStructInstance): AstStructValue { + public interpretStructInstance(ast: A.AstStructInstance): A.AstStructValue { const structTy = getType(this.context, ast.type); // initialize the resulting struct value with // the default values for fields with initializers // or null for uninitialized optional fields - const resultMap: Map = new Map(); + const resultMap: Map = new Map(); for (const field of structTy.fields) { if (typeof field.default !== "undefined") { @@ -1056,17 +1008,17 @@ export class Interpreter { // this will override default fields set above for (const fieldWithInit of ast.args) { const v = this.interpretExpression(fieldWithInit.initializer); - resultMap.set(idText(fieldWithInit.field), v); + resultMap.set(A.idText(fieldWithInit.field), v); } // Create the field entries for the StructValue // The previous loop ensures that the map resultMap cannot return // undefined for each of the fields in ast.args - const structValueFields: AstStructFieldValue[] = []; + const structValueFields: A.AstStructFieldValue[] = []; for (const [fieldName, fieldValue] of resultMap) { // Find the source code declaration, if existent const sourceField = ast.args.find( - (f) => idText(f.field) === fieldName, + (f) => A.idText(f.field) === fieldName, ); if (typeof sourceField !== "undefined") { structValueFields.push( @@ -1091,19 +1043,19 @@ export class Interpreter { return this.util.makeStructValue(structValueFields, ast.type, ast.loc); } - public interpretStructValue(ast: AstStructValue): AstStructValue { + public interpretStructValue(ast: A.AstStructValue): A.AstStructValue { // Struct values are already simplified to their simplest form return ast; } - public interpretFieldAccess(ast: AstFieldAccess): AstLiteral { + public interpretFieldAccess(ast: A.AstFieldAccess): A.AstLiteral { // special case for contract/trait constant accesses via `self.constant` // interpret "self" as a contract/trait access only if "self" // is not already assigned in the environment (this would mean // we are executing inside an extends function) if ( ast.aggregate.kind === "id" && - isSelfId(ast.aggregate) && + A.isSelfId(ast.aggregate) && !this.envStack.selfInEnvironment() ) { const selfTypeRef = getExpType(this.context, ast.aggregate); @@ -1114,7 +1066,7 @@ export class Interpreter { ); const foundContractConst = contractTypeDescription.constants.find((constId) => - eqNames(ast.field, constId.name), + A.eqNames(ast.field, constId.name), ); if (foundContractConst === undefined) { // not a constant, e.g. `self.storageVariable` @@ -1141,7 +1093,7 @@ export class Interpreter { ); } const field = valStruct.args.find( - (f) => idText(ast.field) === idText(f.field), + (f) => A.idText(ast.field) === A.idText(f.field), ); if (typeof field !== "undefined") { return field.initializer; @@ -1154,8 +1106,8 @@ export class Interpreter { } } - public interpretStaticCall(ast: AstStaticCall): AstLiteral { - switch (idText(ast.function)) { + public interpretStaticCall(ast: A.AstStaticCall): A.AstLiteral { + switch (A.idText(ast.function)) { case "ton": { ensureFunArity(1, ast.args, ast.loc); const tons = ensureSimplifiedString( @@ -1435,10 +1387,10 @@ export class Interpreter { ); } default: - if (hasStaticFunction(this.context, idText(ast.function))) { + if (hasStaticFunction(this.context, A.idText(ast.function))) { const functionDescription = getStaticFunction( this.context, - idText(ast.function), + A.idText(ast.function), ); switch (functionDescription.ast.kind) { case "function_def": @@ -1484,15 +1436,15 @@ export class Interpreter { } private evalStaticFunction( - functionCode: AstFunctionDef, - args: AstExpression[], + functionCode: A.AstFunctionDef, + args: A.AstExpression[], returns: TypeRef, - ): AstLiteral { + ): A.AstLiteral { // Evaluate the arguments in the current environment const argValues = args.map(this.interpretExpression, this); // Extract the parameter names const paramNames = functionCode.params.map((param) => - idText(param.name), + A.idText(param.name), ); // Check parameter names do not shadow constants if ( @@ -1501,7 +1453,7 @@ export class Interpreter { ) ) { throwInternalCompilerError( - `some parameter of function ${idText(functionCode.name)} shadows a constant with the same name`, + `some parameter of function ${A.idText(functionCode.name)} shadows a constant with the same name`, functionCode.loc, ); } @@ -1534,7 +1486,7 @@ export class Interpreter { // function is not void if (returns.kind !== "void") { throwInternalCompilerError( - `function ${idText(functionCode.name)} must return a value`, + `function ${A.idText(functionCode.name)} must return a value`, functionCode.loc, ); } else { @@ -1548,7 +1500,7 @@ export class Interpreter { ); } - public interpretStatement(ast: AstStatement): void { + public interpretStatement(ast: A.AstStatement): void { switch (ast.kind) { case "statement_let": this.interpretLetStatement(ast); @@ -1592,24 +1544,24 @@ export class Interpreter { } } - public interpretLetStatement(ast: AstStatementLet) { - if (hasStaticConstant(this.context, idText(ast.name))) { + public interpretLetStatement(ast: A.AstStatementLet) { + if (hasStaticConstant(this.context, A.idText(ast.name))) { // Attempt of shadowing a constant in a let declaration throwInternalCompilerError( - `declaration of ${idText(ast.name)} shadows a constant with the same name`, + `declaration of ${A.idText(ast.name)} shadows a constant with the same name`, ast.loc, ); } const val = this.interpretExpression(ast.expression); - this.envStack.setNewBinding(idText(ast.name), val); + this.envStack.setNewBinding(A.idText(ast.name), val); } - public interpretDestructStatement(ast: AstStatementDestruct) { + public interpretDestructStatement(ast: A.AstStatementDestruct) { for (const [_, name] of ast.identifiers.values()) { - if (hasStaticConstant(this.context, idText(name))) { + if (hasStaticConstant(this.context, A.idText(name))) { // Attempt of shadowing a constant in a destructuring declaration throwInternalCompilerError( - `declaration of ${idText(name)} shadows a constant with the same name`, + `declaration of ${A.idText(name)} shadows a constant with the same name`, ast.loc, ); } @@ -1625,14 +1577,14 @@ export class Interpreter { } // Keep a map of the fields in val for lookup - const valAsMap: Map = new Map(); - val.args.forEach((f) => valAsMap.set(idText(f.field), f.initializer)); + const valAsMap: Map = new Map(); + val.args.forEach((f) => valAsMap.set(A.idText(f.field), f.initializer)); for (const [field, name] of ast.identifiers.values()) { if (name.text === "_") { continue; } - const v = valAsMap.get(idText(field)); + const v = valAsMap.get(A.idText(field)); if (typeof v === "undefined") { throwErrorConstEval( `destructuring assignment expected field ${idTextErr( @@ -1641,14 +1593,14 @@ export class Interpreter { ast.loc, ); } - this.envStack.setNewBinding(idText(name), v); + this.envStack.setNewBinding(A.idText(name), v); } } - public interpretAssignStatement(ast: AstStatementAssign) { + public interpretAssignStatement(ast: A.AstStatementAssign) { if (ast.path.kind === "id") { const val = this.interpretExpression(ast.expression); - this.envStack.updateBinding(idText(ast.path), val); + this.envStack.updateBinding(A.idText(ast.path), val); } else { throwNonFatalErrorConstEval( "only identifiers are currently supported as path expressions", @@ -1657,10 +1609,14 @@ export class Interpreter { } } - public interpretAugmentedAssignStatement(ast: AstStatementAugmentedAssign) { + public interpretAugmentedAssignStatement( + ast: A.AstStatementAugmentedAssign, + ) { if (ast.path.kind === "id") { const updateVal = () => this.interpretExpression(ast.expression); - const currentPathValue = this.envStack.getBinding(idText(ast.path)); + const currentPathValue = this.envStack.getBinding( + A.idText(ast.path), + ); if (currentPathValue === undefined) { throwNonFatalErrorConstEval( "undeclared identifier", @@ -1674,7 +1630,7 @@ export class Interpreter { ast.loc, this.util, ); - this.envStack.updateBinding(idText(ast.path), newVal); + this.envStack.updateBinding(A.idText(ast.path), newVal); } else { throwNonFatalErrorConstEval( "only identifiers are currently supported as path expressions", @@ -1683,7 +1639,7 @@ export class Interpreter { } } - public interpretConditionStatement(ast: AstCondition) { + public interpretConditionStatement(ast: A.AstCondition) { const condition = ensureBoolean( this.interpretExpression(ast.condition), ); @@ -1698,15 +1654,15 @@ export class Interpreter { } } - public interpretExpressionStatement(ast: AstStatementExpression) { + public interpretExpressionStatement(ast: A.AstStatementExpression) { this.interpretExpression(ast.expression); } - public interpretForEachStatement(ast: AstStatementForEach) { + public interpretForEachStatement(ast: A.AstStatementForEach) { throwNonFatalErrorConstEval("foreach currently not supported", ast.loc); } - public interpretRepeatStatement(ast: AstStatementRepeat) { + public interpretRepeatStatement(ast: A.AstStatementRepeat) { const iterations = ensureRepeatInt( this.interpretExpression(ast.iterations), ); @@ -1725,7 +1681,7 @@ export class Interpreter { } } - public interpretReturnStatement(ast: AstStatementReturn) { + public interpretReturnStatement(ast: A.AstStatementReturn) { if (ast.expression !== null) { const val = this.interpretExpression(ast.expression); throw new ReturnSignal(val); @@ -1734,14 +1690,14 @@ export class Interpreter { } } - public interpretTryStatement(ast: AstStatementTry) { + public interpretTryStatement(ast: A.AstStatementTry) { throwNonFatalErrorConstEval( "try statements currently not supported", ast.loc, ); } - public interpretUntilStatement(ast: AstStatementUntil) { + public interpretUntilStatement(ast: A.AstStatementUntil) { let condition; let iterCount = 0; // We can create a single environment for all the iterations in the loop @@ -1770,7 +1726,7 @@ export class Interpreter { }); } - public interpretWhileStatement(ast: AstStatementWhile) { + public interpretWhileStatement(ast: A.AstStatementWhile) { let condition; let iterCount = 0; // We can create a single environment for all the iterations in the loop @@ -1801,7 +1757,7 @@ export class Interpreter { }); } - public interpretBlockStatement(ast: AstStatementBlock) { + public interpretBlockStatement(ast: A.AstStatementBlock) { this.envStack.executeInNewEnvironment(() => { ast.statements.forEach(this.interpretStatement, this); }); diff --git a/src/optimizer/test/partial-eval.spec.ts b/src/optimizer/test/partial-eval.spec.ts index 81c4004d9..2601a249b 100644 --- a/src/optimizer/test/partial-eval.spec.ts +++ b/src/optimizer/test/partial-eval.spec.ts @@ -1,10 +1,4 @@ -import { - AstExpression, - FactoryAst, - eqExpressions, - getAstFactory, - isLiteral, -} from "../../ast/ast"; +import * as A from "../../ast/ast"; import { AstUtil, getAstUtil } from "../../ast/util"; import { getOptimizer } from "../constEval"; import { CompilerContext } from "../../context/context"; @@ -316,7 +310,7 @@ const booleanExpressions = [ function testExpression(original: string, simplified: string) { it(`should simplify ${original} to ${simplified}`, () => { - const ast = getAstFactory(); + const ast = A.getAstFactory(); const { parseExpression } = getParser(ast, defaultParser); const util = getAstUtil(ast); const { partiallyEvalExpression } = getOptimizer(util); @@ -325,7 +319,7 @@ function testExpression(original: string, simplified: string) { new CompilerContext(), ); const simplifiedValue = dummyEval(parseExpression(simplified), ast); - const areMatching = eqExpressions(originalValue, simplifiedValue); + const areMatching = A.eqExpressions(originalValue, simplifiedValue); expect(areMatching).toBe(true); }); } @@ -336,13 +330,13 @@ function testExpressionWithOptimizer( optimizer: ExpressionTransformer, ) { it(`should simplify ${original} to ${simplified}`, () => { - const ast = getAstFactory(); + const ast = A.getAstFactory(); const { parseExpression } = getParser(ast, defaultParser); const originalValue = optimizer.applyRules( dummyEval(parseExpression(original), ast), ); const simplifiedValue = dummyEval(parseExpression(simplified), ast); - const areMatching = eqExpressions(originalValue, simplifiedValue); + const areMatching = A.eqExpressions(originalValue, simplifiedValue); expect(areMatching).toBe(true); }); } @@ -352,10 +346,13 @@ function testExpressionWithOptimizer( // The reason for doing this is that the partial evaluator will actually simplify constant // expressions. So, when comparing for equality of expressions, we also need to simplify // constant expressions. -function dummyEval(ast: AstExpression, astFactory: FactoryAst): AstExpression { +function dummyEval( + ast: A.AstExpression, + astFactory: A.FactoryAst, +): A.AstExpression { const cloneNode = astFactory.cloneNode; const util = getAstUtil(astFactory); - const recurse = (ast: AstExpression): AstExpression => { + const recurse = (ast: A.AstExpression): A.AstExpression => { switch (ast.kind) { case "null": return ast; @@ -393,7 +390,7 @@ function dummyEval(ast: AstExpression, astFactory: FactoryAst): AstExpression { case "op_unary": { const newNode = cloneNode(ast); newNode.operand = recurse(ast.operand); - if (isLiteral(newNode.operand)) { + if (A.isLiteral(newNode.operand)) { return evalUnaryOp(ast.op, newNode.operand, ast.loc, util); } return newNode; @@ -402,7 +399,7 @@ function dummyEval(ast: AstExpression, astFactory: FactoryAst): AstExpression { const newNode = cloneNode(ast); newNode.left = recurse(ast.left); newNode.right = recurse(ast.right); - if (isLiteral(newNode.left) && isLiteral(newNode.right)) { + if (A.isLiteral(newNode.left) && A.isLiteral(newNode.right)) { const valR = newNode.right; return evalBinaryOp( ast.op, @@ -452,13 +449,13 @@ class ParameterizableDummyOptimizer implements ExpressionTransformer { public util: AstUtil; - constructor(rules: Rule[], Ast: FactoryAst) { + constructor(rules: Rule[], Ast: A.FactoryAst) { this.util = getAstUtil(Ast); this.rules = rules; } - public applyRules = (ast: AstExpression): AstExpression => { + public applyRules = (ast: A.AstExpression): A.AstExpression => { return this.rules.reduce( (prev, rule) => rule.applyRule(prev, this), ast, @@ -482,7 +479,7 @@ describe("partial-evaluator", () => { // uses the associative rule 3. const optimizer = new ParameterizableDummyOptimizer( [new AssociativeRule3()], - getAstFactory(), + A.getAstFactory(), ); testExpressionWithOptimizer(test.original, test.simplified, optimizer); diff --git a/src/types/resolveABITypeRef.ts b/src/types/resolveABITypeRef.ts index c0773821b..193b143eb 100644 --- a/src/types/resolveABITypeRef.ts +++ b/src/types/resolveABITypeRef.ts @@ -1,18 +1,5 @@ import { ABITypeRef } from "@ton/core"; -import { - AstFieldDecl, - AstTypeId, - eqNames, - idText, - isAddress, - isBool, - isBuilder, - isCell, - isInt, - isSlice, - isString, - isStringBuilder, -} from "../ast/ast"; +import * as A from "../ast/ast"; import { idTextErr, throwCompilationError, @@ -76,7 +63,7 @@ const builderFormats: FormatDef = { remaining: { type: "builder", format: "remainder" }, }; -export function resolveABIType(src: AstFieldDecl): ABITypeRef { +export function resolveABIType(src: A.AstFieldDecl): ABITypeRef { if ( src.type.kind === "type_id" || (src.type.kind === "optional_type" && @@ -86,7 +73,7 @@ export function resolveABIType(src: AstFieldDecl): ABITypeRef { // Primitive types // - const typeId: AstTypeId = + const typeId: A.AstTypeId = src.type.kind === "type_id" ? src.type : src.type.typeArg.kind === "type_id" @@ -96,9 +83,9 @@ export function resolveABIType(src: AstFieldDecl): ABITypeRef { src.type.typeArg.loc, ); - if (isInt(typeId)) { + if (A.isInt(typeId)) { if (src.as) { - const fmt = intFormats[idText(src.as)]; + const fmt = intFormats[A.idText(src.as)]; if (!fmt) { throwCompilationError( `Unsupported format ${idTextErr(src.as)}`, @@ -119,7 +106,7 @@ export function resolveABIType(src: AstFieldDecl): ABITypeRef { format: 257, }; // Default is maximum size int } - if (isBool(typeId)) { + if (A.isBool(typeId)) { if (src.as) { throwCompilationError( `Unsupported format ${idTextErr(src.as)}`, @@ -132,9 +119,9 @@ export function resolveABIType(src: AstFieldDecl): ABITypeRef { optional: src.type.kind === "optional_type", }; } - if (isCell(typeId)) { + if (A.isCell(typeId)) { if (src.as) { - const fmt = cellFormats[idText(src.as)]; + const fmt = cellFormats[A.idText(src.as)]; if (!fmt) { throwCompilationError( `Unsupported format ${idTextErr(src.as)}`, @@ -154,9 +141,9 @@ export function resolveABIType(src: AstFieldDecl): ABITypeRef { optional: src.type.kind === "optional_type", }; } - if (isSlice(typeId)) { + if (A.isSlice(typeId)) { if (src.as) { - const fmt = sliceFormats[idText(src.as)]; + const fmt = sliceFormats[A.idText(src.as)]; if (!fmt) { throwCompilationError( `Unsupported format ${idTextErr(src.as)}`, @@ -176,9 +163,9 @@ export function resolveABIType(src: AstFieldDecl): ABITypeRef { optional: src.type.kind === "optional_type", }; } - if (isBuilder(typeId)) { + if (A.isBuilder(typeId)) { if (src.as) { - const fmt = builderFormats[idText(src.as)]; + const fmt = builderFormats[A.idText(src.as)]; if (!fmt) { throwCompilationError( `Unsupported format ${idTextErr(src.as)}`, @@ -198,7 +185,7 @@ export function resolveABIType(src: AstFieldDecl): ABITypeRef { optional: src.type.kind === "optional_type", }; } - if (isAddress(typeId)) { + if (A.isAddress(typeId)) { if (src.as) { throwCompilationError( `Unsupported format ${idTextErr(src.as)}`, @@ -211,7 +198,7 @@ export function resolveABIType(src: AstFieldDecl): ABITypeRef { optional: src.type.kind === "optional_type", }; } - if (isString(typeId)) { + if (A.isString(typeId)) { if (src.as) { throwCompilationError( `Unsupported format ${idTextErr(src.as)}`, @@ -224,7 +211,7 @@ export function resolveABIType(src: AstFieldDecl): ABITypeRef { optional: src.type.kind === "optional_type", }; } - if (isStringBuilder(typeId)) { + if (A.isStringBuilder(typeId)) { throwCompilationError(`Unsupported type StringBuilder`, src.loc); } @@ -233,10 +220,10 @@ export function resolveABIType(src: AstFieldDecl): ABITypeRef { // if (src.as) { - if (eqNames(src.as, "reference")) { + if (A.eqNames(src.as, "reference")) { return { kind: "simple", - type: idText(typeId), + type: A.idText(typeId), optional: src.type.kind === "optional_type", format: "ref", }; @@ -249,7 +236,7 @@ export function resolveABIType(src: AstFieldDecl): ABITypeRef { } return { kind: "simple", - type: idText(typeId), + type: A.idText(typeId), optional: src.type.kind === "optional_type", }; } @@ -265,11 +252,11 @@ export function resolveABIType(src: AstFieldDecl): ABITypeRef { let valueFormat: string | number | undefined = undefined; // Resolve key type - if (isInt(src.type.keyType)) { + if (A.isInt(src.type.keyType)) { key = "int"; if (src.type.keyStorageType) { const format = - intMapKeyFormats[idText(src.type.keyStorageType)]; + intMapKeyFormats[A.idText(src.type.keyStorageType)]; if (!format) { throwCompilationError( `Unsupported format ${idTextErr(src.type.keyStorageType)} for map key`, @@ -279,7 +266,7 @@ export function resolveABIType(src: AstFieldDecl): ABITypeRef { key = format.type; keyFormat = format.format; } - } else if (isAddress(src.type.keyType)) { + } else if (A.isAddress(src.type.keyType)) { key = "address"; if (src.type.keyStorageType) { throwCompilationError( @@ -295,21 +282,21 @@ export function resolveABIType(src: AstFieldDecl): ABITypeRef { } // Resolve value type - if (isInt(src.type.valueType)) { + if (A.isInt(src.type.valueType)) { value = "int"; if (src.type.valueStorageType) { const format = - intMapValFormats[idText(src.type.valueStorageType)]; + intMapValFormats[A.idText(src.type.valueStorageType)]; if (!format) { throwCompilationError( - `Unsupported format ${idText(src.type.valueStorageType)} for map value`, + `Unsupported format ${A.idText(src.type.valueStorageType)} for map value`, src.loc, ); } value = format.type; valueFormat = format.format; } - } else if (isBool(src.type.valueType)) { + } else if (A.isBool(src.type.valueType)) { value = "bool"; if (src.type.valueStorageType) { throwCompilationError( @@ -317,24 +304,24 @@ export function resolveABIType(src: AstFieldDecl): ABITypeRef { src.loc, ); } - } else if (isCell(src.type.valueType)) { + } else if (A.isCell(src.type.valueType)) { value = "cell"; valueFormat = "ref"; if ( src.type.valueStorageType && - eqNames(src.type.valueStorageType, "reference") + A.eqNames(src.type.valueStorageType, "reference") ) { throwCompilationError( `Unsupported format ${idTextErr(src.type.valueStorageType)} for map value`, src.loc, ); } - } else if (isSlice(src.type.valueType)) { + } else if (A.isSlice(src.type.valueType)) { throwCompilationError( `Unsupported map value type ${idTextErr(src.type.valueType)}`, src.loc, ); - } else if (isAddress(src.type.valueType)) { + } else if (A.isAddress(src.type.valueType)) { value = "address"; if (src.type.valueStorageType) { throwCompilationError( @@ -342,25 +329,25 @@ export function resolveABIType(src: AstFieldDecl): ABITypeRef { src.loc, ); } - } else if (isString(src.type.valueType)) { + } else if (A.isString(src.type.valueType)) { throwCompilationError( `Unsupported map value type ${idTextErr(src.type.valueType)}`, src.loc, ); } else if ( - isStringBuilder(src.type.valueType) || - isBuilder(src.type.valueType) + A.isStringBuilder(src.type.valueType) || + A.isBuilder(src.type.valueType) ) { throwCompilationError( `Unsupported map value type ${idTextErr(src.type.valueType)}`, src.loc, ); } else { - value = idText(src.type.valueType); + value = A.idText(src.type.valueType); valueFormat = "ref"; if ( src.type.valueStorageType && - eqNames(src.type.valueStorageType, "reference") + A.eqNames(src.type.valueStorageType, "reference") ) { throwCompilationError( `Unsupported format ${idTextErr(src.type.valueStorageType)} for map value`, diff --git a/src/types/resolveStatements.ts b/src/types/resolveStatements.ts index b6be0529d..e1b057b9e 100644 --- a/src/types/resolveStatements.ts +++ b/src/types/resolveStatements.ts @@ -1,16 +1,5 @@ +import * as A from "../ast/ast"; import { CompilerContext } from "../context/context"; -import { - AstCondition, - AstStatement, - tryExtractPath, - AstId, - idText, - isWildcard, - selfId, - isSelfId, - eqNames, - FactoryAst, -} from "../ast/ast"; import { isAssignable } from "./subtyping"; import { idTextErr, @@ -59,24 +48,24 @@ export function emptyContext( function checkVariableExists( ctx: CompilerContext, sctx: StatementContext, - name: AstId, + name: A.AstId, ): void { - if (sctx.vars.has(idText(name))) { + if (sctx.vars.has(A.idText(name))) { throwCompilationError( `Variable already exists: ${idTextErr(name)}`, name.loc, ); } // Check if the user tries to shadow the current function name - if (sctx.funName === idText(name)) { + if (sctx.funName === A.idText(name)) { throwCompilationError( `Variable cannot have the same name as its enclosing function: ${idTextErr(name)}`, name.loc, ); } - if (hasStaticConstant(ctx, idText(name))) { + if (hasStaticConstant(ctx, A.idText(name))) { if (name.loc.origin === "stdlib") { - const constLoc = getStaticConstant(ctx, idText(name)).loc; + const constLoc = getStaticConstant(ctx, A.idText(name)).loc; throwCompilationError( `Constant ${idTextErr(name)} is shadowing an identifier defined in the Tact standard library: pick a different constant name`, constLoc, @@ -118,23 +107,23 @@ function removeRequiredVariable( } function addVariable( - name: AstId, + name: A.AstId, ref: TypeRef, ctx: CompilerContext, sctx: StatementContext, ): StatementContext { checkVariableExists(ctx, sctx, name); // Should happen earlier - if (isWildcard(name)) { + if (A.isWildcard(name)) { return sctx; } return { ...sctx, - vars: new Map(sctx.vars).set(idText(name), ref), + vars: new Map(sctx.vars).set(A.idText(name), ref), }; } function processCondition( - condition: AstCondition, + condition: A.AstCondition, sctx: StatementContext, ctx: CompilerContext, ): { @@ -213,16 +202,16 @@ function processCondition( // Precondition: `self` here means a contract or a trait, // and not a `self` parameter of a mutating method -export function isLvalue(path: AstId[], ctx: CompilerContext): boolean { +export function isLvalue(path: A.AstId[], ctx: CompilerContext): boolean { const headId = path[0]!; - if (isSelfId(headId) && path.length > 1) { + if (A.isSelfId(headId) && path.length > 1) { // we can be dealing with a contract/trait constant `self.constFoo` const selfTypeRef = getExpType(ctx, headId); if (selfTypeRef.kind == "ref") { const contractTypeDescription = getType(ctx, selfTypeRef.name); return ( contractTypeDescription.constants.findIndex((constDescr) => - eqNames(path[1]!, constDescr.name), + A.eqNames(path[1]!, constDescr.name), ) === -1 ); } else { @@ -230,12 +219,12 @@ export function isLvalue(path: AstId[], ctx: CompilerContext): boolean { } } else { // if the head path symbol is a global constant, then the whole path expression is a constant - return !hasStaticConstant(ctx, idText(headId)); + return !hasStaticConstant(ctx, A.idText(headId)); } } function processStatements( - statements: AstStatement[], + statements: A.AstStatement[], sctx: StatementContext, ctx: CompilerContext, ): { @@ -295,7 +284,7 @@ function processStatements( const tempSctx = { ...sctx, requiredFields: [] }; // Process lvalue ctx = resolveExpression(s.path, tempSctx, ctx); - const path = tryExtractPath(s.path); + const path = A.tryExtractPath(s.path); if (path === null) { throwCompilationError( `Assignments are allowed only into path expressions, i.e. identifiers, or sequences of direct contract/struct/message accesses, like "self.foo" or "self.structure.field"`, @@ -339,7 +328,7 @@ function processStatements( // Process lvalue const tempSctx = { ...sctx, requiredFields: [] }; ctx = resolveExpression(s.path, tempSctx, ctx); - const path = tryExtractPath(s.path); + const path = A.tryExtractPath(s.path); if (path === null) { throwCompilationError( `Assignments are allowed only into path expressions, i.e. identifiers, or sequences of direct contract/struct/message accesses, like "self.foo" or "self.structure.field"`, @@ -415,7 +404,7 @@ function processStatements( if ( s.expression.kind === "static_call" && ["throw", "nativeThrow"].includes( - idText(s.expression.function), + A.idText(s.expression.function), ) ) { returnAlwaysReachable = true; @@ -629,7 +618,7 @@ function processStatements( // Resolve map expression ctx = resolveExpression(s.map, sctx, ctx); - const mapPath = tryExtractPath(s.map); + const mapPath = A.tryExtractPath(s.map); if (mapPath === null) { throwCompilationError( `foreach is only allowed over maps that are path expressions, i.e. identifiers, or sequences of direct contract/struct/message accesses, like "self.foo" or "self.structure.field"`, @@ -649,7 +638,7 @@ function processStatements( let foreachSctx = sctx; // Add key and value to statement context - if (!isWildcard(s.keyName)) { + if (!A.isWildcard(s.keyName)) { checkVariableExists(ctx, initialSctx, s.keyName); foreachSctx = addVariable( s.keyName, @@ -658,7 +647,7 @@ function processStatements( initialSctx, ); } - if (!isWildcard(s.valueName)) { + if (!A.isWildcard(s.valueName)) { checkVariableExists(ctx, foreachSctx, s.valueName); foreachSctx = addVariable( s.valueName, @@ -746,7 +735,7 @@ function processStatements( // Add variables s.identifiers.forEach(([field, name], _) => { - const f = ty.fields.find((f) => eqNames(f.name, field)); + const f = ty.fields.find((f) => A.eqNames(f.name, field)); if (!f) { throwCompilationError( `Field '${idTextErr(field)}' not found in type '${expressionType.name}'`, @@ -773,7 +762,7 @@ function processStatements( } function processFunctionBody( - statements: AstStatement[], + statements: A.AstStatement[], sctx: StatementContext, ctx: CompilerContext, ): CompilerContext { @@ -805,7 +794,7 @@ function processFunctionBody( return res.ctx; } -export function resolveStatements(ctx: CompilerContext, Ast: FactoryAst) { +export function resolveStatements(ctx: CompilerContext, Ast: A.FactoryAst) { const util = getAstUtil(Ast); // Process all static functions @@ -830,7 +819,7 @@ export function resolveStatements(ctx: CompilerContext, Ast: FactoryAst) { // Self sctx = addVariable( - selfId, + A.selfId, { kind: "ref", name: t.name, optional: false }, ctx, sctx, @@ -862,7 +851,7 @@ export function resolveStatements(ctx: CompilerContext, Ast: FactoryAst) { // Build statement context let sctx = emptyContext(f.ast.loc, null, { kind: "void" }); sctx = addVariable( - selfId, + A.selfId, { kind: "ref", name: t.name, optional: false }, ctx, sctx, @@ -958,7 +947,7 @@ export function resolveStatements(ctx: CompilerContext, Ast: FactoryAst) { "Self is null where it should not be", ); } - sctx = addVariable(selfId, f.self, ctx, sctx); + sctx = addVariable(A.selfId, f.self, ctx, sctx); // Check for collisions in getter method IDs if (f.isGetter) {