Add some codegen infrastructure
This commit is contained in:
parent
8200a0095f
commit
f43a66f453
6 changed files with 559 additions and 12 deletions
|
@ -14,6 +14,7 @@ import { Checker } from "../checker"
|
||||||
import { SourceFile, TextFile } from "../cst"
|
import { SourceFile, TextFile } from "../cst"
|
||||||
import { parseSourceFile } from ".."
|
import { parseSourceFile } from ".."
|
||||||
import { Analyser } from "../analysis"
|
import { Analyser } from "../analysis"
|
||||||
|
import { Program } from "../program"
|
||||||
|
|
||||||
function debug(value: any) {
|
function debug(value: any) {
|
||||||
console.error(util.inspect(value, { colors: true, depth: Infinity }));
|
console.error(util.inspect(value, { colors: true, depth: Infinity }));
|
||||||
|
@ -37,18 +38,18 @@ yargs
|
||||||
const filename = path.resolve(cwd, args.file);
|
const filename = path.resolve(cwd, args.file);
|
||||||
|
|
||||||
const diagnostics = new ConsoleDiagnostics();
|
const diagnostics = new ConsoleDiagnostics();
|
||||||
const text = fs.readFileSync(filename, 'utf8')
|
const program = new Program([ filename ]);
|
||||||
const file = new TextFile(filename, text);
|
if (program.diagnostics.hasError) {
|
||||||
const sourceFile = parseSourceFile(file, diagnostics);
|
process.exit(1);
|
||||||
if (sourceFile === null) {
|
}
|
||||||
|
program.check();
|
||||||
|
if (program.diagnostics.hasError) {
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
program.emit();
|
||||||
|
if (program.diagnostics.hasError) {
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
//debug(sourceFile.toJSON());
|
|
||||||
const analyser = new Analyser();
|
|
||||||
analyser.addSourceFile(sourceFile);
|
|
||||||
const checker = new Checker(analyser, diagnostics);
|
|
||||||
checker.check(sourceFile);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.help()
|
.help()
|
||||||
|
|
379
src/cast.ts
Normal file
379
src/cast.ts
Normal file
|
@ -0,0 +1,379 @@
|
||||||
|
|
||||||
|
import type stream from "stream"
|
||||||
|
import { IndentWriter } from "./util";
|
||||||
|
|
||||||
|
export const enum CNodeKind {
|
||||||
|
|
||||||
|
// Types
|
||||||
|
BuiltinType,
|
||||||
|
|
||||||
|
// Statements
|
||||||
|
ExprStmt,
|
||||||
|
RetStmt,
|
||||||
|
|
||||||
|
// Expressions
|
||||||
|
CallExpr,
|
||||||
|
RefExpr,
|
||||||
|
ConstExpr,
|
||||||
|
|
||||||
|
// Declarations
|
||||||
|
TypeDecl,
|
||||||
|
VarDecl,
|
||||||
|
FuncDecl,
|
||||||
|
|
||||||
|
// Directives
|
||||||
|
IncDir,
|
||||||
|
|
||||||
|
// Other nodes
|
||||||
|
Program,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum CBuiltinTypeKind {
|
||||||
|
Char,
|
||||||
|
Short,
|
||||||
|
Int,
|
||||||
|
Long,
|
||||||
|
LongLong,
|
||||||
|
UnsignedChar,
|
||||||
|
UnsignedShort,
|
||||||
|
UnsignedInt,
|
||||||
|
UnsignedLong,
|
||||||
|
UnsignedLongLong,
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class CNodeBase {
|
||||||
|
public abstract readonly kind: CNodeKind;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CBuiltinType extends CNodeBase {
|
||||||
|
|
||||||
|
public readonly kind = CNodeKind.BuiltinType;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public typeKind: CBuiltinTypeKind,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CType
|
||||||
|
= CBuiltinType
|
||||||
|
|
||||||
|
export class CRefExpr extends CNodeBase {
|
||||||
|
|
||||||
|
public readonly kind = CNodeKind.RefExpr;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public name: string
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CCallExpr extends CNodeBase {
|
||||||
|
|
||||||
|
public readonly kind = CNodeKind.CallExpr;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public operator: CExpr,
|
||||||
|
public args: CExpr[],
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CConstExpr extends CNodeBase {
|
||||||
|
|
||||||
|
public readonly kind = CNodeKind.ConstExpr;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public value: bigint | string | boolean,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CExpr
|
||||||
|
= CRefExpr
|
||||||
|
| CCallExpr
|
||||||
|
| CConstExpr
|
||||||
|
;
|
||||||
|
|
||||||
|
export class CRetStmt extends CNodeBase {
|
||||||
|
|
||||||
|
public readonly kind = CNodeKind.RetStmt;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public value: CExpr | null,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CExprStmt extends CNodeBase {
|
||||||
|
|
||||||
|
public readonly kind = CNodeKind.ExprStmt;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public expr: CExpr,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CStmt
|
||||||
|
= CExprStmt
|
||||||
|
| CRetStmt;
|
||||||
|
|
||||||
|
export class CTypeDecl extends CNodeBase {
|
||||||
|
|
||||||
|
public readonly kind = CNodeKind.TypeDecl;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public name: string,
|
||||||
|
public type: CType,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CFuncDecl extends CNodeBase {
|
||||||
|
|
||||||
|
public readonly kind = CNodeKind.FuncDecl;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public returnType: CType,
|
||||||
|
public name: string,
|
||||||
|
public params: Array<[CType, string]>,
|
||||||
|
public body: CStmt[] | null,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CVarDecl extends CNodeBase {
|
||||||
|
|
||||||
|
public readonly kind = CNodeKind.VarDecl;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public isExtern: boolean,
|
||||||
|
public type: CType,
|
||||||
|
public name: string,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CDecl
|
||||||
|
= CTypeDecl
|
||||||
|
| CVarDecl
|
||||||
|
| CFuncDecl
|
||||||
|
|
||||||
|
export class CIncDir extends CNodeBase {
|
||||||
|
|
||||||
|
public readonly kind = CNodeKind.IncDir;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public filePath: string,
|
||||||
|
public isSystem = false,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CDir
|
||||||
|
= CIncDir;
|
||||||
|
|
||||||
|
export class CProgram extends CNodeBase {
|
||||||
|
|
||||||
|
public readonly kind = CNodeKind.Program;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public elements: (CDecl | CDir)[],
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CNode
|
||||||
|
= CDecl
|
||||||
|
| CDir
|
||||||
|
| CStmt
|
||||||
|
| CExpr
|
||||||
|
| CType
|
||||||
|
| CProgram
|
||||||
|
|
||||||
|
export class CEmitter {
|
||||||
|
|
||||||
|
private writer: IndentWriter;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public stream: stream.Writable,
|
||||||
|
) {
|
||||||
|
this.writer = new IndentWriter(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
public emit(node: CNode): void {
|
||||||
|
|
||||||
|
switch (node.kind) {
|
||||||
|
|
||||||
|
case CNodeKind.Program:
|
||||||
|
{
|
||||||
|
for (const element of node.elements) {
|
||||||
|
this.emit(element);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CNodeKind.IncDir:
|
||||||
|
{
|
||||||
|
this.writer.write('#include ');
|
||||||
|
this.writer.write(node.isSystem ? '<' : '"');
|
||||||
|
this.writer.write(node.filePath);
|
||||||
|
this.writer.write(node.isSystem ? '>' : '"');
|
||||||
|
this.writer.write('\n\n');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CNodeKind.BuiltinType:
|
||||||
|
{
|
||||||
|
switch (node.typeKind) {
|
||||||
|
case CBuiltinTypeKind.Char:
|
||||||
|
this.writer.write('char');
|
||||||
|
break;
|
||||||
|
case CBuiltinTypeKind.Short:
|
||||||
|
this.writer.write('short');
|
||||||
|
break;
|
||||||
|
case CBuiltinTypeKind.Int:
|
||||||
|
this.writer.write('int');
|
||||||
|
break;
|
||||||
|
case CBuiltinTypeKind.Long:
|
||||||
|
this.writer.write('long');
|
||||||
|
break;
|
||||||
|
case CBuiltinTypeKind.LongLong:
|
||||||
|
this.writer.write('long long');
|
||||||
|
break;
|
||||||
|
case CBuiltinTypeKind.UnsignedChar:
|
||||||
|
this.writer.write('unsigned char');
|
||||||
|
break;
|
||||||
|
case CBuiltinTypeKind.UnsignedShort:
|
||||||
|
this.writer.write('unsigned short');
|
||||||
|
break;
|
||||||
|
case CBuiltinTypeKind.UnsignedInt:
|
||||||
|
this.writer.write('unsigned int');
|
||||||
|
break;
|
||||||
|
case CBuiltinTypeKind.UnsignedLong:
|
||||||
|
this.writer.write('unsigned long');
|
||||||
|
break;
|
||||||
|
case CBuiltinTypeKind.UnsignedLongLong:
|
||||||
|
this.writer.write('unsigned long long');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CNodeKind.FuncDecl:
|
||||||
|
{
|
||||||
|
this.emit(node.returnType);
|
||||||
|
this.writer.write(' ' + node.name + '(');
|
||||||
|
let count = 0;
|
||||||
|
for (const [type, name] of node.params) {
|
||||||
|
this.emit(type);
|
||||||
|
this.writer.write(' ' + name);
|
||||||
|
if (count++ > 0) {
|
||||||
|
this.writer.write(', ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.writer.write(') {\n');
|
||||||
|
this.writer.indent();
|
||||||
|
if (node.body !== null) {
|
||||||
|
for (const element of node.body) {
|
||||||
|
this.emit(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.writer.dedent();
|
||||||
|
this.writer.write('}\n\n');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CNodeKind.ExprStmt:
|
||||||
|
this.emit(node.expr);
|
||||||
|
this.writer.write(';\n');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CNodeKind.RetStmt:
|
||||||
|
{
|
||||||
|
this.writer.write('return');
|
||||||
|
if (node.value !== null) {
|
||||||
|
this.writer.write(' ');
|
||||||
|
this.emit(node.value);
|
||||||
|
}
|
||||||
|
this.writer.write(';\n');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CNodeKind.RefExpr:
|
||||||
|
this.writer.write(node.name);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CNodeKind.CallExpr:
|
||||||
|
{
|
||||||
|
this.emit(node.operator);
|
||||||
|
this.writer.write('(');
|
||||||
|
let count = 0;
|
||||||
|
for (const arg of node.args) {
|
||||||
|
this.emit(arg);
|
||||||
|
if (count++ > 0) {
|
||||||
|
this.writer.write(', ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.writer.write(')');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CNodeKind.ConstExpr:
|
||||||
|
{
|
||||||
|
if (typeof(node.value) === 'string') {
|
||||||
|
this.writer.write('"');
|
||||||
|
for (const ch of node.value) {
|
||||||
|
switch (ch) {
|
||||||
|
case '\b': this.writer.write('\\b'); break;
|
||||||
|
case '\f': this.writer.write('\\f'); break;
|
||||||
|
case '\n': this.writer.write('\\n'); break;
|
||||||
|
case '\r': this.writer.write('\\r'); break;
|
||||||
|
case '\t': this.writer.write('\\t'); break;
|
||||||
|
case '\v': this.writer.write('\\v'); break;
|
||||||
|
case '\0': this.writer.write('\\0'); break;
|
||||||
|
case '\'': this.writer.write('\\\''); break;
|
||||||
|
case '"': this.writer.write('\\"'); break;
|
||||||
|
default: this.writer.write(ch); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.writer.write('"');
|
||||||
|
} else if (typeof(node.value) === 'bigint') {
|
||||||
|
this.writer.write(node.value.toString());
|
||||||
|
} else {
|
||||||
|
throw new Error(`Unexpected type of value in CConstExpr`);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error(`Unexpected ${node.constructor.name}`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
82
src/codegen.ts
Normal file
82
src/codegen.ts
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
|
||||||
|
import { CBuiltinType, CBuiltinTypeKind, CCallExpr, CConstExpr, CDecl, CDir, CExpr, CExprStmt, CFuncDecl, CIncDir, CProgram, CRefExpr, CStmt } from "./cast";
|
||||||
|
import { Expression, SourceFile, Syntax, SyntaxKind } from "./cst";
|
||||||
|
import { assert } from "./util";
|
||||||
|
|
||||||
|
interface Context {
|
||||||
|
body: CStmt[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateCode(sourceFile: SourceFile): CProgram {
|
||||||
|
|
||||||
|
const intType = new CBuiltinType(CBuiltinTypeKind.Int);
|
||||||
|
|
||||||
|
const decls: (CDecl | CDir)[] = [];
|
||||||
|
|
||||||
|
decls.push(new CIncDir("runtime.h"));
|
||||||
|
|
||||||
|
const mainBody: CStmt[] = [];
|
||||||
|
|
||||||
|
decls.push(
|
||||||
|
new CFuncDecl(
|
||||||
|
intType,
|
||||||
|
'main',
|
||||||
|
[],
|
||||||
|
mainBody
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
visit(sourceFile, { body: mainBody });
|
||||||
|
|
||||||
|
return new CProgram(decls);
|
||||||
|
|
||||||
|
function visit(node: Syntax, context: Context): void {
|
||||||
|
|
||||||
|
switch (node.kind) {
|
||||||
|
|
||||||
|
case SyntaxKind.SourceFile:
|
||||||
|
{
|
||||||
|
for (const element of node.elements) {
|
||||||
|
visit(element, context);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SyntaxKind.ExpressionStatement:
|
||||||
|
{
|
||||||
|
context.body.push(
|
||||||
|
new CExprStmt(
|
||||||
|
visitExpression(node.expression, context)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SyntaxKind.LetDeclaration:
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function visitExpression(node: Expression, context: Context): CExpr {
|
||||||
|
switch (node.kind) {
|
||||||
|
case SyntaxKind.ReferenceExpression:
|
||||||
|
assert(node.modulePath.length === 0);
|
||||||
|
return new CRefExpr(node.name.text);
|
||||||
|
case SyntaxKind.CallExpression:
|
||||||
|
const operator = visitExpression(node.func, context);
|
||||||
|
const args = node.args.map(arg => visitExpression(arg, context));
|
||||||
|
return new CCallExpr(operator, args);
|
||||||
|
case SyntaxKind.ConstantExpression:
|
||||||
|
return new CConstExpr(node.token.getValue());
|
||||||
|
default:
|
||||||
|
throw new Error(`Unexpected ${node}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
12
src/cst.ts
12
src/cst.ts
|
@ -3,6 +3,10 @@ import type { InferContext, Kind, Scheme, Type, TypeEnv } from "./checker"
|
||||||
|
|
||||||
export type TextSpan = [number, number];
|
export type TextSpan = [number, number];
|
||||||
|
|
||||||
|
export type Value
|
||||||
|
= bigint
|
||||||
|
| string
|
||||||
|
|
||||||
export class TextPosition {
|
export class TextPosition {
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
|
@ -574,6 +578,10 @@ export class Integer extends TokenBase {
|
||||||
super(startPos);
|
super(startPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getValue(): Value {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
public get text(): string {
|
public get text(): string {
|
||||||
switch (this.radix) {
|
switch (this.radix) {
|
||||||
case 16:
|
case 16:
|
||||||
|
@ -602,6 +610,10 @@ export class StringLiteral extends TokenBase {
|
||||||
super(startPos);
|
super(startPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getValue(): Value {
|
||||||
|
return this.contents;
|
||||||
|
}
|
||||||
|
|
||||||
public get text(): string {
|
public get text(): string {
|
||||||
let out = '"';
|
let out = '"';
|
||||||
for (const ch of this.contents) {
|
for (const ch of this.contents) {
|
||||||
|
|
|
@ -359,6 +359,8 @@ export type Diagnostic
|
||||||
| KindMismatchDiagnostic
|
| KindMismatchDiagnostic
|
||||||
|
|
||||||
export interface Diagnostics {
|
export interface Diagnostics {
|
||||||
|
readonly hasError: boolean;
|
||||||
|
readonly hasFatal: boolean;
|
||||||
add(diagnostic: Diagnostic): void;
|
add(diagnostic: Diagnostic): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -386,10 +388,20 @@ export class DiagnosticStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ConsoleDiagnostics {
|
export class ConsoleDiagnostics {
|
||||||
|
|
||||||
|
private writer = new IndentWriter(process.stderr);
|
||||||
|
|
||||||
|
public hasError = false;
|
||||||
|
public hasFatal = false;
|
||||||
|
|
||||||
public add(diagnostic: Diagnostic): void {
|
public add(diagnostic: Diagnostic): void {
|
||||||
const writer = new IndentWriter(process.stderr);
|
diagnostic.format(this.writer);
|
||||||
diagnostic.format(writer);
|
if (diagnostic.level >= Level.Error) {
|
||||||
|
this.hasError = true;
|
||||||
|
}
|
||||||
|
if (diagnostic.level >= Level.Fatal) {
|
||||||
|
this.hasFatal = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
61
src/program.ts
Normal file
61
src/program.ts
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
import path from "path";
|
||||||
|
import fs from "fs"
|
||||||
|
import { parseSourceFile } from ".";
|
||||||
|
import { SourceFile, TextFile } from "./cst";
|
||||||
|
import { ConsoleDiagnostics, Diagnostics } from "./diagnostics";
|
||||||
|
import { Checker } from "./checker";
|
||||||
|
import { Analyser } from "./analysis";
|
||||||
|
import { generateCode } from "./codegen";
|
||||||
|
import { CEmitter } from "./cast";
|
||||||
|
|
||||||
|
export class Program {
|
||||||
|
|
||||||
|
private sourceFilesByPath = new Map<string, SourceFile>();
|
||||||
|
|
||||||
|
private analyser = new Analyser();
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
public fileNames: string[],
|
||||||
|
public diagnostics: Diagnostics = new ConsoleDiagnostics(),
|
||||||
|
) {
|
||||||
|
for (const fileName of fileNames) {
|
||||||
|
const realPath = path.resolve(fileName);
|
||||||
|
const text = fs.readFileSync(realPath, 'utf-8');
|
||||||
|
const file = new TextFile(fileName, text);
|
||||||
|
const sourceFile = parseSourceFile(file, diagnostics);
|
||||||
|
if (sourceFile !== null) {
|
||||||
|
this.sourceFilesByPath.set(realPath, sourceFile);
|
||||||
|
this.analyser.addSourceFile(sourceFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getSourceFiles(): Iterable<SourceFile> {
|
||||||
|
return this.sourceFilesByPath.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
public check(): void {
|
||||||
|
const checker = new Checker(this.analyser, this.diagnostics);
|
||||||
|
for (const sourceFile of this.getSourceFiles()) {
|
||||||
|
checker.check(sourceFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public emit(): void {
|
||||||
|
for (const [filePath, sourceFile] of this.sourceFilesByPath) {
|
||||||
|
const file = fs.createWriteStream(stripExtension(filePath) + '.c', 'utf-8');
|
||||||
|
const emitter = new CEmitter(file);
|
||||||
|
emitter.emit(generateCode(sourceFile));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function stripExtension(filepath: string): string {
|
||||||
|
const basename = path.basename(filepath);
|
||||||
|
const i = basename.lastIndexOf('.');
|
||||||
|
if (i === -1) {
|
||||||
|
return filepath;
|
||||||
|
}
|
||||||
|
return path.join(path.dirname(filepath), basename.substring(0, i));
|
||||||
|
}
|
Loading…
Reference in a new issue