js-slang
Version:
Javascript-based implementations of Source, written in Typescript
226 lines • 9.95 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.assemble = exports.setBreakpointAtLine = exports.createContext = exports.compileFiles = exports.compile = exports.interrupt = exports.resume = exports.runFilesInContext = exports.runInContext = exports.getNames = exports.hasDeclaration = exports.getAllOccurrencesInScope = exports.getScope = exports.findDeclaration = exports.parseError = exports.SourceDocumentation = void 0;
const source_map_1 = require("source-map");
const createContext_1 = require("./createContext");
exports.createContext = createContext_1.default;
const errors_1 = require("./errors/errors");
const finder_1 = require("./finder");
const utils_1 = require("./parser/utils");
const scope_refactoring_1 = require("./scope-refactoring");
const inspector_1 = require("./stdlib/inspector");
Object.defineProperty(exports, "setBreakpointAtLine", { enumerable: true, get: function () { return inspector_1.setBreakpointAtLine; } });
const types_1 = require("./types");
const svml_assembler_1 = require("./vm/svml-assembler");
Object.defineProperty(exports, "assemble", { enumerable: true, get: function () { return svml_assembler_1.assemble; } });
const svml_compiler_1 = require("./vm/svml-compiler");
var docTooltip_1 = require("./editors/ace/docTooltip");
Object.defineProperty(exports, "SourceDocumentation", { enumerable: true, get: function () { return docTooltip_1.SourceDocumentation; } });
const interpreter_1 = require("./cse-machine/interpreter");
const errors_2 = require("./modules/errors");
const preprocessor_1 = require("./modules/preprocessor");
const filePaths_1 = require("./modules/preprocessor/filePaths");
const name_extractor_1 = require("./name-extractor");
const runner_1 = require("./runner");
// needed to work on browsers
if (typeof window !== 'undefined') {
// @ts-ignore
source_map_1.SourceMapConsumer.initialize({
'lib/mappings.wasm': 'https://unpkg.com/source-map@0.7.3/lib/mappings.wasm'
});
}
let verboseErrors = false;
function parseError(errors, verbose = verboseErrors) {
const errorMessagesArr = errors.map(error => {
// FIXME: Either refactor the parser to output an ESTree-compliant AST, or modify the ESTree types.
const filePath = error.location?.source ? `[${error.location.source}] ` : '';
const line = error.location ? error.location.start.line : '<unknown>';
const column = error.location ? error.location.start.column : '<unknown>';
const explanation = error.explain();
if (verbose) {
// TODO currently elaboration is just tagged on to a new line after the error message itself. find a better
// way to display it.
const elaboration = error.elaborate();
return line < 1
? `${filePath}${explanation}\n${elaboration}\n`
: `${filePath}Line ${line}, Column ${column}: ${explanation}\n${elaboration}\n`;
}
else {
return line < 1 ? explanation : `${filePath}Line ${line}: ${explanation}`;
}
});
return errorMessagesArr.join('\n');
}
exports.parseError = parseError;
function findDeclaration(code, context, loc) {
const program = (0, utils_1.looseParse)(code, context);
if (!program) {
return null;
}
const identifierNode = (0, finder_1.findIdentifierNode)(program, context, loc);
if (!identifierNode) {
return null;
}
const declarationNode = (0, finder_1.findDeclarationNode)(program, identifierNode);
if (!declarationNode || identifierNode === declarationNode) {
return null;
}
return declarationNode.loc;
}
exports.findDeclaration = findDeclaration;
function getScope(code, context, loc) {
const program = (0, utils_1.looseParse)(code, context);
if (!program) {
return [];
}
const identifierNode = (0, finder_1.findIdentifierNode)(program, context, loc);
if (!identifierNode) {
return [];
}
const declarationNode = (0, finder_1.findDeclarationNode)(program, identifierNode);
if (!declarationNode || declarationNode.loc == null || identifierNode !== declarationNode) {
return [];
}
return (0, scope_refactoring_1.getScopeHelper)(declarationNode.loc, program, identifierNode.name);
}
exports.getScope = getScope;
function getAllOccurrencesInScope(code, context, loc) {
const program = (0, utils_1.looseParse)(code, context);
if (!program) {
return [];
}
const identifierNode = (0, finder_1.findIdentifierNode)(program, context, loc);
if (!identifierNode) {
return [];
}
const declarationNode = (0, finder_1.findDeclarationNode)(program, identifierNode);
if (declarationNode == null || declarationNode.loc == null) {
return [];
}
return (0, scope_refactoring_1.getAllOccurrencesInScopeHelper)(declarationNode.loc, program, identifierNode.name);
}
exports.getAllOccurrencesInScope = getAllOccurrencesInScope;
function hasDeclaration(code, context, loc) {
const program = (0, utils_1.looseParse)(code, context);
if (!program) {
return false;
}
const identifierNode = (0, finder_1.findIdentifierNode)(program, context, loc);
if (!identifierNode) {
return false;
}
const declarationNode = (0, finder_1.findDeclarationNode)(program, identifierNode);
if (declarationNode == null || declarationNode.loc == null) {
return false;
}
return true;
}
exports.hasDeclaration = hasDeclaration;
/**
* Gets names present within a string of code
* @param code Code to parse
* @param line Line position of the cursor
* @param col Column position of the cursor
* @param context Evaluation context
* @returns `[NameDeclaration[], true]` if suggestions should be displayed, `[[], false]` otherwise
*/
async function getNames(code, line, col, context) {
const [program, comments] = (0, utils_1.parseWithComments)(code);
if (!program) {
return [[], false];
}
const cursorLoc = { line, column: col };
const [progNames, displaySuggestions] = await (0, name_extractor_1.getProgramNames)(program, comments, cursorLoc);
const keywords = (0, name_extractor_1.getKeywords)(program, cursorLoc, context);
return [progNames.concat(keywords), displaySuggestions];
}
exports.getNames = getNames;
async function runInContext(code, context, options = {}) {
const defaultFilePath = '/default.js';
const files = {};
files[defaultFilePath] = code;
return runFilesInContext(files, defaultFilePath, context, options);
}
exports.runInContext = runInContext;
// this is the first entrypoint for all source files.
// as such, all mapping functions required by alternate languages
// should be defined here.
async function runFilesInContext(files, entrypointFilePath, context, options = {}) {
for (const filePath in files) {
const filePathError = (0, filePaths_1.validateFilePath)(filePath);
if (filePathError !== null) {
context.errors.push(filePathError);
return runner_1.resolvedErrorPromise;
}
}
let result;
if (context.chapter === types_1.Chapter.HTML) {
const code = files[entrypointFilePath];
if (code === undefined) {
context.errors.push(new errors_2.ModuleNotFoundError(entrypointFilePath));
return runner_1.resolvedErrorPromise;
}
result = await (0, runner_1.htmlRunner)(code, context, options);
}
else {
// FIXME: Clean up state management so that the `parseError` function is pure.
// This is not a huge priority, but it would be good not to make use of
// global state.
;
({ result, verboseErrors } = await (0, runner_1.sourceFilesRunner)(p => Promise.resolve(files[p]), entrypointFilePath, context, {
...options,
shouldAddFileName: options.shouldAddFileName ?? Object.keys(files).length > 1
}));
}
return result;
}
exports.runFilesInContext = runFilesInContext;
function resume(result) {
if (result.status === 'finished' || result.status === 'error') {
return result;
}
else if (result.status === 'suspended-cse-eval') {
const value = (0, interpreter_1.resumeEvaluate)(result.context);
return (0, interpreter_1.CSEResultPromise)(result.context, value);
}
else {
return result.scheduler.run(result.it, result.context);
}
}
exports.resume = resume;
function interrupt(context) {
const globalEnvironment = context.runtime.environments[context.runtime.environments.length - 1];
context.runtime.environments = [globalEnvironment];
context.runtime.isRunning = false;
context.errors.push(new errors_1.InterruptedError(context.runtime.nodes[0]));
}
exports.interrupt = interrupt;
function compile(code, context, vmInternalFunctions) {
const defaultFilePath = '/default.js';
const files = {};
files[defaultFilePath] = code;
return compileFiles(files, defaultFilePath, context, vmInternalFunctions);
}
exports.compile = compile;
async function compileFiles(files, entrypointFilePath, context, vmInternalFunctions) {
for (const filePath in files) {
const filePathError = (0, filePaths_1.validateFilePath)(filePath);
if (filePathError !== null) {
context.errors.push(filePathError);
return undefined;
}
}
const preprocessResult = await (0, preprocessor_1.default)(p => Promise.resolve(files[p]), entrypointFilePath, context, { shouldAddFileName: Object.keys(files).length > 1 });
if (!preprocessResult.ok) {
return undefined;
}
try {
return (0, svml_compiler_1.compileToIns)(preprocessResult.program, undefined, vmInternalFunctions);
}
catch (error) {
context.errors.push(error);
return undefined;
}
}
exports.compileFiles = compileFiles;
//# sourceMappingURL=index.js.map
;