mtcute/packages/tl-utils/src/parse.ts

174 lines
4.7 KiB
TypeScript
Raw Normal View History

import { computeConstructorIdFromString } from './ctor-id'
import { TL_PRIMITIVES, TlEntry } from './types'
import { parseTdlibStyleComment } from './utils'
const SINGLE_REGEX = /^(.+?)(?:#([0-9a-f]{1,8}))?(?: \?)?(?: {(.+?:.+?)})? ((?:.+? )*)= (.+);$/
function applyPrefix(prefix: string, type: string): string {
if (type in TL_PRIMITIVES) return type
const m = type.match(/^[Vv]ector[< ](.+?)[> ]$/)
if (m) return `Vector<${applyPrefix(prefix, m[1])}>`
return prefix + type
}
export function parseTlToEntries(
tl: string,
params?: {
panicOnError?: boolean
onError?: (err: Error, line: string, num: number) => void
onOrphanComment?: (comment: string) => void
prefix?: string
applyPrefixToArguments?: boolean
}
): TlEntry[] {
const ret: TlEntry[] = []
const lines = tl.split('\n')
let currentKind: TlEntry['kind'] = 'class'
let currentComment = ''
const prefix = params?.prefix ?? ''
lines.forEach((line, idx) => {
line = line.trim()
if (line === '') {
if (params?.onOrphanComment) {
params.onOrphanComment(currentComment)
}
currentComment = ''
return
}
if (line.match(/^\/\//)) {
if (currentComment) {
if (line[2] === '-') {
currentComment += '\n' + line.substr(3).trim()
} else {
currentComment += ' ' + line.substr(2).trim()
}
} else {
currentComment = line.substr(2).trim()
}
return
}
if (line === '---functions---') {
currentKind = 'method'
return
}
if (line === '---types---') {
currentKind = 'class'
return
}
const match = SINGLE_REGEX.exec(line)
if (!match) {
const err = new Error(`Failed to parse line ${idx + 1}: ${line}`)
if (params?.panicOnError) {
throw err
} else if (params?.onError) {
params.onError(err, line, idx + 1)
} else {
console.warn(err)
}
return
}
const [, typeName, typeId, generics, args, type] = match
if (typeName in TL_PRIMITIVES) {
return
}
const typeIdNum = typeId
? parseInt(typeId, 16)
: computeConstructorIdFromString(line)
const argsParsed =
args && !args.match(/\[ [a-z]+ ]/i)
? args
.trim()
.split(' ')
.map((j) => j.split(':'))
: []
const entry: TlEntry = {
kind: currentKind,
name: prefix + typeName,
id: typeIdNum,
type,
arguments: [],
}
if (generics) {
entry.generics = generics.split(',').map((it) => {
const [name, type] = it.split(':')
return { name, type }
})
}
if (argsParsed.length) {
argsParsed.forEach(([name, typ]) => {
let [predicate, type] = typ.split('?')
if (!type) {
// no predicate, `predicate` is the type
if (params?.applyPrefixToArguments) {
predicate = applyPrefix(prefix, predicate)
}
entry.arguments.push({
name,
type: predicate,
})
} else {
// there is a predicate
if (params?.applyPrefixToArguments) {
type = applyPrefix(prefix, type)
}
entry.arguments.push({
name,
type,
predicate,
})
}
})
}
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)
})
if (currentComment && params?.onOrphanComment) {
params.onOrphanComment(currentComment)
}
return ret
}