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 +4 -4
- data/CHANGELOG.md +1 -1
- data/Manifest.txt +8 -3
- data/Rakefile +23 -0
- data/lib/sportdb/parser/debuggable.rb +53 -0
- data/lib/sportdb/parser/lexer-logger.rb +20 -0
- data/lib/sportdb/parser/lexer-on_goal.rb +1 -6
- data/lib/sportdb/parser/lexer-on_prop_misc.rb +14 -1
- data/lib/sportdb/parser/lexer-on_top.rb +8 -0
- data/lib/sportdb/parser/lexer-tokenize.rb +22 -3
- data/lib/sportdb/parser/lexer.rb +27 -51
- data/lib/sportdb/parser/parse_tree--core.rb +29 -0
- data/lib/sportdb/parser/parse_tree-match.rb +309 -0
- data/lib/sportdb/parser/parse_tree-props.rb +233 -0
- data/lib/sportdb/parser/parse_tree.rb +202 -0
- data/lib/sportdb/parser/parser-top.rb +102 -0
- data/lib/sportdb/parser/parser.rb +1069 -909
- data/lib/sportdb/parser/token-geo.rb +1 -1
- data/lib/sportdb/parser/token-goals.rb +0 -8
- data/lib/sportdb/parser/token-prop.rb +40 -0
- data/lib/sportdb/parser/token.rb +33 -0
- data/lib/sportdb/parser/version.rb +2 -2
- data/lib/sportdb/parser.rb +45 -47
- metadata +10 -5
- data/lib/sportdb/parser/racc_parser.rb +0 -92
- data/lib/sportdb/parser/racc_tree.rb +0 -524
- /data/lib/sportdb/parser/{parser_runtime.rb → parser-runtime.rb} +0 -0
|
@@ -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
|