mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
Refactor build script to simplify it. No changes to the `.rs` files the script creates, only the script itself.
163 lines
5.5 KiB
JavaScript
163 lines
5.5 KiB
JavaScript
import {readFile} from 'fs/promises';
|
|
import {join as pathJoin} from 'path';
|
|
import {fileURLToPath} from 'url';
|
|
import assert from 'assert';
|
|
import {typeAndWrappers} from './utils.mjs';
|
|
|
|
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 types = Object.create(null);
|
|
for (const filename of FILENAMES) {
|
|
const code = await readFile(`${codeDirPath}${filename}`, 'utf8');
|
|
parseFile(code, filename, types);
|
|
}
|
|
return types;
|
|
}
|
|
|
|
function parseFile(code, filename, types) {
|
|
const lines = code.split(/\r?\n/).map(
|
|
line => line.replace(/\s+/g, ' ').replace(/ ?\/\/.*$/, '')
|
|
);
|
|
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
|
const lineMatch = lines[lineIndex].match(/^#\[visited_node ?([\]\(])/);
|
|
if (!lineMatch) continue;
|
|
|
|
let scopeArgs = null;
|
|
if (lineMatch[1] === '(') {
|
|
let line = lines[lineIndex].slice(lineMatch[0].length),
|
|
scopeArgsStr = '';
|
|
while (!line.endsWith(')]')) {
|
|
scopeArgsStr += ` ${line}`;
|
|
line = lines[++lineIndex];
|
|
}
|
|
scopeArgsStr += ` ${line.slice(0, -2)}`;
|
|
scopeArgsStr = scopeArgsStr.trim().replace(/ +/g, ' ');
|
|
|
|
scopeArgs = parseScopeArgs(scopeArgsStr, filename, lineIndex);
|
|
}
|
|
|
|
let match;
|
|
while (true) {
|
|
match = lines[++lineIndex].match(/^pub (enum|struct) ((.+?)(?:<'a>)?) \{/);
|
|
if (match) break;
|
|
}
|
|
const [, kind, rawName, name] = match,
|
|
startLineIndex = lineIndex;
|
|
|
|
const itemLines = [];
|
|
while (true) {
|
|
const line = lines[++lineIndex].trim();
|
|
if (line === '}') break;
|
|
if (line !== '') itemLines.push(line);
|
|
}
|
|
|
|
if (kind === 'struct') {
|
|
types[name] = parseStruct(name, rawName, itemLines, scopeArgs, filename, startLineIndex);
|
|
} else {
|
|
types[name] = parseEnum(name, rawName, itemLines, filename, startLineIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
function parseStruct(name, rawName, lines, scopeArgs, 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, rawTypeName] = match,
|
|
typeName = rawTypeName.replace(/<'a>/g, '').replace(/<'a, ?/g, '<'),
|
|
{name: innerTypeName, wrappers} = typeAndWrappers(typeName);
|
|
|
|
fields.push({name, typeName, rawName, rawTypeName, innerTypeName, wrappers});
|
|
}
|
|
return {kind: 'struct', name, rawName, fields, scopeArgs};
|
|
}
|
|
|
|
function parseEnum(name, rawName, lines, filename, startLineIndex) {
|
|
const variants = [],
|
|
inherits = [];
|
|
for (const [lineIndex, line] of lines.entries()) {
|
|
const match = line.match(/^(.+?)\((.+?)\)(?: ?= ?(\d+))?,$/);
|
|
if (match) {
|
|
const [, name, rawTypeName, discriminantStr] = match,
|
|
typeName = rawTypeName.replace(/<'a>/g, '').replace(/<'a, ?/g, '<'),
|
|
{name: innerTypeName, wrappers} = typeAndWrappers(typeName),
|
|
discriminant = discriminantStr ? +discriminantStr : null;
|
|
variants.push({name, typeName, rawTypeName, innerTypeName, wrappers, 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, rawName, variants, inherits};
|
|
}
|
|
|
|
function parseScopeArgs(argsStr, filename, lineIndex) {
|
|
if (!argsStr) return null;
|
|
|
|
const matchAndConsume = (regex) => {
|
|
const match = argsStr.match(regex);
|
|
assert(match);
|
|
argsStr = argsStr.slice(match[0].length);
|
|
return match.slice(1);
|
|
};
|
|
|
|
const args = {};
|
|
try {
|
|
while (true) {
|
|
const [key] = matchAndConsume(/^([a-z_]+)\(/);
|
|
assert(
|
|
['scope', 'scope_if', 'strict_if', 'enter_scope_before'].includes(key),
|
|
`Unexpected visited_node macro arg: ${key}`
|
|
);
|
|
|
|
let bracketCount = 1,
|
|
index = 0;
|
|
for (; index < argsStr.length; index++) {
|
|
const char = argsStr[index];
|
|
if (char === '(') {
|
|
bracketCount++;
|
|
} else if (char === ')') {
|
|
bracketCount--;
|
|
if (bracketCount === 0) break;
|
|
}
|
|
}
|
|
assert(bracketCount === 0);
|
|
|
|
args[key] = argsStr.slice(0, index).trim();
|
|
argsStr = argsStr.slice(index + 1);
|
|
if (argsStr === '') break;
|
|
|
|
matchAndConsume(/^ ?, ?/);
|
|
}
|
|
|
|
assert(args.scope, 'Missing key `scope`');
|
|
} catch (err) {
|
|
throw new Error(
|
|
`Cannot parse visited_node args: ${argsStr} in ${filename}:${lineIndex}\n${err?.message}`
|
|
);
|
|
}
|
|
|
|
return args;
|
|
}
|