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.
- 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
|