UNPKG

jaycc

Version:

### the Javascript C Compiler

190 lines (189 loc) 4.89 kB
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'); }