From 3caad78015fab9055036af9cb243e9bc9dcebf41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20B=C3=A4dorf?= Date: Wed, 25 Nov 2020 03:45:26 +0100 Subject: [PATCH] Fixed inline comment on first body line --- src/Lexer.mjs | 7 +++++ src/Parser.mjs | 8 +++++- src/visitor.mjs | 12 +++++++-- test/examples/comments.mjs | 10 ++++--- test/spec.mjs | 54 +++++++++++++++----------------------- 5 files changed, 51 insertions(+), 40 deletions(-) diff --git a/src/Lexer.mjs b/src/Lexer.mjs index 37d6441..44c6f03 100644 --- a/src/Lexer.mjs +++ b/src/Lexer.mjs @@ -37,6 +37,11 @@ export const Value = createToken({ line_breaks: true, pop_mode: true, }); +export const CommentStartNewline = createToken({ + name: "CommentStartNewline", + pattern: '\n#', + push_mode: "comment_mode", +}); export const CommentStart = createToken({ name: "CommentStart", pattern: '#', @@ -56,6 +61,7 @@ export const WhiteSpace = createToken({ export const tokens = [ CommentStart, + CommentStartNewline, Value, Comment, SectionHeading, @@ -67,6 +73,7 @@ export default new Lexer({ defaultMode: "line_mode", modes: { line_mode: [ + CommentStartNewline, CommentStart, SectionHeading, Property, diff --git a/src/Parser.mjs b/src/Parser.mjs index ec56777..fb9b47e 100644 --- a/src/Parser.mjs +++ b/src/Parser.mjs @@ -4,6 +4,7 @@ import { tokens, Comment, CommentStart, + CommentStartNewline, Property, SectionHeading, Value, @@ -14,6 +15,11 @@ export default class UnitFileParser extends CstParser { super(tokens); const $ = this; + $.RULE("commentLine", () => { + $.CONSUME(CommentStartNewline); + $.CONSUME(Comment); + }); + $.RULE("comment", () => { $.CONSUME(CommentStart); $.CONSUME(Comment); @@ -41,7 +47,7 @@ export default class UnitFileParser extends CstParser { $.RULE("sectionStatement", () => { $.OR([ { ALT: () => $.SUBRULE($.propertyStatement) }, - { ALT: () => $.SUBRULE($.comment) }, + { ALT: () => $.SUBRULE($.commentLine) }, ]); }); diff --git a/src/visitor.mjs b/src/visitor.mjs index eabe696..0752c8f 100644 --- a/src/visitor.mjs +++ b/src/visitor.mjs @@ -7,13 +7,21 @@ export default (parser) => { this.validateVisitor(); }; - comment(ctx) { + _comment(ctx) { return { type: "comment", value: ctx.Comment.map(({ image }) => image.replace(/^\s/, '').replace(/\s$/, '')).join(''), }; } + comment(ctx) { + return this._comment(ctx); + } + + commentLine(ctx) { + return this._comment(ctx); + } + propertyStatement(ctx) { const name = ctx.Property[0].image; return { @@ -25,7 +33,7 @@ export default (parser) => { } sectionStatement(ctx) { - const content = ctx.propertyStatement?.[0] || ctx.comment?.[0]; + const content = ctx.propertyStatement?.[0] || ctx.commentLine?.[0]; return this.visit(content); } diff --git a/test/examples/comments.mjs b/test/examples/comments.mjs index dcf09c1..5299a96 100644 --- a/test/examples/comments.mjs +++ b/test/examples/comments.mjs @@ -1,7 +1,7 @@ export const content = `# Outside comment [Unit] # Heading comment -# Inline comment Description=Idle manager for Wayland # End of value comment +# Inline comment ExecStart=echo\ "some string" # End of value comment Alias=asdf#Comment Without spaces @@ -19,13 +19,13 @@ export const result = { title: 'Unit', titleComment: { type: 'comment', value: 'Heading comment' }, body: [ - { type: 'comment', value: 'Inline comment' }, { type: 'setting', name: 'Description', value: 'Idle manager for Wayland', comment: { type: 'comment', value: 'End of value comment' } }, + { type: 'comment', value: 'Inline comment' }, { type: 'setting', name: 'ExecStart', @@ -43,8 +43,10 @@ export const result = { { type: 'section', title: 'Install', - titleComment: { type: 'comment', value: 'Comment only in this body' }, - body: [] + titleComment: undefined, + body: [ + { type: 'comment', value: 'Comment only in this body' }, + ] } ] }; diff --git a/test/spec.mjs b/test/spec.mjs index 2029b9b..ecac941 100644 --- a/test/spec.mjs +++ b/test/spec.mjs @@ -6,7 +6,7 @@ import { visitorCreator, } from "../src/mod.mjs"; -const runTest = (input) => { +const runTest = (input, result) => { const parser = new Parser([], { outputCst: true }); const lexingresult = Lexer.tokenize(input); @@ -26,7 +26,25 @@ const runTest = (input) => { const Visitor = visitorCreator(parser); const visitor = new Visitor(); const ast = visitor.visit(cst); - return ast; + + const resultString = JSON.stringify(ast, null, 2); + const expectedString = JSON.stringify(result, null, 2); + if (resultString !== expectedString) { + console.dir(ast, { depth: Infinity }); + console.dir(lexingresult, { depth: Infinity }); + throw new Error(`Mismatching result on testcase: +${input} + +=======Result====== + +${resultString} + +=======Expected====== + +${expectedString} + +`); + } }; (async () => { @@ -45,35 +63,5 @@ const runTest = (input) => { ); const cases = await Promise.all(files.flat().map(file => import(file))); - - const results = cases.map( - ({ content, result }) => { - const ast = runTest(content); - const resultString = JSON.stringify(ast); - const expectedString = JSON.stringify(result); - if (resultString !== expectedString) { - console.dir(ast, { depth: Infinity }); - throw new Error(`Mismatching result on testcase: -${content} - -=======Result====== - -${resultString} - -=======Expected====== - -${expectedString} - -`); - } - - return null; - }, - ) - .filter(r => r !== null); - - if (results.length) { - results.forEach(err => console.error(err)); - throw new Error(`${results.length} failed tests`); - } + const results = cases.forEach(({ content, result }) => runTest(content, result)); })();