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
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 5c2c69264e5b2d3d51bf412351b1221e65f35273a78e382e59bfeb460a762628
|
|
4
|
+
data.tar.gz: e844cd20aa7e00cb5c2840d108bd3597efa065867ac0c2dbd7fbd0b4cfa8a9d1
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 46bc709e594ed668fb62c70bd36e96ad212584fc0d9d961d88a81220a6a0586b5c418cc8310f5f0fcba3906fce15a3d5b10837c8be876d0723496e4b23558495
|
|
7
|
+
data.tar.gz: a30808c1b3815a9bdc74efc59e38354a42da8d6e265ed6283752df5ae6a6ebee2d56265a78d09554267ce6bf7b40091a6733d19c61ef10c3499a591de0a21df7
|
data/CHANGELOG.md
ADDED
data/Manifest.txt
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
CHANGELOG.md
|
|
2
|
+
Manifest.txt
|
|
3
|
+
README.md
|
|
4
|
+
Rakefile
|
|
5
|
+
lib/fbtxt/parser.rb
|
|
6
|
+
lib/fbtxt/parser/debuggable.rb
|
|
7
|
+
lib/fbtxt/parser/lexer-logger.rb
|
|
8
|
+
lib/fbtxt/parser/lexer-on_goal.rb
|
|
9
|
+
lib/fbtxt/parser/lexer-on_group_def.rb
|
|
10
|
+
lib/fbtxt/parser/lexer-on_prop_lineup.rb
|
|
11
|
+
lib/fbtxt/parser/lexer-on_prop_misc.rb
|
|
12
|
+
lib/fbtxt/parser/lexer-on_prop_penalties.rb
|
|
13
|
+
lib/fbtxt/parser/lexer-on_round_def.rb
|
|
14
|
+
lib/fbtxt/parser/lexer-on_top.rb
|
|
15
|
+
lib/fbtxt/parser/lexer-prep_doc.rb
|
|
16
|
+
lib/fbtxt/parser/lexer-prep_line.rb
|
|
17
|
+
lib/fbtxt/parser/lexer-tokenize.rb
|
|
18
|
+
lib/fbtxt/parser/lexer.rb
|
|
19
|
+
lib/fbtxt/parser/lexer_buffer.rb
|
|
20
|
+
lib/fbtxt/parser/lexer_token.rb
|
|
21
|
+
lib/fbtxt/parser/parse_tree--core.rb
|
|
22
|
+
lib/fbtxt/parser/parse_tree-match.rb
|
|
23
|
+
lib/fbtxt/parser/parse_tree-props.rb
|
|
24
|
+
lib/fbtxt/parser/parse_tree.rb
|
|
25
|
+
lib/fbtxt/parser/parser-runtime.rb
|
|
26
|
+
lib/fbtxt/parser/parser-top.rb
|
|
27
|
+
lib/fbtxt/parser/parser.rb
|
|
28
|
+
lib/fbtxt/parser/token-date--helpers.rb
|
|
29
|
+
lib/fbtxt/parser/token-date--names.rb
|
|
30
|
+
lib/fbtxt/parser/token-date.rb
|
|
31
|
+
lib/fbtxt/parser/token-date_duration.rb
|
|
32
|
+
lib/fbtxt/parser/token-geo.rb
|
|
33
|
+
lib/fbtxt/parser/token-goals--helpers.rb
|
|
34
|
+
lib/fbtxt/parser/token-goals.rb
|
|
35
|
+
lib/fbtxt/parser/token-group.rb
|
|
36
|
+
lib/fbtxt/parser/token-note.rb
|
|
37
|
+
lib/fbtxt/parser/token-prop.rb
|
|
38
|
+
lib/fbtxt/parser/token-prop_name.rb
|
|
39
|
+
lib/fbtxt/parser/token-round.rb
|
|
40
|
+
lib/fbtxt/parser/token-score--helpers.rb
|
|
41
|
+
lib/fbtxt/parser/token-score.rb
|
|
42
|
+
lib/fbtxt/parser/token-score_full.rb
|
|
43
|
+
lib/fbtxt/parser/token-score_fuller.rb
|
|
44
|
+
lib/fbtxt/parser/token-score_legs.rb
|
|
45
|
+
lib/fbtxt/parser/token-status.rb
|
|
46
|
+
lib/fbtxt/parser/token-status_inline.rb
|
|
47
|
+
lib/fbtxt/parser/token-text.rb
|
|
48
|
+
lib/fbtxt/parser/token-time.rb
|
|
49
|
+
lib/fbtxt/parser/token.rb
|
|
50
|
+
lib/fbtxt/parser/version.rb
|
data/README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# fbtxt-parser - football.txt match parser (& tokenizer)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
## Usage
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
``` ruby
|
|
8
|
+
result = Fbtxt.lex( txt ) # returns Fbtxt::LexerResult w/ tokens, errors/ok?/nok?/etc.
|
|
9
|
+
result = Fbtxt.parse( txt ) # returns Fbtxt::ParserResult w/ tree, errors/ok?/nok?/etc.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
#-or-
|
|
13
|
+
|
|
14
|
+
lexer = Fbtxt::Lexer.new(txt)
|
|
15
|
+
tokens, errors = lexer.tokenize_with_errors
|
|
16
|
+
|
|
17
|
+
parser = Fbtxt::Parser.new(txt)
|
|
18
|
+
tree, errors = parser.parse_with_errors
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
note - for porcelain / higher-level apis see ``Fbtxt::Document`.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require 'hoe'
|
|
2
|
+
require './lib/fbtxt/parser/version.rb'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
Hoe.spec 'fbtxt-parser' do
|
|
6
|
+
|
|
7
|
+
self.version = Fbtxt::Module::Parser::VERSION
|
|
8
|
+
|
|
9
|
+
self.summary = "fbtxt-parser - football.txt match parser (& tokenizer)"
|
|
10
|
+
self.description = summary
|
|
11
|
+
|
|
12
|
+
self.urls = { home: 'https://github.com/sportdb/sport.db' }
|
|
13
|
+
|
|
14
|
+
self.author = 'Gerald Bauer'
|
|
15
|
+
self.email = 'gerald.bauer@gmail.com'
|
|
16
|
+
|
|
17
|
+
# switch extension to .markdown for gihub formatting
|
|
18
|
+
self.readme_file = 'README.md'
|
|
19
|
+
self.history_file = 'CHANGELOG.md'
|
|
20
|
+
|
|
21
|
+
self.licenses = ['Public Domain']
|
|
22
|
+
|
|
23
|
+
self.extra_deps = [
|
|
24
|
+
['cocos', '>= 0.4.1'],
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
self.spec_extras = {
|
|
28
|
+
required_ruby_version: '>= 3.1.0'
|
|
29
|
+
}
|
|
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/fbtxt/parser/parser.rb'
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
file 'lib/fbtxt/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 Fbtxt
|
|
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 Fbtxt
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
module Fbtxt
|
|
2
|
+
class Lexer
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
GOAL_RE = Regexp.union(
|
|
8
|
+
SPACES_RE,
|
|
9
|
+
GOAL_MINUTE_RE,
|
|
10
|
+
GOAL_MINUTE_NA_RE,
|
|
11
|
+
GOAL_COUNT_RE,
|
|
12
|
+
PROP_NAME_RE, ## note - (re)use prop name for now for (player) name
|
|
13
|
+
GOAL_SEP_ALT_RE, ## note - add dash (-) with (required) spaces
|
|
14
|
+
/ (?<sym> [;,)]) /x
|
|
15
|
+
## todo/fix - add ANY_RE !!!!
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _on_goal( m, ctx: )
|
|
20
|
+
|
|
21
|
+
if m[:space] || m[:spaces]
|
|
22
|
+
nil ## skip space(s)
|
|
23
|
+
elsif m[:goal_sep_alt]
|
|
24
|
+
# was: [:GOAL_SEP_ALT, "<|GOAL_SEP_ALT|>" ] ## e.g. dash (-) WITH leading & trailing space required
|
|
25
|
+
Token.new( :GOAL_SEP_ALT, m[:goal_sep_alt],
|
|
26
|
+
lineno: ctx.lineno, offset: m.offset(:goal_sep_alt))
|
|
27
|
+
elsif m[:prop_name] ## note - change prop_name to player
|
|
28
|
+
Token.new( :PLAYER, m[:name],
|
|
29
|
+
lineno: ctx.lineno, offset: m.offset(:name))
|
|
30
|
+
elsif m[:goal_minute]
|
|
31
|
+
Token.new( :GOAL_MINUTE, m[:goal_minute],
|
|
32
|
+
lineno: ctx.lineno, offset: m.offset(:goal_minute),
|
|
33
|
+
value: _build_goal_minute( m ))
|
|
34
|
+
elsif m[:goal_minute_na]
|
|
35
|
+
## note - (re)use GOAL_MINUTE token; no extra GOAL_MINUTE_NA or such - why? why not?
|
|
36
|
+
## make sure to handle 'm' => nil upstream!!!
|
|
37
|
+
## change to 999 or -1 or such - why? why not?
|
|
38
|
+
Token.new( :GOAL_MINUTE, m[:goal_minute_na],
|
|
39
|
+
lineno: ctx.lineno, offset: m.offset(:goal_minute_na),
|
|
40
|
+
value: _build_goal_minute_na( m ))
|
|
41
|
+
elsif m[:goal_count]
|
|
42
|
+
Token.new( :GOAL_COUNT, m[:goal_count],
|
|
43
|
+
lineno: ctx.lineno, offset: m.offset(:goal_count),
|
|
44
|
+
value: _build_goal_count( m ))
|
|
45
|
+
elsif m[:sym]
|
|
46
|
+
case m[:sym]
|
|
47
|
+
when ')' ## leave goal mode!!
|
|
48
|
+
_trace( "LEAVE GOAL_RE MODE" )
|
|
49
|
+
@re = RE
|
|
50
|
+
## note - use/return GOAL_END token - change to GOAL_END_PAREN(THESIS)
|
|
51
|
+
## or GOAL_PAREN_CLOSE/END ???
|
|
52
|
+
## fix - use ) too - why? why not?
|
|
53
|
+
## was: [:GOALS_END, '<|GOALS_END|>']
|
|
54
|
+
Token.virtual( :GOALS_END, lineno: ctx.lineno )
|
|
55
|
+
else
|
|
56
|
+
Token.literal( m[:sym], lineno: ctx.lineno, offset: m.offset(:sym))
|
|
57
|
+
end
|
|
58
|
+
else
|
|
59
|
+
ctx.warn_on_else( m, mode: 'GOAL' )
|
|
60
|
+
nil
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
GOAL_ALT_RE = Regexp.union(
|
|
68
|
+
SPACES_RE,
|
|
69
|
+
SCORE_RE, ## e.g. 1-0, 0-1, etc.
|
|
70
|
+
GOAL_MINUTE_RE,
|
|
71
|
+
GOAL_TYPE_RE,
|
|
72
|
+
PROP_NAME_RE, ## note - (re)use prop name for now for (player) name
|
|
73
|
+
/ (?<sym> [,)]) /x ## note - no semicolon (;)
|
|
74
|
+
## todo/fix - add ANY_RE !!!!
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
def _on_goal_alt( m, ctx: )
|
|
78
|
+
|
|
79
|
+
if m[:space] || m[:spaces]
|
|
80
|
+
nil ## skip space(s)
|
|
81
|
+
elsif m[:prop_name] ## note - change prop_name to player
|
|
82
|
+
Token.new(:PLAYER, m[:name],
|
|
83
|
+
lineno: ctx.lineno, offset: m.offset(:name))
|
|
84
|
+
elsif m[:goal_minute]
|
|
85
|
+
Token.new( :GOAL_MINUTE, m[:goal_minute],
|
|
86
|
+
lineno: ctx.lineno, offset: m.offset(:goal_minute),
|
|
87
|
+
value: _build_goal_minute( m ))
|
|
88
|
+
elsif m[:goal_type]
|
|
89
|
+
Token.new( :GOAL_TYPE,m[:goal_type],
|
|
90
|
+
lineno: ctx.lineno, offset: m.offset(:goal_type),
|
|
91
|
+
value: _build_goal_type( m ))
|
|
92
|
+
elsif m[:score]
|
|
93
|
+
Token.new( :SCORE, m[:score],
|
|
94
|
+
lineno: ctx.lineno, offset: m.offset(:score),
|
|
95
|
+
value: _build_score( m ))
|
|
96
|
+
elsif m[:sym]
|
|
97
|
+
case m[:sym]
|
|
98
|
+
when ')' ## leave goal mode!!
|
|
99
|
+
_trace( "LEAVE GOAL_ALT_RE MODE" )
|
|
100
|
+
@re = RE
|
|
101
|
+
## note - use/return GOAL_END token - change to GOAL_END_PAREN(THESIS)
|
|
102
|
+
## or GOAL_PAREN_CLOSE/END ???
|
|
103
|
+
## [:GOALS_END, '<|GOALS_END|>']
|
|
104
|
+
Token.virtual( :GOALS_END, lineno: ctx.lineno )
|
|
105
|
+
else
|
|
106
|
+
Token.literal( m[:sym], lineno: ctx.lineno, offset: m.offset(:sym))
|
|
107
|
+
end
|
|
108
|
+
else
|
|
109
|
+
ctx.warn_on_else( m, mode: 'GOAL_ALT' )
|
|
110
|
+
nil
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
GOAL_COMPAT_RE = Regexp.union(
|
|
117
|
+
SPACES_RE,
|
|
118
|
+
SCORE_RE, ## e.g. 1-0, 0-1, etc.
|
|
119
|
+
MINUTE_RE, ## note - matches minute e.g. 92, 7, 7' 7+3, etc.
|
|
120
|
+
GOAL_TYPE_RE,
|
|
121
|
+
PROP_NAME_RE, ## note - (re)use prop name for now for (player) name
|
|
122
|
+
/ (?<sym> [,)]) /x ## note - no semicolon (;)
|
|
123
|
+
## todo/fix - add ANY_RE !!!!
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _on_goal_compat( m, ctx: ) ## note - m is MatchData object
|
|
128
|
+
|
|
129
|
+
if m[:space] || m[:spaces]
|
|
130
|
+
nil ## skip space(s)
|
|
131
|
+
elsif m[:prop_name] ## note - change prop_name to player
|
|
132
|
+
Token.new(:PLAYER, m[:name],
|
|
133
|
+
lineno: ctx.lineno, offset: m.offset(:name))
|
|
134
|
+
elsif m[:minute]
|
|
135
|
+
Token.new(:MINUTE, m[:minute],
|
|
136
|
+
lineno: ctx.lineno, offset: m.offset(:minute),
|
|
137
|
+
value: _build_minute( m ))
|
|
138
|
+
elsif m[:goal_type]
|
|
139
|
+
Token.new( :GOAL_TYPE,m[:goal_type],
|
|
140
|
+
lineno: ctx.lineno, offset: m.offset(:goal_type),
|
|
141
|
+
value: _build_goal_type( m ))
|
|
142
|
+
|
|
143
|
+
elsif m[:score]
|
|
144
|
+
Token.new( :SCORE, m[:score],
|
|
145
|
+
lineno: ctx.lineno, offset: m.offset(:score),
|
|
146
|
+
value: _build_score( m ))
|
|
147
|
+
elsif m[:sym]
|
|
148
|
+
case m[:sym]
|
|
149
|
+
when ')' ## leave goal mode!!
|
|
150
|
+
_trace( "LEAVE GOAL_COMPAT_RE MODE" )
|
|
151
|
+
@re = RE
|
|
152
|
+
## note - use/return GOAL_END token - change to GOAL_END_PAREN(THESIS)
|
|
153
|
+
## or GOAL_PAREN_CLOSE/END ???
|
|
154
|
+
## [:GOALS_END, '<|GOALS_END|>']
|
|
155
|
+
Token.virtual( :GOALS_END, lineno: ctx.lineno )
|
|
156
|
+
else
|
|
157
|
+
Token.literal( m[:sym], lineno: ctx.lineno, offset: m.offset(:sym))
|
|
158
|
+
end
|
|
159
|
+
else
|
|
160
|
+
ctx.warn_on_else( m, mode: 'GOAL_COMPAT' )
|
|
161
|
+
nil
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
end ## class Lexer
|
|
167
|
+
end ## module Fbtxt
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Fbtxt
|
|
2
|
+
class Lexer
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### note - add comma (,) as optional separator
|
|
7
|
+
GROUP_DEF_RE = Regexp.union( SPACES_RE,
|
|
8
|
+
TEXT_RE,
|
|
9
|
+
/ (?<sym> [:|,] ) /x,
|
|
10
|
+
ANY_RE,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _on_group_def( m, ctx: ) ## note - m is MatchData object
|
|
15
|
+
|
|
16
|
+
if m[:spaces] || m[:space]
|
|
17
|
+
nil ## skip spaces
|
|
18
|
+
elsif m[:text]
|
|
19
|
+
Token.new(:TEAM, m[:text],
|
|
20
|
+
lineno: ctx.lineno, offset: m.offset(:text))
|
|
21
|
+
elsif m[:sym]
|
|
22
|
+
Token.literal( m[:sym], lineno: ctx.lineno, offset: m.offset(:sym))
|
|
23
|
+
else
|
|
24
|
+
ctx.warn_on_else( m, mode: 'GROUP_DEF' )
|
|
25
|
+
nil
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
end ## class Lexer
|
|
31
|
+
end ## module Fbtxt
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
module Fbtxt
|
|
2
|
+
class Lexer
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
PROP_LINEUP_RE = Regexp.union(
|
|
7
|
+
SPACES_RE,
|
|
8
|
+
MINUTE_RE, ## e.g. 44 or 44' or 45+1 or 45+1' etc.
|
|
9
|
+
|
|
10
|
+
INLINE_CAPTAIN, ## e.g. [c]
|
|
11
|
+
INLINE_YELLOW, ## e.g. [Y] or [Y 44] or [Y 44'] or [Y 45+1']
|
|
12
|
+
INLINE_YELLOW_RED, ## e.g. [Y/R] or [Y/R 78]
|
|
13
|
+
INLINE_RED, ## e.g. [R] or [R 42] or [R 42']
|
|
14
|
+
|
|
15
|
+
PROP_KEY_INLINE_RE,
|
|
16
|
+
PROP_NAME_RE,
|
|
17
|
+
/ (?<sym> [;,()\[\]-]) /x
|
|
18
|
+
## todo/fix - add ANY_RE here too!!!
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _on_prop_lineup( m, ctx: ) ## note - m is MatchData object
|
|
23
|
+
|
|
24
|
+
if m[:space] || m[:spaces]
|
|
25
|
+
nil ## skip space(s)
|
|
26
|
+
elsif m[:prop_key] ## check for inline prop keys
|
|
27
|
+
key = m[:key]
|
|
28
|
+
## supported for now coach/trainer (add manager?)
|
|
29
|
+
if ['coach',
|
|
30
|
+
'trainer'].include?( key.downcase )
|
|
31
|
+
## use PROP_COACH or COACH_KEY or such - why? why not?
|
|
32
|
+
Token.new(:COACH, m[:key],
|
|
33
|
+
lineno: ctx.lineno, offset: m.offset(:key))
|
|
34
|
+
else
|
|
35
|
+
## report error - for unknown (inline) prop key in lineup
|
|
36
|
+
nil
|
|
37
|
+
end
|
|
38
|
+
elsif m[:inline_captain]
|
|
39
|
+
Token.new(:INLINE_CAPTAIN, m[:inline_captain],
|
|
40
|
+
lineno: ctx.lineno, offset: m.offset(:inline_captain))
|
|
41
|
+
elsif m[:inline_yellow]
|
|
42
|
+
card = {}
|
|
43
|
+
card[:m] = m[:minute].to_i(10) if m[:minute]
|
|
44
|
+
card[:offset] = m[:offset].to_i(10) if m[:offset]
|
|
45
|
+
Token.new(:INLINE_YELLOW, m[:inline_yellow],
|
|
46
|
+
lineno: ctx.lineno, offset: m.offset(:inline_yellow),
|
|
47
|
+
value: card)
|
|
48
|
+
elsif m[:inline_red]
|
|
49
|
+
card = {}
|
|
50
|
+
card[:m] = m[:minute].to_i(10) if m[:minute]
|
|
51
|
+
card[:offset] = m[:offset].to_i(10) if m[:offset]
|
|
52
|
+
Token.new(:INLINE_RED, m[:inline_red],
|
|
53
|
+
lineno: ctx.lineno, offset: m.offset(:inline_red),
|
|
54
|
+
value: card)
|
|
55
|
+
elsif m[:inline_yellow_red]
|
|
56
|
+
card = {}
|
|
57
|
+
card[:m] = m[:minute].to_i(10) if m[:minute]
|
|
58
|
+
card[:offset] = m[:offset].to_i(10) if m[:offset]
|
|
59
|
+
Token.new(:INLINE_YELLOW_RED, m[:inline_yellow_red],
|
|
60
|
+
lineno: ctx.lineno, offset: m.offset(:inline_yellow_red),
|
|
61
|
+
value: card)
|
|
62
|
+
elsif m[:prop_name]
|
|
63
|
+
Token.new(:PROP_NAME, m[:name],
|
|
64
|
+
lineno: ctx.lineno, offset: m.offset(:prop_name))
|
|
65
|
+
elsif m[:minute]
|
|
66
|
+
Token.new(:MINUTE, m[:minute],
|
|
67
|
+
lineno: ctx.lineno, offset: m.offset(:minute),
|
|
68
|
+
value: _build_minute( m ))
|
|
69
|
+
elsif m[:sym]
|
|
70
|
+
Token.literal( m[:sym], lineno: ctx.lineno, offset: m.offset(:sym))
|
|
71
|
+
else
|
|
72
|
+
ctx.warn_on_else( m, mode: 'PROP_LINEUP' )
|
|
73
|
+
nil
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
end ## class Lexer
|
|
79
|
+
end ## module Fbtxt
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
module Fbtxt
|
|
2
|
+
class Lexer
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## note - no inline keys possible
|
|
7
|
+
## todo/fix - use custom (limited) prop basics too
|
|
8
|
+
PROP_CARDS_RE = Regexp.union(
|
|
9
|
+
SPACES_RE,
|
|
10
|
+
CARDS_NONE_LEFT_RE,
|
|
11
|
+
CARDS_NONE_RIGHT_RE,
|
|
12
|
+
MINUTE_RE,
|
|
13
|
+
PROP_NAME_RE,
|
|
14
|
+
CARDS_SEP_ALT_RE, ## note - add dash (-) with (required) spaces
|
|
15
|
+
/ (?<sym> [;,]) /x
|
|
16
|
+
## todo/fix - add ANY_RE here too!!!
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _on_prop_cards( m, ctx: ) ## note - m is MatchData object
|
|
21
|
+
|
|
22
|
+
if m[:space] || m[:spaces]
|
|
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
|
+
|
|
34
|
+
elsif m[:prop_name]
|
|
35
|
+
Token.new(:PROP_NAME, m[:name],
|
|
36
|
+
lineno: ctx.lineno, offset: m.offset(:prop_name))
|
|
37
|
+
elsif m[:minute]
|
|
38
|
+
Token.new(:MINUTE, m[:minute],
|
|
39
|
+
lineno: ctx.lineno, offset: m.offset(:minute),
|
|
40
|
+
value: _build_minute( m ))
|
|
41
|
+
elsif m[:sym]
|
|
42
|
+
Token.literal( m[:sym], lineno: ctx.lineno, offset: m.offset(:sym))
|
|
43
|
+
else
|
|
44
|
+
ctx.warn_on_else( m, mode: 'PROP_CARDS' )
|
|
45
|
+
nil
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
PROP_ATTENDANCE_RE = Regexp.union(
|
|
52
|
+
SPACES_RE,
|
|
53
|
+
ENCLOSED_NAME_RE, # e.g. (sold out) etc. why? why not?
|
|
54
|
+
PROP_NUM_RE, # e.g. 28 000 or 28_000 (NOT 28,000 is not valid!!!)
|
|
55
|
+
## todo/fix - add ANY_RE here too!!!
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
def _on_prop_attendance( m, ctx: ) ## note - m is MatchData object
|
|
59
|
+
|
|
60
|
+
if m[:space] || m[:spaces]
|
|
61
|
+
nil ## skip space(s)
|
|
62
|
+
elsif m[:enclosed_name]
|
|
63
|
+
## reserverd for use for sold out or such (in the future) - why? why not?
|
|
64
|
+
Token.new(:ENCLOSED_NAME, m[:name],
|
|
65
|
+
lineno: ctx.lineno, offset: m.offset(:name))
|
|
66
|
+
elsif m[:num]
|
|
67
|
+
Token.new(:PROP_NUM, m[:num],
|
|
68
|
+
lineno: ctx.lineno, offset: m.offset(:num),
|
|
69
|
+
value: m[:value].to_i(10))
|
|
70
|
+
else
|
|
71
|
+
ctx.warn_on_else( m, mode: 'PROP_ATTENDANCE' )
|
|
72
|
+
nil
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
PROP_REFEREE_RE = Regexp.union(
|
|
79
|
+
SPACES_RE,
|
|
80
|
+
ENCLOSED_NAME_RE, # e.g. (sold out) etc. why? why not?
|
|
81
|
+
PROP_NUM_RE, # e.g. 28 000 or 28_000 (NOT 28,000 is not valid!!!)
|
|
82
|
+
PROP_KEY_INLINE_RE,
|
|
83
|
+
PROP_NAME_RE,
|
|
84
|
+
/ (?<sym> [;,]) /x
|
|
85
|
+
## todo/fix - add ANY_RE here too!!!
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
def _on_prop_referee( m, ctx: ) ## note - m is MatchData object
|
|
89
|
+
|
|
90
|
+
if m[:space] || m[:spaces]
|
|
91
|
+
nil ## skip space(s)
|
|
92
|
+
elsif m[:prop_key] ## check for inline prop keys
|
|
93
|
+
key = m[:key]
|
|
94
|
+
## supported for now coach/trainer (add manager?)
|
|
95
|
+
if ['att', 'attn', 'attendance' ].include?( key.downcase )
|
|
96
|
+
## use ATTENDANCE_PROP or ATTENDANCE_KEY or such - why? why not?
|
|
97
|
+
Token.new(:ATTENDANCE, m[:key],
|
|
98
|
+
lineno: ctx.lineno, offset: m.offset(:key))
|
|
99
|
+
else
|
|
100
|
+
## report error - for unknown (inline) prop key in lineup
|
|
101
|
+
nil
|
|
102
|
+
end
|
|
103
|
+
elsif m[:prop_name] ## note - change prop_name to player or to (plain) name?
|
|
104
|
+
Token.new(:PROP_NAME, m[:name],
|
|
105
|
+
lineno: ctx.lineno, offset: m.offset(:prop_name))
|
|
106
|
+
elsif m[:num]
|
|
107
|
+
Token.new(:PROP_NUM, m[:num],
|
|
108
|
+
lineno: ctx.lineno, offset: m.offset(:num),
|
|
109
|
+
value: m[:value].to_i(10))
|
|
110
|
+
elsif m[:enclosed_name]
|
|
111
|
+
## use HOLD,SAVE,POST or such keys - why? why not?
|
|
112
|
+
Token.new(:ENCLOSED_NAME, m[:name],
|
|
113
|
+
lineno: ctx.lineno, offset: m.offset(:name))
|
|
114
|
+
elsif m[:sym]
|
|
115
|
+
Token.literal( m[:sym], lineno: ctx.lineno, offset: m.offset(:sym))
|
|
116
|
+
else
|
|
117
|
+
ctx.warn_on_else( m, mode: 'PROP_REFEREE' )
|
|
118
|
+
nil
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
end ## class Lexer
|
|
123
|
+
end ## module Fbtxt
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Fbtxt
|
|
2
|
+
class Lexer
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
PROP_PENALTIES_RE = Regexp.union(
|
|
8
|
+
SPACES_RE,
|
|
9
|
+
SCORE_RE, # e.g. 1-1 etc.
|
|
10
|
+
ENCLOSED_NAME_RE, # e.g. (save), (post), etc.
|
|
11
|
+
PROP_NAME_RE,
|
|
12
|
+
/ (?<sym> [;,]) /x ## add [] too - why? why not?
|
|
13
|
+
## todo/fix - add ANY_RE here too!!!
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
def _on_prop_penalties( m, ctx: ) ## note - m is MatchData object
|
|
17
|
+
if m[:space] || m[:spaces]
|
|
18
|
+
nil ## skip space(s)
|
|
19
|
+
elsif m[:prop_name] ## note - change prop_name to player
|
|
20
|
+
Token.new(:PROP_NAME, m[:name],
|
|
21
|
+
lineno: ctx.lineno, offset: m.offset(:prop_name))
|
|
22
|
+
elsif m[:enclosed_name]
|
|
23
|
+
## use HOLD,SAVE,POST or such keys - why? why not?
|
|
24
|
+
Token.new(:ENCLOSED_NAME, m[:name],
|
|
25
|
+
lineno: ctx.lineno, offset: m.offset(:name))
|
|
26
|
+
elsif m[:score]
|
|
27
|
+
Token.new( :SCORE, m[:score],
|
|
28
|
+
lineno: ctx.lineno, offset: m.offset(:score),
|
|
29
|
+
value: _build_score( m ))
|
|
30
|
+
elsif m[:sym]
|
|
31
|
+
Token.literal( m[:sym], lineno: ctx.lineno, offset: m.offset(:sym))
|
|
32
|
+
else
|
|
33
|
+
ctx.warn_on_else( m, mode: 'PROP_PENALTIES ')
|
|
34
|
+
nil
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
end ## class Lexer
|
|
40
|
+
end ## module Fbtxt
|