From bc4c480399dba6a59526f83bfb9b58afb8c5354d Mon Sep 17 00:00:00 2001 From: Taulim Date: Tue, 8 Dec 2020 20:47:14 +0000 Subject: [PATCH 1/7] Implementation of a bold / italic parser. It is still not perfect but is working with simple text. --- src/server/MDParserArcsecond.ts | 120 +++++++++++++++++++++++++++----- 1 file changed, 101 insertions(+), 19 deletions(-) diff --git a/src/server/MDParserArcsecond.ts b/src/server/MDParserArcsecond.ts index 772e51d..cce1706 100644 --- a/src/server/MDParserArcsecond.ts +++ b/src/server/MDParserArcsecond.ts @@ -4,48 +4,130 @@ const { sequenceOf, str, many, + many1, digit, letter, char, anyChar, between, possibly, + whitespace, + recursiveParser, + anyCharExcept, } = Arcsecond; +/// debug +const debugOutput = (type: string) => (x: any) => ({ type: type, value: x }); + +/// Builders +const buildParseHeader = (pNumber: number) => { + const header = '#'.repeat(pNumber); + return sequenceOf([str(`${header} `).map(() => header), parseText]).map( + debugOutput(`h${pNumber}`) + ); +}; + +const buildBetween = (pParser: any) => between(pParser)(pParser); + +const buildParseBold = (pParser: any) => + choice([ + buildBetween(str('**'))(pParser), + buildBetween(str('__'))(pParser), + ]); + +const buildParseItalic = (pParser: any) => + choice([ + buildBetween(char('*'))(pParser), + buildBetween(char('_'))(pParser), + ]); + +// const buildParseText = (pException: any) => +// many1( +// choice([ +// parseEscape, +// letter, +// digit, +// whitespace, +// ...(pException ? [] : [anyCharExcept(pException)]), +// ]) +// ) +// .map((x: any) => x.join('')) +// .map(debugOutput('text2')); + +/// Helpers const parseEscape = sequenceOf([char('\\'), anyChar]).map((x: any) => x[1]); -const parseText = many(choice([parseEscape, anyChar])).map((x: any) => - x.join('') +const parseText = many1(choice([parseEscape, letter, digit, whitespace])) + .map((x: any) => x.join('')) + .map(debugOutput('text')); + +/// Parsers +const _ParseH1 = buildParseHeader(1); +const _ParseH2 = buildParseHeader(2); +const _ParseH3 = buildParseHeader(3); +const _ParseH4 = buildParseHeader(4); +const _ParseH5 = buildParseHeader(5); +const _ParseH6 = buildParseHeader(6); + +const _ParseItalic = buildParseItalic( + recursiveParser(() => + many1(choice([_ParseBold, parseText])).map(debugOutput('italic')) + ) +); + +const _ParseBold = buildParseBold( + recursiveParser(() => + many1(choice([_ParseItalic, parseText])).map(debugOutput('bold')) + ) ); -const _ParseHeader1 = sequenceOf([str('# ').map(() => '#'), parseText]); -const _ParseHeader2 = sequenceOf([str('## ').map(() => '##'), parseText]); -const _ParseHeader3 = sequenceOf([str('### ').map(() => '###'), parseText]); -const _ParseHeader4 = sequenceOf([str('#### ').map(() => '####'), parseText]); -const _ParseHeader5 = sequenceOf([str('##### ').map(() => '#####'), parseText]); -const _ParseHeader6 = sequenceOf([ - str('###### ').map(() => '######'), - parseText, -]); const _MDParser = choice([ - _ParseHeader6, - _ParseHeader5, - _ParseHeader4, - _ParseHeader3, - _ParseHeader2, - _ParseHeader1, + _ParseH6, + _ParseH5, + _ParseH4, + _ParseH3, + _ParseH2, + _ParseH1, + _ParseBold, + _ParseItalic, + parseText, ]); -export const MDParser = _MDParser; +const _FinalParser = many(_MDParser); /// Maybe using a promise is not the best options here, /// but it will allow to change the parse engine quite easy. export function RunParser(pText: string): Promise { return new Promise((resolve, reject) => { try { - const parsed = _MDParser.run(pText); + const parsed = _FinalParser.run(pText); resolve(parsed); } catch (ex) { reject(ex); } }); } + +const testParse = (pString: string) => { + const parsed = _FinalParser.run(pString); + const lineLen = 50; + console.log(`${pString} ${'-'.repeat(lineLen - (pString.length + 1))}`); + console.log(JSON.stringify(parsed, void 0, ' ')); + console.log(`${'-'.repeat(lineLen)}`); +}; + +testParse('# test'); +testParse('## test'); +testParse('### test'); +testParse('#### test'); +testParse('##### test'); +testParse('###### test'); +testParse('test test'); +testParse('*italic text*'); +testParse('_italic text_'); +testParse('**bold text**'); +testParse('__bold text__'); +testParse('*sometext \\* test*'); +testParse('*italic **bold** italic*'); +testParse('**bold *italic* bold**'); +// This parse is still a problem... +testParse('__bold @ text__'); From da665f5552fdd1ad0fbf7d2743307b22bf832e90 Mon Sep 17 00:00:00 2001 From: Taulim Date: Thu, 10 Dec 2020 19:28:24 +0000 Subject: [PATCH 2/7] Better parser for Emphasis and nested parsers --- src/server/MDParserArcsecond.ts | 99 +++++++++++++++++++++++---------- 1 file changed, 70 insertions(+), 29 deletions(-) diff --git a/src/server/MDParserArcsecond.ts b/src/server/MDParserArcsecond.ts index cce1706..4746383 100644 --- a/src/server/MDParserArcsecond.ts +++ b/src/server/MDParserArcsecond.ts @@ -14,15 +14,45 @@ const { whitespace, recursiveParser, anyCharExcept, + everyCharUntil, + skip, + pipeParsers, } = Arcsecond; /// debug const debugOutput = (type: string) => (x: any) => ({ type: type, value: x }); +/// Tokens +interface iTOKEN { + H1: any; + H2: any; + H3: any; + H4: any; + H5: any; + H6: any; + Bold1: any; + Bold2: any; + Italic1: any; + Italic2: any; +} + +const TOKEN: iTOKEN = { + H1: char('#'), + H2: str('##'), + H3: str('###'), + H4: str('####'), + H5: str('#####'), + H6: str('######'), + Bold1: str('**'), + Bold2: str('__'), + Italic1: char('*'), + Italic2: char('_'), +}; + /// Builders const buildParseHeader = (pNumber: number) => { - const header = '#'.repeat(pNumber); - return sequenceOf([str(`${header} `).map(() => header), parseText]).map( + const parser = TOKEN[`H${pNumber}` as keyof iTOKEN]; + return pipeParsers([parser, char(' '), parseText]).map( debugOutput(`h${pNumber}`) ); }; @@ -31,32 +61,34 @@ const buildBetween = (pParser: any) => between(pParser)(pParser); const buildParseBold = (pParser: any) => choice([ - buildBetween(str('**'))(pParser), - buildBetween(str('__'))(pParser), + buildBetween(TOKEN.Bold1)(pParser), + buildBetween(TOKEN.Bold2)(pParser), ]); const buildParseItalic = (pParser: any) => choice([ - buildBetween(char('*'))(pParser), - buildBetween(char('_'))(pParser), + buildBetween(TOKEN.Italic1)(pParser), + buildBetween(TOKEN.Italic2)(pParser), ]); -// const buildParseText = (pException: any) => -// many1( -// choice([ -// parseEscape, -// letter, -// digit, -// whitespace, -// ...(pException ? [] : [anyCharExcept(pException)]), -// ]) -// ) -// .map((x: any) => x.join('')) -// .map(debugOutput('text2')); - /// Helpers +const parseTokens = choice([ + TOKEN.H6, + TOKEN.H5, + TOKEN.H4, + TOKEN.H3, + TOKEN.H2, + TOKEN.H1, + TOKEN.Bold1, + TOKEN.Bold2, + TOKEN.Italic1, + TOKEN.Italic2, +]); const parseEscape = sequenceOf([char('\\'), anyChar]).map((x: any) => x[1]); -const parseText = many1(choice([parseEscape, letter, digit, whitespace])) +const parseRawText = many1(anyChar) + .map((x: any) => x.join('')) + .map(debugOutput('rawText')); +const parseText = many1(choice([parseEscape, anyCharExcept(parseTokens)])) .map((x: any) => x.join('')) .map(debugOutput('text')); @@ -90,9 +122,10 @@ const _MDParser = choice([ _ParseBold, _ParseItalic, parseText, + parseRawText, ]); -const _FinalParser = many(_MDParser); +const _FinalParser = many1(_MDParser); /// Maybe using a promise is not the best options here, /// but it will allow to change the parse engine quite easy. @@ -115,13 +148,13 @@ const testParse = (pString: string) => { console.log(`${'-'.repeat(lineLen)}`); }; -testParse('# test'); -testParse('## test'); -testParse('### test'); -testParse('#### test'); -testParse('##### test'); -testParse('###### test'); -testParse('test test'); +testParse('# Header 1'); +testParse('## Header 2'); +testParse('### Header 3'); +testParse('#### Header 4'); +testParse('##### Header 5'); +testParse('###### Header 6'); +testParse('Simple text'); testParse('*italic text*'); testParse('_italic text_'); testParse('**bold text**'); @@ -129,5 +162,13 @@ testParse('__bold text__'); testParse('*sometext \\* test*'); testParse('*italic **bold** italic*'); testParse('**bold *italic* bold**'); -// This parse is still a problem... testParse('__bold @ text__'); +testParse('_italic @ text_'); +testParse('__bold ~ text__'); +testParse('_italic £ text_'); +testParse('_italic \\* text_'); +testParse('_italic \\_ text_'); +testParse('#test'); +testParse('test #test'); +// This parse is still a problem... +testParse('_test #test_'); From 0e42c38d116eb5a0a0418c7b4bedcd90df7e8735 Mon Sep 17 00:00:00 2001 From: Taulim Date: Tue, 15 Dec 2020 21:16:54 +0000 Subject: [PATCH 3/7] Better Emphasis parsers with a simpler structure. --- .vscode/settings.json | 26 +++--- src/server/MDParserArcsecond.ts | 137 ++++++++++++++++++-------------- 2 files changed, 90 insertions(+), 73 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 105be8c..3ca6f1b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,14 +1,14 @@ { - "deno.enable": true, - "editor.defaultFormatter": "esbenp.prettier-vscode", - "prettier.tabWidth": 4, - "prettier.useTabs": true, - "editor.formatOnPaste": false, - "editor.formatOnType": false, - "editor.formatOnSave": true, - "prettier.singleQuote": true, - "deno.import_intellisense_origins": { - "https://deno.land": true - }, - //"editor.formatOnSaveMode": "modifications", -} \ No newline at end of file + "deno.enable": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "prettier.tabWidth": 4, + "prettier.useTabs": true, + "prettier.singleQuote": true, + "editor.formatOnPaste": false, + "editor.formatOnType": false, + "editor.formatOnSave": true, + "deno.import_intellisense_origins": { + "https://deno.land": true + } + //"editor.formatOnSaveMode": "modifications", +} diff --git a/src/server/MDParserArcsecond.ts b/src/server/MDParserArcsecond.ts index 4746383..1449e93 100644 --- a/src/server/MDParserArcsecond.ts +++ b/src/server/MDParserArcsecond.ts @@ -17,12 +17,15 @@ const { everyCharUntil, skip, pipeParsers, + tapParser, + lookAhead, + regex, } = Arcsecond; /// debug -const debugOutput = (type: string) => (x: any) => ({ type: type, value: x }); +const DebugOutput = (type: string) => (x: any) => ({ type: type, value: x }); -/// Tokens +/// Tokens (Only needed because we need the `as keyof iTOKEN` for type-script) interface iTOKEN { H1: any; H2: any; @@ -50,29 +53,20 @@ const TOKEN: iTOKEN = { }; /// Builders -const buildParseHeader = (pNumber: number) => { - const parser = TOKEN[`H${pNumber}` as keyof iTOKEN]; - return pipeParsers([parser, char(' '), parseText]).map( - debugOutput(`h${pNumber}`) +const _Header = (n: number) => { + const parser = TOKEN[`H${n}` as keyof iTOKEN]; + return pipeParsers([parser, char(' '), ParseText]).map( + DebugOutput(`h${n}`) ); }; -const buildBetween = (pParser: any) => between(pParser)(pParser); +const _CharEx = (ex: any) => anyCharExcept(ex).map(DebugOutput('rawCharEx')); -const buildParseBold = (pParser: any) => - choice([ - buildBetween(TOKEN.Bold1)(pParser), - buildBetween(TOKEN.Bold2)(pParser), - ]); - -const buildParseItalic = (pParser: any) => - choice([ - buildBetween(TOKEN.Italic1)(pParser), - buildBetween(TOKEN.Italic2)(pParser), - ]); +const _Between = (left: any) => (right: any) => (parse: any) => + sequenceOf([left, parse, right]).map((x: any) => x[1]); /// Helpers -const parseTokens = choice([ +const ParseTokens = choice([ TOKEN.H6, TOKEN.H5, TOKEN.H4, @@ -84,55 +78,70 @@ const parseTokens = choice([ TOKEN.Italic1, TOKEN.Italic2, ]); -const parseEscape = sequenceOf([char('\\'), anyChar]).map((x: any) => x[1]); -const parseRawText = many1(anyChar) - .map((x: any) => x.join('')) - .map(debugOutput('rawText')); -const parseText = many1(choice([parseEscape, anyCharExcept(parseTokens)])) +const ParseEscape = sequenceOf([char('\\'), anyChar]).map((x: any) => x[1]); +const ParseText = many1(choice([ParseEscape, anyCharExcept(ParseTokens)])) .map((x: any) => x.join('')) - .map(debugOutput('text')); + .map(DebugOutput('text')); +const ParseRawChar = anyChar.map(DebugOutput('rawChar')); /// Parsers -const _ParseH1 = buildParseHeader(1); -const _ParseH2 = buildParseHeader(2); -const _ParseH3 = buildParseHeader(3); -const _ParseH4 = buildParseHeader(4); -const _ParseH5 = buildParseHeader(5); -const _ParseH6 = buildParseHeader(6); - -const _ParseItalic = buildParseItalic( - recursiveParser(() => - many1(choice([_ParseBold, parseText])).map(debugOutput('italic')) - ) -); - -const _ParseBold = buildParseBold( - recursiveParser(() => - many1(choice([_ParseItalic, parseText])).map(debugOutput('bold')) - ) -); - -const _MDParser = choice([ - _ParseH6, - _ParseH5, - _ParseH4, - _ParseH3, - _ParseH2, - _ParseH1, - _ParseBold, - _ParseItalic, - parseText, - parseRawText, +const ParseH1 = _Header(1); +const ParseH2 = _Header(2); +const ParseH3 = _Header(3); +const ParseH4 = _Header(4); +const ParseH5 = _Header(5); +const ParseH6 = _Header(6); + +const ParseItalic = recursiveParser(() => { + const _Row = (edge: any) => { + const notSpace = lookAhead(regex(/^[^\s]/)); + const left = sequenceOf([edge, notSpace]); + const right = sequenceOf([notSpace, edge]); + return _Between(left)(right)( + many1(choice([ParseBold, ParseText, _CharEx(edge)])).map( + DebugOutput('italic') + ) + ); + }; + return choice([_Row(TOKEN.Italic1), _Row(TOKEN.Italic2)]); +}); + +const ParseBold = recursiveParser(() => { + const _Row = (edge: any) => { + const notSpace = lookAhead(regex(/^[^\s]/)); + const left = sequenceOf([edge, notSpace]); + const right = sequenceOf([notSpace, edge]); + return _Between(left)(right)( + many1(choice([ParseItalic, ParseText, _CharEx(edge)])).map( + DebugOutput('bold') + ) + ); + }; + return choice([_Row(TOKEN.Bold1), _Row(TOKEN.Bold2)]); +}); + +/// Markdown Parser +const MDParser = choice([ + ParseH6, + ParseH5, + ParseH4, + ParseH3, + ParseH2, + ParseH1, + ParseBold, + ParseItalic, + ParseText, + ParseRawChar, ]); -const _FinalParser = many1(_MDParser); +const FinalParser = many1(MDParser); /// Maybe using a promise is not the best options here, /// but it will allow to change the parse engine quite easy. export function RunParser(pText: string): Promise { return new Promise((resolve, reject) => { try { - const parsed = _FinalParser.run(pText); + const parsed = FinalParser.run(pText); resolve(parsed); } catch (ex) { reject(ex); @@ -141,10 +150,17 @@ export function RunParser(pText: string): Promise { } const testParse = (pString: string) => { - const parsed = _FinalParser.run(pString); + const parsed = FinalParser.run(pString); + const { isError, result } = parsed; const lineLen = 50; console.log(`${pString} ${'-'.repeat(lineLen - (pString.length + 1))}`); - console.log(JSON.stringify(parsed, void 0, ' ')); + if (!isError) { + console.log(result); + // console.log(JSON.stringify(parsed, void 0, ' ')); + } else { + console.log('ERROR: '); + console.log(JSON.stringify(parsed, void 0, ' ')); + } console.log(`${'-'.repeat(lineLen)}`); }; @@ -170,5 +186,6 @@ testParse('_italic \\* text_'); testParse('_italic \\_ text_'); testParse('#test'); testParse('test #test'); -// This parse is still a problem... testParse('_test #test_'); +// Extreme case (still a problem?! Should be expected?!) +testParse('text _iii _ text'); From 7c8bd6e037e1d48c143a7b5567e600b1be350f1d Mon Sep 17 00:00:00 2001 From: Taulim Date: Thu, 17 Dec 2020 20:53:26 +0000 Subject: [PATCH 4/7] Parser for code done and parser for images in initial phase. --- src/server/MDParserArcsecond.ts | 98 ++++++++++++++++++++++++--------- 1 file changed, 71 insertions(+), 27 deletions(-) diff --git a/src/server/MDParserArcsecond.ts b/src/server/MDParserArcsecond.ts index 1449e93..33ce1b1 100644 --- a/src/server/MDParserArcsecond.ts +++ b/src/server/MDParserArcsecond.ts @@ -37,6 +37,8 @@ interface iTOKEN { Bold2: any; Italic1: any; Italic2: any; + Code1: any; + Code2: any; } const TOKEN: iTOKEN = { @@ -50,6 +52,8 @@ const TOKEN: iTOKEN = { Bold2: str('__'), Italic1: char('*'), Italic2: char('_'), + Code1: str('``'), + Code2: char('`'), }; /// Builders @@ -61,6 +65,10 @@ const _Header = (n: number) => { }; const _CharEx = (ex: any) => anyCharExcept(ex).map(DebugOutput('rawCharEx')); +const _StrEx = (ex: any) => + many1(anyCharExcept(ex)) + .map((x: any) => x.join('')) + .map(DebugOutput('rawStrEx')); const _Between = (left: any) => (right: any) => (parse: any) => sequenceOf([left, parse, right]).map((x: any) => x[1]); @@ -77,6 +85,8 @@ const ParseTokens = choice([ TOKEN.Bold2, TOKEN.Italic1, TOKEN.Italic2, + TOKEN.Code1, + TOKEN.Code2, ]); const ParseEscape = sequenceOf([char('\\'), anyChar]).map((x: any) => x[1]); const ParseText = many1(choice([ParseEscape, anyCharExcept(ParseTokens)])) @@ -120,6 +130,28 @@ const ParseBold = recursiveParser(() => { return choice([_Row(TOKEN.Bold1), _Row(TOKEN.Bold2)]); }); +const ParseCode = recursiveParser(() => { + const _Row = (edge: any) => { + const left = edge; + const right = edge; + return _Between(left)(right)( + many1(choice([ParseText, _CharEx(edge)])).map(DebugOutput('code')) + ); + }; + return choice([_Row(TOKEN.Code1), _Row(TOKEN.Code2)]); +}); + +const ParseImage = recursiveParser(() => { + const imgAlt = pipeParsers([ + char('!'), + _Between(char('['))(char(']'))(_StrEx(char(']'))), + ]).map(DebugOutput('imgAlt')); + const imgURI = _Between(char('('))(char(')'))(_StrEx(char(')'))).map( + DebugOutput('imgURI') + ); + return sequenceOf([imgAlt, imgURI]).map(DebugOutput('img')); +}); + /// Markdown Parser const MDParser = choice([ ParseH6, @@ -130,6 +162,8 @@ const MDParser = choice([ ParseH1, ParseBold, ParseItalic, + ParseCode, + ParseImage, ParseText, ParseRawChar, ]); @@ -149,8 +183,8 @@ export function RunParser(pText: string): Promise { }); } -const testParse = (pString: string) => { - const parsed = FinalParser.run(pString); +const testParse = (pString: string, pParser?: any) => { + const parsed = (pParser ?? FinalParser).run(pString); const { isError, result } = parsed; const lineLen = 50; console.log(`${pString} ${'-'.repeat(lineLen - (pString.length + 1))}`); @@ -164,28 +198,38 @@ const testParse = (pString: string) => { console.log(`${'-'.repeat(lineLen)}`); }; -testParse('# Header 1'); -testParse('## Header 2'); -testParse('### Header 3'); -testParse('#### Header 4'); -testParse('##### Header 5'); -testParse('###### Header 6'); -testParse('Simple text'); -testParse('*italic text*'); -testParse('_italic text_'); -testParse('**bold text**'); -testParse('__bold text__'); -testParse('*sometext \\* test*'); -testParse('*italic **bold** italic*'); -testParse('**bold *italic* bold**'); -testParse('__bold @ text__'); -testParse('_italic @ text_'); -testParse('__bold ~ text__'); -testParse('_italic £ text_'); -testParse('_italic \\* text_'); -testParse('_italic \\_ text_'); -testParse('#test'); -testParse('test #test'); -testParse('_test #test_'); -// Extreme case (still a problem?! Should be expected?!) -testParse('text _iii _ text'); +// testParse('# Header 1'); +// testParse('## Header 2'); +// testParse('### Header 3'); +// testParse('#### Header 4'); +// testParse('##### Header 5'); +// testParse('###### Header 6'); +// testParse('Simple text'); +// testParse('*italic text*'); +// testParse('_italic text_'); +// testParse('**bold text**'); +// testParse('__bold text__'); +// testParse('*sometext \\* test*'); +// testParse('*italic **bold** italic*'); +// testParse('**bold *italic* bold**'); +// testParse('__bold @ text__'); +// testParse('_italic @ text_'); +// testParse('__bold ~ text__'); +// testParse('_italic £ text_'); +// testParse('_italic \\* text_'); +// testParse('_italic \\_ text_'); +// testParse('#test'); +// testParse('test #test'); +// testParse('_test #test_'); +// testParse('`code here`'); +// testParse('``code here``'); +// testParse('``code ` here``'); +// testParse('text *italic* **bold** ``code ` here`` __bold__'); +testParse('![Alt text](/path/to/img.jpg)', ParseImage); +testParse('![Alt text](/path/to/img.jpg "Optional title")', ParseImage); +testParse('![Alt text][id]', ParseImage); + +// testParse('[an example](http://example.com/ "Title")', ParseLink); +// testParse('[This link](http://example.net/)', ParseLink); +// testParse('[an example][id]', ParseLink); +// testParse('[id]: url/to/image "Optional title attribute"', ParseReferenceLink); From d33265b3ae0d3c7d4c6658b3d4be1f4bfb3e98be Mon Sep 17 00:00:00 2001 From: Taulim Date: Fri, 18 Dec 2020 23:13:21 +0000 Subject: [PATCH 5/7] Parser for image and link are working. --- .devcontainer/devcontainer.json | 1 - src/server/MDParserArcsecond.ts | 130 +++++++++++++++++++++++++++----- 2 files changed, 110 insertions(+), 21 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index b89614f..0023da0 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -18,7 +18,6 @@ "extensions": [ "denoland.vscode-deno", "esbenp.prettier-vscode", - "tombonnike.vscode-status-bar-format-toggle", "alefragnani.bookmarks", "xabikos.javascriptsnippets", "coenraads.bracket-pair-colorizer", diff --git a/src/server/MDParserArcsecond.ts b/src/server/MDParserArcsecond.ts index 33ce1b1..ff5d5c9 100644 --- a/src/server/MDParserArcsecond.ts +++ b/src/server/MDParserArcsecond.ts @@ -64,9 +64,10 @@ const _Header = (n: number) => { ); }; -const _CharEx = (ex: any) => anyCharExcept(ex).map(DebugOutput('rawCharEx')); +const _CharEx = (ex: any) => + choice([ParseEscape, anyCharExcept(ex)]).map(DebugOutput('rawCharEx')); const _StrEx = (ex: any) => - many1(anyCharExcept(ex)) + many1(choice([ParseEscape, anyCharExcept(ex)])) .map((x: any) => x.join('')) .map(DebugOutput('rawStrEx')); @@ -142,16 +143,70 @@ const ParseCode = recursiveParser(() => { }); const ParseImage = recursiveParser(() => { - const imgAlt = pipeParsers([ - char('!'), - _Between(char('['))(char(']'))(_StrEx(char(']'))), - ]).map(DebugOutput('imgAlt')); - const imgURI = _Between(char('('))(char(')'))(_StrEx(char(')'))).map( - DebugOutput('imgURI') + const imgAlt = ((left: any) => (right: any) => { + return pipeParsers([ + char('!'), + _Between(left)(right)(_StrEx(right)), + ]).map(DebugOutput('imgAlt')); + })(char('['))(char(']')); + + const imgURI = ((left: any) => (right: any) => { + const rightURI = choice([ + sequenceOf([char(' '), lookAhead(char('"'))]), + right, + ]); + return sequenceOf([ + _Between(left)(rightURI)(_StrEx(rightURI)).map( + DebugOutput('imgURI') + ), + possibly(_Between(char('"'))(char('"'))(_StrEx(char('"')))).map( + DebugOutput('imgTitle') + ), + ]); + })(char('('))(char(')')); + + const imgID = ((left: any) => (right: any) => { + return _Between(left)(right)(_StrEx(right)).map(DebugOutput('imgID')); + })(choice([str(' ['), char('[')]))(char(']')); + + return sequenceOf([imgAlt, choice([imgURI, imgID])]).map( + DebugOutput('img') ); - return sequenceOf([imgAlt, imgURI]).map(DebugOutput('img')); }); +const ParseLink = recursiveParser(() => { + const linkText = ((left: any) => (right: any) => { + return _Between(left)(right)(_StrEx(right)).map( + DebugOutput('linkText') + ); + })(char('['))(char(']')); + + const linkURI = ((left: any) => (right: any) => { + const rightURI = choice([ + sequenceOf([char(' '), lookAhead(char('"'))]), + right, + ]); + return sequenceOf([ + _Between(left)(rightURI)(_StrEx(rightURI)).map( + DebugOutput('linkURI') + ), + possibly(_Between(char('"'))(char('"'))(_StrEx(char('"')))).map( + DebugOutput('linkTitle') + ), + ]); + })(char('('))(char(')')); + + const linkID = ((left: any) => (right: any) => { + return _Between(left)(right)(_StrEx(right)).map(DebugOutput('imgID')); + })(choice([str(' ['), char('[')]))(char(']')); + + return sequenceOf([linkText, choice([linkURI, linkID])]).map( + DebugOutput('link') + ); +}); + +const ParseReference = str('TO-DO'); + /// Markdown Parser const MDParser = choice([ ParseH6, @@ -164,6 +219,8 @@ const MDParser = choice([ ParseItalic, ParseCode, ParseImage, + ParseLink, + ParseReference, ParseText, ParseRawChar, ]); @@ -183,21 +240,34 @@ export function RunParser(pText: string): Promise { }); } +const clearConsole = () => { + for (let index = 0; index < 50; index++) { + console.log(''); + } +}; + +const TEXT_LOG = true; const testParse = (pString: string, pParser?: any) => { + const dumpObj = (obj: any) => + console.log(JSON.stringify(obj, void 0, ' ')); const parsed = (pParser ?? FinalParser).run(pString); const { isError, result } = parsed; - const lineLen = 50; + const lineLen = 80; console.log(`${pString} ${'-'.repeat(lineLen - (pString.length + 1))}`); if (!isError) { - console.log(result); - // console.log(JSON.stringify(parsed, void 0, ' ')); + if (!TEXT_LOG) { + console.log(result); + } else { + dumpObj(parsed); + } } else { console.log('ERROR: '); - console.log(JSON.stringify(parsed, void 0, ' ')); + dumpObj(parsed); } console.log(`${'-'.repeat(lineLen)}`); }; +clearConsole(); // testParse('# Header 1'); // testParse('## Header 2'); // testParse('### Header 3'); @@ -225,11 +295,31 @@ const testParse = (pString: string, pParser?: any) => { // testParse('``code here``'); // testParse('``code ` here``'); // testParse('text *italic* **bold** ``code ` here`` __bold__'); -testParse('![Alt text](/path/to/img.jpg)', ParseImage); -testParse('![Alt text](/path/to/img.jpg "Optional title")', ParseImage); -testParse('![Alt text][id]', ParseImage); - -// testParse('[an example](http://example.com/ "Title")', ParseLink); -// testParse('[This link](http://example.net/)', ParseLink); +// testParse('![Alt text][id]'); +// testParse('![Alt text] [id]'); +// testParse('![Alt text](/path/to/img.jpg)'); +// testParse('![Alt text](/path/to/img.jpg "Optional title")'); +// testParse('![Alt \\[ \\] text][id]'); +// testParse('![Alt \\[ \\] text] [id]'); +// testParse('![Alt \\[ \\] text](/path/to/img.jpg)'); +// testParse('![Alt \\[ \\] text](/path/to/img.jpg "Optional title")'); +// testParse('![Alt text][i \\[ \\] d]'); +// testParse('![Alt text] [i \\[ \\] d]'); +// testParse('![Alt text](/path/\\[\\ \\]/img.jpg)'); +// testParse('![Alt text](/path/\\[\\ \\]/img.jpg "Optional title")'); +// testParse('![Alt text](/path/to/img.jpg "Optional \\[ \\] title")'); // testParse('[an example][id]', ParseLink); -// testParse('[id]: url/to/image "Optional title attribute"', ParseReferenceLink); +// testParse('[This link](http://example.net/)', ParseLink); +// testParse('[an example](http://example.com/ "Title")', ParseLink); + +testParse('[id]: url/to/image', ParseReference); +testParse('[id]: url/to/image "Optional title attribute"', ParseReference); +// testParse('[id]: url/to/image', ParseReference); +// testParse('[id]: url/to/image', ParseReference); +// testParse('[id]: url/to/image', ParseReference); +// testParse('[id]: url/to/image', ParseReference); +// testParse('[id]: url/to/image "Optional title attribute"', ParseReference); +// testParse('[id]: url/to/image "Optional title attribute"', ParseReference); +// testParse('[id]: url/to/image "Optional title attribute"', ParseReference); +// testParse('[id]: url/to/image "Optional title attribute"', ParseReference); +// testParse('[id]: url/to/image "Optional title attribute"', ParseReference); From 09e0394f70df837b5abe126112c8d63b1c84f91c Mon Sep 17 00:00:00 2001 From: Taulim Date: Mon, 17 May 2021 17:22:24 +0100 Subject: [PATCH 6/7] Update denon --- container/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/container/Dockerfile b/container/Dockerfile index 2fbfead..93e0c0c 100644 --- a/container/Dockerfile +++ b/container/Dockerfile @@ -20,7 +20,7 @@ RUN usermod -u 1000 deno \ USER deno # Install denon -RUN deno install -qAf --unstable https://deno.land/x/denon@2.4.4/denon.ts +RUN deno install -qAf --unstable https://deno.land/x/denon/denon.ts WORKDIR /workspace ENTRYPOINT [] From a60aeab7720469de41022ad19ded1642aef2d238 Mon Sep 17 00:00:00 2001 From: Taulim Date: Mon, 17 May 2021 17:52:03 +0100 Subject: [PATCH 7/7] Set correct permission on container workspace directory --- container/Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/container/Dockerfile b/container/Dockerfile index 93e0c0c..89cfedf 100644 --- a/container/Dockerfile +++ b/container/Dockerfile @@ -6,7 +6,9 @@ RUN usermod -u 1000 deno \ && groupmod -g 1000 deno \ && chown deno:deno /deno-dir/ \ && mkdir -p /home/deno \ + && mkdir -p /workspace \ && chown deno:deno /home/deno \ + && chown deno:deno /workspace \ && chmod 751 /home/deno \ && rm -rf /usr/local/bin/docker-entrypoint.sh \ # Install git