lib/glyph/macro.rb in glyph-0.2.0 vs lib/glyph/macro.rb in glyph-0.3.0
- old
+ new
@@ -7,45 +7,157 @@
# useful methods to be used in macro definitions.
class Macro
include Validators
+ attr_reader :node, :source
+
# Creates a new macro instance from a Node
# @param [Node] node a node populated with macro data
def initialize(node)
@node = node
- @name = @node[:macro]
- @value = @node[:value]
- @source = @node[:source]
- @escaped_pipe = '‡‡‡‡‡ESCAPED¤PIPE‡‡‡‡‡'
+ @name = @node[:name]
+ @updated_source = nil
+ @source = @node[:source][:name] rescue "--"
end
- # Parses the macro parameters (stripping values)
- # @return [Array] the macro parameters
- def params
- return @params if @params
- @params = @value.gsub(/\\\|/, @escaped_pipe).split('|').map{|p| p.strip.gsub @escaped_pipe, "\\|"}
+ # Resets the name of the updated source (call before calling
+ # Macro#interpret)
+ # @param [String] name the source name
+ def update_source(name)
+ @updated_source = {:node => @node, :name => name}
end
+
+ # Returns a Glyph code representation of the specified parameter
+ # @param [Fixnum] n the index of the parameter
+ # @return [String, nil] the string representation of the parameter
+ # @since 0.3.0
+ def raw_parameter(n)
+ @node.parameter(n).contents.to_s rescue nil
+ end
- # Parses the macro parameters (without stripping values)
+ # Returns a Glyph code representation of the specified attribute
+ # @param [String, Symbol] name the name of the attribute
+ # @return [String, nil] the string representation of the attribute
+ # @since 0.3.0
+ def raw_attribute(name)
+ @node.attribute(name).contents.to_s rescue nil
+ end
+
+ # Returns an evaluated macro attribute by name
+ # @param [String, Symbol] name the name of the attribute
+ # @param [Hash] options a hash of options
+ # @option options [Boolean] :strip whether the value is stripped or not
+ # @return [String, nil] the value of the attribute
+ # @since 0.3.0
+ def attribute(name, options={:strip => true})
+ return @attributes[name.to_sym] if @attributes && @attributes[name.to_sym]
+ return nil unless @node.attribute(name)
+ @attributes = {} unless @attributes
+ @attributes[name] = @node.attribute(name).evaluate(@node, :attrs => true).to_s
+ @attributes[name].strip! if options[:strip]
+ @attributes[name]
+ end
+
+ # Returns an evaluated macro parameter by index
+ # @param [Fixnum] n the index of the parameter
+ # @param [Hash] options a hash of options
+ # @option options [Boolean] :strip whether the value is stripped or not
+ # @return [String, nil] the value of the parameter
+ # @since 0.3.0
+ def parameter(n, options={:strip => true})
+ return @parameters[n] if @parameters && @parameters[n]
+ return nil unless @node.parameter(n)
+ @parameters = Array.new(@node.parameters.length) unless @parameters
+ @parameters[n] = @node.parameter(n).evaluate(@node, :params => true).to_s
+ @parameters[n].strip! if options[:strip]
+ @parameters[n]
+ end
+
+ # Returns a hash containing all evaluated macro attributes
+ # @param [Hash] options a hash of options
+ # @option options [Boolean] :strip whether the value is stripped or not
+ # @return [Hash] the macro attributes
+ # @since 0.3.0
+ def attributes(options={:strip => true})
+ return @attributes if @attributes
+ @attributes = {}
+ @node.attributes.each do |value|
+ @attributes[value[:name]] = value.evaluate(@node, :attrs => true)
+ @attributes[value[:name]].strip! if options[:strip]
+ end
+ @attributes
+ end
+
+ # Returns an array containing all evaluated macro parameters
+ # @param [Hash] options a hash of options
+ # @option options [Boolean] :strip whether the value is stripped or not
# @return [Array] the macro parameters
- def raw_params
- return @raw_params if @raw_params
- @params = @value.gsub(/\\\|/, @escaped_pipe).split('|').map{|p| p.gsub @escaped_pipe, "\\|"}
+ # @since 0.3.0
+ def parameters(options={:strip => true})
+ return @parameters if @parameters
+ @parameters = []
+ @node.parameters.each do |value|
+ @parameters << value.evaluate(@node, :params => true)
+ @parameters.last.strip! if options[:strip]
+ end
+ @parameters
end
+ alias params parameters
+ alias param parameter
+ alias attrs attributes
+ alias attr attribute
+ alias raw_param raw_parameter
+ alias raw_attr raw_attribute
+
+ # Equivalent to Glyph::Macro#parameter(0).
+ # @since 0.3.0
+ def value
+ parameter(0)
+ end
+
+ # Equivalent to Glyph::Macro#raw_parameter(0).
+ # @since 0.3.0
+ def raw_value
+ raw_parameter(0)
+ end
+
# Returns the "path" to the macro within the syntax tree.
# @return [String] the macro path
+ # @since 0.3.0
def path
macros = []
- @node.ascend {|n| macros << n[:macro].to_s if n[:macro] }
- macros.reverse.join('/')
+ @node.ascend do |n|
+ case
+ when n.is_a?(Glyph::MacroNode) then
+ if n[:name] == :"|xml|" then
+ name = "xml[#{n[:element]}]"
+ else
+ break if n[:name] == :include
+ name = n[:name].to_s
+ end
+ when n.is_a?(Glyph::ParameterNode) then
+ if n.parent.parameters.length == 1 then
+ name = nil
+ else
+ name = n[:name].to_s
+ end
+ when n.is_a?(Glyph::AttributeNode) then
+ name = "@#{n[:name]}"
+ else
+ name = nil
+ end
+ macros << name
+ end
+ macros.reverse.compact.join('/')
end
# Returns a todo message to include in the document in case of errors.
# @param [String] message the message to include in the document
# @return [String] the resulting todo message
+ # @since 0.2.0
def macro_todo(message)
draft = Glyph['document.draft']
Glyph['document.draft'] = true unless draft
res = interpret "![#{message}]"
Glyph['document.draft'] = false unless draft
@@ -54,63 +166,55 @@
# Raises a macro error (preventing document post-processing)
# @param [String] msg the message to print
# @raise [Glyph::MacroError]
def macro_error(msg, klass=Glyph::MacroError)
- src = @node[:source_name]
- src ||= @node[:source]
- src ||= "--"
- message = "#{msg}\n -> source: #{src}\n -> path: #{path}"
- @node[:document].errors << message
- message += "\n -> value:\n#{"-"*54}\n#{@value}\n#{"-"*54}" if Glyph.debug?
- raise klass, message
+ @node[:document].errors << msg if @node[:document]
+ raise klass.new(msg, self)
end
- # Raises a macro error
+ # Prints a macro earning
# @param [String] msg the message to print
- # @raise [Glyph::MacroError]
- def macro_warning(message)
- src = @node[:source_name]
- src ||= @node[:source]
- src ||= "--"
- Glyph.warning "#{message}\n -> source: #{src}\n -> path: #{path}"
- message += %{\n -> value:\n#{"-"*54}\n#{@value}\n#{"-"*54}} if Glyph.debug?
+ # @param [Exception] e the exception raised
+ # @since 0.2.0
+ def macro_warning(msg, e=nil)
+ if e.is_a?(Glyph::MacroError) then
+ e.display
+ else
+ message = "#{msg}\n source: #{@source}\n path: #{path}"
+ if Glyph.debug? then
+ message << %{\n#{"-"*54}\n#{@node.to_s.gsub(/\t/, ' ')}\n#{"-"*54}}
+ if e then
+ message << "\n"+"-"*20+"[ Backtrace: ]"+"-"*20
+ message << "\n"+e.backtrace.join("\n")
+ message << "\n"+"-"*54
+ end
+ end
+ Glyph.warning message
+ end
end
# Instantiates a Glyph::Interpreter and interprets a string
# @param [String] string the string to interpret
# @return [String] the interpreted output
- # @raise [Glyph::MacroError] in case of mutual macro inclusion (snippet, include macros)
def interpret(string)
- @node[:source] = "#@name[#@value]"
- @node[:source_name] = "#{@name}[...]"
- macro_error "Mutual inclusion", Glyph::MutualInclusionError if @node.find_parent {|n| n[:source] == @node[:source] }
if @node[:escape] then
result = string
else
- @node[:embedded] = true
- result = Glyph::Interpreter.new(string, @node).document.output
+ context = {}
+ context[:source] = @updated_source || @node[:source]
+ context[:embedded] = true
+ context[:document] = @node[:document]
+ interpreter = Glyph::Interpreter.new string, context
+ subtree = interpreter.parse
+ @node << subtree
+ result = interpreter.document.output
end
result.gsub(/\\*([\[\]])/){"\\#$1"}
+ result
end
- # Encodes all macros in a string so that it can be encoded
- # (and interpreted) later on
- # @param [String] string the string to encode
- # @return [String] the encoded string
- def encode(string)
- string.gsub(/([\[\]\|])/) { "‡‡¤#{$1.bytes.to_a[0]}¤‡‡" }
- end
-
- # Decodes a previously encoded string
- # so that it can be interpreted
- # @param [String] string the string to decode
- # @return [String] the decoded string
- def decode(string)
- string.gsub(/‡‡¤(91|93|124)¤‡‡/) { $1.to_i.chr }
- end
-
# @see Glyph::Document#placeholder
def placeholder(&block)
@node[:document].placeholder &block
end
@@ -133,14 +237,15 @@
def header(hash)
@node[:document].header hash
end
# Executes a macro definition in the context of self
- def execute
- res = instance_exec(@node, &Glyph::MACROS[@name]).to_s
+ def expand
+ block = Glyph::MACROS[@name]
+ macro_error "Undefined macro '#@name'}" unless block
+ res = instance_exec(@node, &block).to_s
res.gsub!(/\\*([\[\]\|])/){"\\#$1"}
res
end
end
-
end