2023-10-16 19:23:53 +03:00
|
|
|
import { computeConstructorIdFromEntry } from './ctor-id.js'
|
|
|
|
import { TL_PRIMITIVES, TlArgument, TlEntry } from './types.js'
|
|
|
|
import { parseArgumentType, parseTdlibStyleComment } from './utils.js'
|
2021-11-23 00:03:59 +03:00
|
|
|
|
2023-09-24 01:32:22 +03:00
|
|
|
const SINGLE_REGEX = /^(.+?)(?:#([0-9a-f]{1,8}))?(?: \?)?(?: {(.+?:.+?)})? ((?:.+? )*)= (.+);$/
|
2021-11-23 00:03:59 +03:00
|
|
|
|
2023-10-09 05:59:48 +03:00
|
|
|
export function computeConstructorIdFromString(line: string): number {
|
|
|
|
return computeConstructorIdFromEntry(parseTlToEntries(line, { forIdComputation: true })[0])
|
|
|
|
}
|
|
|
|
|
2022-08-29 14:33:11 +03:00
|
|
|
/**
|
|
|
|
* Parse TL schema into a list of entries.
|
|
|
|
*
|
|
|
|
* @param tl TL schema
|
|
|
|
* @param params Additional parameters
|
|
|
|
*/
|
2021-11-23 00:03:59 +03:00
|
|
|
export function parseTlToEntries(
|
|
|
|
tl: string,
|
2023-07-20 22:07:07 +03:00
|
|
|
params: {
|
2022-08-29 14:33:11 +03:00
|
|
|
/**
|
|
|
|
* Whether to throw an error if a line failed to parse
|
|
|
|
*/
|
2021-11-23 00:03:59 +03:00
|
|
|
panicOnError?: boolean
|
2022-08-29 14:33:11 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to be called if there was an error while parsing a line
|
|
|
|
*
|
|
|
|
* @param err Error
|
|
|
|
* @param line Line that failed to parse
|
|
|
|
* @param num Line number
|
|
|
|
*/
|
2021-11-23 00:03:59 +03:00
|
|
|
onError?: (err: Error, line: string, num: number) => void
|
2022-08-29 14:33:11 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to be called a comment is found not belonging to any entry
|
|
|
|
*
|
|
|
|
* @param comment Comment text
|
|
|
|
*/
|
2021-11-23 00:03:59 +03:00
|
|
|
onOrphanComment?: (comment: string) => void
|
2022-08-29 14:33:11 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Prefix to be applied to all types
|
|
|
|
*/
|
2021-11-23 00:03:59 +03:00
|
|
|
prefix?: string
|
2022-08-29 14:33:11 +03:00
|
|
|
|
|
|
|
/**
|
2023-06-25 03:09:04 +03:00
|
|
|
* Whether this invocation is for computing constructor ids.
|
|
|
|
* If true, the `id` field will be set to 0 for all entries.
|
2022-08-29 14:33:11 +03:00
|
|
|
*/
|
2023-06-25 03:09:04 +03:00
|
|
|
forIdComputation?: boolean
|
2023-07-20 22:07:07 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether to parse typeModifiers for method return types
|
|
|
|
*/
|
|
|
|
parseMethodTypes?: boolean
|
|
|
|
} = {},
|
2021-11-23 00:03:59 +03:00
|
|
|
): TlEntry[] {
|
|
|
|
const ret: TlEntry[] = []
|
|
|
|
|
2023-06-25 03:09:04 +03:00
|
|
|
const entries: Record<string, TlEntry> = {}
|
|
|
|
const unions: Record<string, TlEntry[]> = {}
|
|
|
|
|
2021-11-23 00:03:59 +03:00
|
|
|
const lines = tl.split('\n')
|
|
|
|
|
|
|
|
let currentKind: TlEntry['kind'] = 'class'
|
|
|
|
let currentComment = ''
|
2023-07-20 22:07:07 +03:00
|
|
|
const prefix = params.prefix ?? ''
|
2021-11-23 00:03:59 +03:00
|
|
|
|
|
|
|
lines.forEach((line, idx) => {
|
|
|
|
line = line.trim()
|
|
|
|
|
|
|
|
if (line === '') {
|
2023-07-20 22:07:07 +03:00
|
|
|
if (params.onOrphanComment) {
|
2021-11-23 00:03:59 +03:00
|
|
|
params.onOrphanComment(currentComment)
|
|
|
|
}
|
|
|
|
|
|
|
|
currentComment = ''
|
2023-06-05 03:30:48 +03:00
|
|
|
|
2021-11-23 00:03:59 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if (line.match(/^\/\//)) {
|
|
|
|
if (currentComment) {
|
|
|
|
if (line[2] === '-') {
|
2022-06-30 16:32:56 +03:00
|
|
|
currentComment += '\n' + line.substring(3).trim()
|
2021-11-23 00:03:59 +03:00
|
|
|
} else {
|
2022-06-30 16:32:56 +03:00
|
|
|
currentComment += ' ' + line.substring(2).trim()
|
2021-11-23 00:03:59 +03:00
|
|
|
}
|
|
|
|
} else {
|
2022-06-30 16:32:56 +03:00
|
|
|
currentComment = line.substring(2).trim()
|
2021-11-23 00:03:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if (line === '---functions---') {
|
|
|
|
currentKind = 'method'
|
2023-06-05 03:30:48 +03:00
|
|
|
|
2021-11-23 00:03:59 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if (line === '---types---') {
|
|
|
|
currentKind = 'class'
|
2023-06-05 03:30:48 +03:00
|
|
|
|
2021-11-23 00:03:59 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
const match = SINGLE_REGEX.exec(line)
|
2023-06-05 03:30:48 +03:00
|
|
|
|
2021-11-23 00:03:59 +03:00
|
|
|
if (!match) {
|
|
|
|
const err = new Error(`Failed to parse line ${idx + 1}: ${line}`)
|
|
|
|
|
2023-07-20 22:07:07 +03:00
|
|
|
if (params.panicOnError) {
|
2021-11-23 00:03:59 +03:00
|
|
|
throw err
|
2023-07-20 22:07:07 +03:00
|
|
|
} else if (params.onError) {
|
2021-11-23 00:03:59 +03:00
|
|
|
params.onError(err, line, idx + 1)
|
|
|
|
} else {
|
|
|
|
console.warn(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
const [, typeName, typeId, generics, args, type] = match
|
2023-06-05 03:30:48 +03:00
|
|
|
|
2021-11-23 00:03:59 +03:00
|
|
|
if (typeName in TL_PRIMITIVES) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-06-25 03:09:04 +03:00
|
|
|
let typeIdNum = typeId ? parseInt(typeId, 16) : 0
|
|
|
|
|
2023-07-20 22:07:07 +03:00
|
|
|
if (typeIdNum === 0 && !params.forIdComputation) {
|
2023-06-25 03:09:04 +03:00
|
|
|
typeIdNum = computeConstructorIdFromString(line)
|
|
|
|
}
|
2021-11-23 00:03:59 +03:00
|
|
|
|
|
|
|
const argsParsed =
|
2023-06-05 03:30:48 +03:00
|
|
|
args && !args.match(/\[ [a-z]+ ]/i) ?
|
|
|
|
args
|
|
|
|
.trim()
|
|
|
|
.split(' ')
|
|
|
|
.map((j) => j.split(':')) :
|
|
|
|
[]
|
2021-11-23 00:03:59 +03:00
|
|
|
|
|
|
|
const entry: TlEntry = {
|
|
|
|
kind: currentKind,
|
2023-06-25 03:09:04 +03:00
|
|
|
name: typeName,
|
2021-11-23 00:03:59 +03:00
|
|
|
id: typeIdNum,
|
|
|
|
type,
|
|
|
|
arguments: [],
|
|
|
|
}
|
|
|
|
|
2023-07-20 22:07:07 +03:00
|
|
|
if (entry.kind === 'method' && params.parseMethodTypes) {
|
|
|
|
const [type, modifiers] = parseArgumentType(entry.type)
|
|
|
|
entry.type = type
|
|
|
|
|
|
|
|
if (Object.keys(modifiers).length) {
|
|
|
|
entry.typeModifiers = modifiers
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-23 00:03:59 +03:00
|
|
|
if (generics) {
|
|
|
|
entry.generics = generics.split(',').map((it) => {
|
|
|
|
const [name, type] = it.split(':')
|
2023-06-05 03:30:48 +03:00
|
|
|
|
2021-11-23 00:03:59 +03:00
|
|
|
return { name, type }
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argsParsed.length) {
|
2023-06-25 03:09:04 +03:00
|
|
|
argsParsed.forEach(([name, type_]) => {
|
|
|
|
const [type, modifiers] = parseArgumentType(type_)
|
|
|
|
const item: TlArgument = {
|
|
|
|
name,
|
|
|
|
type,
|
|
|
|
}
|
2021-11-23 00:03:59 +03:00
|
|
|
|
2023-06-25 03:09:04 +03:00
|
|
|
if (Object.keys(modifiers).length) {
|
|
|
|
item.typeModifiers = modifiers
|
2021-11-23 00:03:59 +03:00
|
|
|
}
|
2023-06-25 03:09:04 +03:00
|
|
|
|
|
|
|
entry.arguments.push(item)
|
2021-11-23 00:03:59 +03:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
if (currentComment) {
|
|
|
|
if (currentComment.match(/^@description /)) {
|
|
|
|
// tdlib-style comment
|
|
|
|
const obj = parseTdlibStyleComment(currentComment)
|
|
|
|
|
|
|
|
if (obj.description) entry.comment = obj.description
|
|
|
|
|
|
|
|
entry.arguments.forEach((arg) => {
|
|
|
|
if (arg.name in obj) {
|
|
|
|
arg.comment = obj[arg.name]
|
|
|
|
}
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
entry.comment = currentComment
|
|
|
|
}
|
|
|
|
|
|
|
|
currentComment = ''
|
|
|
|
}
|
|
|
|
|
|
|
|
ret.push(entry)
|
2023-06-25 03:09:04 +03:00
|
|
|
entries[entry.name] = entry
|
|
|
|
|
|
|
|
if (entry.kind === 'class') {
|
|
|
|
if (!unions[entry.type]) unions[entry.type] = []
|
|
|
|
unions[entry.type].push(entry)
|
|
|
|
}
|
2021-11-23 00:03:59 +03:00
|
|
|
})
|
|
|
|
|
2023-07-20 22:07:07 +03:00
|
|
|
if (currentComment && params.onOrphanComment) {
|
2021-11-23 00:03:59 +03:00
|
|
|
params.onOrphanComment(currentComment)
|
|
|
|
}
|
|
|
|
|
2023-06-25 03:09:04 +03:00
|
|
|
// post-process:
|
2023-07-20 22:07:07 +03:00
|
|
|
// - add return type ctor id for methods
|
2023-06-25 03:09:04 +03:00
|
|
|
// - find arguments where type is not a union and put corresponding modifiers
|
|
|
|
// - apply prefix
|
|
|
|
ret.forEach((entry, entryIdx) => {
|
2023-07-20 22:07:07 +03:00
|
|
|
if (params.parseMethodTypes && entry.kind === 'method') {
|
|
|
|
const type = entry.type
|
|
|
|
|
|
|
|
if (type in unions && unions[type].length === 1) {
|
|
|
|
if (!entry.typeModifiers) entry.typeModifiers = {}
|
|
|
|
|
|
|
|
entry.typeModifiers.constructorId = unions[type][0].id
|
|
|
|
} else if (type in entries) {
|
|
|
|
if (!entry.typeModifiers) entry.typeModifiers = {}
|
|
|
|
entry.typeModifiers.isBareType = true
|
|
|
|
entry.typeModifiers.constructorId = entries[type].id
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-25 03:09:04 +03:00
|
|
|
entry.arguments.forEach((arg) => {
|
|
|
|
const type = arg.type
|
|
|
|
|
|
|
|
if (type in TL_PRIMITIVES) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type in unions && arg.typeModifiers?.isBareUnion) {
|
|
|
|
if (unions[type].length !== 1) {
|
|
|
|
const err = new Error(
|
|
|
|
`Union ${type} has more than one entry, cannot use it like %${type} (found in ${entry.name}#${arg.name})`,
|
|
|
|
)
|
|
|
|
|
2023-07-20 22:07:07 +03:00
|
|
|
if (params.panicOnError) {
|
2023-06-25 03:09:04 +03:00
|
|
|
throw err
|
2023-07-20 22:07:07 +03:00
|
|
|
} else if (params.onError) {
|
2023-06-25 03:09:04 +03:00
|
|
|
params.onError(err, '', entryIdx)
|
|
|
|
} else {
|
|
|
|
console.warn(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
arg.typeModifiers.constructorId = unions[type][0].id
|
|
|
|
} else if (type in entries) {
|
|
|
|
if (!arg.typeModifiers) arg.typeModifiers = {}
|
|
|
|
arg.typeModifiers.isBareType = true
|
|
|
|
arg.typeModifiers.constructorId = entries[type].id
|
|
|
|
|
|
|
|
if (prefix) {
|
|
|
|
arg.type = prefix + arg.type
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
if (prefix) {
|
|
|
|
entry.name = prefix + entry.name
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2021-11-23 00:03:59 +03:00
|
|
|
return ret
|
|
|
|
}
|