diff --git a/src/__tests__/__snapshots__/environment.ts.snap b/src/__tests__/__snapshots__/environment.ts.snap index e8e94d1bd..5597fb32f 100644 --- a/src/__tests__/__snapshots__/environment.ts.snap +++ b/src/__tests__/__snapshots__/environment.ts.snap @@ -49,11 +49,11 @@ Object { "head": Object { "x": 2, }, - "id": "3", + "id": "4", "name": "f", "tail": Object { "head": Object { - "2": [Function], + "3": [Function], "f": [Function], }, "id": "1", @@ -196,7 +196,7 @@ Object { "name": "f", "tail": Object { "head": Object { - "2": [Function], + "3": [Function], "f": [Function], }, "id": "1", @@ -293,7 +293,7 @@ Object { exports[`Function params and body identifiers are in different environment 3`] = ` Object { "head": Object { - "2": [Function], + "3": [Function], "f": [Function], }, "id": Any, diff --git a/src/ec-evaluator/instrCreator.ts b/src/ec-evaluator/instrCreator.ts index 70443102b..fc1db204c 100644 --- a/src/ec-evaluator/instrCreator.ts +++ b/src/ec-evaluator/instrCreator.ts @@ -19,8 +19,9 @@ import { WhileInstr } from './types' -export const resetInstr = (): Instr => ({ - instrType: InstrType.RESET +export const resetInstr = (srcNode: es.Node): Instr => ({ + instrType: InstrType.RESET, + srcNode }) export const whileInstr = ( @@ -74,7 +75,7 @@ export const binOpInstr = (symbol: es.BinaryOperator, srcNode: es.Node): BinOpIn srcNode }) -export const popInstr = (): Instr => ({ instrType: InstrType.POP }) +export const popInstr = (srcNode: es.Node): Instr => ({ instrType: InstrType.POP, srcNode }) export const appInstr = (numOfArgs: number, srcNode: es.CallExpression): AppInstr => ({ instrType: InstrType.APPLICATION, @@ -93,46 +94,54 @@ export const branchInstr = ( srcNode }) -export const envInstr = (env: Environment): EnvInstr => ({ +export const envInstr = (env: Environment, srcNode: es.Node): EnvInstr => ({ instrType: InstrType.ENVIRONMENT, - env + env, + srcNode }) -export const pushUndefIfNeededInstr = (): Instr => ({ - instrType: InstrType.PUSH_UNDEFINED_IF_NEEDED +export const pushUndefIfNeededInstr = (srcNode: es.Node): Instr => ({ + instrType: InstrType.PUSH_UNDEFINED_IF_NEEDED, + srcNode }) -export const arrLitInstr = (arity: number): ArrLitInstr => ({ +export const arrLitInstr = (arity: number, srcNode: es.Node): ArrLitInstr => ({ instrType: InstrType.ARRAY_LITERAL, - arity + arity, + srcNode }) -export const arrAccInstr = (): Instr => ({ - instrType: InstrType.ARRAY_ACCESS +export const arrAccInstr = (srcNode: es.Node): Instr => ({ + instrType: InstrType.ARRAY_ACCESS, + srcNode }) -export const arrAssmtInstr = (): Instr => ({ - instrType: InstrType.ARRAY_ASSIGNMENT +export const arrAssmtInstr = (srcNode: es.Node): Instr => ({ + instrType: InstrType.ARRAY_ASSIGNMENT, + srcNode }) -export const markerInstr = (): Instr => ({ - instrType: InstrType.MARKER +export const markerInstr = (srcNode: es.Node): Instr => ({ + instrType: InstrType.MARKER, + srcNode }) -export const contInstr = (): Instr => ({ - instrType: InstrType.CONTINUE +export const contInstr = (srcNode: es.Node): Instr => ({ + instrType: InstrType.CONTINUE, + srcNode }) -export const contMarkerInstr = (): Instr => ({ - instrType: InstrType.CONTINUE_MARKER +export const contMarkerInstr = (srcNode: es.Node): Instr => ({ + instrType: InstrType.CONTINUE_MARKER, + srcNode }) -export const breakInstr = (): Instr => ({ - instrType: InstrType.BREAK +export const breakInstr = (srcNode: es.Node): Instr => ({ + instrType: InstrType.BREAK, + srcNode }) -export const breakMarkerInstr = (): Instr => ({ - instrType: InstrType.BREAK_MARKER +export const breakMarkerInstr = (srcNode: es.Node): Instr => ({ + instrType: InstrType.BREAK_MARKER, + srcNode }) - -// export const breakMarkerInstr = (): IInstr => ({ instrType: InstrTypes.BREAK_MARKER }) diff --git a/src/ec-evaluator/interpreter.ts b/src/ec-evaluator/interpreter.ts index 6f18c6798..495fd39ee 100644 --- a/src/ec-evaluator/interpreter.ts +++ b/src/ec-evaluator/interpreter.ts @@ -20,6 +20,7 @@ import { ModuleFunctions } from '../modules/moduleTypes' import { checkEditorBreakpoints } from '../stdlib/inspector' import { Context, ContiguousArrayElements, Result, Value } from '../types' import * as ast from '../utils/astCreator' +import { dummyStatement } from '../utils/dummyAstCreator' import { evaluateBinaryExpression, evaluateUnaryExpression } from '../utils/operators' import * as rttc from '../utils/rttc' import * as instr from './instrCreator' @@ -49,7 +50,6 @@ import { declareFunctionsAndVariables, declareIdentifier, defineVariable, - envChanging, getVariable, handleRuntimeError, handleSequence, @@ -71,7 +71,7 @@ export class Agenda extends Stack { public constructor(program: es.Program) { super() // Evaluation of last statement is undefined if stash is empty - this.push(instr.pushUndefIfNeededInstr()) + this.push(instr.pushUndefIfNeededInstr(dummyStatement())) // Load program into agenda stack this.push(program) @@ -215,18 +215,26 @@ export function ECEResultPromise(context: Context, value: Value): Promise implements IStack { public size(): number { return this.storage.length } + + public mapStack(mapper: (t: T) => any, num?: number): T[] { + if (num && num <= this.storage.length) { + return this.storage.slice(this.storage.length - num).map(mapper) + } + return [...this.storage].map(mapper) + } } /** @@ -115,7 +122,7 @@ export const handleSequence = (seq: es.Statement[]): AgendaItem[] => { let valueProduced = false for (const command of seq) { if (valueProducing(command)) { - valueProduced ? result.push(instr.popInstr()) : (valueProduced = true) + valueProduced ? result.push(instr.popInstr(command)) : (valueProduced = true) } result.push(command) } diff --git a/src/index.ts b/src/index.ts index 825f0cd3f..bad655327 100644 --- a/src/index.ts +++ b/src/index.ts @@ -33,6 +33,7 @@ import { validateFilePath } from './localImports/filePaths' import preprocessFileImports from './localImports/preprocessor' import { getKeywords, getProgramNames, NameDeclaration } from './name-extractor' import { parse } from './parser/parser' +import { decodeError, decodeValue } from './parser/scheme' import { parseWithComments } from './parser/utils' import { fullJSRunner, @@ -43,7 +44,6 @@ import { } from './runner' import { typeCheck } from './typeChecker/typeChecker' import { typeToString } from './utils/stringify' -import { decodeError, decodeValue } from './parser/scheme' export interface IOptions { scheduler: 'preemptive' | 'async' diff --git a/src/interpreter/closure.ts b/src/interpreter/closure.ts index 9519c3140..5b321f90f 100644 --- a/src/interpreter/closure.ts +++ b/src/interpreter/closure.ts @@ -1,6 +1,7 @@ /* tslint:disable:max-classes-per-file */ import { generate } from 'astring' import * as es from 'estree' +import { uniqueId } from 'lodash' import { Context, Environment, Value } from '../types' import { @@ -9,7 +10,6 @@ import { identifier, returnStatement } from '../utils/astCreator' -import { dummyLocation } from '../utils/dummyAstCreator' import { apply } from './interpreter' const closureToJS = (value: Closure, context: Context, klass: string) => { @@ -60,11 +60,14 @@ export default class Closure extends Callable { function isExpressionBody(body: es.BlockStatement | es.Expression): body is es.Expression { return body.type !== 'BlockStatement' } - const functionBody = isExpressionBody(node.body) + const functionBody: es.Statement[] | es.Expression | es.BlockStatement = isExpressionBody( + node.body + ) ? [returnStatement(node.body, node.body.loc)] : dummyReturn - ? [node.body, returnStatement(identifier('undefined', dummyLocation()), dummyLocation())] + ? [...node.body.body, returnStatement(identifier('undefined', node.body.loc), node.body.loc)] : node.body + const closure = new Closure( blockArrowFunction(node.params as es.Identifier[], functionBody, node.loc), environment, @@ -78,7 +81,10 @@ export default class Closure extends Callable { return closure } - /** Unique ID defined for anonymous closure */ + /** Unique ID defined for closure */ + public id: string + + /** String representation of the closure */ public functionName: string /** Fake closure function */ @@ -101,6 +107,7 @@ export default class Closure extends Callable { return funJS.apply(this, args) }) this.originalNode = node + this.id = uniqueId() if (this.node.type === 'FunctionDeclaration' && this.node.id !== null) { this.functionName = this.node.id.name } else { diff --git a/src/parser/__tests__/scheme-encode-decode.ts b/src/parser/__tests__/scheme-encode-decode.ts index 877690ce0..a458f0bac 100644 --- a/src/parser/__tests__/scheme-encode-decode.ts +++ b/src/parser/__tests__/scheme-encode-decode.ts @@ -1,8 +1,9 @@ -import { decodeError, decodeValue } from '../scheme' -import { encode, decode } from '../../scm-slang/src' -import { UnassignedVariable } from '../../errors/errors' import { Node } from 'estree' + +import { UnassignedVariable } from '../../errors/errors' +import { decode, encode } from '../../scm-slang/src' import { dummyExpression } from '../../utils/dummyAstCreator' +import { decodeError, decodeValue } from '../scheme' describe('Scheme encoder and decoder', () => { it('encodes and decodes strings correctly', () => { diff --git a/src/parser/parser.ts b/src/parser/parser.ts index 220950114..7d7602297 100644 --- a/src/parser/parser.ts +++ b/src/parser/parser.ts @@ -4,9 +4,9 @@ import { Context } from '..' import { Chapter, Variant } from '../types' import { FullJSParser } from './fullJS' import { FullTSParser } from './fullTS' +import { PythonParser } from './python' import { SchemeParser } from './scheme' import { SourceParser } from './source' -import { PythonParser } from './python' import { SourceTypedParser } from './source/typed' import { AcornOptions, Parser } from './types' diff --git a/src/repl/repl.ts b/src/repl/repl.ts index a448326d5..b3b4f7f47 100644 --- a/src/repl/repl.ts +++ b/src/repl/repl.ts @@ -2,7 +2,7 @@ import { start } from 'repl' // 'repl' here refers to the module named 'repl' in index.d.ts import { inspect } from 'util' -import { scmLanguages, sourceLanguages, pyLanguages } from '../constants' +import { pyLanguages, scmLanguages, sourceLanguages } from '../constants' import { createContext, IOptions, parseError, runInContext } from '../index' import Closure from '../interpreter/closure' import { ExecutionMethod, Variant } from '../types' diff --git a/src/utils/astCreator.ts b/src/utils/astCreator.ts index 78f3e3761..9a9404815 100644 --- a/src/utils/astCreator.ts +++ b/src/utils/astCreator.ts @@ -77,7 +77,7 @@ export const expressionStatement = (expression: es.Expression): es.ExpressionSta export const blockArrowFunction = ( params: es.Identifier[], - body: es.Statement[] | es.BlockStatement, + body: es.Statement[] | es.BlockStatement | es.Expression, loc?: es.SourceLocation | null ): es.ArrowFunctionExpression => ({ type: 'ArrowFunctionExpression', @@ -234,12 +234,14 @@ export const arrayExpression = (elements: es.Expression[]): es.ArrayExpression = export const assignmentExpression = ( left: es.Identifier | es.MemberExpression, - right: es.Expression + right: es.Expression, + loc?: es.SourceLocation | null ): es.AssignmentExpression => ({ type: 'AssignmentExpression', operator: '=', left, - right + right, + loc }) export const binaryExpression = ( diff --git a/src/utils/astToString.ts b/src/utils/astToString.ts new file mode 100644 index 000000000..e34a7cfe4 --- /dev/null +++ b/src/utils/astToString.ts @@ -0,0 +1,4 @@ +import { generate } from 'astring' +import { Node } from 'estree' + +export const astToString = (node: Node): string => generate(node)