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 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,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'tex_log_parser'
4
+
5
+ # TODO read stdin, parse, write default format (file-line? json?) to stdout
6
+ # TODO options for output format?
@@ -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,8 @@
1
+ require "#{File.expand_path(__dir__)}/../log_pattern"
2
+
3
+ # TODO: document
4
+ class BadHboxWarning
5
+ include RegExpPattern
6
+
7
+ # TODO: implement
8
+ 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,8 @@
1
+ require "#{File.expand_path(__dir__)}/../log_pattern"
2
+
3
+ # TODO: document
4
+ class FontspecError
5
+ include RegExpPattern
6
+
7
+ # TODO: implement
8
+ 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,8 @@
1
+ require "#{File.expand_path(__dir__)}/../log_pattern"
2
+
3
+ # TODO: document
4
+ class RunawayParameterError
5
+ include RegExpPattern
6
+
7
+ # TODO: implement
8
+ 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: []