glyph 0.2.0 → 0.3.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/AUTHORS.textile +1 -1
- data/CHANGELOG.textile +119 -222
- data/LICENSE.textile +1 -1
- data/README.textile +42 -23
- data/Rakefile +1 -3
- data/VERSION +1 -1
- data/benchmark.rb +72 -0
- data/book/config.yml +4 -4
- data/book/document.glyph +90 -57
- data/book/images/document_generation.png +0 -0
- data/book/lib/macros/reference.rb +75 -22
- data/book/output/html/glyph.html +3183 -2121
- data/book/output/html/images/document_generation.png +0 -0
- data/book/output/pdf/glyph.pdf +7370 -4913
- data/book/resources/document_generation.txt +34 -0
- data/book/snippets.yml +6 -0
- data/book/text/changelog.glyph +45 -34
- data/book/text/compiling/compiling.glyph +23 -0
- data/book/text/compiling/lite_mode.glyph +23 -0
- data/book/text/compiling/programmatic_usage.glyph +77 -0
- data/book/text/extending/bookmarks_headers.glyph +21 -0
- data/book/text/extending/further_reading.glyph +13 -0
- data/book/text/extending/internals.glyph +79 -0
- data/book/text/extending/interpreting.glyph +51 -0
- data/book/text/extending/macro_def.glyph +64 -0
- data/book/text/extending/params_attrs.glyph +70 -0
- data/book/text/extending/placeholders.glyph +34 -0
- data/book/text/extending/validators.glyph +16 -0
- data/book/text/getting_started/configuration.glyph +49 -0
- data/book/text/getting_started/create_project.glyph +41 -0
- data/book/text/getting_started/structure.glyph +55 -0
- data/book/text/introduction.glyph +49 -26
- data/book/text/license.glyph +1 -1
- data/book/text/macros/macros_block.glyph +99 -0
- data/book/text/macros/macros_core.glyph +208 -0
- data/book/text/macros/macros_filters.glyph +40 -0
- data/book/text/macros/macros_inline.glyph +50 -0
- data/book/text/macros/macros_structure.glyph +100 -0
- data/book/text/ref_commands.glyph +94 -73
- data/book/text/ref_config.glyph +34 -42
- data/book/text/ref_macros.glyph +1 -373
- data/book/text/text_editing/code.glyph +51 -0
- data/book/text/text_editing/conditionals.glyph +49 -0
- data/book/text/text_editing/evaluation.glyph +13 -0
- data/book/text/text_editing/glyph_files.glyph +7 -0
- data/book/text/text_editing/images.glyph +29 -0
- data/book/text/text_editing/inclusions.glyph +44 -0
- data/book/text/text_editing/links.glyph +53 -0
- data/book/text/text_editing/macro_intro.glyph +111 -0
- data/book/text/text_editing/raw_html.glyph +112 -0
- data/book/text/text_editing/sections.glyph +63 -0
- data/book/text/text_editing/stylesheets.glyph +36 -0
- data/book/text/troubleshooting/errors_command.glyph +39 -0
- data/book/text/troubleshooting/errors_generic.glyph +29 -0
- data/book/text/troubleshooting/errors_intro.glyph +3 -0
- data/book/text/troubleshooting/errors_macro.glyph +98 -0
- data/book/text/troubleshooting/errors_parser.glyph +29 -0
- data/config.yml +77 -58
- data/document.glyph +25 -25
- data/glyph.gemspec +57 -22
- data/lib/glyph.rb +54 -13
- data/lib/glyph/commands.rb +84 -17
- data/lib/glyph/config.rb +3 -3
- data/lib/glyph/document.rb +14 -8
- data/lib/glyph/interpreter.rb +18 -58
- data/lib/glyph/macro.rb +160 -55
- data/lib/glyph/macro_validators.rb +104 -12
- data/lib/glyph/node.rb +24 -0
- data/lib/glyph/parser.rb +278 -0
- data/lib/glyph/syntax_node.rb +225 -0
- data/macros/core.rb +212 -0
- data/macros/filters.rb +66 -15
- data/macros/html/block.rb +43 -105
- data/macros/html/inline.rb +11 -12
- data/macros/html/structure.rb +123 -58
- data/macros/xml.rb +33 -0
- data/spec/files/container.textile +2 -2
- data/spec/files/document.glyph +2 -2
- data/spec/files/document_with_toc.glyph +3 -3
- data/spec/files/included.textile +1 -1
- data/spec/files/ligature.jpg +0 -0
- data/spec/files/markdown.markdown +2 -1
- data/spec/lib/commands_spec.rb +46 -3
- data/spec/lib/document_spec.rb +4 -4
- data/spec/lib/glyph_spec.rb +17 -46
- data/spec/lib/interpreter_spec.rb +6 -25
- data/spec/lib/macro_spec.rb +141 -43
- data/spec/lib/macro_validators_spec.rb +27 -5
- data/spec/lib/node_spec.rb +26 -1
- data/spec/lib/parser_spec.rb +246 -0
- data/spec/lib/syntax_node_spec.rb +111 -0
- data/spec/macros/core_spec.rb +195 -0
- data/spec/macros/filters_spec.rb +38 -4
- data/spec/macros/macros_spec.rb +20 -176
- data/spec/macros/textile_spec.rb +13 -71
- data/spec/macros/xml_spec.rb +77 -0
- data/spec/spec_helper.rb +50 -10
- data/spec/tasks/load_spec.rb +13 -2
- data/styles/default.css +18 -6
- data/styles/pagination.css +1 -19
- data/tasks/generate.rake +2 -2
- data/tasks/load.rake +27 -17
- data/tasks/project.rake +1 -1
- metadata +75 -62
- data/book/script/compile.rb +0 -8
- data/book/script/prof +0 -1
- data/book/script/prof_results.htm +0 -21079
- data/book/text/authoring.glyph +0 -548
- data/book/text/extending.glyph +0 -224
- data/book/text/getting_started.glyph +0 -158
- data/book/text/troubleshooting.glyph +0 -179
- data/lib/glyph/glyph_language.rb +0 -538
- data/lib/glyph/glyph_language.treetop +0 -27
- data/macros/common.rb +0 -160
@@ -1,48 +1,140 @@
|
|
1
1
|
module Glyph
|
2
2
|
class Macro
|
3
3
|
|
4
|
+
# @since 0.2.0
|
4
5
|
module Validators
|
5
6
|
|
6
7
|
# Validates the macro according to the specified block
|
7
8
|
# @param [String] message the message to display if the validation fails.
|
8
|
-
# @param [Hash] options a hash containing validation options
|
9
|
+
# @param [Hash] options a hash containing validation options
|
10
|
+
# @option options :level the error level (:error, :warning)
|
11
|
+
# @return [Boolean] whether the validation passed or not
|
9
12
|
# @example
|
10
|
-
# validate("Invalid macro value", :level => :error) {
|
11
|
-
# validate("Invalid macro value", :level => :warning) {
|
13
|
+
# validate("Invalid macro value", :level => :error) {value == 'valid'} # Raises an error in case of failure
|
14
|
+
# validate("Invalid macro value", :level => :warning) {value == 'valid'} # Displays a warning in case of failure
|
12
15
|
def validate(message, options={:level => :error}, &block)
|
13
|
-
|
16
|
+
result = instance_eval(&block)
|
17
|
+
unless result then
|
14
18
|
send("macro_#{options[:level]}".to_sym, message)
|
15
19
|
end
|
20
|
+
result
|
21
|
+
end
|
22
|
+
|
23
|
+
# Ensures that the macro element attributes is a valid XML element name.
|
24
|
+
# @param [Hash] options a hash containing validation options (for now the only option is :level)
|
25
|
+
# @return [Boolean] whether the validation passed or not
|
26
|
+
# @since 0.3.0
|
27
|
+
def valid_xml_element(options={:level => :error})
|
28
|
+
validate("Invalid XML element '#{@node[:element]}'", options) { @node[:element].to_s.match(/^([^[:punct:]0-9<>]|_)[^<>"']*/) }
|
29
|
+
end
|
30
|
+
|
31
|
+
# Ensures that a macro attribute name is a valid XML attribute name.
|
32
|
+
# @param [String, Symbol] name the attribute name to validate
|
33
|
+
# @param [Hash] options a hash containing validation options
|
34
|
+
# @option options :level the error level (:error, :warning)
|
35
|
+
# @return [Boolean] whether the validation passed or not
|
36
|
+
# @since 0.3.0
|
37
|
+
def valid_xml_attribute(name, options={:level => :warning})
|
38
|
+
validate("Invalid XML attribute '#{name}'", options) { name.to_s.match(/^([^[:punct:]0-9<>]|_)[^<>"']*/) }
|
16
39
|
end
|
17
40
|
|
18
41
|
# Ensures that the macro receives up to _n_ parameters.
|
19
42
|
# @param [Integer] n the maximum number of parameters allowed for the macro.
|
20
|
-
# @param [Hash] options a hash containing validation options
|
43
|
+
# @param [Hash] options a hash containing validation options
|
44
|
+
# @option options :level the error level (:error, :warning)
|
45
|
+
# @return [Boolean] whether the validation passed or not
|
21
46
|
def max_parameters(n, options={:level=>:error})
|
22
|
-
validate("Macro '#{@name}' takes up to #{n} parameter(s) (#{params.length} given)", options)
|
47
|
+
validate("Macro '#{@name}' takes up to #{n} parameter(s) (#{@node.params.length} given)", options) do
|
48
|
+
if n == 0 then
|
49
|
+
no_parameters options
|
50
|
+
else
|
51
|
+
@node.params.length <= n
|
52
|
+
end
|
53
|
+
end
|
23
54
|
end
|
24
55
|
|
25
56
|
# Ensures that the macro receives at least _n_ parameters.
|
26
57
|
# @param [Integer] n the minimum number of parameters allowed for the macro.
|
27
|
-
# @param [Hash] options a hash containing validation options
|
58
|
+
# @param [Hash] options a hash containing validation options
|
59
|
+
# @option options :level the error level (:error, :warning)
|
60
|
+
# @return [Boolean] whether the validation passed or not
|
28
61
|
def min_parameters(n, options={:level=>:error})
|
29
|
-
validate("Macro '#{@name}' takes at least #{n} parameter(s) (#{params.length} given)", options) { params.length >= n }
|
62
|
+
validate("Macro '#{@name}' takes at least #{n} parameter(s) (#{@node.params.length} given)", options) { @node.params.length >= n }
|
30
63
|
end
|
31
64
|
|
32
65
|
# Ensures that the macro receives exactly _n_ parameters.
|
33
66
|
# @param [Integer] n the number of parameters allowed for the macro.
|
34
|
-
# @param [Hash] options a hash containing validation options
|
67
|
+
# @param [Hash] options a hash containing validation options
|
68
|
+
# @option options :level the error level (:error, :warning)
|
69
|
+
# @return [Boolean] whether the validation passed or not
|
35
70
|
def exact_parameters(n, options={:level=>:error})
|
36
|
-
validate("Macro '#{@name}' takes exactly #{n} parameter(s) (#{params.length} given)", options)
|
71
|
+
validate("Macro '#{@name}' takes exactly #{n} parameter(s) (#{@node.params.length} given)", options) do
|
72
|
+
if n == 0 then
|
73
|
+
no_parameters options
|
74
|
+
else
|
75
|
+
@node.params.length == n
|
76
|
+
end
|
77
|
+
end
|
37
78
|
end
|
38
79
|
|
39
80
|
# Ensures that the macro receives no parameters.
|
40
|
-
# @param [Hash] options a hash containing validation options
|
81
|
+
# @param [Hash] options a hash containing validation options
|
82
|
+
# @option options :level the error level (:error, :warning)
|
83
|
+
# @return [Boolean] whether the validation passed or not
|
41
84
|
def no_parameters(options={:level=>:error})
|
42
|
-
validate("Macro '#{@name}' takes no parameters (#{params.length} given)", options)
|
85
|
+
validate("Macro '#{@name}' takes no parameters (#{@node.params.length} given)", options) do
|
86
|
+
case @node.params.length
|
87
|
+
when 0 then
|
88
|
+
true
|
89
|
+
when 1 then
|
90
|
+
result = true
|
91
|
+
@node.param(0).children.each do |p|
|
92
|
+
result = p.is_a?(Glyph::TextNode) && p[:value].blank?
|
93
|
+
break unless result
|
94
|
+
end
|
95
|
+
result
|
96
|
+
else
|
97
|
+
false
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Raises a macro error if Glyph is running in safe mode.
|
103
|
+
# @raise [Glyph::MacroError] the macro cannot be used allowed in safe mode
|
104
|
+
# @since 0.3.0
|
105
|
+
def safety_check
|
106
|
+
macro_error "Macro '#@name' cannot be used in safe mode" if Glyph.safe?
|
43
107
|
end
|
44
108
|
|
109
|
+
# Ensure that no mutual inclusion occurs within the specified parameter or attribute
|
110
|
+
# @param [Fixnum, Symbol] the parameter index or attribute name to check
|
111
|
+
# @raise [Glyph::MacroError] mutual inclusion was detected
|
112
|
+
# @since 0.3.0
|
113
|
+
def no_mutual_inclusion_in(arg)
|
114
|
+
check_type = arg.is_a?(Symbol) ? :attribute : :parameter
|
115
|
+
check_value = nil
|
116
|
+
found = @node.find_parent do |n|
|
117
|
+
if n.is_a?(Glyph::MacroNode) && Glyph::MACROS[n[:name]] == Glyph::MACROS[@name] then
|
118
|
+
case check_type
|
119
|
+
when :attribute then
|
120
|
+
check_value = n.children.select do |node|
|
121
|
+
node.is_a?(Glyph::AttributeNode) && node[:name] == arg
|
122
|
+
end[0][:value] rescue nil
|
123
|
+
check_value == attr(arg)
|
124
|
+
when :parameter then
|
125
|
+
check_value = n.children.select do |node|
|
126
|
+
node.is_a?(Glyph::ParameterNode) && node[:name] == :"#{arg}"
|
127
|
+
end[0][:value] rescue nil
|
128
|
+
check_value == param(arg)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
if found then
|
133
|
+
macro_error "Mutual Inclusion in #{check_type}(#{arg}): '#{check_value}'", Glyph::MutualInclusionError
|
134
|
+
end
|
135
|
+
end
|
45
136
|
end
|
46
137
|
|
47
138
|
end
|
139
|
+
|
48
140
|
end
|
data/lib/glyph/node.rb
CHANGED
@@ -135,4 +135,28 @@ class Node < Hash
|
|
135
135
|
ascend(parent) {|e| return e unless e.parent }
|
136
136
|
end
|
137
137
|
|
138
|
+
# Converts self to a hash
|
139
|
+
# @return [Hash] the converted hash
|
140
|
+
# @since 0.3.0
|
141
|
+
def to_hash
|
142
|
+
{}.merge(self)
|
143
|
+
end
|
144
|
+
|
145
|
+
# @return [String] a textual representation of self
|
146
|
+
# @since 0.3.0
|
147
|
+
def inspect
|
148
|
+
string = ""
|
149
|
+
descend do |e, level|
|
150
|
+
string << " "*level+e.to_hash.inspect+"\n"
|
151
|
+
end
|
152
|
+
string.chomp
|
153
|
+
end
|
154
|
+
|
155
|
+
# @return (Boolean) true if the nodes are equal
|
156
|
+
# @since 0.3.0
|
157
|
+
def ==(node)
|
158
|
+
return false unless node.is_a? Node
|
159
|
+
self.to_hash == node.to_hash && self.children == node.children
|
160
|
+
end
|
161
|
+
|
138
162
|
end
|
data/lib/glyph/parser.rb
ADDED
@@ -0,0 +1,278 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'strscan'
|
3
|
+
|
4
|
+
module Glyph
|
5
|
+
|
6
|
+
# The Glyph::Parser class can parse a string of text containing Glyph macros and produce the
|
7
|
+
# corresponding syntax tree.
|
8
|
+
# @since 0.3.0
|
9
|
+
class Parser
|
10
|
+
|
11
|
+
# Initializes the parser.
|
12
|
+
# @param [String] text the text to parse
|
13
|
+
# @param [String] source_name the name of the source file (stored in the root node)
|
14
|
+
# @since 0.3.0
|
15
|
+
def initialize(text, source_name="--")
|
16
|
+
@source_name = source_name || "--"
|
17
|
+
@input = StringScanner.new text
|
18
|
+
@output = create_node DocumentNode, :name => @source_name.to_sym
|
19
|
+
@current_macro = nil
|
20
|
+
@current_attribute = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
# Parses the string of text provided during initialization
|
24
|
+
# @return [Glyph::SyntaxNode] the Abstract Syntax Tree corresponding to the string
|
25
|
+
# @since 0.3.0
|
26
|
+
def parse
|
27
|
+
count = 0
|
28
|
+
while result = parse_contents(@output) do
|
29
|
+
@output << result
|
30
|
+
count +=1
|
31
|
+
end
|
32
|
+
if @input.pos < @input.string.length then
|
33
|
+
current_char = @input.string[@input.pos].chr
|
34
|
+
illegal_delimiter = current_char.match(/\]|\[/) rescue nil
|
35
|
+
error "Macro delimiter '#{current_char}' not escaped" if illegal_delimiter
|
36
|
+
end
|
37
|
+
@output
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
def parse_contents(current)
|
43
|
+
escape_sequence(current) ||
|
44
|
+
parameter_delimiter(current) ||
|
45
|
+
escaping_attribute(current) ||
|
46
|
+
escaping_macro(current) ||
|
47
|
+
attribute(current) ||
|
48
|
+
macro(current) ||
|
49
|
+
text(current)
|
50
|
+
end
|
51
|
+
|
52
|
+
def parse_escaped_contents(current)
|
53
|
+
escape_sequence(current) || parameter_delimiter(current) || escaped_text(current)
|
54
|
+
end
|
55
|
+
|
56
|
+
def escaping_macro(current)
|
57
|
+
if @input.scan(/[^\[\]\|\\\s]+\[\=/) then
|
58
|
+
name = @input.matched
|
59
|
+
name.chop!
|
60
|
+
name.chop!
|
61
|
+
error "#{name}[...] - A macro cannot start with '@' or a digit." if name.match(/^[0-1@]/)
|
62
|
+
node = create_node(MacroNode, {
|
63
|
+
:name => name.to_sym,
|
64
|
+
:escape => true,
|
65
|
+
:attributes => [],
|
66
|
+
:parameters => []
|
67
|
+
})
|
68
|
+
while contents = parse_escaped_contents(node) do
|
69
|
+
node << contents unless contents.is_a?(AttributeNode)
|
70
|
+
end
|
71
|
+
@input.scan(/\=\]/) or error "Escaping macro '#{name}' not closed"
|
72
|
+
organize_children_for node
|
73
|
+
node
|
74
|
+
else
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def escaping_attribute(current)
|
80
|
+
if @input.scan(/@[^\[\]\|\\\s]+\[\=/) then
|
81
|
+
error "Attributes cannot be nested" if @current_attribute
|
82
|
+
name = @input.matched[1..@input.matched.length-3]
|
83
|
+
node = create_node(AttributeNode, {
|
84
|
+
:escape => true,
|
85
|
+
:name => name.to_sym
|
86
|
+
})
|
87
|
+
while contents = parse_escaped_contents(node) do
|
88
|
+
node << contents
|
89
|
+
end
|
90
|
+
current[:attributes] << node
|
91
|
+
@input.scan(/\=\]/) or error "Attribute @#{name} not closed"
|
92
|
+
node
|
93
|
+
else
|
94
|
+
nil
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def macro(current)
|
99
|
+
if @input.scan(/[^\[\]\|\\\s]+\[/) then
|
100
|
+
name = @input.matched
|
101
|
+
name.chop!
|
102
|
+
error "#{name}[...] - A macro cannot start with '@' or a digit." if name.match(/^[0-1@]/)
|
103
|
+
node = create_node(MacroNode, {
|
104
|
+
:escape => false,
|
105
|
+
:name => name.to_sym,
|
106
|
+
:attributes => [],
|
107
|
+
:parameters => []
|
108
|
+
})
|
109
|
+
while contents = parse_contents(node) do
|
110
|
+
node << contents unless contents.is_a?(AttributeNode)
|
111
|
+
end
|
112
|
+
@input.scan(/\]/) or error "Macro '#{name}' not closed"
|
113
|
+
organize_children_for node
|
114
|
+
node
|
115
|
+
else
|
116
|
+
nil
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def attribute(current)
|
121
|
+
if @input.scan(/@[^\[\]\|\\\s]+\[/) then
|
122
|
+
error "Attributes cannot be nested" if current.is_a?(AttributeNode)
|
123
|
+
name = @input.matched[1..@input.matched.length-2]
|
124
|
+
node = create_node(AttributeNode, {
|
125
|
+
:escape => false,
|
126
|
+
:name => name.to_sym
|
127
|
+
})
|
128
|
+
while contents = parse_contents(node) do
|
129
|
+
node << contents
|
130
|
+
end
|
131
|
+
current[:attributes] << node
|
132
|
+
@input.scan(/\]/) or error "Attribute @#{name} not closed"
|
133
|
+
node
|
134
|
+
else
|
135
|
+
nil
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def text(current)
|
140
|
+
start_p = @input.pos
|
141
|
+
res = @input.scan_until /(\\.)|(\A(\]|\|)|[^\\](\]|\|)|[^\[\]\|\\\s]+\[|\Z)/
|
142
|
+
offset = @input.matched.match(/^[^\\](\]|\|)$/) ? 1 : @input.matched.length
|
143
|
+
@input.pos = @input.pos - offset rescue @input.pos
|
144
|
+
return nil if @input.pos == start_p
|
145
|
+
match = @input.string[start_p..@input.pos-1]
|
146
|
+
illegal_macro_delimiter? start_p, match
|
147
|
+
if match.length > 0 then
|
148
|
+
create_node TextNode, :value => match
|
149
|
+
else
|
150
|
+
nil
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def escaped_text(current)
|
155
|
+
start_p = @input.pos
|
156
|
+
res = @input.scan_until /(\\.)|(\A(\=\]|\|)|[^\\](\=\]|\|)|\Z)/
|
157
|
+
case
|
158
|
+
when @input.matched.match(/^[^\\]\=\]$/) then
|
159
|
+
offset = 2
|
160
|
+
when @input.matched.match(/^[^\\]\|$/) then
|
161
|
+
offset = 1
|
162
|
+
else
|
163
|
+
offset = @input.matched.length
|
164
|
+
end
|
165
|
+
@input.pos = @input.pos - offset rescue @input.pos
|
166
|
+
return nil if @input.pos == start_p
|
167
|
+
match = @input.string[start_p..@input.pos-1]
|
168
|
+
illegal_nesting = match.match(/([^\[\]\|\\\s]+)\[\=/)[1] rescue nil
|
169
|
+
if illegal_nesting then
|
170
|
+
error "Cannot nest escaping macro '#{illegal_nesting}' within escaping macro '#{current[:name]}'"
|
171
|
+
end
|
172
|
+
if match.length > 0 then
|
173
|
+
create_node TextNode, :value => match, :escaped => true
|
174
|
+
else
|
175
|
+
nil
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def parameter_delimiter(current)
|
180
|
+
if @input.scan(/\|/) then
|
181
|
+
# Parameters are not allowed outside macros or inside attributes
|
182
|
+
if current.is_a?(DocumentNode) || current.is_a?(AttributeNode) then
|
183
|
+
@input.pos = @input.pos-1
|
184
|
+
error "Parameter delimiter '|' not allowed here"
|
185
|
+
end
|
186
|
+
create_node SyntaxNode, :parameter => true
|
187
|
+
else
|
188
|
+
nil
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def escape_sequence(current)
|
193
|
+
if @input.scan(/\\./) then
|
194
|
+
create_node EscapeNode, :value => @input.matched, :escaped => true
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
private
|
199
|
+
|
200
|
+
def aggregate_parameters_for(node)
|
201
|
+
indices = []
|
202
|
+
count = 0
|
203
|
+
node.children.each do |n|
|
204
|
+
indices << count if n[:parameter]
|
205
|
+
count += 1
|
206
|
+
end
|
207
|
+
# No parameter found
|
208
|
+
if indices == [] then
|
209
|
+
node[:parameters][0] = create_node ParameterNode, :name => :"0"
|
210
|
+
node.children.each do |c|
|
211
|
+
node[:parameters][0] << c
|
212
|
+
end
|
213
|
+
else
|
214
|
+
# Parameters found
|
215
|
+
current_index = 0
|
216
|
+
total_parameters = 0
|
217
|
+
save_parameter = lambda do |max_index|
|
218
|
+
parameter = create_node ParameterNode, :name => "#{total_parameters}".to_sym
|
219
|
+
total_parameters +=1
|
220
|
+
current_index.upto(max_index) do |index|
|
221
|
+
parameter << (node & index)
|
222
|
+
end
|
223
|
+
node[:parameters] << parameter
|
224
|
+
end
|
225
|
+
indices.each do |i|
|
226
|
+
save_parameter.call(i-1)
|
227
|
+
current_index = i+1
|
228
|
+
end
|
229
|
+
save_parameter.call(node.children.length-1)
|
230
|
+
end
|
231
|
+
node[:parameters]
|
232
|
+
end
|
233
|
+
|
234
|
+
def organize_children_for(node)
|
235
|
+
aggregate_parameters_for node
|
236
|
+
node.children.clear
|
237
|
+
node[:parameters].each do |p|
|
238
|
+
node << p
|
239
|
+
end
|
240
|
+
empty_parameter =
|
241
|
+
node.children.length == 1 &&
|
242
|
+
((node&0).children.length == 0 ||
|
243
|
+
(node&0).children.length == 0 &&
|
244
|
+
(node&0&0).is_a?(TextNode) &&
|
245
|
+
(node&0&0)[:value].blank?)
|
246
|
+
node.children.clear if empty_parameter
|
247
|
+
node.delete(:parameters)
|
248
|
+
node[:attributes].each do |a|
|
249
|
+
node << a
|
250
|
+
end
|
251
|
+
node.delete(:attributes)
|
252
|
+
end
|
253
|
+
|
254
|
+
def illegal_macro_delimiter?(start_p, string)
|
255
|
+
string.match(/\A(\[|\])|[^\\](\[|\])/)
|
256
|
+
illegal_delimiter = $1 || $2
|
257
|
+
if illegal_delimiter then
|
258
|
+
@input.pos = start_p + string.index(illegal_delimiter)
|
259
|
+
error "Macro delimiter '#{illegal_delimiter}' not escaped"
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def error(msg)
|
264
|
+
lines = @input.string[0..@input.pos].split(/\n/)
|
265
|
+
line = lines.length
|
266
|
+
column = lines.last.length
|
267
|
+
raise Glyph::SyntaxError.new("#{@source_name} [#{line}, #{column}] "+msg)
|
268
|
+
end
|
269
|
+
|
270
|
+
def create_node(klass, hash={})
|
271
|
+
klass.new.from hash
|
272
|
+
end
|
273
|
+
|
274
|
+
end
|
275
|
+
|
276
|
+
end
|
277
|
+
|
278
|
+
|