# This file is part of Desktop App Toolkit, # a set of libraries for developing nice desktop applications. # # For license and copyright information please follow this link: # https://github.com/desktop-app/legal/blob/master/LEGAL import glob, re, binascii, os, sys from pprint import pprint def readInputs(inputFiles): lines = [] names = [] layer = 0 for inputFile in inputFiles: names.append(os.path.basename(inputFile)) lines.append('---types---') with open(inputFile, encoding="utf-8") as f: for line in f: layerline = re.match(r'// LAYER (\d+)', line) if (layerline): layer = int(layerline.group(1)) else: lines.append(line) return lines, layer, names # text serialization: types and funcs def addTextSerialize(typeList, typeData, typesDict, idPrefix, primeType, boxed, prefix): result = '' for restype in typeList: v = typeData[restype] for data in v: name = data[0] prmsList = data[2] prms = data[3] hasFlags = data[4] hasFlags64 = data[5] conditionsList = data[6] conditions = data[7] trivialConditions = data[8] isTemplate = data[9] flagRawType = 'uint64' if hasFlags64 != '' else 'uint32' templateArgument = '' if (isTemplate != ''): templateArgument = '' result += 'bool Serialize_' + name + '(DumpToTextBuffer &to, int32 stage, int32 lev, Types &types, Types &vtypes, Stages &stages, Flags &flags, const ' + primeType + ' *start, const ' + primeType + ' *end, uint64 iflag) {\n' if (len(conditions)): result += '\tauto flag = ' + prefix + name + templateArgument + '::Flags::from_raw(' + flagRawType + '(iflag));\n\n' if (len(prms)): result += '\tif (stage) {\n' result += '\t\tto.add(",\\n").addSpaces(lev);\n' result += '\t} else {\n' result += '\t\tto.add("{ ' + name + '");\n' result += '\t\tto.add("\\n").addSpaces(lev);\n' result += '\t}\n' result += '\tswitch (stage) {\n' stage = 0 for k in prmsList: v = prms[k] result += '\tcase ' + str(stage) + ': to.add(" ' + k + ': "); ++stages.back(); ' if (k == hasFlags): if (hasFlags64 != ''): result += 'if (start + 1 >= end) return false; else flags.back() = int64(*start) + (int64(*(start + 1)) << 32); ' else: result += 'if (start >= end) return false; else flags.back() = *start; ' if (k in trivialConditions): flagBitValue = int(conditions[k]) flagFieldName = hasFlags64 if flagBitValue >= 32 else hasFlags flagBitLogged = (flagBitValue - 32) if flagBitValue >= 32 else flagBitValue result += 'if (flag & ' + prefix + name + templateArgument + '::Flag::f_' + k + ') { ' result += 'to.add("YES [ BY BIT ' + str(flagBitLogged) + ' IN FIELD ' + flagFieldName + ' ]"); ' result += '} else { to.add("[ SKIPPED BY BIT ' + str(flagBitLogged) + ' IN FIELD ' + flagFieldName + ' ]"); } ' else: if (k in conditions): result += 'if (flag & ' + prefix + name + templateArgument + '::Flag::f_' + k + ') { ' result += 'types.push_back(' vtypeget = re.match(r'^[Vv]ector', v) if (vtypeget): if (not re.match(r'^[A-Z]', v)): result += idPrefix + 'vector' else: result += '0' restype = vtypeget.group(1) try: if boxed[restype]: restype = 0 except KeyError: if re.match(r'^[A-Z]', restype): restype = 0 else: restype = v try: if boxed[restype]: restype = 0 except KeyError: if re.match(r'^[A-Z]', restype): restype = 0 if (restype): try: conses = typesDict[restype] if (len(conses) > 1): print('Complex bare type found: "' + restype + '" trying to serialize "' + k + '" of type "' + v + '"') continue if (vtypeget): result += '); vtypes.push_back(' result += idPrefix + conses[0][0] if (not vtypeget): result += '); vtypes.push_back(0' except KeyError: if (vtypeget): result += '); vtypes.push_back(' if (re.match(r'^flags<', restype)): result += idPrefix + 'flags' if (k == hasFlags): if (hasFlags64 != ''): result += '64' else: result += idPrefix + restype + '+0' if (not vtypeget): result += '); vtypes.push_back(0' else: if (not vtypeget): result += '0' result += '); vtypes.push_back(0' result += '); stages.push_back(0); flags.push_back(0); ' if (k in conditions): flagBitValue = int(conditions[k]) flagFieldName = hasFlags64 if flagBitValue >= 32 else hasFlags flagBitLogged = (flagBitValue - 32) if flagBitValue >= 32 else flagBitValue result += '} else { to.add("[ SKIPPED BY BIT ' + str(flagBitLogged) + ' IN FIELD ' + flagFieldName + ' ]"); } ' result += 'break;\n' stage = stage + 1 result += '\tdefault: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;\n' result += '\t}\n' else: result += '\tto.add("{ ' + name + ' }"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();\n' result += '\treturn true;\n' result += '}\n\n' return result # text serialization: types and funcs def addTextSerializeInit(typeList, typeData, idPrefix): result = '' for restype in typeList: v = typeData[restype] for data in v: name = data[0] result += '\t\t{ ' + idPrefix + name + ', Serialize_' + name + ' },\n' return result def generate(scheme): inputFiles = [] outputPath = '' nextOutputPath = False for arg in sys.argv[1:]: if nextOutputPath: nextOutputPath = False outputPath = arg elif arg == '-o': nextOutputPath = True elif re.match(r'^-o(.+)', arg): outputPath = arg[2:] else: inputFiles.append(arg) if len(inputFiles) == 0: raise ValueError('Input file required.') if outputPath == '': raise ValueError('Output path required.') readAndGenerate(inputFiles, outputPath, scheme) def endsWithForTag(comments, tag, ending): position = comments.find('@' + tag + ' ') if (position < 0): return False tail = comments[(position + len(tag) + 1):] till = tail.find('@') line = tail[:till] if till >= 0 else tail stripped = line.strip() fullending = '; ' + ending.strip() if len(stripped) < len(fullending): return False if stripped.endswith(fullending) or stripped.find(fullending + '.') >= 0 or stripped.find(fullending + ';') >= 0 or stripped.find(fullending + ' if') >= 0 or stripped.find(fullending + ' to') >= 0 or stripped.find(fullending + ' otherwise') >= 0 or stripped.find(fullending + ' unless') >= 0 or stripped.find(fullending + ' even') >= 0 or stripped.find(fullending + ' for') >= 0: return True if line.find(ending) >= 0: print('WARNING: Found "' + ending + '" in "' + stripped + '"') return False def paramNameTag(name): return 'param_description' if name == 'description' else name def isBotsOnlyLine(comments): return endsWithForTag(comments, 'description', 'for bots only') or endsWithForTag(comments, 'description', 'bots only') def isBotsOnlyParam(comments, name): return endsWithForTag(comments, paramNameTag(name), 'for bots only') or endsWithForTag(comments, paramNameTag(name), 'bots only') def isMobileOnlyLine(comments): return endsWithForTag(comments, 'description', 'for official mobile applications only') def isNullableVector(comments, name): return name.endswith('s') and endsWithForTag(comments, paramNameTag(name), name + ' may be null') def isNullableParam(comments, name): return endsWithForTag(comments, paramNameTag(name), 'may be null') or endsWithForTag(comments, paramNameTag(name), 'pass null') def readAndGenerate(inputFiles, outputPath, scheme): outputHeader = outputPath + '.h' outputSource = outputPath + '.cpp' outputConversionHeaderFrom = outputPath + '-conversion-from.h' outputConversionSourceFrom = outputPath + '-conversion-from.cpp' outputConversionHeaderTo = outputPath + '-conversion-to.h' outputConversionSourceTo = outputPath + '-conversion-to.cpp' outputSerializationHeader = outputPath + '-dump_to_text.h' outputSerializationSource = outputPath + '-dump_to_text.cpp' outputHeaderBasename = os.path.basename(outputHeader) outputConversionHeaderFromBasename = os.path.basename(outputConversionHeaderFrom) outputConversionHeaderToBasename = os.path.basename(outputConversionHeaderTo) outputSerializationHeaderBasename = os.path.basename(outputSerializationHeader) prefixes = scheme.get('prefixes', {}) dataPrefix = prefixes.get('data', '') typePrefix = prefixes.get('type', '') idPrefix = prefixes.get('id') + '_' constructPrefix = prefixes.get('construct') def normalizedName(name): return name.replace('.', '_') def normalizedBareName(name): full = re.match(r'^([a-zA-Z0-9])+\.([A-Z][a-zA-Z0-9]+)$', name) if (full): return full.group(1) + '_' + full.group(2)[0:1].lower() + full.group(2)[1:] elif name.find('.') >= 0: raise ValueError('Bad name: ' + name) return name[0:1].lower() + name[1:] def fullTypeName(name): return typePrefix + normalizedName(name) def fullBareTypeName(name): return typePrefix + normalizedBareName(name) def fullDataName(name): return dataPrefix + normalizedName(name) def optionalInVector(name): templ = re.match(r'(.*?)([vV]ector<)([A-Za-z0-9_]+)>$', name) if templ: return templ.group(1) + templ.group(2) + 'std::optional<' + templ.group(3) + '>>'; else: raise ValueError('Bad optional vector: ' + name) def handleTemplate(name, process = fullTypeName): templ = re.match(r'^([vV]ector<)([A-Za-z0-9\._<>]+)>$', name) if (templ): vectemplate = templ.group(2) if (vectemplate.find('<') >= 0): return templ.group(1) + process(handleTemplate(vectemplate, process)) + '>' elif (re.match(r'^[A-Z]', vectemplate) or re.match(r'^[a-zA-Z0-9]+\.[A-Z]', vectemplate)): return templ.group(1) + process(vectemplate) + '>' elif (vectemplate in builtinTypes): return templ.group(1) + process(vectemplate) + '>' else: foundmeta = '' for metatype in typesDict: for typedata in typesDict[metatype]: if (typedata[0] == normalizedName(vectemplate)): foundmeta = metatype break if (len(foundmeta) > 0): break if (len(foundmeta) > 0): return templ.group(1) + process(foundmeta) + '>' else: raise ValueError('Bad vector param: ' + vectemplate) else: raise ValueError('Bad template type: ' + name) namespaces = scheme.get('namespaces') globalNamespace = namespaces.get('global', '') creatorNamespace = namespaces.get('creator', '') creatorNamespaceFull = (globalNamespace + '::' if creatorNamespace != '' and globalNamespace != '' else globalNamespace) + creatorNamespace # this is a map (key flags -> map (flag name -> flag bit)) # each key flag of parentFlags should be a subset of the value flag here parentFlagsCheck = {} flagInheritance = scheme.get('flagInheritance', {}) typeIdExceptions = scheme.get('typeIdExceptions', []) renamedTypes = scheme.get('renamedTypes', {}) skipLines = scheme.get('skip', []) builtinTypes = scheme.get('builtin', []) builtinTemplateTypes = scheme.get('builtinTemplates', []) builtinInclude = scheme.get('builtinInclude', '') nullableTypes = scheme.get('nullable', []) synonyms = scheme.get('synonyms', {}) writeSections = scheme.get('sections', []) readWriteSection = 'read-write' in writeSections primitiveTypeNames = scheme.get('types') typeIdType = primitiveTypeNames.get('typeId') primeType = primitiveTypeNames.get('prime', '') bufferType = primitiveTypeNames.get('buffer', '') writeConversion = 'conversion' in scheme optimizeSingleData = 'optimizeSingleData' in scheme conversionScheme = scheme.get('conversion', {}) conversionInclude = conversionScheme.get('include') if writeConversion else '' conversionNamespace = conversionScheme.get('namespace') if writeConversion else '' conversionBuiltinTypes = conversionScheme.get('builtinAdditional', []) conversionBuiltinIncludeFrom = conversionScheme.get('builtinIncludeFrom', '') conversionBuiltinIncludeTo = conversionScheme.get('builtinIncludeTo', '') def conversionName(name): return '::' + conversionNamespace + '::' + name def conversionTemplate(name, param): return conversionName(name) + '<' + conversionName(param) + '>' def conversionPointer(name): return conversionTemplate('object_ptr', name) def conversionMake(name): return conversionTemplate('make_object', name) def conversionMove(name): return conversionTemplate('move_object_as', name) writeSerialization = 'dumpToText' in scheme serializationScheme = scheme.get('dumpToText', {}) serializationInclude = serializationScheme.get('include') if writeSerialization else '' if primeType == '' or bufferType == '': if readWriteSection or writeSerialization: raise ValueError('Required types not provided.') def isBuiltinType(name): return name in builtinTypes or name in builtinTemplateTypes funcs = 0 types = 0 consts = 0 funcsNow = 0 enums = [] funcsDict = {} funcsList = [] typesDict = {} TypesDict = {} typesList = [] TypeConstructors = {} boxed = {} funcsText = '' typesText = '' dataTexts = '' creatorProxyText = '' factories = '' flagOperators = '' methods = '' inlineMethods = '' visitorMethods = '' textSerializeInit = '' textSerializeMethods = '' forwards = '' forwTypedefs = '' conversionHeaderFrom = '' conversionHeaderTo = '' conversionSourceFrom = '' conversionSourceTo = '' accumulatedComments = '' lines, layer, names = readInputs(inputFiles) inputNames = '\'' + '\', \''.join(names) + '\'' for line in lines: comment = '' nocomment = re.match(r'^(.*?)//(.*?)$', line) if (nocomment): line = nocomment.group(1) comment = nocomment.group(2) if (re.match(r'\-\-\-functions\-\-\-', line)): funcsNow = 1 continue if (re.match(r'\-\-\-types\-\-\-', line)): funcsNow = 0 continue if (re.match(r'^\s*$', line)): if not nocomment: accumulatedComments = '' elif comment != '': accumulatedComments += ' ' + comment continue if line.strip() in skipLines: continue nametype = re.match(r'([a-zA-Z\.0-9_]+)(#[0-9a-f]+)?([^=]*)=\s*([a-zA-Z\.<>0-9_]+);', line) if (not nametype): raise ValueError('Bad line found: ' + line) comments = accumulatedComments accumulatedComments = '' if isBotsOnlyLine(comments) or isMobileOnlyLine(comments): continue originalname = nametype.group(1) name = originalname if (name in renamedTypes): name = renamedTypes[name] nameInd = name.rfind('.') if (nameInd >= 0): Name = name[0:nameInd] + '_' + name[nameInd + 1:nameInd + 2].upper() + name[nameInd + 2:] name = normalizedName(name) else: Name = name[0:1].upper() + name[1:] typeid = nametype.group(2) if (typeid and len(typeid) > 0): typeid = typeid[1:]; # Skip '#' while (typeid and len(typeid) > 0 and typeid[0] == '0'): typeid = typeid[1:] cleanline = nametype.group(1) + nametype.group(3) + '= ' + nametype.group(4) cleanline = re.sub(r' [a-zA-Z0-9_]+\:flags2?\.[0-9]+\?true', '', cleanline) cleanline = cleanline.replace('<', ' ').replace('>', ' ').replace(' ', ' ') cleanline = re.sub(r'^ ', '', cleanline) cleanline = re.sub(r' $', '', cleanline) for synonym in synonyms: synonymOf = synonyms[synonym] cleanline = cleanline.replace(':' + synonym + ' ', ':' + synonymOf + ' ') cleanline = cleanline.replace('?' + synonym + ' ', '?' + synonymOf + ' ') cleanline = cleanline.replace('{', '') cleanline = cleanline.replace('}', '') countTypeId = binascii.crc32(binascii.a2b_qp(cleanline)) if (countTypeId < 0): countTypeId += 2 ** 32 countTypeId = re.sub(r'^0x|L$', '', hex(countTypeId)) if (typeid and len(typeid) > 0): typeid = typeid if (typeid != countTypeId): key = originalname + '#' + typeid if (not key in typeIdExceptions): print('Warning: counted ' + countTypeId + ' mismatch with provided ' + typeid + ' (' + key + ', ' + cleanline + ')') continue else: typeid = countTypeId typeid = '0x' + typeid params = nametype.group(3) restype = nametype.group(4) if (restype.find('<') >= 0): restype = handleTemplate(restype) resType = normalizedName(restype) if (restype.find('.') >= 0): parts = re.match(r'([a-zA-Z0-9\.]+)\.([A-Z][A-Za-z0-9<>_]+)', restype) if (parts): restype = parts.group(1).replace('.', '_') + '_' + parts.group(2)[0:1].lower() + parts.group(2)[1:] else: raise ValueError('Bad result type name with dot: ' + restype) else: if (re.match(r'^[A-Z]', restype)): restype = restype[:1].lower() + restype[1:] else: raise ValueError('Bad result type name: ' + restype) boxed[resType] = restype boxed[Name] = name enums.append('\t' + idPrefix + name + ' = ' + typeid) paramsList = params.strip().split(' ') prms = {} conditions = {} trivialConditions = {} # true type nullablePrms = {} nullableVectors = {} botsOnlyPrms = {} prmsList = [] conditionsList = [] isTemplate = hasFlags = hasFlags64 = hasTemplate = '' for param in paramsList: if (re.match(r'^\s*$', param)): continue templ = re.match(r'^{([A-Za-z]+):Type}$', param) if (templ): hasTemplate = templ.group(1) continue pnametype = re.match(r'([a-zA-Z_][a-zA-Z0-9_]*):([A-Za-z0-9<>\._]+|![a-zA-Z]+|\#|[a-z_][a-z0-9_]*\.[0-9]+\?[A-Za-z0-9<>\._]+)$', param) if (not pnametype): raise ValueError('Bad param found: "' + param + '" in line: ' + line) pname = pnametype.group(1) ptypewide = pnametype.group(2) botsOnlyPrm = isBotsOnlyParam(comments, pname) nullableVector = not botsOnlyPrm and isNullableVector(comments, pname) nullablePrm = not botsOnlyPrm and not nullableVector and isNullableParam(comments, pname) if (re.match(r'^!([a-zA-Z]+)$', ptypewide)): if ('!' + hasTemplate == ptypewide): isTemplate = pname ptype = 'TQueryType' else: raise ValueError('Bad template param name: "' + param + '" in line: ' + line) if nullablePrm or nullableVector: raise ValueError('Template param should not be nullable: "' + param + '" in line: ' + line + ', comments: ' + comments) elif (ptypewide == '#'): if hasFlags != '' and pname == hasFlags + '2': hasFlags64 = pname continue hasFlags = pname if funcsNow: ptype = 'flags<' + fullTypeName(name) + '::Flags>' else: ptype = 'flags<' + fullDataName(name) + '::Flags>' if nullablePrm or nullableVector: raise ValueError('Flags param should not be nullable: "' + param + '" in line: ' + line + ', comments: ' + comments) else: ptype = ptypewide if botsOnlyPrm: botsOnlyPrms[pname] = 1 if (ptype.find('?') >= 0): pmasktype = re.match(r'([a-z_][a-z0-9_]*)\.([0-9]+)\?([A-Za-z0-9<>\._]+)', ptype) if not pmasktype: raise ValueError('Bad param found: "' + param + '" in line: ' + line) flagsName = pmasktype.group(1) if flagsName != hasFlags and flagsName != hasFlags64: raise ValueError('Bad param found: "' + param + '" in line: ' + line) if nullablePrm or nullableVector: raise ValueError('Conditional param should not be nullable: "' + param + '" in line: ' + line + ', comments: ' + comments) ptype = pmasktype.group(3) if (ptype.find('<') >= 0): if not readWriteSection: ptype = handleTemplate(ptype, fullBareTypeName) else: ptype = handleTemplate(ptype) if (not pname in conditions): conditionsList.append(pname) if flagsName == hasFlags64: conditions[pname] = str(int(pmasktype.group(2)) + 32) else: conditions[pname] = pmasktype.group(2) if (ptype == 'true'): trivialConditions[pname] = flagsName elif (ptype.find('<') >= 0): if nullablePrm: raise ValueError('Vector param should not be nullable: "' + param + '" in line: ' + line + ', comments: ' + comments) if nullableVector: nullableVectors[pname] = 1 if not readWriteSection: ptype = handleTemplate(ptype, fullBareTypeName) else: ptype = handleTemplate(ptype) elif nullableVector: raise ValueError('Non-vector param should not be vector-nullable: "' + param + '" in line: ' + line + ', comments: ' + comments) elif nullablePrm: nullablePrms[pname] = 1 prmsList.append(pname) if not readWriteSection: normalizedType = normalizedBareName(ptype) else: normalizedType = normalizedName(ptype) if (normalizedType in TypeConstructors): prms[pname] = TypeConstructors[normalizedType]['typeBare'] else: prms[pname] = normalizedType if (isTemplate == '' and resType == 'X'): raise ValueError('Bad response type "X" in "' + name +'" in line: ' + line) if funcsNow: methodBodies = '' if (isTemplate != ''): funcsText += '\ntemplate ' funcsText += '\nclass ' + fullTypeName(name) + ' { // RPC method \'' + nametype.group(1) + '\'\n'; # class funcsText += 'public:\n' prmsStr = [] prmsInit = [] prmsNames = [] if hasFlags != '': flagsType = 'uint64' if hasFlags64 != '' else 'uint32' flagsBit = '1ULL' if hasFlags64 != '' else '1U' funcsText += '\tenum class Flag : ' + flagsType + ' {\n' maxbit = 0 parentFlagsCheck[fullTypeName(name)] = {} for paramName in conditionsList: funcsText += '\t\tf_' + paramName + ' = (' + flagsBit + ' << ' + conditions[paramName] + '),\n' parentFlagsCheck[fullTypeName(name)][paramName] = conditions[paramName] maxbit = max(maxbit, int(conditions[paramName])) if (maxbit > 0): funcsText += '\n' funcsText += '\t\tMAX_FIELD = (' + flagsBit + ' << ' + str(maxbit) + '),\n' funcsText += '\t};\n' funcsText += '\tusing Flags = base::flags;\n' funcsText += '\tfriend inline constexpr bool is_flag_type(Flag) { return true; };\n' funcsText += '\n' if (len(prms) > len(trivialConditions) + len(botsOnlyPrms)): for paramName in prmsList: if (paramName in trivialConditions or paramName in botsOnlyPrms): continue paramType = prms[paramName] prmsInit.append('_' + paramName + '(' + paramName + '_)') prmsNames.append(paramName + '_') if (paramName == isTemplate): ptypeFull = paramType else: ptypeFull = fullTypeName(paramType) if (paramType in ['int', 'Int', 'bool', 'Bool', 'flags', 'long', 'int32', 'int53', 'int64', 'double']): prmsStr.append(ptypeFull + ' ' + paramName + '_') elif paramName in nullableVectors: prmsStr.append('const ' + optionalInVector(ptypeFull) + ' &' + paramName + '_') elif paramName in nullablePrms: prmsStr.append('const std::optional<' + ptypeFull + '> &' + paramName + '_') else: prmsStr.append('const ' + ptypeFull + ' &' + paramName + '_') funcsText += '\t' + fullTypeName(name) + '();\n';# = default; # constructor if (isTemplate != ''): methodBodies += 'template \n' methodBodies += fullTypeName(name) + '::' + fullTypeName(name) + '() = default;\n' else: methodBodies += fullTypeName(name) + '::' + fullTypeName(name) + '() = default;\n' if (len(prms) > len(trivialConditions) + len(botsOnlyPrms)): explicitText = 'explicit ' if (len(prms) - len(trivialConditions) - len(botsOnlyPrms) == 1) else '' funcsText += '\t' + explicitText + fullTypeName(name) + '(' + ', '.join(prmsStr) + ');\n' if (isTemplate != ''): methodBodies += 'template \n' methodBodies += fullTypeName(name) + '::' + fullTypeName(name) + '(' + ', '.join(prmsStr) + ') : ' + ', '.join(prmsInit) + ' {\n}\n' else: methodBodies += fullTypeName(name) + '::' + fullTypeName(name) + '(' + ', '.join(prmsStr) + ') : ' + ', '.join(prmsInit) + ' {\n}\n' funcsText += '\t' + typeIdType + ' type() const {\n\t\treturn ' + idPrefix + name + ';\n\t}\n'; # type id if readWriteSection: funcsText += '\n' funcsText += '\ttemplate \n' funcsText += '\t[[nodiscard]] bool read(const Prime *&from, const Prime *end, ' + typeIdType + ' cons = ' + idPrefix + name + ');\n'; # read method if (isTemplate != ''): methodBodies += 'template \n' methodBodies += 'template \n' methodBodies += 'bool ' + fullTypeName(name) + '::read(const Prime *&from, const Prime *end, ' + typeIdType + ' cons) {\n' else: methodBodies += 'template \n' methodBodies += 'bool ' + fullTypeName(name) + '::read(const Prime *&from, const Prime *end, ' + typeIdType + ' cons) {\n' readFunc = '' for k in prmsList: v = prms[k] if k in conditionsList: if not k in trivialConditions: readFunc += '\t\t&& ((_' + hasFlags + '.v & Flag::f_' + k + ') ? _' + k + '.read(from, end) : ((_' + k + ' = ' + fullTypeName(v) + '()), true))\n' else: readFunc += '\t\t&& _' + k + '.read(from, end)\n' if readFunc != '': methodBodies += '\treturn' + readFunc[4:len(readFunc)-1] + ';\n' else: methodBodies += '\treturn true;\n' methodBodies += '}\n' if isTemplate == '': methodBodies += 'template bool ' + fullTypeName(name) + '::read<' + primeType + '>(const ' + primeType + ' *&from, const ' + primeType + ' *end, ' + typeIdType + ' cons);\n' funcsText += '\ttemplate \n' funcsText += '\tvoid write(Accumulator &to) const;\n'; # write method if (isTemplate != ''): methodBodies += 'template \n' methodBodies += 'template \n' methodBodies += 'void ' + fullTypeName(name) + '::write(Accumulator &to) const {\n' else: methodBodies += 'template \n' methodBodies += 'void ' + fullTypeName(name) + '::write(Accumulator &to) const {\n' for k in prmsList: if k in conditionsList: if not k in trivialConditions: methodBodies += '\tif (_' + hasFlags + '.v & Flag::f_' + k + ') _' + k + '.write(to);\n' else: methodBodies += '\t_' + k + '.write(to);\n' methodBodies += '}\n' if isTemplate == '': methodBodies += 'template void ' + fullTypeName(name) + '::write<' + bufferType + '>(' + bufferType + ' &to) const;\n' methodBodies += 'template void ' + fullTypeName(name) + '::write<::tl::details::LengthCounter>(::tl::details::LengthCounter &to) const;\n' if writeConversion: conversionSourceTo += '\n\ template <>\n\ ExternalGenerator tl_to_generator('+ fullTypeName(name) + ' &&request) {\n\ \treturn [value = std::move(request)]() -> ExternalRequest {\n\ \t\treturn new ' + conversionName(name) + '(' conversionArguments = [] for k in prmsList: prmsTypeBare = prms[k] if (k in conditionsList): raise ValueError('Conversion with flags :(') elif k in botsOnlyPrms: conversionArguments.append('{}') elif prmsTypeBare in builtinTypes or prmsTypeBare == 'bool': conversionArguments.append('tl_to_simple(value.v' + k + '())') elif prmsTypeBare.find('<') >= 0: if (k in nullableVectors): conversionArguments.append('tl_to_vector_optional(value.v' + k + '())') else: conversionArguments.append('tl_to_vector(value.v' + k + '())') else: if (k in nullablePrms): conversionValue = 'value.v' + k + '() ? tl_to(*value.v' + k + '()) : nullptr' else: conversionValue = 'tl_to(value.v' + k + '())' if len(typesDict[prmsTypeBare]) > 1: conversionArguments.append('::td::td_api::object_ptr<' + conversionName(prmsTypeBare[0:1].upper() + prmsTypeBare[1:]) + '>(' + conversionValue + ')') else: conversionArguments.append('::td::td_api::object_ptr<' + conversionName(prmsTypeBare) + '>(' + conversionValue + ')') conversionSourceTo += ', '.join(conversionArguments) + ');\n\ \t};\n\ }\n' if len(prmsList) > 0: funcsText += '\n' for paramName in prmsList: # getters if (paramName in trivialConditions or paramName in botsOnlyPrms): continue paramType = prms[paramName] ptypeFull = fullTypeName(paramType) if (paramName in conditions): funcsText += '\t[[nodiscard]] tl::conditional<' + ptypeFull + '> v' + paramName + '() const;\n' methodBodies += 'tl::conditional<' + ptypeFull + '> ' + fullTypeName(name) + '::v' + paramName + '() const {\n' methodBodies += '\treturn (_' + hasFlags + '.v & Flag::f_' + paramName + ') ? &_' + paramName + ' : nullptr;\n' methodBodies += '}\n' elif (paramName in nullableVectors): funcsText += '\t[[nodiscard]] const ' + optionalInVector(ptypeFull) + ' &v' + paramName + '() const;\n' methodBodies += 'const ' + optionalInVector(ptypeFull) + ' &' + fullTypeName(name) + '::v' + paramName + '() const {\n' methodBodies += '\treturn _' + paramName + ';\n' methodBodies += '}\n' elif (paramName in nullablePrms): funcsText += '\t[[nodiscard]] tl::conditional<' + ptypeFull + '> v' + paramName + '() const;\n' methodBodies += 'tl::conditional<' + ptypeFull + '> ' + fullTypeName(name) + '::v' + paramName + '() const {\n' methodBodies += '\treturn _' + paramName + ' ? &*_' + paramName + ' : nullptr;\n' methodBodies += '}\n' else: funcsText += '\t[[nodiscard]] const ' + ptypeFull + ' &v' + paramName + '() const;\n' methodBodies += 'const ' + ptypeFull + ' &' + fullTypeName(name) + '::v' + paramName + '() const {\n' methodBodies += '\treturn _' + paramName + ';\n' methodBodies += '}\n' if (isTemplate != ''): funcsText += '\n\tusing ResponseType = typename TQueryType::ResponseType;\n\n' inlineMethods += methodBodies else: # method return type if not readWriteSection: funcsText += '\n\tusing ResponseType = ' + fullTypeName(restype) + ';\n\n' else: funcsText += '\n\tusing ResponseType = ' + fullTypeName(resType) + ';\n\n' methods += methodBodies if (len(prms) > len(trivialConditions) + len(botsOnlyPrms)): funcsText += 'private:\n' for paramName in prmsList: if (paramName in trivialConditions or paramName in botsOnlyPrms): continue paramType = prms[paramName] if (paramName == isTemplate): ptypeFull = paramType else: ptypeFull = fullTypeName(paramType) if (paramName in nullableVectors): funcsText += '\t' + optionalInVector(ptypeFull) + ' _' + paramName + ';\n' elif (paramName in nullablePrms): funcsText += '\tstd::optional<' + ptypeFull + '> _' + paramName + ';\n' else: funcsText += '\t' + ptypeFull + ' _' + paramName + ';\n' funcsText += '\n' funcsText += '};\n'; # class ending if readWriteSection: if (isTemplate != ''): funcsText += 'template \n' funcsText += 'using ' + fullTypeName(Name) + ' = tl::boxed<' + fullTypeName(name) + '>;\n' else: funcsText += 'using ' + fullTypeName(Name) + ' = tl::boxed<' + fullTypeName(name) + '>;\n' funcs = funcs + 1 if (not restype in funcsDict): funcsList.append(restype) funcsDict[restype] = [] # TypesDict[restype] = resType funcsDict[restype].append([name, typeid, prmsList, prms, hasFlags, hasFlags64, conditionsList, conditions, trivialConditions, isTemplate, nullablePrms, nullableVectors, botsOnlyPrms]) else: if (isTemplate != ''): print('Template types not allowed: "' + resType + '" in line: ' + line) continue if (not restype in typesDict): typesList.append(restype) typesDict[restype] = [] TypesDict[restype] = resType typesDict[restype].append([name, typeid, prmsList, prms, hasFlags, hasFlags64, conditionsList, conditions, trivialConditions, isTemplate, nullablePrms, nullableVectors, botsOnlyPrms]) TypeConstructors[name] = {'typeBare': restype, 'typeBoxed': resType} consts = consts + 1 if readWriteSection: for typeName in builtinTypes: forwTypedefs += 'using ' + fullTypeName(typeName[:1].upper() + typeName[1:]) + ' = tl::boxed<' + fullTypeName(typeName) + '>;\n' for typeName in builtinTemplateTypes: forwTypedefs += 'template \n' forwTypedefs += 'using ' + fullTypeName(typeName[:1].upper() + typeName[1:]) + ' = tl::boxed<' + fullTypeName(typeName) + '>;\n' if writeSerialization: textSerializeMethods += addTextSerialize(typesList, typesDict, typesDict, idPrefix, primeType, boxed, dataPrefix) textSerializeInit += addTextSerializeInit(typesList, typesDict, idPrefix) + '\n' textSerializeMethods += addTextSerialize(funcsList, funcsDict, typesDict, idPrefix, primeType, boxed, typePrefix) textSerializeInit += addTextSerializeInit(funcsList, funcsDict, idPrefix) + '\n' for restype in typesList: v = typesDict[restype] resType = TypesDict[restype] withData = 0 creatorsDeclarations = '' creatorsBodies = '' flagDeclarations = '' constructsText = '' constructsBodies = '' forwards += 'class ' + fullTypeName(restype) + ';\n' if readWriteSection: forwTypedefs += 'using ' + fullTypeName(resType) + ' = tl::boxed<' + fullTypeName(restype) + '>;\n' withType = (len(v) > 1) nullable = restype in nullableTypes switchLines = '' friendDecl = '' getters = '' visitor = '' reader = '' writer = '' newFast = '' if writeConversion: if not restype in builtinTypes and not restype in conversionBuiltinTypes: if len(v) > 1: conversionType = resType conversionHeaderFrom += 'template <>\n' + fullTypeName(restype) + ' tl_from<' + fullTypeName(restype) + '>(ExternalResponse response);\n' conversionSourceFrom += '\ntemplate <>\n' + fullTypeName(restype) + ' tl_from<' + fullTypeName(restype) + '>(ExternalResponse response) {\n' if nullable: conversionSourceFrom += '\tif (!response) {\n\t\treturn nullptr;\n\t}\n\n' else: conversionSourceFrom += '\tExpects(response != nullptr);\n\n' conversionSourceFrom += '\tswitch (response->get_id()) {\n' for data in v: name = data[0] prmsList = data[2] prms = data[3] trivialConditions = data[8] isTemplate = data[9] nullablePrms = data[10] nullableVectors = data[11] botsOnlyPrms = data[12] conversionSourceFrom += '\tcase ' + conversionName(name) + '::ID: ' if (len(prmsList) == 0): conversionSourceFrom += 'return ' + constructPrefix + name + '();\n' else: conversionSourceFrom += '{\n\t\tconst auto specific = static_cast(response);\n' conversionSourceFrom += '\t\treturn ' + constructPrefix + name + '(' conversionArguments = [] for k in prmsList: prmsTypeBare = prms[k] ptypeFullBare = fullTypeName(prmsTypeBare) if k in conditionsList: raise ValueError('Conversion with flags :(') elif k in botsOnlyPrms: continue elif prmsTypeBare == 'string': conversionArguments.append('tl_from_string(specific->' + k + '_)') elif prmsTypeBare in builtinTypes or prmsTypeBare == 'bool': conversionArguments.append('tl_from_simple(specific->' + k + '_)') elif prmsTypeBare.find('<') >= 0: if k in nullableVectors: conversionArguments.append('tl_from_vector_optional<' + ptypeFullBare + '>(specific->' + k + '_)') else: conversionArguments.append('tl_from_vector<' + ptypeFullBare + '>(specific->' + k + '_)') else: if k in nullablePrms: conversionArguments.append('specific->' + k + '_.get() ? std::make_optional(tl_from<' + ptypeFullBare + '>(specific->' + k + '_.get())) : std::nullopt') else: conversionArguments.append('tl_from<' + ptypeFullBare + '>(specific->' + k + '_.get())') conversionSourceFrom += ', '.join(conversionArguments) + ');\n' conversionSourceFrom += '\t} break;\n' conversionSourceFrom += '\tdefault: Unexpected("Type in ' + fullTypeName(restype) + ' tl_from.");\n\t}\n}\n' else: conversionType = v[0][0] conversionHeaderTo += conversionName(conversionType) + ' *tl_to(const ' + fullTypeName(restype) + ' &value);\n' conversionSourceTo += '\n' + conversionName(conversionType) + ' *tl_to(const ' + fullTypeName(restype) + ' &value) {\n' if withType: conversionSourceTo += '\tswitch (value.type()) {\n' if nullable: conversionSourceTo += '\tcase ' + typeIdType + '(0): return nullptr;\n' for data in v: name = data[0] prms = data[3] trivialConditions = data[8] isTemplate = data[9] nullablePrms = data[10] nullableVectors = data[11] botsOnlyPrms = data[12] if (len(prms) == len(trivialConditions)): conversionSourceTo += '\tcase ' + idPrefix + name + ': return new ' + conversionName(name) + '();\n' else: conversionSourceTo += '\tcase ' + idPrefix + name + ': return tl_to(value.c_' + name + '());\n' conversionSourceTo += '\tdefault: Unexpected("Type in tl_to(' + fullTypeName(restype) + ').");\n\t}\n' else: if nullable: conversionSourceTo += '\tif (!value) {\n\t\treturn nullptr;\n\t}\n' conversionSourceTo += '\treturn tl_to(value.c_' + v[0][0] + '());\n' conversionSourceTo += '}\n' for data in v: name = data[0] typeid = data[1] prmsList = data[2] prms = data[3] hasFlags = data[4] hasFlags64 = data[5] conditionsList = data[6] conditions = data[7] trivialConditions = data[8] isTemplate = data[9] nullablePrms = data[10] nullableVectors = data[11] botsOnlyPrms = data[12] dataText = '' if (len(prms) > len(trivialConditions) + len(botsOnlyPrms)): withData = 1 dataText += '\nclass ' + fullDataName(name) + ' : public tl::details::type_data {\n'; # data class else: dataText += '\nclass ' + fullDataName(name) + ' {\n'; # empty data class for visitors dataText += 'public:\n' dataText += '\ttemplate \n' dataText += '\tstatic constexpr bool Is() { return std::is_same_v, ' + fullDataName(name) + '>; };\n\n' creatorParams = [] creatorParamsList = [] readText = '' writeText = '' if (hasFlags != ''): flagsType = 'uint64' if hasFlags64 != '' else 'uint32' flagsBit = '1ULL' if hasFlags64 != '' else '1U' dataText += '\tenum class Flag : ' + flagsType + ' {\n' maxbit = 0 parentFlagsCheck[fullDataName(name)] = {} for paramName in conditionsList: dataText += '\t\tf_' + paramName + ' = (' + flagsBit + ' << ' + conditions[paramName] + '),\n' parentFlagsCheck[fullDataName(name)][paramName] = conditions[paramName] maxbit = max(maxbit, int(conditions[paramName])) if (maxbit > 0): dataText += '\n' dataText += '\t\tMAX_FIELD = (' + flagsBit + ' << ' + str(maxbit) + '),\n' dataText += '\t};\n' dataText += '\tusing Flags = base::flags;\n' dataText += '\tfriend inline constexpr bool is_flag_type(Flag) { return true; };\n' dataText += '\n' if (len(conditions)): for paramName in conditionsList: if paramName in trivialConditions: dataText += '\t[[nodiscard]] bool is_' + paramName + '() const;\n' constructsBodies += 'bool ' + fullDataName(name) + '::is_' + paramName + '() const {\n' constructsBodies += '\treturn _' + hasFlags + '.v & Flag::f_' + paramName + ';\n' constructsBodies += '}\n' dataText += '\n' switchLines += '\tcase ' + idPrefix + name + ': '; # for by-type-id type constructor getters += '\t[[nodiscard]] const ' + fullDataName(name) + ' &c_' + name + '() const;\n'; # const getter if optimizeSingleData and withData and not withType: getters += '\t[[nodiscard]] const ' + fullDataName(name) + ' &data() const;\n'; visitor += '\tcase ' + idPrefix + name + ': return base::match_method(c_' + name + '(), std::forward(method), std::forward(methods)...);\n' forwards += 'class ' + fullDataName(name) + ';\n'; # data class forward declaration if (len(prms) > len(trivialConditions) + len(botsOnlyPrms)): dataText += '\t' + fullDataName(name) + '();\n'; # default constructor switchLines += 'setData(new ' + fullDataName(name) + '()); ' constructsBodies += fullDataName(name) + '::' + fullDataName(name) + '() = default;\n' constructsBodies += 'const ' + fullDataName(name) + ' &' + fullTypeName(restype) + '::c_' + name + '() const {\n' if (withType): constructsBodies += '\tExpects(_type == ' + idPrefix + name + ');\n\n' constructsBodies += '\treturn queryData<' + fullDataName(name) + '>();\n' constructsBodies += '}\n' if optimizeSingleData and withData and not withType: constructsBodies += 'const ' + fullDataName(name) + ' &' + fullTypeName(restype) + '::data() const {\n' constructsBodies += '\treturn queryData<' + fullDataName(name) + '>();\n' constructsBodies += '}\n' constructsText += '\texplicit ' + fullTypeName(restype) + '(const ' + fullDataName(name) + ' *data);\n'; # by-data type constructor constructsBodies += fullTypeName(restype) + '::' + fullTypeName(restype) + '(const ' + fullDataName(name) + ' *data) : type_owner(data)' if (withType): constructsBodies += ', _type(' + idPrefix + name + ')' constructsBodies += ' {\n}\n' dataText += '\t' + fullDataName(name) + '('; # params constructor prmsStr = [] prmsInit = [] for paramName in prmsList: if (paramName in trivialConditions or paramName in botsOnlyPrms): continue paramType = prms[paramName] ptypeFull = fullTypeName(paramType) if (paramType in ['int', 'Int', 'bool', 'Bool', 'flags', 'long', 'int32', 'int53', 'int64', 'double']): prmsStr.append(ptypeFull + ' ' + paramName + '_') creatorParams.append(ptypeFull + ' ' + paramName + '_') elif paramName in nullableVectors: prmsStr.append('const ' + optionalInVector(ptypeFull) + ' &' + paramName + '_') creatorParams.append('const ' + optionalInVector(ptypeFull) + ' &' + paramName + '_') elif paramName in nullablePrms: prmsStr.append('const std::optional<' + ptypeFull + '> &' + paramName + '_') creatorParams.append('const std::optional<' + ptypeFull + '> &' + paramName + '_') else: prmsStr.append('const ' + ptypeFull + ' &' + paramName + '_') creatorParams.append('const ' + ptypeFull + ' &' + paramName + '_') creatorParamsList.append(paramName + '_') prmsInit.append('_' + paramName + '(' + paramName + '_)') if withType: writeText += '\t' if (paramName in conditions): readText += '\t\t&& (v' + paramName + '() ? _' + paramName + '.read(from, end) : ((_' + paramName + ' = ' + fullTypeName(paramType) + '()), true))\n' writeText += '\tif (const auto v' + paramName + ' = v.v' + paramName + '()) v' + paramName + '->write(to);\n' else: readText += '\t\t&& _' + paramName + '.read(from, end)\n' writeText += '\tv.v' + paramName + '().write(to);\n' dataText += ', '.join(prmsStr) + ');\n' constructsBodies += fullDataName(name) + '::' + fullDataName(name) + '(' + ', '.join(prmsStr) + ') : ' + ', '.join(prmsInit) + ' {\n}\n' if readWriteSection: dataText += '\n' dataText += '\t[[nodiscard]] bool read(const ' + primeType + ' *&from, const ' + primeType + ' *end);\n' constructsBodies += 'bool ' + fullDataName(name) + '::read(const ' + primeType + ' *&from, const ' + primeType + ' *end) {\n' if readText != '': constructsBodies += '\treturn' + readText[4:len(readText)-1] + ';\n' else: constructsBodies += '\treturn true;\n' constructsBodies += '}\n' dataText += '\n' if len(prmsList) > 0: for paramName in prmsList: # getters if (paramName in trivialConditions or paramName in botsOnlyPrms): continue paramType = prms[paramName] ptypeFull = fullTypeName(paramType) if paramName in conditions: dataText += '\t[[nodiscard]] tl::conditional<' + ptypeFull + '> v' + paramName + '() const;\n' constructsBodies += 'tl::conditional<' + ptypeFull + '> ' + fullDataName(name) + '::v' + paramName + '() const {\n' constructsBodies += '\treturn (_' + hasFlags + '.v & Flag::f_' + paramName + ') ? &_' + paramName + ' : nullptr;\n' constructsBodies += '}\n' elif (paramName in nullableVectors): dataText += '\t[[nodiscard]] const ' + optionalInVector(ptypeFull) + ' &v' + paramName + '() const;\n' constructsBodies += 'const ' + optionalInVector(ptypeFull) + ' &' + fullDataName(name) + '::v' + paramName + '() const {\n' constructsBodies += '\treturn _' + paramName + ';\n' constructsBodies += '}\n' elif (paramName in nullablePrms): dataText += '\t[[nodiscard]] tl::conditional<' + ptypeFull + '> v' + paramName + '() const;\n' constructsBodies += 'tl::conditional<' + ptypeFull + '> ' + fullDataName(name) + '::v' + paramName + '() const {\n' constructsBodies += '\treturn _' + paramName + ' ? &*_' + paramName + ' : nullptr;\n' constructsBodies += '}\n' else: dataText += '\t[[nodiscard]] const ' + ptypeFull + ' &v' + paramName + '() const;\n' constructsBodies += 'const ' + ptypeFull + ' &' + fullDataName(name) + '::v' + paramName + '() const {\n' constructsBodies += '\treturn _' + paramName + ';\n' constructsBodies += '}\n' dataText += '\n' dataText += 'private:\n' for paramName in prmsList: # fields declaration paramType = prms[paramName] ptypeFull = fullTypeName(paramType) if (paramName in trivialConditions or paramName in botsOnlyPrms): continue elif (paramName in nullableVectors): dataText += '\t' + optionalInVector(ptypeFull) + ' _' + paramName + ';\n' elif (paramName in nullablePrms): dataText += '\tstd::optional<' + ptypeFull + '> _' + paramName + ';\n' else: dataText += '\t' + ptypeFull + ' _' + paramName + ';\n' dataText += '\n' newFast = 'new ' + fullDataName(name) + '()' else: constructsBodies += 'const ' + fullDataName(name) + ' &' + fullTypeName(restype) + '::c_' + name + '() const {\n' if (withType): constructsBodies += '\tExpects(_type == ' + idPrefix + name + ');\n\n' constructsBodies += '\tstatic const ' + fullDataName(name) + ' result;\n' constructsBodies += '\treturn result;\n' constructsBodies += '}\n' if writeConversion and not restype in builtinTypes and not restype in conversionBuiltinTypes: if (len(v) == 1): conversionHeaderFrom += 'template <>\n' + fullTypeName(restype) + ' tl_from<' + fullTypeName(restype) + '>(ExternalResponse response);\n' conversionSourceFrom += '\ntemplate <>\n' + fullTypeName(restype) + ' tl_from<' + fullTypeName(restype) + '>(ExternalResponse response) {\n' if nullable: conversionSourceFrom += '\tif (!response) {\n\t\treturn nullptr;\n\t}\n\n' else: conversionSourceFrom += '\tExpects(response != nullptr);\n' conversionSourceFrom += '\tExpects(response->get_id() == ' + conversionName(name) + '::ID);\n\n' if (len(prmsList) > 0): conversionSourceFrom += '\tconst auto specific = static_cast(response);\n' conversionSourceFrom += '\treturn ' + constructPrefix + normalizedName(name) + '(' conversionArguments = [] for k in prmsList: prmsTypeBare = prms[k] ptypeFullBare = fullTypeName(prmsTypeBare) if k in conditionsList: raise ValueError('Conversion with flags :(') elif k in botsOnlyPrms: continue elif prmsTypeBare == 'string': conversionArguments.append('tl_from_string(specific->' + k + '_)') elif prmsTypeBare in builtinTypes or prmsTypeBare == 'bool': conversionArguments.append('tl_from_simple(specific->' + k + '_)') elif prmsTypeBare.find('<') >= 0: if k in nullableVectors: conversionArguments.append('tl_from_vector_optional<' + ptypeFullBare + '>(specific->' + k + '_)') else: conversionArguments.append('tl_from_vector<' + ptypeFullBare + '>(specific->' + k + '_)') else: if k in nullablePrms: conversionArguments.append('specific->' + k + '_.get() ? std::make_optional(tl_from<' + ptypeFullBare + '>(specific->' + k + '_.get())) : std::nullopt') else: conversionArguments.append('tl_from<' + ptypeFullBare + '>(specific->' + k + '_.get())') conversionSourceFrom += ', '.join(conversionArguments) + ');\n' conversionSourceFrom += '}\n' conversionHeaderTo += conversionName(name) + ' *tl_to(const ' + fullDataName(name) + ' &value);\n' conversionSourceTo += '\n' + conversionName(name) + ' *tl_to(const ' + fullDataName(name) + ' &value) {\n' conversionSourceTo += '\treturn new ' + conversionName(name) + '(' conversionArguments = [] for k in prmsList: prmsTypeBare = prms[k] if (k in conditionsList): raise ValueError('Conversion with flags :(') elif k in botsOnlyPrms: conversionArguments.append('{}') elif prmsTypeBare in builtinTypes or prmsTypeBare == 'bool': conversionArguments.append('tl_to_simple(value.v' + k + '())') elif prmsTypeBare.find('<') >= 0: if (k in nullableVectors): conversionArguments.append('tl_to_vector_optional(value.v' + k + '())') else: conversionArguments.append('tl_to_vector(value.v' + k + '())') else: if (k in nullablePrms): conversionValue = 'value.v' + k + '() ? tl_to(*value.v' + k + '()) : nullptr' else: conversionValue = 'tl_to(value.v' + k + '())' if len(typesDict[prmsTypeBare]) > 1: conversionArguments.append('::td::td_api::object_ptr<' + conversionName(prmsTypeBare[0:1].upper() + prmsTypeBare[1:]) + '>(' + conversionValue + ')') else: conversionArguments.append('::td::td_api::object_ptr<' + conversionName(prmsTypeBare) + '>(' + conversionValue + ')') conversionSourceTo += ', '.join(conversionArguments) + ');\n}\n' switchLines += 'break;\n' dataText += '};\n'; # class ending dataTexts += dataText; # add data class if not friendDecl: friendDecl += '\tfriend class ::' + creatorNamespaceFull + '::TypeCreator;\n' creatorProxyText += '\tinline static ' + fullTypeName(restype) + ' new_' + name + '(' + ', '.join(creatorParams) + ') {\n' if len(prms) > len(trivialConditions): # creator with params creatorProxyText += '\t\treturn ' + fullTypeName(restype) + '(new ' + fullDataName(name) + '(' + ', '.join(creatorParamsList) + '));\n' else: if withType: # creator by type creatorProxyText += '\t\treturn ' + fullTypeName(restype) + '(' + idPrefix + name + ');\n' else: # single creator creatorProxyText += '\t\treturn ' + fullTypeName(restype) + '();\n' creatorProxyText += '\t}\n' creatorsDeclarations += fullTypeName(restype) + ' ' + constructPrefix + name + '(' + ', '.join(creatorParams) + ');\n' creatorsBodies += fullTypeName(restype) + ' ' + constructPrefix + name + '(' + ', '.join(creatorParams) + ') {\n' creatorsBodies += '\treturn ::' + creatorNamespaceFull + '::TypeCreator::new_' + name + '(' + ', '.join(creatorParamsList) + ');\n' creatorsBodies += '}\n' if (withType): reader += '\tcase ' + idPrefix + name + ': _type = cons; '; # read switch line if (len(prms) > len(trivialConditions)): reader += '{\n' reader += '\t\tif (const auto data = new ' + fullDataName(name) + '(); data->read(from, end)) {\n' reader += '\t\t\tsetData(data);\n' reader += '\t\t} else {\n' reader += '\t\t\tdelete data;\n' reader += '\t\t\treturn false;\n' reader += '\t\t}\n' reader += '\t} break;\n' writer += '\tcase ' + idPrefix + name + ': {\n'; # write switch line writer += '\t\tconst ' + fullDataName(name) + ' &v = c_' + name + '();\n' writer += writeText writer += '\t} break;\n' else: reader += 'break;\n' else: if (len(prms) > len(trivialConditions)): reader += '\tif (const auto data = new ' + fullDataName(name) + '(); data->read(from, end)) {\n' reader += '\t\tsetData(data);\n' reader += '\t} else {\n' reader += '\t\tdelete data;\n' reader += '\t\treturn false;\n' reader += '\t}\n' writer += '\tconst ' + fullDataName(name) + ' &v = c_' + name + '();\n' writer += writeText if nullable: if not withType and not withData: raise ValueError('No way to make a nullable non-data-owner non-type-distinct type') elif readWriteSection: raise ValueError('No way to make read-write code for a nullable type') forwards += '\n' typesText += '\nclass ' + fullTypeName(restype); # type class declaration if withData: typesText += ' : private tl::details::type_owner'; # if has data fields typesText += ' {\n' typesText += 'public:\n' typesText += '\t' + fullTypeName(restype) + '();\n'; # default constructor if withData and not withType: methods += '\n' + fullTypeName(restype) + '::' + fullTypeName(restype) + '() : type_owner(' + newFast + ') {\n}\n' else: methods += '\n' + fullTypeName(restype) + '::' + fullTypeName(restype) + '() = default;\n' if nullable: typesText += '\t' + fullTypeName(restype) + '(std::nullptr_t);\n' methods += fullTypeName(restype) + '::' + fullTypeName(restype) + '(std::nullptr_t) {\n}\n' typesText += '\n' if nullable: typesText += '\texplicit operator bool() const;\n' methods += fullTypeName(restype) + '::operator bool() const {\n\t' if withData: methods += '\treturn hasData();\n' else: methods += '\treturn _type != 0;\n' methods += '}\n' typesText += getters typesText += '\n' typesText += '\ttemplate \n' typesText += '\tdecltype(auto) match(Method &&method, Methods &&...methods) const;\n' visitorMethods += 'template \n' visitorMethods += 'decltype(auto) ' + fullTypeName(restype) + '::match(Method &&method, Methods &&...methods) const {\n' if withType: visitorMethods += '\tswitch (_type) {\n' visitorMethods += visitor visitorMethods += '\t}\n' visitorMethods += '\tUnexpected("Type in ' + fullTypeName(restype) + '::match.");\n' else: visitorMethods += '\treturn base::match_method(c_' + v[0][0] + '(), std::forward(method), std::forward(methods)...);\n' visitorMethods += '}\n\n' typesText += '\t' + typeIdType + ' type() const;\n'; # type id method methods += typeIdType + ' ' + fullTypeName(restype) + '::type() const {\n' if withType: if nullable: methods += '\treturn _type;\n' else: methods += '\tExpects(_type != 0);\n\n' methods += '\treturn _type;\n' else: if nullable: methods += '\treturn hasData() ? ' + idPrefix + v[0][0] + ' : ' + typeIdType + '(0);\n' else: methods += '\treturn ' + idPrefix + v[0][0] + ';\n' methods += '}\n' if readWriteSection: typesText += '\n' typesText += '\t[[nodiscard]] bool read(const ' + primeType + ' *&from, const ' + primeType + ' *end, ' + typeIdType + ' cons'; # read method if (not withType): typesText += ' = ' + idPrefix + name typesText += ');\n' methods += 'bool ' + fullTypeName(restype) + '::read(const ' + primeType + ' *&from, const ' + primeType + ' *end, ' + typeIdType + ' cons) {\n' if (withData): if not (withType): methods += '\tif (cons != ' + idPrefix + v[0][0] + ') return false;\n' if (withType): methods += '\tswitch (cons) {\n' methods += reader methods += '\tdefault: return false;\n' methods += '\t}\n' else: methods += reader methods += '\treturn true;\n' methods += '}\n' typesText += '\ttemplate \n' # write method typesText += '\tvoid write(Accumulator &to) const;\n' methods += 'template \n' methods += 'void ' + fullTypeName(restype) + '::write(Accumulator &to) const {\n' if (withType and writer != ''): methods += '\tswitch (_type) {\n' methods += writer methods += '\t}\n' else: methods += writer methods += '}\n' methods += 'template void ' + fullTypeName(restype) + '::write<' + bufferType + '>(' + bufferType + ' &to) const;\n' methods += 'template void ' + fullTypeName(restype) + '::write<::tl::details::LengthCounter>(::tl::details::LengthCounter &to) const;\n' typesText += '\n\tusing ResponseType = void;\n'; # no response types declared if optimizeSingleData: if withData and not withType: for data in v: name = data[0] typesText += '\tusing SingleDataType = ' + fullDataName(name) + ';\n' else: typesText += '\tusing SingleDataType = NotSingleDataTypePlaceholder;\n'; typesText += '\nprivate:\n'; # private constructors if (withType): # by-type-id constructor typesText += '\texplicit ' + fullTypeName(restype) + '(' + typeIdType + ' type);\n' methods += fullTypeName(restype) + '::' + fullTypeName(restype) + '(' + typeIdType + ' type) : ' methods += '_type(type)' methods += ' {\n' methods += '\tswitch (type) {\n'; # type id check methods += switchLines methods += '\tdefault: Unexpected("Type in ' + fullTypeName(restype) + '::' + fullTypeName(restype) + '.");\n' methods += '\t}\n' methods += '}\n'; # by-type-id constructor end if (withData): typesText += constructsText methods += constructsBodies if (friendDecl): typesText += '\n' + friendDecl if (withType): typesText += '\n\t' + typeIdType + ' _type = 0;\n'; # type field var typesText += '};\n'; # type class ended flagOperators += flagDeclarations factories += creatorsDeclarations methods += creatorsBodies if readWriteSection: typesText += 'using ' + fullTypeName(resType) + ' = tl::boxed<' + fullTypeName(restype) + '>;\n'; # boxed type definition flagOperators += '\n' for pureChildName in flagInheritance: childName = dataPrefix + pureChildName parentName = dataPrefix + flagInheritance[pureChildName] for flag in parentFlagsCheck[childName]: # # 'channelForbidden' has 'until_date' flag and 'channel' doesn't have it. # But as long as flags don't collide this is not a problem. # # if (not flag in parentFlagsCheck[parentName]): # raise ValueError('Flag ' + flag + ' not found in ' + parentName + ' which should be a flags-parent of ' + childName) # if (flag in parentFlagsCheck[parentName]): if (parentFlagsCheck[childName][flag] != parentFlagsCheck[parentName][flag]): raise ValueError('Flag ' + flag + ' has different value in ' + parentName + ' which should be a flags-parent of ' + childName) else: parentFlagsCheck[parentName][flag] = parentFlagsCheck[childName][flag] flagOperators += 'inline ' + parentName + '::Flags mtpCastFlags(' + childName + '::Flags flags) { return static_cast<' + parentName + '::Flag>(flags.value()); }\n' flagOperators += 'inline ' + parentName + '::Flags mtpCastFlags(MTPflags<' + childName + '::Flags> flags) { return mtpCastFlags(flags.v); }\n' textSerializeSource = '' if writeSerialization: # manual types added here textSerializeMethods += '\ bool Serialize_rpc_result(DumpToTextBuffer &to, int32 stage, int32 lev, Types &types, Types &vtypes, Stages &stages, Flags &flags, const ' + primeType + ' *start, const ' + primeType + ' *end, uint64 iflag) {\n\ if (stage) {\n\ to.add(",\\n").addSpaces(lev);\n\ } else {\n\ to.add("{ rpc_result");\n\ to.add("\\n").addSpaces(lev);\n\ }\n\ switch (stage) {\n\ case 0: to.add(" req_msg_id: "); ++stages.back(); types.push_back(' + idPrefix + 'long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;\n\ case 1: to.add(" result: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;\n\ default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;\n\ }\n\ return true;\n\ }\n\ \n\ bool Serialize_msg_container(DumpToTextBuffer &to, int32 stage, int32 lev, Types &types, Types &vtypes, Stages &stages, Flags &flags, const ' + primeType + ' *start, const ' + primeType + ' *end, uint64 iflag) {\n\ if (stage) {\n\ to.add(",\\n").addSpaces(lev);\n\ } else {\n\ to.add("{ msg_container");\n\ to.add("\\n").addSpaces(lev);\n\ }\n\ switch (stage) {\n\ case 0: to.add(" messages: "); ++stages.back(); types.push_back(' + idPrefix + 'vector); vtypes.push_back(' + idPrefix + 'core_message); stages.push_back(0); flags.push_back(0); break;\n\ default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;\n\ }\n\ return true;\n\ }\n\ \n\ bool Serialize_core_message(DumpToTextBuffer &to, int32 stage, int32 lev, Types &types, Types &vtypes, Stages &stages, Flags &flags, const ' + primeType + ' *start, const ' + primeType + ' *end, uint64 iflag) {\n\ if (stage) {\n\ to.add(",\\n").addSpaces(lev);\n\ } else {\n\ to.add("{ core_message");\n\ to.add("\\n").addSpaces(lev);\n\ }\n\ switch (stage) {\n\ case 0: to.add(" msg_id: "); ++stages.back(); types.push_back(' + idPrefix + 'long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;\n\ case 1: to.add(" seq_no: "); ++stages.back(); types.push_back(' + idPrefix + 'int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;\n\ case 2: to.add(" bytes: "); ++stages.back(); types.push_back(' + idPrefix + 'int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;\n\ case 3: to.add(" body: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;\n\ default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;\n\ }\n\ return true;\n\ }\n\ \n' textSerializeInit += '\ { ' + idPrefix + 'rpc_result, Serialize_rpc_result },\n\ { ' + idPrefix + 'msg_container, Serialize_msg_container },\n\ { ' + idPrefix + 'core_message, Serialize_core_message },\n' textSerializeSource = '\ namespace {\n\ \n\ using Types = QVector<' + typeIdType + '>;\n\ using Stages = QVector;\n\ using Flags = QVector;\n\ \n\ ' + textSerializeMethods + '\n\ \n\ using TextSerializer = bool (*)(DumpToTextBuffer &to, int32 stage, int32 lev, Types &types, Types &vtypes, Stages &stages, Flags &flags, const ' + primeType + ' *start, const ' + primeType + ' *end, uint64 iflag);\n\ \n\ base::flat_map<' + typeIdType + ', TextSerializer> CreateTextSerializers() {\n\ return {\n\ ' + textSerializeInit + '\n\ };\n\ }\n\ \n\ } // namespace\n\ \n\ bool DumpToTextType(DumpToTextBuffer &to, const ' + primeType + ' *&from, const ' + primeType + ' *end, ' + primeType + ' cons, uint32 level, ' + primeType + ' vcons) {\n\ static auto kSerializers = CreateTextSerializers();\n\ \n\ Types types, vtypes;\n\ Stages stages;\n\ Flags flags;\n\ types.reserve(20); vtypes.reserve(20); stages.reserve(20); flags.reserve(20);\n\ types.push_back(' + typeIdType + '(cons)); vtypes.push_back(' + typeIdType + '(vcons)); stages.push_back(0); flags.push_back(0);\n\ \n\ ' + typeIdType + ' type = cons, vtype = vcons;\n\ int32 stage = 0;\n\ int64 flag = 0;\n\ \n\ while (!types.isEmpty()) {\n\ type = types.back();\n\ vtype = vtypes.back();\n\ stage = stages.back();\n\ flag = flags.back();\n\ if (!type) {\n\ if (from >= end) {\n\ to.error("insufficient data");\n\ return false;\n\ } else if (stage) {\n\ to.error("unknown type on stage > 0");\n\ return false;\n\ }\n\ types.back() = type = *from;\n\ ++from;\n\ }\n\ \n\ int32 lev = level + types.size() - 1;\n\ auto it = kSerializers.find(type);\n\ if (it != kSerializers.end()) {\n\ if (!(*it->second)(to, stage, lev, types, vtypes, stages, flags, from, end, flag)) {\n\ to.error();\n\ return false;\n\ }\n\ } else if (DumpToTextCore(to, from, end, type, lev, vtype)) {\n\ types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back();\n\ } else {\n\ to.error();\n\ \n\ const auto bad = QByteArray::number(type, 16);\n\ to.add("(ERROR_SCHEME_BAD_CONS:0x").add(bad.data()).add(")");\n\ \n\ return false;\n\ }\n\ }\n\ return true;\n\ }\n' if forwTypedefs != '': forwTypedefs = '// Boxed types definitions\n' + forwTypedefs + '\n' # module itself header = '\ // WARNING! All changes made in this file will be lost!\n\ // Created from ' + inputNames + ' by \'generate.py\'\n\ //\n\ #pragma once\n\ \n\ ' + ('#include "' + builtinInclude + '"\n' if builtinInclude != '' else '') + '\ #include "base/assertion.h"\n\ #include "base/flags.h"\n\ #include "tl/tl_boxed.h"\n\ #include "tl/tl_type_owner.h"\n\ \n\ ' + ('namespace ' + globalNamespace + ' {\n' if globalNamespace != '' else '') + '\ ' + ('namespace ' + creatorNamespace + ' {\n' if creatorNamespace != '' else '') + '\ \n\ ' + ('inline constexpr auto kCurrentLayer = ' + primeType + '(' + str(layer) + ');\n\n' if layer != 0 else '') +'\ class TypeCreator;\n\ \n\ ' + ('} // namespace ' + creatorNamespace + '\n\n' if creatorNamespace != '' else '') + '\ // Type id constants\n\ enum {\n\ ' + ',\n'.join(enums) + '\n\ };\n\ \n\ // Type forward declarations\n\ ' + forwards + '\n\ ' + forwTypedefs + '\ // Type classes definitions\n\ ' + typesText + '\n\ // Type constructors with data\n\ ' + dataTexts + '\n\ // RPC methods\n\ ' + funcsText + '\n\ // Template methods definition\n\ ' + inlineMethods + '\n\ // Visitor definition\n\ ' + visitorMethods + '\n\ // Flag operators definition\n\ ' + flagOperators + '\n\ // Factory methods declaration\n\ ' + factories + '\n\ ' + ('} // namespace ' + globalNamespace + '\n' if globalNamespace != '' else '') source = '\ // WARNING! All changes made in this file will be lost!\n\ // Created from ' + inputNames + ' by \'generate.py\'\n\ //\n\ #include "' + outputHeaderBasename + '"\n\ \n\ // Creator proxy class definition\n\ ' + ('namespace ' + globalNamespace + ' {\n' if globalNamespace != '' else '') + '\ ' + ('namespace ' + creatorNamespace + ' {\n' if creatorNamespace != '' else '') + '\ \n\ class TypeCreator final {\n\ public:\n\ ' + creatorProxyText + '\n\ };\n\ \n\ ' + ('} // namespace ' + creatorNamespace + '\n\n' if creatorNamespace != '' else '') + '\ // Methods definition\n\ ' + methods + '\n\ ' + ('} // namespace ' + globalNamespace + '\n' if globalNamespace != '' else '') conversionHeaderFrom = '\ // WARNING! All changes made in this file will be lost!\n\ // Created from ' + inputNames + ' by \'generate.py\'\n\ //\n\ #pragma once\n\ \n\ #include "' + conversionInclude + '"\n\ #include "' + outputHeaderBasename + '"\n\ \n\ ' + ('namespace ' + globalNamespace + ' {\n\n' if globalNamespace != '' else '') + '\ ' + conversionHeaderFrom + '\n\ ' + ('} // namespace ' + globalNamespace + '\n' if globalNamespace != '' else '') +'\ ' + ('\n#include "' + conversionBuiltinIncludeFrom + '"\n' if conversionBuiltinIncludeFrom != '' else '') conversionHeaderTo = '\ // WARNING! All changes made in this file will be lost!\n\ // Created from ' + inputNames + ' by \'generate.py\'\n\ //\n\ #pragma once\n\ \n\ #include "' + conversionInclude + '"\n\ #include "' + outputHeaderBasename + '"\n\ \n\ ' + ('namespace ' + globalNamespace + ' {\n\n' if globalNamespace != '' else '') + '\ ' + conversionHeaderTo + '\n\ ' + ('} // namespace ' + globalNamespace + '\n' if globalNamespace != '' else '') +'\ ' + ('\n#include "' + conversionBuiltinIncludeTo + '"\n' if conversionBuiltinIncludeTo != '' else '') conversionSourceFrom = '\ // WARNING! All changes made in this file will be lost!\n\ // Created from ' + inputNames + ' by \'generate.py\'\n\ //\n\ #include "' + outputConversionHeaderFromBasename + '"\n\ \n\ ' + ('namespace ' + globalNamespace + ' {\n' if globalNamespace != '' else '') + '\ ' + conversionSourceFrom + '\n\ ' + ('} // namespace ' + globalNamespace + '\n' if globalNamespace != '' else '') conversionSourceTo = '\ // WARNING! All changes made in this file will be lost!\n\ // Created from ' + inputNames + ' by \'generate.py\'\n\ //\n\ #include "' + outputConversionHeaderToBasename + '"\n\ \n\ ' + ('namespace ' + globalNamespace + ' {\n' if globalNamespace != '' else '') + '\ ' + conversionSourceTo + '\n\ ' + ('} // namespace ' + globalNamespace + '\n' if globalNamespace != '' else '') serializationHeader = '\ // WARNING! All changes made in this file will be lost!\n\ // Created from ' + inputNames + ' by \'generate.py\'\n\ //\n\ #pragma once\n\ \n\ ' + ('#include "' + builtinInclude + '"\n\n' if builtinInclude != '' else '') + '\ namespace MTP::details {\n\ \n\ struct DumpToTextBuffer;\n\ \n\ [[nodiscard]] bool DumpToTextType(DumpToTextBuffer &to, const ' + primeType + ' *&from, const ' + primeType + ' *end, ' + primeType + ' cons = 0, uint32 level = 0, ' + primeType + ' vcons = 0);\n\ \n\ } // namespace MTP::details\n' serializationSource = '\ // WARNING! All changes made in this file will be lost!\n\ // Created from ' + inputNames + ' by \'generate.py\'\n\ //\n\ #include "' + outputSerializationHeaderBasename + '"\n\ #include "' + outputHeaderBasename + '"\n\ #include "' + serializationInclude + '"\n\ #include "base/flat_map.h"\n\ \n\ namespace MTP::details {\n\ ' + textSerializeSource + '\n\ } // namespace MTP::details\n' alreadyHeader = '' if os.path.isfile(outputHeader): with open(outputHeader, 'r') as already: alreadyHeader = already.read() if alreadyHeader != header: with open(outputHeader, 'w') as out: out.write(header) alreadySource = '' if os.path.isfile(outputSource): with open(outputSource, 'r') as already: alreadySource = already.read() if alreadySource != source: with open(outputSource, 'w') as out: out.write(source) if writeConversion: alreadyHeader = '' if os.path.isfile(outputConversionHeaderFrom): with open(outputConversionHeaderFrom, 'r') as already: alreadyHeader = already.read() if alreadyHeader != conversionHeaderFrom: with open(outputConversionHeaderFrom, 'w') as out: out.write(conversionHeaderFrom) if writeConversion: alreadyHeader = '' if os.path.isfile(outputConversionHeaderTo): with open(outputConversionHeaderTo, 'r') as already: alreadyHeader = already.read() if alreadyHeader != conversionHeaderTo: with open(outputConversionHeaderTo, 'w') as out: out.write(conversionHeaderTo) alreadySource = '' if os.path.isfile(outputConversionSourceFrom): with open(outputConversionSourceFrom, 'r') as already: alreadySource = already.read() if alreadySource != conversionSourceFrom: with open(outputConversionSourceFrom, 'w') as out: out.write(conversionSourceFrom) alreadySource = '' if os.path.isfile(outputConversionSourceTo): with open(outputConversionSourceTo, 'r') as already: alreadySource = already.read() if alreadySource != conversionSourceTo: with open(outputConversionSourceTo, 'w') as out: out.write(conversionSourceTo) if writeSerialization: alreadyHeader = '' if os.path.isfile(outputSerializationHeader): with open(outputSerializationHeader, 'r') as already: alreadyHeader = already.read() if alreadyHeader != serializationHeader: with open(outputSerializationHeader, 'w') as out: out.write(serializationHeader) alreadySource = '' if os.path.isfile(outputSerializationSource): with open(outputSerializationSource, 'r') as already: alreadySource = already.read() if alreadySource != serializationSource: with open(outputSerializationSource, 'w') as out: out.write(serializationSource) with open(outputPath + '.timestamp', 'w') as out: out.write('1')