diff --git a/package.json b/package.json index f9a654eec..a149062b2 100644 --- a/package.json +++ b/package.json @@ -106,6 +106,7 @@ "testRegex": "/__tests__/.*\\.ts$", "testPathIgnorePatterns": [ "/dist/", + "/src/alt-langs/scheme/scm-slang", ".*benchmark.*", "/__tests__/(.*/)?utils\\.ts" ], @@ -114,7 +115,7 @@ "/node_modules/", "/src/typings/", "/src/utils/testing.ts", - "/src/alt-langs", + "/src/alt-langs/scheme/scm-slang", "/src/py-slang/" ], "reporters": [ diff --git a/src/alt-langs/__tests__/mapper.ts b/src/alt-langs/__tests__/mapper.ts new file mode 100644 index 000000000..8c4b8374b --- /dev/null +++ b/src/alt-langs/__tests__/mapper.ts @@ -0,0 +1,32 @@ +import { mockContext } from "../../mocks/context"; +import { Chapter, Finished } from "../../types"; +import { mapResult } from "../mapper"; + +test("given source, mapper should do nothing (no mapping needed)", () => { + const context = mockContext(); + const result = { + status: "finished", + context: context, + value: 5, + } as Finished; + const mapper = mapResult(context); + expect(mapper(result)).toEqual(result); +}) + +test("given scheme, mapper should map result to scheme representation", () => { + const context = mockContext(Chapter.SCHEME_1); + const result = { + status: "finished", + context: context, + value: [1, 2, 3, 4, 5], + } as Finished; + const mapper = mapResult(context); + expect(mapper(result)).toEqual({ + status: "finished", + context: context, + value: [1, 2, 3, 4, 5], + representation: { + representation: "#(1 2 3 4 5)", + }, + }); +}) \ No newline at end of file diff --git a/src/alt-langs/mapper.ts b/src/alt-langs/mapper.ts new file mode 100644 index 000000000..ffb2c5713 --- /dev/null +++ b/src/alt-langs/mapper.ts @@ -0,0 +1,43 @@ +/** + * A generic mapper for all languages. + * If required, maps the final result produced by js-slang to + * the required representation for the language. + */ + +import { Context, Result } from ".." +import { Chapter } from "../types" +import { mapErrorToScheme, mapResultToScheme } from "./scheme/scheme-mapper" + +/** + * A representation of a value in a language. + * This is used to represent the final value produced by js-slang. + * It is separate from the actual value of the result. + */ +export class Representation { + constructor(public representation: string) {} + toString() { + return this.representation + } +} + +export function mapResult(context: Context): (x: Result) => Result { + switch (context.chapter) { + case Chapter.SCHEME_1: + case Chapter.SCHEME_2: + case Chapter.SCHEME_3: + case Chapter.SCHEME_4: + case Chapter.FULL_SCHEME: + return x => { + if (x.status === 'finished') { + return mapResultToScheme(x) + } else if (x.status === "error") { + context.errors = context.errors.map(mapErrorToScheme) + } + return x + } + default: + // normally js-slang. + // there is no need for a mapper in this case. + return x => x + } +} \ No newline at end of file diff --git a/src/alt-langs/scheme/__tests__/scheme-encode-decode.ts b/src/alt-langs/scheme/__tests__/scheme-encode-decode.ts index 77d9bcde8..2b8193622 100644 --- a/src/alt-langs/scheme/__tests__/scheme-encode-decode.ts +++ b/src/alt-langs/scheme/__tests__/scheme-encode-decode.ts @@ -4,7 +4,7 @@ import { UnassignedVariable } from '../../../errors/errors' import { decode, encode } from '../scm-slang/src' import { cons, set$45$cdr$33$ } from '../scm-slang/src/stdlib/base' import { dummyExpression } from '../../../utils/ast/dummyAstCreator' -import { decodeError, decodeValue } from '../../../parser/scheme' +import { mapErrorToScheme, decodeValue } from '../scheme-mapper' describe('Scheme encoder and decoder', () => { it('encoder and decoder are proper inverses of one another', () => { @@ -62,6 +62,6 @@ describe('Scheme encoder and decoder', () => { const token = `😀` const dummyNode: Node = dummyExpression() const error = new UnassignedVariable(encode(token), dummyNode) - expect(decodeError(error).elaborate()).toContain(`😀`) + expect(mapErrorToScheme(error).elaborate()).toContain(`😀`) }) }) diff --git a/src/alt-langs/scheme/__tests__/scheme-mapper.ts b/src/alt-langs/scheme/__tests__/scheme-mapper.ts new file mode 100644 index 000000000..75c520b68 --- /dev/null +++ b/src/alt-langs/scheme/__tests__/scheme-mapper.ts @@ -0,0 +1,58 @@ +import { schemeVisualise } from "../scheme-mapper" +import { make_number } from "../scm-slang/src/stdlib/core-math" +import { circular$45$list, cons, cons$42$, list } from "../scm-slang/src/stdlib/base" + +test("schemeVisualise: should visualise null properly", () => { + expect(schemeVisualise(null).toString()).toEqual("()") +}) + +test("schemeVisualise: should visualise undefined properly", () => { + expect(schemeVisualise(undefined).toString()).toEqual("undefined") +}) + +test("schemeVisualise: should visualise strings properly", () => { + expect(schemeVisualise("hello").toString()).toEqual("\"hello\"") +}) + +test("schemeVisualise: should visualise scheme numbers properly", () => { + expect(schemeVisualise(make_number("1i")).toString()).toEqual("0+1i") +}) + +test("schemeVisualise: should visualise booleans properly", () => { + expect(schemeVisualise(true).toString()).toEqual("#t") + expect(schemeVisualise(false).toString()).toEqual("#f") +}) + +test("schemeVisualise: should visualise circular lists properly", () => { + const circularList = circular$45$list(1, 2, 3) + //expect(schemeVisualise(circularList).toString()).toEqual("#0=(1 2 3 . #0#)") + //for now, this will do + expect(schemeVisualise(circularList).toString()).toEqual("(circular list)") +}) + +test("schemeVisualise: should visualise dotted lists properly", () => { + const dottedList = cons$42$(1, 2, 3) + expect(schemeVisualise(dottedList).toString()).toEqual("(1 2 . 3)") +}) + +test("schemeVisualise: should visualise proper lists properly", () => { + const properList = list(1, 2, 3, 4) + expect(schemeVisualise(properList).toString()).toEqual("(1 2 3 4)") +}) + +test("schemeVisualise: should visualise vectors properly", () => { + const vector = [1, 2, 3, 4] + expect(schemeVisualise(vector).toString()).toEqual("#(1 2 3 4)") +}) + +test("schemeVisualise: should visualise pairs properly", () => { + const pair = cons(1, 2) + expect(schemeVisualise(pair).toString()).toEqual("(1 . 2)") +}) + +test("schemeVisualise: vectors and pairs should be distinct", () => { + const maybe_pair = [1, 2] + expect(schemeVisualise(maybe_pair).toString()).toEqual("#(1 2)") +}) + +export { schemeVisualise } diff --git a/src/alt-langs/scheme/scheme-mapper.ts b/src/alt-langs/scheme/scheme-mapper.ts new file mode 100644 index 000000000..468c33870 --- /dev/null +++ b/src/alt-langs/scheme/scheme-mapper.ts @@ -0,0 +1,192 @@ +import { ArrowFunctionExpression, Identifier, RestElement } from "estree" +import Closure from "../../cse-machine/closure" +import { decode, estreeDecode } from "./scm-slang/src" +import { boolean$63$, car, cdr, circular$45$list$63$, cons, dotted$45$list$63$, last$45$pair, list$45$tail, null$63$, number$63$, pair$63$, proper$45$list$63$, set$45$cdr$33$, vector$63$ } from "./scm-slang/src/stdlib/source-scheme-library" +import { ErrorType, Result, SourceError } from "../../types" +import { List, Pair } from "../../stdlib/list" +import { Representation } from "../mapper" + +export function mapResultToScheme(res: Result): Result { + if (res.status === "finished" || res.status === "suspended-non-det") { + return { + ...res, + value: decodeValue(res.value), + representation: showSchemeData(res.value) + } + } + return res +} + +// Given an error, decode its message if and +// only if an encoded value may exist in it. +export function mapErrorToScheme(error: SourceError): SourceError { + if (error.type === ErrorType.SYNTAX) { + // Syntax errors are not encoded. + return error + } + const newExplain = decodeString(error.explain()) + const newElaborate = decodeString(error.elaborate()) + return { + ...error, + explain: () => newExplain, + elaborate: () => newElaborate + } +} + +export function showSchemeData(data: any): Representation { + return schemeVisualise(decodeValue(data)) +} + +function decodeString(str: string): string { + return str.replace(/\$scheme_[\w$]+|\$\d+\$/g, match => { + return decode(match) + }) +} + +// Given any value, change the representation of it to +// the required scheme representation. +export function schemeVisualise(x: any): Representation { + // hack: builtins are represented using an object with a toString method + // and minArgsNeeded. + // so to detect these, we use a function that checks for these + function isBuiltinFunction(x: any): boolean { + return x.minArgsNeeded !== undefined && x.toString !== undefined + } + function stringify(x: any): string { + if (null$63$(x)) { + return '()' + } else if (x === undefined) { + return 'undefined' + } else if (typeof x === 'string') { + return `"${x}"` + } else if (number$63$(x)) { + return x.toString() + } else if (boolean$63$(x)) { + return x ? '#t' : '#f' + } else if (x instanceof Closure) { + const node = x.originalNode + const parameters = node.params.map( + (param: Identifier | RestElement) => param.type === "Identifier" + ? param.name + : ". " + (param.argument as Identifier).name) + .join(' ') + .trim() + return `#` + } else if (isBuiltinFunction(x) || typeof x === 'function') { + function decodeParams(params: string[]): string { + // if parameter starts with ... then it is a rest parameter + const convertedparams = params + .map(param => { + if (param.startsWith('...')) { + return `. ${param.slice(3)}` + } + return param + }) + .map(decodeString) + return convertedparams.join(' ') + } + // take the name and parameter out of the defined function name + const name = decodeString(x.funName) + const parameters = decodeParams(x.funParameters) + return `#` + } else if (circular$45$list$63$(x)) { + return '(circular list)' + } else if (dotted$45$list$63$(x) && pair$63$(x)) { + let string = '(' + let current = x + while (pair$63$(current)) { + string += `${schemeVisualise(car(current))} ` + current = cdr(current) + } + return string.trim() + ` . ${schemeVisualise(current)})` + } else if (proper$45$list$63$(x)) { + let string = '(' + let current = x + while (current !== null) { + string += `${schemeVisualise(car(current))} ` + current = cdr(current) + } + return string.trim() + ')' + } else if (vector$63$(x)) { + let string = '#(' + for (let i = 0; i < x.length; i++) { + string += `${schemeVisualise(x[i])} ` + } + return string.trim() + ')' + } else { + return x.toString() + } + } + + // return an object with a toString method that returns the stringified version of x + return new Representation(stringify(x)) +} + +// Given any value, decode it if and +// only if an encoded value may exist in it. +// this function is used to accurately display +// values in the REPL. +export function decodeValue(x: any): any { + // helper version of list_tail that assumes non-null return value + function list_tail(xs: List, i: number): List { + if (i === 0) { + return xs + } else { + return list_tail(list$45$tail(xs), i - 1) + } + } + + if (circular$45$list$63$(x)) { + // May contain encoded strings. + let circular_pair_index = -1 + const all_pairs: Pair[] = [] + + // iterate through all pairs in the list until we find the circular pair + let current = x + while (current !== null) { + if (all_pairs.includes(current)) { + circular_pair_index = all_pairs.indexOf(current) + break + } + all_pairs.push(current) + current = cdr(current) + } + + // assemble a new list using the elements in all_pairs + let new_list = null + for (let i = all_pairs.length - 1; i >= 0; i--) { + new_list = cons(decodeValue(car(all_pairs[i])), new_list) + } + + // finally we can set the last cdr of the new list to the circular-pair itself + + const circular_pair = list_tail(new_list, circular_pair_index) + set$45$cdr$33$(last$45$pair(new_list), circular_pair) + return new_list + } else if (pair$63$(x)) { + // May contain encoded strings. + return cons(decodeValue(car(x)), decodeValue(cdr(x))) + } else if (vector$63$(x)) { + // May contain encoded strings. + return x.map(decodeValue) + } else if (x instanceof Closure) { + const newNode = estreeDecode(x.originalNode) as ArrowFunctionExpression + + // not a big fan of mutation, but we assert we will never need the original node again anyway + x.node = newNode + x.originalNode = newNode + return x + } else if (typeof x === 'function') { + // copy x to avoid modifying the original object + const newX = { ...x } + const newString = decodeString(x.toString()) + // change the toString method to return the decoded string + newX.toString = () => newString + return newX + } else { + // string, number, boolean, null, undefined + // no need to decode. + return x + } +} + diff --git a/src/createContext.ts b/src/createContext.ts index 4b09c6f75..fa5194f05 100644 --- a/src/createContext.ts +++ b/src/createContext.ts @@ -1,6 +1,7 @@ // Variable determining chapter of Source is contained in this file. import * as scheme_libs from './alt-langs/scheme/scm-slang/src/stdlib/source-scheme-library' +import { schemePrelude } from './stdlib/scheme.prelude' import { GLOBAL, JSSLANG_PROPERTIES } from './constants' import { call_with_current_continuation } from './cse-machine/continuations' import Heap from './cse-machine/heap' @@ -30,6 +31,7 @@ import { import { makeWrapper } from './utils/makeWrapper' import * as operators from './utils/operators' import { stringify } from './utils/stringify' +import { schemeVisualise } from './alt-langs/scheme/scheme-mapper' export class LazyBuiltIn { func: (...arg0: any) => any @@ -225,18 +227,40 @@ export function defineBuiltin( value: Value, minArgsNeeded: undefined | number = undefined ) { + function extractName(name: string): string { + return name.split('(')[0].trim() + } + + function extractParameters(name: string): string[] { + // if the function has no () in its name, it has no parameters + if (!name.includes('(')) { + return [] + } + return name + .split('(')[1] + .split(')')[0] + .split(',') + .map(s => s.trim()) + } + if (typeof value === 'function') { - const funName = name.split('(')[0].trim() + const funName = extractName(name) + const funParameters = extractParameters(name) const repr = `function ${name} {\n\t[implementation hidden]\n}` value.toString = () => repr value.minArgsNeeded = minArgsNeeded + value.funName = funName + value.funParameters = funParameters defineSymbol(context, funName, value) } else if (value instanceof LazyBuiltIn) { const wrapped = (...args: any) => value.func(...args) - const funName = name.split('(')[0].trim() + const funName = extractName(name) + const funParameters = extractParameters(name) const repr = `function ${name} {\n\t[implementation hidden]\n}` wrapped.toString = () => repr + wrapped.funName = funName + wrapped.funParameters = funParameters makeWrapper(value.func, wrapped) defineSymbol(context, funName, new LazyBuiltIn(wrapped, value.evaluateArgs)) } else { @@ -586,7 +610,7 @@ export const importBuiltins = (context: Context, externalBuiltIns: CustomBuiltIn */ case Chapter.SCHEME_1: // Display - defineBuiltin(context, 'display(val)', display) + defineBuiltin(context, 'display(val)', (val: any) => display(schemeVisualise(val))) defineBuiltin(context, 'newline()', () => display('')) // I/O @@ -784,6 +808,10 @@ function importPrelude(context: Context) { prelude += nonDetPrelude } + if (context.chapter === Chapter.FULL_SCHEME) { + prelude += schemePrelude + } + if (prelude !== '') { context.prelude = prelude } diff --git a/src/index.ts b/src/index.ts index 1b9a9deec..cc5445e5a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -33,7 +33,6 @@ import { validateFilePath } from './modules/preprocessor/filePaths' import { mergeImportOptions } from './modules/utils' import { getKeywords, getProgramNames, NameDeclaration } from './name-extractor' import { parse } from './parser/parser' -import { decodeError, decodeValue } from './parser/scheme' import { fullJSRunner, hasVerboseErrors, @@ -41,6 +40,7 @@ import { resolvedErrorPromise, sourceFilesRunner } from './runner' +import { mapResult } from './alt-langs/mapper' export interface IOptions { scheduler: 'preemptive' | 'async' @@ -216,72 +216,65 @@ export async function runInContext( return runFilesInContext(files, defaultFilePath, context, options) } +// this is the first entrypoint for all source files. +// as such, all mapping functions required by alternate languages +// should be defined here. export async function runFilesInContext( files: Partial>, entrypointFilePath: string, context: Context, options: RecursivePartial = {} ): Promise { - for (const filePath in files) { - const filePathError = validateFilePath(filePath) - if (filePathError !== null) { - context.errors.push(filePathError) + async function runFilesInContextHelper( + files: Partial>, + entrypointFilePath: string, + context: Context, + options: RecursivePartial = {} + ): Promise { + for (const filePath in files) { + const filePathError = validateFilePath(filePath) + if (filePathError !== null) { + context.errors.push(filePathError) + return resolvedErrorPromise + } + } + + const code = files[entrypointFilePath] + if (code === undefined) { + context.errors.push(new ModuleNotFoundError(entrypointFilePath)) return resolvedErrorPromise } - } - const code = files[entrypointFilePath] - if (code === undefined) { - context.errors.push(new ModuleNotFoundError(entrypointFilePath)) - return resolvedErrorPromise - } + if ( + context.chapter === Chapter.FULL_JS || + context.chapter === Chapter.FULL_TS || + context.chapter === Chapter.PYTHON_1 + ) { + const program = parse(code, context) + if (program === null) { + return resolvedErrorPromise + } - if ( - context.chapter === Chapter.FULL_JS || - context.chapter === Chapter.FULL_TS || - context.chapter === Chapter.PYTHON_1 - ) { - const program = parse(code, context) - if (program === null) { - return resolvedErrorPromise + const fullImportOptions = mergeImportOptions(options.importOptions) + return fullJSRunner(program, context, fullImportOptions) } - const fullImportOptions = mergeImportOptions(options.importOptions) - return fullJSRunner(program, context, fullImportOptions) - } + if (context.chapter === Chapter.HTML) { + return htmlRunner(code, context, options) + } - if (context.chapter === Chapter.HTML) { - return htmlRunner(code, context, options) - } + // 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. + verboseErrors = hasVerboseErrors(code) - if (context.chapter <= +Chapter.SCHEME_1 && context.chapter >= +Chapter.FULL_SCHEME) { - // If the language is scheme, we need to format all errors and returned values first - // Use the standard runner to get the result - const evaluated: Promise = sourceFilesRunner( - files, - entrypointFilePath, - context, - options - ).then(result => { - // Format the returned value - if (result.status === 'finished') { - return { - ...result, - value: decodeValue(result.value) - } as Finished - } - return result - }) - // Format all errors in the context - context.errors = context.errors.map(error => decodeError(error)) - return evaluated + // the sourceFilesRunner + return sourceFilesRunner(files, entrypointFilePath, context, options) } - // 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. - verboseErrors = hasVerboseErrors(code) - return sourceFilesRunner(files, entrypointFilePath, context, options) + return runFilesInContextHelper(files, entrypointFilePath, context, options).then( + mapResult(context) + ) } export function resume(result: Result): Finished | ResultError | Promise { diff --git a/src/interpreter/closure.ts b/src/interpreter/closure.ts index 6f7f2c027..a781d5f57 100644 --- a/src/interpreter/closure.ts +++ b/src/interpreter/closure.ts @@ -103,10 +103,10 @@ export default class Closure extends Callable { public preDefined?: boolean /** The original node that created this Closure */ - public originalNode: es.Function + public originalNode: es.Function | es.ArrowFunctionExpression constructor( - public node: es.Function, + public node: es.Function | es.ArrowFunctionExpression, public environment: Environment, context: Context, isPredefined?: boolean diff --git a/src/parser/scheme/index.ts b/src/parser/scheme/index.ts index 9391bf019..30df2b836 100644 --- a/src/parser/scheme/index.ts +++ b/src/parser/scheme/index.ts @@ -1,20 +1,6 @@ import { Program } from 'estree' - -import { decode, schemeParse } from '../../alt-langs/scheme/scm-slang/src' -import { - car, - cdr, - circular$45$list$63$, - cons, - last$45$pair, - list$45$tail, - pair$63$, - procedure$63$, - set$45$cdr$33$, - vector$63$ -} from '../../alt-langs/scheme/scm-slang/src/stdlib/base' -import { List, Pair } from '../../stdlib/list' -import { Chapter, Context, ErrorType, SourceError } from '../../types' +import { schemeParse } from '../../alt-langs/scheme/scm-slang/src' +import { Chapter, Context } from '../../types' import { FatalSyntaxError } from '../errors' import { AcornOptions, Parser } from '../types' import { positionToSourceLocation } from '../utils' @@ -71,86 +57,3 @@ function getSchemeChapter(chapter: Chapter): number { throw new Error(`SchemeParser was not given a valid chapter!`) } } - -function decodeString(str: string): string { - return str.replace(/\$scheme_[\w$]+|\$\d+\$/g, match => { - return decode(match) - }) -} - -// Given any value, decode it if and -// only if an encoded value may exist in it. -// this function is used to accurately display -// values in the REPL. -export function decodeValue(x: any): any { - // helper version of list_tail that assumes non-null return value - function list_tail(xs: List, i: number): List { - if (i === 0) { - return xs - } else { - return list_tail(list$45$tail(xs), i - 1) - } - } - - if (circular$45$list$63$(x)) { - // May contain encoded strings. - let circular_pair_index = -1 - const all_pairs: Pair[] = [] - - // iterate through all pairs in the list until we find the circular pair - let current = x - while (current !== null) { - if (all_pairs.includes(current)) { - circular_pair_index = all_pairs.indexOf(current) - break - } - all_pairs.push(current) - current = cdr(current) - } - x - // assemble a new list using the elements in all_pairs - let new_list = null - for (let i = all_pairs.length - 1; i >= 0; i--) { - new_list = cons(decodeValue(car(all_pairs[i])), new_list) - } - - // finally we can set the last cdr of the new list to the circular-pair itself - - const circular_pair = list_tail(new_list, circular_pair_index) - set$45$cdr$33$(last$45$pair(new_list), circular_pair) - return new_list - } else if (pair$63$(x)) { - // May contain encoded strings. - return cons(decodeValue(car(x)), decodeValue(cdr(x))) - } else if (vector$63$(x)) { - // May contain encoded strings. - return x.map(decodeValue) - } else if (procedure$63$(x)) { - // copy x to avoid modifying the original object - const newX = { ...x } - const newString = decodeString(x.toString()) - // change the toString method to return the decoded string - newX.toString = () => newString - return newX - } else { - // string, number, boolean, null, undefined - // no need to decode. - return x - } -} - -// Given an error, decode its message if and -// only if an encoded value may exist in it. -export function decodeError(error: SourceError): SourceError { - if (error.type === ErrorType.SYNTAX) { - // Syntax errors are not encoded. - return error - } - const newExplain = decodeString(error.explain()) - const newElaborate = decodeString(error.elaborate()) - return { - ...error, - explain: () => newExplain, - elaborate: () => newElaborate - } -} diff --git a/src/repl/repl.ts b/src/repl/repl.ts index b3b4f7f47..4452978c8 100644 --- a/src/repl/repl.ts +++ b/src/repl/repl.ts @@ -6,6 +6,7 @@ import { pyLanguages, scmLanguages, sourceLanguages } from '../constants' import { createContext, IOptions, parseError, runInContext } from '../index' import Closure from '../interpreter/closure' import { ExecutionMethod, Variant } from '../types' +import { Representation } from '../alt-langs/mapper' function startRepl( chapter = 1, @@ -35,7 +36,9 @@ function startRepl( eval: (cmd, unusedContext, unusedFilename, callback) => { runInContext(cmd, context, options).then(obj => { if (obj.status === 'finished' || obj.status === 'suspended-non-det') { - callback(null, obj.value) + // if the result is a representation, display the representation. + // else, default to standard value representation. + callback(null, obj.representation !== undefined ? obj.representation : obj.value) } else { callback(new Error(parseError(context.errors)), undefined) } @@ -44,7 +47,9 @@ function startRepl( // set depth to a large number so that `parse()` output will not be folded, // setting to null also solves the problem, however a reference loop might crash writer: output => { - return output instanceof Closure || typeof output === 'function' + return output instanceof Closure || + typeof output === 'function' || + output instanceof Representation ? output.toString() : inspect(output, { depth: 1000, diff --git a/src/stdlib/scheme.prelude.ts b/src/stdlib/scheme.prelude.ts new file mode 100644 index 000000000..2562804bd --- /dev/null +++ b/src/stdlib/scheme.prelude.ts @@ -0,0 +1,3 @@ +export const schemePrelude = ` +(define call-with-current-continuation call/cc) +` diff --git a/src/types.ts b/src/types.ts index 65a5bdd07..efecebbde 100644 --- a/src/types.ts +++ b/src/types.ts @@ -12,6 +12,7 @@ import { EnvTree } from './createContext' import Heap from './cse-machine/heap' import { Control, Stash } from './cse-machine/interpreter' import type { ModuleFunctions } from './modules/moduleTypes' +import { Representation } from './alt-langs/mapper' /** * Defines functions that act as built-ins, but might rely on @@ -275,6 +276,10 @@ export interface Finished { status: 'finished' context: Context value: Value + representation?: Representation // if the returned value needs a unique representation, + // (for example if the language used is not JS), + // the display of the result will use the representation + // field instead } export interface Suspended { @@ -286,6 +291,7 @@ export interface Suspended { export type SuspendedNonDet = Omit & { status: 'suspended-non-det' } & { value: Value + representation?: Representation // never used, only kept for consistency with Finished } export interface SuspendedCseEval {