ClsRuby 1.0.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 (71) hide show
  1. data/LICENSE +26 -0
  2. data/README +55 -0
  3. data/THANKS +0 -0
  4. data/docs/base_formatting_methods +89 -0
  5. data/docs/base_parsing_methods +79 -0
  6. data/docs/constructor_params +131 -0
  7. data/docs/examples/log_single_line_format +3 -0
  8. data/docs/examples/service_description +3 -0
  9. data/docs/examples/sms-hist +3 -0
  10. data/docs/examples/tag_any +3 -0
  11. data/docs/fragments/custom_tag_field.rb +20 -0
  12. data/docs/fragments/custom_tag_include.rb +21 -0
  13. data/docs/fragments/field.cls +2 -0
  14. data/docs/fragments/include.cls +4 -0
  15. data/docs/fragments/inherit_tag_params.rb +21 -0
  16. data/docs/fragments/message.cls +6 -0
  17. data/docs/fragments/tag_field.rb +24 -0
  18. data/docs/fragments/tag_message.rb +74 -0
  19. data/docs/fragments/tags_order.rb +41 -0
  20. data/docs/principles +402 -0
  21. data/docs/std_tags_short_description +278 -0
  22. data/docs/syntax +227 -0
  23. data/docs/why_cls +178 -0
  24. data/examples/hex_stream.txt +1 -0
  25. data/examples/log_single_line_formatter.rb +79 -0
  26. data/examples/service_description.day_time.cfg +11 -0
  27. data/examples/service_description.rb +119 -0
  28. data/examples/sms-hist.rb +164 -0
  29. data/examples/space_concat.txt +3 -0
  30. data/examples/tag_any.rb +28 -0
  31. data/lib/cls-ruby/basic_scalars.rb +270 -0
  32. data/lib/cls-ruby/constraints/one_of.rb +36 -0
  33. data/lib/cls-ruby/default_formatter.rb +50 -0
  34. data/lib/cls-ruby/ex.rb +121 -0
  35. data/lib/cls-ruby/formatter.rb +31 -0
  36. data/lib/cls-ruby/lexers/char_classifier.rb +157 -0
  37. data/lib/cls-ruby/lexers/first_stage.rb +112 -0
  38. data/lib/cls-ruby/lexers/lexer.rb +74 -0
  39. data/lib/cls-ruby/oneline_formatter.rb +35 -0
  40. data/lib/cls-ruby/parser.rb +249 -0
  41. data/lib/cls-ruby/tag.rb +428 -0
  42. data/lib/cls-ruby/tag_any.rb +111 -0
  43. data/lib/cls-ruby/tag_no_value.rb +55 -0
  44. data/lib/cls-ruby/tag_scalar.rb +197 -0
  45. data/lib/cls-ruby/tag_scalar_helpers.rb +70 -0
  46. data/lib/cls-ruby/tag_scalar_vector.rb +148 -0
  47. data/lib/cls-ruby/tag_vector_of_different_tags.rb +172 -0
  48. data/lib/cls-ruby/tag_vector_of_tags.rb +116 -0
  49. data/lib/cls-ruby/vector_of_tags_impl.rb +129 -0
  50. data/lib/cls-ruby.rb +5 -0
  51. data/tests/tc_child_tag.rb +51 -0
  52. data/tests/tc_constraint_one_of.rb +47 -0
  53. data/tests/tc_format_helper.rb +27 -0
  54. data/tests/tc_formatters.rb +125 -0
  55. data/tests/tc_lexer.rb +124 -0
  56. data/tests/tc_lexer_char_classifier.rb +121 -0
  57. data/tests/tc_lexer_first_stage.rb +72 -0
  58. data/tests/tc_parser_simple.rb +93 -0
  59. data/tests/tc_scalar_parsers.rb +189 -0
  60. data/tests/tc_tag.rb +68 -0
  61. data/tests/tc_tag_no_value.rb +46 -0
  62. data/tests/tc_tag_scalar_int.rb +83 -0
  63. data/tests/tc_tag_scalar_nonspace_string.rb +46 -0
  64. data/tests/tc_tag_scalar_string.rb +47 -0
  65. data/tests/tc_tag_scalar_vector_int.rb +88 -0
  66. data/tests/tc_tag_scalar_vector_string.rb +48 -0
  67. data/tests/tc_tag_scalar_vector_string_empty.rb +40 -0
  68. data/tests/tc_tag_vector_of_different_tags.rb +109 -0
  69. data/tests/tc_tag_vector_of_tags.rb +103 -0
  70. data/tests/ts_cls_ruby.rb +7 -0
  71. metadata +140 -0
@@ -0,0 +1,270 @@
1
+ #
2
+ # ������ ������� ����� �������� ��������� �������� ��� TagScalar.
3
+ #
4
+
5
+ require 'set'
6
+ require 'time'
7
+
8
+ require 'cls-ruby/tag_scalar'
9
+ require 'cls-ruby/ex'
10
+
11
+ module ClsRuby
12
+
13
+ # ��������� ������, ������� ��������� ������� �������� ��� TagScalar.
14
+ #
15
+ module ScalarFormat
16
+ # ���������� ��� �������� ������ :tok_nonspace.
17
+ #
18
+ # ������� ���������� ��������� ���������� UnexpectedTokenEx.
19
+ def on_tok_nonspace( token )
20
+ raise UnexpectedTokenEx.new(
21
+ ':tok_nonspace unexpected for this format' )
22
+ end
23
+
24
+ # ���������� ��� �������� ������ :tok_string.
25
+ #
26
+ # ������� ���������� ��������� ���������� UnexpectedTokenEx.
27
+ def on_tok_string( token )
28
+ raise UnexpectedTokenEx.new(
29
+ ':tok_string unexpected for this format' )
30
+ end
31
+
32
+ # ���������� ��� �������������� �������� ��� ���������
33
+ # � �������� �����.
34
+ def format( what )
35
+ raise NotSupportedEx.new( 'format not suppoted by ScalarFormat' )
36
+ end
37
+ end
38
+
39
+ # ��������� ������, ������� ��������� �������� ������������ �������� ��������
40
+ # ��� TagScalar.
41
+ #
42
+ # ���� ��������� ������ ������ ��� ����, ����� ����������������� � ����������
43
+ # ���� ���������� � ������� ����������.
44
+ # �� ��������, ���������� ������������ ����� ���, � ������� ���������
45
+ # �������� === (��������, ����� Range).
46
+ #
47
+ class ScalarChecker
48
+ def ===(other)
49
+ raise Ex.new( 'ScalarChecker cannot be used directly!' )
50
+ end
51
+ end
52
+
53
+ # ��������������� �����, ������� �������� �� �������������� ���������
54
+ # �������� � ������������ � CLS ��������.
55
+ class FormatHelper
56
+ # �������������� ������ � :tok_nonspace.
57
+ def FormatHelper.to_tok_nonspace( what )
58
+ translate( what )
59
+ end
60
+
61
+ # �������������� ������ � :tok_space.
62
+ #
63
+ # ��������� ������������ �������� � ������� �������.
64
+ def FormatHelper.to_tok_string( what )
65
+ '"' << translate( what ) << '"'
66
+ end
67
+
68
+ private
69
+ # ��������������� ������, ������� �������� �� �������������
70
+ # ���������� escaping-� ����������� ��������.
71
+ class SpecCharDectector
72
+ @@spec_chars = Set.new( [ ?{, ?}, ?\\, ?|, ?", ?\n, ?\t, ?\r ] )
73
+
74
+ def ===( byte )
75
+ @@spec_chars.member?( byte )
76
+ end
77
+ end
78
+
79
+ # �������, ������� �������� ����������� escaping-�.
80
+ SPEC_CHARS = SpecCharDectector.new
81
+ # �������, ������� �������� ��������� escaping-�.
82
+ UNPRINTABLE_CHARS = 0...32
83
+
84
+ # �������������� ������ �������� CLS �������.
85
+ def FormatHelper.translate( what )
86
+ r = ''
87
+ what.each_byte do |byte|
88
+ case byte
89
+ when SPEC_CHARS
90
+ r << '\\'
91
+ if ?\n == byte
92
+ r << 'n'
93
+ elsif ?\t == byte
94
+ r << 't'
95
+ elsif ?\r == byte
96
+ r << 'r'
97
+ else
98
+ r << byte
99
+ end
100
+ when UNPRINTABLE_CHARS
101
+ r << "\\x%02x" % byte
102
+ else
103
+ r << byte
104
+ end
105
+ end
106
+
107
+ r
108
+ end
109
+ end
110
+
111
+ # ������� ��������� ��� �������� ����������� ����� ����� ��
112
+ # ���������, ������������� � ������������������ �������������.
113
+ module UnsignedIntBinOclHexParsing
114
+ # �������� ��������� ������� � ���������� ����������� �����.
115
+ # ���� ������ ��������� �� �������, �� ��������� ���������� InvalidValueEx.
116
+ def try_parse_bin_oct_hex( token )
117
+ case token
118
+ when /^0b([01]+)$/i
119
+ $1.to_i( 2 )
120
+ when /^0o([0-7]+)$/i
121
+ $1.to_i( 8 )
122
+ when /^0x([[:xdigit:]]+)$/i
123
+ $1.to_i( 16 )
124
+ else
125
+ raise InvalidValueEx.new( "value '#{token}' is not an integer" )
126
+ end
127
+ end
128
+ end
129
+
130
+ # ����� ������� ����������� ����� �����.
131
+ class UnsignedIntScalarParser
132
+ include ScalarFormat
133
+ include UnsignedIntBinOclHexParsing
134
+
135
+ def on_tok_nonspace( token )
136
+ case token
137
+ when /^[[:digit:]]+$/
138
+ token.to_i( 10 )
139
+ else
140
+ try_parse_bin_oct_hex( token )
141
+ end
142
+ end
143
+
144
+ def format( what )
145
+ what.to_s
146
+ end
147
+ end
148
+
149
+ # ����� ������� ����� ����� �� ������.
150
+ #
151
+ # ���� �������������� ������ ��� ���������� ������.
152
+ class SignedIntScalarParser
153
+ include ScalarFormat
154
+ include UnsignedIntBinOclHexParsing
155
+
156
+ def on_tok_nonspace( token )
157
+ case token
158
+ when /^[+-]?[[:digit:]]+$/
159
+ token.to_i( 10 )
160
+ else
161
+ try_parse_bin_oct_hex( token )
162
+ end
163
+ end
164
+
165
+ def format( what )
166
+ what.to_s
167
+ end
168
+ end
169
+
170
+ # ����� ������� ������������ �����.
171
+ class FloatingPointScalarParser
172
+ include ScalarFormat
173
+
174
+ def on_tok_nonspace( token )
175
+ case token
176
+ when /^([+-]?[[:digit:]]*[\.]?[[:digit:]]*([Ee][+-]?[[:digit:]]+)?)$/
177
+ token.to_f
178
+ else
179
+ raise InvalidValueEx.new(
180
+ "value '#{token}' is not a floating point number" )
181
+ end
182
+ end
183
+
184
+ def format( what )
185
+ what.to_s
186
+ end
187
+ end
188
+
189
+ # ����� ������� ��������� ��������.
190
+ #
191
+ # ������ ���������� �������� ������ :tok_string.
192
+ class StringScalarParser
193
+ include ScalarFormat
194
+
195
+ def on_tok_string( token )
196
+ token
197
+ end
198
+
199
+ def format( what )
200
+ FormatHelper.to_tok_string( what )
201
+ end
202
+ end
203
+
204
+ # ����� ������� ��������, ������� �������� ��������, �� ��������
205
+ # ����� :tok_nonspace.
206
+ #
207
+ # ������ ���������� �������� ������ :tok_nonspace.
208
+ class NonspaceStringScalarParser
209
+ include ScalarFormat
210
+
211
+ def on_tok_nonspace( token )
212
+ token
213
+ end
214
+
215
+ def format( what )
216
+ FormatHelper.to_tok_nonspace( what )
217
+ end
218
+ end
219
+
220
+ # ����� ������� �������� ���� Time � ������� XML Schema.
221
+ #
222
+ # �������� ������� ������ ���� ������������ ������� :tok_string.
223
+ #
224
+ # ����� ����������� � ����������� ��� UTC. ��� ����, ����� �������������
225
+ # ����� � ���������, ����� ��������������� ������� Time#getlocal.
226
+ # ��������� ��. �������� Time, Time#xmlschema, Time#getutc, Time#getlocal.
227
+ #
228
+ # ������:
229
+ # # ������������ ������ ��� ����� ����� �������.
230
+ # child_tag :when, ClsRuby::TagScalar,
231
+ # :format => ClsRuby::TimeXmlSchemaScalarParser.new
232
+ #
233
+ # # ����������� ������ � ��������� �����������.
234
+ # child_tag :when, ClsRuby::TagScalar,
235
+ # :format => ClsRuby::TimeXmlSchemaScalarParser.new( 3 )
236
+ #
237
+ class TimeXmlSchemaScalarParser
238
+ include ScalarFormat
239
+
240
+ # _fraction_digits_ ������ ���������� ������, ������� �����
241
+ # ������������ � �������� �����/�������� ����� �������.
242
+ #
243
+ # �� ��������� ������� ������������ ��� ������� �����.
244
+ def initialize( fraction_digits = 0 )
245
+ @fraction_digits = fraction_digits
246
+ end
247
+
248
+ def on_tok_string( token )
249
+ Time.xmlschema( token ).getutc
250
+ end
251
+
252
+ def format( what )
253
+ FormatHelper.to_tok_string( what.getutc.xmlschema( @fraction_digits ) )
254
+ end
255
+ end
256
+
257
+ # ������ ����� ����� �� ������.
258
+ SCALAR_INT = SignedIntScalarParser.new
259
+ # ������ ����������� ����� ����� �� ������.
260
+ SCALAR_UINT = UnsignedIntScalarParser.new
261
+ # ������ ������������ �����.
262
+ SCALAR_FLOAT = FloatingPointScalarParser.new
263
+
264
+ # ������ ��������� ��������.
265
+ SCALAR_STRING = StringScalarParser.new
266
+ # ������ ��������� ��������, ������� �������� ��� :tok_nonspace.
267
+ SCALAR_NONSPACE_STRING = NonspaceStringScalarParser.new
268
+
269
+ end
270
+
@@ -0,0 +1,36 @@
1
+ #
2
+ # ����� �����������, ������� �������, ����� �������� ������� � ������
3
+ # ����������� ��������.
4
+ #
5
+
6
+ require 'set'
7
+
8
+ module ClsRuby
9
+
10
+ module Constraints
11
+
12
+ # ����� �����������, ������� �������, ����� �������� ������� � ������
13
+ # ����������� ��������.
14
+ #
15
+ # ��������:
16
+ # child_tag :mode, ClsRuby::TagStringScalar,
17
+ # :constraint => ClsRuby::Constraints::OneOf.new(
18
+ # "user", "root", "observer", "guest" )
19
+ # ������� ��������� ������ � ������������. ������ ����� ���������
20
+ # ������ ���� �� ��������: user, root, observer ��� guest.
21
+ #
22
+ class OneOf
23
+ # ����������� ������� ������������ ����������� ��������.
24
+ def initialize( *allowed_values )
25
+ @allowed_values = Set.new( allowed_values )
26
+ end
27
+
28
+ def ===(value)
29
+ @allowed_values.member?( value )
30
+ end
31
+ end
32
+
33
+ end # module Constraints
34
+
35
+ end # module ClsRuby
36
+
@@ -0,0 +1,50 @@
1
+ #
2
+ # ����� ������������ ��������� �����.
3
+ #
4
+
5
+ module ClsRuby
6
+
7
+ # ����� ������������ ��������� �����.
8
+ #
9
+ # ����� ������� ���������� ���� ����������� ������� ������.
10
+ # ������� ����������� ����������, ���� � ��������� ������������
11
+ # ���� �� ����� �������� margin.
12
+ #
13
+ # ������ �������������:
14
+ # result = ''
15
+ # formatter = ClsRuby::DefaultFormatter.new( result )
16
+ # some_tag.tag_format( formatter )
17
+ #
18
+ class DefaultFormatter
19
+ # [_receiver_] ��� ������, ��� �������� ��������� �������� ������ <<.
20
+ # [_margin_] ��� �������� ������, ������� ����� ��������������
21
+ # ��� ������������ ��������.
22
+ #
23
+ def initialize( receiver, margin = "\t" )
24
+ @receiver = receiver
25
+ @margin = margin
26
+ @deep = 0
27
+ end
28
+
29
+ def start( name )
30
+ @receiver << "\n" << make_margin << '{' << name
31
+ @deep += 1
32
+ end
33
+
34
+ def value( what )
35
+ @receiver << ' ' << what
36
+ end
37
+
38
+ def finish
39
+ @receiver << ' }'
40
+ @deep -= 1
41
+ end
42
+
43
+ private
44
+ def make_margin
45
+ @margin * @deep
46
+ end
47
+ end
48
+
49
+ end # module ClsRuby
50
+
@@ -0,0 +1,121 @@
1
+ #
2
+ # ������ ���������� ���������� ClsRuby.
3
+ #
4
+
5
+ module ClsRuby
6
+
7
+ # ������� ����� ��� ���� ����������.
8
+ class Ex < Exception
9
+ end
10
+
11
+ # ������� ����� ��� ����������, ������� ��������� ��� �������� ��������
12
+ # ������. � ������� ��� ��� �������� ��� ������ � ����� ������, � �������
13
+ # ��������� ������.
14
+ #
15
+ # ������������� ��������� ��� ������ � ����� ������ � ������ ���������
16
+ # �� ������.
17
+ class ParsingErrorEx < Ex
18
+ # ��� �������� ������.
19
+ attr_reader :stream_name
20
+ # ����� ������ �� ������� ������.
21
+ attr_reader :line_no
22
+
23
+ # ����������, ������� ������� � ��������� ������.
24
+ attr_accessor :nested_exception
25
+
26
+ def initialize( stream_name, line_no, message )
27
+ super( "#{stream_name}:#{line_no}: #{message}" )
28
+
29
+ @stream_name = stream_name
30
+ @line_no = line_no
31
+ end
32
+ end
33
+
34
+ # ����������, ��������� ������������� escape-�������������������
35
+ # �� ������� ������.
36
+ class UnclosedEscapeSeqEx < ParsingErrorEx
37
+ end
38
+
39
+ # ����������, ��������� ������������ escape-�������������������.
40
+ class InvalidEscapeSeqEx < ParsingErrorEx
41
+ end
42
+
43
+ # ����������, ��������� ���������� |-�������������������.
44
+ class UnclosedVerticalBarSeqEx < ParsingErrorEx
45
+ end
46
+
47
+ # ����������, ��������� ������������ |-�������������������.
48
+ class InvalidVerticalBarSeqEx < ParsingErrorEx
49
+ end
50
+
51
+ # ����������, ��������� ���������� ������������� ������������.
52
+ class UnclosedMultilineCommentEx < ParsingErrorEx
53
+ end
54
+
55
+ # ����������, ��������� ���������� �������.
56
+ class UnclosedStringEx < ParsingErrorEx
57
+ end
58
+
59
+ # ����������, ���������� ���, ��� �� ��������� ������������ ���.
60
+ class UndefinedMandatoryTagEx < Ex
61
+ def initialize( tag_name )
62
+ super( "undefined mandatory tag: #{tag_name}" )
63
+ end
64
+ end
65
+
66
+ # ����������, ��������� ���������� �� ������� ������ ������, ��������
67
+ # ��� ���� �� ������ ����.
68
+ class UnexpectedTokenEx < Ex
69
+ end
70
+
71
+ # ����������, ��������� ������������ �� ������� ������ ������������ ����.
72
+ class UnknownTagEx < Ex
73
+ end
74
+
75
+ # ����������, ��������� ���, ��� ���� ����������� ����� �������� ������.
76
+ class EmptyTagStackEx < Ex
77
+ end
78
+
79
+ # ����������, ��������� ���, ��� ���� ����������� ����� �������� �� ������.
80
+ class NonEmptyStackEx < Ex
81
+ end
82
+
83
+ # ����������, ��������� ���, ��� �����-�� ��� ��� �������� ������������.
84
+ class TagAlreadyDefinedEx < Ex
85
+ end
86
+
87
+ # ����������, ��������� ���, ��� ��� TagScalar �� ����� ������ �������.
88
+ class FormatUndefinedEx < Ex
89
+ end
90
+
91
+ # ����������, ��������� ���, ��� ��� TagScalar �� ������� ������ ��
92
+ # ��������� �������� � ������� �������� ����.
93
+ class ValueMissedEx < Ex
94
+ end
95
+
96
+ # ����������, ��������� ���, ��� ��� TagScalar �������� ��� ���� ������.
97
+ class ValueAlreadyDefinedEx < Ex
98
+ end
99
+
100
+ # ����������, ��������� ���, ��� �������� ��� TagScalar �� ������� ���������
101
+ # � �������� ������������ ���������� ��� ���� ��������� �� ����.
102
+ class ValueParsingErrorEx < Ex
103
+ end
104
+
105
+ # ����������, ��������� ���, ��� �������� ��� TagScalar ��������� ��
106
+ # ��������������� �������� ������������.
107
+ class InvalidValueEx < Ex
108
+ end
109
+
110
+ # ����������, ��������� ���, ��� �� ������ �������� ���� ���������� ����
111
+ # ��� TagVectorOfTags.
112
+ class UndefinedTypeEx < Ex
113
+ end
114
+
115
+ # ����������, ��������� ���, ��� ����������� ������������� ����������������
116
+ # �� ��������������.
117
+ class NotSupportedEx < Ex
118
+ end
119
+
120
+ end # module ClsRuby
121
+
@@ -0,0 +1,31 @@
1
+ #
2
+ # ��������� ��������� �����.
3
+ #
4
+
5
+ module ClsRuby
6
+
7
+ # ��������� ��������� �����.
8
+ #
9
+ # ������������ ������ ��� ���������� ������������ ����������
10
+ # ��������� �����.
11
+ #
12
+ module TagFormatter
13
+ # ���������� ��� ������ �������������� ���������� ����.
14
+ def start( name )
15
+ end
16
+
17
+ # ���������� ��� �������������� �������� ���������� ����.
18
+ #
19
+ # ���������, ��� _what_ ��� ����� � CLS ������� � �������
20
+ # �������������� ��� _what_ �� �����������.
21
+ def value( what )
22
+ end
23
+
24
+ # ���������� ��� ���������� �������������� ���������� ����.
25
+ def finish
26
+ end
27
+
28
+ end
29
+
30
+ end # module ClsRuby
31
+
@@ -0,0 +1,157 @@
1
+ #
2
+ # ����������� ���������� ������ ������ -- ������������� ��������� ��������,
3
+ # ��������� ������������ � �����.
4
+ #
5
+
6
+ require 'cls-ruby/ex'
7
+ require 'cls-ruby/lexers/first_stage'
8
+
9
+ module ClsRuby
10
+
11
+ module Lexers
12
+
13
+ # ����������� ����������, ������� �������� ��������� ������� � ��������������
14
+ # ��. ��� �� ����������� � ������� ���������� escape ������������������.
15
+ # ����������� ����������� � ������ � ����� :tok_space.
16
+ #
17
+ # ���������� ��������� ��������� ��������:
18
+ # :tok_open_block
19
+ # :tok_close_block
20
+ # :tok_space
21
+ # :tok_string
22
+ # :tok_nonspace
23
+ # :tok_eof
24
+ #
25
+ class CharClassifier
26
+ # ����, ������� ��������, ��� ������� ����� ��������.
27
+ EOF = [ nil, :tok_eof ]
28
+
29
+ def initialize( stream, stream_name )
30
+ @lexer = FirstStage.new( stream, stream_name )
31
+ end
32
+
33
+ # ���������� ��� ������.
34
+ def stream_name; @lexer.stream_name; end
35
+ # ���������� ����� ������, �� ������� ����������� ������.
36
+ def line_no; @lexer.line_no; end
37
+
38
+ # ���������� ��������� ����.
39
+ #
40
+ # ������������ �������� EOF ���� ��������� ����� ������.
41
+ def next
42
+ n = @lexer.next
43
+ if !n
44
+ EOF
45
+ elsif '|' == n
46
+ parse_comment
47
+ elsif '"' == n
48
+ parse_string
49
+ elsif '\\' == n
50
+ parse_escape_seq
51
+ else
52
+ [ n, classify_char( n ) ]
53
+ end
54
+ end
55
+
56
+ private
57
+ # ���������� ������� ���� �������������, ���� ��������������
58
+ # ������������.
59
+ def parse_comment
60
+ n = @lexer.next
61
+ raise UnclosedVerticalBarSeqEx.new(
62
+ stream_name, line_no,
63
+ "unexpected EOF after |" ) unless n
64
+ if '|' == n
65
+ parse_single_line_comment
66
+ elsif '#' == n
67
+ parse_multi_line_comment
68
+ else
69
+ raise InvalidVerticalBarSeqEx.new(
70
+ stream_name, line_no,
71
+ "invalid |-sequence: |#{n}" )
72
+ end
73
+ end
74
+
75
+ def parse_single_line_comment
76
+ result = '||'
77
+ while nil != ( n = @lexer.next )
78
+ result << n
79
+ break if "\n" == n
80
+ end
81
+ [ result, :tok_space ]
82
+ end
83
+
84
+ def parse_multi_line_comment
85
+ start_line_no = line_no
86
+ result = '|#'
87
+ while nil != ( n = @lexer.next )
88
+ result << n
89
+ break if result[ -2..-1 ] == '#|' && result.size > 3
90
+ end
91
+
92
+ raise UnclosedMultilineCommentEx.new(
93
+ stream_name, line_no,
94
+ "unclosed multiline comment started at: " +
95
+ "#{stream_name}:#{start_line_no}" ) unless n
96
+
97
+ [ result, :tok_space ]
98
+ end
99
+
100
+ # ��������� �� �������� ������ ������.
101
+ #
102
+ # ����������, ����� ����������� ������� ��� ���������.
103
+ # ������������ �������� ������ ��� ����������� �������.
104
+ #
105
+ # ������ '|' �� ����� � ������ ������������ ��������.
106
+ # ������������������ \| ������������� � ������� '|'.
107
+ #
108
+ def parse_string
109
+ start_line_no = line_no
110
+ result = ''
111
+ while nil != ( n = @lexer.next )
112
+ case n
113
+ when '"': return [ result, :tok_string ]
114
+ when '\\' :
115
+ char, tok = parse_escape_seq
116
+ result << char
117
+ else
118
+ result << n
119
+ end
120
+ end
121
+
122
+ raise UnclosedStringEx.new(
123
+ stream_name, line_no,
124
+ "unclosed string started at: #{stream_name}:#{start_line_no}" )
125
+ end
126
+
127
+ # �������������� ������������ �� ������� ������ ���������
128
+ # ����� escape-������������������ � ��������� ������.
129
+ def parse_escape_seq
130
+ n = @lexer.next
131
+ case n
132
+ when /["\\|{}]/: [ n, :tok_nonspace ]
133
+ when 'n' : [ "\n", :tok_space ]
134
+ when 'r' : [ "\r", :tok_space ]
135
+ when 't' : [ "\t", :tok_space ]
136
+ else raise InvalidEscapeSeqEx.new(
137
+ stream_name, line_no,
138
+ "invalid escape sequence: \\#{n}" )
139
+ end
140
+ end
141
+
142
+ # ����������� ���� ���������� �������.
143
+ def classify_char( ch )
144
+ case ch
145
+ when '{': :tok_open_block
146
+ when '}': :tok_close_block
147
+ when /[[:space:]]/: :tok_space
148
+ else :tok_nonspace
149
+ end
150
+ end
151
+
152
+ end
153
+
154
+ end # module Lexers
155
+
156
+ end # module ClsRuby
157
+