tex_log_parser 1.0.0.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +6 -0
- data/bin/texlogparser +6 -0
- data/lib/tex_log_parser/log_message.rb +32 -0
- data/lib/tex_log_parser/log_parser.rb +74 -0
- data/lib/tex_log_parser/log_pattern.rb +65 -0
- data/lib/tex_log_parser/patterns/bad_hbox_warning.rb +8 -0
- data/lib/tex_log_parser/patterns/file_line_error.rb +27 -0
- data/lib/tex_log_parser/patterns/fontspec_error.rb +8 -0
- data/lib/tex_log_parser/patterns/prefixed_multi_line_pattern.rb +51 -0
- data/lib/tex_log_parser/patterns/runaway_parameter_error.rb +8 -0
- data/lib/tex_log_parser.rb +41 -0
- metadata +99 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4278834a923e4825ac4e56bcd07e6b7eeb681643
|
4
|
+
data.tar.gz: 38810767f39844e7ec8eac3b78a445f8ebdbf68c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 701b97b4ab74385a29286fbb30055296ee417881f636f00638d692ece7b74a4fa47b3e68a567c22c403279709240abcd4fad45df80f82d4c6be95fa4a643ab2d
|
7
|
+
data.tar.gz: 0aaa4a74adb27548e8e2487bbf2b3a8782481eb4cce192098b549e250ab39a4bb142ead0796eac745f5f940a3d90ee030274773d853ee7159cb940aad1a4f792
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2018 Raphael R.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
# TeXLogParser
|
2
|
+
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/texlogparser.svg)](https://rubygems.org/gems/texlogparser)
|
4
|
+
[![Travis build status](https://api.travis-ci.org/reitzig/texlogparser.svg?branch=master)](https://travis-ci.org/reitzig/texlogparser)
|
5
|
+
|
6
|
+
TODO write
|
data/bin/texlogparser
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# @attr [String] message
|
2
|
+
# @attr [String,nil] source_file
|
3
|
+
# @attr [Hash<Symbol, Int>,nil] source_lines
|
4
|
+
# @attr [Hash<Symbol, Int>,nil] log_lines
|
5
|
+
# @attr [true,false] preformatted
|
6
|
+
# @attr [:error,:warning,:info,:debug] level
|
7
|
+
class LogMessage
|
8
|
+
def initialize(message:, source_file: nil, source_lines: nil, log_lines: nil, preformatted: false, level: :info)
|
9
|
+
@message = message
|
10
|
+
@source_file = source_file
|
11
|
+
@source_lines = source_lines
|
12
|
+
@log_lines = log_lines
|
13
|
+
@preformatted = preformatted
|
14
|
+
@level = level
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_accessor :message, :source_file, :source_lines, :log_lines,
|
18
|
+
:preformatted, :level
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
lines = if @source_lines.nil?
|
22
|
+
""
|
23
|
+
else
|
24
|
+
# @type [Hash<Symbol, Int>] @source_lines
|
25
|
+
@source_lines.values.join("-")
|
26
|
+
end
|
27
|
+
<<~MSG
|
28
|
+
#{@source_file}:#{lines} #{@level.to_s.capitalize}
|
29
|
+
#{@message}
|
30
|
+
MSG
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# TODO: document
|
2
|
+
module LogParser
|
3
|
+
def initialize
|
4
|
+
@files = []
|
5
|
+
end
|
6
|
+
|
7
|
+
# @return [Array<LogPattern>]
|
8
|
+
def patterns
|
9
|
+
raise NotImplementedError
|
10
|
+
end
|
11
|
+
|
12
|
+
# @param [String] line
|
13
|
+
# @return [Array<String,:pop>] A list of new scopes this line enters (strings)
|
14
|
+
# and leaves (`:pop`).
|
15
|
+
# Read stack operations from left to right.
|
16
|
+
def scope_changes(line)
|
17
|
+
raise NotImplementedError
|
18
|
+
end
|
19
|
+
|
20
|
+
# TODO: document
|
21
|
+
# @param [Array<String>] log_lines
|
22
|
+
# @return [Array<LogMessage>]
|
23
|
+
def parse(log_lines)
|
24
|
+
messages = []
|
25
|
+
|
26
|
+
log_line_number = 1
|
27
|
+
until log_lines.empty?
|
28
|
+
line = log_lines.first
|
29
|
+
|
30
|
+
if line.strip.empty?
|
31
|
+
consumed_lines = 1
|
32
|
+
else
|
33
|
+
# Use the first pattern that matches. Let's hope that's a good heuristic.
|
34
|
+
# If not, we'll have to let all competitors consume and see who wins --
|
35
|
+
# which we'd decide how?
|
36
|
+
matching_pattern = patterns.detect { |p| p.begins_at?(line) }
|
37
|
+
|
38
|
+
if matching_pattern.nil?
|
39
|
+
# In the hope that scope changes happen not on the same
|
40
|
+
# line as messages. Gulp.
|
41
|
+
scope_changes(log_lines.first).each { |op|
|
42
|
+
if op == :pop
|
43
|
+
left = @files.pop
|
44
|
+
#puts "Finished file #{left.nil? ? "nil" : left}" # TODO: debug mode
|
45
|
+
left
|
46
|
+
else
|
47
|
+
#puts "Entered file #{op}" # TODO: debug mode
|
48
|
+
@files.push(op)
|
49
|
+
end
|
50
|
+
}
|
51
|
+
|
52
|
+
# Try again with the next line
|
53
|
+
consumed_lines = 1
|
54
|
+
else
|
55
|
+
# Apply the pattern, i.e. read the next message!
|
56
|
+
# @type [LogMessage] message
|
57
|
+
message, consumed_lines = matching_pattern.read(log_lines)
|
58
|
+
message.log_lines = { from: log_line_number,
|
59
|
+
to: log_line_number + consumed_lines - 1 }
|
60
|
+
message.source_file ||= @files.last
|
61
|
+
|
62
|
+
messages.push(message)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Forward the window
|
67
|
+
log_lines.slice!(0, consumed_lines)
|
68
|
+
log_line_number += consumed_lines
|
69
|
+
end
|
70
|
+
|
71
|
+
#puts "Filestack: #{@files}" # TODO: debug mode
|
72
|
+
messages
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require "#{File.expand_path(__dir__)}/log_message.rb"
|
2
|
+
|
3
|
+
# TODO: Document
|
4
|
+
module LogPattern
|
5
|
+
# TODO: Document
|
6
|
+
# @param [String] line
|
7
|
+
# @return [true,false]
|
8
|
+
def begins_at?(line)
|
9
|
+
raise NotImplementedError
|
10
|
+
end
|
11
|
+
|
12
|
+
# TODO: Document
|
13
|
+
# @param [Array<String>] lines
|
14
|
+
# @return [Array<(LogMessage, Int)>]
|
15
|
+
def read(lines)
|
16
|
+
raise NotImplementedError
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# TODO: document
|
21
|
+
# @attr [Regexp] start
|
22
|
+
# @attr [MatchData] start_match
|
23
|
+
# @attr [Regexp] ending
|
24
|
+
module RegExpPattern
|
25
|
+
include LogPattern
|
26
|
+
|
27
|
+
# @param [Regexp] start
|
28
|
+
# @param [Hash] ending
|
29
|
+
# @option ending [Regexp] :pattern
|
30
|
+
# @option ending [:match,:mismatch] :until
|
31
|
+
# @option ending [true,false] :inclusive
|
32
|
+
def initialize(start, ending = { pattern: ->(_) { /^\s+$/ },
|
33
|
+
until: :match,
|
34
|
+
inclusive: false })
|
35
|
+
@start = start
|
36
|
+
@ending = ending
|
37
|
+
end
|
38
|
+
|
39
|
+
# TODO: document
|
40
|
+
def begins_at?(line)
|
41
|
+
match = @start.match(line)
|
42
|
+
@start_match = match unless match.nil?
|
43
|
+
!match.nil?
|
44
|
+
end
|
45
|
+
|
46
|
+
# TODO: document
|
47
|
+
def ends_at?(line)
|
48
|
+
match = !(@ending[:pattern][@start_match] =~ line).nil?
|
49
|
+
match == (@ending[:until] == :match)
|
50
|
+
end
|
51
|
+
|
52
|
+
# TODO make failable (e.g. EOF)
|
53
|
+
# @param [Array<String>] lines
|
54
|
+
# @return [Array<(LogMessage, Int)>]
|
55
|
+
def read(lines) # TODO: How useful is this? Not very, I'm afraid... there should be functions for source file, line, and level?
|
56
|
+
raise NotImplementedError if @ending.nil?
|
57
|
+
|
58
|
+
ending = lines[1, lines.size].find_index { |l| ends_at?(l) }
|
59
|
+
ending += 1 if @ending[:inclusive]
|
60
|
+
|
61
|
+
# Use ending+1 since ending is the index when we drop the first line!
|
62
|
+
msg = LogMessage.new(message: lines[0, ending+1].join, preformatted: false, level: nil)
|
63
|
+
[msg, ending + 1]
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "#{File.expand_path(__dir__)}/../log_pattern"
|
2
|
+
|
3
|
+
# Matches messages of this form:
|
4
|
+
#
|
5
|
+
# ./plain.tex:31: Undefined control sequence.
|
6
|
+
# l.31 ...t contains some \ref{warnings} and \errors
|
7
|
+
# for testing.
|
8
|
+
class FileLineError
|
9
|
+
include RegExpPattern
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
super(/^(\/?(?:.*?\/)*[^\/]+):(\d+):/)
|
13
|
+
end
|
14
|
+
|
15
|
+
def read(lines)
|
16
|
+
# @type [LogMessage] msg
|
17
|
+
msg, consumed = super(lines)
|
18
|
+
|
19
|
+
msg.source_file = @start_match[1]
|
20
|
+
line = @start_match[2].to_i
|
21
|
+
msg.source_lines = { from: line, to: line}
|
22
|
+
msg.preformatted = true
|
23
|
+
msg.level = :error
|
24
|
+
|
25
|
+
[msg, consumed]
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require "#{File.expand_path(__dir__)}/../log_pattern"
|
2
|
+
|
3
|
+
# Matches messages of this form:
|
4
|
+
#
|
5
|
+
# Package tocbasic Info: omitting babel extension for `toc'
|
6
|
+
# (tocbasic) because of feature `nobabel' available
|
7
|
+
# (tocbasic) for `toc' on input line 132.
|
8
|
+
#
|
9
|
+
# Note: currently fails if lines get broken badly, e.g. in 000.log:634.
|
10
|
+
class PrefixedMultiLinePattern
|
11
|
+
include RegExpPattern
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
super(/(Package|Class|\w+TeX)\s+(?:(\w+)\s+)?(Warning|Error|Info|Message)/,
|
15
|
+
{ pattern: ->(m) { /^\s*\(#{m[2]}\)/ }, # BROKEN_BY_LINEBREAKS
|
16
|
+
until: :mismatch,
|
17
|
+
inclusive: false })
|
18
|
+
end
|
19
|
+
|
20
|
+
def read(lines)
|
21
|
+
# @type [LogMessage] msg
|
22
|
+
# @type [Int] consumed
|
23
|
+
msg, consumed = super(lines)
|
24
|
+
|
25
|
+
case @start_match[3]
|
26
|
+
when 'Error'
|
27
|
+
msg.level = :error
|
28
|
+
when 'Warning'
|
29
|
+
msg.level = :warning
|
30
|
+
when 'Info', 'Message'
|
31
|
+
msg.level = :info
|
32
|
+
else
|
33
|
+
# TODO: abort?
|
34
|
+
# TODO: debug output
|
35
|
+
end
|
36
|
+
|
37
|
+
# source file from scope, parser does it
|
38
|
+
|
39
|
+
# BROKEN_BY_LINEBREAKS
|
40
|
+
# TODO: may be split across lines --> remove whitespace before extracting
|
41
|
+
suffix_match = /on input line\s+(\d+)(?:\.\s*)?\z/.match(msg.message)
|
42
|
+
unless suffix_match.nil?
|
43
|
+
line = suffix_match[1].to_i
|
44
|
+
msg.source_lines = { from: line, to: line }
|
45
|
+
end
|
46
|
+
|
47
|
+
# TODO: message itself contains useless line prefixes --> remove
|
48
|
+
|
49
|
+
[msg, consumed]
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'tex_log_parser/log_parser'
|
2
|
+
Dir["#{File.expand_path(__dir__)}/tex_log_parser/patterns/*.rb"].each { |p| require p }
|
3
|
+
|
4
|
+
# TODO: document
|
5
|
+
class TexLogParser
|
6
|
+
include LogParser
|
7
|
+
|
8
|
+
def patterns
|
9
|
+
[FileLineError.new, PrefixedMultiLinePattern.new]
|
10
|
+
end
|
11
|
+
|
12
|
+
def scope_changes(line)
|
13
|
+
ops = case line
|
14
|
+
when /^\s*\(([^()]*)\)\s+(.*)$/
|
15
|
+
# A scope opened and closed immediately -- log it, then
|
16
|
+
# continue with rest of the line (there can be multiple such
|
17
|
+
# things in one line, see e.g. 000.log:656)
|
18
|
+
[$1, :pop] + ($2.strip.empty? ? [] : scope_changes($2))
|
19
|
+
when /^\s*\(([^()]+?)\s*$/
|
20
|
+
# A scope opened and will be closed later.
|
21
|
+
# Happens on a dedicated line
|
22
|
+
[$1]
|
23
|
+
when /^\s*(\)+)(.*)$/
|
24
|
+
# Scopes close on a dedicated line, except if they don't (cf 000.log:624)
|
25
|
+
# So we have to continue on the rest of the line. Uh oh.
|
26
|
+
([:pop] * $1.length) + scope_changes($2)
|
27
|
+
when /\([^)]*$/
|
28
|
+
# BROKEN_BY_LINEBREAKS
|
29
|
+
# Bad linebreaks can cause trailing ) to spill over. Narf.
|
30
|
+
# e.g. 000.log:502-503
|
31
|
+
["dummy"] # Compensate the bad pop that will happen next line.
|
32
|
+
else
|
33
|
+
[]
|
34
|
+
end
|
35
|
+
|
36
|
+
# puts line # TODO: debug mode
|
37
|
+
# puts ops.to_s
|
38
|
+
# puts ""
|
39
|
+
ops
|
40
|
+
end
|
41
|
+
end
|
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tex_log_parser
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0.pre.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Raphael Reitzig
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-03-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: minitest
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: yard
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: Parses log files of (La)TeX engines
|
56
|
+
email: 4246780+reitzig@users.noreply.github.com
|
57
|
+
executables:
|
58
|
+
- texlogparser
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- LICENSE
|
63
|
+
- README.md
|
64
|
+
- bin/texlogparser
|
65
|
+
- lib/tex_log_parser.rb
|
66
|
+
- lib/tex_log_parser/log_message.rb
|
67
|
+
- lib/tex_log_parser/log_parser.rb
|
68
|
+
- lib/tex_log_parser/log_pattern.rb
|
69
|
+
- lib/tex_log_parser/patterns/bad_hbox_warning.rb
|
70
|
+
- lib/tex_log_parser/patterns/file_line_error.rb
|
71
|
+
- lib/tex_log_parser/patterns/fontspec_error.rb
|
72
|
+
- lib/tex_log_parser/patterns/prefixed_multi_line_pattern.rb
|
73
|
+
- lib/tex_log_parser/patterns/runaway_parameter_error.rb
|
74
|
+
homepage: http://github.com/reitzig/texlogparser
|
75
|
+
licenses:
|
76
|
+
- MIT
|
77
|
+
metadata:
|
78
|
+
yard.run: yri
|
79
|
+
post_install_message:
|
80
|
+
rdoc_options: []
|
81
|
+
require_paths:
|
82
|
+
- lib
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: 2.3.0
|
88
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ">"
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: 1.3.1
|
93
|
+
requirements: []
|
94
|
+
rubyforge_project:
|
95
|
+
rubygems_version: 2.5.2.1
|
96
|
+
signing_key:
|
97
|
+
specification_version: 4
|
98
|
+
summary: Parses log files of (La)TeX engines
|
99
|
+
test_files: []
|