walrus 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/walrus +44 -0
- data/ext/jindex/extconf.rb +11 -0
- data/ext/jindex/jindex.c +79 -0
- data/ext/mkdtemp/extconf.rb +11 -0
- data/ext/mkdtemp/mkdtemp.c +41 -0
- data/lib/walrus/additions/module.rb +36 -0
- data/lib/walrus/additions/string.rb +37 -0
- data/lib/walrus/additions/test/unit/error_collector.rb +62 -0
- data/lib/walrus/compile_error.rb +28 -0
- data/lib/walrus/compiler.rb +124 -0
- data/lib/walrus/contrib/spec/walruscloth_spec.rb +32 -0
- data/lib/walrus/contrib/walruscloth.rb +82 -0
- data/lib/walrus/diff.rb +89 -0
- data/lib/walrus/document.rb +98 -0
- data/lib/walrus/grammar/additions/proc.rb +20 -0
- data/lib/walrus/grammar/additions/regexp.rb +21 -0
- data/lib/walrus/grammar/additions/string.rb +52 -0
- data/lib/walrus/grammar/additions/symbol.rb +42 -0
- data/lib/walrus/grammar/and_predicate.rb +40 -0
- data/lib/walrus/grammar/array_result.rb +19 -0
- data/lib/walrus/grammar/continuation_wrapper_exception.rb +28 -0
- data/lib/walrus/grammar/left_recursion_exception.rb +27 -0
- data/lib/walrus/grammar/location_tracking.rb +105 -0
- data/lib/walrus/grammar/match_data_wrapper.rb +65 -0
- data/lib/walrus/grammar/memoizing.rb +41 -0
- data/lib/walrus/grammar/memoizing_cache.rb +94 -0
- data/lib/walrus/grammar/node.rb +60 -0
- data/lib/walrus/grammar/not_predicate.rb +40 -0
- data/lib/walrus/grammar/parse_error.rb +39 -0
- data/lib/walrus/grammar/parser_state.rb +181 -0
- data/lib/walrus/grammar/parslet.rb +28 -0
- data/lib/walrus/grammar/parslet_choice.rb +120 -0
- data/lib/walrus/grammar/parslet_combination.rb +26 -0
- data/lib/walrus/grammar/parslet_combining.rb +154 -0
- data/lib/walrus/grammar/parslet_merge.rb +88 -0
- data/lib/walrus/grammar/parslet_omission.rb +57 -0
- data/lib/walrus/grammar/parslet_repetition.rb +97 -0
- data/lib/walrus/grammar/parslet_repetition_default.rb +58 -0
- data/lib/walrus/grammar/parslet_sequence.rb +202 -0
- data/lib/walrus/grammar/predicate.rb +57 -0
- data/lib/walrus/grammar/proc_parslet.rb +52 -0
- data/lib/walrus/grammar/regexp_parslet.rb +73 -0
- data/lib/walrus/grammar/skipped_substring_exception.rb +36 -0
- data/lib/walrus/grammar/string_enumerator.rb +45 -0
- data/lib/walrus/grammar/string_parslet.rb +75 -0
- data/lib/walrus/grammar/string_result.rb +24 -0
- data/lib/walrus/grammar/symbol_parslet.rb +63 -0
- data/lib/walrus/grammar.rb +170 -0
- data/lib/walrus/no_parameter_marker.rb +19 -0
- data/lib/walrus/parser.rb +420 -0
- data/lib/walrus/runner.rb +356 -0
- data/lib/walrus/template.rb +75 -0
- data/lib/walrus/walrus_grammar/assignment_expression.rb +24 -0
- data/lib/walrus/walrus_grammar/block_directive.rb +28 -0
- data/lib/walrus/walrus_grammar/comment.rb +24 -0
- data/lib/walrus/walrus_grammar/def_directive.rb +64 -0
- data/lib/walrus/walrus_grammar/echo_directive.rb +44 -0
- data/lib/walrus/walrus_grammar/escape_sequence.rb +24 -0
- data/lib/walrus/walrus_grammar/import_directive.rb +44 -0
- data/lib/walrus/walrus_grammar/include_directive.rb +27 -0
- data/lib/walrus/walrus_grammar/instance_variable.rb +24 -0
- data/lib/walrus/walrus_grammar/literal.rb +24 -0
- data/lib/walrus/walrus_grammar/message_expression.rb +25 -0
- data/lib/walrus/walrus_grammar/multiline_comment.rb +54 -0
- data/lib/walrus/walrus_grammar/placeholder.rb +40 -0
- data/lib/walrus/walrus_grammar/raw_directive.rb +42 -0
- data/lib/walrus/walrus_grammar/raw_text.rb +45 -0
- data/lib/walrus/walrus_grammar/ruby_directive.rb +29 -0
- data/lib/walrus/walrus_grammar/ruby_expression.rb +31 -0
- data/lib/walrus/walrus_grammar/set_directive.rb +24 -0
- data/lib/walrus/walrus_grammar/silent_directive.rb +44 -0
- data/lib/walrus/walrus_grammar/slurp_directive.rb +25 -0
- data/lib/walrus/walrus_grammar/super_directive.rb +27 -0
- data/lib/walrus.rb +64 -0
- data/spec/acceptance/acceptance_spec.rb +97 -0
- data/spec/acceptance/block/basic_block.expected +1 -0
- data/spec/acceptance/block/basic_block.tmpl +3 -0
- data/spec/acceptance/block/nested_blocks.expected +5 -0
- data/spec/acceptance/block/nested_blocks.tmpl +11 -0
- data/spec/acceptance/comments/comments_and_text.expected +3 -0
- data/spec/acceptance/comments/comments_and_text.tmpl +6 -0
- data/spec/acceptance/comments/single_comment.expected +0 -0
- data/spec/acceptance/comments/single_comment.tmpl +1 -0
- data/spec/acceptance/def/alternative_def_calling_conventions.expected +3 -0
- data/spec/acceptance/def/alternative_def_calling_conventions.tmpl +18 -0
- data/spec/acceptance/def/basic_def_block_no_output.expected +0 -0
- data/spec/acceptance/def/basic_def_block_no_output.tmpl +17 -0
- data/spec/acceptance/def/defs_can_be_called_multiple_times.expected +3 -0
- data/spec/acceptance/def/defs_can_be_called_multiple_times.tmpl +6 -0
- data/spec/acceptance/def/defs_can_be_dynamic.expected +4 -0
- data/spec/acceptance/def/defs_can_be_dynamic.tmpl +12 -0
- data/spec/acceptance/echo/echo_directive_with_numeric_literal.expected +1 -0
- data/spec/acceptance/echo/echo_directive_with_numeric_literal.tmpl +1 -0
- data/spec/acceptance/echo/echo_expression_list.expected +1 -0
- data/spec/acceptance/echo/echo_expression_list.tmpl +1 -0
- data/spec/acceptance/echo/echo_short_notation.expected +1 -0
- data/spec/acceptance/echo/echo_short_notation.tmpl +1 -0
- data/spec/acceptance/echo/echo_simple_expression.expected +1 -0
- data/spec/acceptance/echo/echo_simple_expression.tmpl +1 -0
- data/spec/acceptance/echo/echo_single_quoted_string_literal.expected +1 -0
- data/spec/acceptance/echo/echo_single_quoted_string_literal.tmpl +1 -0
- data/spec/acceptance/echo/multiple_echo_statements.expected +1 -0
- data/spec/acceptance/echo/multiple_echo_statements.tmpl +2 -0
- data/spec/acceptance/includes/basic_included_file.txt +1 -0
- data/spec/acceptance/includes/basic_includer.complex +3 -0
- data/spec/acceptance/includes/basic_includer.expected +3 -0
- data/spec/acceptance/includes/basic_includer.rb +38 -0
- data/spec/acceptance/includes/complicated_included_file.txt +3 -0
- data/spec/acceptance/includes/complicated_includer.complex +3 -0
- data/spec/acceptance/includes/complicated_includer.expected +3 -0
- data/spec/acceptance/includes/complicated_includer.rb +41 -0
- data/spec/acceptance/includes/nested_include_1.txt +3 -0
- data/spec/acceptance/includes/nested_include_2.txt +1 -0
- data/spec/acceptance/includes/nested_includer.complex +3 -0
- data/spec/acceptance/includes/nested_includer.expected +4 -0
- data/spec/acceptance/includes/nested_includer.rb +41 -0
- data/spec/acceptance/inheritance/basic_child.complex +10 -0
- data/spec/acceptance/inheritance/basic_child.expected +9 -0
- data/spec/acceptance/inheritance/basic_child.rb +54 -0
- data/spec/acceptance/inheritance/basic_parent.complex +5 -0
- data/spec/acceptance/inheritance/basic_parent.expected +3 -0
- data/spec/acceptance/inheritance/basic_parent.rb +41 -0
- data/spec/acceptance/inheritance/importing_child.complex +8 -0
- data/spec/acceptance/inheritance/importing_child.expected +7 -0
- data/spec/acceptance/inheritance/importing_child.rb +46 -0
- data/spec/acceptance/inheritance/subdirectory/importing_child_in_subdirectory.complex +8 -0
- data/spec/acceptance/inheritance/subdirectory/importing_child_in_subdirectory.expected +7 -0
- data/spec/acceptance/inheritance/subdirectory/importing_child_in_subdirectory.rb +44 -0
- data/spec/acceptance/multiline_comments/multiline_comment_with_directives_inside.expected +0 -0
- data/spec/acceptance/multiline_comments/multiline_comment_with_directives_inside.tmpl +15 -0
- data/spec/acceptance/multiline_comments/simple_multiline_comment.expected +2 -0
- data/spec/acceptance/multiline_comments/simple_multiline_comment.tmpl +4 -0
- data/spec/acceptance/raw/complicated_raw_example.expected +57 -0
- data/spec/acceptance/raw/complicated_raw_example.tmpl +79 -0
- data/spec/acceptance/raw-text/UTF_8.expected +12 -0
- data/spec/acceptance/raw-text/UTF_8.tmpl +12 -0
- data/spec/acceptance/raw-text/empty_file.expected +0 -0
- data/spec/acceptance/raw-text/empty_file.tmpl +0 -0
- data/spec/acceptance/raw-text/multi_line.expected +4 -0
- data/spec/acceptance/raw-text/multi_line.tmpl +4 -0
- data/spec/acceptance/raw-text/single_line.expected +1 -0
- data/spec/acceptance/raw-text/single_line.tmpl +1 -0
- data/spec/acceptance/raw-text/single_line_whitespace.expected +1 -0
- data/spec/acceptance/raw-text/single_line_whitespace.tmpl +1 -0
- data/spec/acceptance/ruby/ruby_directive_is_just_like_silent.expected +1 -0
- data/spec/acceptance/ruby/ruby_directive_is_just_like_silent.tmpl +4 -0
- data/spec/acceptance/ruby/ruby_directive_using_here_doc.expected +1 -0
- data/spec/acceptance/ruby/ruby_directive_using_here_doc.tmpl +4 -0
- data/spec/acceptance/ruby/ruby_directive_using_here_doc_alt_syntax.expected +1 -0
- data/spec/acceptance/ruby/ruby_directive_using_here_doc_alt_syntax.tmpl +4 -0
- data/spec/acceptance/ruby/ruby_directive_with_accumulate.expected +1 -0
- data/spec/acceptance/ruby/ruby_directive_with_accumulate.tmpl +4 -0
- data/spec/acceptance/ruby/ruby_directive_with_accumulate_and_block.expected +1 -0
- data/spec/acceptance/ruby/ruby_directive_with_accumulate_and_block.tmpl +6 -0
- data/spec/acceptance/set/unused_set.expected +0 -0
- data/spec/acceptance/set/unused_set.tmpl +1 -0
- data/spec/acceptance/set/used_set.expected +1 -0
- data/spec/acceptance/set/used_set.tmpl +2 -0
- data/spec/acceptance/silent/silent_and_echo_combined.expected +1 -0
- data/spec/acceptance/silent/silent_and_echo_combined.tmpl +2 -0
- data/spec/acceptance/silent/silent_short_notation.expected +1 -0
- data/spec/acceptance/silent/silent_short_notation.tmpl +1 -0
- data/spec/acceptance/silent/simple_silent_directive.expected +0 -0
- data/spec/acceptance/silent/simple_silent_directive.tmpl +1 -0
- data/spec/acceptance/slurp/basic_slurp_demo.expected +1 -0
- data/spec/acceptance/slurp/basic_slurp_demo.tmpl +4 -0
- data/spec/acceptance/super/super_with_no_effect.expected +4 -0
- data/spec/acceptance/super/super_with_no_effect.tmpl +5 -0
- data/spec/additions/module_spec.rb +126 -0
- data/spec/additions/string_spec.rb +99 -0
- data/spec/compiler_spec.rb +55 -0
- data/spec/grammar/additions/proc_spec.rb +25 -0
- data/spec/grammar/additions/regexp_spec.rb +37 -0
- data/spec/grammar/additions/string_spec.rb +106 -0
- data/spec/grammar/and_predicate_spec.rb +29 -0
- data/spec/grammar/continuation_wrapper_exception_spec.rb +23 -0
- data/spec/grammar/match_data_wrapper_spec.rb +41 -0
- data/spec/grammar/memoizing_cache_spec.rb +112 -0
- data/spec/grammar/node_spec.rb +126 -0
- data/spec/grammar/not_predicate_spec.rb +29 -0
- data/spec/grammar/parser_state_spec.rb +172 -0
- data/spec/grammar/parslet_choice_spec.rb +49 -0
- data/spec/grammar/parslet_combining_spec.rb +287 -0
- data/spec/grammar/parslet_merge_spec.rb +33 -0
- data/spec/grammar/parslet_omission_spec.rb +58 -0
- data/spec/grammar/parslet_repetition_spec.rb +77 -0
- data/spec/grammar/parslet_sequence_spec.rb +49 -0
- data/spec/grammar/parslet_spec.rb +23 -0
- data/spec/grammar/predicate_spec.rb +53 -0
- data/spec/grammar/proc_parslet_spec.rb +52 -0
- data/spec/grammar/regexp_parslet_spec.rb +347 -0
- data/spec/grammar/string_enumerator_spec.rb +94 -0
- data/spec/grammar/string_parslet_spec.rb +143 -0
- data/spec/grammar/symbol_parslet_spec.rb +30 -0
- data/spec/grammar_spec.rb +545 -0
- data/spec/parser_spec.rb +1418 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/walrus_grammar/comment_spec.rb +39 -0
- data/spec/walrus_grammar/echo_directive_spec.rb +63 -0
- data/spec/walrus_grammar/escape_sequence_spec.rb +85 -0
- data/spec/walrus_grammar/literal_spec.rb +41 -0
- data/spec/walrus_grammar/message_expression_spec.rb +37 -0
- data/spec/walrus_grammar/multiline_comment_spec.rb +58 -0
- data/spec/walrus_grammar/placeholder_spec.rb +48 -0
- data/spec/walrus_grammar/raw_directive_spec.rb +81 -0
- data/spec/walrus_grammar/raw_text_spec.rb +65 -0
- data/spec/walrus_grammar/silent_directive_spec.rb +34 -0
- metadata +291 -0
@@ -0,0 +1,420 @@
|
|
1
|
+
# Copyright 2007 Wincent Colaiuta
|
2
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
3
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
4
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
5
|
+
# in the accompanying file, "LICENSE.txt", for more details.
|
6
|
+
#
|
7
|
+
# $Id: /mirrors/Walrus/trunk/walrus/lib/walrus/parser.rb 6755 2007-04-13T14:13:42.727911Z wincent $
|
8
|
+
|
9
|
+
require 'walrus'
|
10
|
+
require 'pathname'
|
11
|
+
|
12
|
+
module Walrus
|
13
|
+
|
14
|
+
# The parser is currently quite slow, although perfectly usable.
|
15
|
+
# The quickest route to optimizing it may be to replace it with a C parser inside a Ruby extension,
|
16
|
+
# possibly generated using Ragel: http://www.cs.queensu.ca/~thurston/ragel/
|
17
|
+
class Parser
|
18
|
+
|
19
|
+
def parse(string, options = {})
|
20
|
+
@@grammar.parse(string, options)
|
21
|
+
end
|
22
|
+
|
23
|
+
def compile(string, options = {})
|
24
|
+
@@compiler ||= Compiler.new
|
25
|
+
parsed = []
|
26
|
+
catch :AndPredicateSuccess do # catch here because an empty file will throw
|
27
|
+
parsed = parse string, options
|
28
|
+
end
|
29
|
+
@@compiler.compile parsed, options
|
30
|
+
end
|
31
|
+
|
32
|
+
@@grammar ||= Grammar.subclass('WalrusGrammar') do
|
33
|
+
|
34
|
+
starting_symbol :template
|
35
|
+
|
36
|
+
skipping :whitespace_or_newlines
|
37
|
+
|
38
|
+
rule :whitespace_or_newlines, /\s+/
|
39
|
+
|
40
|
+
# only spaces and tabs, not newlines
|
41
|
+
rule :whitespace, /[ \t\v]+/
|
42
|
+
rule :newline, /(\r\n|\r|\n)/
|
43
|
+
|
44
|
+
# optional whitespace (tabs and spaces only) followed by a backslash/newline (note: this is not escape-aware)
|
45
|
+
rule :line_continuation, /[ \t\v]*\\\n/
|
46
|
+
rule :end_of_input, /\z/
|
47
|
+
|
48
|
+
rule :template, :template_element.zero_or_more & :end_of_input.and?
|
49
|
+
rule :template_element, :raw_text | :comment | :multiline_comment | :directive | :placeholder |:escape_sequence
|
50
|
+
|
51
|
+
# anything at all other than the three characters which have special meaning in Walrus: $, \ and #
|
52
|
+
rule :raw_text, /[^\$\\#]+/
|
53
|
+
production :raw_text.build(:node)
|
54
|
+
|
55
|
+
node :literal
|
56
|
+
|
57
|
+
rule :string_literal, :single_quoted_string_literal | :double_quoted_string_literal
|
58
|
+
skipping :string_literal, nil
|
59
|
+
node :string_literal, :literal
|
60
|
+
|
61
|
+
rule :single_quoted_string_literal, "'".skip & :single_quoted_string_content.optional & "'".skip
|
62
|
+
production :single_quoted_string_literal.build(:string_literal)
|
63
|
+
rule :single_quoted_string_content, /(\\(?!').|\\'|[^'\\]+)+/
|
64
|
+
rule :double_quoted_string_literal, '"'.skip & :double_quoted_string_content.optional & '"'.skip
|
65
|
+
production :double_quoted_string_literal.build(:string_literal)
|
66
|
+
rule :double_quoted_string_content, /(\\(?!").|\\"|[^"\\]+)+/
|
67
|
+
|
68
|
+
# TODO: support 1_000_000 syntax for numeric_literals
|
69
|
+
rule :numeric_literal, /\d+\.\d+|\d+(?!\.)/
|
70
|
+
production :numeric_literal.build(:literal)
|
71
|
+
|
72
|
+
# this matches both "foo" and "Foo::bar"
|
73
|
+
rule :identifier, /([A-Z][a-zA-Z0-9_]*::)*[a-z_][a-zA-Z0-9_]*/
|
74
|
+
production :identifier.build(:literal)
|
75
|
+
|
76
|
+
# this matches both "Foo" and "Foo::Bar"
|
77
|
+
rule :constant, /([A-Z][a-zA-Z0-9_]*::)*[A-Z][a-zA-Z0-9_]*/
|
78
|
+
production :constant.build(:literal)
|
79
|
+
|
80
|
+
rule :symbol_literal, /:[a-zA-Z_][a-zA-Z0-9_]*/
|
81
|
+
production :symbol_literal.build(:literal)
|
82
|
+
|
83
|
+
rule :escape_sequence, '\\'.skip & /[\$\\#]/
|
84
|
+
production :escape_sequence.build(:node)
|
85
|
+
|
86
|
+
rule :comment, '##'.skip & /.*$/
|
87
|
+
production :comment.build(:node)
|
88
|
+
|
89
|
+
# nested multiline comments
|
90
|
+
rule :multiline_comment, '#*'.skip & :comment_content.zero_or_more('') & '*#'.skip
|
91
|
+
skipping :multiline_comment, nil
|
92
|
+
production :multiline_comment.build(:comment, :content)
|
93
|
+
|
94
|
+
rule :comment_content, (:comment & :newline.skip) |
|
95
|
+
:multiline_comment |
|
96
|
+
/( # three possibilities:
|
97
|
+
[^*#]+ | # 1. any run of characters other than # or *
|
98
|
+
\#(?!\#|\*) | # 2. any # not followed by another # or a *
|
99
|
+
\*(?!\#) # 3. any * not followed by a #
|
100
|
+
)+ # match the three possibilities repeatedly
|
101
|
+
/x
|
102
|
+
|
103
|
+
rule :directive, :block_directive |
|
104
|
+
:def_directive |
|
105
|
+
:echo_directive |
|
106
|
+
:extends_directive |
|
107
|
+
:import_directive |
|
108
|
+
:include_directive |
|
109
|
+
:raw_directive |
|
110
|
+
:ruby_directive |
|
111
|
+
:set_directive |
|
112
|
+
:silent_directive |
|
113
|
+
:slurp_directive |
|
114
|
+
:super_directive
|
115
|
+
|
116
|
+
node :directive
|
117
|
+
|
118
|
+
# directives may span multiple lines if and only if the last character on the line is a backslash \
|
119
|
+
skipping :directive, :whitespace | :line_continuation
|
120
|
+
|
121
|
+
# "Directive tags can be closed explicitly with #, or implicitly with the end of the line"
|
122
|
+
# http://www.cheetahtemplate.org/docs/users_guide_html_multipage/language.directives.closures.html
|
123
|
+
# This means that to follow a directive by a comment on the same line you must explicitly close the directive first (otherwise the grammar would be ambiguous).
|
124
|
+
# Note that "skipping" the end_of_input here is harmless as it isn't actually consumed.
|
125
|
+
rule :directive_end, ( /#/ | :newline | :end_of_input ).skip
|
126
|
+
|
127
|
+
rule :block_directive, '#block'.skip & :identifier & :def_parameter_list.optional([]) & :directive_end &
|
128
|
+
:template_element.zero_or_more([]) &
|
129
|
+
'#end'.skip & :directive_end
|
130
|
+
|
131
|
+
rule :def_directive, '#def'.skip & :identifier & :def_parameter_list.optional([]) & :directive_end &
|
132
|
+
:template_element.zero_or_more([]) &
|
133
|
+
'#end'.skip & :directive_end
|
134
|
+
|
135
|
+
production :def_directive.build(:directive, :identifier, :params, :content) # superclass (DefDirective) must be created
|
136
|
+
production :block_directive.build(:def_directive, :identifier, :params, :content) # before the subclass (BlockDirective)
|
137
|
+
|
138
|
+
# "The #echo directive is used to echo the output from expressions that can't be written as simple $placeholders."
|
139
|
+
# http://www.cheetahtemplate.org/docs/users_guide_html_multipage/output.echo.html
|
140
|
+
#
|
141
|
+
# Convenient alternative short syntax for the #echo directive, similar to ERB (http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/):
|
142
|
+
#
|
143
|
+
# #= expression(s) #
|
144
|
+
#
|
145
|
+
# Is a shortcut equivalent to:
|
146
|
+
#
|
147
|
+
# #echo expression(s) #
|
148
|
+
#
|
149
|
+
# This is similar to the ERB syntax, but even more concise:
|
150
|
+
#
|
151
|
+
# <%= expression(s) =>
|
152
|
+
#
|
153
|
+
# See also the #silent directive, which also has a shortcut syntax.
|
154
|
+
#
|
155
|
+
rule :echo_directive, '#echo'.skip & :ruby_expression_list & :directive_end | # long form
|
156
|
+
'#='.skip & :ruby_expression_list & '#'.skip # short form
|
157
|
+
production :echo_directive.build(:directive, :expression)
|
158
|
+
|
159
|
+
rule :extends_directive, '#extends'.skip & :string_literal & :directive_end
|
160
|
+
rule :import_directive, '#import'.skip & :string_literal & :directive_end
|
161
|
+
production :import_directive.build(:directive, :class_name) # superclass (ImportDirective) must be created
|
162
|
+
production :extends_directive.build(:import_directive, :class_name) # before the subclass (ExtendsDirective)
|
163
|
+
|
164
|
+
rule :include_directive, '#include'.skip & :include_subparslet
|
165
|
+
production :include_directive.build(:directive, :file_name, :subtree)
|
166
|
+
|
167
|
+
rule :include_subparslet, lambda { |string, options|
|
168
|
+
|
169
|
+
# scans a string literal
|
170
|
+
parslet = :string_literal & :directive_end
|
171
|
+
file_name = parslet.parse(string, options)
|
172
|
+
|
173
|
+
# if options contains non-nil "origin" then try to construct relative path; otherwise just look in current working directory
|
174
|
+
if options[:origin]
|
175
|
+
current_location = Pathname.new(options[:origin]).dirname
|
176
|
+
include_target = current_location + file_name.to_s
|
177
|
+
else
|
178
|
+
include_target = Pathname.new file_name.to_s
|
179
|
+
end
|
180
|
+
|
181
|
+
# read file into string
|
182
|
+
content = include_target.read
|
183
|
+
|
184
|
+
# try to parse string in sub-parser
|
185
|
+
sub_options = { :origin => include_target.to_s }
|
186
|
+
sub_result = nil
|
187
|
+
catch :AndPredicateSuccess do
|
188
|
+
sub_result = Parser.new.parse(content, sub_options)
|
189
|
+
end
|
190
|
+
|
191
|
+
# want to insert a bunch of nodes (a subtree) into the parse tree without advance the location counters
|
192
|
+
sub_tree = Grammar::ArrayResult.new [ file_name, sub_result ? sub_result : [] ]
|
193
|
+
sub_tree.start = file_name.start
|
194
|
+
sub_tree.end = file_name.end
|
195
|
+
sub_tree
|
196
|
+
}
|
197
|
+
|
198
|
+
# "Any section of a template definition that is inside a #raw ... #end raw tag pair will be printed verbatim without any parsing of $placeholders or other directives."
|
199
|
+
# http://www.cheetahtemplate.org/docs/users_guide_html_multipage/output.raw.html
|
200
|
+
# Unlike Cheetah, Walrus uses a bare "#end" marker and not an "#end raw" to mark the end of the raw block.
|
201
|
+
# The presence of a literal #end within a raw block is made possible by using an optional "here doc"-style delimiter:
|
202
|
+
#
|
203
|
+
# #raw <<END_MARKER
|
204
|
+
# content goes here
|
205
|
+
# END_MARKER
|
206
|
+
#
|
207
|
+
# Here the opening "END_MARKER" must be the last thing on the line (trailing whitespace up to and including the newline is allowed but it is not considered to be part of the quoted text). The final "END_MARKER" must be the very first and last thing on the line, or it will not be considered to be an end marker at all and will be considered part of the quoted text. The newline immediately prior to the end marker is included in the quoted text.
|
208
|
+
#
|
209
|
+
# Or, if the end marker is to be indented:
|
210
|
+
#
|
211
|
+
# #raw <<-END_MARKER
|
212
|
+
# content
|
213
|
+
# END_MARKER
|
214
|
+
#
|
215
|
+
# Here "END_MARKER" may be preceeded by whitespace (and whitespace only) but it must be the last thing on the line. The preceding whitespace is not considered to be part of the quoted text.
|
216
|
+
rule :raw_directive, '#raw'.skip & ((:directive_end & /([^#]+|#(?!end)+)*/ & '#end'.skip & :directive_end) | :here_document)
|
217
|
+
production :raw_directive.build(:directive, :content)
|
218
|
+
|
219
|
+
# In order to parse "here documents" we adopt a model similar to the one proposed in this message to the ANTLR interest list:
|
220
|
+
# http://www.antlr.org:8080/pipermail/antlr-interest/2005-September/013673.html
|
221
|
+
rule :here_document, lambda { |string, options|
|
222
|
+
|
223
|
+
# for the time-being, not sure if there is much benefit in calling memoizing_parse here
|
224
|
+
state = Grammar::ParserState.new(string, options)
|
225
|
+
parsed = /<<(-?)([a-zA-Z0-9_]+)[ \t\v]*\n/.to_parseable.parse(state.remainder, state.options)
|
226
|
+
state.skipped(parsed)
|
227
|
+
marker = parsed.match_data
|
228
|
+
indenting = (marker[1] == '') ? false : true
|
229
|
+
|
230
|
+
if indenting # whitespace allowed before end marker
|
231
|
+
end_marker = /^[ \t\v]*#{marker[2]}[ \t\v]*(\n|\z)/.to_parseable # will eat trailing newline
|
232
|
+
else # no whitespace allowed before end marker
|
233
|
+
end_marker = /^#{marker[2]}[ \t\v]*(\n|\z)/.to_parseable # will eat trailing newline
|
234
|
+
end
|
235
|
+
|
236
|
+
line = /^.*\n/.to_parseable # for gobbling a line
|
237
|
+
|
238
|
+
while true do
|
239
|
+
begin
|
240
|
+
skipped = end_marker.parse(state.remainder, state.options)
|
241
|
+
state.skipped(skipped) # found end marker, skip it
|
242
|
+
break # all done
|
243
|
+
rescue Grammar::ParseError # didn't find end marker yet, consume a line
|
244
|
+
parsed = line.parse(state.remainder, state.options)
|
245
|
+
state.parsed(parsed)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
# caller will want a String, not an Array
|
250
|
+
results = state.results
|
251
|
+
document = Grammar::StringResult.new(results.to_s)
|
252
|
+
document.start = results.start
|
253
|
+
document.end = results.end
|
254
|
+
document
|
255
|
+
}
|
256
|
+
|
257
|
+
rule :ruby_directive, '#ruby'.skip & ((:directive_end & /([^#]+|#(?!end)+)*/ & '#end'.skip & :directive_end) | :here_document)
|
258
|
+
production :ruby_directive.build(:directive, :content)
|
259
|
+
|
260
|
+
# Unlike a normal Ruby assignement expression, the lvalue of a "#set" directive is an identifier preceded by a dollar sign.
|
261
|
+
rule :set_directive, '#set'.skip & /\$(?![ \r\n\t\v])/.skip & :placeholder_name & '='.skip & (:addition_expression | :unary_expression) & :directive_end
|
262
|
+
production :set_directive.build(:directive, :placeholder, :expression)
|
263
|
+
|
264
|
+
# "#silent is the opposite of #echo. It executes an expression but discards the output."
|
265
|
+
# http://www.cheetahtemplate.org/docs/users_guide_html_multipage/output.silent.html
|
266
|
+
#
|
267
|
+
# Like the #echo directive, a convienient shorthand syntax is available:
|
268
|
+
#
|
269
|
+
# # expressions(s) #
|
270
|
+
#
|
271
|
+
# Equivalent to the long form:
|
272
|
+
#
|
273
|
+
# #silent expressions(s) #
|
274
|
+
#
|
275
|
+
# And similar to but more concise than the ERB syntax:
|
276
|
+
#
|
277
|
+
# <% expressions(s) %>
|
278
|
+
#
|
279
|
+
# Note that the space between the opening hash character and the expression(s) is required in order for Walrus to distinguish the shorthand for the #silent directive from the other directives. That is, the following is not legal:
|
280
|
+
#
|
281
|
+
# #expressions(s) #
|
282
|
+
#
|
283
|
+
rule :silent_directive, '#silent'.skip & :ruby_expression_list & :directive_end | # long form
|
284
|
+
'# '.skip & :ruby_expression_list & '#'.skip # short form
|
285
|
+
production :silent_directive.build(:directive, :expression)
|
286
|
+
|
287
|
+
# Accept multiple expressions separated by a semi-colon.
|
288
|
+
rule :ruby_expression_list, :ruby_expression >> (';'.skip & :ruby_expression ).zero_or_more
|
289
|
+
|
290
|
+
# "The #slurp directive eats up the trailing newline on the line it appears in, joining the following line onto the current line."
|
291
|
+
# http://www.cheetahtemplate.org/docs/users_guide_html_multipage/output.slurp.html
|
292
|
+
# The "slurp" directive must be the last thing on the line (not followed by a comment or directive end marker)
|
293
|
+
rule :slurp_directive, '#slurp' & :whitespace.optional.skip & :newline.skip
|
294
|
+
production :slurp_directive.build(:directive)
|
295
|
+
|
296
|
+
rule :super_directive, :super_with_parentheses | :super_without_parentheses
|
297
|
+
node :super_directive
|
298
|
+
rule :super_with_parentheses, '#super'.skip & :parameter_list.optional & :directive_end
|
299
|
+
production :super_with_parentheses.build(:super_directive, :params)
|
300
|
+
rule :super_without_parentheses, '#super'.skip & :parameter_list_without_parentheses & :directive_end
|
301
|
+
production :super_without_parentheses.build(:super_directive, :params)
|
302
|
+
|
303
|
+
# The "def_parameter_list" is a special case of parameter list which disallows interpolated placeholders.
|
304
|
+
rule :def_parameter_list, '('.skip & ( :def_parameter >> ( ','.skip & :def_parameter ).zero_or_more ).optional & ')'.skip
|
305
|
+
rule :def_parameter, :assignment_expression | :identifier
|
306
|
+
|
307
|
+
rule :parameter_list, '('.skip & ( :parameter >> ( ','.skip & :parameter ).zero_or_more ).optional & ')'.skip
|
308
|
+
rule :parameter_list_without_parentheses, :parameter >> ( ','.skip & :parameter ).zero_or_more
|
309
|
+
rule :parameter, :placeholder | :ruby_expression
|
310
|
+
|
311
|
+
# placeholders may be in long form (${foo}) or short form ($foo)
|
312
|
+
rule :placeholder, :long_placeholder | :short_placeholder
|
313
|
+
node :placeholder
|
314
|
+
|
315
|
+
# No whitespace allowed between the "$" and the opening "{"
|
316
|
+
rule :long_placeholder, '${'.skip & :placeholder_name & :placeholder_parameters.optional([]) & '}'.skip
|
317
|
+
production :long_placeholder.build(:placeholder, :name, :params)
|
318
|
+
|
319
|
+
# No whitespace allowed between the "$" and the placeholder_name
|
320
|
+
rule :short_placeholder, /\$(?![ \r\n\t\v])/.skip & :placeholder_name & :placeholder_parameters.optional([])
|
321
|
+
production :short_placeholder.build(:placeholder, :name, :params)
|
322
|
+
|
323
|
+
rule :placeholder_name, :identifier
|
324
|
+
rule :placeholder_parameters, '('.skip & (:placeholder_parameter >> (','.skip & :placeholder_parameter).zero_or_more).optional & ')'.skip
|
325
|
+
rule :placeholder_parameter, :placeholder | :ruby_expression
|
326
|
+
|
327
|
+
# simplified Ruby subset
|
328
|
+
rule :ruby_expression, :assignment_expression | :addition_expression | :unary_expression
|
329
|
+
node :ruby_expression
|
330
|
+
|
331
|
+
rule :literal_expression, :string_literal |
|
332
|
+
:numeric_literal |
|
333
|
+
:array_literal |
|
334
|
+
:hash_literal |
|
335
|
+
:lvalue |
|
336
|
+
:symbol_literal
|
337
|
+
rule :unary_expression, :message_expression | :literal_expression
|
338
|
+
|
339
|
+
rule :lvalue, :class_variable | :instance_variable | :identifier | :constant
|
340
|
+
|
341
|
+
rule :array_literal, '['.skip & ( :ruby_expression >> (','.skip & :ruby_expression ).zero_or_more ).optional & ']'.skip
|
342
|
+
production :array_literal.build(:ruby_expression, :elements)
|
343
|
+
|
344
|
+
rule :hash_literal, '{'.skip & ( :hash_assignment >> (','.skip & :hash_assignment ).zero_or_more ).optional & '}'.skip
|
345
|
+
production :hash_literal.build(:ruby_expression, :pairs)
|
346
|
+
|
347
|
+
rule :hash_assignment, :unary_expression & '=>'.skip & (:addition_expression | :unary_expression)
|
348
|
+
production :hash_assignment.build(:ruby_expression, :lvalue, :expression)
|
349
|
+
|
350
|
+
rule :assignment_expression, :lvalue & '='.skip & (:addition_expression | :unary_expression)
|
351
|
+
production :assignment_expression.build(:ruby_expression, :lvalue, :expression)
|
352
|
+
|
353
|
+
# addition is left-associative (left-recursive)
|
354
|
+
rule :addition_expression, :addition_expression & '+'.skip & :unary_expression |
|
355
|
+
:unary_expression & '+'.skip & :unary_expression
|
356
|
+
|
357
|
+
production :addition_expression.build(:ruby_expression, :left, :right)
|
358
|
+
|
359
|
+
# message expressions are left-associative (left-recursive)
|
360
|
+
rule :message_expression, :message_expression & '.'.skip & :method_expression |
|
361
|
+
:literal_expression & '.'.skip & :method_expression
|
362
|
+
production :message_expression.build(:ruby_expression, :target, :message)
|
363
|
+
|
364
|
+
rule :method_expression, :method_with_parentheses | :method_without_parentheses
|
365
|
+
node :method_expression, :ruby_expression
|
366
|
+
|
367
|
+
rule :method_with_parentheses, :identifier & :method_parameter_list.optional([])
|
368
|
+
production :method_with_parentheses.build(:method_expression, :name, :params)
|
369
|
+
rule :method_without_parentheses, :identifier & :method_parameter_list_without_parentheses
|
370
|
+
production :method_without_parentheses.build(:method_expression, :name, :params)
|
371
|
+
|
372
|
+
rule :method_parameter_list, '('.skip & ( :method_parameter >> ( ','.skip & :method_parameter ).zero_or_more ).optional & ')'.skip
|
373
|
+
rule :method_parameter, :ruby_expression
|
374
|
+
rule :method_parameter_list_without_parentheses, :method_parameter >> ( ','.skip & :method_parameter ).zero_or_more
|
375
|
+
|
376
|
+
rule :class_variable, '@@'.skip & :identifier
|
377
|
+
skipping :class_variable, nil
|
378
|
+
production :class_variable.build(:ruby_expression)
|
379
|
+
|
380
|
+
rule :instance_variable, '@'.skip & :identifier
|
381
|
+
skipping :instance_variable, nil
|
382
|
+
production :instance_variable.build(:ruby_expression)
|
383
|
+
|
384
|
+
# TODO: regexp literal expression
|
385
|
+
|
386
|
+
# Ruby + allowing placeholders for unary expressions
|
387
|
+
rule :extended_ruby_expression, :extended_unary_expression | :ruby_expression
|
388
|
+
node :extended_ruby_expression
|
389
|
+
|
390
|
+
rule :extended_unary_expression, :placeholder | :unary_expression
|
391
|
+
|
392
|
+
# only after defining the grammar is it safe to extend the classes dynamically created during the grammar definition
|
393
|
+
# TODO: modify the code which creates classes on the fly so that the requires are no longer order-sensitive
|
394
|
+
require 'walrus/walrus_grammar/assignment_expression'
|
395
|
+
require 'walrus/walrus_grammar/block_directive'
|
396
|
+
require 'walrus/walrus_grammar/comment'
|
397
|
+
require 'walrus/walrus_grammar/def_directive'
|
398
|
+
require 'walrus/walrus_grammar/echo_directive'
|
399
|
+
require 'walrus/walrus_grammar/escape_sequence'
|
400
|
+
require 'walrus/walrus_grammar/import_directive'
|
401
|
+
require 'walrus/walrus_grammar/include_directive'
|
402
|
+
require 'walrus/walrus_grammar/instance_variable'
|
403
|
+
require 'walrus/walrus_grammar/literal'
|
404
|
+
require 'walrus/walrus_grammar/message_expression'
|
405
|
+
require 'walrus/walrus_grammar/multiline_comment'
|
406
|
+
require 'walrus/walrus_grammar/placeholder'
|
407
|
+
require 'walrus/walrus_grammar/raw_directive'
|
408
|
+
require 'walrus/walrus_grammar/raw_text'
|
409
|
+
require 'walrus/walrus_grammar/ruby_directive'
|
410
|
+
require 'walrus/walrus_grammar/ruby_expression'
|
411
|
+
require 'walrus/walrus_grammar/set_directive'
|
412
|
+
require 'walrus/walrus_grammar/silent_directive'
|
413
|
+
require 'walrus/walrus_grammar/slurp_directive'
|
414
|
+
require 'walrus/walrus_grammar/super_directive'
|
415
|
+
|
416
|
+
end
|
417
|
+
|
418
|
+
end # class Parser
|
419
|
+
|
420
|
+
end # module Walrus
|