From e27123d8e0e456fd19429eb3817b9a402eae35e3 Mon Sep 17 00:00:00 2001 From: ivojawer Date: Fri, 27 Dec 2024 21:21:47 -0300 Subject: [PATCH 1/7] Interpret line with optinal frame --- src/interpreter/interpreter.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/interpreter/interpreter.ts b/src/interpreter/interpreter.ts index 25a096de..6bc03515 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' @@ -92,6 +92,7 @@ export const getStackTraceSanitized = (e?: Error): string[] => { const fullStack = e?.stack?.slice(0, indexOfTsStack ?? -1) ?? '' return fullStack + .replaceAll('\r', '') .replaceAll('\t', ' ') .replaceAll(' ', ' ') .replaceAll(' ', ' ') @@ -115,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) From b22e12fd7f50c62008567b25d4d1dabd4fda6f59 Mon Sep 17 00:00:00 2001 From: ivojawer Date: Fri, 27 Dec 2024 21:23:18 -0300 Subject: [PATCH 2/7] Adding REPL node during Environment creation --- src/linker.ts | 5 +++-- test/assertions.ts | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) 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/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)) }) From 95066ef9cd14904850a0059d61844c5095fd0f1c Mon Sep 17 00:00:00 2001 From: ivojawer Date: Fri, 27 Dec 2024 21:23:32 -0300 Subject: [PATCH 3/7] Fixing REPL tests --- package.json | 2 +- src/wre/game.ts | 2 +- test/dynamicDiagram.test.ts | 30 ++++---- test/helpers.test.ts | 16 ++--- test/interpreter.test.ts | 132 +++++++++++++++++++----------------- test/linker.test.ts | 11 ++- test/model.test.ts | 2 +- test/utils.ts | 11 ++- 8 files changed, 111 insertions(+), 95 deletions(-) diff --git a/package.json b/package.json index 178fbeee..eafbfbe6 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "test:examples": "npm run test:wtest -- --root language/test/examples", "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:helpers": "mocha -r ts-node/register/transpile-only test/helpers.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", 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/dynamicDiagram.test.ts b/test/dynamicDiagram.test.ts index 2233f5e0..0d3e42cc 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_FILE, 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_FILE + '.Ave', }) checkConnection(elements, { sourceLabel: 'Ave', referenceLabel: 'amigue', targetLabel: 'pepita', targetType: 'object', - sourceModule: 'REPL.Ave', - targetModule: 'REPL.pepita', + sourceModule: INIT_FILE + '.Ave', + targetModule: INIT_FILE + '.pepita', }) checkConnection(elements, { sourceLabel: 'pepita', referenceLabel: 'amigue', targetLabel: 'Ave', targetType: 'object', - sourceModule: 'REPL.pepita', - targetModule: 'REPL.Ave', + sourceModule: INIT_FILE + '.pepita', + targetModule: INIT_FILE + '.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_FILE + '.Ave', }) checkConnection(elements, { sourceLabel: 'Ave', referenceLabel: 'amigue', targetLabel: 'Ave', targetType: 'object', - sourceModule: 'REPL.Ave', - targetModule: 'REPL.Ave', + sourceModule: INIT_FILE + '.Ave', + targetModule: INIT_FILE + '.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 bbe0dd40..d620471b 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, WREEnvironment } from './utils' use(sinonChai) should() @@ -163,8 +163,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)) }) @@ -315,7 +314,7 @@ describe('Wollok Interpreter', () => { } `, }, { - name: REPL, content: ` + name: 'definitions.wlk', content: ` import medico.* object testit { @@ -323,6 +322,7 @@ describe('Wollok Interpreter', () => { } `, }]) + replEnvironment.scope.register([REPL, replEnvironment.getNodeByFQN('definitions')!]) interpreter = new Interpreter(Evaluation.build(replEnvironment, WRENatives)) const { error, result } = interprete(interpreter, 'testit.test()') expect(error).to.be.undefined @@ -384,7 +384,7 @@ describe('Wollok Interpreter', () => { } `, }, { - name: REPL, content: ` + name: 'definitions.wlk', content: ` import pediatra.* object testit { @@ -392,12 +392,42 @@ describe('Wollok Interpreter', () => { } `, }]) + + replEnvironment.scope.register([REPL, replEnvironment.getNodeByFQN('definitions')!]) 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', () => { @@ -457,8 +487,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) { @@ -471,22 +500,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:10]', + ' at definitions.MockingBird.fly(minutes) [definitions.wlk:10]', ] ) }) 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) { @@ -495,27 +522,24 @@ 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:4]', + ' at definitions.Bird.fly(minutes) [definitions.wlk:4]', ] ) }) 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') @@ -523,14 +547,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([ @@ -539,22 +562,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 definitions.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 definitions.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() @@ -572,44 +592,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:7]', - ' at REPL.comun.volar() [REPL:3]', - ' at REPL.Ave.volar() [REPL:16]', + ' at definitions.comun.despegar() [definitions.wlk:7]', + ' at definitions.comun.volar() [definitions.wlk:3]', + ' at definitions.Ave.volar() [definitions.wlk:16]', ]) }) 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:3]', + ' at definitions.pepita.unMetodo() [definitions.wlk:3]', ]) }) 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?)') @@ -618,11 +633,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?)') @@ -630,21 +643,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') }) @@ -652,14 +661,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..566f055f 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -2,10 +2,12 @@ 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_FILE = 'definitions' + 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 +85,13 @@ export const validateExpectationProblem = (expectedProblem: Annotation, nodeProb return effectiveProblem } +export const environmentWithREPLInitializedFile = (content: string, name = INIT_FILE): 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([])) From 59661e9b04dfd5144b78c1f46d9308ca20021489 Mon Sep 17 00:00:00 2001 From: ivojawer Date: Fri, 27 Dec 2024 21:23:48 -0300 Subject: [PATCH 4/7] Adding translations for validations --- src/validator/en.json | 1 + src/validator/es.json | 1 + 2 files changed, 2 insertions(+) diff --git a/src/validator/en.json b/src/validator/en.json index ae0648d6..d8f3e714 100644 --- a/src/validator/en.json +++ b/src/validator/en.json @@ -48,6 +48,7 @@ "shouldNotUseOverride": "Method does not override anything", "shouldNotUseReservedWords": "{0} is a reserved name for a core element", "shouldNotUseVoidMethodAsValue": "Message send \"{0}\" produces no value (missing return in method?)", + "shouldNotUseVoidSingleton": "Named object 'void' produces no value, use 'null' instead", "shouldOnlyInheritFromMixin": "Mixin can only inherit from another mixin", "shouldPassValuesToAllAttributes": "{0} cannot be instantiated, you must pass values to the following attributes: {1}", "shouldUseBooleanValueInIfCondition": "Expecting a boolean", diff --git a/src/validator/es.json b/src/validator/es.json index 9cb550a1..cc52fcd0 100644 --- a/src/validator/es.json +++ b/src/validator/es.json @@ -48,6 +48,7 @@ "shouldNotUseOverride": "Este método no sobrescribe ningún otro método", "shouldNotUseReservedWords": "{0} es una palabra reservada por la biblioteca de Wollok", "shouldNotUseVoidMethodAsValue": "El mensaje \"{0}\" no retorna ningún valor (quizás te falte un return en el método)", + "shouldNotUseVoidSingleton": "El objeto nombrado 'void' no retorna ningún valor (puede usar 'null' en su lugar)", "shouldOnlyInheritFromMixin": "Los mixines solo pueden heredar de otros mixines", "shouldPassValuesToAllAttributes": "No se puede instanciar {0}. Falta pasar valores a los siguientes atributos: {1}", "shouldUseBooleanValueInIfCondition": "Se espera un booleano", From 25b68dd0af909ed3189beade5a8a68f4bdb76224 Mon Sep 17 00:00:00 2001 From: ivojawer Date: Mon, 30 Dec 2024 01:46:27 -0300 Subject: [PATCH 5/7] revert dictionary changes --- src/validator/en.json | 3 +-- src/validator/es.json | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/validator/en.json b/src/validator/en.json index 0e2421f3..47d14c3a 100644 --- a/src/validator/en.json +++ b/src/validator/en.json @@ -56,6 +56,5 @@ "shouldUseConditionalExpression": "Bad usage of if! You must return the condition itself without using if.", "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", - "shouldNotRedefineIdentity": "Identity '===' operator should not be overriden." + "superclassShouldBeLastInLinearization": "Bad Linearization: superclass should be last in linearization" } diff --git a/src/validator/es.json b/src/validator/es.json index 385b3530..2967cc51 100644 --- a/src/validator/es.json +++ b/src/validator/es.json @@ -56,6 +56,5 @@ "shouldUseConditionalExpression": "Estás usando incorrectamente el if. Devolvé simplemente la expresión booleana.", "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", - "shouldNotRedefineIdentity": "No se debe redefinir el operador de identidad '==='." + "superclassShouldBeLastInLinearization": "Linearización: la superclase debería estar última en linearización" } From 2697b7be65f4ffc8527ca840be3168c9027771b0 Mon Sep 17 00:00:00 2001 From: ivojawer Date: Mon, 30 Dec 2024 02:18:34 -0300 Subject: [PATCH 6/7] definition.wlk var --- test/dynamicDiagram.test.ts | 18 +++++++++--------- test/interpreter.test.ts | 26 +++++++++++++------------- test/utils.ts | 5 +++-- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/test/dynamicDiagram.test.ts b/test/dynamicDiagram.test.ts index 0d3e42cc..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 { environmentWithREPLInitializedFile, INIT_FILE, WREEnvironment } from './utils' +import { environmentWithREPLInitializedFile, INIT_PACKAGE_NAME, WREEnvironment } from './utils' describe('Dynamic diagram', () => { @@ -263,23 +263,23 @@ describe('Dynamic diagram', () => { referenceLabel: 'pepona', targetLabel: 'Ave', targetType: 'object', - targetModule: INIT_FILE + '.Ave', + targetModule: INIT_PACKAGE_NAME + '.Ave', }) checkConnection(elements, { sourceLabel: 'Ave', referenceLabel: 'amigue', targetLabel: 'pepita', targetType: 'object', - sourceModule: INIT_FILE + '.Ave', - targetModule: INIT_FILE + '.pepita', + sourceModule: INIT_PACKAGE_NAME + '.Ave', + targetModule: INIT_PACKAGE_NAME + '.pepita', }) checkConnection(elements, { sourceLabel: 'pepita', referenceLabel: 'amigue', targetLabel: 'Ave', targetType: 'object', - sourceModule: INIT_FILE + '.pepita', - targetModule: INIT_FILE + '.Ave', + sourceModule: INIT_PACKAGE_NAME + '.pepita', + targetModule: INIT_PACKAGE_NAME + '.Ave', }) checkNoConnectionToREPL(elements, 'pepita') }) @@ -299,15 +299,15 @@ describe('Dynamic diagram', () => { referenceLabel: 'pepita', targetLabel: 'Ave', targetType: 'object', - targetModule: INIT_FILE + '.Ave', + targetModule: INIT_PACKAGE_NAME + '.Ave', }) checkConnection(elements, { sourceLabel: 'Ave', referenceLabel: 'amigue', targetLabel: 'Ave', targetType: 'object', - sourceModule: INIT_FILE + '.Ave', - targetModule: INIT_FILE + '.Ave', + sourceModule: INIT_PACKAGE_NAME + '.Ave', + targetModule: INIT_PACKAGE_NAME + '.Ave', }) }) diff --git a/test/interpreter.test.ts b/test/interpreter.test.ts index 5bec5f19..43d992d6 100644 --- a/test/interpreter.test.ts +++ b/test/interpreter.test.ts @@ -5,7 +5,7 @@ import { buildEnvironment, Evaluation, EXCEPTION_MODULE, REPL, WRENatives } from 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 { environmentWithREPLInitializedFile, WREEnvironment } from './utils' +import { environmentWithREPLInitializedFile, INIT_FILE, INIT_PACKAGE_NAME, WREEnvironment } from './utils' use(sinonChai) should() @@ -312,7 +312,7 @@ describe('Wollok Interpreter', () => { } `, }, { - name: 'definitions.wlk', content: ` + name: INIT_FILE, content: ` import medico.* object testit { @@ -320,7 +320,7 @@ describe('Wollok Interpreter', () => { } `, }]) - replEnvironment.scope.register([REPL, replEnvironment.getNodeByFQN('definitions')!]) + 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: 'definitions.wlk', content: ` + name: INIT_PACKAGE_NAME, content: ` import pediatra.* object testit { @@ -391,7 +391,7 @@ describe('Wollok Interpreter', () => { `, }]) - replEnvironment.scope.register([REPL, replEnvironment.getNodeByFQN('definitions')!]) + 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 @@ -505,7 +505,7 @@ describe('Wollok Interpreter', () => { 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 definitions.MockingBird.fly(minutes) [definitions.wlk:11]', + ` at ${INIT_PACKAGE_NAME}.MockingBird.fly(minutes) [${INIT_PACKAGE_NAME}.wlk:11]`, ] ) }) @@ -527,7 +527,7 @@ describe('Wollok Interpreter', () => { expect(getStackTraceSanitized(error)).to.deep.equal( [ 'wollok.lang.EvaluationError: RangeError: Message fly - if condition produces no value, cannot use it', - ' at definitions.Bird.fly(minutes) [definitions.wlk:5]', + ` at ${INIT_PACKAGE_NAME}.Bird.fly(minutes) [${INIT_PACKAGE_NAME}.wlk:5]`, ] ) }) @@ -643,8 +643,8 @@ describe('Wollok Interpreter', () => { } `) interpreter = new Interpreter(Evaluation.build(replEnvironment, WRENatives)) - expectError('new Bird(energy = void)', 'wollok.lang.EvaluationError: RangeError: new definitions.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 definitions.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', () => { @@ -672,9 +672,9 @@ describe('Wollok Interpreter', () => { assertBasicError(error) expect(getStackTraceSanitized(error)).to.deep.equal([ 'wollok.lang.EvaluationError: TypeError: Message plusDays: parameter "wollok.lang.Date" should be a number', - ' at definitions.comun.despegar() [definitions.wlk:8]', - ' at definitions.comun.volar() [definitions.wlk:4]', - ' at definitions.Ave.volar() [definitions.wlk: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]`, ]) }) @@ -691,7 +691,7 @@ describe('Wollok Interpreter', () => { assertBasicError(error) expect(getStackTraceSanitized(error)).to.deep.equal([ 'wollok.lang.EvaluationError: RangeError: Cannot send message +, receiver is an expression that produces no value.', - ' at definitions.pepita.unMetodo() [definitions.wlk:4]', + ` at ${INIT_PACKAGE_NAME}.pepita.unMetodo() [${INIT_PACKAGE_NAME}.wlk:4]`, ]) }) diff --git a/test/utils.ts b/test/utils.ts index 566f055f..fc782563 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -6,7 +6,8 @@ import { Annotation, buildEnvironment, Class, Environment, FileContent, fromJSON import { divideOn, List, notEmpty } from '../src/extensions' import wre from '../src/wre/wre.json' -export const INIT_FILE = 'definitions' +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 => ({ @@ -85,7 +86,7 @@ export const validateExpectationProblem = (expectedProblem: Annotation, nodeProb return effectiveProblem } -export const environmentWithREPLInitializedFile = (content: string, name = INIT_FILE): Environment => { +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]) From 8093b505d9cb4e24e419a910e7b7ec6d7c899d70 Mon Sep 17 00:00:00 2001 From: ivojawer Date: Wed, 1 Jan 2025 23:31:26 -0300 Subject: [PATCH 7/7] fix interpreter test script --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 652d9ff6..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 --parallel 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",