walrus 0.2 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. data/bin/walrus +19 -12
  2. data/lib/walrus.rb +27 -70
  3. data/lib/walrus/additions/string.rb +21 -35
  4. data/lib/walrus/compile_error.rb +19 -16
  5. data/lib/walrus/compiler.rb +66 -55
  6. data/lib/walrus/contrib/spec/walruscloth_spec.rb +21 -17
  7. data/lib/walrus/contrib/walruscloth.rb +19 -11
  8. data/lib/walrus/document.rb +41 -33
  9. data/lib/walrus/grammar.rb +474 -162
  10. data/lib/walrus/grammar/assignment_expression.rb +33 -0
  11. data/lib/walrus/grammar/block_directive.rb +37 -0
  12. data/lib/walrus/grammar/comment.rb +33 -0
  13. data/lib/walrus/grammar/def_directive.rb +80 -0
  14. data/lib/walrus/grammar/echo_directive.rb +56 -0
  15. data/lib/walrus/grammar/escape_sequence.rb +33 -0
  16. data/lib/walrus/grammar/import_directive.rb +54 -0
  17. data/lib/walrus/grammar/include_directive.rb +36 -0
  18. data/lib/walrus/grammar/instance_variable.rb +33 -0
  19. data/lib/walrus/grammar/literal.rb +33 -0
  20. data/lib/walrus/grammar/message_expression.rb +34 -0
  21. data/lib/walrus/grammar/multiline_comment.rb +70 -0
  22. data/lib/walrus/grammar/placeholder.rb +47 -0
  23. data/lib/walrus/grammar/raw_directive.rb +50 -0
  24. data/lib/walrus/grammar/raw_text.rb +56 -0
  25. data/lib/walrus/grammar/ruby_directive.rb +41 -0
  26. data/lib/walrus/grammar/ruby_expression.rb +42 -0
  27. data/lib/walrus/grammar/set_directive.rb +34 -0
  28. data/lib/walrus/grammar/silent_directive.rb +51 -0
  29. data/lib/walrus/grammar/slurp_directive.rb +36 -0
  30. data/lib/walrus/grammar/super_directive.rb +34 -0
  31. data/lib/walrus/parser.rb +26 -408
  32. data/lib/walrus/runner.rb +37 -20
  33. data/lib/walrus/template.rb +34 -25
  34. data/lib/walrus/version.rb +24 -1
  35. metadata +57 -71
  36. data/ext/extconf.rb +0 -16
  37. data/ext/jindex.c +0 -92
  38. data/lib/walrus/diff.rb +0 -95
  39. data/lib/walrus/grammar/additions/proc.rb +0 -26
  40. data/lib/walrus/grammar/additions/regexp.rb +0 -27
  41. data/lib/walrus/grammar/additions/string.rb +0 -58
  42. data/lib/walrus/grammar/additions/symbol.rb +0 -49
  43. data/lib/walrus/grammar/and_predicate.rb +0 -46
  44. data/lib/walrus/grammar/array_result.rb +0 -25
  45. data/lib/walrus/grammar/continuation_wrapper_exception.rb +0 -34
  46. data/lib/walrus/grammar/left_recursion_exception.rb +0 -33
  47. data/lib/walrus/grammar/location_tracking.rb +0 -115
  48. data/lib/walrus/grammar/match_data_wrapper.rb +0 -71
  49. data/lib/walrus/grammar/memoizing.rb +0 -47
  50. data/lib/walrus/grammar/memoizing_cache.rb +0 -103
  51. data/lib/walrus/grammar/node.rb +0 -66
  52. data/lib/walrus/grammar/not_predicate.rb +0 -46
  53. data/lib/walrus/grammar/parse_error.rb +0 -45
  54. data/lib/walrus/grammar/parser_state.rb +0 -187
  55. data/lib/walrus/grammar/parslet.rb +0 -34
  56. data/lib/walrus/grammar/parslet_choice.rb +0 -128
  57. data/lib/walrus/grammar/parslet_combination.rb +0 -32
  58. data/lib/walrus/grammar/parslet_combining.rb +0 -160
  59. data/lib/walrus/grammar/parslet_merge.rb +0 -94
  60. data/lib/walrus/grammar/parslet_omission.rb +0 -63
  61. data/lib/walrus/grammar/parslet_repetition.rb +0 -106
  62. data/lib/walrus/grammar/parslet_repetition_default.rb +0 -64
  63. data/lib/walrus/grammar/parslet_sequence.rb +0 -214
  64. data/lib/walrus/grammar/predicate.rb +0 -63
  65. data/lib/walrus/grammar/proc_parslet.rb +0 -58
  66. data/lib/walrus/grammar/regexp_parslet.rb +0 -79
  67. data/lib/walrus/grammar/skipped_substring_exception.rb +0 -42
  68. data/lib/walrus/grammar/string_enumerator.rb +0 -53
  69. data/lib/walrus/grammar/string_parslet.rb +0 -81
  70. data/lib/walrus/grammar/string_result.rb +0 -30
  71. data/lib/walrus/grammar/symbol_parslet.rb +0 -69
  72. data/lib/walrus/no_parameter_marker.rb +0 -25
  73. data/lib/walrus/walrus_grammar/assignment_expression.rb +0 -30
  74. data/lib/walrus/walrus_grammar/block_directive.rb +0 -34
  75. data/lib/walrus/walrus_grammar/comment.rb +0 -30
  76. data/lib/walrus/walrus_grammar/def_directive.rb +0 -72
  77. data/lib/walrus/walrus_grammar/echo_directive.rb +0 -50
  78. data/lib/walrus/walrus_grammar/escape_sequence.rb +0 -30
  79. data/lib/walrus/walrus_grammar/import_directive.rb +0 -50
  80. data/lib/walrus/walrus_grammar/include_directive.rb +0 -33
  81. data/lib/walrus/walrus_grammar/instance_variable.rb +0 -30
  82. data/lib/walrus/walrus_grammar/literal.rb +0 -30
  83. data/lib/walrus/walrus_grammar/message_expression.rb +0 -31
  84. data/lib/walrus/walrus_grammar/multiline_comment.rb +0 -60
  85. data/lib/walrus/walrus_grammar/placeholder.rb +0 -46
  86. data/lib/walrus/walrus_grammar/raw_directive.rb +0 -48
  87. data/lib/walrus/walrus_grammar/raw_text.rb +0 -51
  88. data/lib/walrus/walrus_grammar/ruby_directive.rb +0 -35
  89. data/lib/walrus/walrus_grammar/ruby_expression.rb +0 -37
  90. data/lib/walrus/walrus_grammar/set_directive.rb +0 -30
  91. data/lib/walrus/walrus_grammar/silent_directive.rb +0 -50
  92. data/lib/walrus/walrus_grammar/slurp_directive.rb +0 -31
  93. data/lib/walrus/walrus_grammar/super_directive.rb +0 -33
@@ -1,47 +0,0 @@
1
- # Copyright 2007 Wincent Colaiuta
2
- # This program is free software: you can redistribute it and/or modify
3
- # it under the terms of the GNU General Public License as published by
4
- # the Free Software Foundation, either version 3 of the License, or
5
- # (at your option) any later version.
6
- #
7
- # This program is distributed in the hope that it will be useful,
8
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
9
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
- # GNU General Public License for more details.
11
- #
12
- # You should have received a copy of the GNU General Public License
13
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
14
-
15
- require 'walrus'
16
-
17
- module Walrus
18
- class Grammar
19
-
20
- module Memoizing
21
-
22
- # This method provides a clean, optional implementation of memoizing by serving as a wrapper for all parse invocations. Rather than calling the parse methods directly, this method should be called; if it is appropriate to use a memoizer then it will be invoked, otherwise control will fall through to the real parse method. Turning off memoizing is as simple as not parsing a value with the :memoizer key in the options hash.
23
- # This method defined is in a separate module so that it can easily be mixed in with all Parslets, ParsletCombinations and Predicates.
24
- def memoizing_parse(string, options = {})
25
-
26
- # Will use memoizer if available and not instructed to ignore it
27
- if options.has_key?(:memoizer) and not (options.has_key?(:ignore_memoizer) and options[:ignore_memoizer])
28
- options[:parseable] = self
29
- options[:memoizer].parse(string, options)
30
- else # otherwise will proceed as normal
31
- options[:ignore_memoizer] = false
32
- parse(string, options)
33
- end
34
-
35
- end
36
-
37
- # Can only check for left recursion if memoizing is turned on (the help of the memoizer is needed).
38
- def check_left_recursion(parseable, options = {})
39
- return unless options.has_key?(:memoizer)
40
- options[:memoizer].check_left_recursion(parseable, options)
41
- end
42
-
43
- end # module Memoizing
44
-
45
- end # class Grammar
46
- end # module Walrus
47
-
@@ -1,103 +0,0 @@
1
- # Copyright 2007-2009 Wincent Colaiuta
2
- # This program is free software: you can redistribute it and/or modify
3
- # it under the terms of the GNU General Public License as published by
4
- # the Free Software Foundation, either version 3 of the License, or
5
- # (at your option) any later version.
6
- #
7
- # This program is distributed in the hope that it will be useful,
8
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
9
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
- # GNU General Public License for more details.
11
- #
12
- # You should have received a copy of the GNU General Public License
13
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
14
-
15
- require 'walrus'
16
-
17
- module Walrus
18
- class Grammar
19
-
20
- # The MemoizingCache class memoizes the outcomes of parse operations. The functionality is implemented as a separate class so as to minimize the amount of "contamination" of other classes by memoizing code, and to allow memoizing to be cleanly turned on or off at will. If a MemoizingCache is passed to a Parslet, ParsletCombination or Predicate as a value for the :memoizer key in the options hash passed to a parse method, the class implementing that method will call the parse method on the cache rather than proceeding normally. The cache will either propagate the previously memoized result, or will defer back to the original class to obtain the result. A circular dependency is avoided by setting the :skip_memoizer flag in the options dictionary. If no MemoizingCache is passed then normal program flow takes place.
21
- class MemoizingCache
22
-
23
- # Singleton class that serves as a default value for unset keys in a Hash.
24
- class NoValueForKey
25
-
26
- require 'singleton'
27
- include Singleton
28
-
29
- end
30
-
31
- def initialize
32
- # The results of parse operations are stored (memoized) in a cache, keyed on a unique identifier comprising the Parslet, ParsletCombination or Predicate used in the parse operation, the location of the operation (the line_start and column_start), and the skipping override (if any). The values may be:
33
- #
34
- # - ParseErrors raised during parsing
35
- # - SkippedSubstringExceptions raised during parsing
36
- # - :ZeroWidthParseSuccess symbols thrown during parsing
37
- # - :AndPredicateSuccess symbols thrown during parsing
38
- # - :NotPredicateSuccess symbols thrown during parsing
39
- # - String instances returned as parse results
40
- # - MatchDataWrapper instance returned as parse results
41
- # - Array instances containing ordered collections of parse results
42
- # - Node subclass instances containing AST productions
43
- #
44
- @cache = Hash.new(NoValueForKey.instance)
45
- end
46
-
47
- # The receiver checks whether there is already a stored result corresponding to that a unique identifier that specifies the "coordinates" of a parsing operation (location, parseable, skipping override). If found propogates the result directly to the caller rather than performing the parse method all over again. Here "propagation" means re-raising parse errors, re-throwing symbols, and returning object references. If not found, performs the parsing operation and stores the result in the cache before propagating it.
48
- def parse(string, options = {})
49
- raise ArgumentError if string.nil?
50
-
51
- # construct a unique identifier
52
- identifier = [options[:parseable], options[:line_start], options[:column_start]]
53
- identifier << options[:origin] if options.has_key? :origin
54
- identifier << options[:skipping_override] if options.has_key? :skipping_override
55
-
56
- if (result = @cache[identifier]) != NoValueForKey.instance
57
- if result.kind_of? Symbol
58
- throw result
59
- elsif result.kind_of? Exception
60
- raise result
61
- else
62
- return result
63
- end
64
- else # first time for this parseable/location/skipping_override (etc) combination, capture result and propagate
65
- catch :NotPredicateSuccess do
66
- catch :AndPredicateSuccess do
67
- catch :ZeroWidthParseSuccess do
68
- begin
69
- options[:ignore_memoizer] = true
70
-
71
- # short-circuit left recursion here rather than infinite looping
72
- if options[:parseable].kind_of? SymbolParslet
73
- check_left_recursion(options[:parseable], options)
74
- @last_seen_symbol_parslet = options[:parseable]
75
- @last_seen_symbol_parslet_location = [options[:line_start], options[:column_start]]
76
- end
77
-
78
- return @cache[identifier] = options[:parseable].memoizing_parse(string, options) # store and return
79
- rescue Exception => e
80
- raise @cache[identifier] = e # store and re-raise
81
- end
82
- end
83
- throw @cache[identifier] = :ZeroWidthParseSuccess # store and re-throw
84
- end
85
- throw @cache[identifier] = :AndPredicateSuccess # store and re-throw
86
- end
87
- throw @cache[identifier] = :NotPredicateSuccess # store and re-throw
88
- end
89
- end
90
-
91
- def check_left_recursion(parseable, options = {})
92
- if parseable.kind_of? SymbolParslet and
93
- @last_seen_symbol_parslet == parseable and
94
- @last_seen_symbol_parslet_location == [options[:line_start], options[:column_start]]
95
- raise LeftRecursionException
96
- end
97
- end
98
-
99
- end # class MemoizingCache
100
-
101
- end # class Grammar
102
- end # module Walrus
103
-
@@ -1,66 +0,0 @@
1
- # Copyright 2007 Wincent Colaiuta
2
- # This program is free software: you can redistribute it and/or modify
3
- # it under the terms of the GNU General Public License as published by
4
- # the Free Software Foundation, either version 3 of the License, or
5
- # (at your option) any later version.
6
- #
7
- # This program is distributed in the hope that it will be useful,
8
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
9
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
- # GNU General Public License for more details.
11
- #
12
- # You should have received a copy of the GNU General Public License
13
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
14
-
15
- require 'walrus'
16
-
17
- module Walrus
18
- class Grammar
19
-
20
- # Make subclasses of this for us in Abstract Syntax Trees (ASTs).
21
- class Node
22
-
23
- include Walrus::Grammar::LocationTracking
24
-
25
- def to_s
26
- @string_value
27
- end
28
-
29
- # Dynamically creates a Node descendant.
30
- # subclass_name should be a Symbol or String containing the name of the subclass to be created.
31
- # namespace should be the module in which the new subclass should be created; it defaults to Walrus::Grammar.
32
- # results are optional symbols expected to be parsed when initializing an instance of the subclass. If no optional symbols are provided then a default initializer is created that expects a single parameter and stores a reference to it in an instance variable called "lexeme".
33
- def self.subclass(subclass_name, namespace = Walrus::Grammar, *results)
34
- raise ArgumentError if subclass_name.nil?
35
-
36
- # create new anonymous class with Node as superclass, assigning it to a constant effectively names the class
37
- new_class = namespace.const_set(subclass_name.to_s, Class.new(self))
38
-
39
- # set up accessors
40
- for result in results
41
- new_class.class_eval { attr_reader result }
42
- end
43
-
44
- # set up initializer
45
- if results.length == 0 # default case, store sole parameter in "lexeme"
46
- new_class.class_eval { attr_reader :lexeme }
47
- initialize_body = "def initialize(lexeme)\n"
48
- initialize_body << "@string_value = lexeme.to_s\n"
49
- initialize_body << "@lexeme = lexeme\n"
50
- else
51
- initialize_body = "def initialize(#{results.collect { |symbol| symbol.to_s}.join(', ')})\n"
52
- initialize_body << "@string_value = \"\"\n"
53
- for result in results
54
- initialize_body << "@#{result.to_s} = #{result.to_s}\n"
55
- initialize_body << "@string_value << #{result.to_s}.to_s\n"
56
- end
57
- end
58
- initialize_body << "end\n"
59
- new_class.class_eval initialize_body
60
- new_class
61
- end
62
-
63
- end # class Node
64
-
65
- end # class Grammar
66
- end # module Walrus
@@ -1,46 +0,0 @@
1
- # Copyright 2007 Wincent Colaiuta
2
- # This program is free software: you can redistribute it and/or modify
3
- # it under the terms of the GNU General Public License as published by
4
- # the Free Software Foundation, either version 3 of the License, or
5
- # (at your option) any later version.
6
- #
7
- # This program is distributed in the hope that it will be useful,
8
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
9
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
- # GNU General Public License for more details.
11
- #
12
- # You should have received a copy of the GNU General Public License
13
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
14
-
15
- require 'walrus'
16
-
17
- module Walrus
18
- class Grammar
19
-
20
- class NotPredicate < Predicate
21
-
22
- def parse(string, options = {})
23
- raise ArgumentError if string.nil?
24
- catch :ZeroWidthParseSuccess do
25
- begin
26
- @parseable.memoizing_parse(string, options)
27
- rescue ParseError # failed to pass (which is just what we wanted)
28
- throw :NotPredicateSuccess
29
- end
30
- end
31
-
32
- # getting this far means that parsing succeeded (not what we wanted)
33
- raise ParseError.new('predicate not satisfied ("%s" not allowed) while parsing "%s"' % [@parseable.to_s, string],
34
- :line_end => options[:line_start], :column_end => options[:column_start])
35
- end
36
-
37
- private
38
-
39
- def hash_offset
40
- 11
41
- end
42
-
43
- end
44
-
45
- end # class Grammar
46
- end # module Walrus
@@ -1,45 +0,0 @@
1
- # Copyright 2007 Wincent Colaiuta
2
- # This program is free software: you can redistribute it and/or modify
3
- # it under the terms of the GNU General Public License as published by
4
- # the Free Software Foundation, either version 3 of the License, or
5
- # (at your option) any later version.
6
- #
7
- # This program is distributed in the hope that it will be useful,
8
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
9
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
- # GNU General Public License for more details.
11
- #
12
- # You should have received a copy of the GNU General Public License
13
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
14
-
15
- require 'walrus'
16
-
17
- module Walrus
18
- class Grammar
19
-
20
- class ParseError < Exception
21
-
22
- include Walrus::Grammar::LocationTracking
23
-
24
- # Takes an optional hash (for packing extra info into exception).
25
- # position in string (irrespective of line number, column number)
26
- # line number, column number
27
- # filename
28
- def initialize(message, info = {})
29
- super message
30
- self.line_start = info[:line_start]
31
- self.column_start = info[:column_start]
32
- self.line_end = info[:line_end]
33
- self.column_end = info[:column_end]
34
- end
35
-
36
- def inspect
37
- # TODO also return filename if available
38
- '#<%s: %s @line_end=%d, @column_end=%d>' % [ self.class.to_s, self.to_s, self.line_end, self.column_end ]
39
- end
40
-
41
- end # class ParseError
42
-
43
- end # class Grammar
44
- end # module Walrus
45
-
@@ -1,187 +0,0 @@
1
- # Copyright 2007 Wincent Colaiuta
2
- # This program is free software: you can redistribute it and/or modify
3
- # it under the terms of the GNU General Public License as published by
4
- # the Free Software Foundation, either version 3 of the License, or
5
- # (at your option) any later version.
6
- #
7
- # This program is distributed in the hope that it will be useful,
8
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
9
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
- # GNU General Public License for more details.
11
- #
12
- # You should have received a copy of the GNU General Public License
13
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
14
-
15
- require 'walrus'
16
-
17
- module Walrus
18
- class Grammar
19
-
20
- # Simple class for maintaining state during a parse operation.
21
- class ParserState
22
-
23
- attr_reader :options
24
-
25
- # Returns the remainder (the unparsed portion) of the string. Will return an empty string if already at the end of the string.
26
- attr_reader :remainder
27
-
28
- # Raises an ArgumentError if string is nil.
29
- def initialize(string, options = {})
30
- raise ArgumentError if string.nil?
31
- self.base_string = string
32
- @results = ArrayResult.new # for accumulating results
33
- @remainder = @base_string.clone
34
- @scanned = ''
35
- @options = options.clone
36
-
37
- # start wherever we last finished (doesn't seem to behave different to the alternative)
38
- @options[:line_start] = (@options[:line_end] or @options[:line_start] or 0)
39
- @options[:column_start] = (@options[:column_end] or @options[:column_start] or 0)
40
- # @options[:line_start] = 0 if @options[:line_start].nil?
41
- # @options[:column_start] = 0 if @options[:column_start].nil?
42
-
43
- @options[:line_end] = @options[:line_start] # before parsing begins, end point is equal to start point
44
- @options[:column_end] = @options[:column_start]
45
- @original_line_start = @options[:line_start]
46
- @original_column_start = @options[:column_start]
47
- end
48
-
49
- # The parsed method is used to inform the receiver of a successful parsing event.
50
- # Note that substring need not actually be a String but it must respond to the following messages:
51
- # - "line_end" and "column_end" so that the end position of the receiver can be updated
52
- # As a convenience returns the remainder.
53
- # Raises an ArgumentError if substring is nil.
54
- def parsed(substring)
55
- raise ArgumentError if substring.nil?
56
- update_and_return_remainder_for_string(substring, true)
57
- end
58
-
59
- # The skipped method is used to inform the receiver of a successful parsing event where the parsed substring should be consumed but not included in the accumulated results.
60
- # The substring should respond to "line_end" and "column_end".
61
- # In all other respects this method behaves exactly like the parsed method.
62
- def skipped(substring)
63
- raise ArgumentError if substring.nil?
64
- update_and_return_remainder_for_string(substring)
65
- end
66
-
67
- # The skipped method is used to inform the receiver of a successful parsing event where the parsed substring should be consumed but not included in the accumulated results and furthermore the parse event should not effect the overall bounds of the parse result. In reality this means that the method is only ever called upon the successful use of a automatic intertoken "skipping" parslet. By definition this method should only be called for intertoken skipping otherwise incorrect results will be produced.
68
- def auto_skipped(substring)
69
- raise ArgumentError if substring.nil?
70
- a, b, c, d = @options[:line_start], @options[:column_start], @options[:line_end], @options[:column_end] # save
71
- remainder = update_and_return_remainder_for_string(substring)
72
- @options[:line_start], @options[:column_start], @options[:line_end], @options[:column_end] = a, b, c, d # restore
73
- remainder
74
- end
75
-
76
- # Returns the results accumulated so far.
77
- # Returns an empty array if no results have been accumulated.
78
- # Returns a single object if only one result has been accumulated.
79
- # Returns an array of objects if multiple results have been accumulated.
80
- def results
81
-
82
- updated_start = [@original_line_start, @original_column_start]
83
- updated_end = [@options[:line_end], @options[:column_end]]
84
- updated_source_text = @scanned.clone
85
-
86
- if @results.length == 1
87
-
88
- # he we ask the single result to exhibit container-like properties
89
- # use the "outer" variants so as to not overwrite any data internal to the result itself
90
- # this can happen where a lone result is surrounded only by skipped elements
91
- # the result has to convey data about its own limits, plus those of the context just around it
92
- results = @results[0]
93
- results.outer_start = updated_start if results.start != updated_start
94
- results.outer_end = updated_end if results.end != updated_end
95
- results.outer_source_text = updated_source_text if results.source_text != updated_source_text
96
-
97
- # the above trick fixes some of the location tracking issues but opens up another can of worms
98
- # uncomment this line to see
99
- #return results
100
-
101
- # need some way of handling unwrapped results (raw results, not AST nodes) as well
102
- results.start = updated_start
103
- results.end = updated_end
104
- results.source_text = updated_source_text
105
-
106
- else
107
- results = @results
108
- results.start = updated_start
109
- results.end = updated_end
110
- results.source_text = updated_source_text
111
- end
112
-
113
- results
114
- end
115
-
116
- # Returns the number of results accumulated so far.
117
- def length
118
- @results.length
119
- end
120
-
121
- # TODO: possibly implement "undo/rollback" and "reset" methods
122
- # if I implement "undo" will probbaly do it as a stack
123
- # will have the option of implementing "redo" as well but I'd only do that if I could think of a use for it
124
-
125
- private
126
-
127
- def update_and_return_remainder_for_string(input, store = false)
128
- previous_line_end = @options[:line_end] # remember old end point
129
- previous_column_end = @options[:column_end] # remember old end point
130
-
131
- # special case handling for literal String objects
132
- if input.instance_of? String
133
- input = StringResult.new(input)
134
- input.start = [previous_line_end, previous_column_end]
135
- if (line_count = input.scan(/\r\n|\r|\n/).length) != 0 # count number of newlines in receiver
136
- column_end = input.jlength - input.jrindex(/\r|\n/) - 1 # calculate characters on last line
137
- else # no newlines in match
138
- column_end = input.jlength + previous_column_end
139
- end
140
- input.end = [previous_line_end + line_count, column_end]
141
- end
142
-
143
- @results << input if (store)
144
-
145
- if input.line_end > previous_line_end # end line has advanced
146
- @options[:line_end] = input.line_end
147
- @options[:column_end] = 0
148
- end
149
-
150
- if input.column_end > @options[:column_end] # end column has advanced
151
- @options[:column_end] = input.column_end
152
- end
153
-
154
-
155
- @options[:line_start] = @options[:line_end] # new start point is old end point
156
- @options[:column_start] = @options[:column_end] # new start point is old end point
157
-
158
- # calculate remainder
159
- line_delta = @options[:line_end] - previous_line_end
160
- if line_delta > 0 # have consumed newline(s)
161
- line_delta.times do # remove them from remainder
162
- newline_location = @remainder.jindex /\r\n|\r|\n/ # find the location of the next newline
163
- newline_location += $~[0].length # add the actual characters used to indicate the newline
164
- @scanned << @remainder[0...newline_location] # record scanned text
165
- @remainder = @remainder[newline_location..-1] # strip everything up to and including the newline
166
- end
167
- @scanned << @remainder[0...@options[:column_end]]
168
- @remainder = @remainder[@options[:column_end]..-1] # delete up to the current column
169
- else # no newlines consumed
170
- column_delta = @options[:column_end] - previous_column_end
171
- if column_delta > 0 # there was movement within currentline
172
- @scanned << @remainder[0...column_delta]
173
- @remainder = @remainder[column_delta..-1] # delete up to the current column
174
- end
175
- end
176
- @remainder
177
- end
178
-
179
- def base_string=(string)
180
- @base_string = (string.clone rescue string)
181
- end
182
-
183
- end # class ParserState
184
-
185
- end # class Grammar
186
- end # module Walrus
187
-