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,112 @@
|
|
1
|
+
#
|
2
|
+
# ���������� ������ ClsRuby::Lexers::FirstStage
|
3
|
+
#
|
4
|
+
|
5
|
+
require 'cls-ruby/ex.rb'
|
6
|
+
|
7
|
+
module ClsRuby
|
8
|
+
|
9
|
+
module Lexers
|
10
|
+
|
11
|
+
# ����� ��� ���������� ������ ������ ������������ �������:
|
12
|
+
# �������������� �������� escape-������������������� � �������.
|
13
|
+
class FirstStage
|
14
|
+
# ������ � ����� �������� ������.
|
15
|
+
attr_reader :stream_name
|
16
|
+
# ������ � ������ ������ �������� ������, �� ������� ����������� ������.
|
17
|
+
attr_reader :line_no
|
18
|
+
|
19
|
+
# �����������.
|
20
|
+
#
|
21
|
+
# [_stream_] ������� �����. ������, ������� ������������� ������
|
22
|
+
# getc � ungetc ������������� ������� IO#getc � IO#ungetc.
|
23
|
+
# [_stream_name_] ��� �������� ������.
|
24
|
+
def initialize( stream, stream_name )
|
25
|
+
@stream = stream
|
26
|
+
@stream_name = stream_name
|
27
|
+
@line_no = 1
|
28
|
+
end
|
29
|
+
|
30
|
+
# ���������� ���������� ������� �� �������� ������.
|
31
|
+
#
|
32
|
+
# ���������� nil ��� ���������� ����� ������. ��� ��������� �����������
|
33
|
+
# ������.
|
34
|
+
#
|
35
|
+
def next
|
36
|
+
ch = @stream.getc
|
37
|
+
if ch
|
38
|
+
process_next_char( ch.chr )
|
39
|
+
else
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# ��������� ���������� ������������ �� �������� ������ �������.
|
47
|
+
# ��������� �������� �� ������ �������� escape-������������������.
|
48
|
+
#
|
49
|
+
# ���������� ���� ���������� ��� � �������� ��������� ������, ����
|
50
|
+
# ������������ � escape-������������������ ������.
|
51
|
+
def process_next_char( ch )
|
52
|
+
if '\\' == ch
|
53
|
+
n = @stream.getc
|
54
|
+
raise UnclosedEscapeSeqEx.new( stream_name, line_no,
|
55
|
+
"unexpected EOF after \\" ) unless n
|
56
|
+
|
57
|
+
case n.chr
|
58
|
+
when /[obx]/i
|
59
|
+
parse_digital_escape_seq( n.chr )
|
60
|
+
else
|
61
|
+
@stream.ungetc( n )
|
62
|
+
ch
|
63
|
+
end
|
64
|
+
else
|
65
|
+
@line_no += 1 if "\n" == ch
|
66
|
+
ch
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# ���������� � ��������� �������� ������������������ ��
|
71
|
+
# �������� ������ � �������������� �� � ������.
|
72
|
+
#
|
73
|
+
# [_escape_leading_symbol_] ������� ������������������, �������
|
74
|
+
# ���������� ������� ���������.
|
75
|
+
def parse_digital_escape_seq( escape_leading_symbol )
|
76
|
+
enabled_chars, base, max_len = case escape_leading_symbol
|
77
|
+
when /b/i
|
78
|
+
[ /[01]/, 2, 8 ]
|
79
|
+
when /o/i
|
80
|
+
[ /[0-7]/, 8, 3 ]
|
81
|
+
when /x/i
|
82
|
+
[ /[[:xdigit:]]/, 16, 2 ]
|
83
|
+
end
|
84
|
+
|
85
|
+
tmp = ''
|
86
|
+
for i in 1..max_len
|
87
|
+
n = @stream.getc
|
88
|
+
break unless n
|
89
|
+
|
90
|
+
c = n.chr
|
91
|
+
if enabled_chars =~ c
|
92
|
+
tmp << c
|
93
|
+
else
|
94
|
+
# ������ ������ ������, ������� �� �����������
|
95
|
+
# escape-������������������. �������� ����� ���������.
|
96
|
+
@stream.ungetc( n )
|
97
|
+
break
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
raise UnclosedEscapeSeqEx.new( stream_name, line_no,
|
102
|
+
"zero length digital escape sequence \\#{escape_leading_symbol}" ) if
|
103
|
+
0 == tmp.size
|
104
|
+
|
105
|
+
tmp.to_i( base ).chr
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end # Lexers
|
110
|
+
|
111
|
+
end # ClsRuby
|
112
|
+
|
@@ -0,0 +1,74 @@
|
|
1
|
+
#
|
2
|
+
# ����������� ���������� ������� ������ -- ����������� ��������������� ������
|
3
|
+
# ������� :tok_space � :tok_nonspace � ���� �������.
|
4
|
+
#
|
5
|
+
|
6
|
+
require 'cls-ruby/ex'
|
7
|
+
require 'cls-ruby/lexers/char_classifier'
|
8
|
+
|
9
|
+
module ClsRuby
|
10
|
+
|
11
|
+
module Lexers
|
12
|
+
|
13
|
+
# ����������� ����������, ������� ���������� � ���� ������� ������������������
|
14
|
+
# ������� :tok_space � :tok_nonspace.
|
15
|
+
#
|
16
|
+
class Lexer
|
17
|
+
EOF = CharClassifier::EOF
|
18
|
+
|
19
|
+
def initialize( stream, stream_name )
|
20
|
+
@classifier = CharClassifier.new( stream, stream_name )
|
21
|
+
|
22
|
+
# �������� ��������� ����������� �� �������� ������ �����,
|
23
|
+
# ������� �� �������� :tok_space ��� :tok_nonspace.
|
24
|
+
# ���� ����� nil, �� �� ������ ����� ��������� ��������� ������.
|
25
|
+
@last_unprocessed_pair = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def stream_name; @classifier.stream_name; end
|
29
|
+
def line_no; @classifier.line_no; end
|
30
|
+
|
31
|
+
# ���������� ��������� �������.
|
32
|
+
#
|
33
|
+
# ���������� �������� EOF, ���� ��������� ����� ������.
|
34
|
+
def next
|
35
|
+
pair = get_next_pair
|
36
|
+
if :tok_space == pair[ 1 ] || :tok_nonspace == pair[ 1 ]
|
37
|
+
union_sequence( pair )
|
38
|
+
else
|
39
|
+
pair
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
# ���������� ��������� ������� �� ������.
|
45
|
+
#
|
46
|
+
# ���� ���� �������������� ������� � @last_unprocessed_pair, ��
|
47
|
+
# ������������ ��� ��� ������������� ������.
|
48
|
+
def get_next_pair
|
49
|
+
n = @last_unprocessed_pair; @last_unprocessed_pair = nil
|
50
|
+
n = @classifier.next unless n
|
51
|
+
n
|
52
|
+
end
|
53
|
+
|
54
|
+
# ����������� ���� ��������������� ������ ������ � ����,
|
55
|
+
# ���� ��� ��������� � ������ ��������.
|
56
|
+
def union_sequence( first )
|
57
|
+
result = first.dup
|
58
|
+
while EOF != ( n = @classifier.next )
|
59
|
+
if result[ 1 ] == n[ 1 ]
|
60
|
+
result[ 0 ] << n[ 0 ]
|
61
|
+
else
|
62
|
+
@last_unprocessed_pair = n
|
63
|
+
break
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
result
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end # Lexers
|
72
|
+
|
73
|
+
end # ClsRuby
|
74
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
#
|
2
|
+
# ����� ��������� �����, ������� ����������� ��� �������� � ���� ������.
|
3
|
+
#
|
4
|
+
|
5
|
+
module ClsRuby
|
6
|
+
|
7
|
+
# ����� ��������� �����, ������� ����������� ��� �������� � ���� ������.
|
8
|
+
#
|
9
|
+
# ������ �������������:
|
10
|
+
# result = ''
|
11
|
+
# formatter = ClsRuby::OneLineFormatter.new( result )
|
12
|
+
# some_tag.tag_format( formatter )
|
13
|
+
#
|
14
|
+
class OneLineFormatter
|
15
|
+
# [_receiver_] ��� ������, ��� �������� ��������� �������� ������ <<.
|
16
|
+
#
|
17
|
+
def initialize( receiver )
|
18
|
+
@receiver = receiver
|
19
|
+
end
|
20
|
+
|
21
|
+
def start( name )
|
22
|
+
@receiver << '{' << name << ' '
|
23
|
+
end
|
24
|
+
|
25
|
+
def value( what )
|
26
|
+
@receiver << what << ' '
|
27
|
+
end
|
28
|
+
|
29
|
+
def finish
|
30
|
+
@receiver << '} '
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end # module ClsRuby
|
35
|
+
|
@@ -0,0 +1,249 @@
|
|
1
|
+
#
|
2
|
+
# ����� ������� CLS ������.
|
3
|
+
#
|
4
|
+
|
5
|
+
require 'stringio'
|
6
|
+
|
7
|
+
require 'cls-ruby/tag'
|
8
|
+
require 'cls-ruby/lexers/lexer'
|
9
|
+
|
10
|
+
module ClsRuby
|
11
|
+
|
12
|
+
# ������� ������, ������������ � ������� IO.
|
13
|
+
#
|
14
|
+
# ������:
|
15
|
+
# fisrt = TagFirst.new
|
16
|
+
# second = TagSecond.new
|
17
|
+
# ...
|
18
|
+
# ClsRuby.parse_io( stream, 'some_name', first, second )
|
19
|
+
#
|
20
|
+
def ClsRuby.parse_io( stream, stream_name, *tags )
|
21
|
+
parser = Parser.new( stream, stream_name, tags )
|
22
|
+
parser.parse
|
23
|
+
|
24
|
+
tags
|
25
|
+
end
|
26
|
+
|
27
|
+
# ������� ������, ������������ � ������� String.
|
28
|
+
#
|
29
|
+
# ������:
|
30
|
+
# content = File.read( some_name )
|
31
|
+
# first = TagFirst.new
|
32
|
+
# second = TagSecond.new
|
33
|
+
# ...
|
34
|
+
# ClsRuby.parse_string( content, first, second )
|
35
|
+
#
|
36
|
+
# ������ ����� ������ ����� �������������� �������� '-'.
|
37
|
+
def ClsRuby.parse_string( string, *tags )
|
38
|
+
stream = StringIO.new( string )
|
39
|
+
parse_io( stream, '-', *tags )
|
40
|
+
end
|
41
|
+
|
42
|
+
# ������� �����.
|
43
|
+
#
|
44
|
+
# ������:
|
45
|
+
# fisrt = TagFirst.new
|
46
|
+
# second = TagSecond.new
|
47
|
+
# ...
|
48
|
+
# ClsRuby.parse_file( 'some_name', first, second )
|
49
|
+
def ClsRuby.parse_file( file_name, *tags )
|
50
|
+
File.open( file_name, 'r' ) do |f|
|
51
|
+
parse_io( f, file_name, *tags )
|
52
|
+
end
|
53
|
+
|
54
|
+
tags
|
55
|
+
end
|
56
|
+
|
57
|
+
# ����� ������� CLS ������.
|
58
|
+
#
|
59
|
+
# ������ �������������
|
60
|
+
# parser = Parser.new( stream, stream_name, tags )
|
61
|
+
# parser.parse
|
62
|
+
# ���� �� ����� �������� �������������� ������, �� ����� Parser#parse
|
63
|
+
# ����������� ����������.
|
64
|
+
#
|
65
|
+
class Parser
|
66
|
+
# �����������.
|
67
|
+
#
|
68
|
+
# [_stream_] ������� �����. ������ ������������� ������ getc � ungetc
|
69
|
+
# ����������� ������� �� ������ IO.
|
70
|
+
# [_stream_name_] ��� �������� ������.
|
71
|
+
# [_tags_] ������ �����, ������� ����� ����������� � ��������.
|
72
|
+
def initialize( stream, stream_name, tags )
|
73
|
+
@stack = [ TopLevelTag.new( tags ) ]
|
74
|
+
@lexer = Lexers::Lexer.new( stream, stream_name )
|
75
|
+
end
|
76
|
+
|
77
|
+
# ���������� �������� �������� ������.
|
78
|
+
#
|
79
|
+
# ��������� �������� ����� ����������� � �����, ������� ����
|
80
|
+
# ��������� � ������������.
|
81
|
+
def parse
|
82
|
+
begin
|
83
|
+
run_parsing
|
84
|
+
rescue Exception => x
|
85
|
+
envelop_and_reraise_exception( x )
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
EOF = Lexers::Lexer::EOF
|
91
|
+
|
92
|
+
# ������������ ����� ������ ������� � ��������, ������� �����
|
93
|
+
# �������� � �������� ���� ��� ��������� ����.
|
94
|
+
TOKEN_HANDLES = {
|
95
|
+
:tok_space => :tag_on_tok_space,
|
96
|
+
:tok_nonspace => :tag_on_tok_nonspace,
|
97
|
+
:tok_string => :tag_on_tok_string
|
98
|
+
}
|
99
|
+
|
100
|
+
# ���������, ������� ��������, ��� ����������� ������ � ������
|
101
|
+
# �������� �������� ����� �����, ���� ���� ��� TopLevelTag.
|
102
|
+
ACCESS_TO_TOPLEVEL_ENABLED = 0
|
103
|
+
|
104
|
+
# ��������������� ����� ����, ������� ����� ��������� � �������
|
105
|
+
# ����� �������. ��� ������ -- ��������� ���������� ����������, ����
|
106
|
+
# �� ����� ������� ������ ����������� ������ :tok_string ���
|
107
|
+
# :tok_nonspace.
|
108
|
+
#
|
109
|
+
# �������� � ������������ ������ �������� ����� � ������������� ���������
|
110
|
+
# �� � ���� � �������� ��������.
|
111
|
+
class TopLevelTag < Tag
|
112
|
+
def initialize( tags )
|
113
|
+
super( { :name => '' } )
|
114
|
+
|
115
|
+
tags.each do |t| tag_add( t ) end
|
116
|
+
end
|
117
|
+
|
118
|
+
def tag_on_tok_nonspace( token )
|
119
|
+
raise UnexpectedTokenEx.new(
|
120
|
+
"unexpected :tok_nonspace at top level ('#{token}')" )
|
121
|
+
end
|
122
|
+
|
123
|
+
def tag_on_tok_string( token )
|
124
|
+
raise UnexpectedTokenEx.new(
|
125
|
+
"expected :tok_string at top level ('#{token}')" )
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# ���������� �������� �������� �� ��������.
|
130
|
+
def run_parsing
|
131
|
+
while EOF != ( n = @lexer.next )
|
132
|
+
if n[1] == :tok_open_block
|
133
|
+
process_tag_start
|
134
|
+
elsif n[1] == :tok_close_block
|
135
|
+
process_tag_finish
|
136
|
+
else
|
137
|
+
process_token( n )
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
process_parsing_finish
|
142
|
+
end
|
143
|
+
|
144
|
+
# ��������� ���������� ParsingErrorEx, � ������� ��������� ��������
|
145
|
+
# �������������� ����������. ������ � ������� � ����� �������������
|
146
|
+
# ��������.
|
147
|
+
def envelop_and_reraise_exception( x )
|
148
|
+
open_tags_names = make_open_tags_names.chop
|
149
|
+
open_tags_names += ': ' if 0 != open_tags_names.size
|
150
|
+
envelop = ParsingErrorEx.new(
|
151
|
+
@lexer.stream_name,
|
152
|
+
@lexer.line_no,
|
153
|
+
"#{open_tags_names} #{x.message} [#{x.class.name}]" )
|
154
|
+
envelop.nested_exception = x
|
155
|
+
envelop.set_backtrace( x.backtrace )
|
156
|
+
raise envelop
|
157
|
+
end
|
158
|
+
|
159
|
+
# ��������� ������ ���������� ����.
|
160
|
+
#
|
161
|
+
# ��������� ������, ���� ������ ��������� �� �������� ������
|
162
|
+
# ���-����, �������� �� :tok_non_space.
|
163
|
+
def process_tag_start
|
164
|
+
n = @lexer.next
|
165
|
+
if :tok_nonspace == n[1]
|
166
|
+
try_start_tag_with_name( n[0] )
|
167
|
+
else
|
168
|
+
raise UnexpectedTokenEx.new( "tag name expected instead of #{n[1]}" )
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# ��������� ���������� ���������� ����.
|
173
|
+
def process_tag_finish
|
174
|
+
tag_to_finish = top_tag
|
175
|
+
tag_to_finish.tag_on_finish
|
176
|
+
pop_tag
|
177
|
+
|
178
|
+
top_tag( ACCESS_TO_TOPLEVEL_ENABLED ).tag_on_tag( tag_to_finish )
|
179
|
+
end
|
180
|
+
|
181
|
+
# �������� ���� �������� ����.
|
182
|
+
def process_token( token )
|
183
|
+
method = TOKEN_HANDLES.fetch( token[1], nil )
|
184
|
+
raise UnexpectedTokenEx.new( "unable to handle token #{token[1]} " +
|
185
|
+
"('#{token[0]}')" ) unless method
|
186
|
+
|
187
|
+
# ���� ����� ����� �������� ���� � TopLevelTag.
|
188
|
+
top_tag( ACCESS_TO_TOPLEVEL_ENABLED ).send( method, token[0] )
|
189
|
+
end
|
190
|
+
|
191
|
+
# ��������� ���������� ��������.
|
192
|
+
#
|
193
|
+
# � ����� ������ ���� ������ ���� ��� � ��� TopLevelTag ����������
|
194
|
+
# tag_on_finish ��� ����, ����� ���������, ��� ��� ������������ ����
|
195
|
+
# ����������.
|
196
|
+
def process_parsing_finish
|
197
|
+
raise NonEmptyStackEx.new( "too few }" ) if 1 != @stack.size
|
198
|
+
@stack[ 0 ].tag_on_finish
|
199
|
+
end
|
200
|
+
|
201
|
+
# ������� ������ ���� � ��������� ������.
|
202
|
+
#
|
203
|
+
# ���������, ���� �� ��� � ����� ������ � ������ �������� �����
|
204
|
+
# �������� ����.
|
205
|
+
def try_start_tag_with_name( name )
|
206
|
+
tag_to_start = top_tag( ACCESS_TO_TOPLEVEL_ENABLED ).tag_tags.find do |tag|
|
207
|
+
tag.tag_compare_name( name )
|
208
|
+
end
|
209
|
+
|
210
|
+
raise UnknownTagEx.new(
|
211
|
+
"unknown tag name: #{name}" ) unless tag_to_start
|
212
|
+
|
213
|
+
tag_to_start.tag_on_start( name )
|
214
|
+
push_tag( tag_to_start )
|
215
|
+
end
|
216
|
+
|
217
|
+
# ��������� ������� � ������ �������� ���� � �����.
|
218
|
+
#
|
219
|
+
# ��������� ����������, ���� � ����� �������� ����� ���� TopLevelTag.
|
220
|
+
def top_tag( danger_limit = 1 )
|
221
|
+
raise EmptyTagStackEx.new( "no tags in stack" ) if danger_limit == @stack.size
|
222
|
+
@stack.last
|
223
|
+
end
|
224
|
+
|
225
|
+
# ��������� ���������� ���� � ������� �����.
|
226
|
+
def push_tag( tag )
|
227
|
+
@stack.push( tag )
|
228
|
+
end
|
229
|
+
|
230
|
+
# ������ ���� � ������� �����.
|
231
|
+
#
|
232
|
+
# ��������� ����������, ���� � ����� �������� ����� ���� TopLevelTag.
|
233
|
+
def pop_tag
|
234
|
+
raise EmptyTagStackEx.new( "too many }" ) if 1 >= @stack.size
|
235
|
+
@stack.pop
|
236
|
+
end
|
237
|
+
|
238
|
+
# ���������� � ���� ������ ����� ���� ���������� � �������� ������� �����.
|
239
|
+
def make_open_tags_names
|
240
|
+
@stack[ 1...@stack.size ].inject( '' ) do
|
241
|
+
|names, tag|
|
242
|
+
names << "{#{tag.tag_name} "
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
end
|
247
|
+
|
248
|
+
end # module ClsRuby
|
249
|
+
|