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.
- data/LICENSE +26 -0
- data/README +55 -0
- data/THANKS +0 -0
- data/docs/base_formatting_methods +89 -0
- data/docs/base_parsing_methods +79 -0
- data/docs/constructor_params +131 -0
- data/docs/examples/log_single_line_format +3 -0
- data/docs/examples/service_description +3 -0
- data/docs/examples/sms-hist +3 -0
- data/docs/examples/tag_any +3 -0
- data/docs/fragments/custom_tag_field.rb +20 -0
- data/docs/fragments/custom_tag_include.rb +21 -0
- data/docs/fragments/field.cls +2 -0
- data/docs/fragments/include.cls +4 -0
- data/docs/fragments/inherit_tag_params.rb +21 -0
- data/docs/fragments/message.cls +6 -0
- data/docs/fragments/tag_field.rb +24 -0
- data/docs/fragments/tag_message.rb +74 -0
- data/docs/fragments/tags_order.rb +41 -0
- data/docs/principles +402 -0
- data/docs/std_tags_short_description +278 -0
- data/docs/syntax +227 -0
- data/docs/why_cls +178 -0
- data/examples/hex_stream.txt +1 -0
- data/examples/log_single_line_formatter.rb +79 -0
- data/examples/service_description.day_time.cfg +11 -0
- data/examples/service_description.rb +119 -0
- data/examples/sms-hist.rb +164 -0
- data/examples/space_concat.txt +3 -0
- data/examples/tag_any.rb +28 -0
- data/lib/cls-ruby/basic_scalars.rb +270 -0
- data/lib/cls-ruby/constraints/one_of.rb +36 -0
- data/lib/cls-ruby/default_formatter.rb +50 -0
- data/lib/cls-ruby/ex.rb +121 -0
- data/lib/cls-ruby/formatter.rb +31 -0
- data/lib/cls-ruby/lexers/char_classifier.rb +157 -0
- data/lib/cls-ruby/lexers/first_stage.rb +112 -0
- data/lib/cls-ruby/lexers/lexer.rb +74 -0
- data/lib/cls-ruby/oneline_formatter.rb +35 -0
- data/lib/cls-ruby/parser.rb +249 -0
- data/lib/cls-ruby/tag.rb +428 -0
- data/lib/cls-ruby/tag_any.rb +111 -0
- data/lib/cls-ruby/tag_no_value.rb +55 -0
- data/lib/cls-ruby/tag_scalar.rb +197 -0
- data/lib/cls-ruby/tag_scalar_helpers.rb +70 -0
- data/lib/cls-ruby/tag_scalar_vector.rb +148 -0
- data/lib/cls-ruby/tag_vector_of_different_tags.rb +172 -0
- data/lib/cls-ruby/tag_vector_of_tags.rb +116 -0
- data/lib/cls-ruby/vector_of_tags_impl.rb +129 -0
- data/lib/cls-ruby.rb +5 -0
- data/tests/tc_child_tag.rb +51 -0
- data/tests/tc_constraint_one_of.rb +47 -0
- data/tests/tc_format_helper.rb +27 -0
- data/tests/tc_formatters.rb +125 -0
- data/tests/tc_lexer.rb +124 -0
- data/tests/tc_lexer_char_classifier.rb +121 -0
- data/tests/tc_lexer_first_stage.rb +72 -0
- data/tests/tc_parser_simple.rb +93 -0
- data/tests/tc_scalar_parsers.rb +189 -0
- data/tests/tc_tag.rb +68 -0
- data/tests/tc_tag_no_value.rb +46 -0
- data/tests/tc_tag_scalar_int.rb +83 -0
- data/tests/tc_tag_scalar_nonspace_string.rb +46 -0
- data/tests/tc_tag_scalar_string.rb +47 -0
- data/tests/tc_tag_scalar_vector_int.rb +88 -0
- data/tests/tc_tag_scalar_vector_string.rb +48 -0
- data/tests/tc_tag_scalar_vector_string_empty.rb +40 -0
- data/tests/tc_tag_vector_of_different_tags.rb +109 -0
- data/tests/tc_tag_vector_of_tags.rb +103 -0
- data/tests/ts_cls_ruby.rb +7 -0
- 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
|
+
|
data/lib/cls-ruby/ex.rb
ADDED
@@ -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
|
+
|