glyph 0.1.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 (68) hide show
  1. data/README.textile +80 -0
  2. data/Rakefile +51 -0
  3. data/VERSION +1 -0
  4. data/bin/glyph +7 -0
  5. data/book/config.yml +5 -0
  6. data/book/document.glyph +55 -0
  7. data/book/images/glyph.png +0 -0
  8. data/book/images/glyph.svg +351 -0
  9. data/book/lib/macros/reference.rb +98 -0
  10. data/book/output/html/glyph.html +1809 -0
  11. data/book/output/html/images/glyph.png +0 -0
  12. data/book/output/html/images/glyph.svg +351 -0
  13. data/book/output/pdf/glyph.pdf +4277 -0
  14. data/book/snippets.yml +13 -0
  15. data/book/styles/css3.css +220 -0
  16. data/book/styles/default.css +190 -0
  17. data/book/text/authoring.textile +351 -0
  18. data/book/text/extending.textile +148 -0
  19. data/book/text/getting_started.textile +152 -0
  20. data/book/text/introduction.textile +88 -0
  21. data/book/text/ref_commands.textile +74 -0
  22. data/book/text/ref_config.textile +0 -0
  23. data/book/text/ref_macros.textile +256 -0
  24. data/book/text/troubleshooting.textile +118 -0
  25. data/config.yml +63 -0
  26. data/document.glyph +29 -0
  27. data/glyph.gemspec +138 -0
  28. data/lib/glyph.rb +128 -0
  29. data/lib/glyph/commands.rb +124 -0
  30. data/lib/glyph/config.rb +152 -0
  31. data/lib/glyph/document.rb +145 -0
  32. data/lib/glyph/glyph_language.rb +530 -0
  33. data/lib/glyph/glyph_language.treetop +27 -0
  34. data/lib/glyph/interpreter.rb +84 -0
  35. data/lib/glyph/macro.rb +69 -0
  36. data/lib/glyph/node.rb +126 -0
  37. data/lib/glyph/system_extensions.rb +77 -0
  38. data/macros/common.rb +66 -0
  39. data/macros/filters.rb +69 -0
  40. data/macros/html/block.rb +119 -0
  41. data/macros/html/inline.rb +43 -0
  42. data/macros/html/structure.rb +138 -0
  43. data/spec/files/container.textile +5 -0
  44. data/spec/files/document.glyph +2 -0
  45. data/spec/files/document_with_toc.glyph +3 -0
  46. data/spec/files/included.textile +4 -0
  47. data/spec/files/ligature.jpg +449 -0
  48. data/spec/files/markdown.markdown +8 -0
  49. data/spec/files/test.sass +2 -0
  50. data/spec/lib/commands_spec.rb +83 -0
  51. data/spec/lib/config_spec.rb +79 -0
  52. data/spec/lib/document_spec.rb +100 -0
  53. data/spec/lib/glyph_spec.rb +76 -0
  54. data/spec/lib/interpreter_spec.rb +90 -0
  55. data/spec/lib/macro_spec.rb +60 -0
  56. data/spec/lib/node_spec.rb +76 -0
  57. data/spec/macros/filters_spec.rb +42 -0
  58. data/spec/macros/macros_spec.rb +159 -0
  59. data/spec/spec_helper.rb +92 -0
  60. data/spec/tasks/generate_spec.rb +31 -0
  61. data/spec/tasks/load_spec.rb +37 -0
  62. data/spec/tasks/project_spec.rb +41 -0
  63. data/styles/css3.css +220 -0
  64. data/styles/default.css +190 -0
  65. data/tasks/generate.rake +57 -0
  66. data/tasks/load.rake +55 -0
  67. data/tasks/project.rake +33 -0
  68. metadata +192 -0
@@ -0,0 +1,27 @@
1
+ grammar GlyphLanguage
2
+
3
+ rule expression
4
+ (escaping_macro / macro / escaped_text)* <GlyphSyntaxNode>
5
+ end
6
+
7
+ rule escaping_macro
8
+ macro_name '[=' text '=]' <MacroNode>
9
+ end
10
+
11
+ rule macro
12
+ macro_name '[' expression ']' <MacroNode>
13
+ end
14
+
15
+ rule escaped_text
16
+ (('\\' .) / !((macro_name ('[' / '[=')) / (']' / '=]')) .)+ <TextNode>
17
+ end
18
+
19
+ rule text
20
+ (('\\' .) / !((macro_name '[=') / '=]') .)+ <TextNode>
21
+ end
22
+
23
+ rule macro_name
24
+ [^\[\]\|\\\s]+
25
+ end
26
+
27
+ end
@@ -0,0 +1,84 @@
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
+ value = elements.map { |e| e.evaluate(context, current) if e.respond_to? :evaluate }.join
10
+ value
11
+ end
12
+
13
+ end
14
+
15
+ # @private
16
+ class MacroNode < GlyphSyntaxNode
17
+
18
+ def evaluate(context, current=nil)
19
+ name = macro_name.text_value.to_sym
20
+ raise RuntimeError, "Undefined macro '#{name}'" unless Glyph::MACROS.include? name
21
+ @data = {:macro => name, :source => context[:source], :document => context[:document]}.to_node
22
+ current << @data
23
+ value = super(context, @data).strip
24
+ @data[:value] = value
25
+ Glyph::Macro.new(@data).execute
26
+ end
27
+
28
+ end
29
+
30
+ # @private
31
+ class TextNode < GlyphSyntaxNode
32
+
33
+ def evaluate(context, current=nil)
34
+ text_value
35
+ end
36
+
37
+ end
38
+
39
+
40
+ module Glyph
41
+
42
+ # A Glyph::Interpreter object perform the following actions:
43
+ # * Parses a string of text containing Glyph macros
44
+ # * Creates a document based on the parsed syntax tree
45
+ # * Analyzes and finalizes the document
46
+ class Interpreter
47
+
48
+ # Creates a new Glyph::Interpreter object.
49
+ # @param [String] text the string to interpret
50
+ # @param [Hash] context the context to pass along when evaluating macros
51
+ def initialize(text, context=nil)
52
+ context ||= {:source => '--'}
53
+ @parser = GlyphLanguageParser.new
54
+ @raw = @parser.parse text
55
+ @context = context
56
+ @document = Glyph::Document.new @raw, @context
57
+ @document.inherit_from @context[:document] if @context[:document]
58
+ end
59
+
60
+ # @see Glyph::Document#analyze
61
+ def process
62
+ @document.analyze
63
+ end
64
+
65
+ # @see Glyph::Document#finalize
66
+ def postprocess
67
+ @document.finalize
68
+ end
69
+
70
+ # Returns the finalized @document (calls self#process and self#postprocess if necessary)
71
+ # @return [Glyph::Document] the finalized document
72
+ def document
73
+ return @document if @document.finalized?
74
+ process if @document.new?
75
+ postprocess if @document.analyzed?
76
+ @document
77
+ end
78
+
79
+
80
+ end
81
+
82
+ end
83
+
84
+
@@ -0,0 +1,69 @@
1
+ module Glyph
2
+
3
+ # A Macro object is instantiated by a Glyph::Interpreter whenever a macro is found in the parsed text.
4
+ # The Macro class contains shortcut methods to access the current node and document, as well as other
5
+ # useful methods to be used in macro definitions.
6
+ class Macro
7
+
8
+ # Creates a new macro instance from a Node
9
+ # @param [Node] node a node populated with macro data
10
+ def initialize(node)
11
+ @node = node
12
+ @name = @node[:macro]
13
+ @value = @node[:value]
14
+ @source = @node[:source]
15
+ esc = '‡‡‡‡‡ESCAPED¤PIPE‡‡‡‡‡'
16
+ @params = @value.gsub(/\\\|/, esc).split('|').map{|p| p.strip.gsub esc, '|'}
17
+ end
18
+
19
+ # Raises a macro error
20
+ # @param [String] msg the message to print
21
+ # @raise [MacroError]
22
+ def macro_error(msg)
23
+ raise MacroError.new @node, msg
24
+ end
25
+
26
+ # Instantiates a Glyph::Interpreter and interprets a string
27
+ # @param [String] string the string to interpret
28
+ # @return [String] the interpreted output
29
+ # @raise [MacroError] in case of mutual macro inclusion (snippet, include macros)
30
+ def interpret(string)
31
+ @node[:source] = "#{@name}: #{@value}"
32
+ @node[:analyze_only] = true
33
+ macro_error "Mutual inclusion" if @node.find_parent {|n| n[:source] == @node[:source] }
34
+ Glyph::Interpreter.new(string, @node).document.output
35
+ end
36
+
37
+ # @see Glyph::Document#placeholder
38
+ def placeholder(&block)
39
+ @node[:document].placeholder &block
40
+ end
41
+
42
+ # @see Glyph::Document#bookmark
43
+ def bookmark(hash)
44
+ @node[:document].bookmark hash
45
+ end
46
+
47
+ # @see Glyph::Document#bookmark?
48
+ def bookmark?(ident)
49
+ @node[:document].bookmark? ident
50
+ end
51
+
52
+ # @see Glyph::Document#header?
53
+ def header?(ident)
54
+ @node[:document].header? ident
55
+ end
56
+
57
+ # @see Glyph::Document#header
58
+ def header(hash)
59
+ @node[:document].header hash
60
+ end
61
+
62
+ # Executes a macro definition in the context of self
63
+ def execute
64
+ instance_exec(@node, &Glyph::MACROS[@name]).to_s
65
+ end
66
+
67
+ end
68
+
69
+ end
data/lib/glyph/node.rb ADDED
@@ -0,0 +1,126 @@
1
+ class Hash
2
+
3
+ # Converts self to a Node
4
+ # @return [Node] the converted Node
5
+ def to_node
6
+ Node.new.replace self
7
+ end
8
+
9
+ end
10
+
11
+
12
+ # A Node is a Hash with an array of children and a parent associated to it.
13
+ class Node < Hash
14
+
15
+ attr_reader :children
16
+ attr_accessor :parent
17
+
18
+ # Creates a new Node
19
+ def initialize(*args)
20
+ super(*args)
21
+ @children = []
22
+ end
23
+
24
+ # @return [Node] Returns self
25
+ def to_node
26
+ self
27
+ end
28
+
29
+ # Clone another node (replaces all keys, parent and children)
30
+ # @param [#to_node] node the Node to clone
31
+ # @return [self]
32
+ def from(node)
33
+ n = node.to_node
34
+ replace node
35
+ @parent = n.parent
36
+ @children = n.children
37
+ self
38
+ end
39
+
40
+ # Clears all keys, parent and children
41
+ def clear
42
+ super
43
+ @children.clear
44
+ @parent = nil
45
+ end
46
+
47
+ # Adds a child node to self
48
+ # @param [Hash] hash the new child
49
+ # @return [Array] the node's children
50
+ # @raise [ArgumentError] unless a Hash is passed as parameter
51
+ def <<(hash)
52
+ raise ArgumentError, "#{hash} is not a Hash" unless hash.is_a? Hash
53
+ ht = hash.to_node
54
+ ht.parent = self
55
+ @children << ht
56
+ end
57
+
58
+ # Returns a child by its index
59
+ # @return [Node] the child node or nil
60
+ # @param [Integer] index the child index
61
+ def child(index)
62
+ @children[index]
63
+ end
64
+
65
+ alias >> child
66
+
67
+ # Iterates through children
68
+ # @yieldparam [Node] c the child node
69
+ def each_child
70
+ @children.each {|c| yield c }
71
+ end
72
+
73
+
74
+ # Iterates through children recursively (including self)
75
+ # @param [Node, nil] element the node to process
76
+ # @yieldparam [Integer] level the initial tree depth
77
+ # @yieldparam [Node] element the current node
78
+ # @yieldparam [Integer] level the current tree depth
79
+ def descend(element=nil, level=0, &block)
80
+ element ||= self
81
+ yield element, level
82
+ element.each_child do |c|
83
+ descend c, level+1, &block
84
+ end
85
+ end
86
+
87
+ # Descend children until the block returns something.
88
+ # Each child is passed to the block.
89
+ # @param [Proc] &block the block to call on each child
90
+ # @return [Node, nil] returns the child node if found, nil otherwise
91
+ def find_child(&block)
92
+ children.each do |c|
93
+ c.descend do |node, level|
94
+ return node if block.call(node)
95
+ end
96
+ end
97
+ nil
98
+ end
99
+
100
+ # Ascend parents until the block returns something.
101
+ # Each parent is passed to the block.
102
+ # @param [Proc] &block the block to call on each parent
103
+ # @return [Node, nil] returns the parent node if found, nil otherwise
104
+ def find_parent(&block)
105
+ return nil unless parent
106
+ parent.ascend do |node|
107
+ return node if block.call(node)
108
+ end
109
+ nil
110
+ end
111
+
112
+ # Iterates through parents recursively (including self)
113
+ # @param [Node, nil] element the node to process
114
+ # @yieldparam [Node] element the current node
115
+ def ascend(element=nil, &block)
116
+ element ||= self
117
+ yield element
118
+ ascend(element.parent, &block) if element.parent
119
+ end
120
+
121
+ # @return [Node] Returns the root node
122
+ def root
123
+ ascend(parent) {|e| return e unless e.parent }
124
+ end
125
+
126
+ end
@@ -0,0 +1,77 @@
1
+ module Kernel
2
+
3
+ # Prints a message
4
+ # @param [#to_s] message the message to print
5
+ def info(message)
6
+ puts "#{message}" unless cfg :quiet
7
+ end
8
+
9
+ # Prints a warning
10
+ # @param [#to_s] message the message to print
11
+ def warning(message)
12
+ puts "warning: #{message}" unless cfg :quiet
13
+ end
14
+
15
+ # @see Glyph::Config#get
16
+ def cfg(setting)
17
+ Glyph::CONFIG.get(setting)
18
+ end
19
+
20
+ # Dumps and serialize an object to a YAML file
21
+ # @param [#to_s] file the file to write to
22
+ # @param [Object] obj the object to serialize
23
+ def yaml_dump(file, obj)
24
+ File.open(file.to_s, 'w+') {|f| f.write obj.to_yaml}
25
+ end
26
+
27
+ # Loads and deserialiaze the contents of a YAML file
28
+ # @param [#to_s] file the YAML file to load
29
+ # @return [Object] the contents of the YAML file, deserialized
30
+ def yaml_load(file)
31
+ YAML.load_file(file.to_s)
32
+ end
33
+
34
+ # Loads the contents of a file
35
+ # @param [#to_s] file the file to load
36
+ # @return [String] the contents of the file
37
+ def file_load(file)
38
+ result = ""
39
+ File.open(file.to_s, 'r') do |f|
40
+ while l = f.gets
41
+ result << l
42
+ end
43
+ end
44
+ result
45
+ end
46
+
47
+ # Writes a string to a file
48
+ # @param [#to_s] file the file to write
49
+ # @param [String] contents the string to write
50
+ # @return [String] the string written to the file
51
+ def file_write(file, contents="")
52
+ File.open(file.to_s, 'w+') do |f|
53
+ f.print contents
54
+ end
55
+ contents
56
+ end
57
+
58
+ # An alias for FileUtils#cp
59
+ # @param [String] source the source file
60
+ # @param [String] dest the destination file or directory
61
+ # @param [Hash] options copy options
62
+ def file_copy(source, dest, options={})
63
+ FileUtils.cp source, dest, options
64
+ end
65
+
66
+ end
67
+
68
+ class MacroError < RuntimeError
69
+ attr_reader :node
70
+ def initialize(node, msg)
71
+ @node = node
72
+ source = @node[:source] || "--"
73
+ macros = []
74
+ @node.ascend {|n| macros << n[:macro].to_s if n[:macro] }
75
+ super("#{msg}\n -> source: #{source}\n -> path: #{macros.reverse.join('/')}")
76
+ end
77
+ end
data/macros/common.rb ADDED
@@ -0,0 +1,66 @@
1
+ #!/usr/bin/env ruby
2
+
3
+
4
+ macro :comment do
5
+ ""
6
+ end
7
+
8
+ macro :todo do
9
+ todo = "[#{@source}] -- #{@value}"
10
+ Glyph::TODOS << todo unless Glyph::TODOS.include? todo
11
+ ""
12
+ end
13
+
14
+ macro :snippet do
15
+ begin
16
+ ident = @params[0].to_sym
17
+ macro_error "Snippet '#{ident}' does not exist" unless Glyph::SNIPPETS.has_key? ident
18
+ interpret Glyph::SNIPPETS[ident]
19
+ rescue Exception => e
20
+ warning e.message
21
+ "[SNIPPET '#{@value}' NOT PROCESSED]"
22
+ end
23
+ end
24
+
25
+ macro :include do
26
+ begin
27
+ file = nil
28
+ (Glyph::PROJECT/"text").find do |f|
29
+ file = f if f.to_s.match /\/#{@value}$/
30
+ end
31
+ macro_error "File '#{@value}' no found." unless file
32
+ contents = file_load file
33
+ if cfg("filters.by_file_extension") then
34
+ begin
35
+ ext = @value.match(/\.(.*)$/)[1]
36
+ macro_error "Filter macro '#{ext}' not found" unless Glyph::MACROS.include?(ext.to_sym)
37
+ contents = "#{ext}[#{contents}]"
38
+ rescue MacroError => e
39
+ warning e.message
40
+ end
41
+ end
42
+ interpret contents
43
+ rescue MacroError => e
44
+ warning e
45
+ "[FILE '#{@value}' NOT FOUND]"
46
+ end
47
+ end
48
+
49
+ macro :ruby do
50
+ Kernel.instance_eval(@value)
51
+ end
52
+
53
+ macro :config do
54
+ cfg @value
55
+ end
56
+
57
+ macro :escape do
58
+ @value
59
+ end
60
+
61
+ macro_alias '--' => :comment
62
+ macro_alias '&' => :snippet
63
+ macro_alias '@' => :include
64
+ macro_alias '%' => :ruby
65
+ macro_alias '$' => :config
66
+ macro_alias '.' => :escape