diff --git a/package.json b/package.json index 6b956bec..72748626 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "test:game": "mocha --parallel -r ts-node/register/transpile-only test/**/game.test.ts", "test:dynamicDiagram": "mocha --parallel -r ts-node/register/transpile-only test/dynamicDiagram.test.ts", "test:helpers": "mocha --parallel -r ts-node/register/transpile-only test/helpers.test.ts", - "test:interpreter": "mocha -r ts-node/register/transpile-only test/interpreter.test.ts", + "test:interpreter": "mocha --parallel -r ts-node/register/transpile-only test/interpreter.test.ts", "test:linker": "mocha --parallel -r ts-node/register/transpile-only test/linker.test.ts", "test:messageReporter": "mocha --parallel -r ts-node/register/transpile-only test/messageReporter.test.ts", "test:model": "mocha --parallel -r ts-node/register/transpile-only test/model.test.ts", diff --git a/src/interpreter/interpreter.ts b/src/interpreter/interpreter.ts index a9bfc9b9..b15ceeb5 100644 --- a/src/interpreter/interpreter.ts +++ b/src/interpreter/interpreter.ts @@ -1,7 +1,7 @@ import { linkSentenceInNode } from '../linker' import { Entity, Environment, Import, Method, Module, Name, Node, Reference, Sentence } from '../model' import WRENatives from '../wre/wre.natives' -import { Evaluation, Execution, ExecutionDefinition, Natives, RuntimeObject, RuntimeValue, WollokException } from './runtimeModel' +import { Evaluation, Execution, ExecutionDefinition, Frame, Natives, RuntimeObject, RuntimeValue, WollokException } from './runtimeModel' import * as parse from '../parser' import { notEmpty } from '../extensions' import { WOLLOK_EXTRA_STACK_TRACE_HEADER } from '../constants' @@ -116,25 +116,27 @@ export class Interpreter extends AbstractInterpreter { } -export function interprete(interpreter: Interpreter, line: string): ExecutionResult { +export function interprete(interpreter: AbstractInterpreter, line: string, frame?: Frame): ExecutionResult { try { const sentenceOrImport = parse.Import.or(parse.Variable).or(parse.Assignment).or(parse.Expression).tryParse(line) const error = [sentenceOrImport, ...sentenceOrImport.descendants].flatMap(_ => _.problems ?? []).find(_ => _.level === 'error') if (error) throw error if (sentenceOrImport.is(Sentence)) { - const environment = interpreter.evaluation.environment - linkSentenceInNode(sentenceOrImport, environment.replNode()) + linkSentenceInNode(sentenceOrImport, frame ? frame.node.parentPackage! : interpreter.evaluation.environment.replNode()) const unlinkedNode = [sentenceOrImport, ...sentenceOrImport.descendants].find(_ => _.is(Reference) && !_.target) if (unlinkedNode) { if (unlinkedNode.is(Reference)) { - if (!interpreter.evaluation.currentFrame.get(unlinkedNode.name)) + if (!(frame ?? interpreter.evaluation.currentFrame).get(unlinkedNode.name)) return failureResult(`Unknown reference ${unlinkedNode.name}`) } else return failureResult(`Unknown reference at ${unlinkedNode.sourceInfo}`) } - const result = interpreter.exec(sentenceOrImport) + const result = frame ? + interpreter.do(function () { return interpreter.evaluation.exec(sentenceOrImport, frame) }) : + interpreter.exec(sentenceOrImport) + const stringResult = !result || isVoid(result) ? '' : result.showShortValue(interpreter) diff --git a/src/linker.ts b/src/linker.ts index c23ac82d..5268e362 100644 --- a/src/linker.ts +++ b/src/linker.ts @@ -1,6 +1,7 @@ import { v4 as uuid } from 'uuid' import { divideOn, is, List } from './extensions' import { BaseProblem, Entity, Environment, Field, Id, Import, Level, Module, Name, Node, Package, Parameter, ParameterizedType, Reference, Scope, Sentence, SourceMap, Variable } from './model' +import { REPL } from './constants' const { assign } = Object @@ -67,7 +68,7 @@ export class LocalScope implements Scope { register(...contributions: [Name, Node][]): void { const shouldBeOverrided = (older: Node, newer: Node) => // Override wtest files with same name than wlk - older.is(Package) && newer.is(Package) && older.isTestFile && !newer.isTestFile + older.is(Package) && newer.is(Package) && !newer.isTestFile for (const [name, node] of contributions) { const alreadyRegistered = this.contributions.get(name) if (!alreadyRegistered || shouldBeOverrided(alreadyRegistered, node)) { @@ -134,7 +135,7 @@ export default (newPackages: List, baseEnvironment?: Environment): Envi const environment = new Environment({ id: uuid(), scope: undefined, - members: newPackages.reduce(mergePackage, baseEnvironment?.members ?? []) as List, + members: newPackages.reduce(mergePackage, baseEnvironment?.members ?? [new Package({ name: REPL })]) as List, }).transform(node => node.copy({ id: uuid() })) const nodeCache = new Map() diff --git a/src/validator/en.json b/src/validator/en.json index d8f3e714..47d14c3a 100644 --- a/src/validator/en.json +++ b/src/validator/en.json @@ -57,4 +57,4 @@ "shouldUseOverrideKeyword": "Method should be marked as override, since it overrides a superclass method", "shouldUseSelfAndNotSingletonReference": "Don't use the name within the object. Use 'self' instead.", "superclassShouldBeLastInLinearization": "Bad Linearization: superclass should be last in linearization" -} \ No newline at end of file +} diff --git a/src/validator/es.json b/src/validator/es.json index cc52fcd0..2967cc51 100644 --- a/src/validator/es.json +++ b/src/validator/es.json @@ -57,4 +57,4 @@ "shouldUseOverrideKeyword": "Debería marcarse el método con 'override', ya que sobrescribe el de sus superclases", "shouldUseSelfAndNotSingletonReference": "No debe usar el nombre del objeto dentro del mismo. Use 'self'.", "superclassShouldBeLastInLinearization": "Linearización: la superclase debería estar última en linearización" -} \ No newline at end of file +} diff --git a/src/wre/game.ts b/src/wre/game.ts index 8a4c3c9d..5a6684d3 100644 --- a/src/wre/game.ts +++ b/src/wre/game.ts @@ -44,7 +44,7 @@ const game: Natives = { // Avoid to invoke method position() for optimisation reasons. // -> If method isSynthetic then it is a getter, we can access to the field directly const method = visual.module.lookupMethod('position', 0)! - const otherPosition = method.isSynthetic ? visual.get('position') :yield* this.invoke(method, visual) + const otherPosition = method.isSynthetic ? visual.get('position') : yield* this.invoke(method, visual) const otherX = otherPosition?.get('x')?.innerNumber const otherY = otherPosition?.get('y')?.innerNumber diff --git a/test/assertions.ts b/test/assertions.ts index 16a7f2d2..b9c7716f 100644 --- a/test/assertions.ts +++ b/test/assertions.ts @@ -6,7 +6,7 @@ import { join } from 'path' import { buildEnvironment as buildEnv, print } from '../src' import { List } from '../src/extensions' import link from '../src/linker' -import { Environment, Environment as EnvironmentType, Name, Node, Package, Reference, SourceIndex } from '../src/model' +import { Environment as EnvironmentType, Name, Node, Package, Reference, SourceIndex } from '../src/model' import { ParseError } from '../src/parser' import validate, { Validation } from '../src/validator' @@ -145,7 +145,7 @@ export const linkerAssertions: Chai.ChaiPlugin = ({ Assertion }) => { Assertion.addMethod('linkedInto', function (expected: List) { const dropLinkedFields = dropKeys('id', 'scope') const actualEnvironment = link(this._obj) - const expectedEnvironment = new Environment({ members: expected }) + const expectedEnvironment = link(expected) new Assertion(dropLinkedFields(actualEnvironment)).to.deep.equal(dropLinkedFields(expectedEnvironment)) }) diff --git a/test/dynamicDiagram.test.ts b/test/dynamicDiagram.test.ts index 2233f5e0..e6281077 100644 --- a/test/dynamicDiagram.test.ts +++ b/test/dynamicDiagram.test.ts @@ -3,7 +3,7 @@ import { BOOLEAN_MODULE, buildEnvironment, CLOSURE_MODULE, DATE_MODULE, DICTIONA import { DynamicDiagramElement, DynamicDiagramNode, DynamicDiagramReference } from '../src/interpreter/dynamicDiagram' import { interprete, Interpreter } from '../src/interpreter/interpreter' import linker from '../src/linker' -import { WREEnvironment } from './utils' +import { environmentWithREPLInitializedFile, INIT_PACKAGE_NAME, WREEnvironment } from './utils' describe('Dynamic diagram', () => { @@ -245,8 +245,7 @@ describe('Dynamic diagram', () => { }) it('should include bidirectional relationships', () => { - const replEnvironment = buildEnvironment([{ - name: REPL, content: ` + const replEnvironment = environmentWithREPLInitializedFile(` class Ave { var property amigue = null method tieneEnergia() = energia > 0 @@ -255,8 +254,7 @@ describe('Dynamic diagram', () => { object pepita inherits Ave { override method tieneEnergia() = true } - `, - }]) + `) interpreter = new Interpreter(Evaluation.build(replEnvironment, WRENatives)) interprete(interpreter, 'const pepona = new Ave(amigue = pepita)') interprete(interpreter, 'pepita.amigue(pepona)') @@ -265,36 +263,34 @@ describe('Dynamic diagram', () => { referenceLabel: 'pepona', targetLabel: 'Ave', targetType: 'object', - targetModule: 'REPL.Ave', + targetModule: INIT_PACKAGE_NAME + '.Ave', }) checkConnection(elements, { sourceLabel: 'Ave', referenceLabel: 'amigue', targetLabel: 'pepita', targetType: 'object', - sourceModule: 'REPL.Ave', - targetModule: 'REPL.pepita', + sourceModule: INIT_PACKAGE_NAME + '.Ave', + targetModule: INIT_PACKAGE_NAME + '.pepita', }) checkConnection(elements, { sourceLabel: 'pepita', referenceLabel: 'amigue', targetLabel: 'Ave', targetType: 'object', - sourceModule: 'REPL.pepita', - targetModule: 'REPL.Ave', + sourceModule: INIT_PACKAGE_NAME + '.pepita', + targetModule: INIT_PACKAGE_NAME + '.Ave', }) checkNoConnectionToREPL(elements, 'pepita') }) it('should include recursive relationships', () => { - const replEnvironment = buildEnvironment([{ - name: REPL, content: ` + const replEnvironment = environmentWithREPLInitializedFile(` class Ave { var property amigue = null override method tieneEnergia() = true } - `, - }]) + `) interpreter = new Interpreter(Evaluation.build(replEnvironment, WRENatives)) interprete(interpreter, 'const pepita = new Ave()') interprete(interpreter, 'pepita.amigue(pepita)') @@ -303,15 +299,15 @@ describe('Dynamic diagram', () => { referenceLabel: 'pepita', targetLabel: 'Ave', targetType: 'object', - targetModule: 'REPL.Ave', + targetModule: INIT_PACKAGE_NAME + '.Ave', }) checkConnection(elements, { sourceLabel: 'Ave', referenceLabel: 'amigue', targetLabel: 'Ave', targetType: 'object', - sourceModule: 'REPL.Ave', - targetModule: 'REPL.Ave', + sourceModule: INIT_PACKAGE_NAME + '.Ave', + targetModule: INIT_PACKAGE_NAME + '.Ave', }) }) diff --git a/test/helpers.test.ts b/test/helpers.test.ts index 8092a8e1..214b1628 100644 --- a/test/helpers.test.ts +++ b/test/helpers.test.ts @@ -1,7 +1,7 @@ import { expect, should, use } from 'chai' import sinonChai from 'sinon-chai' import { BOOLEAN_MODULE, Body, Class, Describe, Environment, Evaluation, Field, Import, Interpreter, isError, LIST_MODULE, Literal, Method, methodByFQN, NUMBER_MODULE, New, OBJECT_MODULE, Package, Parameter, Reference, STRING_MODULE, Self, Send, Singleton, Test, Variable, WRENatives, allAvailableMethods, allScopedVariables, allVariables, implicitImport, isNamedSingleton, isNotImportedIn, link, linkSentenceInNode, literalValueToClass, mayExecute, parentModule, parse, projectPackages, hasNullValue, hasBooleanValue, projectToJSON, getNodeDefinition, ParameterizedType, sendDefinitions, Super, SourceMap, isVoid, VOID_WKO, REPL, buildEnvironment, assertNotVoid, showParameter, getMethodContainer, Program, getExpressionFor, Expression, If, Return } from '../src' -import { WREEnvironment, environmentWithEntities } from './utils' +import { WREEnvironment, environmentWithEntities, environmentWithREPLInitializedFile } from './utils' import { RuntimeObject } from '../src/interpreter/runtimeModel' use(sinonChai) @@ -131,10 +131,10 @@ describe('Wollok helpers', () => { it('should return the right package from an environment', () => { const environment = basicEnvironmentWithSingleClass() - const mainPackage = environment.getNodeByFQN('aves') - projectPackages(environment).should.deep.equal([mainPackage]) + const mainPackage = environment.getNodeByFQN('aves') + projectPackages(environment).includes(mainPackage) + projectPackages(environment).includes(environment.replNode()) }) - }) describe('isNotImportedIn', () => { @@ -894,8 +894,7 @@ describe('Wollok helpers', () => { }) describe('getExpression', () => { - const replEnvironment = buildEnvironment([{ - name: REPL, content: ` + const replEnvironment = environmentWithREPLInitializedFile(` object pajarito { energia = 100 contenta = false @@ -916,9 +915,8 @@ describe('Wollok helpers', () => { method bad() { throw new Exception(message = "Do not call me!") } - }`, - }, - ]) + }` + ) it('should show if expression', () => { const birdSingleton = replEnvironment.getNodeByFQN(REPL + '.pajarito') as Singleton diff --git a/test/interpreter.test.ts b/test/interpreter.test.ts index 1ed3d17c..43d992d6 100644 --- a/test/interpreter.test.ts +++ b/test/interpreter.test.ts @@ -1,11 +1,11 @@ import { expect, should, use } from 'chai' import { restore } from 'sinon' import sinonChai from 'sinon-chai' -import { EXCEPTION_MODULE, Evaluation, REPL, WRENatives, buildEnvironment } from '../src' +import { buildEnvironment, Evaluation, EXCEPTION_MODULE, REPL, WRENatives } from '../src' import { DirectedInterpreter, getStackTraceSanitized, interprete, Interpreter } from '../src/interpreter/interpreter' import link from '../src/linker' import { Body, Class, Field, Literal, Method, Package, ParameterizedType, Reference, Return, Send, Singleton, SourceIndex, SourceMap } from '../src/model' -import { WREEnvironment } from './utils' +import { environmentWithREPLInitializedFile, INIT_FILE, INIT_PACKAGE_NAME, WREEnvironment } from './utils' use(sinonChai) should() @@ -161,8 +161,7 @@ describe('Wollok Interpreter', () => { } beforeEach(() => { - const replPackage = new Package({ name: REPL }) - const environment = link([replPackage], WREEnvironment) + const environment = link([], WREEnvironment) interpreter = new Interpreter(Evaluation.build(environment, WRENatives)) }) @@ -313,7 +312,7 @@ describe('Wollok Interpreter', () => { } `, }, { - name: REPL, content: ` + name: INIT_FILE, content: ` import medico.* object testit { @@ -321,6 +320,7 @@ describe('Wollok Interpreter', () => { } `, }]) + replEnvironment.scope.register([REPL, replEnvironment.getNodeByFQN(INIT_PACKAGE_NAME)!]) interpreter = new Interpreter(Evaluation.build(replEnvironment, WRENatives)) const { error, result } = interprete(interpreter, 'testit.test()') expect(error).to.be.undefined @@ -382,7 +382,7 @@ describe('Wollok Interpreter', () => { } `, }, { - name: REPL, content: ` + name: INIT_PACKAGE_NAME, content: ` import pediatra.* object testit { @@ -390,12 +390,42 @@ describe('Wollok Interpreter', () => { } `, }]) + + replEnvironment.scope.register([REPL, replEnvironment.getNodeByFQN(INIT_PACKAGE_NAME)!]) interpreter = new Interpreter(Evaluation.build(replEnvironment, WRENatives)) const { error, result } = interprete(interpreter, 'testit.test()') expect(error).to.be.undefined expect(result).to.equal('"hola"') }) + it('should be able to interprete sentences within a certain context', () => { + const environment = buildEnvironment([{ + name: 'pepita-file.wlk', + content: ` + object pepita { + var energia = 100 + method volar() { + energia = energia - 10 + } + }`, + }, + { + name: 'pepita-tests.wtest', + content: ` + import pepita-file.* + + test "testPepita" { + pepita.volar() + }`, + }]) + const directedInterpreter: DirectedInterpreter = new DirectedInterpreter(Evaluation.build(environment, WRENatives)) + const executionDirector = directedInterpreter.exec(directedInterpreter.evaluation.environment.getNodeByFQN('pepita-tests."testPepita"')) + executionDirector.addBreakpoint(directedInterpreter.evaluation.environment.getNodeByFQN('pepita-file.pepita').methods[0]) + executionDirector.resume() + const { error, result } = interprete(new Interpreter(directedInterpreter.evaluation), 'energia', directedInterpreter.evaluation.currentFrame) + expect(error).to.be.undefined + expect(result).to.equal('100') + }) }) describe('sanitize stack trace', () => { @@ -455,8 +485,7 @@ describe('Wollok Interpreter', () => { }) it('should wrap void validation errors for void parameter in super call', () => { - const replEnvironment = buildEnvironment([{ - name: REPL, content: ` + const replEnvironment = environmentWithREPLInitializedFile(` class Bird { var energy = 100 method fly(minutes) { @@ -469,22 +498,20 @@ describe('Wollok Interpreter', () => { super([1, 2].add(4)) } } - `, - }]) + `) interpreter = new Interpreter(Evaluation.build(replEnvironment, WRENatives)) const { error } = interprete(interpreter, 'new MockingBird().fly(2)') assertBasicError(error) expect(getStackTraceSanitized(error)).to.deep.equal( [ 'wollok.lang.EvaluationError: RangeError: super call for message fly/1: parameter #1 produces no value, cannot use it', - ' at REPL.MockingBird.fly(minutes) [REPL:11]', + ` at ${INIT_PACKAGE_NAME}.MockingBird.fly(minutes) [${INIT_PACKAGE_NAME}.wlk:11]`, ] ) }) it('should wrap void validation errors for void condition in if', () => { - const replEnvironment = buildEnvironment([{ - name: REPL, content: ` + const replEnvironment = environmentWithREPLInitializedFile(` class Bird { var energy = 100 method fly(minutes) { @@ -493,15 +520,14 @@ describe('Wollok Interpreter', () => { } } } - `, - }]) + `) interpreter = new Interpreter(Evaluation.build(replEnvironment, WRENatives)) const { error } = interprete(interpreter, 'new Bird().fly(2)') assertBasicError(error) expect(getStackTraceSanitized(error)).to.deep.equal( [ 'wollok.lang.EvaluationError: RangeError: Message fly - if condition produces no value, cannot use it', - ' at REPL.Bird.fly(minutes) [REPL:5]', + ` at ${INIT_PACKAGE_NAME}.Bird.fly(minutes) [${INIT_PACKAGE_NAME}.wlk:5]`, ] ) }) @@ -583,13 +609,11 @@ describe('Wollok Interpreter', () => { }) it('should wrap void validation errors for assignment to void value', () => { - const replEnvironment = buildEnvironment([{ - name: REPL, content: ` + const replEnvironment = environmentWithREPLInitializedFile(` object pepita { method volar() { } - }`, - }]) + }`) interpreter = new Interpreter(Evaluation.build(replEnvironment, WRENatives)) expectError('const a = pepita.volar()', 'wollok.lang.EvaluationError: RangeError: Cannot assign to variable \'a\': message volar/0 produces no value, cannot assign it to a variable') expectError('const a = if (4 > 5) true else pepita.volar()', 'wollok.lang.EvaluationError: RangeError: Cannot assign to variable \'a\': if expression produces no value, cannot assign it to a variable') @@ -597,14 +621,13 @@ describe('Wollok Interpreter', () => { }) it('should wrap void validation errors for void method used in expression', () => { - const replEnvironment = buildEnvironment([{ - name: REPL, content: ` + const replEnvironment = environmentWithREPLInitializedFile(` object pepita { method volar() { } - }`, - }]) + }`) interpreter = new Interpreter(Evaluation.build(replEnvironment, WRENatives)) + const { error } = interprete(interpreter, '5 + pepita.volar()') assertBasicError(error) expect(getStackTraceSanitized(error)).to.deep.equal([ @@ -613,22 +636,19 @@ describe('Wollok Interpreter', () => { }) it('should handle errors when using void values in new named parameters', () => { - const replEnvironment = buildEnvironment([{ - name: REPL, content: ` + const replEnvironment = environmentWithREPLInitializedFile(` class Bird { var energy = 100 var name = "Pepita" } - `, - }]) + `) interpreter = new Interpreter(Evaluation.build(replEnvironment, WRENatives)) - expectError('new Bird(energy = void)', 'wollok.lang.EvaluationError: RangeError: new REPL.Bird: value of parameter \'energy\' produces no value, cannot use it') - expectError('new Bird(energy = 150, name = [1].add(2))', 'wollok.lang.EvaluationError: RangeError: new REPL.Bird: value of parameter \'name\' produces no value, cannot use it') + expectError('new Bird(energy = void)', `wollok.lang.EvaluationError: RangeError: new ${INIT_PACKAGE_NAME}.Bird: value of parameter 'energy' produces no value, cannot use it`) + expectError('new Bird(energy = 150, name = [1].add(2))', `wollok.lang.EvaluationError: RangeError: new ${INIT_PACKAGE_NAME}.Bird: value of parameter 'name' produces no value, cannot use it`) }) it('should show Wollok stack', () => { - const replEnvironment = buildEnvironment([{ - name: REPL, content: ` + const replEnvironment = environmentWithREPLInitializedFile(` object comun { method volar() { self.despegar() @@ -646,44 +666,39 @@ describe('Wollok Interpreter', () => { method volar() { formaVolar.volar() } - }`, - }]) + }`) interpreter = new Interpreter(Evaluation.build(replEnvironment, WRENatives)) const { error } = interprete(interpreter, 'new Ave().volar()') assertBasicError(error) expect(getStackTraceSanitized(error)).to.deep.equal([ 'wollok.lang.EvaluationError: TypeError: Message plusDays: parameter "wollok.lang.Date" should be a number', - ' at REPL.comun.despegar() [REPL:8]', - ' at REPL.comun.volar() [REPL:4]', - ' at REPL.Ave.volar() [REPL:17]', + ` at ${INIT_PACKAGE_NAME}.comun.despegar() [${INIT_PACKAGE_NAME}.wlk:8]`, + ` at ${INIT_PACKAGE_NAME}.comun.volar() [${INIT_PACKAGE_NAME}.wlk:4]`, + ` at ${INIT_PACKAGE_NAME}.Ave.volar() [${INIT_PACKAGE_NAME}.wlk:17]`, ]) }) it('should handle errors when using void return values for wko', () => { - const replEnvironment = buildEnvironment([{ - name: REPL, content: ` + const replEnvironment = environmentWithREPLInitializedFile(` object pepita { method unMetodo() { return [1,2,3].add(4) + 5 } } - `, - }]) + `) interpreter = new Interpreter(Evaluation.build(replEnvironment, WRENatives)) const { error } = interprete(interpreter, 'pepita.unMetodo()') assertBasicError(error) expect(getStackTraceSanitized(error)).to.deep.equal([ 'wollok.lang.EvaluationError: RangeError: Cannot send message +, receiver is an expression that produces no value.', - ' at REPL.pepita.unMetodo() [REPL:4]', + ` at ${INIT_PACKAGE_NAME}.pepita.unMetodo() [${INIT_PACKAGE_NAME}.wlk:4]`, ]) }) it('should handle errors when using void closures inside native list methods', () => { - const replEnvironment = buildEnvironment([{ - name: REPL, content: ` + const replEnvironment = environmentWithREPLInitializedFile(` const pepita = object { method energia(total) { } } - `, - }]) + `) interpreter = new Interpreter(Evaluation.build(replEnvironment, WRENatives)) expectError('[1, 2].filter { n => pepita.energia(n) }', 'wollok.lang.EvaluationError: RangeError: Message filter: closure produces no value. Check the return type of the closure (missing return?)') expectError('[1, 2].findOrElse({ n => pepita.energia(n) }, {})', 'wollok.lang.EvaluationError: RangeError: Message findOrElse: predicate produces no value. Check the return type of the closure (missing return?)') @@ -692,11 +707,9 @@ describe('Wollok Interpreter', () => { }) it('should handle errors when using void closures inside native set methods', () => { - const replEnvironment = buildEnvironment([{ - name: REPL, content: ` + const replEnvironment = environmentWithREPLInitializedFile(` const pepita = object { method energia(total) { } } - `, - }]) + `) interpreter = new Interpreter(Evaluation.build(replEnvironment, WRENatives)) expectError('#{1, 2}.filter { n => pepita.energia(n) }', 'wollok.lang.EvaluationError: RangeError: Message filter: closure produces no value. Check the return type of the closure (missing return?)') expectError('#{1, 2}.findOrElse({ n => pepita.energia(n) }, {})', 'wollok.lang.EvaluationError: RangeError: Message findOrElse: predicate produces no value. Check the return type of the closure (missing return?)') @@ -704,21 +717,17 @@ describe('Wollok Interpreter', () => { }) it('should handle errors when using void closures inside Wollok list methods', () => { - const replEnvironment = buildEnvironment([{ - name: REPL, content: ` + const replEnvironment = environmentWithREPLInitializedFile(` const pepita = object { method energia(total) { } } - `, - }]) + `) interpreter = new Interpreter(Evaluation.build(replEnvironment, WRENatives)) expectError('[1, 2].map { n => pepita.energia(n) }', 'wollok.lang.EvaluationError: RangeError: map - while sending message List.add/1: parameter #1 produces no value, cannot use it') }) it('should handle errors when using void parameters', () => { - const replEnvironment = buildEnvironment([{ - name: REPL, content: ` + const replEnvironment = environmentWithREPLInitializedFile(` const pepita = object { method energia() { } } - `, - }]) + `) interpreter = new Interpreter(Evaluation.build(replEnvironment, WRENatives)) expect('[].add(pepita.energia())', 'wollok.lang.EvaluationError: RangeError: Message List.add/1: parameter #1 produces no value, cannot use it') }) @@ -726,14 +735,11 @@ describe('Wollok Interpreter', () => { }) it('should handle void values for assert', () => { - const replEnvironment = buildEnvironment([{ - name: REPL, content: ` + const replEnvironment = environmentWithREPLInitializedFile(` object pajarito { method volar() { } - } - `, - }]) + }`) interpreter = new Interpreter(Evaluation.build(replEnvironment, WRENatives)) expectError('assert.that(pajarito.volar())', 'wollok.lang.EvaluationError: RangeError: Message assert.that/1: parameter #1 produces no value, cannot use it') }) diff --git a/test/linker.test.ts b/test/linker.test.ts index c960b66e..379e6630 100644 --- a/test/linker.test.ts +++ b/test/linker.test.ts @@ -1,5 +1,5 @@ import { expect, should, use } from 'chai' -import { GAME_MODULE, OBJECT_MODULE } from '../src' +import { GAME_MODULE, OBJECT_MODULE, REPL } from '../src' import { getPotentiallyUninitializedLazy } from '../src/decorators' import link, { canBeReferenced, linkSentenceInNode } from '../src/linker' import { Body, Class, Closure, Describe, Environment, Field, Import, Method, Mixin, NamedArgument, Node, Package, Parameter, ParameterizedType, Reference, Return, Sentence, Singleton, Test, Variable, Literal } from '../src/model' @@ -13,6 +13,13 @@ use(linkerAssertions) const MINIMAL_LANG = environmentWithEntities(OBJECT_MODULE, GAME_MODULE) describe('Wollok linker', () => { + it('should always link the repl package', () => { + it('an environment should always include the REPL package', () => { + [].should.be.linkedInto([ + new Package({ name: REPL }), + ]) + }) + }) describe('merge', () => { @@ -172,7 +179,7 @@ describe('Wollok linker', () => { }), ], baseEnvironment) - const p = nextEnvironment.members[1] + const p = nextEnvironment.getNodeByFQN('p') const Y = p.members[0] p.members.should.have.lengthOf(1) diff --git a/test/model.test.ts b/test/model.test.ts index 536e483c..cb5a3cce 100644 --- a/test/model.test.ts +++ b/test/model.test.ts @@ -51,7 +51,7 @@ describe('Wollok model', () => { ], })], fromJSON(wre)) - const pepita: Singleton = (env.members[1].members[0] as Package).members[0] as Singleton + const pepita = env.getNodeByFQN('src.pepitaFile.pepita') pepita.parentPackage?.name.should.equal('pepitaFile') }) diff --git a/test/utils.ts b/test/utils.ts index 4af36a91..fc782563 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -2,10 +2,13 @@ import { fail } from 'assert' import { readFileSync } from 'fs' import globby from 'globby' import { join } from 'path' -import { Annotation, buildEnvironment, Class, Environment, FileContent, fromJSON, link, Literal, Node, Package, Problem, PROGRAM_FILE_EXTENSION, Reference, SourceMap, TEST_FILE_EXTENSION, WOLLOK_FILE_EXTENSION } from '../src' +import { Annotation, buildEnvironment, Class, Environment, FileContent, fromJSON, link, Literal, Node, Package, Problem, PROGRAM_FILE_EXTENSION, Reference, REPL, SourceMap, TEST_FILE_EXTENSION, WOLLOK_FILE_EXTENSION } from '../src' import { divideOn, List, notEmpty } from '../src/extensions' import wre from '../src/wre/wre.json' +export const INIT_PACKAGE_NAME = 'definitions' +export const INIT_FILE = INIT_PACKAGE_NAME + '.wlk' + export function buildEnvironmentForEachFile(folderPath: string, iterator: (filePackage: Package, fileContent: FileContent) => void): void { const files = globby.sync(`**/*.@(${WOLLOK_FILE_EXTENSION}|${TEST_FILE_EXTENSION}|${PROGRAM_FILE_EXTENSION})`, { cwd: folderPath }).map(name => ({ name, @@ -83,6 +86,13 @@ export const validateExpectationProblem = (expectedProblem: Annotation, nodeProb return effectiveProblem } +export const environmentWithREPLInitializedFile = (content: string, name = INIT_PACKAGE_NAME): Environment => { + const environment = buildEnvironment([{ name: name + '.wlk', content }]) + const initPackage = environment.getNodeByFQN(name) + environment.scope.register([REPL, initPackage]) + return environment +} + export const environmentWithEntities = (...fqns: string[]): Environment => fqns.reduce((env, fqn) => link([newPackageWith(WREEnvironment, fqn)], env), link([]))