sportdb-parser 0.7.2 → 0.8.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: 35a69fdc1a31058e1a0069e89106bb68f2c9bbff10465a4111d0d0bf0eb3a210
4
- data.tar.gz: 98f2e0b3f1d21d71b783d96c6e5cac7f2357d436951cae0865558a2289dd2bca
3
+ metadata.gz: 4b9d6cc209285ddfacdf76565fd43541500cac28c516edeecf779c65c98fe556
4
+ data.tar.gz: 329531cc04507bd0a5bc3f8e3488a2e2539ddebe955a48e37aa6d07760135777
5
5
  SHA512:
6
- metadata.gz: b7f43a3c0fd14130b2f5c278b0d0742b4a2d71a489383b80c8a8349c70ebb14280c54b53fc008782c9b539b4ffa4df1442c2f4e82e70ebea05a695343472f613
7
- data.tar.gz: 7b636234a8a66671f35d5d7bdc0113b2df0131b9e065e86eb8f8c688b58ce2eb4d931e21a91a69bfb23dab5d56ed2b78d04400fd308c960fe9f1eb3653f5ab53
6
+ metadata.gz: 57949562e6cd27b1d8058afa549e1d24a8cfd820590d14407d8ed5b34c2095b451ca53065057ef24b86db2dee5d69570cbb054830bd3796ae5b400008da343cf
7
+ data.tar.gz: 2e1b841018e66468d65727947290f30e143340789b271de27c4edd2fb18e169b04446bcc203433329ba343174138d42de112d9801eb608de8c7c5b9144d5c920
data/CHANGELOG.md CHANGED
@@ -1,4 +1,4 @@
1
- ### 0.7.2
1
+ ### 0.8.0
2
2
  ### 0.0.1 / 2024-07-12
3
3
 
4
4
  * Everything is new. First release.
data/Manifest.txt CHANGED
@@ -3,6 +3,8 @@ Manifest.txt
3
3
  README.md
4
4
  Rakefile
5
5
  lib/sportdb/parser.rb
6
+ lib/sportdb/parser/debuggable.rb
7
+ lib/sportdb/parser/lexer-logger.rb
6
8
  lib/sportdb/parser/lexer-on_goal.rb
7
9
  lib/sportdb/parser/lexer-on_group_def.rb
8
10
  lib/sportdb/parser/lexer-on_prop_lineup.rb
@@ -16,10 +18,13 @@ lib/sportdb/parser/lexer-tokenize.rb
16
18
  lib/sportdb/parser/lexer.rb
17
19
  lib/sportdb/parser/lexer_buffer.rb
18
20
  lib/sportdb/parser/lexer_token.rb
21
+ lib/sportdb/parser/parse_tree--core.rb
22
+ lib/sportdb/parser/parse_tree-match.rb
23
+ lib/sportdb/parser/parse_tree-props.rb
24
+ lib/sportdb/parser/parse_tree.rb
25
+ lib/sportdb/parser/parser-runtime.rb
26
+ lib/sportdb/parser/parser-top.rb
19
27
  lib/sportdb/parser/parser.rb
20
- lib/sportdb/parser/parser_runtime.rb
21
- lib/sportdb/parser/racc_parser.rb
22
- lib/sportdb/parser/racc_tree.rb
23
28
  lib/sportdb/parser/token-date--helpers.rb
24
29
  lib/sportdb/parser/token-date--names.rb
25
30
  lib/sportdb/parser/token-date.rb
data/Rakefile CHANGED
@@ -28,3 +28,26 @@ Hoe.spec 'sportdb-parser' do
28
28
  required_ruby_version: '>= 3.1.0'
29
29
  }
30
30
  end
31
+
32
+
33
+
34
+ ###
35
+ ## add our own g task to build parser.rb (via racc & erb)
36
+
37
+ desc 'Generate parser.rb (via racc & erb)'
38
+ task g: 'lib/sportdb/parser/parser.rb'
39
+
40
+
41
+ file 'lib/sportdb/parser/parser.rb' => 'fbtxt/parser.y' do |t|
42
+ puts " [racc] generate parser.rb from parser.y"
43
+ ## sh "racc -v -o #{t.name} #{t.source}"
44
+ sh "racc -o #{t.name} #{t.source}"
45
+ end
46
+
47
+
48
+ file 'fbtxt/parser.y' => FileList['fbtxt/grammar/*.y'] do |t|
49
+ puts " [erb] generate parser.y; merge grammar/*"
50
+ Dir.chdir( 'fbtxt' ) do
51
+ sh "erb parser.y.erb > parser.y"
52
+ end
53
+ end
@@ -0,0 +1,53 @@
1
+ ###
2
+ ## debugg(able) mix-in helper
3
+ ## used for lexer & parser
4
+
5
+ module Debuggable
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+ def debug=(value)
12
+ raise ArgumentError, "true|false required for debug flag; got #{value}" unless value.is_a?(TrueClass) || value.is_a?(FalseClass)
13
+ @debug=value;
14
+ end
15
+ def debug() @debug || false; end ## note - default to false
16
+
17
+ def debug?() @debug == true; end
18
+ end
19
+
20
+
21
+ ########
22
+ ## InstanceMethods
23
+ def debug?() self.class.debug?; end
24
+
25
+ ##
26
+ ## todo/check - allow/support multiple msg args - why? why not?
27
+
28
+ ## note - use trace for debug for now - why? why not?
29
+ def _trace( *args ) __log__( :DEBUG, *args ); end
30
+
31
+ def _info( *args ) __log__( :INFO, *args ); end
32
+ def _warn( *args) __log__( :WARN, *args ); end
33
+ def _error( *args ) __log__( :ERROR, *args ); end
34
+
35
+
36
+
37
+ private
38
+ def __log__( level, *args ) ## :DEBUG, :INFO, :WARN, :ERROR, :FATAL etc.
39
+
40
+ if level == :DEBUG && !debug?
41
+ ## print nothing
42
+ else
43
+ msg = args.map do |arg|
44
+ arg.to_str
45
+ end.join(' | ')
46
+
47
+ fout = level == :WARN || level == :ERROR || level == :FATAL ? STDERR : STDOUT
48
+
49
+ ## todo/check/fix - add/use/check for "local" logger
50
+ fout.puts "#{level} [#{self.class.name}] -- #{msg}"
51
+ end
52
+ end
53
+ end # module Debuggable
@@ -0,0 +1,20 @@
1
+
2
+ module SportDb
3
+ class Lexer
4
+
5
+
6
+ def log( msg )
7
+ ## append msg to ./logs.txt
8
+ ## use ./errors.txt - why? why not?
9
+ ##
10
+ ## change to ./logs_lexer.txt or such - why? why not?
11
+ ## auto-add/prepend [Lexer] and timestamp!!! to msg - why? why not?
12
+ File.open( './logs.txt', 'a:utf-8' ) do |f|
13
+ f.write( "[Lexer] " + msg )
14
+ f.write( "\n" )
15
+ end
16
+ end
17
+
18
+
19
+ end # class Lexer
20
+ end # module SportDb
@@ -6,7 +6,6 @@ class Lexer
6
6
 
7
7
  GOAL_RE = Regexp.union(
8
8
  SPACES_RE,
9
- GOAL_NONE_RE,
10
9
  GOAL_MINUTE_RE,
11
10
  GOAL_MINUTE_NA_RE,
12
11
  GOAL_COUNT_RE,
@@ -16,15 +15,11 @@ GOAL_RE = Regexp.union(
16
15
  ## todo/fix - add ANY_RE !!!!
17
16
  )
18
17
 
18
+
19
19
  def _on_goal( m, ctx: )
20
20
 
21
21
  if m[:space] || m[:spaces]
22
22
  nil ## skip space(s)
23
- elsif m[:goals_none] ## note - eats-up semicolon!! e.g. -; or - ;
24
- # was:[:GOALS_NONE,"<|GOALS_NONE|>"]
25
- ## use literal text!!
26
- Token.new( :GOALS_NONE, m[:goals_none],
27
- lineno: ctx.lineno, offset: m.offset(:goals_none))
28
23
  elsif m[:goal_sep_alt]
29
24
  # was: [:GOAL_SEP_ALT, "<|GOAL_SEP_ALT|>" ] ## e.g. dash (-) WITH leading & trailing space required
30
25
  Token.new( :GOAL_SEP_ALT, m[:goal_sep_alt],
@@ -7,9 +7,12 @@ class Lexer
7
7
  ## todo/fix - use custom (limited) prop basics too
8
8
  PROP_CARDS_RE = Regexp.union(
9
9
  SPACES_RE,
10
+ CARDS_NONE_LEFT_RE,
11
+ CARDS_NONE_RIGHT_RE,
10
12
  MINUTE_RE,
11
13
  PROP_NAME_RE,
12
- / (?<sym> [;,-]) /x
14
+ CARDS_SEP_ALT_RE, ## note - add dash (-) with (required) spaces
15
+ / (?<sym> [;,]) /x
13
16
  ## todo/fix - add ANY_RE here too!!!
14
17
  )
15
18
 
@@ -18,6 +21,16 @@ def _on_prop_cards( m, ctx: ) ## note - m is MatchData object
18
21
 
19
22
  if m[:space] || m[:spaces]
20
23
  nil ## skip space(s)
24
+ elsif m[:cards_none_left]
25
+ Token.new(:CARDS_NONE_LEFT, m[0],
26
+ lineno: ctx.lineno, offset: m.offset(0))
27
+ elsif m[:cards_none_right]
28
+ Token.new(:CARDS_NONE_RIGHT, m[0],
29
+ lineno: ctx.lineno, offset: m.offset(0))
30
+ elsif m[:cards_sep_alt]
31
+ Token.new( :CARDS_SEP_ALT, m[0],
32
+ lineno: ctx.lineno, offset: m.offset(0))
33
+
21
34
  elsif m[:prop_name]
22
35
  Token.new(:PROP_NAME, m[:name],
23
36
  lineno: ctx.lineno, offset: m.offset(:prop_name))
@@ -46,6 +46,14 @@ def _on_top( m, ctx: ) ## note - m is MatchData object
46
46
  lineno: ctx.lineno, offset: m.offset(:inline_awd))
47
47
  elsif m[:inline_canc] then Token.new(:INLINE_CANC, m[:inline_canc],
48
48
  lineno: ctx.lineno, offset: m.offset(:inline_canc))
49
+
50
+ elsif m[:inline_round_short] then Token.new(:INLINE_ROUND_SHORT, m[0],
51
+ lineno: ctx.lineno, offset: m.offset(0),
52
+ value: m[:inline_round_text])
53
+ elsif m[:inline_round_big] then Token.new(:INLINE_ROUND_BIG, m[0],
54
+ lineno: ctx.lineno, offset: m.offset(0),
55
+ value: m[:inline_round_text])
56
+
49
57
  elsif m[:status] then Token.new(:STATUS, m[:status],
50
58
  lineno: ctx.lineno, offset: m.offset(:status),
51
59
  value: _build_status( m ))
@@ -138,7 +138,8 @@ def _tokenize_line( line, lineno )
138
138
  ### todo/fix - add prop yellow/red cards too - why? why not?
139
139
  ## todo/fix - separate sent off and red card
140
140
  ## sent-off - incl. red card, yellow/red card and the era before red cards!!
141
- if ['sent off'].include?( key.downcase)
141
+ if ['sent-off',
142
+ 'sent off'].include?( key.downcase)
142
143
  @re = PROP_CARDS_RE ## use CARDS_RE ???
143
144
  tokens << Token.new(:PROP_SENTOFF, m[:key],
144
145
  lineno: lineno, offset: m.offset(:key))
@@ -150,6 +151,18 @@ def _tokenize_line( line, lineno )
150
151
  @re = PROP_CARDS_RE
151
152
  tokens << Token.new(:PROP_YELLOWCARDS, m[:key],
152
153
  lineno: lineno, offset: m.offset(:key))
154
+
155
+ ## todo: check allow yellow/red cards: or such as key!!!
156
+ ## check for alternate spellings
157
+ ## 'yellow/red cards' - note - meaning closes to yellow or red !!
158
+ ## thus use yellow-red cards as default/standard - why? why not?
159
+ elsif ['yellow-red cards',
160
+ 'yellowred cards'].include?( key.downcase )
161
+ @re = PROP_CARDS_RE
162
+ tokens << Token.new(:PROP_YELLOWREDCARDS, m[:key],
163
+ lineno: lineno, offset: m.offset(:key))
164
+
165
+
153
166
  elsif ['ref', 'referee',
154
167
  'refs', 'referees' ## note - allow/support assistant refs
155
168
  ].include?( key.downcase )
@@ -354,7 +367,9 @@ def _tokenize_line( line, lineno )
354
367
  when '>' then @geo_count = 0;
355
368
  Token.literal( ',', lineno: lineno, offset: m.offset(:sym))
356
369
  ## note - treat geo sep > (ascii) like comma for now!!!
357
- when '[' then
370
+
371
+
372
+ when '[','▪'
358
373
  ##
359
374
  ## todo/fix
360
375
  ## add virtual geo_end token!!!
@@ -363,6 +378,7 @@ def _tokenize_line( line, lineno )
363
378
  @re = RE
364
379
  pos = old_pos
365
380
  next ## backtrack (resume new loop step)
381
+ ## fix-fix-fix merge '▪' with '['
366
382
  else
367
383
  Token.literal( m[:sym], lineno: lineno, offset: m.offset(:sym))
368
384
  end
@@ -426,7 +442,10 @@ def _tokenize_line( line, lineno )
426
442
  @re == PROP_PENALTIES_RE ||
427
443
  @re == PROP_ATTENDANCE_RE ||
428
444
  @re == PROP_REFEREE_RE
429
- if [',', '-', ';'].include?( tokens[-1].type)
445
+
446
+ ## note - add :CARDS_SEP_ALT (aka -)!!! too
447
+ ## maybe later CARDS_NONE_LEFT too - why? why not?
448
+ if [',', '-', ';', :CARDS_SEP_ALT].include?( tokens[-1].type)
430
449
  ## continue/stay in PROP_RE mode
431
450
  ## todo/check - auto-add PROP_CONT token or such
432
451
  ## to help parser with possible NEWLINE
@@ -1,57 +1,30 @@
1
1
 
2
2
  module SportDb
3
- class Lexer
4
-
5
-
6
3
 
7
- def log( msg )
8
- ## append msg to ./logs.txt
9
- ## use ./errors.txt - why? why not?
10
- ##
11
- ## change to ./logs_lexer.txt or such - why? why not?
12
- ## auto-add/prepend [Lexer] and timestamp!!! to msg - why? why not?
13
- File.open( './logs.txt', 'a:utf-8' ) do |f|
14
- f.write( msg )
15
- f.write( "\n" )
4
+ class LexerResult
5
+ attr_reader :tokens, :errors
6
+ def initialize( tokens, errors=[] )
7
+ @tokens, @errors = tokens, errors
16
8
  end
17
- end
18
-
19
-
20
- def _trace( *args )
21
- if debug?
22
- print "[DEBUG] Lexer -- "
23
- args.each { |arg| puts args }
24
- end
25
- end
26
-
27
- def _warn( *args )
28
- print "!! [WARN] Lexer -- "
29
- args.each { |arg| puts args }
30
- end
31
-
32
- def _info( *args )
33
- print "[INFO] Lexer -- "
34
- args.each { |arg| puts args }
35
- end
36
9
 
37
-
38
- def debug?() @debug == true; end
10
+ def ok?() @errors.size == 0; end
11
+ def nok?() !ok?; end
12
+ end # class LexerResult
39
13
 
40
14
 
41
15
 
16
+ class Lexer
17
+ include Debuggable ## auto-adds debug?, _trace, _info, etc.
42
18
 
43
19
 
44
- def initialize( txt, debug: false )
45
- raise ArgumentError, "text as string expected for lexer; got #{txt.class.name}" unless txt.is_a?(String)
20
+ def initialize( txt )
21
+ raise ArgumentError, "text as string expected for lexer; got #{txt.class}" unless txt.is_a?(String)
46
22
 
47
23
  @txt = txt
48
- @debug = debug
49
24
  end
50
25
 
51
26
 
52
-
53
-
54
- def tokenize_with_errors
27
+ def tokenize_with_errors( flatten: true )
55
28
 
56
29
  tokens_by_line = [] ## note: add tokens line-by-line (flatten later)
57
30
  errors = [] ## keep a list of errors - why? why not?
@@ -91,13 +64,12 @@ def tokenize_with_errors
91
64
  ## strip (inline) end-of-line comments (from line)
92
65
  ## check/discuss: make - inline comment require trailing space
93
66
  ## e.g. #1 vs # 1 - why? why not?
94
- line = line.sub( / [ ]* ## (eat-up) optional leading space(s)
67
+ line = line.sub( / [ ]* ## (eat-up) optional leading space(s) too - why? why not?
95
68
  \#{1,}.*?
96
69
  \z
97
70
  /x, '' )
98
71
 
99
72
 
100
- ####
101
73
  # support __END__ marker to cut-off input
102
74
  break if line.match?( /\A [ ]* ## optional leading space(s)
103
75
  __END__
@@ -138,6 +110,10 @@ def tokenize_with_errors
138
110
  tokens_by_line << more_tokens
139
111
  errors += more_errors
140
112
  end
113
+
114
+ ## output last line from tokens by line in debug mode
115
+ _trace( " #{tokens_by_line[-1].size} token(s): " + tokens_by_line[-1].pretty_inspect )
116
+
141
117
  end # each line
142
118
 
143
119
 
@@ -226,10 +202,10 @@ def tokenize_with_errors
226
202
  ## puts "tokens_by_line:"
227
203
  ## pp tokens_by_line
228
204
 
229
-
230
- ## flatten tokens
231
- tokens = []
232
- tokens_by_line.each do |tok_line|
205
+ if flatten
206
+ ## flatten tokens
207
+ tokens = []
208
+ tokens_by_line.each do |tok_line|
233
209
 
234
210
  ## if debug?
235
211
  ## pp tok_line
@@ -243,12 +219,12 @@ def tokenize_with_errors
243
219
  ## use last - why? why not?
244
220
  tokens << Token.newline( lineno: tok_line[0].lineno )
245
221
  end
246
- end
247
-
248
- [tokens,errors]
249
-
250
- end # method tokenize_with_errors
251
-
222
+ end
223
+ [tokens,errors]
224
+ else
225
+ [tokens_by_line, errors]
226
+ end
227
+ end # method tokenize
252
228
 
253
229
 
254
230
  end # class Lexer
@@ -0,0 +1,29 @@
1
+
2
+
3
+ # module JsonSerializable
4
+ # def to_json(*args)
5
+ # as_json.to_json(*args)
6
+ # end
7
+ # end
8
+
9
+ ###
10
+ ## add as_json to Object/Array/Hash
11
+ class Object
12
+ def as_json(*)
13
+ self
14
+ end
15
+ end
16
+
17
+ class Array
18
+ def as_json(*)
19
+ map { |v| v.as_json }
20
+ end
21
+ end
22
+
23
+ class Hash
24
+ def as_json(*)
25
+ each_with_object({}) do |(k, v), h|
26
+ h[k.to_s] = v.as_json
27
+ end
28
+ end
29
+ end