| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- define ->
- # [fullLine, lineNumber, messageType, message]
- LINE_SPLITTER_REGEX = /^\[(\d+)].*>\s(INFO|WARN|ERROR)\s-\s(.*)$/
- MESSAGE_LEVELS = {
- "INFO": "info"
- "WARN": "warning"
- "ERROR": "error"
- }
- BibLogParser = (text, options) ->
- if typeof text != 'string'
- throw new Error("BibLogParser Error: text parameter must be a string")
- @text = text.replace(/(\r\n)|\r/g, '\n')
- @options = options || {}
- @lines = text.split('\n')
- return
- consume = (logText, regex, process) ->
- text = logText
- result = []
- re = regex
- iterationCount = 0
- while match = re.exec(text)
- iterationCount += 1
- if iterationCount >= 10000
- return result
- newEntry = process(match)
- result.push newEntry
- text = (
- (match.input.slice(0, match.index)) +
- (match.input.slice(match.index+match[0].length+1, match.input.length))
- )
- return [result, text]
- MULTILINE_WARNING_REGEX = /^Warning--(.+)\n--line (\d+) of file (.+)$/m
- SINGLELINE_WARNING_REGEX = /^Warning--(.+)$/m
- MULTILINE_ERROR_REGEX = /^(.*)---line (\d+) of file (.*)\n([^]+?)\nI'm skipping whatever remains of this entry$/m
- BAD_CROSS_REFERENCE_REGEX = /^(A bad cross reference---entry ".+?"\nrefers to entry.+?, which doesn't exist)$/m
- MULTILINE_COMMAND_ERROR_REGEX = /^(.*)\n?---line (\d+) of file (.*)\n([^]+?)\nI'm skipping whatever remains of this command$/m
- # each parser is a pair of [regex, processFunction], where processFunction
- # describes how to transform the regex mactch into a log entry object.
- warningParsers = [
- [
- MULTILINE_WARNING_REGEX,
- (match) ->
- [fullMatch, message, lineNumber, fileName] = match
- {
- file: fileName,
- level: "warning",
- message: message,
- line: lineNumber,
- raw: fullMatch
- }
- ],
- [
- SINGLELINE_WARNING_REGEX,
- (match) ->
- [fullMatch, message] = match
- {
- file: '',
- level: "warning",
- message: message,
- line: '',
- raw: fullMatch
- }
- ]
- ]
- errorParsers = [
- [
- MULTILINE_ERROR_REGEX,
- (match) ->
- [fullMatch, firstMessage, lineNumber, fileName, secondMessage] = match
- {
- file: fileName,
- level: "error",
- message: firstMessage + '\n' + secondMessage,
- line: lineNumber,
- raw: fullMatch
- }
- ],
- [
- BAD_CROSS_REFERENCE_REGEX,
- (match) ->
- [fullMatch, message] = match
- {
- file: '',
- level: "error",
- message: message,
- line: '',
- raw: fullMatch
- }
- ],
- [
- MULTILINE_COMMAND_ERROR_REGEX,
- (match) ->
- [fullMatch, firstMessage, lineNumber, fileName, secondMessage] = match
- {
- file: fileName,
- level: "error",
- message: firstMessage + '\n' + secondMessage,
- line: lineNumber,
- raw: fullMatch
- }
- ]
- ]
- (->
- @parseBibtex = () ->
- result = {
- all: [],
- errors: [],
- warnings: [],
- files: [], # not used
- typesetting: [] # not used
- }
- # reduce over the parsers, starting with the log text,
- [allWarnings, remainingText] = warningParsers.reduce(
- (accumulator, parser) ->
- [currentWarnings, text] = accumulator
- [regex, process] = parser
- [warnings, _remainingText] = consume text, regex, process
- return [currentWarnings.concat(warnings), _remainingText]
- , [[], @text]
- )
- [allErrors, remainingText] = errorParsers.reduce(
- (accumulator, parser) ->
- [currentErrors, text] = accumulator
- [regex, process] = parser
- [errors, _remainingText] = consume text, regex, process
- return [currentErrors.concat(errors), _remainingText]
- , [[], remainingText]
- )
- result.warnings = allWarnings
- result.errors = allErrors
- result.all = allWarnings.concat(allErrors)
- return result
- @parseBiber = () ->
- result = {
- all: [],
- errors: [],
- warnings: [],
- files: [], # not used
- typesetting: [] # not used
- }
- @lines.forEach (line) ->
- match = line.match(LINE_SPLITTER_REGEX)
- if match
- [fullLine, lineNumber, messageType, message] = match
- newEntry = {
- file: '',
- level: MESSAGE_LEVELS[messageType] || "INFO",
- message: message,
- line: '',
- raw: fullLine
- }
- # try extract file, line-number and the 'real' message from lines like:
- # BibTeX subsystem: /.../original.bib_123.utf8, line 8, syntax error: it's bad
- lineMatch = newEntry.message.match(/^BibTeX subsystem: \/.+\/(\w+\.\w+)_.+, line (\d+), (.+)$/)
- if lineMatch && lineMatch.length == 4
- [_, fileName, lineNumber, realMessage] = lineMatch
- newEntry.file = fileName
- newEntry.line = lineNumber
- newEntry.message = realMessage
- result.all.push newEntry
- switch newEntry.level
- when 'error' then result.errors.push newEntry
- when 'warning' then result.warnings.push newEntry
- return result
- @parse = () ->
- firstLine = @lines[0]
- if firstLine.match(/^.*INFO - This is Biber.*$/)
- @parseBiber()
- else if firstLine.match(/^This is BibTeX, Version.+$/)
- @parseBibtex()
- else
- throw new Error("BibLogParser Error: cannot determine whether text is biber or bibtex output")
- ).call(BibLogParser.prototype)
- BibLogParser.parse = (text, options) ->
- new BibLogParser(text, options).parse()
- return BibLogParser
|