diff --git a/crates/oxc_traverse/scripts/lib/parse.mjs b/crates/oxc_traverse/scripts/lib/parse.mjs index 955122d988c9d7..861564934e04f8 100644 --- a/crates/oxc_traverse/scripts/lib/parse.mjs +++ b/crates/oxc_traverse/scripts/lib/parse.mjs @@ -3,69 +3,90 @@ import {join as pathJoin} from 'path'; import {fileURLToPath} from 'url'; import assert from 'assert'; +const FILENAMES = ['js.rs', 'jsx.rs', 'literal.rs', 'ts.rs']; + +/** + * Parse type defs from Rust files. + */ export default async function getTypesFromCode() { const codeDirPath = pathJoin(fileURLToPath(import.meta.url), '../../../../oxc_ast/src/ast/'); - const filenames = ['js.rs', 'jsx.rs', 'literal.rs', 'ts.rs']; - // Parse type defs from Rust files const types = Object.create(null); - for (const filename of filenames) { - const code = await readFile(`${codeDirPath}${filename}`, 'utf8'), - lines = code.split(/\r?\n/); - for (let i = 0; i < lines.length; i++) { - if (lines[i] === '#[visited_node]') { - let match; - while (true) { - match = lines[++i].match(/^pub (enum|struct) (.+?)(<'a>)? \{/); - if (match) break; - } - const [, kind, name, lifetimeStr] = match, - hasLifetime = !!lifetimeStr; - const itemLines = []; - while (true) { - const line = lines[++i].replace(/\/\/.*$/, '').replace(/\s+/g, ' ').trim(); - if (line === '}') break; - if (line !== '') itemLines.push(line); - } + for (const filename of FILENAMES) { + const code = await readFile(`${codeDirPath}${filename}`, 'utf8'); + parseFile(code, filename, types); + } + return types; +} - if (kind === 'enum') { - const variants = [], - inherits = []; - for (const line of itemLines) { - const match = line.match(/^(.+?)\((.+?)\)(?: ?= ?(\d+))?,$/); - if (match) { - let [, name, type, discriminant] = match; - type = type.replace(/<'a>/g, '').replace(/<'a,\s*/g, '<'); - discriminant = discriminant ? +discriminant : null; - variants.push({name, type, discriminant}); - } else { - const match2 = line.match(/^@inherit ([A-Za-z]+)$/); - assert(match2, `Cannot parse line ${i} in '${filename}' as enum variant: '${line}'`); - inherits.push(match2[1]); - } - } - types[name] = {kind: 'enum', name, hasLifetime, variants, inherits}; - } else { - const fields = []; - for (let i = 0; i < itemLines.length; i++) { - const line = itemLines[i]; - if (line.startsWith('#[')) { - while (!itemLines[i].endsWith(']')) { - i++; - } - continue; - } +function parseFile(code, filename, types) { + const lines = code.split(/\r?\n/); + for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { + if (lines[lineIndex] !== '#[visited_node]') continue; - const match = line.match(/^pub ((?:r#)?([a-z_]+)): (.+),(?: ?\/\/.+)?$/); - assert(match, `Cannot parse line ${i} in '${filename}' as struct field: '${line}'`); - const [, rawName, name, rawType] = match, - type = rawType.replace(/<'a>/g, '').replace(/<'a, ?/g, '<'); - fields.push({name, type, rawName, rawType}); - } - types[name] = {kind: 'struct', name, hasLifetime, fields}; - } + let match; + while (true) { + match = lines[++lineIndex].match(/^pub (enum|struct) (.+?)(<'a>)? \{/); + if (match) break; + } + const [, kind, name, lifetimeStr] = match, + hasLifetime = !!lifetimeStr; + const itemLines = []; + while (true) { + const line = lines[++lineIndex].replace(/\/\/.*$/, '').replace(/\s+/g, ' ').trim(); + if (line === '}') break; + if (line !== '') itemLines.push(line); + } + + if (kind === 'enum') { + types[name] = parseEnum(name, hasLifetime, itemLines, filename, lineIndex); + } else { + types[name] = parseStruct(name, hasLifetime, itemLines, filename, lineIndex); + } + } +} + +function parseEnum(name, hasLifetime, lines, filename, startLineIndex) { + const variants = [], + inherits = []; + for (const [lineIndex, line] of lines.entries()) { + const match = line.match(/^(.+?)\((.+?)\)(?: ?= ?(\d+))?,$/); + if (match) { + const [, name, rawType, discriminantStr] = match, + type = rawType.replace(/<'a>/g, '').replace(/<'a,\s*/g, '<'), + discriminant = discriminantStr ? +discriminantStr : null; + variants.push({name, type, rawType, discriminant}); + } else { + const match2 = line.match(/^@inherit ([A-Za-z]+)$/); + assert( + match2, + `Cannot parse line ${startLineIndex + lineIndex} in '${filename}' as enum variant: '${line}'` + ); + inherits.push(match2[1]); + } + } + return {kind: 'enum', name, hasLifetime, variants, inherits}; +} + +function parseStruct(name, hasLifetime, lines, filename, startLineIndex) { + const fields = []; + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + if (line.startsWith('#[')) { + while (!lines[i].endsWith(']')) { + i++; } + continue; } + + const match = line.match(/^pub ((?:r#)?([a-z_]+)): (.+),$/); + assert( + match, + `Cannot parse line ${startLineIndex + i} in '${filename}' as struct field: '${line}'` + ); + const [, rawName, name, rawType] = match, + type = rawType.replace(/<'a>/g, '').replace(/<'a, ?/g, '<'); + fields.push({name, type, rawName, rawType}); } - return types; + return {kind: 'struct', name, hasLifetime, fields}; }