js-slang
Version:
Javascript-based implementations of Source, written in Typescript
98 lines • 4.7 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.FullTSParser = void 0;
const parser_1 = require("@babel/parser");
const bootstrap_1 = require("@ts-morph/bootstrap");
const typeErrorChecker_1 = require("../../typeChecker/typeErrorChecker");
const errors_1 = require("../errors");
const utils_1 = require("../source/typed/utils");
const utils_2 = require("../utils");
const IMPORT_TOP_LEVEL_ERROR = 'An import declaration can only be used at the top level of a namespace or module.';
const START_OF_MODULE_ERROR = 'Cannot find module ';
class FullTSParser {
parse(programStr, context, options, throwOnError) {
let code = '';
// Add builtins to code
// Each declaration is replaced with a single constant declaration with type `any`
// to reduce evaluation time
for (const builtin of context.nativeStorage.builtins) {
code += `const ${builtin[0]}: any = 1\n`;
}
// Add prelude functions to code
// Each declaration is replaced with a single constant declaration with type `any`
// to reduce evaluation time
if (context.prelude) {
const preludeFns = context.prelude.split('\nfunction ').slice(1);
preludeFns.forEach(fnString => {
const fnName = fnString.split('(')[0];
// Functions in prelude that start with $ are not added
if (fnName.startsWith('$')) {
return;
}
code += `const ${fnName}: any = 1\n`;
});
}
// Get line offset
const lineOffset = code.split('\n').length - 1;
// Add program string to code string,
// wrapping it in a block to allow redeclaration of variables
code = code + '{' + programStr + '}';
// Initialize file to analyze
const project = (0, bootstrap_1.createProjectSync)({ useInMemoryFileSystem: true });
const filename = 'program.ts';
project.createSourceFile(filename, code);
// Get TS diagnostics from file, formatted as TS error string
const diagnostics = bootstrap_1.ts.getPreEmitDiagnostics(project.createProgram());
const formattedString = project.formatDiagnosticsWithColorAndContext(diagnostics);
// Reformat TS error string to Source error by getting line number using regex
// This is because logic to retrieve line number is only present in
// formatDiagnosticsWithColorAndContext and cannot be called directly
const lineNumRegex = /(?<=\[7m)\d+/;
diagnostics.forEach(diagnostic => {
const message = diagnostic.messageText.toString();
// Ignore errors regarding imports
// as TS does not have information about Source modules
if (message === IMPORT_TOP_LEVEL_ERROR || message.startsWith(START_OF_MODULE_ERROR)) {
return;
}
const lineNumRegExpArr = lineNumRegex.exec(formattedString.split(message)[1]);
const lineNum = (lineNumRegExpArr === null ? 0 : parseInt(lineNumRegExpArr[0])) - lineOffset;
// Ignore any errors that occur in builtins/prelude (line number <= 0)
if (lineNum <= 0) {
return;
}
const position = { line: lineNum, column: 0, offset: 0 };
context.errors.push(new errors_1.FatalSyntaxError((0, utils_2.positionToSourceLocation)(position), message));
});
if (context.errors.length > 0) {
return null;
}
// Parse code into Babel AST, which supports type syntax
const ast = (0, parser_1.parse)(programStr, {
...utils_2.defaultBabelOptions,
sourceFilename: options?.sourceFile,
errorRecovery: throwOnError ?? true
});
if (ast.errors.length) {
ast.errors
.filter(error => error instanceof SyntaxError)
.forEach(error => {
context.errors.push(new errors_1.FatalSyntaxError((0, utils_2.positionToSourceLocation)(error.loc, options?.sourceFile), error.toString()));
});
return null;
}
// Transform Babel AST into ESTree AST
const typedProgram = ast.program;
const transpiledProgram = (0, typeErrorChecker_1.removeTSNodes)(typedProgram);
(0, utils_1.transformBabelASTToESTreeCompliantAST)(transpiledProgram);
return transpiledProgram;
}
validate(_ast, _context, _throwOnError) {
return true;
}
toString() {
return 'FullTSParser';
}
}
exports.FullTSParser = FullTSParser;
//# sourceMappingURL=index.js.map
;