jaycc
Version:
### the Javascript C Compiler
190 lines (189 loc) • 4.89 kB
JavaScript
let Version;
Version = '0.0.6';
import { FileLexer } from './lexer.js';
import { Parser } from './parser.js';
import { architecture, CodeGen, state } from './codegen.js';
import { open, readFile, writeFile } from 'node:fs/promises';
import { execFileSync } from 'node:child_process';
var Stage;
(function (Stage) {
Stage[Stage["Linker"] = 0] = "Linker";
Stage[Stage["Assembler"] = 1] = "Assembler";
Stage[Stage["CodeGenerator"] = 2] = "CodeGenerator";
})(Stage || (Stage = {}));
let config;
config = (arch) => ({
env: '/usr/bin/env',
asm: 'nasm',
linker: 'ld',
arch,
stopat: Stage.Linker
});
class Compiler {
inputfile;
outputfile;
tokens;
ast;
parser;
state;
lexer;
sourcefile;
code;
asm;
config;
constructor(inputfile, cfg, outputfile) {
this.state = state();
this.inputfile = inputfile;
this.outputfile = outputfile
?? this.replaceext(this.inputfile, '');
this.config = cfg;
}
replaceext(file, ext) {
let regexp;
let ret;
regexp = /\..+$/;
ret = file
.replace(regexp, ext);
return ret;
}
async compile() {
let fd;
let asmfile;
let ret;
fd = await open(this.inputfile, 'r');
if (!fd) {
throw new Error('Unable to open file: ' + this.inputfile);
return false;
}
this.sourcefile = await readFile(fd, { encoding: 'utf8', flag: 'r' });
fd.close();
if (!this.sourcefile) {
throw new Error('Unable to read input file');
return false;
}
this.lexer = this.lex();
this.tokens = this.lexer.tokens;
this.ast = this.parse();
this.asm = this.codegen();
asmfile = this.replaceext(this.inputfile, '.asm');
fd = await open(asmfile, 'w');
if (!fd) {
throw new Error('Unable to open file: ' + asmfile);
return false;
}
ret = await writeFile(fd, this.asm, { encoding: 'utf8', mode: 0o644, flag: 'w' });
if (ret !== undefined) {
throw new Error('Unable to write to file: ' + asmfile);
return false;
}
fd.close();
if (this.config.stopat == Stage.CodeGenerator)
return true;
else
await this.link(asmfile);
return true;
}
lex() {
return new FileLexer(this.sourcefile ?? '');
}
parse() {
this.parser = new Parser(this.tokens ?? []);
return this.parser.parse();
}
codegen() {
this.code = new CodeGen(this.state, this.ast, this.config.arch);
return this.code.emit;
}
link(asmfile) {
let objectfile;
try {
execFileSync(this.config.env, [this.config.asm, '-f', 'elf', asmfile]);
}
catch (err) {
if (err && err instanceof Error) {
throw err;
return void 0;
}
}
if (this.config.stopat == Stage.Assembler)
return void 0;
objectfile = this.replaceext(this.inputfile, '.o');
try {
execFileSync(this.config.env, [this.config.linker, objectfile, '-o', this.outputfile]);
}
catch (err) {
if (err && err instanceof Error) {
throw err;
return void 0;
}
}
}
}
let configuration;
let comp;
let retval;
let basename;
let args;
let argc;
let dashs;
let dashc;
let dasho;
let input;
let usage;
let shver;
shver = () => {
console.log(`jcc version ${Version} (the javascript c compiler)`);
process.exit(0);
return true;
};
usage = () => {
console.warn('Usage: jcc [-v] inputfile.c -o outputfile [ -s | -c ]');
process.exit(1);
return false;
};
basename = (file) => {
let x;
let regexp;
regexp = /[^/]+$/;
x = file.replace(regexp, '');
return file
.replace(x, '');
};
args = process.argv;
if (basename(args[0]) == 'node')
args.shift();
if (!args.length)
usage();
else
args.shift();
argc = args.length;
if ((argc) && (args[0] == '-v'))
shver();
if ((argc < 3) || (argc > 4))
usage();
if (argc == 4) {
dashs = (args[3] == '-s') ? true : false;
dashc = (args[3] == '-c') ? true : false;
}
else
dashs = dashc = false;
if (args[1] != '-o')
usage();
input = args[0];
dasho = args[2];
configuration = config(architecture.ia32);
if (dashs)
configuration.stopat = Stage.CodeGenerator;
else if (dashc)
configuration.stopat = Stage.Assembler;
retval = false;
try {
comp = new Compiler(input, configuration, dasho);
retval = await comp.compile();
}
catch (err) {
if (err && (typeof err === 'object') && (err instanceof Error))
console.error(err);
else
console.error('Compilation failed');
}