sportdb-parser 0.7.1 → 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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -1
  3. data/Manifest.txt +25 -7
  4. data/Rakefile +23 -0
  5. data/lib/sportdb/parser/debuggable.rb +53 -0
  6. data/lib/sportdb/parser/lexer-logger.rb +20 -0
  7. data/lib/sportdb/parser/lexer-on_goal.rb +167 -0
  8. data/lib/sportdb/parser/lexer-on_group_def.rb +31 -0
  9. data/lib/sportdb/parser/lexer-on_prop_lineup.rb +79 -0
  10. data/lib/sportdb/parser/lexer-on_prop_misc.rb +123 -0
  11. data/lib/sportdb/parser/lexer-on_prop_penalties.rb +40 -0
  12. data/lib/sportdb/parser/lexer-on_round_def.rb +37 -0
  13. data/lib/sportdb/parser/lexer-on_top.rb +133 -0
  14. data/lib/sportdb/parser/lexer-prep_doc.rb +131 -0
  15. data/lib/sportdb/parser/lexer-prep_line.rb +63 -0
  16. data/lib/sportdb/parser/lexer-tokenize.rb +468 -0
  17. data/lib/sportdb/parser/lexer.rb +122 -1376
  18. data/lib/sportdb/parser/lexer_buffer.rb +8 -37
  19. data/lib/sportdb/parser/lexer_token.rb +126 -0
  20. data/lib/sportdb/parser/parse_tree--core.rb +29 -0
  21. data/lib/sportdb/parser/parse_tree-match.rb +309 -0
  22. data/lib/sportdb/parser/parse_tree-props.rb +233 -0
  23. data/lib/sportdb/parser/parse_tree.rb +202 -0
  24. data/lib/sportdb/parser/parser-top.rb +102 -0
  25. data/lib/sportdb/parser/parser.rb +1253 -1392
  26. data/lib/sportdb/parser/token-date--helpers.rb +130 -0
  27. data/lib/sportdb/parser/token-date--names.rb +108 -0
  28. data/lib/sportdb/parser/token-date.rb +20 -192
  29. data/lib/sportdb/parser/token-date_duration.rb +8 -27
  30. data/lib/sportdb/parser/token-geo.rb +16 -16
  31. data/lib/sportdb/parser/token-goals--helpers.rb +114 -0
  32. data/lib/sportdb/parser/token-goals.rb +101 -255
  33. data/lib/sportdb/parser/token-group.rb +8 -22
  34. data/lib/sportdb/parser/token-prop.rb +178 -124
  35. data/lib/sportdb/parser/token-prop_name.rb +48 -39
  36. data/lib/sportdb/parser/token-round.rb +21 -35
  37. data/lib/sportdb/parser/token-score--helpers.rb +189 -0
  38. data/lib/sportdb/parser/token-score.rb +9 -393
  39. data/lib/sportdb/parser/token-score_full.rb +331 -0
  40. data/lib/sportdb/parser/token-status.rb +44 -46
  41. data/lib/sportdb/parser/token-status_inline.rb +112 -0
  42. data/lib/sportdb/parser/token-text.rb +41 -31
  43. data/lib/sportdb/parser/token-time.rb +29 -26
  44. data/lib/sportdb/parser/token.rb +90 -158
  45. data/lib/sportdb/parser/version.rb +2 -2
  46. data/lib/sportdb/parser.rb +88 -62
  47. metadata +27 -9
  48. data/lib/sportdb/parser/blocktxt.rb +0 -99
  49. data/lib/sportdb/parser/lexer_tty.rb +0 -111
  50. data/lib/sportdb/parser/racc_parser.rb +0 -88
  51. data/lib/sportdb/parser/racc_tree.rb +0 -557
  52. data/lib/sportdb/parser/token-table.rb +0 -149
  53. data/lib/sportdb/parser/token_helpers.rb +0 -92
  54. /data/lib/sportdb/parser/{parser_runtime.rb → parser-runtime.rb} +0 -0
@@ -0,0 +1,233 @@
1
+ module SportDb
2
+ class Parser
3
+
4
+
5
+ =begin
6
+ RefereeLine = Struct.new( :name, :country ) do
7
+ def pretty_print( printer )
8
+ printer.text( "<RefereeLine " )
9
+ printer.text( name )
10
+ printer.text( " (#{country})" ) if country
11
+ printer.text( ">" )
12
+ end
13
+ end
14
+ =end
15
+
16
+
17
+ ## support multiple referees (incl. assistant refs etc.)
18
+ RefereeLine = Struct.new( :referees ) do
19
+ def pretty_print( q )
20
+ q.group( 4, '<RefereeLine ', '>') do
21
+ q.pp( referees )
22
+ end
23
+ end
24
+ end # RefereeLine
25
+
26
+ Referee = Struct.new( :name, :country ) do
27
+ def to_s
28
+ buf = String.new
29
+ buf << name
30
+ buf << " (#{country})" if country
31
+ buf
32
+ end
33
+
34
+ def pretty_print( q )
35
+ q.text( to_s )
36
+ end
37
+ end # Referee
38
+
39
+
40
+ AttendanceLine = Struct.new( :att ) do
41
+ def pretty_print( q )
42
+ q.group( 4, '<AttendanceLine ', '>') do
43
+ q.pp( att )
44
+ end
45
+ end
46
+ end # AttendanceLine
47
+
48
+
49
+
50
+
51
+ PenaltiesLine = Struct.new( :penalties ) do
52
+ def as_json(*)
53
+ ['<PenaltiesLine>', { 'penalties' => penalties.as_json }]
54
+ end
55
+
56
+ def pretty_print( q )
57
+ q.group( 4, '< ', '>') do
58
+ q.pp( penalties )
59
+ end
60
+ end
61
+ end # PenaltiesLine
62
+
63
+
64
+ Penalty = Struct.new( :name, :score, :note ) do
65
+ def as_json(*)
66
+ h = { 'name' => name }
67
+ h['score'] = "#{score[0]}-#{score[1]}" if score
68
+ h['note'] = "(#{note})" if note
69
+
70
+ ## ['<Penalty>', h]
71
+ h # note - return "inline" json object
72
+ end
73
+
74
+ def to_s
75
+ buf = String.new
76
+ buf << "#{score[0]}-#{score[1]} " if score
77
+ buf << name
78
+ buf << " (#{note})" if note
79
+ buf
80
+ end
81
+
82
+
83
+ def pretty_print( q )
84
+ q.text( to_s )
85
+ end
86
+ end # Penalty
87
+
88
+
89
+
90
+
91
+ ## find a better name for player (use bookings?) - note - red/yellow card for trainer possible
92
+ CardsLine = Struct.new( :type,
93
+ :bookings,
94
+ ) do
95
+ def pretty_print( q )
96
+ q.group( 4, '<CardsLine ', '>') do
97
+ q.text( type )
98
+ ## note - either (all-in-one or undetermined) bookings: []
99
+ ## or bookings1/2 aka: [[],[]]
100
+ q.text( " bookings=" )
101
+ q.pp( bookings )
102
+ end
103
+ end
104
+ end # CardsLine
105
+
106
+
107
+ Booking = Struct.new( :name, :minute ) do
108
+ def to_s
109
+ buf = String.new
110
+ buf << "#{name}"
111
+ buf << " #{minute.to_s}" if minute
112
+ buf
113
+ end
114
+
115
+ def pretty_print( q )
116
+ q.text( to_s )
117
+ end
118
+ end # Booking
119
+
120
+
121
+
122
+
123
+ ##
124
+ ## change :lineup to :lineups (e.g. requires array of lineups)
125
+ LineupLine = Struct.new( :team, :lineup, :coach ) do
126
+ def as_json(*)
127
+ h = { 'team' => team.as_json,
128
+ 'lineups' => lineup.as_json
129
+ }
130
+ h['coach'] = coach.as_json if coach
131
+
132
+ ['<LineupLine>', h]
133
+ end
134
+
135
+ def pretty_print( q )
136
+ q.group( 4, '<LineupLine ', '>') do
137
+ q.text( team )
138
+ q.text( ' ' )
139
+
140
+ ## todo/fix - check for "flat" items - no formation
141
+ ## or empty lineup (possible?)
142
+ ## add formation e.g. (1-) 3-4-2-1
143
+ formation = lineup.map { |item| item.size }
144
+ ## note - remove first entry (assume it's 1 for goalee!)
145
+ formation.shift
146
+ q.text( formation.join('-'))
147
+ q.text( ' ')
148
+
149
+ q.pp( lineup )
150
+
151
+ if coach
152
+ q.breakable
153
+ q.text( "coach=" + coach )
154
+ end
155
+ end
156
+ end
157
+ end # LineupLine
158
+
159
+
160
+ Lineup = Struct.new( :name, :captain, :cards, :sub ) do
161
+ def as_json(*)
162
+ h = { 'name' => name.as_json }
163
+ h['captain'] = captain.as_json if captain
164
+ h['cards'] = cards.as_json if cards
165
+ h['sub'] = sub.as_json if sub
166
+
167
+ ## ['<Lineup>', h]
168
+ h ## note - return "inline" json object (not tagged array with json object)
169
+ end
170
+
171
+ def pretty_print( q )
172
+ q.group( 0, '', '') do ## group( indent, open, close)
173
+ q.text( name )
174
+ q.text( ' [c]' ) if captain
175
+
176
+ if cards
177
+ q.text( ' ' )
178
+ q.pp( cards )
179
+ end
180
+
181
+ if sub
182
+ q.breakable ## can become either a space or a newline
183
+ q.pp( sub )
184
+ end
185
+ end
186
+ end
187
+ end # Lineup
188
+
189
+
190
+
191
+
192
+
193
+ Card = Struct.new( :name, :minute ) do
194
+ def as_json(*) to_s; end
195
+
196
+ def to_s
197
+ buf = String.new
198
+ buf << "#{name}"
199
+ buf << " #{minute.to_s}" if minute
200
+ buf
201
+ end
202
+
203
+ def pretty_print( q )
204
+ q.text( to_s )
205
+ end
206
+ end
207
+
208
+
209
+
210
+ ##
211
+ ## fix-fix-fix
212
+ ## change :sub to :lineup - why? why not?
213
+
214
+ Sub = Struct.new( :minute, :sub ) do
215
+ def as_json(*)
216
+ h = { 'lineup' => sub.as_json }
217
+ h['minute'] = minute.as_json if minute
218
+
219
+ ## ['<Sub>', h]
220
+ h # note - return "inline" json object (not tagged array with json object)
221
+ end
222
+
223
+ def pretty_print( q )
224
+ q.group( 0, '(', ')') do
225
+ q.text( "#{minute.to_s} " ) if minute
226
+ q.pp( sub )
227
+ end
228
+ end
229
+ end # Sub
230
+
231
+
232
+ end # class Parser
233
+ end # module SportDb
@@ -0,0 +1,202 @@
1
+
2
+ ####
3
+ # Parser support machinery (incl. node classes/abstract syntax tree)
4
+
5
+
6
+ module SportDb
7
+ class Parser
8
+
9
+
10
+ GroupDef = Struct.new( :name, :teams ) do
11
+
12
+ def as_json(*)
13
+ ["<GroupDef>", { 'name' => name.as_json,
14
+ 'teams' => teams.as_json }
15
+ ]
16
+ end
17
+
18
+ def pretty_print( q )
19
+ q.group( 4, '<GroupDef ', '>' ) do ## group( indent, open, close)
20
+ q.text( name )
21
+ q.text( " teams=" )
22
+ q.pp( teams )
23
+ end
24
+ end
25
+ end # GroupDef
26
+
27
+
28
+
29
+ ##
30
+ ## note - for now exclusive really
31
+ ## either date OR duration
32
+ RoundDef = Struct.new( :name, :date, :duration ) do
33
+
34
+ def as_json(*)
35
+ h = { 'name' => name.as_json }
36
+
37
+ h['date'] = date.as_json if date
38
+ h['duration'] = duration.as_json if duration
39
+
40
+ ["<RoundDef>", h]
41
+ end
42
+
43
+ def pretty_print( q )
44
+ q.group( 4, '<RoundDef ', '>' ) do
45
+ q.text( name )
46
+ if date
47
+ q.text( " date=")
48
+ q.pp( date )
49
+ end
50
+ if duration
51
+ q.text( " duration=")
52
+ q.pp( duration )
53
+ end
54
+ end
55
+ end
56
+
57
+ end # RoundDef
58
+
59
+
60
+ ##
61
+ ## note - for now exclusive really
62
+ ## either date OR year
63
+ DateHeader = Struct.new( :date, :year ) do
64
+
65
+ def as_json(*)
66
+ h = {}
67
+ h['date'] = date.as_json if date
68
+ h['year'] = year.as_json if year
69
+
70
+ ['<DateHeader>', h]
71
+ end
72
+
73
+
74
+ def pretty_print( q )
75
+ q.group( 4, '<DateHeader ', '>' ) do
76
+ q.pp( date ) if date
77
+ q.pp( year ) if year
78
+ end
79
+ end
80
+ end # DateHeader
81
+
82
+
83
+
84
+ DateHeaderLegs = Struct.new( :date1, :date2 ) do
85
+
86
+ def as_json(*)
87
+ ['<DateHeaderLegs>', { 'leg1' => date1.as_json,
88
+ 'leg2' => date2.as_json,
89
+ }
90
+ ]
91
+ end
92
+
93
+ def pretty_print( q )
94
+ q.group( 4, '<DateHeaderLegs ', '>' ) do
95
+ q.text( "leg1=" )
96
+ q.pp( date1 )
97
+ q.text( " leg2=" )
98
+ q.pp( date2 )
99
+ end
100
+ end
101
+ end # DateHeaderLegs
102
+
103
+
104
+
105
+ RoundOutline = Struct.new( :outline, :level ) do
106
+ def as_json(*)
107
+ ['<RoundOutline>', { 'outline' => outline.as_json,
108
+ 'level' => level.as_json, }
109
+ ]
110
+ end
111
+
112
+ def pretty_print( q )
113
+ q.group( 4, '<RoundOutline ', '>' ) do
114
+ q.text( outline )
115
+ q.text( " level=#{level}" )
116
+ end
117
+ end
118
+ end # RoundOutline
119
+
120
+
121
+
122
+
123
+
124
+ class BlankLine
125
+ def as_json(*)
126
+ ['<BlankLine>']
127
+ end
128
+
129
+ def pretty_print( q )
130
+ q.text( "<BlankLine>" )
131
+ end
132
+ end # BlankLine
133
+
134
+
135
+
136
+ NoteLine = Struct.new( :text ) do
137
+ def as_json(*)
138
+ ['<NoteLine>', { 'text' => text.as_json } ]
139
+ end
140
+ def pretty_print( q )
141
+ q.group( 4, '<NoteLine ', '>' ) do
142
+ q.text( text )
143
+ end
144
+ end
145
+ end # NoteLine
146
+
147
+
148
+ NotaBene = Struct.new( :text ) do
149
+ def as_json(*)
150
+ ['<NotaBene>', { 'text' => text.as_json } ]
151
+ end
152
+ def pretty_print( q )
153
+ q.group( 4, '<NotaBene ', '>' ) do
154
+ q.text( text )
155
+ end
156
+ end
157
+ end # NotaBene
158
+
159
+
160
+
161
+
162
+
163
+ ## todo/check - use a generic Heading instead of Heading1/2/3 - why? why not?
164
+ Heading1 = Struct.new( :text ) do
165
+ def as_json(*)
166
+ ['<Heading1>', { 'text' => text.as_json }]
167
+ end
168
+
169
+ def pretty_print( q )
170
+ q.group( 4, '<Heading1 ', '>' ) do
171
+ q.text( text )
172
+ end
173
+ end
174
+ end # Heading1
175
+
176
+ Heading2 = Struct.new( :text ) do
177
+ def as_json(*)
178
+ ['<Heading2>', { 'text' => text.as_json }]
179
+ end
180
+
181
+ def pretty_print( q )
182
+ q.group( 4, '<Heading2 ', '>' ) do
183
+ q.text( text )
184
+ end
185
+ end
186
+ end # Heading2
187
+
188
+ Heading3 = Struct.new( :text ) do
189
+ def as_json(*)
190
+ ['<Heading3>', { 'text' => text.as_json }]
191
+ end
192
+
193
+ def pretty_print( q )
194
+ q.group( 4, '<Heading3 ', '>' ) do
195
+ q.text( text )
196
+ end
197
+ end
198
+ end # Heading3
199
+
200
+
201
+ end # class Parser
202
+ end # module SportDb
@@ -0,0 +1,102 @@
1
+
2
+
3
+ module SportDb
4
+
5
+ class ParserResult
6
+ attr_reader :tree, :errors
7
+ def initialize( tree, errors=[] )
8
+ @tree, @errors = tree, errors
9
+ end
10
+
11
+ def ok?() @errors.size == 0; end
12
+ def nok?() !ok?; end
13
+ end # class ParserResult
14
+
15
+
16
+ class Parser
17
+ include Debuggable ## auto-adds debug?, _trace, _info, etc.
18
+
19
+ def initialize( txt )
20
+ @tree = []
21
+ @errors = []
22
+
23
+ lexer = Lexer.new( txt )
24
+ ## note - use tokenize_with_errors and add/collect tokenize errors
25
+ @tokens, @errors = lexer.tokenize_with_errors
26
+ ## pp @tokens
27
+ end
28
+
29
+
30
+ def next_token
31
+ tok = @tokens.shift
32
+ ## _trace( "next_token => #{tok.pretty_inspect}" )
33
+
34
+ ## convert to racc format single char literal tokens e.g. '@' etc.
35
+ ## note - literal token MUST be string (NOT symbol)
36
+ ## note - racc expects array with to items
37
+ ## - item[0] is the token id
38
+ ## - item[1] is the token value
39
+
40
+ ## note - returns nil for end-of-file !!!
41
+ tok = [tok.type, tok] if tok
42
+
43
+ tok
44
+ end
45
+
46
+
47
+ # on_error do |error_token_id, error_value, value_stack|
48
+ # puts "Parse error on token: #{error_token_id}, value: #{error_value}"
49
+ # end
50
+
51
+ def parse_with_errors
52
+ _trace( "start parse:" )
53
+ do_parse
54
+
55
+ _trace( "#{@tree.size} parse tree node(s): " + @tree.pretty_inspect)
56
+
57
+ [@tree, @errors]
58
+ end
59
+
60
+
61
+ def parse ## convenience shortcut (ignores errors)
62
+ puts "[DEPRECATED] parse; use new (porcelain/public) Fbtxt.parse api!!"
63
+ tree, _ = parse_with_errors
64
+ tree
65
+ end
66
+
67
+ attr_reader :errors
68
+ def errors?
69
+ puts "[DEPRECATED] errors?; use new (porcelain/public) Fbtxt.parse api!!"
70
+ @errors.size > 0;
71
+ end
72
+
73
+
74
+ def on_error(error_token_id, error_value, value_stack)
75
+ ## note - get error_token (as string) e.g. MINUTE, DATE, etc.
76
+ error_token = Racc_token_to_s_table[error_token_id]
77
+
78
+ msg = "parse error on token: #{error_token}"
79
+
80
+ ## add error_value - might be Lexer::Token or value
81
+ if error_value.is_a?( Lexer::Token)
82
+ if error_value.type == :NEWLINE
83
+ ## skip print of "literal" newline
84
+ else
85
+ msg << " >#{error_value.text}<"
86
+ end
87
+ msg << " @#{error_value.lineno}"
88
+ else
89
+ msg << " >#{error_value.inspect}"
90
+ end
91
+
92
+ ## add stack - why? why not?
93
+ ## "stack: #{value_stack.inspect}"
94
+
95
+ _error( msg )
96
+
97
+ @errors << msg
98
+ end
99
+
100
+
101
+ end # class Parser
102
+ end # module SportDb