2020-05-23 14:18:20 +02:00
|
|
|
|
|
|
|
import * as path from "path"
|
|
|
|
import * as fs from "fs-extra"
|
|
|
|
import { now } from "microtime"
|
|
|
|
import { EventEmitter } from "events"
|
|
|
|
|
|
|
|
import { Program } from "./program"
|
2020-05-26 21:01:38 +02:00
|
|
|
import { emitNode } from "./emitter"
|
|
|
|
import { Syntax, BoltSourceFile, SourceFile, NodeVisitor } from "./ast"
|
|
|
|
import { getFileStem, MapLike } from "./util"
|
2020-05-23 14:18:20 +02:00
|
|
|
import { verbose, memoize } from "./util"
|
2020-05-26 21:01:38 +02:00
|
|
|
import { Container, Newable } from "./di"
|
2020-05-23 14:18:20 +02:00
|
|
|
import ExpandBoltTransform from "./transforms/expand"
|
|
|
|
import CompileBoltToJSTransform from "./transforms/boltToJS"
|
|
|
|
import ConstFoldTransform from "./transforms/constFold"
|
|
|
|
import { TransformManager } from "./transforms/index"
|
2020-05-23 21:15:20 +02:00
|
|
|
import {DiagnosticPrinter} from "./diagnostics"
|
2020-05-26 21:01:38 +02:00
|
|
|
import { TypeChecker } from "./types"
|
|
|
|
import { checkServerIdentity } from "tls"
|
|
|
|
import { CheckInvalidFilePaths, CheckTypeAssignments } from "./checks"
|
|
|
|
import { SymbolResolver, BoltSymbolResolutionStrategy } from "./resolver"
|
2020-05-23 14:18:20 +02:00
|
|
|
|
|
|
|
const targetExtensions: MapLike<string> = {
|
|
|
|
'JS': '.mjs',
|
|
|
|
'Bolt': '.bolt',
|
|
|
|
'C': '.c',
|
|
|
|
};
|
|
|
|
|
|
|
|
interface TimingInfo {
|
|
|
|
timestamp: number;
|
|
|
|
refCount: number;
|
|
|
|
}
|
|
|
|
|
|
|
|
class Timing extends EventEmitter {
|
|
|
|
|
2020-05-26 21:01:38 +02:00
|
|
|
private runningTasks: MapLike<TimingInfo> = Object.create(null);
|
2020-05-23 14:18:20 +02:00
|
|
|
|
|
|
|
public start(name: string) {
|
|
|
|
if (this.runningTasks[name] !== undefined) {
|
|
|
|
this.runningTasks[name].refCount++;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.runningTasks[name] = { timestamp: now(), refCount: 1 };
|
|
|
|
this.emit(`start ${name}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
public end(name: string) {
|
|
|
|
if (this.runningTasks[name] === undefined) {
|
|
|
|
throw new Error(`Task '${name}' was never started.`);
|
|
|
|
}
|
|
|
|
const info = this.runningTasks[name];
|
|
|
|
info.refCount--;
|
|
|
|
if (info.refCount === 0) {
|
|
|
|
const usecs = now() - info.timestamp;
|
|
|
|
verbose(`Task '${name}' completed after ${usecs} microseconds.`);
|
|
|
|
this.emit(`end ${name}`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
export class Frontend {
|
|
|
|
|
2020-05-26 21:01:38 +02:00
|
|
|
//public resolver = new SymbolResolver();
|
|
|
|
//public evaluator = new Evaluator(this.resolver);
|
2020-05-23 21:15:20 +02:00
|
|
|
public diagnostics: DiagnosticPrinter;
|
2020-05-23 14:18:20 +02:00
|
|
|
public timing: Timing;
|
|
|
|
|
|
|
|
constructor() {
|
2020-05-23 21:15:20 +02:00
|
|
|
this.diagnostics = new DiagnosticPrinter();
|
2020-05-23 14:18:20 +02:00
|
|
|
this.timing = new Timing();
|
|
|
|
}
|
|
|
|
|
2020-05-26 21:01:38 +02:00
|
|
|
//@memoize(filepath => path.resolve(filepath))
|
|
|
|
//public getPackage(filepath: string) {
|
|
|
|
// const file = this.getTextFile(filepath)
|
|
|
|
// const projectFile = upsearchSync('Boltfile', path.dirname(file.fullPath));
|
|
|
|
// if (projectFile === null) {
|
|
|
|
// return null;
|
|
|
|
// }
|
|
|
|
// const projectDir = path.resolve(path.dirname(projectFile));
|
|
|
|
// return new Package(projectDir);
|
|
|
|
//}
|
|
|
|
|
|
|
|
public check(program: Program) {
|
|
|
|
|
|
|
|
const resolver = new SymbolResolver(program, new BoltSymbolResolutionStrategy);
|
|
|
|
const checker = new TypeChecker(resolver);
|
|
|
|
|
|
|
|
const container = new Container();
|
|
|
|
container.bindSelf(program);
|
|
|
|
container.bindSelf(resolver);
|
|
|
|
container.bindSelf(checker);
|
|
|
|
|
|
|
|
const checks: Newable<NodeVisitor>[] = [
|
|
|
|
CheckInvalidFilePaths,
|
|
|
|
CheckTypeAssignments,
|
|
|
|
];
|
|
|
|
|
|
|
|
const checkers = checks.map(check => container.createInstance(check));
|
2020-05-23 14:18:20 +02:00
|
|
|
|
2020-05-23 21:15:20 +02:00
|
|
|
for (const sourceFile of program.getAllSourceFiles()) {
|
2020-05-26 21:01:38 +02:00
|
|
|
resolver.registerSourceFile(sourceFile as BoltSourceFile);
|
2020-05-23 21:15:20 +02:00
|
|
|
}
|
|
|
|
for (const sourceFile of program.getAllSourceFiles()) {
|
2020-05-26 21:01:38 +02:00
|
|
|
sourceFile.visit(checkers)
|
2020-05-23 21:15:20 +02:00
|
|
|
}
|
2020-05-23 22:37:41 +02:00
|
|
|
}
|
2020-05-23 21:15:20 +02:00
|
|
|
|
2020-05-23 22:37:41 +02:00
|
|
|
public compile(program: Program, target: string) {
|
2020-05-23 21:15:20 +02:00
|
|
|
|
2020-05-25 15:52:11 +02:00
|
|
|
const container = new Container();
|
2020-05-26 21:01:38 +02:00
|
|
|
const resolver = new SymbolResolver(program, new BoltSymbolResolutionStrategy);
|
|
|
|
for (const sourceFile of program.getAllSourceFiles()) {
|
|
|
|
resolver.registerSourceFile(sourceFile as BoltSourceFile);
|
|
|
|
}
|
|
|
|
const transforms = new TransformManager(container);
|
|
|
|
container.bindSelf(transforms);
|
|
|
|
container.bindSelf(program);
|
|
|
|
container.bindSelf(resolver);
|
2020-05-25 15:52:11 +02:00
|
|
|
|
2020-05-23 14:18:20 +02:00
|
|
|
switch (target) {
|
|
|
|
|
|
|
|
case "JS":
|
|
|
|
transforms.register(ExpandBoltTransform);
|
|
|
|
transforms.register(CompileBoltToJSTransform);
|
|
|
|
transforms.register(ConstFoldTransform);
|
|
|
|
transforms.apply(program);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw new Error(`"${target}" is an invalid compile target.`);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const sourceFile of program.getAllSourceFiles()) {
|
|
|
|
fs.mkdirp('.bolt-work');
|
2020-05-26 21:01:38 +02:00
|
|
|
fs.writeFileSync(this.mapToTargetFile(sourceFile), emitNode(sourceFile), 'utf8');
|
2020-05-23 14:18:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-05-25 11:29:19 +02:00
|
|
|
private mapToTargetFile(node: SourceFile) {
|
2020-05-23 14:18:20 +02:00
|
|
|
return path.join('.bolt-work', getFileStem(node.span!.file.fullPath) + getDefaultExtension(getLanguage(node)));
|
|
|
|
}
|
|
|
|
|
|
|
|
public eval(program: Program) {
|
|
|
|
for (const sourceFile of program.getAllSourceFiles()) {
|
|
|
|
this.evaluator.eval(sourceFile)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
function getDefaultExtension(target: string) {
|
|
|
|
if (targetExtensions[target] === undefined) {
|
|
|
|
throw new Error(`Could not derive an appropriate extension for target "${target}".`)
|
|
|
|
}
|
|
|
|
return targetExtensions[target];
|
|
|
|
}
|
|
|
|
|