PageTemplate 1.2.0 → 2.0.0
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.
- data/Changes +24 -0
- data/MANIFEST +0 -1
- data/README.txt +10 -8
- data/Rakefile +30 -9
- data/lib/PageTemplate/case.rb +72 -0
- data/lib/PageTemplate/commands.rb +500 -0
- data/lib/PageTemplate/parser.rb +373 -0
- data/lib/PageTemplate.rb +47 -991
- data/setup-usage.txt +129 -0
- data/setup.rb +1360 -0
- data/tdata/dummy.txt +2 -0
- data/test.rb +616 -0
- metadata +13 -169
- data/CVS/Entries +0 -10
- data/CVS/Repository +0 -1
- data/CVS/Root +0 -1
- data/Changes~ +0 -7
- data/InstalledFiles +0 -7
- data/Rakefile~ +0 -54
- data/TC_PageTemplate.rb +0 -974
- data/TC_PageTemplate.rb~ +0 -965
- data/config.save +0 -12
- data/doc/classes/BlockCommand.html +0 -229
- data/doc/classes/BlockCommand.src/M000004.html +0 -18
- data/doc/classes/BlockCommand.src/M000005.html +0 -25
- data/doc/classes/BlockCommand.src/M000006.html +0 -18
- data/doc/classes/BlockCommand.src/M000007.html +0 -23
- data/doc/classes/BlockCommand.src/M000008.html +0 -24
- data/doc/classes/Command.html +0 -167
- data/doc/classes/Command.src/M000032.html +0 -19
- data/doc/classes/Command.src/M000033.html +0 -18
- data/doc/classes/CommentCommand.html +0 -146
- data/doc/classes/CommentCommand.src/M000009.html +0 -18
- data/doc/classes/IfCommand.html +0 -193
- data/doc/classes/IfCommand.src/M000042.html +0 -19
- data/doc/classes/IfCommand.src/M000043.html +0 -20
- data/doc/classes/IfCommand.src/M000044.html +0 -20
- data/doc/classes/IfElseCommand.html +0 -189
- data/doc/classes/IfElseCommand.src/M000025.html +0 -19
- data/doc/classes/IfElseCommand.src/M000026.html +0 -23
- data/doc/classes/IfElseCommand.src/M000027.html +0 -20
- data/doc/classes/IncludeCommand.html +0 -171
- data/doc/classes/IncludeCommand.src/M000019.html +0 -26
- data/doc/classes/IncludeCommand.src/M000020.html +0 -28
- data/doc/classes/IncludeCommand.src/M000021.html +0 -18
- data/doc/classes/IncludeCommandError.html +0 -118
- data/doc/classes/LoopCommand.html +0 -197
- data/doc/classes/LoopCommand.src/M000001.html +0 -19
- data/doc/classes/LoopCommand.src/M000002.html +0 -42
- data/doc/classes/LoopCommand.src/M000003.html +0 -20
- data/doc/classes/LoopElseCommand.html +0 -190
- data/doc/classes/LoopElseCommand.src/M000022.html +0 -19
- data/doc/classes/LoopElseCommand.src/M000023.html +0 -23
- data/doc/classes/LoopElseCommand.src/M000024.html +0 -20
- data/doc/classes/Namespace.html +0 -297
- data/doc/classes/Namespace.src/M000034.html +0 -19
- data/doc/classes/Namespace.src/M000035.html +0 -18
- data/doc/classes/Namespace.src/M000036.html +0 -18
- data/doc/classes/Namespace.src/M000037.html +0 -19
- data/doc/classes/Namespace.src/M000038.html +0 -36
- data/doc/classes/Namespace.src/M000039.html +0 -21
- data/doc/classes/Namespace.src/M000040.html +0 -18
- data/doc/classes/Namespace.src/M000041.html +0 -18
- data/doc/classes/PageTemplate.html +0 -380
- data/doc/classes/PageTemplate.src/M000010.html +0 -49
- data/doc/classes/PageTemplate.src/M000011.html +0 -18
- data/doc/classes/PageTemplate.src/M000012.html +0 -29
- data/doc/classes/PageTemplate.src/M000013.html +0 -18
- data/doc/classes/PageTemplate.src/M000014.html +0 -18
- data/doc/classes/PageTemplate.src/M000015.html +0 -18
- data/doc/classes/PageTemplate.src/M000016.html +0 -18
- data/doc/classes/PageTemplate.src/M000017.html +0 -18
- data/doc/classes/PageTemplate.src/M000018.html +0 -19
- data/doc/classes/Syntax/CachedParser.html +0 -163
- data/doc/classes/Syntax/CachedParser.src/M000048.html +0 -19
- data/doc/classes/Syntax/CachedParser.src/M000049.html +0 -53
- data/doc/classes/Syntax/Glossary.html +0 -251
- data/doc/classes/Syntax/Glossary.src/M000050.html +0 -20
- data/doc/classes/Syntax/Glossary.src/M000051.html +0 -29
- data/doc/classes/Syntax/Parser.html +0 -261
- data/doc/classes/Syntax/Parser.src/M000052.html +0 -22
- data/doc/classes/Syntax/Parser.src/M000053.html +0 -36
- data/doc/classes/Syntax/Parser.src/M000054.html +0 -18
- data/doc/classes/Syntax/Parser.src/M000055.html +0 -21
- data/doc/classes/Syntax/Parser.src/M000056.html +0 -53
- data/doc/classes/Syntax/Parser.src/M000057.html +0 -111
- data/doc/classes/Syntax.html +0 -139
- data/doc/classes/TextCommand.html +0 -185
- data/doc/classes/TextCommand.src/M000045.html +0 -18
- data/doc/classes/TextCommand.src/M000046.html +0 -18
- data/doc/classes/TextCommand.src/M000047.html +0 -18
- data/doc/classes/UnlessCommand.html +0 -151
- data/doc/classes/UnlessCommand.src/M000028.html +0 -20
- data/doc/classes/ValueCommand.html +0 -186
- data/doc/classes/ValueCommand.src/M000029.html +0 -18
- data/doc/classes/ValueCommand.src/M000030.html +0 -18
- data/doc/classes/ValueCommand.src/M000031.html +0 -20
- data/doc/created.rid +0 -1
- data/doc/files/README_txt.html +0 -224
- data/doc/files/lib/PageTemplate_rb.html +0 -119
- data/doc/fr_class_index.html +0 -44
- data/doc/fr_file_index.html +0 -28
- data/doc/fr_method_index.html +0 -83
- data/doc/index.html +0 -24
- data/doc/rdoc-style.css +0 -208
- data/install.rb +0 -1015
- data/lib/CVS/Entries +0 -2
- data/lib/CVS/Repository +0 -1
- data/lib/CVS/Root +0 -1
- data/lib/PageTemplate.rb~ +0 -1004
- data/pkg/PageTemplate-1.2.0.gem +0 -0
- data/tdata/CVS/Entries +0 -28
- data/tdata/CVS/Repository +0 -1
- data/tdata/CVS/Root +0 -1
- data/tdata/cm.txt +0 -3
- data/tdata/complex.txt +0 -116
- data/tdata/i1.txt +0 -1
- data/tdata/i2.txt +0 -7
- data/tdata/ib1.txt +0 -7
- data/tdata/ib2.txt +0 -13
- data/tdata/ie1.txt +0 -4
- data/tdata/ie2.txt +0 -15
- data/tdata/include.1.txt +0 -1
- data/tdata/include.2.txt +0 -1
- data/tdata/include.3.txt +0 -1
- data/tdata/include.4.nf.out.txt +0 -2
- data/tdata/include.4.out.txt +0 -3
- data/tdata/include.4.txt +0 -2
- data/tdata/include.4a.txt +0 -1
- data/tdata/include.4b.txt +0 -2
- data/tdata/include.5.txt +0 -2
- data/tdata/l1.txt +0 -5
- data/tdata/l2.txt +0 -7
- data/tdata/metadata.1.txt +0 -5
- data/tdata/metadata.2.txt +0 -5
- data/tdata/metadata.3.txt +0 -5
- data/tdata/metadata.4.txt +0 -5
- data/tdata/nm.txt +0 -4
- data/tdata/p/CVS/Entries +0 -5
- data/tdata/p/CVS/Repository +0 -1
- data/tdata/p/CVS/Root +0 -1
- data/tdata/p/CVS/b2.txt,t +0 -0
- data/tdata/p/_b1.txt +0 -0
- data/tdata/p/b1.txt +0 -4
- data/tdata/p/b2.txt +0 -4
- data/tdata/p/b2.txt~ +0 -4
- data/tdata/p/cb1.txt +0 -4
- data/tdata/t.txt +0 -2
- data/tdata/v1.txt +0 -11
- data/tdata/v2.txt +0 -11
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
# parser.rb
|
|
2
|
+
#
|
|
3
|
+
# This contains the guts of the engines that make PageTemplate work.
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
############################################################
|
|
7
|
+
|
|
8
|
+
class PageTemplate
|
|
9
|
+
|
|
10
|
+
# A Namespace object consists of three things:
|
|
11
|
+
#
|
|
12
|
+
# parent: A parent object to get a value from if the namespace
|
|
13
|
+
# does not 'know' a value.
|
|
14
|
+
#
|
|
15
|
+
# object: An object is a hash or list that contains the values that
|
|
16
|
+
# this namespace will refer to. It may also be an object, in which
|
|
17
|
+
# case, its methods are treated as a hash, with respond_to? and
|
|
18
|
+
# send()
|
|
19
|
+
#
|
|
20
|
+
# Cache: A cache ensures that a method on an object will only be
|
|
21
|
+
# called once.
|
|
22
|
+
class Namespace
|
|
23
|
+
attr_accessor :parent, :object
|
|
24
|
+
def initialize(parent=nil,object=nil)
|
|
25
|
+
@values = Hash.new
|
|
26
|
+
@parent = parent
|
|
27
|
+
@object = object
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Clears the cache
|
|
31
|
+
def clear_cache
|
|
32
|
+
@values = Hash.new
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Saves a variable +key+ as the string +value+ in the global
|
|
36
|
+
# namespace.
|
|
37
|
+
def set(key, value)
|
|
38
|
+
@values[key] = value
|
|
39
|
+
end
|
|
40
|
+
alias_method :[]=, :set
|
|
41
|
+
|
|
42
|
+
# Returns the first value found for +key+ in the nested namespaces.
|
|
43
|
+
# Returns nil if no value is found.
|
|
44
|
+
#
|
|
45
|
+
# Values are checked for in this order through each level of namespace:
|
|
46
|
+
#
|
|
47
|
+
# * level.key
|
|
48
|
+
# * level.key()
|
|
49
|
+
# * level[key]
|
|
50
|
+
#
|
|
51
|
+
# If a value is not found in any of the nested namespaces, get()
|
|
52
|
+
# searches for the key in the global namespace.
|
|
53
|
+
#
|
|
54
|
+
# If +val+ is a dot-separated list of words, then 'key' is the
|
|
55
|
+
# first part of it. The remainder of the words are sent to key
|
|
56
|
+
# (and further results) to premit accessing attributes of objects.
|
|
57
|
+
def get(val)
|
|
58
|
+
# Is it ours, or is it cached?
|
|
59
|
+
key, rest = val.split(/\./,2)
|
|
60
|
+
value = case
|
|
61
|
+
when @values.has_key?(key)
|
|
62
|
+
@values[key]
|
|
63
|
+
when @object.respond_to?(:has_key?) && @object.has_key?(key)
|
|
64
|
+
@values[key] = @object[key]
|
|
65
|
+
when @object && @object.respond_to?(key.to_sym)
|
|
66
|
+
# This sets the cache and returns the value, too!
|
|
67
|
+
@values[key] = @object.send(key)
|
|
68
|
+
when @object && key == '__ITEM__'
|
|
69
|
+
@object
|
|
70
|
+
when @parent
|
|
71
|
+
@parent.get(key)
|
|
72
|
+
else
|
|
73
|
+
# We're the global namespace
|
|
74
|
+
nil
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
if rest
|
|
78
|
+
rest.split(/\./).each do |i|
|
|
79
|
+
begin
|
|
80
|
+
value = value.send(i)
|
|
81
|
+
rescue NoMethodError => er
|
|
82
|
+
return nil
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
value
|
|
87
|
+
end
|
|
88
|
+
alias_method :[], :get
|
|
89
|
+
|
|
90
|
+
# A convenience method to test whether a variable has a true
|
|
91
|
+
# value. Returns nil if +flag+ is not found in the namespace,
|
|
92
|
+
# or +flag+ has a nil value attached to it.
|
|
93
|
+
def true?(flag)
|
|
94
|
+
get(flag) ? true : false
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# A Source is a source from which templates are drawn.
|
|
99
|
+
#
|
|
100
|
+
# Source#get(name) must return the body of the template that is to
|
|
101
|
+
# be parsed, or nil if it doesn't exist.
|
|
102
|
+
class Source
|
|
103
|
+
def initialize(args = {})
|
|
104
|
+
@args = args
|
|
105
|
+
end
|
|
106
|
+
def get(name)
|
|
107
|
+
name
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# FileSource provides access to files within the 'include_paths'
|
|
112
|
+
# argument.
|
|
113
|
+
#
|
|
114
|
+
# It attempts to be secure by not allowing any access outside of
|
|
115
|
+
# The directories detailed within include_paths
|
|
116
|
+
class FileSource < Source
|
|
117
|
+
attr_reader :paths
|
|
118
|
+
|
|
119
|
+
def initialize(args = {})
|
|
120
|
+
@args = args
|
|
121
|
+
@paths = @args['include_paths']
|
|
122
|
+
@paths ||= [@args['include_path']].compact
|
|
123
|
+
@paths = [Dir.getwd] if @paths.empty?
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Return the contents of the file +name+, which must be within the
|
|
127
|
+
# include_paths.
|
|
128
|
+
def get(name)
|
|
129
|
+
begin
|
|
130
|
+
fn = get_filename(name)
|
|
131
|
+
if fn
|
|
132
|
+
return IO.read(fn)
|
|
133
|
+
else
|
|
134
|
+
return nil
|
|
135
|
+
end
|
|
136
|
+
rescue Exception => er
|
|
137
|
+
return "[ Unable to open file #{fn} ]"
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
def get_filename(file)
|
|
141
|
+
file = file.gsub(/\.\.\//,'')
|
|
142
|
+
file.untaint
|
|
143
|
+
@paths.each do |path|
|
|
144
|
+
fn = File.join(path,file)
|
|
145
|
+
return fn if File.exists?(fn)
|
|
146
|
+
end
|
|
147
|
+
return nil
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# This is the dictionary of commands and the directive that Parser
|
|
152
|
+
# uses to compile a template into a tree of commands.
|
|
153
|
+
#
|
|
154
|
+
# directive is the general format of a PageTemplate command.
|
|
155
|
+
# Default: /\[%(.+?)%\]/m
|
|
156
|
+
#
|
|
157
|
+
# @glossary is a hash table of regexp->Command objects. These
|
|
158
|
+
# regexps should not contain PageTemplate command text. i.e:
|
|
159
|
+
# /var \w+/i should be used instead of /[% var %]/
|
|
160
|
+
class SyntaxGlossary
|
|
161
|
+
attr_accessor :directive
|
|
162
|
+
|
|
163
|
+
def initialize(directive, glossary)
|
|
164
|
+
@directive = directive
|
|
165
|
+
@glossary = glossary
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Look up +text+ to see if it matches a command within the lookup
|
|
169
|
+
# table, returning the command for it, or UnknownCommand if none
|
|
170
|
+
# match.
|
|
171
|
+
def lookup(text)
|
|
172
|
+
@glossary.each do |key,val|
|
|
173
|
+
if m = key.match(text)
|
|
174
|
+
return val
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
return UnknownCommand
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Define a regexp -> Command mapping.
|
|
182
|
+
# +rx+ is inserted in the lookup table as a key for +command+
|
|
183
|
+
def define(rx,command)
|
|
184
|
+
raise ArgumentError, 'First argument to define must be a Regexp' unless rx.is_a?(Regexp)
|
|
185
|
+
@glossary[rx] = command
|
|
186
|
+
end
|
|
187
|
+
# This is shorthand for define(+key+,Valuecommand), and also
|
|
188
|
+
# allows +key+ to be a string, converting it to a regexp before
|
|
189
|
+
# adding it to the dictionary.
|
|
190
|
+
def define_global_var(key)
|
|
191
|
+
rx = key
|
|
192
|
+
rx = /^#{key.to_s}(?:\.\w+\??)*(?:\s:(\w+))?$/ unless key.is_a?(Regexp)
|
|
193
|
+
@glossary[rx] = ValueCommand
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# This is the regexp we tried using for grabbing an entire line
|
|
198
|
+
# Good for everything but ValueCommand :-/.
|
|
199
|
+
# /(?:^\s*\[%([^\]]+?)%\]\s*$\r?\n?|\[%(.+?)%\])/m,
|
|
200
|
+
DEFAULTGLOSSARY = SyntaxGlossary.new(
|
|
201
|
+
/\[%(.+?)%\]/m,
|
|
202
|
+
|
|
203
|
+
/^var ((\w+)(\.\w+\??)*)(\s+:\w+)?$/i => ValueCommand,
|
|
204
|
+
/^--/i => CommentCommand,
|
|
205
|
+
/^(if|unless) ((\w+)(\.\w+\??)*)$/i => IfCommand,
|
|
206
|
+
/^(in|loop) ((\w+)(\.\w+\??)*)(:\s+\w+)?$/i => LoopCommand,
|
|
207
|
+
/^(include) ((\w+)(\.\w+\??)*)$/i => IncludeCommand
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
# Preprocessor is a parent class for preprocessors.
|
|
211
|
+
# A preprocessor is used to process or otherwise prepare output for
|
|
212
|
+
# printing by Valuecommand
|
|
213
|
+
#
|
|
214
|
+
# Preprocessors must be singletons, and take a string to convert in
|
|
215
|
+
# some form.
|
|
216
|
+
class Preprocessor
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# DefaultPreprocessor is a preprocessor with simple processors.
|
|
220
|
+
class DefaultPreprocessor < Preprocessor
|
|
221
|
+
# Just the string itself.
|
|
222
|
+
def DefaultPreprocessor.process(str)
|
|
223
|
+
str
|
|
224
|
+
end
|
|
225
|
+
# Reverse the string. (Just to demonstrate preprocessor)
|
|
226
|
+
def DefaultPreprocessor.reverse(str)
|
|
227
|
+
str.reverse
|
|
228
|
+
end
|
|
229
|
+
# HTML's escapeURI
|
|
230
|
+
def DefaultPreprocessor.escapeURI(string)
|
|
231
|
+
string.gsub(/([^ a-zA-Z0-9_.-]+)/n) do
|
|
232
|
+
'%' + $1.unpack('H2' * $1.size).join('%').upcase
|
|
233
|
+
end.tr(' ','+')
|
|
234
|
+
end
|
|
235
|
+
# escape HTML.
|
|
236
|
+
def DefaultPreprocessor.escapeHTML(string)
|
|
237
|
+
str = string.gsub(/&/n, '&')
|
|
238
|
+
str.gsub!(/\"/n, '"')
|
|
239
|
+
str.gsub!(/>/n, '>')
|
|
240
|
+
str.gsub!(/</n, '<')
|
|
241
|
+
str
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# The big ass parser that does all the dirty work of turning
|
|
246
|
+
# templates into compiled commands.
|
|
247
|
+
#
|
|
248
|
+
# Parser.new() accepts a hash as an argument, and looks for these
|
|
249
|
+
# keys: (with defaults)
|
|
250
|
+
#
|
|
251
|
+
# 'namespace' => A namespace object. (A new namespace)
|
|
252
|
+
# 'glossary' => A SyntaxGlossary object. (a dup of DEFAULTGLOSSARY)
|
|
253
|
+
# 'preprocessor' => The preprocessor. (DefaultPreprocessor)
|
|
254
|
+
# 'default_processor' => The processor. (:process)
|
|
255
|
+
# 'source' => The Source for templates. (FileSource)
|
|
256
|
+
#
|
|
257
|
+
# Once the parser is created, it can compile and parse any number of
|
|
258
|
+
# templates.
|
|
259
|
+
#
|
|
260
|
+
# It can be treated as a one-template item by using
|
|
261
|
+
# Parser#load(template), and calling Parser.output
|
|
262
|
+
#
|
|
263
|
+
# To create separate generated templates from the same engine, use
|
|
264
|
+
# Parser#parse, or Parser#load. (It will still keep the most recent
|
|
265
|
+
# one it's #load'd, but that will not affect previously parsed or
|
|
266
|
+
# loaded)
|
|
267
|
+
class Parser
|
|
268
|
+
attr_reader :preprocessor, :default_processor
|
|
269
|
+
attr_reader :glossary, :namespace, :source
|
|
270
|
+
attr_reader :args, :commands
|
|
271
|
+
# Parser.new() accepts a hash as an argument, and looks for these
|
|
272
|
+
# keys: (with defaults)
|
|
273
|
+
#
|
|
274
|
+
# 'namespace' => A namespace object. (A new namespace)
|
|
275
|
+
# 'glossary' => A SyntaxGlossary object. (a dup of DEFAULTGLOSSARY)
|
|
276
|
+
# 'preprocessor' => The preprocessor. (DefaultPreprocessor)
|
|
277
|
+
# 'default_processor' => The processor. (:process)
|
|
278
|
+
# 'source' => The Source for templates. (FileSource)
|
|
279
|
+
def initialize(args = {})
|
|
280
|
+
@args = args # For sub-commands
|
|
281
|
+
@namespace = args['namespace'] || Namespace.new
|
|
282
|
+
@glossary = args['glossary'] || DEFAULTGLOSSARY.dup
|
|
283
|
+
@preprocessor = args['preprocessor'] || DefaultPreprocessor
|
|
284
|
+
@default_processor = args['default_processor'] || :process
|
|
285
|
+
@source = (args['source'] || FileSource).new(@args)
|
|
286
|
+
@commands = nil
|
|
287
|
+
end
|
|
288
|
+
# Load +name+ from a template, and save it to allow this parser to
|
|
289
|
+
# use it for output.
|
|
290
|
+
def load(name)
|
|
291
|
+
@commands = compile(name)
|
|
292
|
+
end
|
|
293
|
+
# Load +name+ from a template, but do not save it.
|
|
294
|
+
def compile(name)
|
|
295
|
+
body = @source.get(name)
|
|
296
|
+
if body
|
|
297
|
+
parse(body)
|
|
298
|
+
else
|
|
299
|
+
TextCommand.new("[ Template '#{name}' not found ]")
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
# Compile a Template (BlockCommand) from a string. Does not save
|
|
303
|
+
# the commands.
|
|
304
|
+
def parse(body)
|
|
305
|
+
rx = @glossary.directive
|
|
306
|
+
stack = [Template.new(self)]
|
|
307
|
+
while (m = rx.match(body))
|
|
308
|
+
pre = m.pre_match
|
|
309
|
+
command = m[1].strip.gsub(/\s+/,' ')
|
|
310
|
+
body = m.post_match
|
|
311
|
+
|
|
312
|
+
# Convert all pre-text to a TextCommand
|
|
313
|
+
if (pre && pre.length > 0)
|
|
314
|
+
stack.last.add TextCommand.new(pre)
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# If the command at the top of the stack is a 'Stacker',
|
|
318
|
+
# Does this command modify it? If so, just skip back.
|
|
319
|
+
next if stack.last.modifies?(command)
|
|
320
|
+
|
|
321
|
+
# If it closes, we're done changing this. Pop it off the
|
|
322
|
+
# Stack and add it to the one above it.
|
|
323
|
+
if stack.last.closes?(command)
|
|
324
|
+
cmd = stack.pop
|
|
325
|
+
stack.last.add(cmd)
|
|
326
|
+
next
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
# Find what command it is.
|
|
330
|
+
cmd = @glossary.lookup(command)
|
|
331
|
+
|
|
332
|
+
# If the command takes it, pass the parser.
|
|
333
|
+
block = if (cmd.instance_method(:initialize).arity == 2)
|
|
334
|
+
cmd.new(command,self)
|
|
335
|
+
else
|
|
336
|
+
cmd.new(command)
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
# If it's a stacking command, push it on the stack
|
|
340
|
+
if block.is_a?(StackableCommand)
|
|
341
|
+
stack.push block
|
|
342
|
+
else
|
|
343
|
+
stack.last.add block
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
stack.last.add TextCommand.new(body) if body && body.length > 0
|
|
347
|
+
if (stack.length > 1)
|
|
348
|
+
raise ArgumentError, 'Mismatched command closures in template'
|
|
349
|
+
end
|
|
350
|
+
stack[0]
|
|
351
|
+
end
|
|
352
|
+
# Not really of any point, but clears the saved commands.
|
|
353
|
+
def clearCommands
|
|
354
|
+
@commands = nil
|
|
355
|
+
end
|
|
356
|
+
# Sets this parser's namespace values.
|
|
357
|
+
def []=(key,val)
|
|
358
|
+
@namespace[key] = val
|
|
359
|
+
end
|
|
360
|
+
# Gets this parser's namespace values.
|
|
361
|
+
def [](key)
|
|
362
|
+
@namespace[key]
|
|
363
|
+
end
|
|
364
|
+
# If any commands are loaded and saved, return a string of it.
|
|
365
|
+
def output(namespace=nil)
|
|
366
|
+
return '' unless @commands
|
|
367
|
+
@namespace.parent = namespace if namespace
|
|
368
|
+
str = @commands.output(@namespace)
|
|
369
|
+
@namespace.parent = nil
|
|
370
|
+
str
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
end
|