racc 1.5.2 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. checksums.yaml +4 -4
  2. data/README.ja.rdoc +0 -1
  3. data/README.rdoc +6 -7
  4. data/{rdoc → doc}/en/NEWS.en.rdoc +0 -0
  5. data/{rdoc → doc}/en/grammar.en.rdoc +27 -31
  6. data/doc/en/grammar2.en.rdoc +219 -0
  7. data/{rdoc → doc}/ja/NEWS.ja.rdoc +0 -0
  8. data/{rdoc → doc}/ja/command.ja.html +0 -0
  9. data/{rdoc → doc}/ja/debug.ja.rdoc +0 -0
  10. data/{rdoc → doc}/ja/grammar.ja.rdoc +0 -0
  11. data/{rdoc → doc}/ja/index.ja.html +0 -0
  12. data/{rdoc → doc}/ja/parser.ja.rdoc +0 -0
  13. data/{rdoc → doc}/ja/usage.ja.html +0 -0
  14. data/ext/racc/cparse/cparse.c +1 -1
  15. data/ext/racc/cparse/extconf.rb +1 -0
  16. data/lib/racc/info.rb +1 -1
  17. data/lib/racc/parser-text.rb +1 -1
  18. data/lib/racc/parser.rb +1 -1
  19. data/lib/racc/parserfilegenerator.rb +0 -44
  20. data/lib/racc/statetransitiontable.rb +2 -8
  21. metadata +15 -121
  22. data/Rakefile +0 -79
  23. data/ext/racc/com/headius/racc/Cparse.java +0 -849
  24. data/lib/racc/pre-setup +0 -13
  25. data/sample/array.y +0 -67
  26. data/sample/array2.y +0 -59
  27. data/sample/calc-ja.y +0 -66
  28. data/sample/calc.y +0 -65
  29. data/sample/conflict.y +0 -15
  30. data/sample/hash.y +0 -60
  31. data/sample/lalr.y +0 -17
  32. data/sample/lists.y +0 -57
  33. data/sample/syntax.y +0 -46
  34. data/sample/yyerr.y +0 -46
  35. data/test/assets/cadenza.y +0 -170
  36. data/test/assets/cast.y +0 -926
  37. data/test/assets/chk.y +0 -126
  38. data/test/assets/conf.y +0 -16
  39. data/test/assets/csspool.y +0 -729
  40. data/test/assets/digraph.y +0 -29
  41. data/test/assets/echk.y +0 -118
  42. data/test/assets/edtf.y +0 -583
  43. data/test/assets/err.y +0 -60
  44. data/test/assets/error_recovery.y +0 -35
  45. data/test/assets/expect.y +0 -7
  46. data/test/assets/firstline.y +0 -4
  47. data/test/assets/huia.y +0 -318
  48. data/test/assets/ichk.y +0 -102
  49. data/test/assets/intp.y +0 -546
  50. data/test/assets/journey.y +0 -47
  51. data/test/assets/liquor.y +0 -313
  52. data/test/assets/machete.y +0 -423
  53. data/test/assets/macruby.y +0 -2197
  54. data/test/assets/mailp.y +0 -437
  55. data/test/assets/mediacloth.y +0 -599
  56. data/test/assets/mof.y +0 -649
  57. data/test/assets/namae.y +0 -302
  58. data/test/assets/nasl.y +0 -626
  59. data/test/assets/newsyn.y +0 -25
  60. data/test/assets/noend.y +0 -4
  61. data/test/assets/nokogiri-css.y +0 -255
  62. data/test/assets/nonass.y +0 -41
  63. data/test/assets/normal.y +0 -27
  64. data/test/assets/norule.y +0 -4
  65. data/test/assets/nullbug1.y +0 -25
  66. data/test/assets/nullbug2.y +0 -15
  67. data/test/assets/opal.y +0 -1807
  68. data/test/assets/opt.y +0 -123
  69. data/test/assets/percent.y +0 -35
  70. data/test/assets/php_serialization.y +0 -98
  71. data/test/assets/recv.y +0 -97
  72. data/test/assets/riml.y +0 -665
  73. data/test/assets/rrconf.y +0 -14
  74. data/test/assets/ruby18.y +0 -1943
  75. data/test/assets/ruby19.y +0 -2174
  76. data/test/assets/ruby20.y +0 -2350
  77. data/test/assets/ruby21.y +0 -2359
  78. data/test/assets/ruby22.y +0 -2381
  79. data/test/assets/scan.y +0 -72
  80. data/test/assets/syntax.y +0 -50
  81. data/test/assets/tp_plus.y +0 -622
  82. data/test/assets/twowaysql.y +0 -278
  83. data/test/assets/unterm.y +0 -5
  84. data/test/assets/useless.y +0 -12
  85. data/test/assets/yyerr.y +0 -46
  86. data/test/bench.y +0 -36
  87. data/test/helper.rb +0 -115
  88. data/test/infini.y +0 -8
  89. data/test/regress/cadenza +0 -796
  90. data/test/regress/cast +0 -3428
  91. data/test/regress/csspool +0 -2314
  92. data/test/regress/edtf +0 -1794
  93. data/test/regress/huia +0 -1392
  94. data/test/regress/journey +0 -222
  95. data/test/regress/liquor +0 -885
  96. data/test/regress/machete +0 -833
  97. data/test/regress/mediacloth +0 -1463
  98. data/test/regress/mof +0 -1368
  99. data/test/regress/namae +0 -634
  100. data/test/regress/nasl +0 -2058
  101. data/test/regress/nokogiri-css +0 -836
  102. data/test/regress/opal +0 -6431
  103. data/test/regress/php_serialization +0 -336
  104. data/test/regress/riml +0 -3283
  105. data/test/regress/ruby18 +0 -6344
  106. data/test/regress/ruby22 +0 -7460
  107. data/test/regress/tp_plus +0 -1933
  108. data/test/regress/twowaysql +0 -556
  109. data/test/scandata/brace +0 -7
  110. data/test/scandata/gvar +0 -1
  111. data/test/scandata/normal +0 -4
  112. data/test/scandata/percent +0 -18
  113. data/test/scandata/slash +0 -10
  114. data/test/src.intp +0 -34
  115. data/test/start.y +0 -20
  116. data/test/test_chk_y.rb +0 -52
  117. data/test/test_grammar_file_parser.rb +0 -15
  118. data/test/test_racc_command.rb +0 -339
  119. data/test/test_scan_y.rb +0 -52
  120. data/test/testscanner.rb +0 -51
  121. data/web/racc.en.rhtml +0 -42
  122. data/web/racc.ja.rhtml +0 -51
@@ -1,278 +0,0 @@
1
- # Copyright 2008-2015 Takuto Wada
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
-
15
- class TwoWaySQL::Parser
16
-
17
- rule
18
-
19
- sql : stmt_list
20
- {
21
- result = RootNode.new( val[0] )
22
- }
23
-
24
- stmt_list :
25
- {
26
- result = []
27
- }
28
- | stmt_list stmt
29
- {
30
- result.push val[1]
31
- }
32
-
33
- stmt : primary
34
- | if_stmt
35
- | begin_stmt
36
-
37
- begin_stmt : BEGIN stmt_list END
38
- {
39
- result = BeginNode.new( val[1] )
40
- }
41
-
42
- if_stmt : IF sub_stmt else_stmt END
43
- {
44
- result = IfNode.new( val[0][1], val[1], val[2] )
45
- }
46
-
47
- else_stmt : ELSE sub_stmt
48
- {
49
- result = val[1]
50
- }
51
- |
52
- {
53
- result = nil
54
- }
55
-
56
- sub_stmt : and_stmt
57
- | or_stmt
58
- | stmt_list
59
-
60
- and_stmt : AND stmt_list
61
- {
62
- result = SubStatementNode.new( val[0][1], val[1] )
63
- }
64
-
65
- or_stmt : OR stmt_list
66
- {
67
- result = SubStatementNode.new( val[0][1], val[1] )
68
- }
69
-
70
- primary : IDENT
71
- {
72
- result = LiteralNode.new( val[0][1] )
73
- }
74
- | STRING_LITERAL
75
- {
76
- result = LiteralNode.new( val[0][1] )
77
- }
78
- | AND
79
- {
80
- result = LiteralNode.new( val[0][1] )
81
- }
82
- | OR
83
- {
84
- result = LiteralNode.new( val[0][1] )
85
- }
86
- | SPACES
87
- {
88
- result = WhiteSpaceNode.new( val[0][1], @preserve_space )
89
- }
90
- | COMMA
91
- {
92
- result = LiteralNode.new( val[0][1] )
93
- }
94
- | LPAREN
95
- {
96
- result = LiteralNode.new( val[0][1] )
97
- }
98
- | RPAREN
99
- {
100
- result = LiteralNode.new( val[0][1] )
101
- }
102
- | QUESTION
103
- {
104
- @num_questions += 1
105
- result = QuestionNode.new( @num_questions )
106
- }
107
- | ACTUAL_COMMENT
108
- {
109
- result = ActualCommentNode.new( val[0][1] , val[0][2] )
110
- }
111
- | bind_var
112
- | embed_var
113
-
114
- bind_var : BIND_VARIABLE STRING_LITERAL
115
- {
116
- result = BindVariableNode.new( val[0][1] )
117
- }
118
- | BIND_VARIABLE SPACES STRING_LITERAL
119
- {
120
- result = BindVariableNode.new( val[0][1] )
121
- }
122
- | BIND_VARIABLE IDENT
123
- {
124
- result = BindVariableNode.new( val[0][1] )
125
- }
126
- | BIND_VARIABLE SPACES IDENT
127
- {
128
- result = BindVariableNode.new( val[0][1] )
129
- }
130
- | PAREN_BIND_VARIABLE
131
- {
132
- result = ParenBindVariableNode.new( val[0][1] )
133
- }
134
-
135
- embed_var : EMBED_VARIABLE IDENT
136
- {
137
- result = EmbedVariableNode.new( val[0][1] )
138
- }
139
- | EMBED_VARIABLE SPACES IDENT
140
- {
141
- result = EmbedVariableNode.new( val[0][1] )
142
- }
143
-
144
- end
145
-
146
-
147
- ---- inner
148
-
149
- require 'strscan'
150
-
151
- def initialize(opts={})
152
- opts = {
153
- :debug => false,
154
- :preserve_space => true,
155
- :preserve_comment => false
156
- }.merge(opts)
157
- @yydebug = opts[:debug]
158
- @preserve_space = opts[:preserve_space]
159
- @preserve_comment = opts[:preserve_comment]
160
- @num_questions = 0
161
- end
162
-
163
-
164
- PAREN_EXAMPLE = '\([^\)]+\)'
165
- BEGIN_BIND_VARIABLE = '(\/|\#)\*([^\*]+)\*\1'
166
- BIND_VARIABLE_PATTERN = /\A#{BEGIN_BIND_VARIABLE}\s*/
167
- PAREN_BIND_VARIABLE_PATTERN = /\A#{BEGIN_BIND_VARIABLE}\s*#{PAREN_EXAMPLE}/
168
- EMBED_VARIABLE_PATTERN = /\A(\/|\#)\*\$([^\*]+)\*\1\s*/
169
-
170
- CONDITIONAL_PATTERN = /\A(\/|\#)\*(IF)\s+([^\*]+)\s*\*\1/
171
- BEGIN_END_PATTERN = /\A(\/|\#)\*(BEGIN|END)\s*\*\1/
172
- STRING_LITERAL_PATTERN = /\A(\'(?:[^\']+|\'\')*\')/ ## quoted string
173
- SPLIT_TOKEN_PATTERN = /\A(\S+?)(?=\s*(?:(?:\/|\#)\*|-{2,}|\(|\)|\,))/ ## stop on delimiters --,/*,#*,',',(,)
174
- LITERAL_PATTERN = /\A([^;\s]+)/
175
- SPACES_PATTERN = /\A(\s+)/
176
- QUESTION_PATTERN = /\A\?/
177
- COMMA_PATTERN = /\A\,/
178
- LPAREN_PATTERN = /\A\(/
179
- RPAREN_PATTERN = /\A\)/
180
- ACTUAL_COMMENT_PATTERN = /\A(\/|\#)\*(\s{1,}(?:.*?))\*\1/m ## start with spaces
181
- SEMICOLON_AT_INPUT_END_PATTERN = /\A\;\s*\Z/
182
- UNMATCHED_COMMENT_START_PATTERN = /\A(?:(?:\/|\#)\*)/
183
-
184
- #TODO: remove trailing spaces for S2Dao compatibility, but this spec sometimes causes SQL bugs...
185
- ELSE_PATTERN = /\A\-{2,}\s*ELSE\s*/
186
- AND_PATTERN = /\A(\ *AND)\b/i
187
- OR_PATTERN = /\A(\ *OR)\b/i
188
-
189
-
190
- def parse( io )
191
- @q = []
192
- io.each_line(nil) do |whole|
193
- @s = StringScanner.new(whole)
194
- end
195
- scan_str
196
-
197
- # @q.push [ false, nil ]
198
- @q.push [ false, [@s.pos, nil] ]
199
-
200
- ## call racc's private parse method
201
- do_parse
202
- end
203
-
204
-
205
- ## called by racc
206
- def next_token
207
- @q.shift
208
- end
209
-
210
-
211
- def scan_str
212
- until @s.eos? do
213
- case
214
- when @s.scan(AND_PATTERN)
215
- @q.push [ :AND, [@s.pos, @s[1]] ]
216
- when @s.scan(OR_PATTERN)
217
- @q.push [ :OR, [@s.pos, @s[1]] ]
218
- when @s.scan(SPACES_PATTERN)
219
- @q.push [ :SPACES, [@s.pos, @s[1]] ]
220
- when @s.scan(QUESTION_PATTERN)
221
- @q.push [ :QUESTION, [@s.pos, nil] ]
222
- when @s.scan(COMMA_PATTERN)
223
- @q.push [ :COMMA, [@s.pos, ','] ]
224
- when @s.scan(LPAREN_PATTERN)
225
- @q.push [ :LPAREN, [@s.pos, '('] ]
226
- when @s.scan(RPAREN_PATTERN)
227
- @q.push [ :RPAREN, [@s.pos, ')'] ]
228
- when @s.scan(ELSE_PATTERN)
229
- @q.push [ :ELSE, [@s.pos, nil] ]
230
- when @s.scan(ACTUAL_COMMENT_PATTERN)
231
- @q.push [ :ACTUAL_COMMENT, [@s.pos, @s[1], @s[2]] ] if @preserve_comment
232
- when @s.scan(BEGIN_END_PATTERN)
233
- @q.push [ @s[2].intern, [@s.pos, nil] ]
234
- when @s.scan(CONDITIONAL_PATTERN)
235
- @q.push [ @s[2].intern, [@s.pos, @s[3]] ]
236
- when @s.scan(EMBED_VARIABLE_PATTERN)
237
- @q.push [ :EMBED_VARIABLE, [@s.pos, @s[2]] ]
238
- when @s.scan(PAREN_BIND_VARIABLE_PATTERN)
239
- @q.push [ :PAREN_BIND_VARIABLE, [@s.pos, @s[2]] ]
240
- when @s.scan(BIND_VARIABLE_PATTERN)
241
- @q.push [ :BIND_VARIABLE, [@s.pos, @s[2]] ]
242
- when @s.scan(STRING_LITERAL_PATTERN)
243
- @q.push [ :STRING_LITERAL, [@s.pos, @s[1]] ]
244
- when @s.scan(SPLIT_TOKEN_PATTERN)
245
- @q.push [ :IDENT, [@s.pos, @s[1]] ]
246
- when @s.scan(UNMATCHED_COMMENT_START_PATTERN) ## unmatched comment start, '/*','#*'
247
- raise Racc::ParseError, "unmatched comment. line:[#{line_no(@s.pos)}], str:[#{@s.rest}]"
248
- when @s.scan(LITERAL_PATTERN) ## other string token
249
- @q.push [ :IDENT, [@s.pos, @s[1]] ]
250
- when @s.scan(SEMICOLON_AT_INPUT_END_PATTERN)
251
- #drop semicolon at input end
252
- else
253
- raise Racc::ParseError, "syntax error at or near line:[#{line_no(@s.pos)}], str:[#{@s.rest}]"
254
- end
255
- end
256
- end
257
-
258
-
259
- ## override racc's default on_error method
260
- def on_error(t, v, vstack)
261
- ## cursor in value-stack is an array of two items,
262
- ## that have position value as 0th item. like [731, "ctx[:limit] "]
263
- cursor = vstack.find do |tokens|
264
- tokens.size == 2 and tokens[0].kind_of?(Fixnum)
265
- end
266
- pos = cursor[0]
267
- line = line_no(pos)
268
- rest = @s.string[pos .. -1]
269
- raise Racc::ParseError, "syntax error at or near line:[#{line}], str:[#{rest}]"
270
- end
271
-
272
-
273
- def line_no(pos)
274
- lines = 0
275
- scanned = @s.string[0..(pos)]
276
- scanned.each_line { lines += 1 }
277
- lines
278
- end
data/test/assets/unterm.y DELETED
@@ -1,5 +0,0 @@
1
- # unterminated action
2
-
3
- class A
4
- rule
5
- a: A {
@@ -1,12 +0,0 @@
1
-
2
-
3
- class A
4
- token A B C X
5
- rule
6
-
7
- targ : A list B
8
- | A B C
9
-
10
- list: list X
11
-
12
- end
data/test/assets/yyerr.y DELETED
@@ -1,46 +0,0 @@
1
- #
2
- # yyerror/yyerrok/yyaccept test
3
- #
4
-
5
- class A
6
- rule
7
-
8
- target: a b c
9
-
10
- a:
11
- {
12
- yyerror
13
- raise ArgumentError, "yyerror failed"
14
- }
15
- | error
16
-
17
- b:
18
- {
19
- yyerrok
20
- }
21
-
22
- c:
23
- {
24
- yyaccept
25
- raise ArgumentError, "yyaccept failed"
26
- }
27
-
28
- end
29
-
30
- ---- inner
31
-
32
- def parse
33
- do_parse
34
- end
35
-
36
- def next_token
37
- [false, '$end']
38
- end
39
-
40
- def on_error( *args )
41
- $stderr.puts "on_error called: args=#{args.inspect}"
42
- end
43
-
44
- ---- footer
45
-
46
- A.new.parse
data/test/bench.y DELETED
@@ -1,36 +0,0 @@
1
- class BenchmarkParser
2
-
3
- rule
4
-
5
- target: a a a a a a a a a a;
6
- a: b b b b b b b b b b;
7
- b: c c c c c c c c c c;
8
- c: d d d d d d d d d d;
9
- d: e e e e e e e e e e;
10
-
11
- end
12
-
13
- ---- inner
14
-
15
- def initialize
16
- @old = [ :e, 'e' ]
17
- @i = 0
18
- end
19
-
20
- def next_token
21
- return [false, '$'] if @i >= 10_0000
22
- @i += 1
23
- @old
24
- end
25
-
26
- def parse
27
- do_parse
28
- end
29
-
30
- ---- footer
31
-
32
- require 'benchmark'
33
-
34
- Benchmark.bm do |x|
35
- x.report { BenchmarkParser.new.parse }
36
- end
data/test/helper.rb DELETED
@@ -1,115 +0,0 @@
1
- verbose = $VERBOSE
2
- $VERBOSE = true
3
- begin
4
-
5
- require 'test/unit'
6
- begin
7
- require_relative './lib/core_assertions'
8
- Test::Unit::TestCase.include Test::Unit::CoreAssertions
9
- rescue LoadError
10
- end
11
- require 'racc/static'
12
- require 'fileutils'
13
- require 'tempfile'
14
- require 'timeout'
15
-
16
- module Racc
17
- class TestCase < Test::Unit::TestCase
18
- PROJECT_DIR = File.expand_path(File.join(File.dirname(__FILE__), '..'))
19
-
20
- test_dir = File.join(PROJECT_DIR, 'test')
21
- test_dir = File.join(PROJECT_DIR, 'racc') unless File.exist?(test_dir)
22
- TEST_DIR = test_dir
23
- racc = File.join(PROJECT_DIR, 'bin', 'racc')
24
- racc = File.join(PROJECT_DIR, '..', 'libexec', 'racc') unless File.exist?(racc)
25
- RACC = racc
26
- ASSET_DIR = File.join(TEST_DIR, 'assets') # test grammars
27
- REGRESS_DIR = File.join(TEST_DIR, 'regress') # known-good generated outputs
28
-
29
- INC = [
30
- File.join(PROJECT_DIR, 'lib'),
31
- File.join(PROJECT_DIR, 'ext'),
32
- ].join(':')
33
-
34
- def setup
35
- @TEMP_DIR = Dir.mktmpdir("racc")
36
- @OUT_DIR = File.join(@TEMP_DIR, 'out')
37
- @TAB_DIR = File.join(@TEMP_DIR, 'tab') # generated parsers go here
38
- @LOG_DIR = File.join(@TEMP_DIR, 'log')
39
- @ERR_DIR = File.join(@TEMP_DIR, 'err')
40
- FileUtils.mkdir_p([@OUT_DIR, @TAB_DIR, @LOG_DIR, @ERR_DIR])
41
- FileUtils.cp File.join(TEST_DIR, "src.intp"), @TEMP_DIR
42
- end
43
-
44
- def teardown
45
- FileUtils.rm_f(File.join(@TEMP_DIR, "src.intp"))
46
- FileUtils.rm_rf([@OUT_DIR, @TAB_DIR, @LOG_DIR, @ERR_DIR, @TEMP_DIR])
47
- end
48
-
49
- def assert_compile(asset, args = [], **opt)
50
- file = File.basename(asset, '.y')
51
- args = ([args].flatten) + [
52
- "#{ASSET_DIR}/#{file}.y",
53
- '-Do',
54
- "-O#{@OUT_DIR}/#{file}",
55
- "-o#{@TAB_DIR}/#{file}",
56
- ]
57
- racc(*args, **opt)
58
- end
59
-
60
- def assert_debugfile(asset, ok)
61
- file = File.basename(asset, '.y')
62
- Dir.chdir(@LOG_DIR) do
63
- File.foreach("#{file}.y") do |line|
64
- line.strip!
65
- case line
66
- when /sr/ then assert_equal "sr#{ok[0]}", line
67
- when /rr/ then assert_equal "rr#{ok[1]}", line
68
- when /un/ then assert_equal "un#{ok[2]}", line
69
- when /ur/ then assert_equal "ur#{ok[3]}", line
70
- when /ex/ then assert_equal "ex#{ok[4]}", line
71
- else
72
- raise TestFailed, 'racc outputs unknown debug report???'
73
- end
74
- end
75
- end
76
- end
77
-
78
- def assert_exec(asset)
79
- lib_path = File.expand_path("../../lib", __FILE__)
80
- file = File.basename(asset, '.y')
81
- ruby "-I#{lib_path}", "#{@TAB_DIR}/#{file}"
82
- end
83
-
84
- def strip_version(source)
85
- source.sub(/This file is automatically generated by Racc \d+\.\d+\.\d+/, '')
86
- end
87
-
88
- def assert_output_unchanged(asset)
89
- file = File.basename(asset, '.y')
90
-
91
- # Code to re-generate the expectation files
92
- # File.write("#{REGRESS_DIR}/#{file}", File.read("#{@TAB_DIR}/#{file}"))
93
-
94
- expected = File.read("#{REGRESS_DIR}/#{file}")
95
- actual = File.read("#{@TAB_DIR}/#{file}")
96
- result = (strip_version(expected) == strip_version(actual))
97
-
98
- assert(result, "Output of test/assets/#{file}.y differed from " \
99
- "expectation. Try compiling it and diff with test/regress/#{file}.")
100
- end
101
-
102
- def racc(*arg, **opt)
103
- lib_path = File.expand_path("../../lib", __FILE__)
104
- ruby "-I#{lib_path}", "-S", RACC, *arg, **opt
105
- end
106
-
107
- def ruby(*arg, **opt)
108
- assert_ruby_status(["-C", @TEMP_DIR, *arg], **opt)
109
- end
110
- end
111
- end
112
-
113
- ensure
114
- $VERBOSE = verbose
115
- end
data/test/infini.y DELETED
@@ -1,8 +0,0 @@
1
-
2
- class I
3
-
4
- rule
5
-
6
- list: list X
7
-
8
- end