diff --git a/src/codegen/generators/generator.ts b/src/codegen/generators/generator.ts index 08ed131..f2a3a9d 100644 --- a/src/codegen/generators/generator.ts +++ b/src/codegen/generators/generator.ts @@ -1,3 +1,4 @@ +import { TLBCode, TLBType } from "../ast" import { GenDeclaration } from "./typescript/tsgen" export interface CodeGenerator { @@ -5,4 +6,5 @@ export interface CodeGenerator { addTonCoreClassUsage(name: string): void addBitLenFunction(): void + addTlbType(tlbType: TLBType, tlbCode: TLBCode, input: string[], jsCodeConstructorDeclarations: GenDeclaration[], jsCodeFunctionsDeclarations: GenDeclaration[]): void } \ No newline at end of file diff --git a/src/codegen/generators/typescript/generator.ts b/src/codegen/generators/typescript/generator.ts index 07e9760..dab38d1 100644 --- a/src/codegen/generators/typescript/generator.ts +++ b/src/codegen/generators/typescript/generator.ts @@ -1,9 +1,13 @@ +import { TLBCode, TLBType } from "../../ast"; +import { handleField } from "../../field_handler"; +import { firstLower, getStringDeclaration, getSubStructName, goodVariableName } from "../../utils"; import { CodeGenerator } from "../generator"; -import { GenDeclaration, tExpressionStatement, tFunctionDeclaration, tIdentifier, tImportDeclaration, tStringLiteral, tTypeParametersExpression, tTypedIdentifier } from "./tsgen"; +import { BinaryExpression, GenDeclaration, ObjectProperty, Statement, StructDeclaration, TypeExpression, TypeParametersExpression, TypedIdentifier, tArrowFunctionExpression, tArrowFunctionType, tBinaryExpression, tComment, tExpressionStatement, tFunctionCall, tFunctionDeclaration, tIdentifier, tIfStatement, tImportDeclaration, tMemberExpression, tNumericLiteral, tObjectExpression, tObjectProperty, tReturnStatement, tStringLiteral, tStructDeclaration, tTypeParametersExpression, tTypeWithParameters, tTypedIdentifier, tUnaryOpExpression, tUnionTypeDeclaration, tUnionTypeExpression, toCode } from "./tsgen"; +import { convertToAST, getCondition, getParamVarExpr, getTypeParametersExpression } from "./utils"; export class TypescriptGenerator implements CodeGenerator { jsCodeDeclarations: GenDeclaration[] = [] - + addTonCoreClassUsage(name: string) { this.jsCodeDeclarations.push(tImportDeclaration(tIdentifier(name), tStringLiteral('ton'))) } @@ -12,4 +16,151 @@ export class TypescriptGenerator implements CodeGenerator { tExpressionStatement(tIdentifier('return n.toString(2).length;')) ])) } + addTlbType(tlbType: TLBType, tlbCode: TLBCode, input: string[], jsCodeConstructorDeclarations: GenDeclaration[], jsCodeFunctionsDeclarations: GenDeclaration[]): void { + let variableCombinatorName = goodVariableName(firstLower(tlbType.name), '0') + let subStructsUnion: TypeExpression[] = [] + let subStructDeclarations: StructDeclaration[] = [] + + let loadStatements: Statement[] = [] + let storeStatements: Statement[] = [] + + let structTypeParametersExpr: TypeParametersExpression = tTypeParametersExpression([]); + + tlbType.constructors.forEach(constructor => { + let constructorLoadStatements: Statement[] = [] + let declaration = constructor.declaration; + let subStructName: string = getSubStructName(tlbType, constructor); + + let variableSubStructName = goodVariableName(firstLower(subStructName), '_' + constructor.name) + + let subStructProperties: TypedIdentifier[] = [tTypedIdentifier(tIdentifier('kind'), tStringLiteral(subStructName))] + let subStructLoadProperties: ObjectProperty[] = [tObjectProperty(tIdentifier('kind'), tStringLiteral(subStructName))] + let subStructStoreStatements: Statement[] = [] + + if (constructor.tag == undefined) { + return; + } + + structTypeParametersExpr = getTypeParametersExpression(constructor.parameters); + + let slicePrefix: number[] = [0]; + + constructor.variables.forEach((variable) => { + if (variable.negated) { + if (variable.deriveExpr) { + subStructLoadProperties.push(tObjectProperty(tIdentifier(goodVariableName(variable.name)), convertToAST(variable.deriveExpr, constructor))); + } + } + }) + + let fieldIndex = 0; + declaration?.fields.forEach(element => { handleField(element, slicePrefix, tlbCode, constructor, constructorLoadStatements, subStructStoreStatements, subStructProperties, subStructLoadProperties, variableCombinatorName, variableSubStructName, jsCodeFunctionsDeclarations, fieldIndex.toString()); fieldIndex++; }) + + subStructsUnion.push(tTypeWithParameters(tIdentifier(subStructName), structTypeParametersExpr)); + + let structX = tStructDeclaration(tIdentifier(subStructName), subStructProperties, structTypeParametersExpr); + + constructor.constraints.forEach(constraint => { + let loadConstraintAST = convertToAST(constraint, constructor, true); + let storeConstraintAST = convertToAST(constraint, constructor, true, tIdentifier(variableCombinatorName)); + let exceptionCommentLastPart = ` is not satisfied while loading "${getSubStructName(tlbType, constructor)}" for type "${tlbType.name}"` + constructorLoadStatements.push(tIfStatement(tUnaryOpExpression('!', loadConstraintAST), [tExpressionStatement(tIdentifier("throw new Error('Condition " + toCode(loadConstraintAST).code + exceptionCommentLastPart + "')"))])); + subStructStoreStatements.push(tIfStatement(tUnaryOpExpression('!', storeConstraintAST), [tExpressionStatement(tIdentifier("throw new Error('Condition " + toCode(storeConstraintAST).code + exceptionCommentLastPart + "')"))])) + }); + + constructorLoadStatements.push(tReturnStatement(tObjectExpression(subStructLoadProperties))); + if (constructor.tag.bitLen != 0 || tlbType.constructors.length > 1) { + let conditions: Array = [] + if (constructor.tag.bitLen != 0) { + conditions.push(tBinaryExpression(tMemberExpression(tIdentifier('slice'), tIdentifier('remainingBits')), '>=', tNumericLiteral(constructor.tag.bitLen))) + conditions.push(tBinaryExpression(tFunctionCall(tMemberExpression(tIdentifier('slice'), tIdentifier('preloadUint')), [tNumericLiteral(constructor.tag.bitLen)]), '==', tIdentifier(constructor.tag.binary))) + let loadBitsStatement: Statement[] = [tExpressionStatement(tFunctionCall(tMemberExpression(tIdentifier('slice'), tIdentifier('loadUint')), [tNumericLiteral(constructor.tag.bitLen)]))] + constructorLoadStatements = loadBitsStatement.concat(constructorLoadStatements); + } + constructor.parameters.forEach(param => { + if (param.variable.const && !param.variable.negated) { + let argName = goodVariableName(param.variable.name); + if (param.argName) { + argName = param.argName + } + conditions.push(tBinaryExpression(tIdentifier(argName), '==', getParamVarExpr(param, constructor))) + } + }); + loadStatements.push(tIfStatement(getCondition(conditions), constructorLoadStatements)) + } else { + loadStatements = loadStatements.concat(constructorLoadStatements); + } + + if (constructor.tag.bitLen != 0) { + let preStoreStatement: Statement[] = [tExpressionStatement(tFunctionCall(tMemberExpression(tIdentifier('builder'), tIdentifier('storeUint')), [tIdentifier(constructor.tag.binary), tNumericLiteral(constructor.tag.bitLen)]))]; + subStructStoreStatements = preStoreStatement.concat(subStructStoreStatements) + } + let storeStatement: Statement = tReturnStatement(tArrowFunctionExpression([tTypedIdentifier(tIdentifier('builder'), tIdentifier('Builder'))], subStructStoreStatements)); + if (tlbType.constructors.length > 1) { + storeStatement = tIfStatement(tBinaryExpression(tMemberExpression(tIdentifier(variableCombinatorName), tIdentifier('kind')), '==', tStringLiteral(subStructName)), [storeStatement]) + } + storeStatements.push(storeStatement); + + subStructDeclarations.push(structX) + + jsCodeFunctionsDeclarations.push(tComment(getStringDeclaration(constructor.declaration, input))) + + }); + + + // loadTheType: (slice: Slice) => TheType + + let exceptionTypesComment = tlbType.constructors.map(constructor => { return `"${getSubStructName(tlbType, constructor)}"` }).join(', ') + let exceptionComment = tExpressionStatement(tIdentifier("throw new Error('" + `Expected one of ${exceptionTypesComment} in loading "${tlbType.name}", but data does not satisfy any constructor` + "')")) + if (tlbType.constructors.length > 1 || tlbType.constructors.at(0)?.tag.bitLen != 0) { + let neededTypesComment = ''; + tlbType.constructors.forEach(constructor => { + neededTypesComment += getSubStructName(tlbType, constructor) + }) + loadStatements.push(exceptionComment) + } + if (tlbType.constructors.length > 1) { + storeStatements.push(exceptionComment) + } + + let loadFunctionParameters = [tTypedIdentifier(tIdentifier('slice'), tIdentifier('Slice'))] + let storeFunctionParameters = [tTypedIdentifier(tIdentifier(variableCombinatorName), tTypeWithParameters(tIdentifier(tlbType.name), structTypeParametersExpr))] + + let anyConstructor = tlbType.constructors[0]; + if (anyConstructor) { + anyConstructor.parameters.forEach(element => { + if (element.variable.type == 'Type') { + loadFunctionParameters.push(tTypedIdentifier(tIdentifier('load' + element.variable.name), tArrowFunctionType([tTypedIdentifier(tIdentifier('slice'), tIdentifier('Slice'))], tIdentifier(goodVariableName(element.variable.name))))) + + storeFunctionParameters.push( + tTypedIdentifier(tIdentifier('store' + element.variable.name), + tArrowFunctionType( + [tTypedIdentifier(tIdentifier(goodVariableName(firstLower(element.variable.name), '0')), tIdentifier(goodVariableName(element.variable.name)))], + tArrowFunctionType([tTypedIdentifier(tIdentifier('builder'), tIdentifier('Builder'))], tIdentifier('void'))))) + } + if (element.variable.type == '#' && !element.variable.negated) { + if (element.argName) { + loadFunctionParameters.push(tTypedIdentifier(tIdentifier(element.argName), tIdentifier('number'))) + } else { + loadFunctionParameters.push(tTypedIdentifier(tIdentifier(goodVariableName(element.variable.name)), tIdentifier('number'))) + } + } + }); + } + + let loadFunction = tFunctionDeclaration(tIdentifier('load' + tlbType.name), structTypeParametersExpr, tTypeWithParameters(tIdentifier(tlbType.name), structTypeParametersExpr), loadFunctionParameters, loadStatements); + + let storeFunction = tFunctionDeclaration(tIdentifier('store' + tlbType.name), structTypeParametersExpr, tIdentifier('(builder: Builder) => void'), storeFunctionParameters, storeStatements) + + if (tlbType.constructors.length > 1) { + let unionTypeDecl = tUnionTypeDeclaration(tTypeWithParameters(tIdentifier(tlbType.name), structTypeParametersExpr), tUnionTypeExpression(subStructsUnion)) + jsCodeConstructorDeclarations.push(unionTypeDecl) + } + subStructDeclarations.forEach(element => { + jsCodeConstructorDeclarations.push(element) + }); + + jsCodeFunctionsDeclarations.push(loadFunction) + jsCodeFunctionsDeclarations.push(storeFunction) + } } \ No newline at end of file diff --git a/src/codegen/main.ts b/src/codegen/main.ts index c804116..088e51e 100644 --- a/src/codegen/main.ts +++ b/src/codegen/main.ts @@ -37,159 +37,13 @@ export function generate(tree: Program, input: string) { let jsCodeFunctionsDeclarations: GenDeclaration[] = [] - let tlbCode: TLBCode = { types: new Map() } let splittedInput = input.split('\n') + fillConstructors(tree.declarations, tlbCode, splittedInput); - tlbCode.types.forEach((tlbType: TLBType) => { - let variableCombinatorName = goodVariableName(firstLower(tlbType.name), '0') - let subStructsUnion: TypeExpression[] = [] - let subStructDeclarations: StructDeclaration[] = [] - - let loadStatements: Statement[] = [] - let storeStatements: Statement[] = [] - - let structTypeParametersExpr: TypeParametersExpression = tTypeParametersExpression([]); - - tlbType.constructors.forEach(constructor => { - let constructorLoadStatements: Statement[] = [] - let declaration = constructor.declaration; - let subStructName: string = getSubStructName(tlbType, constructor); - - let variableSubStructName = goodVariableName(firstLower(subStructName), '_' + constructor.name) - - let subStructProperties: TypedIdentifier[] = [tTypedIdentifier(tIdentifier('kind'), tStringLiteral(subStructName))] - let subStructLoadProperties: ObjectProperty[] = [tObjectProperty(tIdentifier('kind'), tStringLiteral(subStructName))] - let subStructStoreStatements: Statement[] = [] - - if (constructor.tag == undefined) { - return; - } - - structTypeParametersExpr = getTypeParametersExpression(constructor.parameters); - - let slicePrefix: number[] = [0]; - - constructor.variables.forEach((variable) => { - if (variable.negated) { - if (variable.deriveExpr) { - subStructLoadProperties.push(tObjectProperty(tIdentifier(goodVariableName(variable.name)), convertToAST(variable.deriveExpr, constructor))); - } - } - }) - - let fieldIndex = 0; - declaration?.fields.forEach(element => { handleField(element, slicePrefix, tlbCode, constructor, constructorLoadStatements, subStructStoreStatements, subStructProperties, subStructLoadProperties, variableCombinatorName, variableSubStructName, jsCodeFunctionsDeclarations, fieldIndex.toString()); fieldIndex++; }) - - subStructsUnion.push(tTypeWithParameters(tIdentifier(subStructName), structTypeParametersExpr)); - - let structX = tStructDeclaration(tIdentifier(subStructName), subStructProperties, structTypeParametersExpr); - - constructor.constraints.forEach(constraint => { - let loadConstraintAST = convertToAST(constraint, constructor, true); - let storeConstraintAST = convertToAST(constraint, constructor, true, tIdentifier(variableCombinatorName)); - let exceptionCommentLastPart = ` is not satisfied while loading "${getSubStructName(tlbType, constructor)}" for type "${tlbType.name}"` - constructorLoadStatements.push(tIfStatement(tUnaryOpExpression('!', loadConstraintAST), [tExpressionStatement(tIdentifier("throw new Error('Condition " + toCode(loadConstraintAST).code + exceptionCommentLastPart + "')"))])); - subStructStoreStatements.push(tIfStatement(tUnaryOpExpression('!', storeConstraintAST), [tExpressionStatement(tIdentifier("throw new Error('Condition " + toCode(storeConstraintAST).code + exceptionCommentLastPart + "')"))])) - }); - - constructorLoadStatements.push(tReturnStatement(tObjectExpression(subStructLoadProperties))); - if (constructor.tag.bitLen != 0 || tlbType.constructors.length > 1) { - let conditions: Array = [] - if (constructor.tag.bitLen != 0) { - conditions.push(tBinaryExpression(tMemberExpression(tIdentifier('slice'), tIdentifier('remainingBits')), '>=', tNumericLiteral(constructor.tag.bitLen))) - conditions.push(tBinaryExpression(tFunctionCall(tMemberExpression(tIdentifier('slice'), tIdentifier('preloadUint')), [tNumericLiteral(constructor.tag.bitLen)]), '==', tIdentifier(constructor.tag.binary))) - let loadBitsStatement: Statement[] = [tExpressionStatement(tFunctionCall(tMemberExpression(tIdentifier('slice'), tIdentifier('loadUint')), [tNumericLiteral(constructor.tag.bitLen)]))] - constructorLoadStatements = loadBitsStatement.concat(constructorLoadStatements); - } - constructor.parameters.forEach(param => { - if (param.variable.const && !param.variable.negated) { - let argName = goodVariableName(param.variable.name); - if (param.argName) { - argName = param.argName - } - conditions.push(tBinaryExpression(tIdentifier(argName), '==', getParamVarExpr(param, constructor))) - } - }); - loadStatements.push(tIfStatement(getCondition(conditions), constructorLoadStatements)) - } else { - loadStatements = loadStatements.concat(constructorLoadStatements); - } - - if (constructor.tag.bitLen != 0) { - let preStoreStatement: Statement[] = [tExpressionStatement(tFunctionCall(tMemberExpression(tIdentifier('builder'), tIdentifier('storeUint')), [tIdentifier(constructor.tag.binary), tNumericLiteral(constructor.tag.bitLen)]))]; - subStructStoreStatements = preStoreStatement.concat(subStructStoreStatements) - } - let storeStatement: Statement = tReturnStatement(tArrowFunctionExpression([tTypedIdentifier(tIdentifier('builder'), tIdentifier('Builder'))], subStructStoreStatements)); - if (tlbType.constructors.length > 1) { - storeStatement = tIfStatement(tBinaryExpression(tMemberExpression(tIdentifier(variableCombinatorName), tIdentifier('kind')), '==', tStringLiteral(subStructName)), [storeStatement]) - } - storeStatements.push(storeStatement); - - subStructDeclarations.push(structX) - - jsCodeFunctionsDeclarations.push(tComment(getStringDeclaration(constructor.declaration, splittedInput))) - - }); - - - // loadTheType: (slice: Slice) => TheType - - let exceptionTypesComment = tlbType.constructors.map(constructor => {return `"${getSubStructName(tlbType, constructor)}"`}).join(', ') - let exceptionComment = tExpressionStatement(tIdentifier("throw new Error('" + `Expected one of ${exceptionTypesComment} in loading "${tlbType.name}", but data does not satisfy any constructor` + "')")) - if (tlbType.constructors.length > 1 || tlbType.constructors.at(0)?.tag.bitLen != 0) { - let neededTypesComment = ''; - tlbType.constructors.forEach(constructor => { - neededTypesComment += getSubStructName(tlbType, constructor) - }) - loadStatements.push(exceptionComment) - } - if (tlbType.constructors.length > 1) { - storeStatements.push(exceptionComment) - } - - let loadFunctionParameters = [tTypedIdentifier(tIdentifier('slice'), tIdentifier('Slice'))] - let storeFunctionParameters = [tTypedIdentifier(tIdentifier(variableCombinatorName), tTypeWithParameters(tIdentifier(tlbType.name), structTypeParametersExpr))] - - let anyConstructor = tlbType.constructors[0]; - if (anyConstructor) { - anyConstructor.parameters.forEach(element => { - if (element.variable.type == 'Type') { - loadFunctionParameters.push(tTypedIdentifier(tIdentifier('load' + element.variable.name), tArrowFunctionType([tTypedIdentifier(tIdentifier('slice'), tIdentifier('Slice'))], tIdentifier(goodVariableName(element.variable.name))))) - - storeFunctionParameters.push( - tTypedIdentifier(tIdentifier('store' + element.variable.name), - tArrowFunctionType( - [tTypedIdentifier(tIdentifier(goodVariableName(firstLower(element.variable.name), '0')), tIdentifier(goodVariableName(element.variable.name)))], - tArrowFunctionType([tTypedIdentifier(tIdentifier('builder'), tIdentifier('Builder'))], tIdentifier('void'))))) - } - if (element.variable.type == '#' && !element.variable.negated) { - if (element.argName) { - loadFunctionParameters.push(tTypedIdentifier(tIdentifier(element.argName), tIdentifier('number'))) - } else { - loadFunctionParameters.push(tTypedIdentifier(tIdentifier(goodVariableName(element.variable.name)), tIdentifier('number'))) - } - } - }); - } - - let loadFunction = tFunctionDeclaration(tIdentifier('load' + tlbType.name), structTypeParametersExpr, tTypeWithParameters(tIdentifier(tlbType.name), structTypeParametersExpr), loadFunctionParameters, loadStatements); - - let storeFunction = tFunctionDeclaration(tIdentifier('store' + tlbType.name), structTypeParametersExpr, tIdentifier('(builder: Builder) => void'), storeFunctionParameters, storeStatements) - - if (tlbType.constructors.length > 1) { - let unionTypeDecl = tUnionTypeDeclaration(tTypeWithParameters(tIdentifier(tlbType.name), structTypeParametersExpr), tUnionTypeExpression(subStructsUnion)) - jsCodeConstructorDeclarations.push(unionTypeDecl) - } - subStructDeclarations.forEach(element => { - jsCodeConstructorDeclarations.push(element) - }); - - jsCodeFunctionsDeclarations.push(loadFunction) - jsCodeFunctionsDeclarations.push(storeFunction) - }); + tlbCode.types.forEach((tlbType: TLBType) => { codeGenerator.addTlbType(tlbType, tlbCode, splittedInput, jsCodeConstructorDeclarations, jsCodeFunctionsDeclarations) }); let generatedCode = ''