From 435d593e1a7aaf8dae692ac54dd7b248bb9d70f6 Mon Sep 17 00:00:00 2001 From: ivojawer Date: Sat, 29 Jul 2023 13:24:11 -0300 Subject: [PATCH 1/3] recover malfomed sentences in expression bodies --- src/model.ts | 1 + src/parser.ts | 20 ++++++++++++++------ test/game.test.ts | 2 +- test/validator.test.ts | 10 +++++----- 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/model.ts b/src/model.ts index edec5294..d22d606d 100644 --- a/src/model.ts +++ b/src/model.ts @@ -41,6 +41,7 @@ export class SourceMap { toString(): string { return `[${this.start}, ${this.end}]` } covers(offset: number): boolean { return this.start.offset <= offset && this.end.offset >= offset } + includes(other: SourceMap): boolean { return this.start.offset <= other.start.offset && this.end.offset >= other.end.offset } } export class Annotation { diff --git a/src/parser.ts b/src/parser.ts index afe33987..ac4233e0 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -1,4 +1,4 @@ -import Parsimmon, { alt as alt_parser, index, lazy, makeSuccess, notFollowedBy, of, Parser, regex, seq, seqObj, string, whitespace, any, Index } from 'parsimmon' +import Parsimmon, { alt as alt_parser, index, lazy, makeSuccess, notFollowedBy, of, Parser, regex, seq, seqObj, string, whitespace, any, Index, newline } from 'parsimmon' import unraw from 'unraw' import { BaseProblem, SourceIndex, Assignment as AssignmentNode, Body as BodyNode, Catch as CatchNode, Class as ClassNode, Describe as DescribeNode, Entity as EntityNode, Expression as ExpressionNode, Field as FieldNode, If as IfNode, Import as ImportNode, Literal as LiteralNode, Method as MethodNode, Mixin as MixinNode, Name, NamedArgument as NamedArgumentNode, New as NewNode, Node, Package as PackageNode, Parameter as ParameterNode, Program as ProgramNode, Reference as ReferenceNode, Return as ReturnNode, Self as SelfNode, Send as SendNode, Sentence as SentenceNode, Singleton as SingletonNode, Super as SuperNode, Test as TestNode, Throw as ThrowNode, Try as TryNode, Variable as VariableNode, SourceMap, Closure as ClosureNode, ParameterizedType as ParameterizedTypeNode, Level, LiteralValue, Annotation } from './model' import { List, mapObject, discriminate, is } from './extensions' @@ -58,7 +58,7 @@ const error = (code: string) => (...safewords: string[]) => { alt( skippableContext, comment, - notFollowedBy(alt(key('}'), ...breakpoints)).then(any), + notFollowedBy(alt(key('}'), newline, ...breakpoints)).then(any), ) ) @@ -178,6 +178,17 @@ export const Body: Parser = node(BodyNode)(() => obj({ sentences: alt(Sentence.skip(__), sentenceError).many() }).wrap(key('{'), key('}')).map(recover) ) +export const ExpressionBody: Parser = node(BodyNode)(() => { + return obj( + { + sentences: alt(Expression.skip(__).map(value => { + return new ReturnNode({ value }) + }), sentenceError).times(1), + } + ).wrap(_, _).map(recover) +}) + + const inlineableBody: Parser = Body.or( node(BodyNode)(() => obj({ sentences: Sentence.times(1) })).map(body => body.copy({ @@ -315,10 +326,7 @@ export const Method: Parser = node(MethodNode)(() => name: key('method').then(alt(name, operator(ALL_OPERATORS))), parameters, body: alt( - key('=').then(Expression.map(value => new BodyNode({ - sentences: [new ReturnNode({ value })], - sourceMap: value.sourceMap, - }))), + key('=').then(ExpressionBody), key('native'), diff --git a/test/game.test.ts b/test/game.test.ts index 7176b055..8b3b55c1 100644 --- a/test/game.test.ts +++ b/test/game.test.ts @@ -14,7 +14,7 @@ describe('Wollok Game', () => { describe('actions', () => { let environment: Environment - let interpreter: Interpreter + let interpreter: Interpreter before(async () => { diff --git a/test/validator.test.ts b/test/validator.test.ts index 9b4d36f1..51751f30 100644 --- a/test/validator.test.ts +++ b/test/validator.test.ts @@ -19,9 +19,9 @@ describe('Wollok Validations', () => { })) const environment = buildEnvironment(files) - const matchesExpectation = (problem: Problem, expected: Annotation) => { + const matchesExpectation = (problem: Problem, annotatedNode: Node, expected: Annotation) => { const code = expected.args.get('code')! - return problem.code === code && problem.sourceMap?.toString() === problem.node.sourceMap?.toString() + return problem.code === code && annotatedNode.sourceMap?.includes(problem.sourceMap!) } const errorLocation = (node: Node | Problem): string => `${node.sourceMap}` @@ -53,11 +53,11 @@ describe('Wollok Validations', () => { if (!code) fail('Missing required "code" argument in @Expect annotation') - const errors = problems.filter(problem => !matchesExpectation(problem, expectedProblem)) + const errors = problems.filter(problem => !matchesExpectation(problem, node, expectedProblem)) if (notEmpty(errors)) fail(`File contains errors: ${errors.map((_error) => _error.code + ' at ' + errorLocation(_error)).join(', ')}`) - const effectiveProblem = problems.find(problem => matchesExpectation(problem, expectedProblem)) + const effectiveProblem = problems.find(problem => matchesExpectation(problem, node, expectedProblem)) if (!effectiveProblem) fail(`Missing expected ${code} ${level ?? 'problem'} at ${errorLocation(node)}`) @@ -67,7 +67,7 @@ describe('Wollok Validations', () => { } for (const problem of problems) { - if (!expectedProblems.some(expectedProblem => matchesExpectation(problem, expectedProblem))) + if (!expectedProblems.some(expectedProblem => matchesExpectation(problem, node, expectedProblem))) fail(`Unexpected ${problem.code} ${problem.level} at ${errorLocation(node)}`) } }) From ca5e7769ed01867758d3350d0d9144d11ed94bbe Mon Sep 17 00:00:00 2001 From: ivojawer Date: Sat, 29 Jul 2023 13:24:36 -0300 Subject: [PATCH 2/3] validator source map typo --- src/validator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/validator.ts b/src/validator.ts index 67c310f0..2dcf71ec 100644 --- a/src/validator.ts +++ b/src/validator.ts @@ -766,7 +766,7 @@ const validationsByKind = (node: Node): Record> => match export default (target: Node): List => target.reduce((found, node) => { return [ ...found, - ...node.problems?.map(({ code }) => ({ code, level: 'error', node, values: [], source: node.sourceMap } as const) ) ?? [], + ...node.problems?.map(({ code, sourceMap, level, values }) => ({ code, level, node, values, sourceMap: sourceMap ?? node.sourceMap } as Problem) ) ?? [], ...entries(validationsByKind(node)) .map(([code, validation]) => validation(node, code)!) .filter(result => result !== null), From ca39afd51bba4b5e3636150132fd800fea78cf3e Mon Sep 17 00:00:00 2001 From: palumbon Date: Sun, 13 Aug 2023 19:07:27 +0200 Subject: [PATCH 3/3] Fixing parser test --- test/parser.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/parser.test.ts b/test/parser.test.ts index 0f030688..5e05ca7d 100644 --- a/test/parser.test.ts +++ b/test/parser.test.ts @@ -1445,7 +1445,7 @@ describe('Wollok parser', () => { }) it('should parse annotated subnodes within expression bodies', () => { - 'method m() = @A(x = 1) 5'.should.be.parsedBy(parser).into( + 'method m() = (@A(x = 1) 5)'.should.be.parsedBy(parser).into( new Method({ name: 'm', body: new Body({ sentences: [