cucumber 0.6.3 → 0.6.4

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 (54) hide show
  1. data/History.txt +25 -4
  2. data/VERSION.yml +1 -1
  3. data/cucumber.gemspec +33 -29
  4. data/examples/i18n/{cat → ca}/Rakefile +0 -0
  5. data/examples/i18n/{cat → ca}/features/step_definitons/calculator_steps.rb +0 -0
  6. data/examples/i18n/{cat → ca}/features/suma.feature +1 -1
  7. data/examples/i18n/{cat → ca}/lib/calculadora.rb +0 -0
  8. data/examples/i18n/{se → sr-Cyrl}/Rakefile +0 -0
  9. data/examples/i18n/{sr → sr-Cyrl}/features/sabiranje.feature +1 -1
  10. data/examples/i18n/{sr → sr-Cyrl}/features/step_definitons/calculator_steps.rb +0 -0
  11. data/examples/i18n/{sr → sr-Cyrl}/features/support/env.rb +0 -0
  12. data/examples/i18n/{sr-latn → sr-Cyrl}/lib/calculator.rb +0 -0
  13. data/examples/i18n/{sr-latn → sr-Latn}/Rakefile +0 -0
  14. data/examples/i18n/{sr-latn → sr-Latn}/features/sabiranje.feature +0 -0
  15. data/examples/i18n/{sr-latn → sr-Latn}/features/step_definitons/calculator_steps.rb +0 -0
  16. data/examples/i18n/{sr → sr-Latn}/lib/calculator.rb +0 -0
  17. data/examples/i18n/{sr → sv}/Rakefile +0 -0
  18. data/examples/i18n/{se → sv}/features/step_definitons/kalkulator_steps.rb +0 -0
  19. data/examples/i18n/{se → sv}/features/summering.feature +1 -1
  20. data/examples/i18n/{se → sv}/lib/kalkulator.rb +0 -0
  21. data/features/announce.feature +27 -5
  22. data/features/bug_585_tab_indentation.feature +22 -0
  23. data/features/exception_in_after_block.feature +25 -0
  24. data/features/exception_in_before_block.feature +21 -0
  25. data/features/language_help.feature +6 -6
  26. data/features/snippets_when_using_star_keyword.feature +36 -0
  27. data/features/wire_protocol_tags.feature +50 -10
  28. data/gem_tasks/contributors.rake +4 -2
  29. data/lib/autotest/cucumber_rails_rspec2.rb +6 -0
  30. data/lib/autotest/cucumber_rspec2.rb +6 -0
  31. data/lib/cucumber/ast/outline_table.rb +8 -0
  32. data/lib/cucumber/ast/py_string.rb +5 -1
  33. data/lib/cucumber/ast/step_invocation.rb +2 -2
  34. data/lib/cucumber/cli/options.rb +1 -0
  35. data/lib/cucumber/cli/profile_loader.rb +8 -2
  36. data/lib/cucumber/formatter/pdf.rb +9 -2
  37. data/lib/cucumber/formatter/progress.rb +24 -2
  38. data/lib/cucumber/formatter/unicode.rb +11 -11
  39. data/lib/cucumber/languages.yml +14 -13
  40. data/lib/cucumber/parser/feature.rb +0 -257
  41. data/lib/cucumber/parser/feature.tt +0 -32
  42. data/lib/cucumber/parser/i18n.tt +2 -1
  43. data/lib/cucumber/parser/natural_language.rb +7 -4
  44. data/lib/cucumber/parser/py_string.rb +2 -2
  45. data/lib/cucumber/parser/py_string.tt +2 -2
  46. data/lib/cucumber/rb_support/rb_world.rb +9 -0
  47. data/lib/cucumber/step_mother.rb +3 -3
  48. data/spec/cucumber/ast/feature_factory.rb +1 -1
  49. data/spec/cucumber/ast/py_string_spec.rb +4 -4
  50. data/spec/cucumber/ast/step_spec.rb +1 -1
  51. data/spec/cucumber/cli/configuration_spec.rb +7 -0
  52. data/spec/cucumber/cli/profile_loader_spec.rb +29 -4
  53. data/spec/cucumber/step_mother_spec.rb +13 -3
  54. metadata +34 -30
@@ -5,7 +5,15 @@ Feature: Wire protocol tags
5
5
 
6
6
  Background:
7
7
  Given a standard Cucumber project directory structure
8
- And a file named "features/wired.feature" with:
8
+ And a file named "features/step_definitions/some_remote_place.wire" with:
9
+ """
10
+ host: localhost
11
+ port: 54321
12
+
13
+ """
14
+
15
+ Scenario: Run a scenario
16
+ Given a file named "features/wired.feature" with:
9
17
  """
10
18
  @foo @bar
11
19
  Feature: Wired
@@ -15,15 +23,7 @@ Feature: Wire protocol tags
15
23
  Given we're all wired
16
24
 
17
25
  """
18
- And a file named "features/step_definitions/some_remote_place.wire" with:
19
- """
20
- host: localhost
21
- port: 54321
22
-
23
- """
24
-
25
- Scenario: Run the scenario
26
- Given there is a wire server running on port 54321 which understands the following protocol:
26
+ And there is a wire server running on port 54321 which understands the following protocol:
27
27
  | request | response |
28
28
  | ["step_matches",{"name_to_match":"we're all wired"}] | ["success",[{"id":"1", "args":[]}]] |
29
29
  | ["begin_scenario", {"tags":["bar","baz","foo"]}] | ["success"] |
@@ -45,3 +45,43 @@ Feature: Wire protocol tags
45
45
 
46
46
  """
47
47
 
48
+ Scenario: Run a scenario outline example
49
+ Given a file named "features/wired.feature" with:
50
+ """
51
+ @foo @bar
52
+ Feature: Wired
53
+
54
+ @baz
55
+ Scenario Outline: Everybody's Wired
56
+ Given we're all <something>
57
+
58
+ Examples:
59
+ | something |
60
+ | wired |
61
+
62
+ """
63
+ And there is a wire server running on port 54321 which understands the following protocol:
64
+ | request | response |
65
+ | ["step_matches",{"name_to_match":"we're all wired"}] | ["success",[{"id":"1", "args":[]}]] |
66
+ | ["begin_scenario", {"tags":["bar","baz","foo"]}] | ["success"] |
67
+ | ["invoke",{"id":"1","args":[]}] | ["success"] |
68
+ | ["end_scenario", {"tags":["bar","baz","foo"]}] | ["success"] |
69
+ When I run cucumber -f pretty -q
70
+ Then STDERR should be empty
71
+ And it should pass with
72
+ """
73
+ @foo @bar
74
+ Feature: Wired
75
+
76
+ @baz
77
+ Scenario Outline: Everybody's Wired
78
+ Given we're all <something>
79
+
80
+ Examples:
81
+ | something |
82
+ | wired |
83
+
84
+ 1 scenario (1 passed)
85
+ 1 step (1 passed)
86
+
87
+ """
@@ -1,7 +1,9 @@
1
1
  # encoding: utf-8
2
+ desc 'List contributors'
2
3
  task :contributors do
3
- contributors = `git log --pretty=short --no-merges | git shortlog -ne | egrep -ve '^ +' | egrep -ve '^$'`
4
- puts contributors.split("\n").length
4
+ contributors = `git log --pretty=short --no-merges | git shortlog -ne | egrep -ve '^ +' | egrep -ve '^$'`.split("\n")
5
+ puts contributors
6
+ puts "Total: #{contributors.length}"
5
7
  end
6
8
 
7
9
  task :codeswarm do
@@ -0,0 +1,6 @@
1
+ require 'autotest/cucumber_mixin'
2
+ require 'autotest/rails_rspec2'
3
+
4
+ class Autotest::CucumberRailsRspec2 < Autotest::RailsRspec2
5
+ include CucumberMixin
6
+ end
@@ -0,0 +1,6 @@
1
+ require 'autotest/cucumber_mixin'
2
+ require 'autotest/rspec2'
3
+
4
+ class Autotest::CucumberRspec2 < Autotest::Rspec2
5
+ include CucumberMixin
6
+ end
@@ -23,6 +23,10 @@ module Cucumber
23
23
  def accept_hook?(hook)
24
24
  @scenario_outline.accept_hook?(hook)
25
25
  end
26
+
27
+ def source_tag_names
28
+ @scenario_outline.source_tag_names
29
+ end
26
30
 
27
31
  def skip_invoke!
28
32
  example_rows.each do |cells|
@@ -61,6 +65,10 @@ module Cucumber
61
65
  super
62
66
  @scenario_exception = nil
63
67
  end
68
+
69
+ def source_tag_names
70
+ @table.source_tag_names
71
+ end
64
72
 
65
73
  def create_step_invocations!(scenario_outline)
66
74
  @scenario_outline = scenario_outline
@@ -29,7 +29,11 @@ module Cucumber
29
29
  end
30
30
 
31
31
  def to_s
32
- @string.indent(-@quotes_indent)
32
+ # Assume all whitespace before the first triple quote is the same.
33
+ # Also assume the contents of the pystring is indented with the same prefix.
34
+ # This allows indentation with both " " and "\t" characters.
35
+ return @string if @quotes_indent == ""
36
+ @string.gsub(/^#{@quotes_indent[0..0]}{0,#{@quotes_indent.length}}/, "")
33
37
  end
34
38
 
35
39
  def accept(visitor)
@@ -142,11 +142,11 @@ module Cucumber
142
142
  end
143
143
 
144
144
  def actual_keyword
145
- repeat_keywords = [language.but_keywords(false), language.and_keywords(false)].flatten
145
+ repeat_keywords = [language.but_keywords(false), language.and_keywords(false)].flatten.uniq.reject{|kw| kw == '*'}
146
146
  if repeat_keywords.index(@step.keyword) && previous
147
147
  previous.actual_keyword
148
148
  else
149
- keyword
149
+ keyword == '*' ? language.given_keyword : keyword
150
150
  end
151
151
  end
152
152
 
@@ -352,6 +352,7 @@ module Cucumber
352
352
  end
353
353
  @options[:source] &= other_options[:source]
354
354
  @options[:snippets] &= other_options[:snippets]
355
+ @options[:strict] |= other_options[:strict]
355
356
 
356
357
  @profiles += other_options.profiles
357
358
  @expanded_args += other_options.expanded_args
@@ -22,8 +22,14 @@ Defined profiles in cucumber.yml:
22
22
  case(args_from_yml)
23
23
  when String
24
24
  raise YmlLoadError, "The '#{profile}' profile in cucumber.yml was blank. Please define the command line arguments for the '#{profile}' profile in cucumber.yml.\n" if args_from_yml =~ /^\s*$/
25
- require 'shellwords'
26
- args_from_yml = Shellwords.shellwords(args_from_yml)
25
+ if(Cucumber::WINDOWS)
26
+ #Shellwords treats backslash as an escape character so here's a rudimentary approximation of the same code
27
+ args_from_yml = args_from_yml.split
28
+ args_from_yml = args_from_yml.collect {|x| x.gsub(/^\"(.*)\"/,'\1') }
29
+ else
30
+ require 'shellwords'
31
+ args_from_yml = Shellwords.shellwords(args_from_yml)
32
+ end
27
33
  when Array
28
34
  raise YmlLoadError, "The '#{profile}' profile in cucumber.yml was empty. Please define the command line arguments for the '#{profile}' profile in cucumber.yml.\n" if args_from_yml.empty?
29
35
  else
@@ -39,9 +39,9 @@ module Cucumber
39
39
  @coder = HTMLEntities.new
40
40
 
41
41
  if(options[:dry_run])
42
- @status_colors = { :passed => BLACK, :skipped => BLACK, :undefined => BLACK, :failed => BLACK}
42
+ @status_colors = { :passed => BLACK, :skipped => BLACK, :undefined => BLACK, :failed => BLACK, :announced => GREY}
43
43
  else
44
- @status_colors = { :passed => '055902', :skipped => GREY, :undefined => 'F27405', :failed => '730202'}
44
+ @status_colors = { :passed => '055902', :skipped => GREY, :undefined => 'F27405', :failed => '730202', :announced => GREY}
45
45
  end
46
46
 
47
47
  @pdf = Prawn::Document.new
@@ -82,6 +82,13 @@ module Cucumber
82
82
  end
83
83
  end
84
84
 
85
+ def announce(announcement)
86
+ @pdf.fill_color(@status_colors[:announced])
87
+ @pdf.text announcement, :size => 10
88
+ @pdf.fill_color BLACK
89
+ end
90
+
91
+
85
92
  def after_features(features)
86
93
  @pdf.render_file(@file.path)
87
94
  puts "\ndone"
@@ -19,15 +19,33 @@ module Cucumber
19
19
  print_summary(features)
20
20
  end
21
21
 
22
+ def before_feature_element(*args)
23
+ @exception_raised = false
24
+ end
25
+
26
+ def after_feature_element(*args)
27
+ progress(:failed) if @exception_raised
28
+ @exception_raised = false
29
+ end
30
+
31
+ def before_steps(*args)
32
+ progress(:failed) if @exception_raised
33
+ @exception_raised = false
34
+ end
35
+
36
+ def after_steps(*args)
37
+ @exception_raised = false
38
+ end
39
+
22
40
  def after_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background)
23
41
  progress(status)
24
42
  @status = status
25
43
  end
26
-
44
+
27
45
  def before_outline_table(outline_table)
28
46
  @outline_table = outline_table
29
47
  end
30
-
48
+
31
49
  def after_outline_table(outline_table)
32
50
  @outline_table = nil
33
51
  end
@@ -38,6 +56,10 @@ module Cucumber
38
56
  progress(status) unless table_header_cell?(status)
39
57
  end
40
58
 
59
+ def exception(*args)
60
+ @exception_raised = true
61
+ end
62
+
41
63
  private
42
64
 
43
65
  def print_summary(features)
@@ -7,6 +7,17 @@ $KCODE='u' unless Cucumber::RUBY_1_9
7
7
  if Cucumber::WINDOWS
8
8
  require 'iconv'
9
9
 
10
+ if ENV['CUCUMBER_OUTPUT_ENCODING']
11
+ Cucumber::CODEPAGE = ENV['CUCUMBER_OUTPUT_ENCODING']
12
+ elsif Cucumber::WINDOWS_MRI
13
+ Cucumber::CODEPAGE = "cp#{Win32::Console::OutputCP()}"
14
+ elsif `cmd /c chcp` =~ /(\d+)/
15
+ Cucumber::CODEPAGE = "cp#{$1.to_i}"
16
+ else
17
+ Cucumber::CODEPAGE = "cp1252"
18
+ STDERR.puts("WARNING: Couldn't detect your output codepage. Assuming it is 1252. You may have to chcp 1252 or SET CUCUMBER_OUTPUT_ENCODING=cp1252.")
19
+ end
20
+
10
21
  module Cucumber
11
22
  module WindowsOutput #:nodoc:
12
23
  def self.extended(o)
@@ -44,15 +55,4 @@ if Cucumber::WINDOWS
44
55
  STDERR.extend(self)
45
56
  end
46
57
  end
47
-
48
- if ENV['CUCUMBER_OUTPUT_ENCODING']
49
- Cucumber::CODEPAGE = ENV['CUCUMBER_OUTPUT_ENCODING']
50
- elsif Cucumber::WINDOWS_MRI
51
- Cucumber::CODEPAGE = "cp#{Win32::Console::OutputCP()}"
52
- elsif `cmd /c chcp` =~ /(\d+)/
53
- Cucumber::CODEPAGE = "cp#{$1.to_i}"
54
- else
55
- Cucumber::CODEPAGE = "cp1252"
56
- STDERR.cucumber_puts("WARNING: Couldn't detect your output codepage. Assuming it is 1252. You may have to chcp 1252 or SET CUCUMBER_OUTPUT_ENCODING=cp1252.")
57
- end
58
58
  end
@@ -1,11 +1,16 @@
1
1
  # encoding: UTF-8
2
- # We use the codes here (prefer 2 letters when possible)
3
- # http://en.wikipedia.org/wiki/List_of_ISO_639-2_codes
2
+ #
3
+ # We use ISO 639-1 (language) and ISO 3166 alpha-2 (region - if appliccable):
4
+ # http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
5
+ # http://en.wikipedia.org/wiki/ISO_3166-1
4
6
  #
5
7
  # If you want several aliases for a keyword, just separate them
6
8
  # with a | character. Make sure there are no ambiguities in the
7
9
  # keywords.
8
10
  #
11
+ # If you do *not* want a trailing space after a keyword, end it with a < character.
12
+ # (See Chinese for examples).
13
+ #
9
14
  "en":
10
15
  name: English
11
16
  native: English
@@ -20,10 +25,6 @@
20
25
  and: "*|And"
21
26
  but: "*|But"
22
27
 
23
- # Please help us keeping the languages below uptodate. The parsers for a language
24
- # that is missing a keyword will expect the English word until the missing word(s)
25
- # are added.
26
- #
27
28
  # Please keep the grammars in alphabetical order by name from here and down.
28
29
 
29
30
  "ar":
@@ -52,7 +53,7 @@
52
53
  then: "*|То"
53
54
  and: "*|И"
54
55
  but: "*|Но"
55
- "cat":
56
+ "ca":
56
57
  name: Catalan
57
58
  native: català
58
59
  background: Rerefons|Antecedents
@@ -65,7 +66,7 @@
65
66
  then: "*|Aleshores|Cal"
66
67
  and: "*|I"
67
68
  but: "*|Però"
68
- "cy":
69
+ "cy-GB":
69
70
  name: Welsh
70
71
  native: Cymraeg
71
72
  background: Cefndir
@@ -390,7 +391,7 @@
390
391
  then: "*|Atunci"
391
392
  and: "*|Si"
392
393
  but: "*|Dar"
393
- "ro2":
394
+ "ro-RO":
394
395
  name: Romanian (diacritical)
395
396
  native: română (diacritical)
396
397
  background: Condiţii
@@ -416,7 +417,7 @@
416
417
  then: "*|То"
417
418
  and: "*|И|К тому же"
418
419
  but: "*|Но|А"
419
- "se":
420
+ "sv":
420
421
  name: Swedish
421
422
  native: Svenska
422
423
  feature: Egenskap
@@ -443,8 +444,8 @@
443
444
  and: "*|A"
444
445
  but: "*|Ale"
445
446
  "sr-Latn":
446
- name: Serbian_latin
447
- native: Srpski_latinica
447
+ name: Serbian (Latin)
448
+ native: Srpski (Latinica)
448
449
  feature: Funkcionalnost|Mogućnost|Mogucnost|Osobina
449
450
  background: Kontekst|Osnova|Pozadina
450
451
  scenario: Scenario|Primer
@@ -455,7 +456,7 @@
455
456
  then: "*|Onda"
456
457
  and: "*|I"
457
458
  but: "*|Ali"
458
- "sr":
459
+ "sr-Cyrl":
459
460
  name: Serbian
460
461
  native: Српски
461
462
  feature: Функционалност|Могућност|Особина
@@ -1542,263 +1542,6 @@ module Cucumber
1542
1542
  r0
1543
1543
  end
1544
1544
 
1545
- module PyString0
1546
- end
1547
-
1548
- module PyString1
1549
- def open_py_string
1550
- elements[0]
1551
- end
1552
-
1553
- def s
1554
- elements[1]
1555
- end
1556
-
1557
- def close_py_string
1558
- elements[2]
1559
- end
1560
- end
1561
-
1562
- module PyString2
1563
- def at_line?(line)
1564
- line >= open_py_string.line && line <= close_py_string.line
1565
- end
1566
-
1567
- def build
1568
- Ast::PyString.new(open_py_string.line, close_py_string.line, s.text_value, open_py_string.indentation)
1569
- end
1570
- end
1571
-
1572
- def _nt_py_string
1573
- start_index = index
1574
- if node_cache[:py_string].has_key?(index)
1575
- cached = node_cache[:py_string][index]
1576
- if cached
1577
- cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
1578
- @index = cached.interval.end
1579
- end
1580
- return cached
1581
- end
1582
-
1583
- i0, s0 = index, []
1584
- r1 = _nt_open_py_string
1585
- s0 << r1
1586
- if r1
1587
- s2, i2 = [], index
1588
- loop do
1589
- i3, s3 = index, []
1590
- i4 = index
1591
- r5 = _nt_close_py_string
1592
- if r5
1593
- r4 = nil
1594
- else
1595
- @index = i4
1596
- r4 = instantiate_node(SyntaxNode,input, index...index)
1597
- end
1598
- s3 << r4
1599
- if r4
1600
- if index < input_length
1601
- r6 = instantiate_node(SyntaxNode,input, index...(index + 1))
1602
- @index += 1
1603
- else
1604
- terminal_parse_failure("any character")
1605
- r6 = nil
1606
- end
1607
- s3 << r6
1608
- end
1609
- if s3.last
1610
- r3 = instantiate_node(SyntaxNode,input, i3...index, s3)
1611
- r3.extend(PyString0)
1612
- else
1613
- @index = i3
1614
- r3 = nil
1615
- end
1616
- if r3
1617
- s2 << r3
1618
- else
1619
- break
1620
- end
1621
- end
1622
- r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
1623
- s0 << r2
1624
- if r2
1625
- r7 = _nt_close_py_string
1626
- s0 << r7
1627
- end
1628
- end
1629
- if s0.last
1630
- r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
1631
- r0.extend(PyString1)
1632
- r0.extend(PyString2)
1633
- else
1634
- @index = i0
1635
- r0 = nil
1636
- end
1637
-
1638
- node_cache[:py_string][start_index] = r0
1639
-
1640
- r0
1641
- end
1642
-
1643
- module OpenPyString0
1644
- def indent
1645
- elements[0]
1646
- end
1647
-
1648
- def eol
1649
- elements[3]
1650
- end
1651
- end
1652
-
1653
- module OpenPyString1
1654
- def indentation
1655
- indent.text_value.length
1656
- end
1657
-
1658
- def line
1659
- indent.line
1660
- end
1661
- end
1662
-
1663
- def _nt_open_py_string
1664
- start_index = index
1665
- if node_cache[:open_py_string].has_key?(index)
1666
- cached = node_cache[:open_py_string][index]
1667
- if cached
1668
- cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
1669
- @index = cached.interval.end
1670
- end
1671
- return cached
1672
- end
1673
-
1674
- i0, s0 = index, []
1675
- s1, i1 = [], index
1676
- loop do
1677
- r2 = _nt_space
1678
- if r2
1679
- s1 << r2
1680
- else
1681
- break
1682
- end
1683
- end
1684
- r1 = instantiate_node(SyntaxNode,input, i1...index, s1)
1685
- s0 << r1
1686
- if r1
1687
- if has_terminal?('"""', false, index)
1688
- r3 = instantiate_node(SyntaxNode,input, index...(index + 3))
1689
- @index += 3
1690
- else
1691
- terminal_parse_failure('"""')
1692
- r3 = nil
1693
- end
1694
- s0 << r3
1695
- if r3
1696
- s4, i4 = [], index
1697
- loop do
1698
- r5 = _nt_space
1699
- if r5
1700
- s4 << r5
1701
- else
1702
- break
1703
- end
1704
- end
1705
- r4 = instantiate_node(SyntaxNode,input, i4...index, s4)
1706
- s0 << r4
1707
- if r4
1708
- r6 = _nt_eol
1709
- s0 << r6
1710
- end
1711
- end
1712
- end
1713
- if s0.last
1714
- r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
1715
- r0.extend(OpenPyString0)
1716
- r0.extend(OpenPyString1)
1717
- else
1718
- @index = i0
1719
- r0 = nil
1720
- end
1721
-
1722
- node_cache[:open_py_string][start_index] = r0
1723
-
1724
- r0
1725
- end
1726
-
1727
- module ClosePyString0
1728
- def eol
1729
- elements[0]
1730
- end
1731
-
1732
- def quotes
1733
- elements[2]
1734
- end
1735
-
1736
- def white
1737
- elements[3]
1738
- end
1739
- end
1740
-
1741
- module ClosePyString1
1742
- def line
1743
- quotes.line
1744
- end
1745
- end
1746
-
1747
- def _nt_close_py_string
1748
- start_index = index
1749
- if node_cache[:close_py_string].has_key?(index)
1750
- cached = node_cache[:close_py_string][index]
1751
- if cached
1752
- cached = SyntaxNode.new(input, index...(index + 1)) if cached == true
1753
- @index = cached.interval.end
1754
- end
1755
- return cached
1756
- end
1757
-
1758
- i0, s0 = index, []
1759
- r1 = _nt_eol
1760
- s0 << r1
1761
- if r1
1762
- s2, i2 = [], index
1763
- loop do
1764
- r3 = _nt_space
1765
- if r3
1766
- s2 << r3
1767
- else
1768
- break
1769
- end
1770
- end
1771
- r2 = instantiate_node(SyntaxNode,input, i2...index, s2)
1772
- s0 << r2
1773
- if r2
1774
- if has_terminal?('"""', false, index)
1775
- r4 = instantiate_node(SyntaxNode,input, index...(index + 3))
1776
- @index += 3
1777
- else
1778
- terminal_parse_failure('"""')
1779
- r4 = nil
1780
- end
1781
- s0 << r4
1782
- if r4
1783
- r5 = _nt_white
1784
- s0 << r5
1785
- end
1786
- end
1787
- end
1788
- if s0.last
1789
- r0 = instantiate_node(SyntaxNode,input, i0...index, s0)
1790
- r0.extend(ClosePyString0)
1791
- r0.extend(ClosePyString1)
1792
- else
1793
- @index = i0
1794
- r0 = nil
1795
- end
1796
-
1797
- node_cache[:close_py_string][start_index] = r0
1798
-
1799
- r0
1800
- end
1801
-
1802
1545
  def _nt_white
1803
1546
  start_index = index
1804
1547
  if node_cache[:white].has_key?(index)