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.
@@ -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