fbtxt-parser 0.9.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 +7 -0
- data/CHANGELOG.md +4 -0
- data/Manifest.txt +50 -0
- data/README.md +22 -0
- data/Rakefile +53 -0
- data/lib/fbtxt/parser/debuggable.rb +53 -0
- data/lib/fbtxt/parser/lexer-logger.rb +20 -0
- data/lib/fbtxt/parser/lexer-on_goal.rb +167 -0
- data/lib/fbtxt/parser/lexer-on_group_def.rb +31 -0
- data/lib/fbtxt/parser/lexer-on_prop_lineup.rb +79 -0
- data/lib/fbtxt/parser/lexer-on_prop_misc.rb +123 -0
- data/lib/fbtxt/parser/lexer-on_prop_penalties.rb +40 -0
- data/lib/fbtxt/parser/lexer-on_round_def.rb +37 -0
- data/lib/fbtxt/parser/lexer-on_top.rb +133 -0
- data/lib/fbtxt/parser/lexer-prep_doc.rb +131 -0
- data/lib/fbtxt/parser/lexer-prep_line.rb +63 -0
- data/lib/fbtxt/parser/lexer-tokenize.rb +468 -0
- data/lib/fbtxt/parser/lexer.rb +231 -0
- data/lib/fbtxt/parser/lexer_buffer.rb +68 -0
- data/lib/fbtxt/parser/lexer_token.rb +126 -0
- data/lib/fbtxt/parser/parse_tree--core.rb +29 -0
- data/lib/fbtxt/parser/parse_tree-match.rb +309 -0
- data/lib/fbtxt/parser/parse_tree-props.rb +233 -0
- data/lib/fbtxt/parser/parse_tree.rb +202 -0
- data/lib/fbtxt/parser/parser-runtime.rb +379 -0
- data/lib/fbtxt/parser/parser-top.rb +102 -0
- data/lib/fbtxt/parser/parser.rb +2343 -0
- data/lib/fbtxt/parser/token-date--helpers.rb +130 -0
- data/lib/fbtxt/parser/token-date--names.rb +108 -0
- data/lib/fbtxt/parser/token-date.rb +200 -0
- data/lib/fbtxt/parser/token-date_duration.rb +171 -0
- data/lib/fbtxt/parser/token-geo.rb +134 -0
- data/lib/fbtxt/parser/token-goals--helpers.rb +114 -0
- data/lib/fbtxt/parser/token-goals.rb +306 -0
- data/lib/fbtxt/parser/token-group.rb +29 -0
- data/lib/fbtxt/parser/token-note.rb +40 -0
- data/lib/fbtxt/parser/token-prop.rb +309 -0
- data/lib/fbtxt/parser/token-prop_name.rb +83 -0
- data/lib/fbtxt/parser/token-round.rb +88 -0
- data/lib/fbtxt/parser/token-score--helpers.rb +189 -0
- data/lib/fbtxt/parser/token-score.rb +60 -0
- data/lib/fbtxt/parser/token-score_full.rb +331 -0
- data/lib/fbtxt/parser/token-score_fuller.rb +434 -0
- data/lib/fbtxt/parser/token-score_legs.rb +59 -0
- data/lib/fbtxt/parser/token-status.rb +192 -0
- data/lib/fbtxt/parser/token-status_inline.rb +112 -0
- data/lib/fbtxt/parser/token-text.rb +221 -0
- data/lib/fbtxt/parser/token-time.rb +144 -0
- data/lib/fbtxt/parser/token.rb +224 -0
- data/lib/fbtxt/parser/version.rb +24 -0
- data/lib/fbtxt/parser.rb +140 -0
- metadata +145 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
|
|
2
|
+
module Fbtxt
|
|
3
|
+
|
|
4
|
+
## note - Tokens was placed inside Lexer - keep "top-level" for now inside Fbtxt
|
|
5
|
+
## for easier reuse with (new) lexer variants!!
|
|
6
|
+
|
|
7
|
+
## transforms
|
|
8
|
+
##
|
|
9
|
+
## Netherlands 1-2 (1-1) England
|
|
10
|
+
## => text => team
|
|
11
|
+
## score|vs
|
|
12
|
+
## text => team
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## token iter/find better name
|
|
17
|
+
## e.g. TokenBuffer/Scanner or such ??
|
|
18
|
+
class Tokens
|
|
19
|
+
def initialize( tokens )
|
|
20
|
+
@tokens = tokens
|
|
21
|
+
@pos = 0
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def pos() @pos; end
|
|
25
|
+
def eos?() @pos >= @tokens.size; end
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
## pattern e.g. [:TEXT, [:VS,:SCORE], :TEXT]
|
|
31
|
+
def match?( *pattern )
|
|
32
|
+
## puts " starting match? #{pattern.inspect} @ #{@pos}"
|
|
33
|
+
pattern.each_with_index do |types,offset|
|
|
34
|
+
tok = peek(offset)
|
|
35
|
+
return false if tok.nil? ## no more tokens (cannot match)
|
|
36
|
+
|
|
37
|
+
## if single symbol wrap in array
|
|
38
|
+
types = types.is_a?(Array) ? types : [types]
|
|
39
|
+
return false unless types.include?( tok.type )
|
|
40
|
+
end
|
|
41
|
+
true
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def cur() peek(0); end
|
|
47
|
+
|
|
48
|
+
def peek(offset=1)
|
|
49
|
+
## return nil if eos
|
|
50
|
+
if @pos+offset >= @tokens.size
|
|
51
|
+
nil
|
|
52
|
+
else
|
|
53
|
+
@tokens[@pos+offset]
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def next
|
|
58
|
+
# if @pos >= @tokens.size
|
|
59
|
+
# raise ArgumentError, "end of array - #{@pos} >= #{@tokens.size}"
|
|
60
|
+
# end
|
|
61
|
+
# throw (standard) end of iteration here why? why not?
|
|
62
|
+
|
|
63
|
+
t = @tokens[@pos]
|
|
64
|
+
@pos += 1
|
|
65
|
+
t
|
|
66
|
+
end
|
|
67
|
+
end # class Tokens
|
|
68
|
+
end # module Fbtxt
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
module Fbtxt
|
|
2
|
+
class Lexer
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Token
|
|
7
|
+
|
|
8
|
+
## Token.newline( lineno: 1, offset: [1,2] )
|
|
9
|
+
## maps to =>
|
|
10
|
+
## Token.new( :NEWLINE, "\n", lineno: 1, offset: [1,2])
|
|
11
|
+
##
|
|
12
|
+
## use self.nl ?
|
|
13
|
+
def self.newline( lineno:, offset: [])
|
|
14
|
+
new( :NEWLINE, "\n", lineno: lineno, offset: offset )
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
## Token.literal( ",", lineno: 4, offset: [5,6])
|
|
18
|
+
## # maps to =>
|
|
19
|
+
## Token.new( ",", ",", lineno: 4, offset: [5,6])
|
|
20
|
+
##
|
|
21
|
+
## use self.lit?
|
|
22
|
+
def self.literal( literal, lineno:, offset: [])
|
|
23
|
+
new( literal, literal, lineno: lineno, offset: offset )
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
## or use virt or pseudo - why? why not?
|
|
27
|
+
def self.virtual( type, lineno:, offset: [])
|
|
28
|
+
## note - offset (start/end) should be same number (zero-width assertions!!)
|
|
29
|
+
## e.g. :GOALS_COMPAT, "<|GOALS_COMPAT|>"
|
|
30
|
+
new( type, '', lineno: lineno, offset: offset )
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
attr_reader :type, :text,
|
|
35
|
+
:lineno, :offset
|
|
36
|
+
|
|
37
|
+
def initialize( type, text='',
|
|
38
|
+
lineno:, offset: [],
|
|
39
|
+
value: nil )
|
|
40
|
+
@type = type
|
|
41
|
+
@text = text # note - lexeme (string from source)
|
|
42
|
+
@lineno = lineno # note - lineno (integer number - not line as string) !!!
|
|
43
|
+
|
|
44
|
+
raise TypeError, "type Array required for offset; got #{offset.inspect}" unless offset.is_a?( Array )
|
|
45
|
+
@offset = offset # note - for now char offset [start,end] in line (NOT absolute!!)
|
|
46
|
+
# maybe latter add MatchData#byteoffset instead - why? why not?
|
|
47
|
+
@value = value # might be (union of) string/array/hash
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def value
|
|
51
|
+
## note - if value is not set (nil) return text (lexeme)
|
|
52
|
+
## no need to duplicate text as value
|
|
53
|
+
@value.nil? ? @text : @value
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
## note: do NOT use as_text/text to avoid confusion with (raw) text (lexeme)
|
|
58
|
+
##
|
|
59
|
+
## use
|
|
60
|
+
## as_str -- value (as String)
|
|
61
|
+
## as_int -- value (as Integer)
|
|
62
|
+
## as_hash -- value (as Hash)
|
|
63
|
+
## as_ary -- value (as Array)
|
|
64
|
+
|
|
65
|
+
def as_str
|
|
66
|
+
raise TypeError, "token value #{@value.inspect} is #{@value.class.name} NOT string; sorry" if @value && !@value.is_a?(String)
|
|
67
|
+
## note - if value is not set (nil) return text (lexeme)
|
|
68
|
+
## no need to duplicate text as value
|
|
69
|
+
@value.nil? ? @text : @value
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def as_int
|
|
73
|
+
raise TypeError, "token value #{@value.inspect} is #{@value.class.name} NOT integer; sorry" if !@value.is_a?(Integer)
|
|
74
|
+
@value
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def as_hash
|
|
78
|
+
raise TypeError, "token value #{@value.inspect} is #{@value.class.name} NOT hash; sorry" if !@value.is_a?(Hash)
|
|
79
|
+
@value
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def as_ary
|
|
83
|
+
raise TypeError, "token value #{@value.inspect} is #{@value.class.name} NOT array; sorry" if !@value.is_a?(Array)
|
|
84
|
+
@value
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def to_legacy
|
|
89
|
+
## return old "legacy" array format
|
|
90
|
+
if @value.nil?
|
|
91
|
+
[@type, @text]
|
|
92
|
+
else
|
|
93
|
+
[@type, [@text, @value]]
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
## pretty print
|
|
99
|
+
def pretty_print( printer )
|
|
100
|
+
## check for literal e.g. "," etc.
|
|
101
|
+
if @type.is_a?( String ) && @type == @text && @value.nil?
|
|
102
|
+
printer.text( "[#{@type.inspect}" )
|
|
103
|
+
elsif @type.is_a?( Symbol ) && @text == '' && @value.nil?
|
|
104
|
+
## assume virtual token (zero-width)
|
|
105
|
+
## use <!...!> style
|
|
106
|
+
printer.text( "[<|#{@type}|>" )
|
|
107
|
+
else
|
|
108
|
+
printer.text( "[#{@type.inspect} #{@text.inspect}" )
|
|
109
|
+
printer.text( ", #{value.inspect}") if @value
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
printer.text( " @#{@lineno}" )
|
|
114
|
+
## note - for now print only start_offset (offset[0])
|
|
115
|
+
## to keep dump/output shorter
|
|
116
|
+
## note - start counting columns at one (NOT zero), thus, add +1 !!
|
|
117
|
+
printer.text( ":#{@offset[0]+1}" ) if @offset.is_a?(Array) && @offset.size == 2
|
|
118
|
+
printer.text( "]" )
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
end # class Token
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
end # class Lexer
|
|
126
|
+
end # module Fbtxt
|
|
@@ -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
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
module Fbtxt
|
|
2
|
+
class Parser
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
MatchLineBye = Struct.new( :team, :note ) do
|
|
7
|
+
def pretty_print( q )
|
|
8
|
+
q.group( 4, '<MatchLineBye ', '>') do ## group( indent, open, close)
|
|
9
|
+
q.text( "#{team} bye")
|
|
10
|
+
if note
|
|
11
|
+
q.text( " note=" )
|
|
12
|
+
q.pp( note )
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end # MatchLineBye
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
MatchLineLegs = Struct.new( :team1, :team2,
|
|
22
|
+
:score ) do
|
|
23
|
+
def pretty_print( q )
|
|
24
|
+
q.group( 4, '<MatchLineLegs ', '>') do ## group( indent, open, close)
|
|
25
|
+
q.text( "#{team1} v #{team2}")
|
|
26
|
+
|
|
27
|
+
members.zip(values) do |name, value|
|
|
28
|
+
next if [:team1, :team2].include?( name )
|
|
29
|
+
next if value.nil?
|
|
30
|
+
|
|
31
|
+
q.breakable
|
|
32
|
+
q.text( "#{name}=" )
|
|
33
|
+
q.pp( value )
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
#
|
|
41
|
+
# note: use two status attributes for now
|
|
42
|
+
# 1) inline_status and 2) (note_)status
|
|
43
|
+
# for now e.g. A abd. B vs A v B [abadoned]
|
|
44
|
+
# A 3-0 awd B vs A 3-0 B [awarded]
|
|
45
|
+
# note - BOTH might be present at the same time
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
MatchLine = Struct.new( :header, :tty, ## tty = TELETYPE MODE for teams and score!!
|
|
49
|
+
:num, :date, :time, :time_local, :year,
|
|
50
|
+
:team1, :team2,
|
|
51
|
+
:score,
|
|
52
|
+
:status, :status_inline, :status_note,
|
|
53
|
+
:round_inline_short, :round_inline_big,
|
|
54
|
+
:geo,
|
|
55
|
+
:neutral, ## true/false - NOT -home/away - neutral ground
|
|
56
|
+
:note,
|
|
57
|
+
:att ) do ## change to geos - why? why not?
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def as_json(*)
|
|
61
|
+
h = { 'team1' => team1.as_json,
|
|
62
|
+
'team2' => team2.as_json,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
members.zip(values) do |name, value|
|
|
66
|
+
next if [:team1, :team2].include?( name )
|
|
67
|
+
next if value.nil?
|
|
68
|
+
|
|
69
|
+
h[name.to_s] = value.as_json
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
['<MatchLine>', h]
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def pretty_print( q )
|
|
77
|
+
q.group( 4, '<MatchLine ', '>') do ## group( indent, open, close)
|
|
78
|
+
q.text( "#{team1} v #{team2}")
|
|
79
|
+
|
|
80
|
+
members.zip(values) do |name, value|
|
|
81
|
+
next if [:team1, :team2].include?( name )
|
|
82
|
+
next if value.nil?
|
|
83
|
+
|
|
84
|
+
q.breakable
|
|
85
|
+
q.text( "#{name}=" )
|
|
86
|
+
q.pp( value )
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
end # struct MatchLine
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
GoalLine = Struct.new( :goals ) do
|
|
96
|
+
|
|
97
|
+
def as_json(*)
|
|
98
|
+
['<GoalLine>', { 'goals' => goals.as_json }]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def pretty_print( q )
|
|
102
|
+
q.group( 4, '<GoalLine ', '>') do
|
|
103
|
+
q.text( "goals=" )
|
|
104
|
+
q.pp( goals )
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end # GoalLine
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
###
|
|
112
|
+
## change/rename Goal to GoalScorer - why? why not?
|
|
113
|
+
|
|
114
|
+
Goal = Struct.new( :player, :minutes, :count ) do
|
|
115
|
+
|
|
116
|
+
def as_json(*)
|
|
117
|
+
h = { 'player' => player.as_json }
|
|
118
|
+
|
|
119
|
+
h['count'] = count.as_json if count
|
|
120
|
+
h['minutes'] = minutes.as_json if minutes && !minutes.empty?
|
|
121
|
+
|
|
122
|
+
## ['<Goal>', h]
|
|
123
|
+
h # note - return "inline" json object
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def to_s
|
|
128
|
+
buf = String.new
|
|
129
|
+
buf << "#{player}"
|
|
130
|
+
if count
|
|
131
|
+
buf << (" " + count.inspect + ",")
|
|
132
|
+
else
|
|
133
|
+
if minutes.nil? || minutes.empty?
|
|
134
|
+
## add nothing if no minutes available/present
|
|
135
|
+
else
|
|
136
|
+
buf << " "
|
|
137
|
+
buf << minutes.map { |min| min.to_s }.join(' ')
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
buf
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def pretty_print( q )
|
|
144
|
+
q.text( to_s )
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
## check - use a different name e.g. GoalLineScore or such - why? why not?
|
|
150
|
+
GoalLineAlt = Struct.new( :goals ) do
|
|
151
|
+
def pretty_print( q )
|
|
152
|
+
q.group( 4, '<GoalLineAlt ', '>') do
|
|
153
|
+
q.text( "goals=" )
|
|
154
|
+
q.pp( goals )
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end # GoalLineAlt
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
##
|
|
161
|
+
## score and player REQUIRED
|
|
162
|
+
## note - (goal) minute is optional
|
|
163
|
+
## if no minute goal type is optional e.g. "standalone" (p), (og), etc.
|
|
164
|
+
|
|
165
|
+
GoalAlt = Struct.new( :score, :player, :minute, :goal_type ) do
|
|
166
|
+
def to_s
|
|
167
|
+
buf = String.new
|
|
168
|
+
buf << "#{score[0]}-#{score[1]}"
|
|
169
|
+
buf << " #{player}"
|
|
170
|
+
buf << " #{minute}" if minute
|
|
171
|
+
buf << " #{goal_type}" if goal_type
|
|
172
|
+
buf
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def pretty_print( q )
|
|
176
|
+
q.text( to_s )
|
|
177
|
+
end
|
|
178
|
+
end # GoalAlt
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
GoalLineCompat = Struct.new( :goals ) do
|
|
183
|
+
def pretty_print( q )
|
|
184
|
+
q.group( 4, '<GoalLineCompat ', '>') do
|
|
185
|
+
q.text( "goals=" )
|
|
186
|
+
q.pp( goals )
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end # GoalLineCompat
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
##
|
|
193
|
+
## minute and player REQUIRED
|
|
194
|
+
## note - score (e.g. 1-1) is optional
|
|
195
|
+
## goal type is optional e.g. "standalone" (p), (og), etc.
|
|
196
|
+
|
|
197
|
+
GoalCompat = Struct.new( :score, :player, :minute, :goal_type ) do
|
|
198
|
+
def to_s
|
|
199
|
+
buf = String.new
|
|
200
|
+
buf << "#{minute}"
|
|
201
|
+
buf << " #{player}"
|
|
202
|
+
buf << " #{goal_type}" if goal_type
|
|
203
|
+
buf << " #{score[0]}-#{score[1]}" if score
|
|
204
|
+
buf
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def pretty_print( q )
|
|
208
|
+
q.text( to_s )
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
## FIX/FIX/FIX
|
|
215
|
+
## split into Minute and
|
|
216
|
+
## GoalMinute (Minute+GoalType)
|
|
217
|
+
##
|
|
218
|
+
## fix - move :og, :pen to Goal if possible - why? why not?
|
|
219
|
+
## or change to GoalMinute ???
|
|
220
|
+
##
|
|
221
|
+
## fix!!! split into GoalMinute and Minute
|
|
222
|
+
## goal_minute incl. :og, :pen, :freekick, :header,
|
|
223
|
+
## seconds etc.
|
|
224
|
+
##
|
|
225
|
+
## GoalMinute = Minute + GoalType !!!
|
|
226
|
+
|
|
227
|
+
GoalMinute = Struct.new( :m, :offset, :secs,
|
|
228
|
+
:og, :pen, :header, :freekick,
|
|
229
|
+
) do
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def as_json(*) to_s; end
|
|
233
|
+
|
|
234
|
+
def to_s
|
|
235
|
+
buf = String.new
|
|
236
|
+
buf << "#{m}"
|
|
237
|
+
buf << "'"
|
|
238
|
+
buf << "+#{offset}" if offset
|
|
239
|
+
buf << "(og)" if og
|
|
240
|
+
buf << "(p)" if pen
|
|
241
|
+
buf << "(f)" if freekick
|
|
242
|
+
buf << "(h)" if header
|
|
243
|
+
buf << " (#{secs} secs)" if secs
|
|
244
|
+
buf
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def pretty_print( q )
|
|
248
|
+
q.text( to_s )
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
### quick hack:
|
|
252
|
+
### split struct into Minute+GoalType structs
|
|
253
|
+
def to_minute
|
|
254
|
+
Minute.new( m: m,
|
|
255
|
+
offset: offset,
|
|
256
|
+
secs: secs )
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def to_goal_type
|
|
260
|
+
if og || pen || header || freekick
|
|
261
|
+
GoalType.new( og: og,
|
|
262
|
+
pen: pen,
|
|
263
|
+
header: header,
|
|
264
|
+
freekick: freekick )
|
|
265
|
+
else
|
|
266
|
+
nil ## note - return nil; if no goal type present!!
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
GoalType = Struct.new( :og, :pen, :header, :freekick ) do
|
|
273
|
+
def to_s
|
|
274
|
+
buf = String.new
|
|
275
|
+
buf << "(og)" if og
|
|
276
|
+
buf << "(pen)" if pen
|
|
277
|
+
buf << "(f)" if freekick
|
|
278
|
+
buf << "(h)" if header
|
|
279
|
+
buf
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def pretty_print( q )
|
|
283
|
+
q.text( to_s )
|
|
284
|
+
end
|
|
285
|
+
end # GoalType
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
Minute = Struct.new( :m, :offset, :secs ) do
|
|
290
|
+
def as_json(*) to_s; end
|
|
291
|
+
|
|
292
|
+
def to_s
|
|
293
|
+
buf = String.new
|
|
294
|
+
buf << "#{m}"
|
|
295
|
+
buf << "'"
|
|
296
|
+
buf << "+#{offset}" if offset
|
|
297
|
+
buf << "/#{secs} secs" if secs
|
|
298
|
+
buf
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def pretty_print( q )
|
|
302
|
+
q.text( to_s )
|
|
303
|
+
end
|
|
304
|
+
end # Minute
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
end # class Parser
|
|
309
|
+
end # module Fbtxt
|