tex_log_parser 1.0.0.pre.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
[](https://rubygems.org/gems/texlogparser)
|
4
|
+
[](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: []
|