fbtxt-lexer 0.0.1

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 (49) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +3 -0
  3. data/Manifest.txt +47 -0
  4. data/README.md +18 -0
  5. data/Rakefile +30 -0
  6. data/lib/fbtxt/lexer/debuggable.rb +58 -0
  7. data/lib/fbtxt/lexer/lexer-logger.rb +20 -0
  8. data/lib/fbtxt/lexer/lexer-on_goal.rb +167 -0
  9. data/lib/fbtxt/lexer/lexer-on_group_def.rb +31 -0
  10. data/lib/fbtxt/lexer/lexer-on_prop_cards.rb +61 -0
  11. data/lib/fbtxt/lexer/lexer-on_prop_lineup.rb +82 -0
  12. data/lib/fbtxt/lexer/lexer-on_prop_misc.rb +108 -0
  13. data/lib/fbtxt/lexer/lexer-on_prop_penalties.rb +44 -0
  14. data/lib/fbtxt/lexer/lexer-on_round_def.rb +37 -0
  15. data/lib/fbtxt/lexer/lexer-on_top.rb +133 -0
  16. data/lib/fbtxt/lexer/lexer-prep_doc.rb +131 -0
  17. data/lib/fbtxt/lexer/lexer-prep_line.rb +63 -0
  18. data/lib/fbtxt/lexer/lexer-props.rb +66 -0
  19. data/lib/fbtxt/lexer/lexer-tokenize_line.rb +381 -0
  20. data/lib/fbtxt/lexer/lexer-tokenize_norm.rb +93 -0
  21. data/lib/fbtxt/lexer/lexer.rb +192 -0
  22. data/lib/fbtxt/lexer/lexer_buffer.rb +68 -0
  23. data/lib/fbtxt/lexer/lexer_context.rb +70 -0
  24. data/lib/fbtxt/lexer/lexer_token.rb +127 -0
  25. data/lib/fbtxt/lexer/token-date--helpers.rb +130 -0
  26. data/lib/fbtxt/lexer/token-date--names.rb +108 -0
  27. data/lib/fbtxt/lexer/token-date.rb +200 -0
  28. data/lib/fbtxt/lexer/token-date_duration.rb +171 -0
  29. data/lib/fbtxt/lexer/token-geo.rb +173 -0
  30. data/lib/fbtxt/lexer/token-goals--helpers.rb +114 -0
  31. data/lib/fbtxt/lexer/token-goals.rb +306 -0
  32. data/lib/fbtxt/lexer/token-group.rb +29 -0
  33. data/lib/fbtxt/lexer/token-note.rb +40 -0
  34. data/lib/fbtxt/lexer/token-prop.rb +334 -0
  35. data/lib/fbtxt/lexer/token-prop_name.rb +83 -0
  36. data/lib/fbtxt/lexer/token-round.rb +88 -0
  37. data/lib/fbtxt/lexer/token-score--helpers.rb +189 -0
  38. data/lib/fbtxt/lexer/token-score.rb +60 -0
  39. data/lib/fbtxt/lexer/token-score_full.rb +331 -0
  40. data/lib/fbtxt/lexer/token-score_fuller.rb +434 -0
  41. data/lib/fbtxt/lexer/token-score_legs.rb +59 -0
  42. data/lib/fbtxt/lexer/token-status.rb +192 -0
  43. data/lib/fbtxt/lexer/token-status_inline.rb +112 -0
  44. data/lib/fbtxt/lexer/token-text.rb +221 -0
  45. data/lib/fbtxt/lexer/token-time.rb +144 -0
  46. data/lib/fbtxt/lexer/token.rb +224 -0
  47. data/lib/fbtxt/lexer/version.rb +24 -0
  48. data/lib/fbtxt/lexer.rb +118 -0
  49. metadata +142 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3adf411ebce4d6898db3420ee0f5e0a5351cbd02356ecd136337db829ac43afc
4
+ data.tar.gz: 54009bc1ff2ad87d5b759f031e07504ee4794560266b3b3f886831d26a3f662f
5
+ SHA512:
6
+ metadata.gz: 8e57cf05f3719c60b6470a83f0c3839652180e6027e956cc1c498cf5f3c8968068e3a06d479c7a15d6fcf6ca7ffdc70a6ac37ec15b9f3d4ac6be201b214787f6
7
+ data.tar.gz: 05a5b695db33fe546eaf9988d8d31a599144b04a301f6589effe0259572503b40845c2fc3b146c540acd425ac8b7725a809f38a56d765f268c52cef28ff3c231
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ### 0.0.1 / 2026-08-16
2
+
3
+ * Everything is new. First release.
data/Manifest.txt ADDED
@@ -0,0 +1,47 @@
1
+ CHANGELOG.md
2
+ Manifest.txt
3
+ README.md
4
+ Rakefile
5
+ lib/fbtxt/lexer.rb
6
+ lib/fbtxt/lexer/debuggable.rb
7
+ lib/fbtxt/lexer/lexer-logger.rb
8
+ lib/fbtxt/lexer/lexer-on_goal.rb
9
+ lib/fbtxt/lexer/lexer-on_group_def.rb
10
+ lib/fbtxt/lexer/lexer-on_prop_cards.rb
11
+ lib/fbtxt/lexer/lexer-on_prop_lineup.rb
12
+ lib/fbtxt/lexer/lexer-on_prop_misc.rb
13
+ lib/fbtxt/lexer/lexer-on_prop_penalties.rb
14
+ lib/fbtxt/lexer/lexer-on_round_def.rb
15
+ lib/fbtxt/lexer/lexer-on_top.rb
16
+ lib/fbtxt/lexer/lexer-prep_doc.rb
17
+ lib/fbtxt/lexer/lexer-prep_line.rb
18
+ lib/fbtxt/lexer/lexer-props.rb
19
+ lib/fbtxt/lexer/lexer-tokenize_line.rb
20
+ lib/fbtxt/lexer/lexer-tokenize_norm.rb
21
+ lib/fbtxt/lexer/lexer.rb
22
+ lib/fbtxt/lexer/lexer_buffer.rb
23
+ lib/fbtxt/lexer/lexer_context.rb
24
+ lib/fbtxt/lexer/lexer_token.rb
25
+ lib/fbtxt/lexer/token-date--helpers.rb
26
+ lib/fbtxt/lexer/token-date--names.rb
27
+ lib/fbtxt/lexer/token-date.rb
28
+ lib/fbtxt/lexer/token-date_duration.rb
29
+ lib/fbtxt/lexer/token-geo.rb
30
+ lib/fbtxt/lexer/token-goals--helpers.rb
31
+ lib/fbtxt/lexer/token-goals.rb
32
+ lib/fbtxt/lexer/token-group.rb
33
+ lib/fbtxt/lexer/token-note.rb
34
+ lib/fbtxt/lexer/token-prop.rb
35
+ lib/fbtxt/lexer/token-prop_name.rb
36
+ lib/fbtxt/lexer/token-round.rb
37
+ lib/fbtxt/lexer/token-score--helpers.rb
38
+ lib/fbtxt/lexer/token-score.rb
39
+ lib/fbtxt/lexer/token-score_full.rb
40
+ lib/fbtxt/lexer/token-score_fuller.rb
41
+ lib/fbtxt/lexer/token-score_legs.rb
42
+ lib/fbtxt/lexer/token-status.rb
43
+ lib/fbtxt/lexer/token-status_inline.rb
44
+ lib/fbtxt/lexer/token-text.rb
45
+ lib/fbtxt/lexer/token-time.rb
46
+ lib/fbtxt/lexer/token.rb
47
+ lib/fbtxt/lexer/version.rb
data/README.md ADDED
@@ -0,0 +1,18 @@
1
+ # fbtxt-lexer - football.txt match lexer (& tokenizer)
2
+
3
+
4
+ ## Usage
5
+
6
+
7
+ ``` ruby
8
+ result = Fbtxt.lex( txt ) # returns Fbtxt::LexerResult w/ tokens, errors/ok?/nok?/etc.
9
+
10
+
11
+ #-or-
12
+
13
+ lexer = Fbtxt::Lexer.new(txt)
14
+ tokens, errors = lexer.tokenize_with_errors
15
+ ```
16
+
17
+
18
+ note - for porcelain / higher-level apis see `Fbtxt::Document`.
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ require 'hoe'
2
+ require './lib/fbtxt/lexer/version.rb'
3
+
4
+
5
+ Hoe.spec 'fbtxt-lexer' do
6
+
7
+ self.version = Fbtxt::Module::Lexer::VERSION
8
+
9
+ self.summary = "fbtxt-lexer - football.txt match lexer (& 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.2'],
25
+ ]
26
+
27
+ self.spec_extras = {
28
+ required_ruby_version: '>= 3.1.0'
29
+ }
30
+ end
@@ -0,0 +1,58 @@
1
+ ###
2
+ ## debugg(able) mix-in helper
3
+ ## used for lexer & parser
4
+
5
+
6
+ ############
7
+ ## fix - move (upcstream) to cocos - why? why not?
8
+
9
+
10
+ module Debuggable
11
+ def self.included(base)
12
+ base.extend(ClassMethods)
13
+ end
14
+
15
+ module ClassMethods
16
+ def debug=(value)
17
+ raise ArgumentError, "true|false required for debug flag; got #{value}" unless value.is_a?(TrueClass) || value.is_a?(FalseClass)
18
+ @debug=value;
19
+ end
20
+ def debug() @debug || false; end ## note - default to false
21
+
22
+ def debug?() @debug == true; end
23
+ end
24
+
25
+
26
+ ########
27
+ ## InstanceMethods
28
+ def debug?() self.class.debug?; end
29
+
30
+ ##
31
+ ## todo/check - allow/support multiple msg args - why? why not?
32
+
33
+ ## note - use trace for debug for now - why? why not?
34
+ def _trace( *args ) __log__( :DEBUG, *args ); end
35
+
36
+ def _info( *args ) __log__( :INFO, *args ); end
37
+ def _warn( *args) __log__( :WARN, *args ); end
38
+ def _error( *args ) __log__( :ERROR, *args ); end
39
+
40
+
41
+
42
+ private
43
+ def __log__( level, *args ) ## :DEBUG, :INFO, :WARN, :ERROR, :FATAL etc.
44
+
45
+ if level == :DEBUG && !debug?
46
+ ## print nothing
47
+ else
48
+ msg = args.map do |arg|
49
+ arg.to_str
50
+ end.join(' | ')
51
+
52
+ fout = level == :WARN || level == :ERROR || level == :FATAL ? STDERR : STDOUT
53
+
54
+ ## todo/check/fix - add/use/check for "local" logger
55
+ fout.puts "#{level} [#{self.class.name}] -- #{msg}"
56
+ end
57
+ end
58
+ 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,61 @@
1
+ module Fbtxt
2
+ class Lexer
3
+
4
+
5
+ ##
6
+ ## todo - move CARDS_NONE_LEFT_RE,
7
+ ## CARDS_SEP_ALT_RE here!!
8
+
9
+
10
+
11
+ ######
12
+ ## todo - add examples for prop cards here !!:
13
+
14
+
15
+ ## note - no inline keys possible
16
+ ## todo/fix - use custom (limited) prop basics too
17
+ PROP_CARDS_RE = Regexp.union(
18
+ SPACES_RE,
19
+ CARDS_NONE_LEFT_RE,
20
+ CARDS_NONE_RIGHT_RE,
21
+ MINUTE_RE,
22
+ PROP_NAME_RE,
23
+ CARDS_SEP_ALT_RE, ## note - add dash (-) with (required) spaces
24
+ / (?<sym> [;,]) /x
25
+ ## todo/fix - add ANY_RE here too!!!
26
+ )
27
+
28
+
29
+ def _on_prop_cards( m, ctx: ) ## note - m is MatchData object
30
+
31
+ if m[:space] || m[:spaces]
32
+ nil ## skip space(s)
33
+ elsif m[:cards_none_left]
34
+ Token.new(:CARDS_NONE_LEFT, m[0],
35
+ lineno: ctx.lineno, offset: m.offset(0))
36
+ elsif m[:cards_none_right]
37
+ Token.new(:CARDS_NONE_RIGHT, m[0],
38
+ lineno: ctx.lineno, offset: m.offset(0))
39
+ elsif m[:cards_sep_alt]
40
+ Token.new( :CARDS_SEP_ALT, m[0],
41
+ lineno: ctx.lineno, offset: m.offset(0))
42
+
43
+ elsif m[:prop_name]
44
+ Token.new(:PROP_NAME, m[:name],
45
+ lineno: ctx.lineno, offset: m.offset(:prop_name))
46
+ elsif m[:minute]
47
+ Token.new(:MINUTE, m[:minute],
48
+ lineno: ctx.lineno, offset: m.offset(:minute),
49
+ value: _build_minute( m ))
50
+ elsif m[:sym]
51
+ Token.literal( m[:sym], lineno: ctx.lineno, offset: m.offset(:sym))
52
+ else
53
+ ctx.warn_on_else( m, mode: 'PROP_CARDS' )
54
+ nil
55
+ end
56
+ end
57
+
58
+
59
+
60
+ end ## class Lexer
61
+ end ## module Fbtxt
@@ -0,0 +1,82 @@
1
+ module Fbtxt
2
+ class Lexer
3
+
4
+
5
+ ###
6
+ ## todo - move INLINE_CAPTAIN,
7
+ ## INLINE_YELLOW ... and FORMATION_RE here!!!!
8
+
9
+ ##
10
+ ## todo - add LINEUP_SEP_ALT (for _-_) - make space required!! ??
11
+
12
+
13
+ PROP_LINEUP_RE = Regexp.union(
14
+ SPACES_RE,
15
+ MINUTE_RE, ## e.g. 44 or 44' or 45+1 or 45+1' etc.
16
+
17
+ INLINE_CAPTAIN, ## e.g. [c]
18
+ INLINE_YELLOW, ## e.g. [Y] or [Y 44] or [Y 44'] or [Y 45+1']
19
+ INLINE_YELLOW_RED, ## e.g. [Y/R] or [Y/R 78] or [YR]
20
+ INLINE_RED, ## e.g. [R] or [R 42] or [R 42']
21
+
22
+ FORMATION_RE, ## e.g. (4-3-3), (4-1-4-1) etc.
23
+
24
+ PROP_NAME_RE,
25
+ / (?<sym> [;,()\[\]-]) /x
26
+ ## todo/fix - add ANY_RE here too!!!
27
+ )
28
+
29
+
30
+
31
+ def _on_prop_lineup( m, ctx: ) ## note - m is MatchData object
32
+
33
+ if m[:space] || m[:spaces]
34
+ nil ## skip space(s)
35
+ elsif m[:formation]
36
+ ## e.g. (4-3-3) or (433)
37
+ ## note - value is 4-3-3 or 433 as string (without parenthesis)
38
+ Token.new(:FORMATION, m[:formation],
39
+ lineno: ctx.lineno, offset: m.offset(:formation),
40
+ value: m[:value])
41
+ elsif m[:inline_captain]
42
+ Token.new(:INLINE_CAPTAIN, m[:inline_captain],
43
+ lineno: ctx.lineno, offset: m.offset(:inline_captain))
44
+ elsif m[:inline_yellow]
45
+ card = {}
46
+ card[:m] = m[:minute].to_i(10) if m[:minute]
47
+ card[:offset] = m[:offset].to_i(10) if m[:offset]
48
+ Token.new(:INLINE_YELLOW, m[:inline_yellow],
49
+ lineno: ctx.lineno, offset: m.offset(:inline_yellow),
50
+ value: card)
51
+ elsif m[:inline_red]
52
+ card = {}
53
+ card[:m] = m[:minute].to_i(10) if m[:minute]
54
+ card[:offset] = m[:offset].to_i(10) if m[:offset]
55
+ Token.new(:INLINE_RED, m[:inline_red],
56
+ lineno: ctx.lineno, offset: m.offset(:inline_red),
57
+ value: card)
58
+ elsif m[:inline_yellow_red]
59
+ card = {}
60
+ card[:m] = m[:minute].to_i(10) if m[:minute]
61
+ card[:offset] = m[:offset].to_i(10) if m[:offset]
62
+ Token.new(:INLINE_YELLOW_RED, m[:inline_yellow_red],
63
+ lineno: ctx.lineno, offset: m.offset(:inline_yellow_red),
64
+ value: card)
65
+ elsif m[:prop_name]
66
+ Token.new(:PROP_NAME, m[:name],
67
+ lineno: ctx.lineno, offset: m.offset(:prop_name))
68
+ elsif m[:minute]
69
+ Token.new(:MINUTE, m[:minute],
70
+ lineno: ctx.lineno, offset: m.offset(:minute),
71
+ value: _build_minute( m ))
72
+ elsif m[:sym]
73
+ Token.literal( m[:sym], lineno: ctx.lineno, offset: m.offset(:sym))
74
+ else
75
+ ctx.warn_on_else( m, mode: 'PROP_LINEUP' )
76
+ nil
77
+ end
78
+ end
79
+
80
+
81
+ end ## class Lexer
82
+ end ## module Fbtxt
@@ -0,0 +1,108 @@
1
+ module Fbtxt
2
+ class Lexer
3
+
4
+
5
+
6
+ PROP_ATTENDANCE_RE = Regexp.union(
7
+ SPACES_RE,
8
+ ENCLOSED_NAME_RE, # e.g. (sold out) etc. why? why not?
9
+ PROP_NUM_RE, # e.g. 28 000 or 28_000 (NOT 28,000 is not valid!!!)
10
+ ## todo/fix - add ANY_RE here too!!!
11
+ )
12
+
13
+ def _on_prop_attendance( m, ctx: ) ## note - m is MatchData object
14
+
15
+ if m[:space] || m[:spaces]
16
+ nil ## skip space(s)
17
+ elsif m[:enclosed_name]
18
+ ## reserverd for use for sold out or such (in the future) - why? why not?
19
+ Token.new(:ENCLOSED_NAME, m[:name],
20
+ lineno: ctx.lineno, offset: m.offset(:name))
21
+ elsif m[:num]
22
+ Token.new(:PROP_NUM, m[:num],
23
+ lineno: ctx.lineno, offset: m.offset(:num),
24
+ value: m[:value].to_i(10))
25
+ else
26
+ ctx.warn_on_else( m, mode: 'PROP_ATTENDANCE' )
27
+ nil
28
+ end
29
+ end
30
+
31
+
32
+
33
+ PROP_REFEREE_RE = Regexp.union(
34
+ SPACES_RE,
35
+ ENCLOSED_NAME_RE, # e.g. (sold out) etc. why? why not?
36
+ PROP_NUM_RE, # e.g. 28 000 or 28_000 (NOT 28,000 is not valid!!!)
37
+ PROP_KEY_INLINE_RE,
38
+ PROP_NAME_RE,
39
+ / (?<sym> [;,]) /x
40
+ ## todo/fix - add ANY_RE here too!!!
41
+ )
42
+
43
+ def _on_prop_referee( m, ctx: ) ## note - m is MatchData object
44
+
45
+ if m[:space] || m[:spaces]
46
+ nil ## skip space(s)
47
+ elsif m[:prop_key] ## check for inline prop keys
48
+ key = m[:key]
49
+ ## supported for now coach/trainer (add manager?)
50
+ if ['att', 'attn', 'attendance' ].include?( key.downcase )
51
+ ## use ATTENDANCE_PROP or ATTENDANCE_KEY or such - why? why not?
52
+ Token.new(:ATTENDANCE, m[:key],
53
+ lineno: ctx.lineno, offset: m.offset(:key))
54
+ else
55
+ ## report error - for unknown (inline) prop key in lineup
56
+ nil
57
+ end
58
+ elsif m[:prop_name] ## note - change prop_name to player or to (plain) name?
59
+ Token.new(:PROP_NAME, m[:name],
60
+ lineno: ctx.lineno, offset: m.offset(:prop_name))
61
+ elsif m[:num]
62
+ Token.new(:PROP_NUM, m[:num],
63
+ lineno: ctx.lineno, offset: m.offset(:num),
64
+ value: m[:value].to_i(10))
65
+ elsif m[:enclosed_name]
66
+ ## use HOLD,SAVE,POST or such keys - why? why not?
67
+ Token.new(:ENCLOSED_NAME, m[:name],
68
+ lineno: ctx.lineno, offset: m.offset(:name))
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_REFEREE' )
73
+ nil
74
+ end
75
+ end
76
+
77
+
78
+
79
+ PROP_COACH_RE = Regexp.union(
80
+ SPACES_RE,
81
+ ENCLOSED_NAME_RE, # e.g. (sold out) etc. why? why not?
82
+ PROP_NAME_RE,
83
+ / (?<sym> [;,]) /x
84
+ ## todo/fix - add ANY_RE here too!!!
85
+ )
86
+
87
+ def _on_prop_coach( m, ctx: ) ## note - m is MatchData object
88
+
89
+ if m[:space] || m[:spaces]
90
+ nil ## skip space(s)
91
+ elsif m[:prop_name] ## note - change prop_name to player or to (plain) name?
92
+ Token.new(:PROP_NAME, m[:name],
93
+ lineno: ctx.lineno, offset: m.offset(:prop_name))
94
+ elsif m[:enclosed_name]
95
+ ## use for country or such - why? why not?
96
+ Token.new(:ENCLOSED_NAME, m[:name],
97
+ lineno: ctx.lineno, offset: m.offset(:name))
98
+ elsif m[:sym]
99
+ Token.literal( m[:sym], lineno: ctx.lineno, offset: m.offset(:sym))
100
+ else
101
+ ctx.warn_on_else( m, mode: 'PROP_COACH' )
102
+ nil
103
+ end
104
+ end
105
+
106
+
107
+ end ## class Lexer
108
+ end ## module Fbtxt
@@ -0,0 +1,44 @@
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
+ PENALTIES_SEP_ALT_RE, ## note - add dash (-) with (required) spaces
13
+ / (?<sym> [;,]) /x ## add [] too - why? why not?
14
+ ## todo/fix - add ANY_RE here too!!!
15
+ )
16
+
17
+ def _on_prop_penalties( m, ctx: ) ## note - m is MatchData object
18
+ if m[:space] || m[:spaces]
19
+ nil ## skip space(s)
20
+ elsif m[:prop_name] ## note - change prop_name to player
21
+ Token.new(:PROP_NAME, m[:name],
22
+ lineno: ctx.lineno, offset: m.offset(:prop_name))
23
+ elsif m[:enclosed_name]
24
+ ## use HOLD,SAVE,POST or such keys - why? why not?
25
+ Token.new(:ENCLOSED_NAME, m[:name],
26
+ lineno: ctx.lineno, offset: m.offset(:name))
27
+ elsif m[:score]
28
+ Token.new( :SCORE, m[:score],
29
+ lineno: ctx.lineno, offset: m.offset(:score),
30
+ value: _build_score( m ))
31
+ elsif m[:penalties_sep_alt]
32
+ Token.new( :PENALTIES_SEP_ALT, m[0],
33
+ lineno: ctx.lineno, offset: m.offset(0))
34
+ elsif m[:sym]
35
+ Token.literal( m[:sym], lineno: ctx.lineno, offset: m.offset(:sym))
36
+ else
37
+ ctx.warn_on_else( m, mode: 'PROP_PENALTIES ')
38
+ nil
39
+ end
40
+ end
41
+
42
+
43
+ end ## class Lexer
44
+ end ## module Fbtxt