glyph 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +80 -0
- data/Rakefile +51 -0
- data/VERSION +1 -0
- data/bin/glyph +7 -0
- data/book/config.yml +5 -0
- data/book/document.glyph +55 -0
- data/book/images/glyph.png +0 -0
- data/book/images/glyph.svg +351 -0
- data/book/lib/macros/reference.rb +98 -0
- data/book/output/html/glyph.html +1809 -0
- data/book/output/html/images/glyph.png +0 -0
- data/book/output/html/images/glyph.svg +351 -0
- data/book/output/pdf/glyph.pdf +4277 -0
- data/book/snippets.yml +13 -0
- data/book/styles/css3.css +220 -0
- data/book/styles/default.css +190 -0
- data/book/text/authoring.textile +351 -0
- data/book/text/extending.textile +148 -0
- data/book/text/getting_started.textile +152 -0
- data/book/text/introduction.textile +88 -0
- data/book/text/ref_commands.textile +74 -0
- data/book/text/ref_config.textile +0 -0
- data/book/text/ref_macros.textile +256 -0
- data/book/text/troubleshooting.textile +118 -0
- data/config.yml +63 -0
- data/document.glyph +29 -0
- data/glyph.gemspec +138 -0
- data/lib/glyph.rb +128 -0
- data/lib/glyph/commands.rb +124 -0
- data/lib/glyph/config.rb +152 -0
- data/lib/glyph/document.rb +145 -0
- data/lib/glyph/glyph_language.rb +530 -0
- data/lib/glyph/glyph_language.treetop +27 -0
- data/lib/glyph/interpreter.rb +84 -0
- data/lib/glyph/macro.rb +69 -0
- data/lib/glyph/node.rb +126 -0
- data/lib/glyph/system_extensions.rb +77 -0
- data/macros/common.rb +66 -0
- data/macros/filters.rb +69 -0
- data/macros/html/block.rb +119 -0
- data/macros/html/inline.rb +43 -0
- data/macros/html/structure.rb +138 -0
- data/spec/files/container.textile +5 -0
- data/spec/files/document.glyph +2 -0
- data/spec/files/document_with_toc.glyph +3 -0
- data/spec/files/included.textile +4 -0
- data/spec/files/ligature.jpg +449 -0
- data/spec/files/markdown.markdown +8 -0
- data/spec/files/test.sass +2 -0
- data/spec/lib/commands_spec.rb +83 -0
- data/spec/lib/config_spec.rb +79 -0
- data/spec/lib/document_spec.rb +100 -0
- data/spec/lib/glyph_spec.rb +76 -0
- data/spec/lib/interpreter_spec.rb +90 -0
- data/spec/lib/macro_spec.rb +60 -0
- data/spec/lib/node_spec.rb +76 -0
- data/spec/macros/filters_spec.rb +42 -0
- data/spec/macros/macros_spec.rb +159 -0
- data/spec/spec_helper.rb +92 -0
- data/spec/tasks/generate_spec.rb +31 -0
- data/spec/tasks/load_spec.rb +37 -0
- data/spec/tasks/project_spec.rb +41 -0
- data/styles/css3.css +220 -0
- data/styles/default.css +190 -0
- data/tasks/generate.rake +57 -0
- data/tasks/load.rake +55 -0
- data/tasks/project.rake +33 -0
- 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
|
+
|
data/lib/glyph/macro.rb
ADDED
@@ -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
|