walrus 0.2 → 0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/walrus +19 -12
- data/lib/walrus.rb +27 -70
- data/lib/walrus/additions/string.rb +21 -35
- data/lib/walrus/compile_error.rb +19 -16
- data/lib/walrus/compiler.rb +66 -55
- data/lib/walrus/contrib/spec/walruscloth_spec.rb +21 -17
- data/lib/walrus/contrib/walruscloth.rb +19 -11
- data/lib/walrus/document.rb +41 -33
- data/lib/walrus/grammar.rb +474 -162
- data/lib/walrus/grammar/assignment_expression.rb +33 -0
- data/lib/walrus/grammar/block_directive.rb +37 -0
- data/lib/walrus/grammar/comment.rb +33 -0
- data/lib/walrus/grammar/def_directive.rb +80 -0
- data/lib/walrus/grammar/echo_directive.rb +56 -0
- data/lib/walrus/grammar/escape_sequence.rb +33 -0
- data/lib/walrus/grammar/import_directive.rb +54 -0
- data/lib/walrus/grammar/include_directive.rb +36 -0
- data/lib/walrus/grammar/instance_variable.rb +33 -0
- data/lib/walrus/grammar/literal.rb +33 -0
- data/lib/walrus/grammar/message_expression.rb +34 -0
- data/lib/walrus/grammar/multiline_comment.rb +70 -0
- data/lib/walrus/grammar/placeholder.rb +47 -0
- data/lib/walrus/grammar/raw_directive.rb +50 -0
- data/lib/walrus/grammar/raw_text.rb +56 -0
- data/lib/walrus/grammar/ruby_directive.rb +41 -0
- data/lib/walrus/grammar/ruby_expression.rb +42 -0
- data/lib/walrus/grammar/set_directive.rb +34 -0
- data/lib/walrus/grammar/silent_directive.rb +51 -0
- data/lib/walrus/grammar/slurp_directive.rb +36 -0
- data/lib/walrus/grammar/super_directive.rb +34 -0
- data/lib/walrus/parser.rb +26 -408
- data/lib/walrus/runner.rb +37 -20
- data/lib/walrus/template.rb +34 -25
- data/lib/walrus/version.rb +24 -1
- metadata +57 -71
- data/ext/extconf.rb +0 -16
- data/ext/jindex.c +0 -92
- data/lib/walrus/diff.rb +0 -95
- data/lib/walrus/grammar/additions/proc.rb +0 -26
- data/lib/walrus/grammar/additions/regexp.rb +0 -27
- data/lib/walrus/grammar/additions/string.rb +0 -58
- data/lib/walrus/grammar/additions/symbol.rb +0 -49
- data/lib/walrus/grammar/and_predicate.rb +0 -46
- data/lib/walrus/grammar/array_result.rb +0 -25
- data/lib/walrus/grammar/continuation_wrapper_exception.rb +0 -34
- data/lib/walrus/grammar/left_recursion_exception.rb +0 -33
- data/lib/walrus/grammar/location_tracking.rb +0 -115
- data/lib/walrus/grammar/match_data_wrapper.rb +0 -71
- data/lib/walrus/grammar/memoizing.rb +0 -47
- data/lib/walrus/grammar/memoizing_cache.rb +0 -103
- data/lib/walrus/grammar/node.rb +0 -66
- data/lib/walrus/grammar/not_predicate.rb +0 -46
- data/lib/walrus/grammar/parse_error.rb +0 -45
- data/lib/walrus/grammar/parser_state.rb +0 -187
- data/lib/walrus/grammar/parslet.rb +0 -34
- data/lib/walrus/grammar/parslet_choice.rb +0 -128
- data/lib/walrus/grammar/parslet_combination.rb +0 -32
- data/lib/walrus/grammar/parslet_combining.rb +0 -160
- data/lib/walrus/grammar/parslet_merge.rb +0 -94
- data/lib/walrus/grammar/parslet_omission.rb +0 -63
- data/lib/walrus/grammar/parslet_repetition.rb +0 -106
- data/lib/walrus/grammar/parslet_repetition_default.rb +0 -64
- data/lib/walrus/grammar/parslet_sequence.rb +0 -214
- data/lib/walrus/grammar/predicate.rb +0 -63
- data/lib/walrus/grammar/proc_parslet.rb +0 -58
- data/lib/walrus/grammar/regexp_parslet.rb +0 -79
- data/lib/walrus/grammar/skipped_substring_exception.rb +0 -42
- data/lib/walrus/grammar/string_enumerator.rb +0 -53
- data/lib/walrus/grammar/string_parslet.rb +0 -81
- data/lib/walrus/grammar/string_result.rb +0 -30
- data/lib/walrus/grammar/symbol_parslet.rb +0 -69
- data/lib/walrus/no_parameter_marker.rb +0 -25
- data/lib/walrus/walrus_grammar/assignment_expression.rb +0 -30
- data/lib/walrus/walrus_grammar/block_directive.rb +0 -34
- data/lib/walrus/walrus_grammar/comment.rb +0 -30
- data/lib/walrus/walrus_grammar/def_directive.rb +0 -72
- data/lib/walrus/walrus_grammar/echo_directive.rb +0 -50
- data/lib/walrus/walrus_grammar/escape_sequence.rb +0 -30
- data/lib/walrus/walrus_grammar/import_directive.rb +0 -50
- data/lib/walrus/walrus_grammar/include_directive.rb +0 -33
- data/lib/walrus/walrus_grammar/instance_variable.rb +0 -30
- data/lib/walrus/walrus_grammar/literal.rb +0 -30
- data/lib/walrus/walrus_grammar/message_expression.rb +0 -31
- data/lib/walrus/walrus_grammar/multiline_comment.rb +0 -60
- data/lib/walrus/walrus_grammar/placeholder.rb +0 -46
- data/lib/walrus/walrus_grammar/raw_directive.rb +0 -48
- data/lib/walrus/walrus_grammar/raw_text.rb +0 -51
- data/lib/walrus/walrus_grammar/ruby_directive.rb +0 -35
- data/lib/walrus/walrus_grammar/ruby_expression.rb +0 -37
- data/lib/walrus/walrus_grammar/set_directive.rb +0 -30
- data/lib/walrus/walrus_grammar/silent_directive.rb +0 -50
- data/lib/walrus/walrus_grammar/slurp_directive.rb +0 -31
- 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
|
-
|
data/lib/walrus/grammar/node.rb
DELETED
@@ -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
|
-
|