fbtok 0.3.4 → 0.4.0
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 +4 -4
- data/CHANGELOG.md +1 -1
- data/Manifest.txt +2 -4
- data/README.md +55 -8
- data/Rakefile +3 -5
- data/bin/{fbt → fbquick} +25 -34
- data/bin/fbtok +29 -40
- data/bin/fbtree +26 -32
- data/bin/fbx +16 -59
- data/lib/fbtok/pathspec.rb +230 -0
- data/lib/fbtok.rb +2 -9
- metadata +10 -13
- data/bin/fbchk +0 -158
- data/lib/fbtok/linter.rb +0 -119
- data/lib/fbtok/opts.rb +0 -195
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
module SportDb
|
|
4
|
+
|
|
5
|
+
class Pathspec
|
|
6
|
+
|
|
7
|
+
def self.debug=(value) @@debug = value; end
|
|
8
|
+
def self.debug?() @@debug ||= false; end ## note: default is FALSE
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
SEASON_RE = %r{ (?:
|
|
12
|
+
(?<season>\d{4}-\d{2})
|
|
13
|
+
| (?<season>\d{4})
|
|
14
|
+
)
|
|
15
|
+
}x
|
|
16
|
+
|
|
17
|
+
## note: if pattern includes directory add here
|
|
18
|
+
## (otherwise move to more "generic" datafile) - why? why not?
|
|
19
|
+
## update - note include/allow dot (.) too
|
|
20
|
+
## BUT NOT as first character!!! (e.g. exclude .confg.txt !!!)
|
|
21
|
+
## e.g. 2024-25/at.1.txt
|
|
22
|
+
## change to at_1 or uefa_cl or such - why? why not?
|
|
23
|
+
MATCH_RE = %r{
|
|
24
|
+
## "classic" variant i) with season folder
|
|
25
|
+
## e.g. /1930/cup.txt
|
|
26
|
+
(?:
|
|
27
|
+
(?: ^|/ ) # beginning (^) or beginning of path (/)
|
|
28
|
+
|
|
29
|
+
#{SEASON_RE}
|
|
30
|
+
(?: --[a-z0-9_-]+
|
|
31
|
+
)?
|
|
32
|
+
/
|
|
33
|
+
[a-z0-9][a-z0-9._-]* \.txt ## e.g /1-premierleague.txt
|
|
34
|
+
$
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
|
38
|
+
## "compact" variant ii) with season in filename
|
|
39
|
+
## e.g. 1930.txt or 2024_br.txt etc.
|
|
40
|
+
(?:
|
|
41
|
+
(?: ^|/ ) # beginning (^) or beginning of path (/)
|
|
42
|
+
#{SEASON_RE}
|
|
43
|
+
(?: _ ## allow more than one underscore - why? why not?
|
|
44
|
+
[a-z0-9][a-z0-9._-]*
|
|
45
|
+
)?
|
|
46
|
+
\.txt
|
|
47
|
+
$
|
|
48
|
+
)
|
|
49
|
+
}x
|
|
50
|
+
|
|
51
|
+
### add support for matchdatafile with season NOT in directory
|
|
52
|
+
## but starting filename e.g. 2024_friendlies.txt or 2024-25_bundesliga.txt
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def self._norm_seasons( seasons )
|
|
56
|
+
seasons.map {|season| Season(season) }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
## todo/check - rename to glob or expand or such - why? why not?
|
|
61
|
+
def self._find( path, seasons: nil )
|
|
62
|
+
## check - rename dir
|
|
63
|
+
## use root_dir or work_dir or cd or such - why? why not?
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
## note: normalize path - use File.expand_path ??
|
|
67
|
+
## change all backslash to slash for now
|
|
68
|
+
## path = path.gsub( "\\", '/' )
|
|
69
|
+
path = File.expand_path( path )
|
|
70
|
+
|
|
71
|
+
####
|
|
72
|
+
## note - make sure path exists; raise error if not
|
|
73
|
+
raise Errno::ENOENT, "No such directory - #{path})" unless Dir.exist?( path )
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
if seasons && seasons.size > 0
|
|
77
|
+
seasons = _norm_seasons( seasons ) ## norm seasons (string, integer => Season obj)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
## check all txt files
|
|
82
|
+
## note: incl. files starting with dot (.)) as candidates
|
|
83
|
+
## (normally excluded with just *)
|
|
84
|
+
## was: Dir.glob( "#{path}/**/{*,.*}.txt" )
|
|
85
|
+
|
|
86
|
+
candidates = Dir.glob( "#{path}/**/*.txt" )
|
|
87
|
+
## pp candidates
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
datafiles = []
|
|
91
|
+
candidates.each do |candidate|
|
|
92
|
+
if m = MATCH_RE.match( candidate )
|
|
93
|
+
|
|
94
|
+
## check for seasons filter
|
|
95
|
+
next if seasons && seasons.size > 0 &&
|
|
96
|
+
!seasons.include?( Season.parse( m[:season] ))
|
|
97
|
+
|
|
98
|
+
### exclude squad
|
|
99
|
+
## and .v2 or .v2603
|
|
100
|
+
##
|
|
101
|
+
## - worldcup/more/1930_squads.txt => squads
|
|
102
|
+
## - 2014--brazil/cup.v2.txt",
|
|
103
|
+
## - 2014--brazil/cup.v260318_164934.txt",
|
|
104
|
+
|
|
105
|
+
basename = File.basename( candidate, File.extname( candidate ))
|
|
106
|
+
|
|
107
|
+
next if /squad/i.match( basename )
|
|
108
|
+
next if /\.v[0-9][0-9_]*/i.match( basename )
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
datafiles << candidate
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
## pp datafiles
|
|
116
|
+
datafiles
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def self.read( src )
|
|
124
|
+
recs = read_csv( src )
|
|
125
|
+
pp recs if debug?
|
|
126
|
+
|
|
127
|
+
## note - make pathspecs relative to passed in file arg!!!
|
|
128
|
+
basedir = File.dirname( src )
|
|
129
|
+
|
|
130
|
+
recs.each do |rec|
|
|
131
|
+
path = rec['path']
|
|
132
|
+
fullpath = File.expand_path( path, basedir )
|
|
133
|
+
datafiles = _find( fullpath )
|
|
134
|
+
|
|
135
|
+
## add (new) datafiles column (from expanded pathspec)
|
|
136
|
+
rec['datafiles'] = datafiles
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
recs
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
end # class Pathspec
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
##
|
|
150
|
+
# PathspecReport (aka/formerly BatchReport)
|
|
151
|
+
|
|
152
|
+
class PathspecReport
|
|
153
|
+
def initialize( specs, title: )
|
|
154
|
+
@specs = specs
|
|
155
|
+
@title = title
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def build
|
|
159
|
+
buf = String.new
|
|
160
|
+
buf << "# #{@title} - #{@specs.size} dataset(s)\n\n"
|
|
161
|
+
|
|
162
|
+
@specs.each_with_index do |rec,i|
|
|
163
|
+
datafiles = rec['datafiles']
|
|
164
|
+
errors = rec['errors']
|
|
165
|
+
|
|
166
|
+
if errors.size > 0
|
|
167
|
+
buf << "!! #{errors.size} ERROR(S) "
|
|
168
|
+
else
|
|
169
|
+
buf << " OK "
|
|
170
|
+
end
|
|
171
|
+
buf << "%-20s" % rec['path']
|
|
172
|
+
buf << " - #{datafiles.size} datafile(s)"
|
|
173
|
+
buf << "\n"
|
|
174
|
+
|
|
175
|
+
if errors.size > 0
|
|
176
|
+
buf << errors.pretty_inspect
|
|
177
|
+
buf << "\n"
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
buf
|
|
182
|
+
end # method build
|
|
183
|
+
end # class BatchReport
|
|
184
|
+
|
|
185
|
+
BatchReport = PathspecReport
|
|
186
|
+
|
|
187
|
+
end # module Sportdb
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
##
|
|
192
|
+
## keep helpers as global functions - why? why not?
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def build_pathspecs( args ) ### note: was expand_args
|
|
197
|
+
specs = []
|
|
198
|
+
|
|
199
|
+
## note - collect all "loose/standalone" files (NOT directories)
|
|
200
|
+
## in single default pathspec node
|
|
201
|
+
more = []
|
|
202
|
+
|
|
203
|
+
args.each do |arg|
|
|
204
|
+
## check if directory
|
|
205
|
+
if Dir.exist?( arg )
|
|
206
|
+
datafiles = SportDb::Pathspec._find( arg )
|
|
207
|
+
specs << { 'path' => arg,
|
|
208
|
+
'datafiles' => datafiles }
|
|
209
|
+
elsif File.file?( arg ) ## assume it's a file
|
|
210
|
+
## make sure path exists; raise error if not
|
|
211
|
+
## (auto-)expand path to normalize - why? why not?
|
|
212
|
+
more << arg
|
|
213
|
+
else
|
|
214
|
+
raise Errno::ENOENT, "No such file or directory - #{arg}"
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
if more.size > 0
|
|
219
|
+
specs << { 'path' => '<input>',
|
|
220
|
+
'datafiles' => more }
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
specs
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def read_pathspecs( src )
|
|
229
|
+
SportDb::Pathspec.read( src )
|
|
230
|
+
end
|
data/lib/fbtok.rb
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
|
|
2
|
-
require 'sportdb/
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## more stdlibs
|
|
6
|
-
require 'optparse' ## check - already auto-required in cocos? keep? why? why not?
|
|
2
|
+
require 'sportdb/quick'
|
|
7
3
|
|
|
8
4
|
|
|
9
5
|
## our own code
|
|
10
|
-
require_relative 'fbtok/
|
|
11
|
-
require_relative 'fbtok/linter'
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
require_relative 'fbtok/pathspec'
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fbtok
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gerald Bauer
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-05-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: sportdb-parser
|
|
@@ -16,28 +16,28 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 0.
|
|
19
|
+
version: 0.7.1
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 0.
|
|
26
|
+
version: 0.7.1
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name: sportdb-
|
|
28
|
+
name: sportdb-quick
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
31
|
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version:
|
|
33
|
+
version: 0.7.0
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version:
|
|
40
|
+
version: 0.7.0
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: rdoc
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -75,8 +75,7 @@ dependencies:
|
|
|
75
75
|
description: fbtok - football.txt lint tools incl. tokenizer, parser & more
|
|
76
76
|
email: gerald.bauer@gmail.com
|
|
77
77
|
executables:
|
|
78
|
-
-
|
|
79
|
-
- fbt
|
|
78
|
+
- fbquick
|
|
80
79
|
- fbtok
|
|
81
80
|
- fbtree
|
|
82
81
|
- fbx
|
|
@@ -90,14 +89,12 @@ files:
|
|
|
90
89
|
- Manifest.txt
|
|
91
90
|
- README.md
|
|
92
91
|
- Rakefile
|
|
93
|
-
- bin/
|
|
94
|
-
- bin/fbt
|
|
92
|
+
- bin/fbquick
|
|
95
93
|
- bin/fbtok
|
|
96
94
|
- bin/fbtree
|
|
97
95
|
- bin/fbx
|
|
98
96
|
- lib/fbtok.rb
|
|
99
|
-
- lib/fbtok/
|
|
100
|
-
- lib/fbtok/opts.rb
|
|
97
|
+
- lib/fbtok/pathspec.rb
|
|
101
98
|
homepage: https://github.com/sportdb/footty
|
|
102
99
|
licenses:
|
|
103
100
|
- Public Domain
|
data/bin/fbchk
DELETED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
|
|
3
|
-
## tip: to test run:
|
|
4
|
-
## ruby -I ./lib bin/fbchk
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## local hack for debugging
|
|
8
|
-
## use local versions if present
|
|
9
|
-
$LOAD_PATH.unshift( File.expand_path( '/sports/sportdb/sport.db/sportdb-structs/lib' ))
|
|
10
|
-
$LOAD_PATH.unshift( File.expand_path( '/sports/sportdb/sport.db/sportdb-catalogs/lib' ))
|
|
11
|
-
$LOAD_PATH.unshift( File.expand_path( '/sports/sportdb/sport.db/sportdb-search/lib' ))
|
|
12
|
-
$LOAD_PATH.unshift( File.expand_path( '/sports/sportdb/sport.db/sportdb-formats/lib' ))
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
## our own code
|
|
16
|
-
require 'fbtok'
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
## local hack
|
|
21
|
-
## if exists up-to-date catalog db (use local version NOT built-in)
|
|
22
|
-
catalog_path = '/sports/sportdb/sport.db/catalog/catalog.db'
|
|
23
|
-
if File.exist?( catalog_path )
|
|
24
|
-
SportDb::Import.config.catalog_path = catalog_path
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
args = ARGV
|
|
29
|
-
opts = { debug: false,
|
|
30
|
-
file: nil,
|
|
31
|
-
teams: true, ## check/lint teams (name errros etc.)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
parser = OptionParser.new do |parser|
|
|
35
|
-
parser.banner = "Usage: #{$PROGRAM_NAME} [options]"
|
|
36
|
-
|
|
37
|
-
##
|
|
38
|
-
## check if git has a offline option?? (use same)
|
|
39
|
-
## check for other tools - why? why not?
|
|
40
|
-
# parser.on( "-q", "--quiet",
|
|
41
|
-
# "less debug output/messages - default is (#{!opts[:debug]})" ) do |debug|
|
|
42
|
-
# opts[:debug] = false
|
|
43
|
-
# end
|
|
44
|
-
parser.on( "--verbose", "--debug",
|
|
45
|
-
"turn on verbose / debug output (default: #{opts[:debug]})" ) do |debug|
|
|
46
|
-
opts[:debug] = true
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
parser.on( "-f FILE", "--file FILE",
|
|
50
|
-
"read datafiles (pathspecs) via .csv file") do |file|
|
|
51
|
-
opts[:file] = file
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
parser.on( "--[no-]teams",
|
|
55
|
-
"turn on/off team name checks (default: #{opts[:teams]})") do |teams|
|
|
56
|
-
opts[:teams] = teams
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
parser.parse!( args )
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if opts[:debug]
|
|
63
|
-
puts "OPTS:"
|
|
64
|
-
p opts
|
|
65
|
-
puts "ARGV:"
|
|
66
|
-
p args
|
|
67
|
-
|
|
68
|
-
SportDb::QuickMatchLinter.debug = true
|
|
69
|
-
SportDb::QuickMatchReader.debug = true
|
|
70
|
-
SportDb::MatchParser.debug = true
|
|
71
|
-
else
|
|
72
|
-
SportDb::QuickMatchLinter.debug = false
|
|
73
|
-
SportDb::QuickMatchReader.debug = false
|
|
74
|
-
SportDb::MatchParser.debug = false
|
|
75
|
-
LogUtils::Logger.root.level = :info
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
## todo/check - use packs or projects or such
|
|
82
|
-
## instead of specs - why? why not?
|
|
83
|
-
specs = if opts[:file]
|
|
84
|
-
SportDb::Parser::Opts.read_pathspecs( opts[:file] )
|
|
85
|
-
else
|
|
86
|
-
paths = if args.empty?
|
|
87
|
-
[]
|
|
88
|
-
else
|
|
89
|
-
## check for directories
|
|
90
|
-
## and auto-expand
|
|
91
|
-
SportDb::Parser::Opts.expand_args( args )
|
|
92
|
-
end
|
|
93
|
-
## always return array of specs
|
|
94
|
-
[SportDb::Parser::Opts.build_pathspec( paths: paths)]
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
specs.each_with_index do |spec,i|
|
|
101
|
-
paths = spec.paths
|
|
102
|
-
rec = spec.rec
|
|
103
|
-
|
|
104
|
-
errors = []
|
|
105
|
-
paths.each_with_index do |path,j|
|
|
106
|
-
puts "==> [#{i+1}/#{specs.size}, #{j+1}/#{paths.size}] reading >#{path}<..."
|
|
107
|
-
quick = SportDb::QuickMatchLinter.new( read_text( path ),
|
|
108
|
-
check_teams: opts[:teams] )
|
|
109
|
-
matches = quick.parse
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if quick.errors?
|
|
113
|
-
puts "!! #{quick.errors.size} error(s):"
|
|
114
|
-
pp quick.errors
|
|
115
|
-
|
|
116
|
-
quick.errors.each do |err|
|
|
117
|
-
errors << [ path, *err ] # note: use splat (*) to add extra values (starting with msg)
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
puts " #{matches.size} match(es)"
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
if errors.size > 0
|
|
124
|
-
puts
|
|
125
|
-
puts "!! #{errors.size} PARSE ERRORS in #{paths.size} datafile(s)"
|
|
126
|
-
pp errors
|
|
127
|
-
else
|
|
128
|
-
puts
|
|
129
|
-
puts " OK - no parse errors in #{paths.size} datafile(s)"
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
## add errors to rec via rec['errors'] to allow
|
|
133
|
-
## for further processing/reporting
|
|
134
|
-
rec['errors'] = errors
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
###
|
|
140
|
-
## generate a report if --file option used
|
|
141
|
-
if opts[:file]
|
|
142
|
-
buf = SportDb::Parser::BatchReport.new(
|
|
143
|
-
specs,
|
|
144
|
-
title: 'fbchk summary report' ).build
|
|
145
|
-
|
|
146
|
-
puts
|
|
147
|
-
puts "SUMMARY:"
|
|
148
|
-
puts buf
|
|
149
|
-
|
|
150
|
-
basedir = File.dirname( opts[:file] )
|
|
151
|
-
basename = File.basename( opts[:file], File.extname( opts[:file] ))
|
|
152
|
-
outpath = "#{basedir}/fbchk.#{basename}.txt"
|
|
153
|
-
write_text( outpath, buf )
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
puts "bye"
|
|
158
|
-
|
data/lib/fbtok/linter.rb
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
module SportDb
|
|
3
|
-
class Parser
|
|
4
|
-
|
|
5
|
-
###
|
|
6
|
-
## note - Linter for now nested inside Parser - keep? why? why not?
|
|
7
|
-
class Linter
|
|
8
|
-
|
|
9
|
-
def self.debug=(value) @@debug = value; end
|
|
10
|
-
def self.debug?() @@debug ||= false; end ## note: default is FALSE
|
|
11
|
-
def debug?() self.class.debug?; end
|
|
12
|
-
|
|
13
|
-
## turn warn(ings) into errors (default is false)
|
|
14
|
-
def self.warn=(value) @@warn = value; end
|
|
15
|
-
def self.warn?() @@warn ||= false; end ## note: default is FALSE
|
|
16
|
-
def warn?() self.class.warn?; end
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
attr_reader :errors
|
|
20
|
-
|
|
21
|
-
def initialize
|
|
22
|
-
@errors = []
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def errors?() @errors.size > 0; end
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
#########
|
|
31
|
-
## parse - false (default) - tokenize (only)
|
|
32
|
-
## - true - tokenize & parse
|
|
33
|
-
def read( path, parse: true )
|
|
34
|
-
## note: every (new) read call - resets errors list to empty
|
|
35
|
-
@errors = []
|
|
36
|
-
@tree = []
|
|
37
|
-
|
|
38
|
-
outline = QuickMatchOutline.read( path )
|
|
39
|
-
|
|
40
|
-
outline.each_para do |lines|
|
|
41
|
-
|
|
42
|
-
## flatten lines (array of strings) into all-in-one string
|
|
43
|
-
txt = lines.reduce( String.new ) do |mem, line|
|
|
44
|
-
mem << line
|
|
45
|
-
mem << "\n"
|
|
46
|
-
mem
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
if parse
|
|
50
|
-
if debug?
|
|
51
|
-
puts "lines:"
|
|
52
|
-
pp txt
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
## pass along debug flag to parser (& tokenizer)?
|
|
56
|
-
parser = RaccMatchParser.new( txt ) ## use own parser instance (not shared) - why? why not?
|
|
57
|
-
tree, errors = parser.parse_with_errors
|
|
58
|
-
|
|
59
|
-
if errors.size > 0
|
|
60
|
-
## add to "global" error list
|
|
61
|
-
## make a triplet tuple (file / msg / line text)
|
|
62
|
-
errors.each do |msg|
|
|
63
|
-
@errors << [path, msg]
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
if debug?
|
|
68
|
-
puts "parse tree:"
|
|
69
|
-
pp tree
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
@tree += tree ## add nodes
|
|
73
|
-
|
|
74
|
-
else ## process for tokenize only
|
|
75
|
-
|
|
76
|
-
if debug?
|
|
77
|
-
puts "lines:"
|
|
78
|
-
pp txt
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
##
|
|
82
|
-
## add (bakc) a line-by-line tracing (debug) option - why? why not?
|
|
83
|
-
## now debug output is by section (not line-by-line)
|
|
84
|
-
|
|
85
|
-
lexer = Lexer.new( txt )
|
|
86
|
-
t, errors = lexer.tokenize_with_errors
|
|
87
|
-
|
|
88
|
-
if errors.size > 0
|
|
89
|
-
## add to "global" error list
|
|
90
|
-
## make a triplet tuple (file / msg / line text)
|
|
91
|
-
errors.each do |msg|
|
|
92
|
-
@errors << [path, msg]
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
if debug?
|
|
97
|
-
puts "tokens:"
|
|
98
|
-
pp t
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
@tree += t ## add tokens to "tree"
|
|
102
|
-
end # parse? (or tokenize?)
|
|
103
|
-
end # each para (node)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
##
|
|
107
|
-
## auto-add error if no tokens/tree nodes
|
|
108
|
-
## note - do NOT report empty file error by default
|
|
109
|
-
if @tree.empty? && warn? ## @tree.size == 0
|
|
110
|
-
@errors << [path, "empty; no #{parse ? 'parse tree nodes' : 'tokens'}"]
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
@tree ## return parse tree
|
|
114
|
-
end # method read
|
|
115
|
-
end # class Linter
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
end # class Parser
|
|
119
|
-
end # module SportDb
|