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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f1d30fb729e926e1745a62c927272839f8be9b6578b92a6d4aa6415872c49164
4
- data.tar.gz: 9eb174553656a9394993c554969600262807f8210dd1b4d01a5b392431bf14f0
3
+ metadata.gz: d2f0738b7fe70aa6b73cb62e99c6319fe782be09aca0f73f7891a037b98ba020
4
+ data.tar.gz: b801c24152bac941a766f1340409e3a32aeefb69a217bff94f7f86953749d2b1
5
5
  SHA512:
6
- metadata.gz: 9fadde8d521bf4dc46f20650789b1b9de1e8ffe68289bd499ea5d0807e8911bfe7a424a082dfcc599c8e7e97dce63b30ed9962d93cba4cf6b4d5ee3e4f8703bf
7
- data.tar.gz: 197a3385174d42bab7045a8d3e64844f85d131da66d51973cfca71cd2176e88baa2831b40a9d1cfe31b8f13352971576f5427b02575c8b2a12d7f38d81fc3ce9
6
+ metadata.gz: 406113d724520254adf88aedbe49a6a11ea46aaeaebe85666f2a7a78ea1712936508db4b27bdda928436551d64f52503bc7c9cf98aef464af3cb200917ff324d
7
+ data.tar.gz: 9e5d237e6d3344b5c4ffb005d5d0c32d40003933ed99594d5e7a6480b6779254f92a15bfde8c4cc6b8b67feb39f4e40e8489d8cae009708eccccbf13b05fdc6b
data/CHANGELOG.md CHANGED
@@ -1,4 +1,4 @@
1
- ### 0.3.4
1
+ ### 0.4.0
2
2
  ### 0.0.1 / 2025-01-02
3
3
 
4
4
  * Everything is new. First release.
data/Manifest.txt CHANGED
@@ -2,11 +2,9 @@ CHANGELOG.md
2
2
  Manifest.txt
3
3
  README.md
4
4
  Rakefile
5
- bin/fbchk
6
- bin/fbt
5
+ bin/fbquick
7
6
  bin/fbtok
8
7
  bin/fbtree
9
8
  bin/fbx
10
9
  lib/fbtok.rb
11
- lib/fbtok/linter.rb
12
- lib/fbtok/opts.rb
10
+ lib/fbtok/pathspec.rb
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
- # fbtok - football.txt lint tools incl. tokenizer, parser & more
2
-
1
+ # fbtok - football.txt lint tools incl. tokenizer (lexer), parser & more
2
+
3
3
 
4
4
 
5
5
  * home :: [github.com/sportdb/footty](https://github.com/sportdb/footty)
@@ -20,20 +20,67 @@ $ gem install fbtok
20
20
  ## Usage
21
21
 
22
22
 
23
- ### fbtok - use tokenizer/parser
23
+ ### fbtok & fbtree - use tokenizer (lexer) & parser
24
24
 
25
25
  - depends on sportdb-parser
26
26
 
27
- ### fbt - use quick match reader & fbx - dump match schedule - uses quick league & match readers
27
+ get help
28
+ ```
29
+ $ fbtok -h
30
+ $ fbtree -h
31
+ ```
32
+
33
+ run on single / individual (data)files
34
+ ````
35
+ $ fbtok england/2025-26/1-permierleague.txt
36
+ $ fbtok worldcup/min/2022.txt
37
+
38
+ $ fbtree england/2025-26/1-permierleague.txt
39
+ $ fbtree worldcup/min/2022.txt
40
+
41
+ ```
42
+
43
+ or on directories (auto-collecting all datafiles)
44
+
45
+ $ fbtok england
46
+ $ fbtok worldcup
47
+
48
+ $ fbtree england
49
+ $ fbtree worldcup
50
+ ```
51
+
28
52
 
29
- - depends on sportdb-quick
30
53
 
31
- ### fbchk - use quick match linter; check league and team names via search & more
54
+ ### fbquick & fbx - use quick match reader & dump match schedule
55
+
56
+ - depends on sportdb-quick (& sportdb-parser)
57
+
58
+ get help
59
+ ```
60
+ $ fbquick -h
61
+ $ fbx -h
62
+ ```
63
+
64
+ run on single / individual (data)files
65
+ ````
66
+ $ fbquick england/2025-26/1-permierleague.txt
67
+ $ fbquick worldcup/min/2022.txt
68
+
69
+ $ fbx england/2025-26/1-permierleague.txt
70
+ $ fbx worldcup/min/2022.txt
71
+
72
+ ```
73
+
74
+ or on directories (auto-collecting all datafiles)
75
+
76
+ $ fbquick england
77
+ $ fbquick worldcup
78
+ ```
79
+
80
+ note: `fbx` only works with single / individual (data)files
32
81
 
33
- - depends on sportdb-formats (incl. sportdb-search & sportdb-catalogs)
34
82
 
35
83
 
36
- ...
37
84
 
38
85
 
39
86
 
data/Rakefile CHANGED
@@ -2,7 +2,7 @@ require 'hoe'
2
2
 
3
3
 
4
4
  Hoe.spec 'fbtok' do
5
- self.version = '0.3.4'
5
+ self.version = '0.4.0'
6
6
 
7
7
  self.summary = "fbtok - football.txt lint tools incl. tokenizer, parser & more"
8
8
  self.description = summary
@@ -19,10 +19,8 @@ Hoe.spec 'fbtok' do
19
19
  self.licenses = ['Public Domain']
20
20
 
21
21
  self.extra_deps = [
22
- # ['sportdb-structs', '>= 0.5.0'],
23
- # ['logutils', '>= 0.6.1'],
24
- ['sportdb-parser', '>= 0.5.8'],
25
- ['sportdb-formats', '>= 2.1.2'],
22
+ ['sportdb-parser', '>= 0.7.1'],
23
+ ['sportdb-quick', '>= 0.7.0'],
26
24
  ]
27
25
 
28
26
  self.spec_extras = {
@@ -1,24 +1,20 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  ## tip: to test run:
4
- ## ruby -I ./lib bin/fbt
4
+ ## $ ruby -I ./lib bin/fbquick
5
5
  ## -or-
6
- ## ruby -I ../parser/lib -I ./lib bin/fbt
6
+ ## $ ruby -I ../parser/lib -I ./lib bin/fbquick
7
7
  ## -or-
8
- ## ruby -I ../parser/lib -I ../sportdb-structs/lib -I ./lib bin/fbt
8
+ ## $ ruby -I ../parser/lib -I ../quick/lib -I ./lib bin/fbquick
9
+
10
+ ##
11
+ ## todo/fix - add (shortcut) alias fbquik,fbqk - why? why not?
9
12
 
10
13
 
11
14
  ## our own code
12
15
  require 'fbtok'
13
16
 
14
17
 
15
- ##
16
- ## read textfile
17
- ## and dump tokens
18
- ##
19
- ## fbt ../openfootball/.../euro.txt
20
-
21
-
22
18
 
23
19
 
24
20
  args = ARGV
@@ -57,7 +53,7 @@ if opts[:debug]
57
53
  p opts
58
54
  puts "ARGV:"
59
55
  p args
60
-
56
+
61
57
  SportDb::QuickMatchReader.debug = true
62
58
  SportDb::MatchParser.debug = true
63
59
  else
@@ -70,30 +66,26 @@ end
70
66
  ## todo/check - use packs or projects or such
71
67
  ## instead of specs - why? why not?
72
68
  specs = if opts[:file]
73
- SportDb::Parser::Opts.read_pathspecs( opts[:file] )
69
+ read_pathspecs( opts[:file] )
74
70
  else
75
- paths = if args.empty?
76
- ['/sports/openfootball/euro/2021--europe/euro.txt',
77
- '/sports/openfootball/euro/2024--germany/euro.txt']
78
- else
79
- ## check for directories
80
- ## and auto-expand
81
- SportDb::Parser::Opts.expand_args( args )
82
- end
83
- ## always return array of specs
84
- [SportDb::Parser::Opts.build_pathspec( paths: paths)]
71
+ args = ['/sports/openfootball/euro/2021--europe/euro.txt',
72
+ '/sports/openfootball/euro/2024--germany/euro.txt'] if args.empty?
73
+
74
+ build_pathspecs( args )
85
75
  end
86
76
 
77
+ pp specs
87
78
 
88
79
 
89
- specs.each_with_index do |spec,i|
90
- paths = spec.paths
91
- rec = spec.rec
80
+
81
+ specs.each_with_index do |rec,i|
82
+ datafiles = rec['datafiles']
92
83
  errors = []
93
84
 
94
- paths.each_with_index do |path,j|
95
- puts "==> [#{j+1}/#{paths.size}] reading >#{path}<..."
96
- quick = SportDb::QuickMatchReader.new( read_text( path ) )
85
+ datafiles.each_with_index do |path,j|
86
+ puts "==> [#{i+1}/#{specs.size}, #{j+1}/#{datafiles.size}] reading >#{path}<..."
87
+ txt = read_text( path )
88
+ quick = SportDb::QuickMatchReader.new( txt )
97
89
  matches = quick.parse
98
90
 
99
91
  if quick.errors?
@@ -109,11 +101,11 @@ specs = if opts[:file]
109
101
 
110
102
  if errors.size > 0
111
103
  puts
112
- puts "!! #{errors.size} PARSE ERRORS in #{paths.size} datafile(s)"
104
+ puts "!! #{errors.size} PARSE ERRORS in #{datafiles.size} datafile(s)"
113
105
  pp errors
114
106
  else
115
107
  puts
116
- puts " OK - no parse errors in #{paths.size} datafile(s)"
108
+ puts " OK - no parse errors in #{datafiles.size} datafile(s)"
117
109
  end
118
110
 
119
111
  ## add errors to rec via rec['errors'] to allow
@@ -126,9 +118,9 @@ specs = if opts[:file]
126
118
  ###
127
119
  ## generate a report if --file option used
128
120
  if opts[:file]
129
- buf = SportDb::Parser::BatchReport.new(
130
- specs,
131
- title: 'fbt summary report' ).build
121
+ buf = SportDb::PathspecReport.new(
122
+ specs,
123
+ title: 'fbquick summary report' ).build
132
124
 
133
125
  puts
134
126
  puts "SUMMARY:"
@@ -142,4 +134,3 @@ end
142
134
 
143
135
 
144
136
  puts "bye"
145
-
data/bin/fbtok CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  ## tip: to test run:
4
- ## ruby -I ./lib bin/fbtok
4
+ ## $ ruby -I ./lib bin/fbtok
5
5
 
6
6
  require 'fbtok'
7
7
 
8
8
  args = ARGV
9
-
9
+
10
10
 
11
11
  opts = {
12
12
  debug: true,
@@ -31,14 +31,6 @@ parser = OptionParser.new do |parser|
31
31
  opts[:warn] = true
32
32
  end
33
33
 
34
- =begin
35
- parser.on( "--metal",
36
- "turn off typed parse tree; show to the metal tokens"+
37
- " (default: #{opts[:metal]})" ) do |metal|
38
- opts[:metal] = true
39
- end
40
- =end
41
-
42
34
 
43
35
  parser.on( "-f FILE", "--file FILE",
44
36
  "read datafiles (pathspecs) via .csv file") do |file|
@@ -58,57 +50,56 @@ if opts[:debug]
58
50
  p args
59
51
  end
60
52
 
61
- SportDb::Parser::Linter.debug = opts[:debug]
62
- SportDb::Parser::Opts.debug = opts[:debug]
63
53
 
64
- SportDb::Parser::Linter.warn = opts[:warn]
54
+ # SportDb::Parser::Linter.debug = opts[:debug]
55
+ # SportDb::Parser::Linter.warn = opts[:warn]
65
56
 
66
57
 
67
58
 
68
59
  ## todo/check - use packs or projects or such
69
60
  ## instead of specs - why? why not?
70
61
  specs = if opts[:file]
71
- SportDb::Parser::Opts.read_pathspecs( opts[:file] )
62
+ read_pathspecs( opts[:file] )
72
63
  else
73
- paths = if args.empty?
74
- ['/sports/openfootball/euro/2021--europe/euro.txt',
75
- '/sports/openfootball/euro/2024--germany/euro.txt']
76
- else
77
- ## check for directories
78
- ## and auto-expand
79
- SportDb::Parser::Opts.expand_args( args )
80
- end
81
- ## always return array of specs
82
- [SportDb::Parser::Opts.build_pathspec( paths: paths)]
64
+ args = ['/sports/openfootball/euro/2021--europe/euro.txt',
65
+ '/sports/openfootball/euro/2024--germany/euro.txt'] if args.empty?
66
+
67
+ build_pathspecs( args )
83
68
  end
84
69
 
85
70
 
86
- linter = SportDb::Parser::Linter.new
71
+ pp specs
72
+
87
73
 
88
74
 
89
- specs.each_with_index do |spec,i|
90
- paths = spec.paths
91
- rec = spec.rec
92
-
75
+ specs.each_with_index do |rec,i|
76
+ datafiles = rec['datafiles']
77
+
93
78
  errors = []
94
79
 
95
- paths.each_with_index do |path,j|
96
- puts "==> [#{i+1}/#{specs.size}, #{j+1}/#{paths.size}] reading >#{path}<..."
97
- tokens = linter.read( path, parse: false ) ## only tokenize (do NOT parse)
80
+ datafiles.each_with_index do |path,j|
81
+ puts "==> [#{i+1}/#{specs.size}, #{j+1}/#{datafiles.size}] reading >#{path}<..."
82
+
83
+ txt = read_text( path )
84
+ lexer = SportDb::Lexer.new( txt, debug: opts[:debug] )
85
+ tokens, more_errors = lexer.tokenize_with_errors
86
+
87
+ ####
88
+ ## todo - report error on empty file (no tokens!!!)
98
89
 
99
- puts " #{tokens.size} token(s)"
90
+ puts " #{tokens.size} token(s)"
100
91
 
101
- errors += linter.errors if linter.errors?
92
+ errors += more_errors if more_errors.size > 0
102
93
  end
103
94
 
104
95
  if errors.size > 0
105
96
  puts
106
97
  pp errors
107
98
  puts
108
- puts "!! #{errors.size} tokenize error(s) in #{paths.size} datafiles(s)"
99
+ puts "!! #{errors.size} tokenize error(s) in #{datafiles.size} datafiles(s)"
109
100
  else
110
101
  puts
111
- puts "OK no tokenize errors found in #{paths.size} datafile(s)"
102
+ puts "OK no tokenize errors found in #{datafiles.size} datafile(s)"
112
103
  end
113
104
 
114
105
  ## add errors to rec via rec['errors'] to allow
@@ -120,9 +111,8 @@ end
120
111
  ###
121
112
  ## generate a report if --file option used
122
113
  if opts[:file]
123
-
124
- buf = SportDb::Parser::BatchReport.new(
125
- specs,
114
+ buf = SportDb::PathspecReport.new(
115
+ specs,
126
116
  title: 'fbtok summary report' ).build
127
117
 
128
118
  puts
@@ -135,4 +125,3 @@ if opts[:file]
135
125
  end
136
126
 
137
127
  puts "bye"
138
-
data/bin/fbtree CHANGED
@@ -1,12 +1,12 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  ## tip: to test run:
4
- ## ruby -I ./lib bin/fbtree
4
+ ## $ ruby -I ./lib bin/fbtree
5
5
 
6
6
  require 'fbtok'
7
7
 
8
8
  args = ARGV
9
-
9
+
10
10
 
11
11
  opts = {
12
12
  debug: true,
@@ -50,10 +50,9 @@ if opts[:debug]
50
50
  p args
51
51
  end
52
52
 
53
- SportDb::Parser::Linter.debug = opts[:debug]
54
- SportDb::Parser::Opts.debug = opts[:debug]
55
53
 
56
- SportDb::Parser::Linter.warn = opts[:warn]
54
+ # SportDb::Parser::Linter.debug = opts[:debug]
55
+ # SportDb::Parser::Linter.warn = opts[:warn]
57
56
 
58
57
 
59
58
 
@@ -61,18 +60,12 @@ SportDb::Parser::Linter.warn = opts[:warn]
61
60
  ## todo/check - use packs or projects or such
62
61
  ## instead of specs - why? why not?
63
62
  specs = if opts[:file]
64
- SportDb::Parser::Opts.read_pathspecs( opts[:file] )
63
+ read_pathspecs( opts[:file] )
65
64
  else
66
- paths = if args.empty?
67
- ['/sports/openfootball/euro/2021--europe/euro.txt',
68
- '/sports/openfootball/euro/2024--germany/euro.txt']
69
- else
70
- ## check for directories
71
- ## and auto-expand
72
- SportDb::Parser::Opts.expand_args( args )
73
- end
74
- ## always return array of specs
75
- [SportDb::Parser::Opts.build_pathspec( paths: paths)]
65
+ args = ['/sports/openfootball/euro/2021--europe/euro.txt',
66
+ '/sports/openfootball/euro/2024--germany/euro.txt'] if args.empty?
67
+
68
+ build_pathspecs( args )
76
69
  end
77
70
 
78
71
 
@@ -80,12 +73,12 @@ specs = if opts[:file]
80
73
  def dump_tree_stats( tree )
81
74
  stats = Hash.new(0) ## track counts only for now
82
75
  tree.each do |node|
83
- stats[ node.class ] += 1
76
+ stats[ node.class ] += 1
84
77
  end
85
78
 
86
79
  match_count = stats[ RaccMatchParser::MatchLine ]
87
80
  goal_count = stats[ RaccMatchParser::GoalLine ]
88
- lineup_count = stats[ RaccMatchParser::LineupLine ]
81
+ lineup_count = stats[ RaccMatchParser::LineupLine ]
89
82
 
90
83
  puts " #{match_count} MatchLine(s)" if match_count > 0
91
84
  puts " #{goal_count} GoalLine(s)" if goal_count > 0
@@ -95,31 +88,32 @@ end
95
88
 
96
89
 
97
90
 
98
- linter = SportDb::Parser::Linter.new
99
91
 
92
+ specs.each_with_index do |rec,i|
93
+ datafiles = rec['datafiles']
100
94
 
101
- specs.each_with_index do |spec,i|
102
- paths = spec.paths
103
- rec = spec.rec
104
-
105
95
  errors = []
106
96
 
107
- paths.each_with_index do |path,j|
108
- puts "==> [#{i+1}/#{specs.size}, #{j+1}/#{paths.size}] reading >#{path}<..."
109
- tree = linter.read( path, parse: true )
97
+ datafiles.each_with_index do |path,j|
98
+ puts "==> [#{i+1}/#{specs.size}, #{j+1}/#{datafiles.size}] reading >#{path}<..."
99
+
100
+ txt = read_text( path )
101
+ parser = RaccMatchParser.new( txt, debug: opts[:debug] )
102
+ tree = parser.parse
103
+
110
104
  dump_tree_stats( tree )
111
-
112
- errors += linter.errors if linter.errors?
105
+
106
+ errors += parser.errors if parser.errors?
113
107
  end
114
108
 
115
109
  if errors.size > 0
116
110
  puts
117
111
  pp errors
118
112
  puts
119
- puts "!! #{errors.size} parse error(s) in #{paths.size} datafiles(s)"
113
+ puts "!! #{errors.size} parse error(s) in #{datafiles.size} datafiles(s)"
120
114
  else
121
115
  puts
122
- puts "OK no parse errors found in #{paths.size} datafile(s)"
116
+ puts "OK no parse errors found in #{datafiles.size} datafile(s)"
123
117
  end
124
118
 
125
119
  ## add errors to rec via rec['errors'] to allow
@@ -132,8 +126,8 @@ end
132
126
  ## generate a report if --file option used
133
127
  if opts[:file]
134
128
 
135
- buf = SportDb::Parser::BatchReport.new(
136
- specs,
129
+ buf = SportDb::PathspecReport.new(
130
+ specs,
137
131
  title: 'fbtree summary report' ).build
138
132
 
139
133
  puts
data/bin/fbx CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  ## tip: to test run:
4
- ## ruby -I ./lib -I ../parser/lib bin/fbx
4
+ ## $ ruby -I ./lib -I ../parser/lib bin/fbx
5
5
 
6
6
  ##
7
7
  ## todo/fix - use for testing
@@ -40,10 +40,10 @@ require 'fbtok'
40
40
  opts[:debug] = debug
41
41
  end
42
42
 
43
- parser.on( "--outline",
44
- "turn on outline (only) output (default: #{opts[:outline]})" ) do |outline|
45
- opts[:outline] = outline
46
- end
43
+ # parser.on( "--outline",
44
+ # "turn on outline (only) output (default: #{opts[:outline]})" ) do |outline|
45
+ # opts[:outline] = outline
46
+ # end
47
47
  end
48
48
  parser.parse!( args )
49
49
 
@@ -53,27 +53,23 @@ if opts[:debug]
53
53
  p opts
54
54
  puts "ARGV:"
55
55
  p args
56
-
57
- SportDb::MatchParser.debug = true
56
+
57
+ SportDb::MatchParser.debug = true
58
58
  else
59
- SportDb::MatchParser.debug = false
59
+ SportDb::MatchParser.debug = false
60
60
  LogUtils::Logger.root.level = :info
61
61
  end
62
62
 
63
63
 
64
- paths = if args.empty?
65
- [
64
+ args = [
66
65
  '../../../openfootball/euro/2021--europe/euro.txt',
67
66
  '../../../openfootball/euro/2024--germany/euro.txt',
68
- ]
69
- else
70
- ## check for directories
71
- ## and auto-expand
67
+ ] if args.empty?
72
68
 
73
- SportDb::Parser::Opts.expand_args( args )
74
- end
69
+ pp args
75
70
 
76
71
 
72
+ paths = args
77
73
 
78
74
 
79
75
  ## errors = []
@@ -83,58 +79,20 @@ paths.each_with_index do |path,i|
83
79
  puts "==> [#{i+1}/#{paths.size}] reading >#{path}<..."
84
80
 
85
81
  txt = read_text( path )
86
- outline = SportDb::QuickLeagueOutline.parse( txt )
87
-
88
- ## todo/fix - add each_sec_with_index upstream
89
- ## add outline.secs.size - why? why not?
90
- j = 0
91
- secs_count = '?'
92
- outline.each_sec do |sec| ## sec(tion)s
93
- season = Season.parse( sec.season ) ## convert (str) to season obj!!!
94
- league = sec.league
95
- stage = sec.stage
96
- lines = sec.lines
97
-
98
- puts " section #{j+1}/#{secs_count} - #{league} #{season}, #{stage} - #{lines.size} line(s)"
99
-
100
- next if opts[:outline]
101
82
 
102
- =begin
103
- ### check if event info availabe - use start_date;
104
- ## otherwise we have to guess (use a "synthetic" start_date)
105
- event_info = catalog.events.find_by( season: season,
106
- league: league )
107
-
108
- start = if event_info && event_info.start_date
109
- puts "event info found:"
110
- puts " using start date from event: "
111
- pp event_info
112
- pp event_info.start_date
113
- event_info.start_date
114
- else
115
- =end
116
- start = if season.year?
117
- Date.new( season.start_year, 1, 1 )
118
- else
119
- Date.new( season.start_year, 7, 1 )
120
- end
121
-
122
- parser = SportDb::MatchParser.new( lines,
123
- start ) ## note: keep season start_at date for now (no need for more specific stage date need for now)
83
+ parser = SportDb::MatchParser.new( txt, start: nil )
124
84
 
125
- auto_conf_teams, matches, rounds, groups = parser.parse
85
+ auto_conf_teams, matches, rounds, groups = parser.parse
126
86
 
127
87
  puts ">>> #{auto_conf_teams.size} teams:"
128
88
  pp auto_conf_teams
129
89
  puts ">>> #{matches.size} matches:"
130
- ## pp matches
90
+ pp matches[0,2] ## print first two matches
91
+ puts "..."
131
92
  puts ">>> #{rounds.size} rounds:"
132
93
  pp rounds
133
94
  puts ">>> #{groups.size} groups:"
134
95
  pp groups
135
-
136
- j += 1
137
- end # each secs
138
96
  end # each paths
139
97
 
140
98
  =begin
@@ -150,4 +108,3 @@ end
150
108
  =end
151
109
 
152
110
  puts "bye"
153
-