This commit is contained in:
TheBrokenRail 2024-01-04 17:00:19 -05:00
parent e99239fecf
commit ddbbbcb8e3
5 changed files with 120 additions and 91 deletions

View File

@ -3,9 +3,12 @@ import * as fs from 'node:fs';
export const INDENT = ' '; export const INDENT = ' ';
export const POINTER_SIZE = 4; export const POINTER_SIZE = 4;
export const EXTENSION = '.def'; export const EXTENSION = '.def';
export const STRUCTURE_FILES: {[id: string]: string} = {};
export function readDefinition(name: string) { export function readDefinition(name: string) {
const file = `${__dirname}/../symbols/${name}${EXTENSION}`; if (!STRUCTURE_FILES[name]) {
return fs.readFileSync(file, {encoding: 'utf8'}); throw new Error(`Missing Definition File: ${name}${EXTENSION}`);
}
return fs.readFileSync(STRUCTURE_FILES[name]!, {encoding: 'utf8'});
} }
export function parseTypeAndName(parts: string[]) { export function parseTypeAndName(parts: string[]) {
let type = parts[0]!; let type = parts[0]!;

View File

@ -1,29 +1,50 @@
import * as fs from 'node:fs'; import * as fs from 'node:fs';
import { EXTENSION, INDENT } from './common'; import { STRUCTURE_FILES, EXTENSION, INDENT } from './common';
import { getStructure, setCppAllowed } from './map'; import { getStructure, setCppAllowed } from './map';
// Output Directory // Arguments
let outputDirectory = process.argv[2]; if (process.argv.length < 5) {
if (!outputDirectory) { console.log('USAGE: npm run <Source Output File> <Header Output File> <Input Files...>');
outputDirectory = `${__dirname}/../out`; process.exit(1);
} }
fs.rmSync(outputDirectory, {force: true, recursive: true}); process.argv.shift();
fs.mkdirSync(outputDirectory, {recursive: true}); process.argv.shift();
function invalidFileType(file: string) {
// Main throw new Error(`Invalid File Type: ${file}`);
function loadSymbols() { }
// Load const sourceOutput = process.argv.shift()!;
const files = fs.readdirSync(`${__dirname}/../symbols`); if (!sourceOutput.endsWith('.cpp')) {
const structureObjects = []; invalidFileType(sourceOutput);
for (const file of files) { }
const headerOutput = process.argv.shift()!;
if (!headerOutput.endsWith('.h')) {
invalidFileType(headerOutput);
}
const extraHeaders: string[] = [];
while (process.argv.length > 0) {
const file = process.argv.shift()!;
if (file.endsWith(EXTENSION)) { if (file.endsWith(EXTENSION)) {
// Get Name // Get Class Name
const parts = file.split('/'); const parts = file.split('/');
let name = parts[parts.length - 1]!; let name = parts[parts.length - 1]!;
name = name.substring(0, name.length - EXTENSION.length); name = name.substring(0, name.length - EXTENSION.length);
// Structures // Store
structureObjects.push(getStructure(name)); STRUCTURE_FILES[name] = file;
} else if (file.endsWith('.h')) {
// Extra Headers
extraHeaders.push(file);
} else {
// Invalid File Type
invalidFileType(file);
} }
}
// Load And Sort Structures
function loadSymbols() {
// Load
const structureObjects = [];
for (const name in STRUCTURE_FILES) {
structureObjects.push(getStructure(name));
} }
// Sort // Sort
structureObjects.sort((a, b) => { structureObjects.sort((a, b) => {
@ -38,20 +59,15 @@ function loadSymbols() {
// Return // Return
return structureObjects; return structureObjects;
} }
function makeHeader(output: string, allowCpp: boolean) {
// Generate Part Of Header
function makeHeaderPart(allowCpp: boolean) {
// Set Mode // Set Mode
setCppAllowed(allowCpp); setCppAllowed(allowCpp);
// Load Symbols // Load Symbols
const structureObjects = loadSymbols(); const structureObjects = loadSymbols();
// Forward Declarations
let forwardDeclarations = '// Forward Declarations\n';
for (const structure of structureObjects) {
const name = structure.getName();
forwardDeclarations += `typedef struct ${name} ${name};\n`;
}
// Sort Structures By Dependency // Sort Structures By Dependency
while (true) { while (true) {
let valid = true; let valid = true;
@ -84,9 +100,15 @@ function makeHeader(output: string, allowCpp: boolean) {
// Generate Code // Generate Code
let structures = ''; let structures = '';
let isFirst = true;
for (const structure of structureObjects) { for (const structure of structureObjects) {
const name = structure.getName(); const name = structure.getName();
structures += `\n// ${name}\n`; if (isFirst) {
isFirst = false;
} else {
structures += '\n';
}
structures += `// ${name}\n`;
try { try {
structures += structure.generate(); structures += structure.generate();
} catch (e) { } catch (e) {
@ -95,22 +117,50 @@ function makeHeader(output: string, allowCpp: boolean) {
} }
} }
// Write // Return
let result = ''; return structures;
result += '#pragma once\n';
result += '\n';
result += forwardDeclarations;
result += '\n// Init\n';
result += 'void init_symbols();';
result += '\n// Extra Definitions\n';
result += fs.readFileSync(`${__dirname}/../symbols/extra.h`, {encoding: 'utf8'}).trim() + '\n';
result += structures;
fs.writeFileSync(output, result);
} }
// Run // Create Main Header
makeHeader(`${outputDirectory}/minecraft_c.h`, false); function makeMainHeader(output: string) {
makeHeader(`${outputDirectory}/minecraft_cpp.h`, true); let result = '';
result += '#pragma once\n';
result += '\n// Suppress Warnings\n';
result += '#pragma GCC diagnostic push\n';
result += '#pragma GCC diagnostic ignored "-Wunused-variable"\n';
result += '#pragma GCC diagnostic ignored "-Wunused-function"\n';
result += '\n// Shortcuts\n';
result += 'typedef unsigned char uchar;\n';
result += 'typedef unsigned int uint;\n';
result += '\n// Init\n';
result += 'void init_symbols();\n';
result += '\n// Forward Declarations\n';
for (const name in STRUCTURE_FILES) {
result += `typedef struct ${name} ${name};\n`;
}result += '\n// Extra Headers\n';
for (const file of extraHeaders) {
result += fs.readFileSync(file, {encoding: 'utf8'});
}
result += '\n// Switch Mode\n';
result += '#ifndef __cplusplus\n';
result += '\n// C Mode\n';
result += 'typedef uchar bool;\n\n';
result += makeHeaderPart(false);
result += '\n// End C Mode\n';
result += '#else\n';
result += '\n// C++ Mode\n';
result += '#include <string>\n';
result += '#include <vector>\n';
result += 'extern "C" {\n\n';
result += makeHeaderPart(true);
result += '\n// End C++ Mode\n';
result += '}\n';
result += '#endif\n';
result += '\n// Stop Suppressing Warnings\n';
result += '#pragma GCC diagnostic pop\n';
fs.writeFileSync(output, result);
}
makeMainHeader(headerOutput);
// Generate Compiled Code // Generate Compiled Code
function makeCompiledCode(output: string) { function makeCompiledCode(output: string) {
@ -122,10 +172,17 @@ function makeCompiledCode(output: string) {
// Generate // Generate
let declarations = ''; let declarations = '';
let init = ''; let init = '';
let isFirst = true;
for (const structure of structureObjects) { for (const structure of structureObjects) {
const name = structure.getName(); const name = structure.getName();
declarations += `\n// ${name}\n`; if (isFirst) {
init += `\n${INDENT}// ${name}\n`; isFirst = false;
} else {
declarations += '\n';
init += '\n';
}
declarations += `// ${name}\n`;
init += `${INDENT}// ${name}\n`;
try { try {
const code = structure.generateCode(); const code = structure.generateCode();
declarations += code.functions; declarations += code.functions;
@ -138,39 +195,13 @@ function makeCompiledCode(output: string) {
// Write // Write
let result = ''; let result = '';
result += '#include "minecraft.h"\n'; result += `#include "${fs.realpathSync(headerOutput)}"\n`;
result += '\n#include <cstring>\n';
result += '\n// Init\n'; result += '\n// Init\n';
result += 'void init_symbols() {'; result += 'void init_symbols() {\n';
result += init; result += init;
result += '}\n'; result += '}\n\n';
result += declarations; result += declarations;
fs.writeFileSync(output, result); fs.writeFileSync(output, result);
} }
makeCompiledCode(`${outputDirectory}/minecraft.cpp`); makeCompiledCode(sourceOutput);
// Create Main Header
function makeMainHeader(output: string) {
let result = '';
result += '#pragma once\n';
result += '#pragma GCC diagnostic push\n';
result += '#pragma GCC diagnostic ignored "-Wunused-variable"\n';
result += '#pragma GCC diagnostic ignored "-Wunused-function"\n';
result += 'typedef unsigned char uchar;\n';
result += 'typedef unsigned int uint;\n';
result += '#include <string.h>\n';
result += '#include <stdlib.h>\n';
result += '#include <stddef.h>\n';
result += '#ifndef __cplusplus\n';
result += 'typedef unsigned char bool;\n';
result += '#include "minecraft_c.h"\n';
result += '#else\n';
result += '#include <string>\n';
result += '#include <vector>\n';
result += 'extern "C" {\n';
result += '#include "minecraft_cpp.h"\n';
result += '}\n';
result += '#endif\n';
result += '#pragma GCC diagnostic pop\n';
fs.writeFileSync(output, result);
}
makeMainHeader(`${outputDirectory}/minecraft.h`);

View File

@ -4,6 +4,7 @@ import { Method } from './method';
import { SimpleProperty, StaticProperty } from './property'; import { SimpleProperty, StaticProperty } from './property';
import { Struct } from './struct'; import { Struct } from './struct';
// Error Handling
function safeParseInt(str: string) { function safeParseInt(str: string) {
const x = parseInt(str); const x = parseInt(str);
if (isNaN(x)) { if (isNaN(x)) {
@ -14,7 +15,6 @@ function safeParseInt(str: string) {
function syntaxError(message?: string) { function syntaxError(message?: string) {
throw new Error('Syntax Error' + (message ? `: ${message}` : '')); throw new Error('Syntax Error' + (message ? `: ${message}` : ''));
} }
export class ErrorOnLine { export class ErrorOnLine {
readonly error: unknown; readonly error: unknown;
readonly file: string; readonly file: string;
@ -199,14 +199,6 @@ export function load(target: Struct, name: string, isExtended: boolean) {
} }
break; break;
} }
case 'requires': {
// Add Dependency
if (args.length === 0) {
syntaxError('Missing Dependency Name');
}
target.addDependency(args);
break;
}
default: { default: {
throw new Error(`Invalid Command: ${command}`); throw new Error(`Invalid Command: ${command}`);
} }

View File

@ -16,7 +16,7 @@ const structures: {[id: string]: Struct} = {};
// Get Or Load Structure // Get Or Load Structure
export function getStructure(name: string) { export function getStructure(name: string) {
if (Object.prototype.hasOwnProperty.call(structures, name)) { if (name in structures) {
// Already Loaded // Already Loaded
return structures[name]!; return structures[name]!;
} else { } else {
@ -45,8 +45,6 @@ export function getStructure(name: string) {
// Clear Loaded Structures // Clear Loaded Structures
function clearStructures() { function clearStructures() {
for (const name in structures) { for (const name in structures) {
if (Object.prototype.hasOwnProperty.call(structures, name)) {
delete structures[name]; delete structures[name];
} }
}
} }

View File

@ -1,4 +1,4 @@
import { INDENT, MIN_SIZE, toUpperSnakeCase, formatType } from './common'; import { INDENT, MIN_SIZE, toUpperSnakeCase, formatType, STRUCTURE_FILES } from './common';
import { isCppAllowed } from './map'; import { isCppAllowed } from './map';
import { Method } from './method'; import { Method } from './method';
import { Property, StaticProperty } from './property'; import { Property, StaticProperty } from './property';
@ -25,7 +25,7 @@ export class Struct {
} }
// Dependencies // Dependencies
addDependency(dependency: string) { #addDependency(dependency: string) {
this.#dependencies.push(dependency); this.#dependencies.push(dependency);
} }
getDependencies() { getDependencies() {
@ -94,6 +94,11 @@ export class Struct {
// Add Property // Add Property
addProperty(property: Property) { addProperty(property: Property) {
this.#properties.push(property); this.#properties.push(property);
// Add Dependency If Needed
const type = property.propertyType();
if (type in STRUCTURE_FILES) {
this.#addDependency(type);
}
} }
// Add Static Property // Add Static Property
addStaticProperty(property: StaticProperty) { addStaticProperty(property: StaticProperty) {