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.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +4 -0
  3. data/Manifest.txt +50 -0
  4. data/README.md +22 -0
  5. data/Rakefile +53 -0
  6. data/lib/fbtxt/parser/debuggable.rb +53 -0
  7. data/lib/fbtxt/parser/lexer-logger.rb +20 -0
  8. data/lib/fbtxt/parser/lexer-on_goal.rb +167 -0
  9. data/lib/fbtxt/parser/lexer-on_group_def.rb +31 -0
  10. data/lib/fbtxt/parser/lexer-on_prop_lineup.rb +79 -0
  11. data/lib/fbtxt/parser/lexer-on_prop_misc.rb +123 -0
  12. data/lib/fbtxt/parser/lexer-on_prop_penalties.rb +40 -0
  13. data/lib/fbtxt/parser/lexer-on_round_def.rb +37 -0
  14. data/lib/fbtxt/parser/lexer-on_top.rb +133 -0
  15. data/lib/fbtxt/parser/lexer-prep_doc.rb +131 -0
  16. data/lib/fbtxt/parser/lexer-prep_line.rb +63 -0
  17. data/lib/fbtxt/parser/lexer-tokenize.rb +468 -0
  18. data/lib/fbtxt/parser/lexer.rb +231 -0
  19. data/lib/fbtxt/parser/lexer_buffer.rb +68 -0
  20. data/lib/fbtxt/parser/lexer_token.rb +126 -0
  21. data/lib/fbtxt/parser/parse_tree--core.rb +29 -0
  22. data/lib/fbtxt/parser/parse_tree-match.rb +309 -0
  23. data/lib/fbtxt/parser/parse_tree-props.rb +233 -0
  24. data/lib/fbtxt/parser/parse_tree.rb +202 -0
  25. data/lib/fbtxt/parser/parser-runtime.rb +379 -0
  26. data/lib/fbtxt/parser/parser-top.rb +102 -0
  27. data/lib/fbtxt/parser/parser.rb +2343 -0
  28. data/lib/fbtxt/parser/token-date--helpers.rb +130 -0
  29. data/lib/fbtxt/parser/token-date--names.rb +108 -0
  30. data/lib/fbtxt/parser/token-date.rb +200 -0
  31. data/lib/fbtxt/parser/token-date_duration.rb +171 -0
  32. data/lib/fbtxt/parser/token-geo.rb +134 -0
  33. data/lib/fbtxt/parser/token-goals--helpers.rb +114 -0
  34. data/lib/fbtxt/parser/token-goals.rb +306 -0
  35. data/lib/fbtxt/parser/token-group.rb +29 -0
  36. data/lib/fbtxt/parser/token-note.rb +40 -0
  37. data/lib/fbtxt/parser/token-prop.rb +309 -0
  38. data/lib/fbtxt/parser/token-prop_name.rb +83 -0
  39. data/lib/fbtxt/parser/token-round.rb +88 -0
  40. data/lib/fbtxt/parser/token-score--helpers.rb +189 -0
  41. data/lib/fbtxt/parser/token-score.rb +60 -0
  42. data/lib/fbtxt/parser/token-score_full.rb +331 -0
  43. data/lib/fbtxt/parser/token-score_fuller.rb +434 -0
  44. data/lib/fbtxt/parser/token-score_legs.rb +59 -0
  45. data/lib/fbtxt/parser/token-status.rb +192 -0
  46. data/lib/fbtxt/parser/token-status_inline.rb +112 -0
  47. data/lib/fbtxt/parser/token-text.rb +221 -0
  48. data/lib/fbtxt/parser/token-time.rb +144 -0
  49. data/lib/fbtxt/parser/token.rb +224 -0
  50. data/lib/fbtxt/parser/version.rb +24 -0
  51. data/lib/fbtxt/parser.rb +140 -0
  52. 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
@@ -0,0 +1,4 @@
1
+ ### 0.9.0
2
+ ### 0.0.1 / 2024-07-12
3
+
4
+ * Everything is new. First release.
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