UNPKG

js-slang

Version:

Javascript-based implementations of Source, written in Typescript

226 lines 9.95 kB
"use strict"; 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