glyph 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/AUTHORS.textile +8 -0
  2. data/CHANGELOG.textile +260 -0
  3. data/LICENSE.textile +26 -0
  4. data/README.textile +49 -21
  5. data/Rakefile +17 -7
  6. data/VERSION +1 -1
  7. data/book/config.yml +11 -5
  8. data/book/document.glyph +24 -13
  9. data/book/lib/macros/reference.rb +41 -14
  10. data/book/output/html/glyph.html +2298 -687
  11. data/book/output/pdf/glyph.pdf +6218 -2698
  12. data/book/script/authors +1 -0
  13. data/book/script/changelog +1 -0
  14. data/book/script/compile.rb +8 -0
  15. data/book/script/license +1 -0
  16. data/book/script/prof +1 -0
  17. data/book/script/prof_results.htm +21079 -0
  18. data/book/script/readme +1 -0
  19. data/book/snippets.yml +3 -4
  20. data/book/text/acknowledgement.glyph +8 -0
  21. data/book/text/authoring.glyph +548 -0
  22. data/book/text/changelog.glyph +76 -0
  23. data/book/text/extending.glyph +224 -0
  24. data/book/text/{getting_started.textile → getting_started.glyph} +30 -24
  25. data/book/text/{introduction.textile → introduction.glyph} +22 -12
  26. data/book/text/license.glyph +21 -0
  27. data/book/text/{ref_commands.textile → ref_commands.glyph} +30 -8
  28. data/book/text/ref_config.glyph +108 -0
  29. data/book/text/ref_macros.glyph +378 -0
  30. data/book/text/troubleshooting.glyph +179 -0
  31. data/config.yml +16 -4
  32. data/glyph.gemspec +83 -22
  33. data/lib/glyph.rb +164 -31
  34. data/lib/glyph/commands.rb +98 -23
  35. data/lib/glyph/document.rb +13 -7
  36. data/lib/glyph/glyph_language.rb +9 -1
  37. data/lib/glyph/glyph_language.treetop +1 -1
  38. data/lib/glyph/interpreter.rb +19 -9
  39. data/lib/glyph/macro.rb +88 -11
  40. data/lib/glyph/macro_validators.rb +48 -0
  41. data/lib/glyph/node.rb +13 -1
  42. data/lib/glyph/system_extensions.rb +0 -28
  43. data/macros/common.rb +125 -31
  44. data/macros/filters.rb +19 -13
  45. data/macros/html/block.rb +119 -68
  46. data/macros/html/inline.rb +29 -3
  47. data/macros/html/structure.rb +40 -40
  48. data/spec/files/article.glyph +5 -0
  49. data/spec/lib/commands_spec.rb +98 -3
  50. data/spec/lib/document_spec.rb +15 -2
  51. data/spec/lib/glyph_spec.rb +39 -10
  52. data/spec/lib/interpreter_spec.rb +8 -2
  53. data/spec/lib/macro_spec.rb +54 -6
  54. data/spec/lib/macro_validators_spec.rb +33 -0
  55. data/spec/lib/node_spec.rb +11 -3
  56. data/spec/macros/filters_spec.rb +5 -5
  57. data/spec/macros/macros_spec.rb +185 -8
  58. data/spec/macros/textile_spec.rb +217 -0
  59. data/spec/spec_helper.rb +25 -15
  60. data/spec/tasks/generate_spec.rb +3 -3
  61. data/spec/tasks/load_spec.rb +11 -1
  62. data/spec/tasks/project_spec.rb +0 -3
  63. data/styles/coderay.css +121 -0
  64. data/styles/default.css +54 -20
  65. data/{book/styles/css3.css → styles/pagination.css} +35 -7
  66. data/styles/ultraviolet/active4d.css +114 -0
  67. data/styles/ultraviolet/all_hallows_eve.css +72 -0
  68. data/styles/ultraviolet/amy.css +147 -0
  69. data/styles/ultraviolet/blackboard.css +88 -0
  70. data/styles/ultraviolet/brilliance_black.css +605 -0
  71. data/styles/ultraviolet/brilliance_dull.css +599 -0
  72. data/styles/ultraviolet/cobalt.css +149 -0
  73. data/styles/ultraviolet/dawn.css +121 -0
  74. data/styles/ultraviolet/eiffel.css +121 -0
  75. data/styles/ultraviolet/espresso_libre.css +109 -0
  76. data/styles/ultraviolet/idle.css +62 -0
  77. data/styles/ultraviolet/iplastic.css +80 -0
  78. data/styles/ultraviolet/lazy.css +73 -0
  79. data/styles/ultraviolet/mac_classic.css +123 -0
  80. data/styles/ultraviolet/magicwb_amiga.css +104 -0
  81. data/styles/ultraviolet/pastels_on_dark.css +188 -0
  82. data/styles/ultraviolet/slush_poppies.css +85 -0
  83. data/styles/ultraviolet/spacecadet.css +51 -0
  84. data/styles/ultraviolet/sunburst.css +180 -0
  85. data/styles/ultraviolet/twilight.css +137 -0
  86. data/styles/ultraviolet/zenburnesque.css +91 -0
  87. data/tasks/generate.rake +45 -26
  88. data/tasks/load.rake +21 -18
  89. data/tasks/project.rake +3 -1
  90. metadata +210 -41
  91. data/book/styles/default.css +0 -190
  92. data/book/text/authoring.textile +0 -351
  93. data/book/text/extending.textile +0 -148
  94. data/book/text/ref_config.textile +0 -0
  95. data/book/text/ref_macros.textile +0 -256
  96. data/book/text/troubleshooting.textile +0 -118
  97. data/styles/css3.css +0 -220
@@ -21,24 +21,92 @@ command :add do |c|
21
21
  end
22
22
 
23
23
  GLI.desc 'Compile the project'
24
- arg_name "[output_target]"
24
+ arg_name "[source_file]"
25
+ arg_name "[destination_file]"
25
26
  command :compile do |c|
26
27
  c.desc "Specify a glyph file to compile (default: document.glyph)"
27
28
  c.flag [:s, :source]
28
29
  c.desc "Specify the format of the output file (default: html)"
29
30
  c.flag [:f, :format]
31
+ c.desc "Auto-regenerate output on file changes"
32
+ c.switch :auto
30
33
  c.action do |global_options, options, args|
31
- Glyph.run 'load:config'
34
+ raise ArgumentError, "Too many arguments" if args.length > 2
35
+ Glyph.lite_mode = true unless args.blank?
36
+ Glyph.run! 'load:config'
37
+ original_config = Glyph::CONFIG.dup
32
38
  output_targets = Glyph::CONFIG.get('document.output_targets')
33
39
  target = nil
34
- Glyph.config_override('document.output', options[:f]) if options[:f]
35
- target = cfg('document.output')
40
+ Glyph['document.output'] = options[:f] if options[:f]
41
+ target = Glyph['document.output']
36
42
  target = nil if target.blank?
37
- target ||= cfg('filters.target')
38
- Glyph.config_override('document.source', options[:s]) if options[:s]
43
+ target ||= Glyph['filters.target']
44
+ Glyph['document.source'] = options[:s] if options[:s]
39
45
  raise ArgumentError, "Output target not specified" unless target
40
46
  raise ArgumentError, "Unknown output target '#{target}'" unless output_targets.include? target.to_sym
41
- Glyph.run "generate:#{target}"
47
+
48
+ # Lite mode
49
+ if Glyph.lite? then
50
+ source_file = Pathname.new args[0]
51
+ filename = source_file.basename(source_file.extname).to_s
52
+ destination_file = Pathname.new(args[1]) rescue nil
53
+ src_extension = Regexp.escape(source_file.extname)
54
+ dst_extension = ".#{Glyph['document.output']}"
55
+ destination_file ||= Pathname.new(source_file.to_s.gsub(/#{src_extension}$/, dst_extension))
56
+ raise ArgumentError, "Source file '#{source_file}' does not exist" unless source_file.exist?
57
+ raise ArgumentError, "Source and destination file are the same" if source_file.to_s == destination_file.to_s
58
+ Glyph['document.filename'] = filename
59
+ Glyph['document.source'] = source_file.to_s
60
+ Glyph['document.output_dir'] = destination_file.parent.to_s # System use only
61
+ Glyph['document.output_file'] = destination_file.basename.to_s # System use only
62
+ end
63
+ begin
64
+ Glyph.run "generate:#{target}"
65
+ rescue Exception => e
66
+ message = e.message
67
+ if Glyph.debug? then
68
+ message << "\n"+"-"*20+"[ Backtrace: ]"+"-"*20
69
+ message << "\n"+e.backtrace.join("\n")
70
+ message << "\n"+"-"*54
71
+ end
72
+ raise RuntimeError, message if Glyph.library?
73
+ Glyph.error message
74
+ end
75
+
76
+ # Auto-regeneration
77
+ if options[:auto] && !Glyph.lite? then
78
+ Glyph.lite_mode = false
79
+ begin
80
+ require 'directory_watcher'
81
+ rescue LoadError
82
+ raise RuntimeError, "DirectoryWatcher is not available. Install it with: gem install directory_watcher"
83
+ end
84
+ Glyph.info 'Auto-regeneration enabled'
85
+ Glyph.info 'Use ^C to interrupt'
86
+ glob = ['*.glyph', 'config.yml', 'images/**/*', 'lib/**/*', 'snippets.yml', 'styles/**/*', 'text/**/*']
87
+ dw = DirectoryWatcher.new(Glyph::PROJECT, :glob => glob, :interval => 1, :pre_load => true)
88
+ dw.add_observer do |*args|
89
+ puts "="*50
90
+ Glyph.info "Regeneration started: #{args.size} files changed"
91
+ Glyph.reset
92
+ begin
93
+ Glyph.run! "generate:#{target}"
94
+ rescue Exception => e
95
+ Glyph.error e.message
96
+ if Glyph.debug? then
97
+ puts "-"*20+"[ Backtrace: ]"+"-"*20
98
+ puts e.backtrace
99
+ pits "-"*54
100
+ end
101
+ end
102
+ end
103
+ dw.start
104
+ begin
105
+ sleep
106
+ rescue Interrupt
107
+ end
108
+ dw.stop
109
+ end
42
110
  end
43
111
  end
44
112
 
@@ -46,13 +114,13 @@ GLI.desc 'Display all project TODO items'
46
114
  command :todo do |c|
47
115
  c.action do |global_options, options, args|
48
116
  Glyph.run "generate:document"
49
- unless Glyph::TODOS.blank?
50
- info "*** TODOs: ***"
51
- Glyph::TODOS.each do |t|
52
- info t
117
+ unless Glyph.document.todos.blank?
118
+ Glyph.info "*** TODOs: ***"
119
+ Glyph.document.todos.each do |t|
120
+ Glyph.info t
53
121
  end
54
122
  else
55
- info "Nothing left to do."
123
+ Glyph.info "Nothing left to do."
56
124
  end
57
125
  end
58
126
  end
@@ -65,20 +133,27 @@ command :config do |c|
65
133
  c.action do |global_options,options,args|
66
134
  Glyph.run 'load:config'
67
135
  if options[:g] then
68
- cfg = Glyph::GLOBAL_CONFIG
136
+ config = Glyph::GLOBAL_CONFIG
69
137
  else
70
- cfg = Glyph::PROJECT_CONFIG
138
+ config = Glyph::PROJECT_CONFIG
71
139
  end
72
140
  case args.length
73
141
  when 0 then
74
142
  raise ArgumentError, "Too few arguments."
75
143
  when 1 then # read current config
76
- setting = cfg(args[0])
144
+ setting = Glyph[args[0]]
77
145
  raise RuntimeError, "Unknown setting '#{args[0]}'" if setting.blank?
78
- info Glyph::CONFIG.get(args[0])
146
+ Glyph.info setting
79
147
  when 2 then
80
- cfg.set args[0], args[1]
81
- Glyph.reset_config
148
+ # Remove all overrides
149
+ Glyph.config_reset
150
+ # Reload current project config
151
+ config.read
152
+ config.set args[0], args[1]
153
+ # Write changes to file
154
+ config.write
155
+ # Refresh configuration
156
+ Glyph.config_refresh
82
157
  else
83
158
  raise ArgumentError, "Too many arguments."
84
159
  end
@@ -90,7 +165,7 @@ pre do |global,command,options,args|
90
165
  # Return true to proceed; false to abourt and not call the
91
166
  # chosen command
92
167
  if global[:d] then
93
- Glyph::DEBUG = true
168
+ Glyph.debug_mode = true
94
169
  end
95
170
  if !command || command.name == :help then
96
171
  puts "====================================="
@@ -105,17 +180,17 @@ post do |global,command,options,args|
105
180
  end
106
181
 
107
182
  on_error do |exception|
108
- if exception.is_a? MacroError then
109
- #warning exception.message
110
- puts exception.message
183
+ if exception.is_a? Glyph::MacroError then
184
+ Glyph.warning exception.message
111
185
  false
112
186
  else
113
- if Glyph.const_defined? :DEBUG then
187
+ if Glyph.debug? then
114
188
  puts "Exception: #{exception.message}"
115
189
  puts "Backtrace:"
116
190
  exception.backtrace.each do |b|
117
191
  puts b
118
192
  end
193
+ Glyph.debug_mode = false
119
194
  end
120
195
  true
121
196
  end
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  module Glyph
2
4
 
3
5
  # The Glyph::Document class stores information about a document or a chunk of text
@@ -16,19 +18,20 @@ module Glyph
16
18
  ['\\|', '|']
17
19
  ]
18
20
 
19
- attr_reader :bookmarks, :placeholders, :headers, :context
21
+ attr_reader :bookmarks, :placeholders, :headers, :context, :errors, :todos
20
22
 
21
23
  # Creates a new document
22
24
  # @param [GlyphSyntaxNode] tree the syntax tree to be evaluate
23
25
  # @param [Glyph::Node] context the context associated with the tree
24
26
  # @raise [RuntimeError] unless tree responds to :evaluate
25
27
  def initialize(tree, context)
26
- raise RuntimeError, "Document contains syntax errors." unless tree.respond_to? :evaluate
27
28
  @tree = tree
28
29
  @context = context
29
30
  @bookmarks = {}
30
31
  @placeholders = {}
31
32
  @headers = []
33
+ @errors = []
34
+ @todos = []
32
35
  @state = :new
33
36
  end
34
37
 
@@ -40,11 +43,12 @@ module Glyph
40
43
  @tree.data
41
44
  end
42
45
 
43
- # Copies bookmarks, headers and placeholders from another Glyph::Document
46
+ # Copies bookmarks, headers, todos and placeholders from another Glyph::Document
44
47
  # @param [Glyph::Document] document a valid Glyph::Document
45
48
  def inherit_from(document)
46
49
  @bookmarks = document.bookmarks
47
50
  @headers = document.headers
51
+ @todos = document.todos
48
52
  @placeholders = document.placeholders
49
53
  end
50
54
 
@@ -101,18 +105,20 @@ module Glyph
101
105
 
102
106
  # Finalizes the document by evaluating its @placeholders
103
107
  # @return [:finalized]
104
- # @raise [RuntimeError] unless the document the document is analyzed or
105
- # if it is already finalized
108
+ # @raise [RuntimeError] if the document the document has not been analyzed,
109
+ # if it is already finalized or if errors occurred during analysis
106
110
  def finalize
107
111
  raise RuntimeError, "Document has not been analyzed" unless analyzed?
108
112
  raise RuntimeError, "Document has already been finalized" if finalized?
109
- return (@state = :finalized) if @context[:analyze_only]
113
+ return (@state = :finalized) if @context[:embedded]
114
+ raise RuntimeError, "Document cannot be finalized due to previous errors" unless @context[:document].errors.blank?
115
+ # Substitute placeholders
110
116
  ESCAPES.each{|e| @output.gsub! e[0], e[1]}
111
117
  @placeholders.each_pair do |key, value|
112
118
  begin
113
119
  @output.gsub! key.to_s, value.call(self).to_s
114
120
  rescue Exception => e
115
- warning e.message
121
+ Glyph.warning e.message
116
122
  end
117
123
  end
118
124
  @state = :finalized
@@ -49,6 +49,7 @@ module GlyphLanguage
49
49
  r0
50
50
  end
51
51
 
52
+ # @private
52
53
  module EscapingMacro0
53
54
  def macro_name
54
55
  elements[0]
@@ -96,7 +97,7 @@ module GlyphLanguage
96
97
  end
97
98
  end
98
99
  if s0.last
99
- r0 = instantiate_node(MacroNode,input, i0...index, s0)
100
+ r0 = instantiate_node(EscapingMacroNode,input, i0...index, s0)
100
101
  r0.extend(EscapingMacro0)
101
102
  else
102
103
  @index = i0
@@ -108,6 +109,7 @@ module GlyphLanguage
108
109
  r0
109
110
  end
110
111
 
112
+ # @private
111
113
  module Macro0
112
114
  def macro_name
113
115
  elements[0]
@@ -167,9 +169,11 @@ module GlyphLanguage
167
169
  r0
168
170
  end
169
171
 
172
+ # @private
170
173
  module EscapedText0
171
174
  end
172
175
 
176
+ # @private
173
177
  module EscapedText1
174
178
  def macro_name
175
179
  elements[0]
@@ -177,6 +181,7 @@ module GlyphLanguage
177
181
 
178
182
  end
179
183
 
184
+ # @private
180
185
  module EscapedText2
181
186
  end
182
187
 
@@ -345,9 +350,11 @@ module GlyphLanguage
345
350
  r0
346
351
  end
347
352
 
353
+ # @private
348
354
  module Text0
349
355
  end
350
356
 
357
+ # @private
351
358
  module Text1
352
359
  def macro_name
353
360
  elements[0]
@@ -355,6 +362,7 @@ module GlyphLanguage
355
362
 
356
363
  end
357
364
 
365
+ # @private
358
366
  module Text2
359
367
  end
360
368
 
@@ -5,7 +5,7 @@ grammar GlyphLanguage
5
5
  end
6
6
 
7
7
  rule escaping_macro
8
- macro_name '[=' text '=]' <MacroNode>
8
+ macro_name '[=' text '=]' <EscapingMacroNode>
9
9
  end
10
10
 
11
11
  rule macro
@@ -6,27 +6,29 @@ class GlyphSyntaxNode < Treetop::Runtime::SyntaxNode
6
6
  def evaluate(context, current=nil)
7
7
  current ||= context.to_node
8
8
  @data ||= current.to_node
9
- value = elements.map { |e| e.evaluate(context, current) if e.respond_to? :evaluate }.join
10
- value
9
+ elements.map { |e| e.evaluate(context, current) if e.respond_to? :evaluate }.join
11
10
  end
12
11
 
13
- end
12
+ end
14
13
 
15
14
  # @private
16
15
  class MacroNode < GlyphSyntaxNode
17
16
 
18
- def evaluate(context, current=nil)
17
+ def evaluate(context, current)
19
18
  name = macro_name.text_value.to_sym
20
- raise RuntimeError, "Undefined macro '#{name}'" unless Glyph::MACROS.include? name
19
+ raise Glyph::SyntaxError, "Undefined macro '#{name}'\n -> source: #{current[:source]}" unless Glyph::MACROS.include? name
21
20
  @data = {:macro => name, :source => context[:source], :document => context[:document]}.to_node
21
+ @data[:escape] = true if is_a? EscapingMacroNode
22
22
  current << @data
23
- value = super(context, @data).strip
24
- @data[:value] = value
23
+ @data[:value] = super(context, @data).strip
25
24
  Glyph::Macro.new(@data).execute
26
25
  end
27
26
 
28
27
  end
29
28
 
29
+ # @private
30
+ class EscapingMacroNode < MacroNode; end
31
+
30
32
  # @private
31
33
  class TextNode < GlyphSyntaxNode
32
34
 
@@ -45,14 +47,22 @@ module Glyph
45
47
  # * Analyzes and finalizes the document
46
48
  class Interpreter
47
49
 
50
+ PARSER = GlyphLanguageParser.new
51
+
48
52
  # Creates a new Glyph::Interpreter object.
49
53
  # @param [String] text the string to interpret
50
54
  # @param [Hash] context the context to pass along when evaluating macros
51
55
  def initialize(text, context=nil)
52
56
  context ||= {:source => '--'}
53
- @parser = GlyphLanguageParser.new
54
- @raw = @parser.parse text
57
+ @raw = PARSER.parse text
55
58
  @context = context
59
+ tf = PARSER.terminal_failures
60
+ if !@raw.respond_to?(:evaluate) then
61
+ reason = "Incorrect macro syntax"
62
+ err = "#{reason}\n -> #{@context[:source]} [Line #{PARSER.failure_line}, Column #{PARSER.failure_column}]"
63
+ @context[:document].errors << err if @context[:document] && !@context[:embedded]
64
+ raise Glyph::SyntaxError, err
65
+ end
56
66
  @document = Glyph::Document.new @raw, @context
57
67
  @document.inherit_from @context[:document] if @context[:document]
58
68
  end
data/lib/glyph/macro.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  module Glyph
2
4
 
3
5
  # A Macro object is instantiated by a Glyph::Interpreter whenever a macro is found in the parsed text.
@@ -5,6 +7,8 @@ module Glyph
5
7
  # useful methods to be used in macro definitions.
6
8
  class Macro
7
9
 
10
+ include Validators
11
+
8
12
  # Creates a new macro instance from a Node
9
13
  # @param [Node] node a node populated with macro data
10
14
  def initialize(node)
@@ -12,26 +16,97 @@ module Glyph
12
16
  @name = @node[:macro]
13
17
  @value = @node[:value]
14
18
  @source = @node[:source]
15
- esc = '‡‡‡‡‡ESCAPED¤PIPE‡‡‡‡‡'
16
- @params = @value.gsub(/\\\|/, esc).split('|').map{|p| p.strip.gsub esc, '|'}
19
+ @escaped_pipe = '‡‡‡‡‡ESCAPED¤PIPE‡‡‡‡‡'
20
+ end
21
+
22
+ # Parses the macro parameters (stripping values)
23
+ # @return [Array] the macro parameters
24
+ def params
25
+ return @params if @params
26
+ @params = @value.gsub(/\\\|/, @escaped_pipe).split('|').map{|p| p.strip.gsub @escaped_pipe, "\\|"}
27
+ end
28
+
29
+ # Parses the macro parameters (without stripping values)
30
+ # @return [Array] the macro parameters
31
+ def raw_params
32
+ return @raw_params if @raw_params
33
+ @params = @value.gsub(/\\\|/, @escaped_pipe).split('|').map{|p| p.gsub @escaped_pipe, "\\|"}
34
+ end
35
+
36
+ # Returns the "path" to the macro within the syntax tree.
37
+ # @return [String] the macro path
38
+ def path
39
+ macros = []
40
+ @node.ascend {|n| macros << n[:macro].to_s if n[:macro] }
41
+ macros.reverse.join('/')
42
+ end
43
+
44
+ # Returns a todo message to include in the document in case of errors.
45
+ # @param [String] message the message to include in the document
46
+ # @return [String] the resulting todo message
47
+ def macro_todo(message)
48
+ draft = Glyph['document.draft']
49
+ Glyph['document.draft'] = true unless draft
50
+ res = interpret "![#{message}]"
51
+ Glyph['document.draft'] = false unless draft
52
+ res
53
+ end
54
+
55
+ # Raises a macro error (preventing document post-processing)
56
+ # @param [String] msg the message to print
57
+ # @raise [Glyph::MacroError]
58
+ def macro_error(msg, klass=Glyph::MacroError)
59
+ src = @node[:source_name]
60
+ src ||= @node[:source]
61
+ src ||= "--"
62
+ message = "#{msg}\n -> source: #{src}\n -> path: #{path}"
63
+ @node[:document].errors << message
64
+ message += "\n -> value:\n#{"-"*54}\n#{@value}\n#{"-"*54}" if Glyph.debug?
65
+ raise klass, message
17
66
  end
18
67
 
19
68
  # Raises a macro error
20
69
  # @param [String] msg the message to print
21
- # @raise [MacroError]
22
- def macro_error(msg)
23
- raise MacroError.new @node, msg
70
+ # @raise [Glyph::MacroError]
71
+ def macro_warning(message)
72
+ src = @node[:source_name]
73
+ src ||= @node[:source]
74
+ src ||= "--"
75
+ Glyph.warning "#{message}\n -> source: #{src}\n -> path: #{path}"
76
+ message += %{\n -> value:\n#{"-"*54}\n#{@value}\n#{"-"*54}} if Glyph.debug?
24
77
  end
25
78
 
26
79
  # Instantiates a Glyph::Interpreter and interprets a string
27
80
  # @param [String] string the string to interpret
28
81
  # @return [String] the interpreted output
29
- # @raise [MacroError] in case of mutual macro inclusion (snippet, include macros)
82
+ # @raise [Glyph::MacroError] in case of mutual macro inclusion (snippet, include macros)
30
83
  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
84
+ @node[:source] = "#@name[#@value]"
85
+ @node[:source_name] = "#{@name}[...]"
86
+ macro_error "Mutual inclusion", Glyph::MutualInclusionError if @node.find_parent {|n| n[:source] == @node[:source] }
87
+ if @node[:escape] then
88
+ result = string
89
+ else
90
+ @node[:embedded] = true
91
+ result = Glyph::Interpreter.new(string, @node).document.output
92
+ end
93
+ result.gsub(/\\*([\[\]])/){"\\#$1"}
94
+ end
95
+
96
+ # Encodes all macros in a string so that it can be encoded
97
+ # (and interpreted) later on
98
+ # @param [String] string the string to encode
99
+ # @return [String] the encoded string
100
+ def encode(string)
101
+ string.gsub(/([\[\]\|])/) { "‡‡¤#{$1.bytes.to_a[0]}¤‡‡" }
102
+ end
103
+
104
+ # Decodes a previously encoded string
105
+ # so that it can be interpreted
106
+ # @param [String] string the string to decode
107
+ # @return [String] the decoded string
108
+ def decode(string)
109
+ string.gsub(/‡‡¤(91|93|124)¤‡‡/) { $1.to_i.chr }
35
110
  end
36
111
 
37
112
  # @see Glyph::Document#placeholder
@@ -61,7 +136,9 @@ module Glyph
61
136
 
62
137
  # Executes a macro definition in the context of self
63
138
  def execute
64
- instance_exec(@node, &Glyph::MACROS[@name]).to_s
139
+ res = instance_exec(@node, &Glyph::MACROS[@name]).to_s
140
+ res.gsub!(/\\*([\[\]\|])/){"\\#$1"}
141
+ res
65
142
  end
66
143
 
67
144
  end