glyph 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (132) hide show
  1. data/Rakefile +19 -40
  2. data/benchmark.rb +1 -2
  3. data/book/config.yml +8 -8
  4. data/book/document.glyph +18 -19
  5. data/book/images/glyph/commands_tasks.png +0 -0
  6. data/book/lib/layouts/bookindex.glyph +113 -111
  7. data/book/lib/layouts/bookpage.glyph +112 -108
  8. data/book/lib/macros/reference.rb +2 -2
  9. data/book/lib/tasks/tasks.rake +1 -1
  10. data/book/text/acknowledgements.glyph +1 -0
  11. data/book/text/changelog.glyph +140 -112
  12. data/book/text/compiling/compiling.glyph +36 -24
  13. data/book/text/compiling/lite_mode.glyph +11 -11
  14. data/book/text/compiling/programmatic_usage.glyph +57 -57
  15. data/book/text/config/document.glyph +7 -1
  16. data/book/text/config/options.glyph +5 -3
  17. data/book/text/config/output.glyph +41 -8
  18. data/book/text/extending/bookmarks_headers.glyph +13 -11
  19. data/book/text/extending/command.glyph +1 -1
  20. data/book/text/extending/commands_tasks.glyph +2 -2
  21. data/book/text/extending/internals.glyph +29 -29
  22. data/book/text/extending/interpreting.glyph +48 -9
  23. data/book/text/extending/macro_def.glyph +80 -32
  24. data/book/text/extending/output_format.glyph +2 -2
  25. data/book/text/extending/placeholders.glyph +27 -15
  26. data/book/text/extending/validators.glyph +1 -1
  27. data/book/text/getting_started/configuration.glyph +1 -1
  28. data/book/text/getting_started/create_project.glyph +4 -5
  29. data/book/text/getting_started/structure.glyph +2 -2
  30. data/book/text/macros/macros_core.glyph +341 -199
  31. data/book/text/macros/macros_filters.glyph +1 -1
  32. data/book/text/macros/macros_inline.glyph +3 -3
  33. data/book/text/macros/macros_structure.glyph +1 -1
  34. data/book/text/ref_commands.glyph +98 -93
  35. data/book/text/snippets.glyph +18 -0
  36. data/book/text/stats/bookmarks.glyph +16 -16
  37. data/book/text/stats/links.glyph +23 -23
  38. data/book/text/stats/macros.glyph +14 -14
  39. data/book/text/stats/snippets.glyph +26 -26
  40. data/book/text/stats/stats.glyph +14 -15
  41. data/book/text/text_editing/attribute_intro.glyph +8 -8
  42. data/book/text/text_editing/code.glyph +16 -16
  43. data/book/text/text_editing/conditionals.glyph +12 -13
  44. data/book/text/text_editing/esc_quot.glyph +3 -3
  45. data/book/text/text_editing/evaluation.glyph +57 -3
  46. data/book/text/text_editing/inclusions.glyph +34 -25
  47. data/book/text/text_editing/macro_composition.glyph +28 -0
  48. data/book/text/text_editing/macro_intro.glyph +4 -4
  49. data/book/text/text_editing/section_aliases.glyph +23 -23
  50. data/book/text/text_editing/sections.glyph +22 -22
  51. data/book/text/text_editing/stylesheets.glyph +33 -33
  52. data/book/text/text_editing/topics.glyph +6 -6
  53. data/book/text/text_editing/xml_fallback.glyph +1 -1
  54. data/book/text/troubleshooting/errors_command.glyph +4 -4
  55. data/book/text/troubleshooting/errors_generic.glyph +16 -10
  56. data/book/text/troubleshooting/errors_macro.glyph +18 -21
  57. data/book/text/troubleshooting/errors_parser.glyph +7 -7
  58. data/config.yml +51 -29
  59. data/document.glyph +18 -18
  60. data/glyph.gemspec +39 -287
  61. data/layouts/web5/topic.glyph +1 -1
  62. data/lib/glyph.rb +42 -10
  63. data/lib/glyph/analyzer.rb +5 -2
  64. data/lib/glyph/bookmark.rb +3 -1
  65. data/lib/glyph/commands.rb +11 -9
  66. data/lib/glyph/commands/add.rb +3 -1
  67. data/lib/glyph/commands/compile.rb +10 -7
  68. data/lib/glyph/commands/config.rb +7 -5
  69. data/lib/glyph/commands/init.rb +2 -0
  70. data/lib/glyph/commands/outline.rb +2 -0
  71. data/lib/glyph/commands/stats.rb +3 -0
  72. data/lib/glyph/commands/todo.rb +2 -0
  73. data/lib/glyph/config.rb +2 -0
  74. data/lib/glyph/document.rb +34 -9
  75. data/lib/glyph/interpreter.rb +2 -0
  76. data/lib/glyph/macro.rb +97 -25
  77. data/lib/glyph/macro_validators.rb +9 -6
  78. data/lib/glyph/node.rb +3 -1
  79. data/lib/glyph/parser.rb +68 -33
  80. data/lib/glyph/reporter.rb +3 -1
  81. data/lib/glyph/syntax_node.rb +23 -28
  82. data/lib/glyph/system_extensions.rb +21 -0
  83. data/lib/glyph/utils.rb +24 -4
  84. data/macros/block.rb +111 -0
  85. data/macros/core.rb +163 -33
  86. data/macros/filters.rb +3 -2
  87. data/macros/inline.rb +97 -0
  88. data/macros/reps/html.rb +180 -0
  89. data/macros/reps/html5.rb +100 -0
  90. data/macros/reps/web.rb +4 -0
  91. data/macros/reps/web5.rb +4 -0
  92. data/macros/structure.rb +229 -0
  93. data/macros/xml.rb +29 -24
  94. data/spec/config.yml +7 -0
  95. data/spec/files/article.glyph +2 -2
  96. data/spec/files/web_doc.glyph +9 -5
  97. data/spec/lib/commands_spec.rb +13 -9
  98. data/spec/lib/config_spec.rb +2 -2
  99. data/spec/lib/document_spec.rb +10 -0
  100. data/spec/lib/glyph_spec.rb +30 -8
  101. data/spec/lib/macro_spec.rb +39 -4
  102. data/spec/lib/macro_validators_spec.rb +3 -4
  103. data/spec/lib/parser_spec.rb +7 -0
  104. data/spec/lib/reporter_spec.rb +1 -0
  105. data/spec/lib/syntax_node_spec.rb +40 -6
  106. data/spec/macros/core_spec.rb +154 -21
  107. data/spec/macros/filters_spec.rb +1 -1
  108. data/spec/macros/html5_spec.rb +4 -5
  109. data/spec/macros/macros_spec.rb +6 -7
  110. data/spec/macros/web5_spec.rb +3 -3
  111. data/spec/macros/web_spec.rb +10 -7
  112. data/spec/macros/xml_spec.rb +11 -2
  113. data/spec/spec_helper.rb +11 -5
  114. data/spec/tasks/generate_spec.rb +40 -5
  115. data/spec/tasks/load_spec.rb +1 -13
  116. data/styles/coderay.css +147 -38
  117. data/styles/default.css +19 -22
  118. data/styles/pagination.css +30 -30
  119. data/tasks/generate.rake +54 -18
  120. data/tasks/load.rake +9 -24
  121. data/tasks/project.rake +0 -2
  122. metadata +208 -207
  123. data/.gitignore +0 -7
  124. data/VERSION +0 -1
  125. data/book/snippets.yml +0 -18
  126. data/lib/glyph/macro_helpers.rb +0 -282
  127. data/macros/html/block.rb +0 -124
  128. data/macros/html/inline.rb +0 -42
  129. data/macros/html/structure.rb +0 -191
  130. data/macros/html5/block.rb +0 -69
  131. data/macros/html5/inline.rb +0 -24
  132. data/macros/html5/structure.rb +0 -140
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  module Glyph
2
4
 
3
5
  # A Glyph::Interpreter object perform the following actions:
data/lib/glyph/macro.rb CHANGED
@@ -8,14 +8,14 @@ module Glyph
8
8
  class Macro
9
9
 
10
10
  include Validators
11
- include Helpers
12
11
  include Utils
13
12
 
14
- attr_reader :node, :source_name, :source_file, :source_topic
13
+ attr_reader :node, :source_name, :source_file, :source_topic, :name
15
14
 
16
15
  # Creates a new macro instance from a Node
17
16
  # @param [Node] node a node populated with macro data
18
17
  def initialize(node)
18
+ @data = {}
19
19
  @node = node
20
20
  @name = @node[:name]
21
21
  @updated_source = nil
@@ -34,7 +34,7 @@ module Glyph
34
34
  file ||= @node[:source][:file] rescue nil
35
35
  @updated_source = {:name => name, :file => file, :topic => topic}
36
36
  end
37
-
37
+
38
38
  # Returns a Glyph code representation of the specified parameter
39
39
  # @param [Fixnum] n the index of the parameter
40
40
  # @return [String, nil] the string representation of the parameter
@@ -109,8 +109,8 @@ module Glyph
109
109
  @parameters = []
110
110
  @node.parameters.each do |value|
111
111
  @parameters << value.evaluate(@node, :params => true)
112
- @parameters.last.strip! if options[:strip]
113
- @parameters.last = nil if @parameters.last.blank? && options[:null_if_blank]
112
+ @parameters[@parameters.length-1].strip! if options[:strip]
113
+ @parameters[@parameters.length-1] = nil if @parameters.last.blank? && options[:null_if_blank]
114
114
  end
115
115
  @parameters
116
116
  end
@@ -163,7 +163,7 @@ module Glyph
163
163
  end
164
164
  macros.reverse.compact.join('/')
165
165
  end
166
-
166
+
167
167
  # Returns a todo message to include in the document in case of errors.
168
168
  # @param [String] message the message to include in the document
169
169
  # @return [String] the resulting todo message
@@ -209,23 +209,10 @@ module Glyph
209
209
  # @param [String] string the string to interpret
210
210
  # @return [String] the interpreted output
211
211
  def interpret(string)
212
- if @node[:escape] then
213
- result = string
214
- else
215
- context = {}
216
- context[:source] = @updated_source || @node[:source]
217
- context[:embedded] = true
218
- context[:document] = @node[:document]
219
- interpreter = Glyph::Interpreter.new string, context
220
- subtree = interpreter.parse
221
- subtree[:source] = context[:source]
222
- @node << subtree
223
- result = interpreter.document.output
224
- end
225
- result
212
+ @node[:escape] ? string : inject(string).document.output
226
213
  end
227
214
 
228
- # @see Glyph::Document#placeholder
215
+ # @see Glyph::Document#placeholder
229
216
  def placeholder(&block)
230
217
  @node[:document].placeholder &block
231
218
  end
@@ -250,14 +237,99 @@ module Glyph
250
237
  @node[:document].header hash
251
238
  end
252
239
 
240
+ # @see Glyph::Document#snippet
241
+ def snippet(key, value)
242
+ @node[:document].snippet key, value
243
+ end
244
+
245
+ # @see Glyph::Document#snippet?
246
+ def snippet?(ident)
247
+ @node[:document].snippet? ident
248
+ end
249
+
250
+ # @since 0.5.0
251
+ # Renders a macro representation
252
+ # @param [Symbol, String] rep the representation to render
253
+ # @param [Hash] data the data to pass to the representation
254
+ def render(rep=nil, data=nil)
255
+ rep ||= @name
256
+ data ||= @data
257
+ block = Glyph::REPS[rep.to_sym]
258
+ macro_error "No macro representation for '#{rep}'", e unless block
259
+ instance_exec(data, &block).to_s
260
+ end
261
+
262
+ # Stores a block of code to be "dispatched" via macro composition
263
+ # @since 0.5.0
264
+ # @example
265
+ # # Macro definition (Ruby)
266
+ # macro :greet do
267
+ # dispatch do |node|
268
+ # "#{node[:name]}, #{node.param 0}!"
269
+ # end
270
+ # end
271
+ #
272
+ # # macro usage (Glyph)
273
+ # greet/Hello[World] --[Outputs: Hello, World!]
274
+ # greet/GoodBye[John] --[Outputs: Goodbye, John!]
275
+ def dispatch(&block)
276
+ @node[:dispatch] = block
277
+ value
278
+ end
279
+
280
+ # Performs parameter/attribute substitution and interprets text
281
+ # @since 0.5.0
282
+ # @param [String] text the text to interpret
283
+ # @return [String] the interpreted output
284
+ def apply(text)
285
+ body = text.dup
286
+ # Parameters
287
+ body.gsub!(/\{\{(\d+)\}\}/) do
288
+ raw_param($1.to_i).to_s.strip
289
+ end
290
+ # Attributes
291
+ body.gsub!(/\{\{([^\[\]\|\\\s]+)\}\}/) do
292
+ raw_attr($1.to_sym).to_s.strip
293
+ end
294
+ interpret body
295
+ end
296
+
297
+ # Parses text and injects the syntax tree into the current node
298
+ # @since 0.5.0
299
+ # @param [String] text the text to parse
300
+ # @return [Glyph::Interpreter] the interpreter instance used to parse text
301
+ def inject(text)
302
+ context = create_context
303
+ interpreter = Glyph::Interpreter.new text, context
304
+ subtree = interpreter.parse
305
+ subtree[:source] = context[:source]
306
+ @node << subtree
307
+ interpreter
308
+ end
309
+
310
+ # Parses text
311
+ # @since 0.5.0
312
+ # @param [String] text the text to parse
313
+ # @return [Glyph::Node] the syntax tree generated by parsing
314
+ def parse(text)
315
+ Glyph::Interpreter.new(text, create_context).parse
316
+ end
317
+
253
318
  # Executes a macro definition in the context of self
254
319
  def expand
255
320
  block = Glyph::MACROS[@name]
256
- macro_error "Undefined macro '#@name'}" unless block
257
- res = instance_exec(@node, &block).to_s
258
- res.gsub!(/\\?([\[\]\|])/){"\\#$1"}
259
- res
321
+ macro_error "Undefined macro '#@name'" unless block
322
+ instance_exec(@node, &block).to_s.gsub(/\\?([\[\]\|])/){"\\#$1"}
260
323
  end
261
324
 
325
+ private
326
+
327
+ def create_context
328
+ context = {}
329
+ context[:source] = @updated_source || @node[:source]
330
+ context[:embedded] = true
331
+ context[:document] = @node[:document]
332
+ context
333
+ end
262
334
  end
263
335
  end
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  module Glyph
2
4
  class Macro
3
5
 
@@ -20,12 +22,13 @@ module Glyph
20
22
  result
21
23
  end
22
24
 
23
- # Ensures that the macro element attributes is a valid XML element name.
25
+ # Ensures that the provided name is a valid XML element name.
26
+ # @param [String, Symbol] name the element name to validate
24
27
  # @param [Hash] options a hash containing validation options (for now the only option is :level)
25
28
  # @return [Boolean] whether the validation passed or not
26
29
  # @since 0.3.0
27
- def valid_xml_element(options={:level => :error})
28
- validate("Invalid XML element '#{@node[:element]}'", options) { @node[:element].to_s.match(/^([^[:punct:]0-9<>]|_)[^<>"']*/) }
30
+ def valid_xml_element(name, options={:level => :error})
31
+ validate("Invalid XML element '#{name}'", options) { name.to_s.match(/^([^[:punct:]0-9<>]|_)[^<>"']*/) }
29
32
  end
30
33
 
31
34
  # Ensures that a macro attribute name is a valid XML attribute name.
@@ -118,7 +121,7 @@ module Glyph
118
121
  macro_error "Macro '#@name' cannot be used in safe mode" if Glyph.safe?
119
122
  end
120
123
 
121
- # Ensure that no mutual inclusion occurs within the specified parameter or attribute
124
+ # Ensures that no mutual inclusion occurs within the specified parameter or attribute
122
125
  # @param [Fixnum, Symbol] the parameter index or attribute name to check
123
126
  # @raise [Glyph::MacroError] mutual inclusion was detected
124
127
  # @since 0.3.0
@@ -146,7 +149,7 @@ module Glyph
146
149
  end
147
150
  end
148
151
 
149
- # Ensure that the macros is within another
152
+ # Ensures that the macros is within another
150
153
  # @param [String, Symbol] arg the name of the container macro
151
154
  # @param [Hash] options a hash containing validation options
152
155
  # @option options :level the error level (:error, :warning)
@@ -158,7 +161,7 @@ module Glyph
158
161
  end
159
162
  end
160
163
 
161
- # Ensure that the macros is _not_ within another
164
+ # Ensures that the macros is _not_ within another
162
165
  # @param [String, Symbol] arg the name of the container macro
163
166
  # @param [Hash] options a hash containing validation options
164
167
  # @option options :level the error level (:error, :warning)
data/lib/glyph/node.rb CHANGED
@@ -1,3 +1,6 @@
1
+ # encoding: utf-8
2
+
3
+ # Core Hash class.
1
4
  class Hash
2
5
 
3
6
  # Converts self to a Node
@@ -85,7 +88,6 @@ class Node < Hash
85
88
 
86
89
  # Iterates through children recursively (including self)
87
90
  # @param [Node, nil] element the node to process
88
- # @yieldparam [Integer] level the initial tree depth
89
91
  # @yieldparam [Node] element the current node
90
92
  # @yieldparam [Integer] level the current tree depth
91
93
  def descend(element=nil, level=0, &block)
data/lib/glyph/parser.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+
2
3
  require 'strscan'
3
4
 
4
5
  module Glyph
@@ -13,6 +14,7 @@ module Glyph
13
14
  # @param [String] source_name the name of the source file (stored in the root node)
14
15
  # @since 0.3.0
15
16
  def initialize(text, source_name="--")
17
+ @ruby19 = RUBY_VERSION >= '1.9' ? true : false
16
18
  @source_name = source_name || "--"
17
19
  @input = StringScanner.new text
18
20
  @output = create_node DocumentNode, :name => @source_name.to_sym
@@ -29,8 +31,8 @@ module Glyph
29
31
  @output << result
30
32
  count +=1
31
33
  end
32
- if @input.pos < @input.string.length then
33
- current_char = @input.string[@input.pos].chr
34
+ if @input.pos < @input.string.bytesize then
35
+ current_char = @input.string[@input.pos].chr rescue nil
34
36
  illegal_delimiter = current_char.match(/\]|\[/) rescue nil
35
37
  error "Macro delimiter '#{current_char}' not escaped" if illegal_delimiter
36
38
  end
@@ -58,18 +60,15 @@ module Glyph
58
60
  name = @input.matched
59
61
  name.chop!
60
62
  name.chop!
61
- error "#{name}[...] - A macro cannot start with '@' or a digit." if name.match(/^[0-1@]/)
62
- node = create_node(MacroNode, {
63
- :name => name.to_sym,
64
- :escape => true,
65
- :attributes => [],
66
- :parameters => []
67
- })
68
- while contents = parse_escaped_contents(node) do
69
- node << contents unless contents.is_a?(AttributeNode)
63
+ error "#{name}[=...=] - A macro cannot start with a digit or contain '@'" if (name.match(/^[0-1]/) || name.match(/@/)) && !name.match(/^@:?$/)
64
+ node = macro_node_for name, true
65
+ leaf = node
66
+ node.descend { |n, level| leaf = n }
67
+ while contents = parse_escaped_contents(leaf) do
68
+ leaf << contents unless contents.is_a?(AttributeNode)
70
69
  end
71
70
  @input.scan(/\=\]/) or error "Escaping macro '#{name}' not closed"
72
- organize_children_for node
71
+ organize_children_for leaf
73
72
  node
74
73
  else
75
74
  nil
@@ -77,9 +76,9 @@ module Glyph
77
76
  end
78
77
 
79
78
  def escaping_attribute(current)
80
- if @input.scan(/@[^\[\]\|\\\s]+\[\=/) then
79
+ if @input.scan(/@[^:\[\]\|\\\s]+\[\=/) then
81
80
  error "Attributes cannot be nested" if @current_attribute
82
- name = @input.matched[1..@input.matched.length-3]
81
+ name = @input.matched[1..@input.matched.bytesize-3]
83
82
  node = create_node(AttributeNode, {
84
83
  :escape => true,
85
84
  :name => name.to_sym
@@ -99,18 +98,15 @@ module Glyph
99
98
  if @input.scan(/[^\[\]\|\\\s]+\[/) then
100
99
  name = @input.matched
101
100
  name.chop!
102
- error "#{name}[...] - A macro cannot start with '@' or a digit." if name.match(/^[0-1@]/)
103
- node = create_node(MacroNode, {
104
- :escape => false,
105
- :name => name.to_sym,
106
- :attributes => [],
107
- :parameters => []
108
- })
109
- while contents = parse_contents(node) do
110
- node << contents unless contents.is_a?(AttributeNode)
101
+ error "#{name}[...] - A macro cannot start with a digit or contain '@'" if (name.match(/^[0-1]/) || name.match(/@/)) && !name.match(/^@:?$/)
102
+ node = macro_node_for name
103
+ leaf = node
104
+ node.descend { |n, level| leaf = n }
105
+ while contents = parse_contents(leaf) do
106
+ leaf << contents unless contents.is_a?(AttributeNode)
111
107
  end
112
108
  @input.scan(/\]/) or error "Macro '#{name}' not closed"
113
- organize_children_for node
109
+ organize_children_for leaf
114
110
  node
115
111
  else
116
112
  nil
@@ -118,9 +114,9 @@ module Glyph
118
114
  end
119
115
 
120
116
  def attribute(current)
121
- if @input.scan(/@[^\[\]\|\\\s]+\[/) then
117
+ if @input.scan(/@[^:\[\]\|\\\s]+\[/) then
122
118
  error "Attributes cannot be nested" if current.is_a?(AttributeNode)
123
- name = @input.matched[1..@input.matched.length-2]
119
+ name = @input.matched[1..@input.matched.bytesize-2]
124
120
  node = create_node(AttributeNode, {
125
121
  :escape => false,
126
122
  :name => name.to_sym
@@ -139,12 +135,12 @@ module Glyph
139
135
  def text(current)
140
136
  start_p = @input.pos
141
137
  res = @input.scan_until /(\\.)|(\A(\]|\|)|[^\\](\]|\|)|[^\[\]\|\\\s]+\[|\Z)/
142
- offset = @input.matched.match(/^[^\\](\]|\|)$/) ? 1 : @input.matched.length
138
+ offset = @input.matched.match(/^[^\\](\]|\|)$/) ? 1 : @input.matched.bytesize
143
139
  @input.pos = @input.pos - offset rescue @input.pos
144
140
  return nil if @input.pos == start_p
145
- match = @input.string[start_p..@input.pos-1]
141
+ match = extract_string(start_p..@input.pos-1)
146
142
  illegal_macro_delimiter? start_p, match
147
- if match.length > 0 then
143
+ if match.bytesize > 0 then
148
144
  create_node TextNode, :value => match
149
145
  else
150
146
  nil
@@ -160,16 +156,16 @@ module Glyph
160
156
  when @input.matched.match(/^[^\\]\|$/) then
161
157
  offset = 1
162
158
  else
163
- offset = @input.matched.length
159
+ offset = @input.matched.bytesize
164
160
  end
165
161
  @input.pos = @input.pos - offset rescue @input.pos
166
162
  return nil if @input.pos == start_p
167
- match = @input.string[start_p..@input.pos-1]
163
+ match = extract_string(start_p..@input.pos-1)
168
164
  illegal_nesting = match.match(/([^\[\]\|\\\s]+)\[\=/)[1] rescue nil
169
165
  if illegal_nesting then
170
166
  error "Cannot nest escaping macro '#{illegal_nesting}' within escaping macro '#{current[:name]}'"
171
167
  end
172
- if match.length > 0 then
168
+ if match.bytesize > 0 then
173
169
  create_node TextNode, :value => match, :escaped => true
174
170
  else
175
171
  nil
@@ -197,6 +193,45 @@ module Glyph
197
193
 
198
194
  private
199
195
 
196
+ def macro_node_for(ident, escape=false)
197
+ macro_names = ident.split(/\//).select{|e| !e.blank?}
198
+ nest_node = lambda do |parent, count|
199
+ node = create_node(MacroNode, {
200
+ :escape => false,
201
+ :name => macro_names[count].to_sym
202
+ })
203
+ parent ? (parent&0) << node : parent = node
204
+ if macro_names[count+1] then
205
+ node << create_node(ParameterNode, :name => :"0")
206
+ nest_node.call(node, count+1)
207
+ else
208
+ node[:parameters] = []
209
+ node[:attributes] = []
210
+ node[:escape] = escape
211
+ end
212
+ node
213
+ end
214
+ nest_node.call(nil, 0)
215
+ end
216
+
217
+ # Thanks Thomas Leitner
218
+ # http://redmine.ruby-lang.org/issues/show/2645
219
+ def extract_string(range)
220
+ result = nil
221
+ if @ruby19 then
222
+ begin
223
+ enc = @input.string.encoding
224
+ @input.string.force_encoding('ASCII-8BIT')
225
+ result = @input.string[range].force_encoding(enc)
226
+ ensure
227
+ @input.string.force_encoding(enc)
228
+ end
229
+ else
230
+ result = @input.string[range]
231
+ end
232
+ result
233
+ end
234
+
200
235
  def aggregate_parameters_for(node)
201
236
  indices = []
202
237
  count = 0
@@ -216,7 +251,7 @@ module Glyph
216
251
  total_parameters = 0
217
252
  save_parameter = lambda do |max_index|
218
253
  parameter = create_node ParameterNode, :name => "#{total_parameters}".to_sym
219
- total_parameters +=1
254
+ total_parameters +=1
220
255
  current_index.upto(max_index) do |index|
221
256
  parameter << (node & index)
222
257
  end
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  module Glyph
2
4
 
3
5
  # This class is used to display statistics collected by a Glyph::Analyzer.
@@ -92,7 +94,7 @@ module Glyph
92
94
  section "Snippet '#{s[:param]}'"
93
95
  info "Definition:"
94
96
  puts "-------------------"
95
- puts SNIPPETS[s[:param]]
97
+ puts @stats[:snippets][:values][s[:param]]
96
98
  puts "-------------------"
97
99
  total :used_instances, s[:stats][:total]
98
100
  occurrences s[:stats][:files], "Usage Details:" if @detailed
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  module Glyph
2
4
 
3
5
  # A subclass of Glyph::SyntaxNode is instantiated by Glyph::Parser whenever a known
@@ -59,7 +61,13 @@ module Glyph
59
61
  # @since 0.3.0
60
62
  def to_s
61
63
  e = self[:escape] ? "=" : ""
62
- "#{self[:name]}["+e+attributes.join+parameters.join("|")+e+"]"
64
+ "#{self[:name]}["+e+contents+e+"]"
65
+ end
66
+
67
+ # @return [String] a textual representation of the macro parameters.
68
+ # @since 0.3.0
69
+ def contents
70
+ attributes.join+parameters.join("|")
63
71
  end
64
72
 
65
73
  # Expands the macro
@@ -123,11 +131,18 @@ module Glyph
123
131
  # @return [String] the value of the macro
124
132
  # @since 0.3.0
125
133
  def expand(context)
126
- xml_element(context)
127
134
  self[:source] = context[:source]
128
135
  self[:document] = context[:document]
129
136
  self[:info] = context[:info]
130
137
  self[:value] = ""
138
+ dispatched = parent_macro.dispatch(self) if parent_macro
139
+ return dispatched if dispatched
140
+ if Glyph['options.macro_set'] == "xml" || Glyph::MACROS[self[:name]].blank? && Glyph['options.xml_fallback'] then
141
+ m = Glyph::MacroNode.new
142
+ m[:name] = :xml
143
+ Glyph::Macro.new(m).expand
144
+ return m[:dispatch].call self
145
+ end
131
146
  Glyph::Macro.new(self).expand
132
147
  end
133
148
 
@@ -139,33 +154,13 @@ module Glyph
139
154
  s
140
155
  end
141
156
 
142
- protected
143
-
144
- def xml_element(context)
145
- known_macro = Glyph::MACROS.include? self[:name]
146
- name = self[:name].to_s
147
- if !known_macro && name.match(/^=(.+)/) then
148
- # Force tag name override if macro starts with a '='
149
- name.gsub! /^=(.+)/, '\1'
150
- end
151
- case
152
- # Use XML syntax
153
- when Glyph['options.macro_set'] == 'xml' then
154
- self[:element] = name
155
- self[:name] = :"|xml|"
156
- # Fallback to XML syntax
157
- when Glyph["options.xml_fallback"] then
158
- unless known_macro then
159
- self[:element] = name
160
- self[:fallback] = true
161
- self[:name] = :"|xml|"
162
- end
163
- else
164
- # Unknown macro
165
- raise RuntimeError, "Undefined macro '#{name}'\n -> source: #{context[:source][:name]}" unless known_macro
166
- end
157
+ # @since 0.5.0
158
+ # Calls the ruby block stored in the :dispatch key, if present,
159
+ # in the context of node
160
+ def dispatch(node)
161
+ return self[:dispatch].call node if self[:dispatch]
162
+ false
167
163
  end
168
-
169
164
  end
170
165
 
171
166
  # A piece of text in Glyph Abstract Syntax Tree