glyph 0.2.0 → 0.3.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.
Files changed (114) hide show
  1. data/AUTHORS.textile +1 -1
  2. data/CHANGELOG.textile +119 -222
  3. data/LICENSE.textile +1 -1
  4. data/README.textile +42 -23
  5. data/Rakefile +1 -3
  6. data/VERSION +1 -1
  7. data/benchmark.rb +72 -0
  8. data/book/config.yml +4 -4
  9. data/book/document.glyph +90 -57
  10. data/book/images/document_generation.png +0 -0
  11. data/book/lib/macros/reference.rb +75 -22
  12. data/book/output/html/glyph.html +3183 -2121
  13. data/book/output/html/images/document_generation.png +0 -0
  14. data/book/output/pdf/glyph.pdf +7370 -4913
  15. data/book/resources/document_generation.txt +34 -0
  16. data/book/snippets.yml +6 -0
  17. data/book/text/changelog.glyph +45 -34
  18. data/book/text/compiling/compiling.glyph +23 -0
  19. data/book/text/compiling/lite_mode.glyph +23 -0
  20. data/book/text/compiling/programmatic_usage.glyph +77 -0
  21. data/book/text/extending/bookmarks_headers.glyph +21 -0
  22. data/book/text/extending/further_reading.glyph +13 -0
  23. data/book/text/extending/internals.glyph +79 -0
  24. data/book/text/extending/interpreting.glyph +51 -0
  25. data/book/text/extending/macro_def.glyph +64 -0
  26. data/book/text/extending/params_attrs.glyph +70 -0
  27. data/book/text/extending/placeholders.glyph +34 -0
  28. data/book/text/extending/validators.glyph +16 -0
  29. data/book/text/getting_started/configuration.glyph +49 -0
  30. data/book/text/getting_started/create_project.glyph +41 -0
  31. data/book/text/getting_started/structure.glyph +55 -0
  32. data/book/text/introduction.glyph +49 -26
  33. data/book/text/license.glyph +1 -1
  34. data/book/text/macros/macros_block.glyph +99 -0
  35. data/book/text/macros/macros_core.glyph +208 -0
  36. data/book/text/macros/macros_filters.glyph +40 -0
  37. data/book/text/macros/macros_inline.glyph +50 -0
  38. data/book/text/macros/macros_structure.glyph +100 -0
  39. data/book/text/ref_commands.glyph +94 -73
  40. data/book/text/ref_config.glyph +34 -42
  41. data/book/text/ref_macros.glyph +1 -373
  42. data/book/text/text_editing/code.glyph +51 -0
  43. data/book/text/text_editing/conditionals.glyph +49 -0
  44. data/book/text/text_editing/evaluation.glyph +13 -0
  45. data/book/text/text_editing/glyph_files.glyph +7 -0
  46. data/book/text/text_editing/images.glyph +29 -0
  47. data/book/text/text_editing/inclusions.glyph +44 -0
  48. data/book/text/text_editing/links.glyph +53 -0
  49. data/book/text/text_editing/macro_intro.glyph +111 -0
  50. data/book/text/text_editing/raw_html.glyph +112 -0
  51. data/book/text/text_editing/sections.glyph +63 -0
  52. data/book/text/text_editing/stylesheets.glyph +36 -0
  53. data/book/text/troubleshooting/errors_command.glyph +39 -0
  54. data/book/text/troubleshooting/errors_generic.glyph +29 -0
  55. data/book/text/troubleshooting/errors_intro.glyph +3 -0
  56. data/book/text/troubleshooting/errors_macro.glyph +98 -0
  57. data/book/text/troubleshooting/errors_parser.glyph +29 -0
  58. data/config.yml +77 -58
  59. data/document.glyph +25 -25
  60. data/glyph.gemspec +57 -22
  61. data/lib/glyph.rb +54 -13
  62. data/lib/glyph/commands.rb +84 -17
  63. data/lib/glyph/config.rb +3 -3
  64. data/lib/glyph/document.rb +14 -8
  65. data/lib/glyph/interpreter.rb +18 -58
  66. data/lib/glyph/macro.rb +160 -55
  67. data/lib/glyph/macro_validators.rb +104 -12
  68. data/lib/glyph/node.rb +24 -0
  69. data/lib/glyph/parser.rb +278 -0
  70. data/lib/glyph/syntax_node.rb +225 -0
  71. data/macros/core.rb +212 -0
  72. data/macros/filters.rb +66 -15
  73. data/macros/html/block.rb +43 -105
  74. data/macros/html/inline.rb +11 -12
  75. data/macros/html/structure.rb +123 -58
  76. data/macros/xml.rb +33 -0
  77. data/spec/files/container.textile +2 -2
  78. data/spec/files/document.glyph +2 -2
  79. data/spec/files/document_with_toc.glyph +3 -3
  80. data/spec/files/included.textile +1 -1
  81. data/spec/files/ligature.jpg +0 -0
  82. data/spec/files/markdown.markdown +2 -1
  83. data/spec/lib/commands_spec.rb +46 -3
  84. data/spec/lib/document_spec.rb +4 -4
  85. data/spec/lib/glyph_spec.rb +17 -46
  86. data/spec/lib/interpreter_spec.rb +6 -25
  87. data/spec/lib/macro_spec.rb +141 -43
  88. data/spec/lib/macro_validators_spec.rb +27 -5
  89. data/spec/lib/node_spec.rb +26 -1
  90. data/spec/lib/parser_spec.rb +246 -0
  91. data/spec/lib/syntax_node_spec.rb +111 -0
  92. data/spec/macros/core_spec.rb +195 -0
  93. data/spec/macros/filters_spec.rb +38 -4
  94. data/spec/macros/macros_spec.rb +20 -176
  95. data/spec/macros/textile_spec.rb +13 -71
  96. data/spec/macros/xml_spec.rb +77 -0
  97. data/spec/spec_helper.rb +50 -10
  98. data/spec/tasks/load_spec.rb +13 -2
  99. data/styles/default.css +18 -6
  100. data/styles/pagination.css +1 -19
  101. data/tasks/generate.rake +2 -2
  102. data/tasks/load.rake +27 -17
  103. data/tasks/project.rake +1 -1
  104. metadata +75 -62
  105. data/book/script/compile.rb +0 -8
  106. data/book/script/prof +0 -1
  107. data/book/script/prof_results.htm +0 -21079
  108. data/book/text/authoring.glyph +0 -548
  109. data/book/text/extending.glyph +0 -224
  110. data/book/text/getting_started.glyph +0 -158
  111. data/book/text/troubleshooting.glyph +0 -179
  112. data/lib/glyph/glyph_language.rb +0 -538
  113. data/lib/glyph/glyph_language.treetop +0 -27
  114. data/macros/common.rb +0 -160
data/lib/glyph/config.rb CHANGED
@@ -77,9 +77,9 @@ module Glyph
77
77
  # @raise [ArgumentError] if the setting refers to an invalid namespace
78
78
  # @example
79
79
  # cfg = Glyph::Config.new
80
- # cfg.set "quiet", true # Sets :quiet => true
80
+ # cfg.set "system.quiet", true # Sets "system.quiet" => true
81
81
  # cfg.set "test.test_value", "[1,2,3]" # Sets :test => {:test_value => [1,2,3]}
82
- # cfg.sef :quiet, "false" # Sets :quiet => false
82
+ # cfg.set "system.quiet", "false" # Sets "system.quiet" => false
83
83
  def set(setting, value)
84
84
  raise RuntimeError, "Configuration cannot be changed" unless @options[:mutable]
85
85
  if value.is_a?(String) && value.match(/^(["'].*["']|:.+|\[.*\]|\{.*\}|true|false|nil)$/) then
@@ -122,7 +122,7 @@ module Glyph
122
122
  # @see Glyph::Config#set
123
123
  # @example
124
124
  # cfg = Glyph::Config.new
125
- # cfg.get :quiet # true
125
+ # cfg.get "system.quiet" # true
126
126
  # cfg.get "test.test_value" # [1,2,3]
127
127
  def get(setting)
128
128
  @data.instance_eval "self#{setting.to_s.split(".").map{|key| "[:#{key}]" }.join}" rescue nil
@@ -6,7 +6,7 @@ module Glyph
6
6
  # currently being interpreted.
7
7
  #
8
8
  # It is responsible of analyzing (evaluating) the syntax tree and return the corresponding output
9
- # as well as evaluating placeholders.
9
+ # as well as replacing placeholders.
10
10
  class Document
11
11
 
12
12
  ESCAPES = [
@@ -40,7 +40,7 @@ module Glyph
40
40
  # @raise [RuntimeError] unless the document has been analized
41
41
  def structure
42
42
  raise RuntimeError, "Document has not been analyzed" unless analyzed? || finalized?
43
- @tree.data
43
+ @tree
44
44
  end
45
45
 
46
46
  # Copies bookmarks, headers, todos and placeholders from another Glyph::Document
@@ -99,7 +99,7 @@ module Glyph
99
99
  def analyze
100
100
  raise RuntimeError, "Document is #{@state}" if analyzed? || finalized?
101
101
  @context[:document] = self
102
- @output = @tree.evaluate @context, nil
102
+ @output = @tree.evaluate @context
103
103
  @state = :analyzed
104
104
  end
105
105
 
@@ -114,12 +114,18 @@ module Glyph
114
114
  raise RuntimeError, "Document cannot be finalized due to previous errors" unless @context[:document].errors.blank?
115
115
  # Substitute placeholders
116
116
  ESCAPES.each{|e| @output.gsub! e[0], e[1]}
117
- @placeholders.each_pair do |key, value|
118
- begin
119
- @output.gsub! key.to_s, value.call(self).to_s
120
- rescue Exception => e
121
- Glyph.warning e.message
117
+ begin
118
+ @placeholders.each_pair do |key, value|
119
+ begin
120
+ @output.gsub! key.to_s, value.call(self).to_s
121
+ rescue Glyph::MacroError => e
122
+ e.macro.macro_warning e.message, e
123
+ rescue Exception => e
124
+ puts e.class
125
+ Glyph.warning e.message
126
+ end
122
127
  end
128
+ rescue Exception => e
123
129
  end
124
130
  @state = :finalized
125
131
  end
@@ -1,74 +1,24 @@
1
- # @private
2
- class GlyphSyntaxNode < Treetop::Runtime::SyntaxNode
3
-
4
- attr_reader :data
5
-
6
- def evaluate(context, current=nil)
7
- current ||= context.to_node
8
- @data ||= current.to_node
9
- elements.map { |e| e.evaluate(context, current) if e.respond_to? :evaluate }.join
10
- end
11
-
12
- end
13
-
14
- # @private
15
- class MacroNode < GlyphSyntaxNode
16
-
17
- def evaluate(context, current)
18
- name = macro_name.text_value.to_sym
19
- raise Glyph::SyntaxError, "Undefined macro '#{name}'\n -> source: #{current[:source]}" unless Glyph::MACROS.include? name
20
- @data = {:macro => name, :source => context[:source], :document => context[:document]}.to_node
21
- @data[:escape] = true if is_a? EscapingMacroNode
22
- current << @data
23
- @data[:value] = super(context, @data).strip
24
- Glyph::Macro.new(@data).execute
25
- end
26
-
27
- end
28
-
29
- # @private
30
- class EscapingMacroNode < MacroNode; end
31
-
32
- # @private
33
- class TextNode < GlyphSyntaxNode
34
-
35
- def evaluate(context, current=nil)
36
- text_value
37
- end
38
-
39
- end
40
-
41
-
42
1
  module Glyph
43
-
2
+
44
3
  # A Glyph::Interpreter object perform the following actions:
45
4
  # * Parses a string of text containing Glyph macros
46
5
  # * Creates a document based on the parsed syntax tree
47
6
  # * Analyzes and finalizes the document
48
7
  class Interpreter
49
8
 
50
- PARSER = GlyphLanguageParser.new
51
-
52
9
  # Creates a new Glyph::Interpreter object.
53
10
  # @param [String] text the string to interpret
54
- # @param [Hash] context the context to pass along when evaluating macros
55
- def initialize(text, context=nil)
56
- context ||= {:source => '--'}
57
- @raw = PARSER.parse text
11
+ # @param [Hash] context the context to pass along when expanding macros
12
+ def initialize(text, context={})
58
13
  @context = context
59
- tf = PARSER.terminal_failures
60
- if !@raw.respond_to?(:evaluate) then
61
- reason = "Incorrect macro syntax"
62
- err = "#{reason}\n -> #{@context[:source]} [Line #{PARSER.failure_line}, Column #{PARSER.failure_column}]"
63
- @context[:document].errors << err if @context[:document] && !@context[:embedded]
64
- raise Glyph::SyntaxError, err
65
- end
66
- @document = Glyph::Document.new @raw, @context
67
- @document.inherit_from @context[:document] if @context[:document]
14
+ @context[:source] ||= {:name => "--"}
15
+ @text = text
16
+ @parser = Glyph::Parser.new text, @context[:source][:name]
68
17
  end
69
18
 
70
19
  # @see Glyph::Document#analyze
71
20
  def process
21
+ parse unless @tree
72
22
  @document.analyze
73
23
  end
74
24
 
@@ -80,15 +30,25 @@ module Glyph
80
30
  # Returns the finalized @document (calls self#process and self#postprocess if necessary)
81
31
  # @return [Glyph::Document] the finalized document
82
32
  def document
33
+ parse unless @tree
83
34
  return @document if @document.finalized?
84
35
  process if @document.new?
85
36
  postprocess if @document.analyzed?
86
37
  @document
87
38
  end
88
39
 
40
+ # Parses the string provided during initialization
41
+ # @return [Glyph::SyntaxNode] the Abstract Syntax Tree generated from the string
42
+ # @since 0.3.0
43
+ def parse
44
+ Glyph.info "Parsing: #{@context[:source][:name]}" if Glyph.debug? && @context[:info] && @context[:source][:name]
45
+ @tree = @parser.parse
46
+ @document = Glyph::Document.new @tree, @context
47
+ @document.inherit_from @context[:document] if @context[:document]
48
+ @tree
49
+ end
89
50
 
90
51
  end
91
-
92
52
  end
93
53
 
94
54
 
data/lib/glyph/macro.rb CHANGED
@@ -9,41 +9,153 @@ module Glyph
9
9
 
10
10
  include Validators
11
11
 
12
+ attr_reader :node, :source
13
+
12
14
  # Creates a new macro instance from a Node
13
15
  # @param [Node] node a node populated with macro data
14
16
  def initialize(node)
15
17
  @node = node
16
- @name = @node[:macro]
17
- @value = @node[:value]
18
- @source = @node[:source]
19
- @escaped_pipe = '‡‡‡‡‡ESCAPED¤PIPE‡‡‡‡‡'
18
+ @name = @node[:name]
19
+ @updated_source = nil
20
+ @source = @node[:source][:name] rescue "--"
20
21
  end
21
22
 
22
- # Parses the macro parameters (stripping values)
23
- # @return [Array] the macro parameters
24
- def params
25
- return @params if @params
26
- @params = @value.gsub(/\\\|/, @escaped_pipe).split('|').map{|p| p.strip.gsub @escaped_pipe, "\\|"}
23
+ # Resets the name of the updated source (call before calling
24
+ # Macro#interpret)
25
+ # @param [String] name the source name
26
+ def update_source(name)
27
+ @updated_source = {:node => @node, :name => name}
28
+ end
29
+
30
+ # Returns a Glyph code representation of the specified parameter
31
+ # @param [Fixnum] n the index of the parameter
32
+ # @return [String, nil] the string representation of the parameter
33
+ # @since 0.3.0
34
+ def raw_parameter(n)
35
+ @node.parameter(n).contents.to_s rescue nil
36
+ end
37
+
38
+ # Returns a Glyph code representation of the specified attribute
39
+ # @param [String, Symbol] name the name of the attribute
40
+ # @return [String, nil] the string representation of the attribute
41
+ # @since 0.3.0
42
+ def raw_attribute(name)
43
+ @node.attribute(name).contents.to_s rescue nil
44
+ end
45
+
46
+ # Returns an evaluated macro attribute by name
47
+ # @param [String, Symbol] name the name of the attribute
48
+ # @param [Hash] options a hash of options
49
+ # @option options [Boolean] :strip whether the value is stripped or not
50
+ # @return [String, nil] the value of the attribute
51
+ # @since 0.3.0
52
+ def attribute(name, options={:strip => true})
53
+ return @attributes[name.to_sym] if @attributes && @attributes[name.to_sym]
54
+ return nil unless @node.attribute(name)
55
+ @attributes = {} unless @attributes
56
+ @attributes[name] = @node.attribute(name).evaluate(@node, :attrs => true).to_s
57
+ @attributes[name].strip! if options[:strip]
58
+ @attributes[name]
59
+ end
60
+
61
+ # Returns an evaluated macro parameter by index
62
+ # @param [Fixnum] n the index of the parameter
63
+ # @param [Hash] options a hash of options
64
+ # @option options [Boolean] :strip whether the value is stripped or not
65
+ # @return [String, nil] the value of the parameter
66
+ # @since 0.3.0
67
+ def parameter(n, options={:strip => true})
68
+ return @parameters[n] if @parameters && @parameters[n]
69
+ return nil unless @node.parameter(n)
70
+ @parameters = Array.new(@node.parameters.length) unless @parameters
71
+ @parameters[n] = @node.parameter(n).evaluate(@node, :params => true).to_s
72
+ @parameters[n].strip! if options[:strip]
73
+ @parameters[n]
27
74
  end
28
75
 
29
- # Parses the macro parameters (without stripping values)
76
+ # Returns a hash containing all evaluated macro attributes
77
+ # @param [Hash] options a hash of options
78
+ # @option options [Boolean] :strip whether the value is stripped or not
79
+ # @return [Hash] the macro attributes
80
+ # @since 0.3.0
81
+ def attributes(options={:strip => true})
82
+ return @attributes if @attributes
83
+ @attributes = {}
84
+ @node.attributes.each do |value|
85
+ @attributes[value[:name]] = value.evaluate(@node, :attrs => true)
86
+ @attributes[value[:name]].strip! if options[:strip]
87
+ end
88
+ @attributes
89
+ end
90
+
91
+ # Returns an array containing all evaluated macro parameters
92
+ # @param [Hash] options a hash of options
93
+ # @option options [Boolean] :strip whether the value is stripped or not
30
94
  # @return [Array] the macro parameters
31
- def raw_params
32
- return @raw_params if @raw_params
33
- @params = @value.gsub(/\\\|/, @escaped_pipe).split('|').map{|p| p.gsub @escaped_pipe, "\\|"}
95
+ # @since 0.3.0
96
+ def parameters(options={:strip => true})
97
+ return @parameters if @parameters
98
+ @parameters = []
99
+ @node.parameters.each do |value|
100
+ @parameters << value.evaluate(@node, :params => true)
101
+ @parameters.last.strip! if options[:strip]
102
+ end
103
+ @parameters
104
+ end
105
+
106
+ alias params parameters
107
+ alias param parameter
108
+ alias attrs attributes
109
+ alias attr attribute
110
+ alias raw_param raw_parameter
111
+ alias raw_attr raw_attribute
112
+
113
+ # Equivalent to Glyph::Macro#parameter(0).
114
+ # @since 0.3.0
115
+ def value
116
+ parameter(0)
117
+ end
118
+
119
+ # Equivalent to Glyph::Macro#raw_parameter(0).
120
+ # @since 0.3.0
121
+ def raw_value
122
+ raw_parameter(0)
34
123
  end
35
124
 
36
125
  # Returns the "path" to the macro within the syntax tree.
37
126
  # @return [String] the macro path
127
+ # @since 0.3.0
38
128
  def path
39
129
  macros = []
40
- @node.ascend {|n| macros << n[:macro].to_s if n[:macro] }
41
- macros.reverse.join('/')
130
+ @node.ascend do |n|
131
+ case
132
+ when n.is_a?(Glyph::MacroNode) then
133
+ if n[:name] == :"|xml|" then
134
+ name = "xml[#{n[:element]}]"
135
+ else
136
+ break if n[:name] == :include
137
+ name = n[:name].to_s
138
+ end
139
+ when n.is_a?(Glyph::ParameterNode) then
140
+ if n.parent.parameters.length == 1 then
141
+ name = nil
142
+ else
143
+ name = n[:name].to_s
144
+ end
145
+ when n.is_a?(Glyph::AttributeNode) then
146
+ name = "@#{n[:name]}"
147
+ else
148
+ name = nil
149
+ end
150
+ macros << name
151
+ end
152
+ macros.reverse.compact.join('/')
42
153
  end
43
154
 
44
155
  # Returns a todo message to include in the document in case of errors.
45
156
  # @param [String] message the message to include in the document
46
157
  # @return [String] the resulting todo message
158
+ # @since 0.2.0
47
159
  def macro_todo(message)
48
160
  draft = Glyph['document.draft']
49
161
  Glyph['document.draft'] = true unless draft
@@ -56,57 +168,49 @@ module Glyph
56
168
  # @param [String] msg the message to print
57
169
  # @raise [Glyph::MacroError]
58
170
  def macro_error(msg, klass=Glyph::MacroError)
59
- src = @node[:source_name]
60
- src ||= @node[:source]
61
- src ||= "--"
62
- message = "#{msg}\n -> source: #{src}\n -> path: #{path}"
63
- @node[:document].errors << message
64
- message += "\n -> value:\n#{"-"*54}\n#{@value}\n#{"-"*54}" if Glyph.debug?
65
- raise klass, message
171
+ @node[:document].errors << msg if @node[:document]
172
+ raise klass.new(msg, self)
66
173
  end
67
174
 
68
- # Raises a macro error
175
+ # Prints a macro earning
69
176
  # @param [String] msg the message to print
70
- # @raise [Glyph::MacroError]
71
- def macro_warning(message)
72
- src = @node[:source_name]
73
- src ||= @node[:source]
74
- src ||= "--"
75
- Glyph.warning "#{message}\n -> source: #{src}\n -> path: #{path}"
76
- message += %{\n -> value:\n#{"-"*54}\n#{@value}\n#{"-"*54}} if Glyph.debug?
177
+ # @param [Exception] e the exception raised
178
+ # @since 0.2.0
179
+ def macro_warning(msg, e=nil)
180
+ if e.is_a?(Glyph::MacroError) then
181
+ e.display
182
+ else
183
+ message = "#{msg}\n source: #{@source}\n path: #{path}"
184
+ if Glyph.debug? then
185
+ message << %{\n#{"-"*54}\n#{@node.to_s.gsub(/\t/, ' ')}\n#{"-"*54}}
186
+ if e then
187
+ message << "\n"+"-"*20+"[ Backtrace: ]"+"-"*20
188
+ message << "\n"+e.backtrace.join("\n")
189
+ message << "\n"+"-"*54
190
+ end
191
+ end
192
+ Glyph.warning message
193
+ end
77
194
  end
78
195
 
79
196
  # Instantiates a Glyph::Interpreter and interprets a string
80
197
  # @param [String] string the string to interpret
81
198
  # @return [String] the interpreted output
82
- # @raise [Glyph::MacroError] in case of mutual macro inclusion (snippet, include macros)
83
199
  def interpret(string)
84
- @node[:source] = "#@name[#@value]"
85
- @node[:source_name] = "#{@name}[...]"
86
- macro_error "Mutual inclusion", Glyph::MutualInclusionError if @node.find_parent {|n| n[:source] == @node[:source] }
87
200
  if @node[:escape] then
88
201
  result = string
89
202
  else
90
- @node[:embedded] = true
91
- result = Glyph::Interpreter.new(string, @node).document.output
203
+ context = {}
204
+ context[:source] = @updated_source || @node[:source]
205
+ context[:embedded] = true
206
+ context[:document] = @node[:document]
207
+ interpreter = Glyph::Interpreter.new string, context
208
+ subtree = interpreter.parse
209
+ @node << subtree
210
+ result = interpreter.document.output
92
211
  end
93
212
  result.gsub(/\\*([\[\]])/){"\\#$1"}
94
- end
95
-
96
- # Encodes all macros in a string so that it can be encoded
97
- # (and interpreted) later on
98
- # @param [String] string the string to encode
99
- # @return [String] the encoded string
100
- def encode(string)
101
- string.gsub(/([\[\]\|])/) { "‡‡¤#{$1.bytes.to_a[0]}¤‡‡" }
102
- end
103
-
104
- # Decodes a previously encoded string
105
- # so that it can be interpreted
106
- # @param [String] string the string to decode
107
- # @return [String] the decoded string
108
- def decode(string)
109
- string.gsub(/‡‡¤(91|93|124)¤‡‡/) { $1.to_i.chr }
213
+ result
110
214
  end
111
215
 
112
216
  # @see Glyph::Document#placeholder
@@ -135,12 +239,13 @@ module Glyph
135
239
  end
136
240
 
137
241
  # Executes a macro definition in the context of self
138
- def execute
139
- res = instance_exec(@node, &Glyph::MACROS[@name]).to_s
242
+ def expand
243
+ block = Glyph::MACROS[@name]
244
+ macro_error "Undefined macro '#@name'}" unless block
245
+ res = instance_exec(@node, &block).to_s
140
246
  res.gsub!(/\\*([\[\]\|])/){"\\#$1"}
141
247
  res
142
248
  end
143
249
 
144
250
  end
145
-
146
251
  end