glyph 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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