symbol-processor/src/loader.ts

209 lines
7.2 KiB
TypeScript
Raw Normal View History

2024-02-01 19:31:25 +00:00
import { COMMENT, EXTENSION, getSelfArg, parseTypeAndName, readDefinition, safeParseInt, syntaxError } from './common';
2024-01-04 20:27:02 +00:00
import { isCppAllowed } from './map';
import { Method } from './method';
import { SimpleProperty, StaticProperty } from './property';
import { Struct } from './struct';
2024-01-04 22:00:19 +00:00
// Error Handling
2024-01-04 20:27:02 +00:00
export class ErrorOnLine {
readonly error: unknown;
readonly file: string;
readonly line: number;
constructor (error: unknown, file: string, line: number) {
this.error = error;
this.file = file;
this.line = line;
}
}
// Parse Property
function parseProperty(args: string) {
2024-01-12 03:09:34 +00:00
const parts = args.split(' = ');
if (parts.length !== 2) {
2024-01-04 20:27:02 +00:00
syntaxError('Invalid Piece Count');
}
2024-01-12 03:09:34 +00:00
const {type, name} = parseTypeAndName(parts[0]!);
const offset = safeParseInt(parts[1]!);
2024-01-04 20:27:02 +00:00
return {type, name, offset};
}
// Parse Method
function parseMethod(args: string, self: string, insertSelfArg: boolean) {
2024-01-06 09:06:00 +00:00
const argsStart = args.indexOf('(');
2024-01-04 20:27:02 +00:00
if (argsStart === -1) {
syntaxError('Cannot Find Arguments');
}
2024-01-12 03:09:34 +00:00
const start = args.substring(0, argsStart).trim();
const {type, name} = parseTypeAndName(start);
2024-01-06 09:06:00 +00:00
const end = args.substring(argsStart).trim().split(' = ');
2024-01-04 20:27:02 +00:00
if (end.length !== 2) {
syntaxError('Invalid Piece Count');
}
let methodArgs = end[0]!;
if (!methodArgs.startsWith('(') || !methodArgs.endsWith(')')) {
syntaxError('Invalid Method Arguments');
}
if (insertSelfArg) {
2024-02-01 19:31:25 +00:00
let selfArg = `(${getSelfArg(self)}`;
2024-01-04 20:27:02 +00:00
if (methodArgs !== '()') {
selfArg += ', ';
}
methodArgs = selfArg + methodArgs.substring(1);
}
const address = safeParseInt(end[1]!);
return new Method(self, name, type, methodArgs, address);
}
// Load Structure
export function load(target: Struct, name: string, isExtended: boolean) {
// Read File
let data = readDefinition(name);
// Strip Comments
const lines = [];
for (let line of data.split('\n')) {
// Trim
line = line.trim();
// Remove Comments
const index = line.indexOf(COMMENT);
if (index !== -1) {
line = line.substring(0, index);
}
// Store Line
lines.push(line);
}
data = lines.join('\n');
2024-01-06 09:06:00 +00:00
// Iterate Over Pieces
2024-01-04 20:27:02 +00:00
let cursor = 0;
for (let piece of data.split(';')) {
// Find Start Of Command For Error Handling
const startOfCommand = cursor + piece.search(/\S|$/);
// Advance Cursor
cursor += piece.length + 1;
// Handle Errors
try {
// Trim
piece = piece.trim();
// Replace Newlines With Spaces
piece = piece.replace(/\n/g, ' ');
// Simplify
piece = piece.replace(/\s+/g, ' ');
// Skip Empty Piece
if (piece.length === 0) {
continue;
}
// Skip C++ Types If Applicable
if (!isCppAllowed() && piece.includes('std::')) {
continue;
}
// Handle Commands
let firstSpace = piece.indexOf(' ');
if (firstSpace === -1) {
firstSpace = piece.length;
}
const command = piece.substring(0, firstSpace);
const args = piece.substring(firstSpace + 1);
switch (command) {
case 'extends': {
// Load Parent
load(target, args, true);
break;
}
case 'size': {
// Set Size
2024-02-01 19:31:25 +00:00
target.setSize(safeParseInt(args), !isExtended);
2024-01-04 20:27:02 +00:00
break;
}
case 'vtable-size': {
// Set VTable Size
2024-02-01 19:31:25 +00:00
target.setVTableSize(safeParseInt(args), !isExtended);
2024-01-04 20:27:02 +00:00
break;
}
case 'vtable': {
// Set VTable Address
if (!isExtended) {
target.setVTableAddress(safeParseInt(args));
}
break;
}
case 'property': {
// Add Property
const info = parseProperty(args);
target.addProperty(new SimpleProperty(info.offset, info.type, info.name));
break;
}
case 'static-property': {
// Add Static Property
if (!isExtended) {
const info = parseProperty(args);
2024-01-07 07:41:08 +00:00
target.addStaticProperty(new StaticProperty(info.offset, info.type, info.name, target.getName(), false));
}
break;
}
case 'static-property-array': {
// Add Static Array Property
if (!isExtended) {
const info = parseProperty(args);
target.addStaticProperty(new StaticProperty(info.offset, info.type, info.name, target.getName(), true));
2024-01-04 20:27:02 +00:00
}
break;
}
case 'method': {
// Add Method
const method = parseMethod(args, target.getName(), true);
target.addMethod(method, false);
break;
}
case 'virtual-method': {
// Add Virtual Method
const method = parseMethod(args, target.getName(), true);
target.addMethod(method, true);
break;
}
case 'static-method': {
// Add Static Method
if (!isExtended) {
const method = parseMethod(args, target.getName(), false);
target.addMethod(method, false);
}
break;
}
case 'constructor': {
// Constructor
if (!isExtended) {
let data = `${target.getName()} *constructor`;
if (args.startsWith('(')) {
// No Custom Name
data += ' ';
} else {
// Use Custom Name
data += '_';
}
data += args;
const method = parseMethod(data, target.getName(), true);
target.addMethod(method, false);
}
break;
}
default: {
throw new Error(`Invalid Command: ${command}`);
}
}
} catch (e) {
if (e instanceof ErrorOnLine) {
throw e;
}
// Find Line Number
let lineNumber = 1;
for (let i = 0; i <= startOfCommand; i++) {
if (data.charAt(i) === '\n') {
lineNumber++;
}
}
// Rethrow Error With Line Number
throw new ErrorOnLine(e, `${name}${EXTENSION}`, lineNumber);
}
}
}