grammar.ne (4479B)
1 @preprocessor typescript 2 3 @{% 4 import { default as moo } from 'moo'; 5 const { compile, keywords, error } = moo; 6 const appendItem = function (a: number, b: number) { return function (d: any) { return d[a].concat(d[b]); } }; 7 const empty = function (d: any) { return []; }; 8 9 const lexer = compile({ 10 count: /(?:0|[1-9][0-9]*)\./, 11 ws: /[ \t]+/, 12 keyword: ["Error:", "Broken link in", "to"], 13 number: /[0-9]+/, 14 internalMetaMessage: ["> Successfully checked"], 15 metaMessages: ["pages (", "orphan),", "sections", "-> Site content:", "Checking site...", "Checking all internal links with anchors.", "internal link(s) with anchors.", "Checking", "external link(s).", "Skipping", "> Checked", "external link(s):", "error(s) found.", "Done in"], 16 misc: [":", "ms."], 17 path: /(?:(?:.\/|\/)[.a-zA-Z0-9_-]+)+/, 18 url_with_error: /\w*?:\/\/.*?(?=: )/, 19 string: /(?!\s*$).+/, 20 lexerError: error, 21 newline: {match: '\n', lineBreaks: true} 22 }); 23 %} 24 25 # Pass your lexer object using the @lexer option: 26 @lexer lexer 27 28 # Meta info is in stdOut 29 stdOutInput -> stdOutRow {% id %} 30 | stdOutInput %newline stdOutRow {% appendItem(0,2) %} 31 | input {% id %} 32 33 stdOutRow -> metaMessage {% empty %} 34 | successReport {% 35 function(data) { 36 return { 37 successReport: data[0], 38 }; 39 } 40 %} 41 | internalLinkMessage {% 42 function(data) { 43 return { 44 internal_links: data[0], 45 }; 46 } 47 %} 48 | externalLinkCheckingWithSkippedLinkMessage {% 49 function(data) { 50 return { 51 external_links_planed_checking: data[0], 52 }; 53 } 54 %} 55 | externalLinkCheckingMessage {% 56 function(data) { 57 return { 58 external_links_planed_checking: data[0], 59 }; 60 } 61 %} 62 | externalLinkCheckingLinkMessage {% 63 function(data) { 64 return { 65 external_links_checked: data[0], 66 }; 67 } 68 %} 69 | null {% empty %} 70 71 metaMessage -> %metaMessages 72 | %metaMessages %ws %number %misc 73 successReport -> %metaMessages %ws %number %ws %metaMessages %number %ws %metaMessages %ws %number %ws %metaMessages {% 74 function(data) { 75 return { 76 pages: data[2]["value"], 77 orphans: data[5]["value"], 78 sections: data[9]["value"], 79 }; 80 } 81 %} 82 internalLinkMessage -> %internalMetaMessage %ws %number %ws %metaMessages {% 83 function(data) { 84 return { 85 total: data[2]["value"], 86 }; 87 } 88 %} 89 externalLinkCheckingWithSkippedLinkMessage -> %metaMessages %ws %number %ws %metaMessages %ws %metaMessages %ws %number %ws %metaMessages {% 90 function(data) { 91 return { 92 total: data[2]["value"], 93 skipped: data[8]["value"] 94 }; 95 } 96 %} 97 externalLinkCheckingMessage -> "Checking" %ws %number %ws %metaMessages {% 98 function(data) { 99 return { 100 total: data[2]["value"], 101 }; 102 } 103 %} 104 105 externalLinkCheckingLinkMessage -> %metaMessages %ws %number %ws %metaMessages %ws %number %ws %metaMessages {% 106 function(data) { 107 return { 108 total: data[2]["value"], 109 errors: data[6]["value"] 110 }; 111 } 112 %} 113 114 # This is the whole input. We have newline delimited messages but we dont know the EOL 115 input -> row {% id %} 116 | input %newline row {% appendItem(0,2) %} 117 118 row -> broke_link_message 119 | error 120 | null {% empty %} 121 122 # An Error message is just a string but this gives us slightly nicer types 123 error_message -> %string {% function(data) { return data[0]["value"]; } %} 124 # A message prefixed with "Error:" 125 error -> %keyword %ws error_message {% function(data) { return {error_message: data[2]}; } %} 126 127 # A Helper to allow easy checking if there is indentation or not 128 prefix -> null | %ws 129 # The main information about failed links 130 broke_link_message -> prefix %count %ws %keyword %ws %path %ws %keyword %ws %url_with_error %misc %ws %string {% 131 function(data) { 132 return { 133 file: data[5]["value"], 134 url: data[9]["value"], 135 error_message: data[12]["value"] 136 }; 137 } 138 %}