Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Re-implemented xml support using macro composition.

  • Loading branch information...
commit 59b08e774776163b6dfc6b67cf506b80226a3bfb 1 parent 2f77c96
@h3rald authored
View
2  layouts/web5/topic.glyph
@@ -4,7 +4,7 @@ document[
]
body[
@class[topic]
- =article[
+ xml/article[
section[
@title[{{title}}]
@id[{{id}}]
View
16 lib/glyph/macro.rb
@@ -10,7 +10,7 @@ class Macro
include Validators
include Utils
- attr_reader :node, :source_name, :source_file, :source_topic
+ attr_reader :node, :source_name, :source_file, :source_topic, :name
# Creates a new macro instance from a Node
# @param [Node] node a node populated with macro data
@@ -259,17 +259,19 @@ def render(rep=nil, data=nil)
data ||= @data
block = Glyph::REPS[rep.to_sym]
macro_error "No macro representation for '#{rep}'", e unless block
- begin
- instance_exec(data, &block).to_s
- rescue Exception => e
- macro_error "An error occurred when rendering 'rep'", e
- end
+ instance_exec(data, &block).to_s
+ end
+
+ # TODO: block
+ def dispatch(&block)
+ @node[:dispatch] = block
+ value
end
# Executes a macro definition in the context of self
def expand
block = Glyph::MACROS[@name]
- macro_error "Undefined macro '#@name'", e unless block
+ macro_error "Undefined macro '#@name'" unless block
res = instance_exec(@node, &block).to_s
res.gsub!(/\\?([\[\]\|])/){"\\#$1"}
res
View
7 lib/glyph/macro_validators.rb
@@ -22,12 +22,13 @@ def validate(message, options={:level => :error}, &block)
result
end
- # Ensures that the macro element attributes is a valid XML element name.
+ # Ensures that the provided name is a valid XML element name.
+ # @params [String, Symbol] name the element name to validate
# @param [Hash] options a hash containing validation options (for now the only option is :level)
# @return [Boolean] whether the validation passed or not
# @since 0.3.0
- def valid_xml_element(options={:level => :error})
- validate("Invalid XML element '#{@node[:element]}'", options) { @node[:element].to_s.match(/^([^[:punct:]0-9<>]|_)[^<>"']*/) }
+ def valid_xml_element(name, options={:level => :error})
+ validate("Invalid XML element '#{name}'", options) { name.to_s.match(/^([^[:punct:]0-9<>]|_)[^<>"']*/) }
end
# Ensures that a macro attribute name is a valid XML attribute name.
View
40 lib/glyph/syntax_node.rb
@@ -125,11 +125,18 @@ def attribute(name)
# @return [String] the value of the macro
# @since 0.3.0
def expand(context)
- xml_element(context)
self[:source] = context[:source]
self[:document] = context[:document]
self[:info] = context[:info]
self[:value] = ""
+ dispatched = parent_macro.dispatch(self) rescue nil
+ return dispatched if dispatched
+ if Glyph['options.macro_set'] == "xml" || Glyph::MACROS[self[:name]].blank? && Glyph['options.xml_fallback'] then
+ m = Glyph::MacroNode.new
+ m[:name] = :xml
+ Glyph::Macro.new(m).expand
+ return m[:dispatch].call self
+ end
Glyph::Macro.new(self).expand
end
@@ -141,33 +148,12 @@ def source
s
end
- protected
-
- def xml_element(context)
- known_macro = Glyph::MACROS.include? self[:name]
- name = self[:name].to_s
- if !known_macro && name.match(/^=(.+)/) then
- # Force tag name override if macro starts with a '='
- name.gsub! /^=(.+)/, '\1'
- end
- case
- # Use XML syntax
- when Glyph['options.macro_set'] == 'xml' then
- self[:element] = name
- self[:name] = :"|xml|"
- # Fallback to XML syntax
- when Glyph["options.xml_fallback"] then
- unless known_macro then
- self[:element] = name
- self[:fallback] = true
- self[:name] = :"|xml|"
- end
- else
- # Unknown macro
- raise RuntimeError, "Undefined macro '#{name}'\n -> source: #{context[:source][:name]}" unless known_macro
- end
+ # @since 0.5.0
+ # TODO: doc
+ def dispatch(node)
+ return self[:dispatch].call node if self[:dispatch]
+ false
end
-
end
# A piece of text in Glyph Abstract Syntax Tree
View
6 macros/reps/html5.rb
@@ -36,9 +36,9 @@
# TODO: change fallback
rep :figure do |data|
- interpret %{=figure[
-img[@src[#{data[:src]}]#{data[:attrs].join}]
- figcaption[#{data[:caption]}]
+ interpret %{xml/figure[
+xml/img[@src[#{data[:src]}]#{data[:attrs].join}]
+ xml/figcaption[#{data[:caption]}]
]}
end
View
52 macros/xml.rb
@@ -1,34 +1,38 @@
#!/usr/bin/env ruby
# encoding: utf-8
-macro "|xml|" do
- begin
- valid_xml_element
+macro :xml do
+ dispatch do |node|
+ name = node[:name]
+ valid_xml_element name
max_parameters 1
- rescue Exception => e
- if @node[:fallback] then
- macro_error "Unknown macro '#{@node[:element]}'"
+ if Glyph["options.xml_blacklist"] && name.to_s.in?(Glyph['options.xml_blacklist']) then
+ ""
else
- raise
- end
- end
- name = @node[:element]
- if Glyph["options.xml_blacklist"] && name.to_s.in?(Glyph['options.xml_blacklist']) then
- ""
- else
- attributes # evaluate attributes
- xml_attributes = @node.children.select{|node| node.is_a?(Glyph::AttributeNode)}.
- map do |e|
- if valid_xml_attribute(e[:name]) then
- %|#{e[:name]}="#{e[:value]}"|
- else
- nil
- end
+ attributes # evaluate attributes
+ xml_attributes = node.children.select{|n| n.is_a?(Glyph::AttributeNode)}.map do |e|
+ if valid_xml_attribute(e[:name]) then
+ attr_v = e[:value].blank? ? e.evaluate(node, :attrs => true) : e[:value]
+ %|#{e[:name]}="#{attr_v}"|
+ else
+ nil
+ end
end.compact.join(" ")
xml_attributes = " "+xml_attributes unless xml_attributes.blank?
- end_first_tag = param(0) ? ">" : ""
- end_tag = param(0) ? "</#{name}>" : " />"
- contents = (@node.param(0)&0) && (@node.param(0)&0)[:element] ? "\n#{param(0)}\n" : param(0)
+ end_first_tag = node.param(0) ? ">" : ""
+ end_tag = node.param(0) ? "</#{name}>" : " />"
+ if node.param(0) then
+ param_0 = node.param(0)[:value].blank? ? node.param(0).evaluate(node, :params => true) : node.param(0)[:value]
+ if (node.param(0)&0) && (node.param(0)&0)[:name] then
+ contents = "\n#{param_0}\n"
+ else
+ contents = param_0
+ end
+ else
+ # no parameters
+ contents = ""
+ end
%{<#{name}#{xml_attributes}#{end_first_tag}#{contents}#{end_tag}}
+ end
end
end
View
2  spec/files/web_doc.glyph
@@ -5,7 +5,7 @@ document[
]
body[
toc[]
- contents[
+ section[
section[
@title[Web Document]
...
View
17 spec/lib/macro_spec.rb
@@ -240,4 +240,21 @@
Glyph::Macro.new({}).render(:em_with_rep, :value => "test").should == "<em>!test!</em>"
end
+ it "should perform dispatching" do
+ Glyph.macro :dispatcher do
+ dispatch do |node|
+ "dispatched: #{node[:name]}" if node[:name] == :em
+ end
+ end
+ Glyph.macro :another_macro do
+ "...#{value}"
+ end
+ define_em_macro
+ output_for("dispatcher[em[test]]").should == "dispatched: em"
+ output_for("dispatcher[em[@attr[test]]]").should == "dispatched: em"
+ output_for("dispatcher[...|em[@attr[test]]]").should == "..." # Dispatcher macros should only take one parameter
+ output_for("dispatcher[another_macro[test]]").should == "...test"
+ output_for("dispatcher[another_macro[another_macro[test]]]").should == "......test"
+ end
+
end
View
4 spec/lib/macro_validators_spec.rb
@@ -48,13 +48,13 @@
it "should validate XML attributes" do
language 'xml'
- output_for("test[test @.test[test]]").should == "<test>test</test>"
+ output_for("test[test @.test[test]]").should == "<test>test </test>"
end
it "should validate required attributes" do
Glyph['document.output'] = 'web'
Glyph.run! 'load:macros'
- lambda { output_for("contents[section[@src[test]]]") }.should raise_error(Glyph::MacroError, "Macro 'section' requires a 'title' attribute")
+ lambda { output_for("section[section[@src[test]]]") }.should raise_error(Glyph::MacroError, "Macro 'section' requires a 'title' attribute")
end
it "should validate if a macro is within another one" do
View
38 spec/lib/syntax_node_spec.rb
@@ -83,12 +83,6 @@
@n.expand({}).should == "--test:test--"
end
- it "should resolve to an XML element" do
- reset_quiet
- @n.expand({}) rescue nil # |xml| macro not defined
- @n[:element].should == "test"
- end
-
it "should retrieve parameter nodes easily" do
@n.parameter(0).should == @p
@n.parameters.should == [@p]
@@ -107,5 +101,37 @@
@p.to_s.should == "test"
@p.contents.should == ".[=test=]"
end
+
+ it "should perform macro dispatching" do
+ dispatch_proc = lambda do |node|
+ "dispatched: #{node[:name]}"
+ end
+ Glyph.macro :test_macro do
+ "--test macro--"
+ end
+ # Parent dispatcher via parameter
+ d = macro_node :dispatcher
+ d[:dispatch] = dispatch_proc
+ p = p_node 0
+ m = macro_node :test_macro
+ p << m
+ d << p
+ m.expand({}).should == "dispatched: test_macro"
+ # Parent dispatcher via attribute
+ d = macro_node :dispatcher
+ d[:dispatch] = dispatch_proc
+ a = a_node :attr1
+ m = macro_node :test_macro
+ a << m
+ d << a
+ m.expand({}).should == "dispatched: test_macro"
+ # No dispatcher
+ d = macro_node :no_dispatcher
+ a = a_node :attr1
+ m = macro_node :test_macro
+ a << m
+ d << a
+ m.expand({}).should == "--test macro--"
+ end
end
View
2  spec/macros/web5_spec.rb
@@ -17,7 +17,7 @@
end
it "section (topic)" do
- interpret("contents[section[@src[a/web1.glyph]@title[Test]]]")
+ interpret("section[section[@src[a/web1.glyph]@title[Test]]]")
topic = @p.document.topics[0]
topic[:contents].match(/<article>/).blank?.should == false
end
View
13 spec/macros/xml_spec.rb
@@ -35,7 +35,7 @@
it "should support XML attributes" do
language('xml')
output_for("span[@class[test] @style[color:red;] test...]").should == %{
- <span class="test" style="color:red;">test...</span>
+ <span class="test" style="color:red;"> test...</span>
}.strip
end
@@ -48,7 +48,7 @@
it "should notify the user that a macro is not found for invalid elements if xml_fallback is enabled" do
# Assuming options.xml_fallback = true
language('glyph')
- lambda { interpret("*a[test]").document }.should raise_error(Glyph::MacroError, "Unknown macro '*a'")
+ lambda { interpret("*a[test]").document }.should raise_error(Glyph::MacroError, "Invalid XML element '*a'")
end
it "should not render blacklisted tags" do
@@ -74,4 +74,13 @@
output_for(text).gsub(/\s/, '').should == "<test>test</test>"
end
+ it "should work with macro composition" do
+ language('glyph')
+ output_for("xml/a[@test[...]xyz]").should == "<a test=\"...\">xyz</a>"
+ output_for("xml/a[@test[...]xml/b[test]]").should == "<a test=\"...\">\n<b>test</b>\n</a>"
+ output_for("xml/a[xml/b[test]xml/c[test]]").should == "<a>\n<b>test</b><c>test</c>\n</a>"
+ output_for("xml/a[xml/b[test]xml/c[@test[test_attr]test]]").should == "<a>\n<b>test</b><c test=\"test_attr\">test</c>\n</a>"
+ output_for("xml/a[xml/b[@test[true]]]").should == "<a>\n<b test=\"true\" />\n</a>"
+ end
+
end
Please sign in to comment.
Something went wrong with that request. Please try again.