maruku 0.4.1 → 0.4.2

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.
@@ -41,9 +41,11 @@ CharSource = CharSourceManual # faster! 58ms vs. 65ms
41
41
  class CharSourceManual
42
42
  include MaRuKu::Strings
43
43
 
44
- def initialize(s)
44
+ def initialize(s, parent=nil)
45
+ raise "Passed #{s.class}" if not s.kind_of? String
45
46
  @buffer = s
46
47
  @buffer_index = 0
48
+ @parent = parent
47
49
  end
48
50
 
49
51
  # Return current char as a FixNum (or nil).
@@ -140,7 +142,11 @@ class CharSourceManual
140
142
  end
141
143
 
142
144
  def describe
143
- describe_pos(@buffer, @buffer_index)
145
+ s = describe_pos(@buffer, @buffer_index)
146
+ if @parent
147
+ s += "\n\n" + @parent.describe
148
+ end
149
+ s
144
150
  end
145
151
  include SpanLevelParser
146
152
  end
@@ -24,6 +24,15 @@ module MaRuKu; module In; module Markdown; module BlockLevelParser
24
24
  include Helpers
25
25
  include MaRuKu::Strings
26
26
  include MaRuKu::In::Markdown::SpanLevelParser
27
+
28
+ class BlockContext < Array
29
+ def describe
30
+ n = 5
31
+ desc = size > n ? self[-n,n] : self
32
+ "Last #{n} elements: "+
33
+ desc.map{|x| "\n -" + x.inspect}.join
34
+ end
35
+ end
27
36
 
28
37
  # Splits the string and calls parse_lines_as_markdown
29
38
  def parse_text_as_markdown(text)
@@ -32,25 +41,23 @@ module MaRuKu; module In; module Markdown; module BlockLevelParser
32
41
  return parse_blocks(src)
33
42
  end
34
43
 
44
+ # Input is a LineSource
35
45
  def parse_blocks(src)
36
- output = [];
46
+ output = BlockContext.new
37
47
 
38
48
  # run state machine
39
49
  while src.cur_line
40
50
  # Prints detected type (useful for debugging)
41
- #puts "#{src.cur_line.md_type}|#{src.cur_line}"
51
+ # puts "#{src.cur_line.md_type}|#{src.cur_line}"
42
52
  case src.cur_line.md_type
43
53
  when :empty;
54
+ output.push :empty
44
55
  src.ignore_line
45
56
  when :ial
46
- m = /\s*\{([^\}]*)\}\s*/.match src.shift_line
47
- al = read_attribute_list(CharSource.new(m[1]), context=nil, break_on=[nil])
48
- if last = output.last
49
- last.al = al
50
- else
51
- maruku_error "An attribute list at beginning of context {#{al.to_md}}", src
52
- maruku_recover "I will ignore this AL: {#{al.to_md}}", src
53
- end
57
+ m = InlineAttributeList.match src.shift_line
58
+ content = m[1] || ""
59
+ src2 = CharSource.new(content, src)
60
+ interpret_extension(src2, output, [nil])
54
61
  when :ald
55
62
  output.push read_ald(src)
56
63
  when :text
@@ -61,7 +68,8 @@ module MaRuKu; module In; module Markdown; module BlockLevelParser
61
68
  output.push read_header12(src)
62
69
  elsif eventually_comes_a_def_list(src)
63
70
  definition = read_definition(src)
64
- if output.last && output.last.node_type == :definition_list
71
+ if output.last.kind_of?(MDElement) &&
72
+ output.last.node_type == :definition_list then
65
73
  output.last.children << definition
66
74
  else
67
75
  output.push md_el(:definition_list, [definition])
@@ -79,7 +87,8 @@ module MaRuKu; module In; module Markdown; module BlockLevelParser
79
87
  list_type = src.cur_line.md_type == :ulist ? :ul : :ol
80
88
  li = read_list_item(src)
81
89
  # append to current list if we have one
82
- if output.last && output.last.node_type == list_type
90
+ if output.last.kind_of?(MDElement) &&
91
+ output.last.node_type == list_type then
83
92
  output.last.children << li
84
93
  else
85
94
  output.push md_el(list_type, [li])
@@ -91,7 +100,7 @@ module MaRuKu; module In; module Markdown; module BlockLevelParser
91
100
  when :footnote_text; output.push read_footnote_text(src)
92
101
  when :ref_definition; output.push read_ref_definition(src)
93
102
  when :abbreviation; output.push read_abbreviation(src)
94
- when :xml_instr; output.push read_xml_instruction(src)
103
+ when :xml_instr; read_xml_instruction(src, output)
95
104
  # # these do not produce output
96
105
  when :metadata;
97
106
  maruku_error "Please use the new meta-data syntax: \n"+
@@ -104,7 +113,13 @@ module MaRuKu; module In; module Markdown; module BlockLevelParser
104
113
  src.shift_line
105
114
  end
106
115
  end
116
+
117
+ merge_ial(output, src, output)
118
+ output.delete_if {|x| x.kind_of?(MDElement) &&
119
+ x.node_type == :ial}
107
120
 
121
+ # get rid of empty line markers
122
+ output.delete_if {|x| x == :empty}
108
123
  # See for each list if we can omit the paragraphs and use li_span
109
124
  # TODO: do this after
110
125
  output.each do |c|
@@ -135,7 +150,7 @@ module MaRuKu; module In; module Markdown; module BlockLevelParser
135
150
  def read_ald(src)
136
151
  if (l=src.shift_line) =~ AttributeDefinitionList
137
152
  id = $1; al=$2;
138
- al = read_attribute_list(CharSource.new(al), context=nil, break_on=[nil])
153
+ al = read_attribute_list(CharSource.new(al,src), context=nil, break_on=[nil])
139
154
  self.ald[id] = al;
140
155
  return md_ald(id, al)
141
156
  else
@@ -152,7 +167,7 @@ module MaRuKu; module In; module Markdown; module BlockLevelParser
152
167
  if new_meta_data? and line =~ /^(.*)\{(.*)\}\s*$/
153
168
  line = $1.strip
154
169
  ial = $2
155
- al = read_attribute_list(CharSource.new(ial), context=nil, break_on=[nil])
170
+ al = read_attribute_list(CharSource.new(ial,src), context=nil, break_on=[nil])
156
171
  end
157
172
  text = parse_lines_as_span [ line ]
158
173
  level = src.cur_line.md_type == :header2 ? 2 : 1;
@@ -168,14 +183,14 @@ module MaRuKu; module In; module Markdown; module BlockLevelParser
168
183
  if new_meta_data? and line =~ /^(.*)\{(.*)\}\s*$/
169
184
  line = $1.strip
170
185
  ial = $2
171
- al = read_attribute_list(CharSource.new(ial), context=nil, break_on=[nil])
186
+ al = read_attribute_list(CharSource.new(ial,src), context=nil, break_on=[nil])
172
187
  end
173
188
  level = num_leading_hashes(line)
174
189
  text = parse_lines_as_span [strip_hashes(line)]
175
190
  return md_header(level, text, al)
176
191
  end
177
192
 
178
- def read_xml_instruction(src)
193
+ def read_xml_instruction(src, output)
179
194
  m = /^\s*<\?((\w+)\s*)?(.*)$/.match src.shift_line
180
195
  raise "BugBug" if not m
181
196
  target = m[2] || ''
@@ -190,7 +205,18 @@ module MaRuKu; module In; module Markdown; module BlockLevelParser
190
205
  end
191
206
  code.gsub!(/\?>\s*$/, '')
192
207
 
193
- return md_xml_instr(target, code)
208
+ if target == 'mrk' && MaRuKu::Globals[:unsafe_features]
209
+ result = safe_execute_code(self, code)
210
+ if result
211
+ if result.kind_of? String
212
+ raise "Not expected"
213
+ else
214
+ output.push *result
215
+ end
216
+ end
217
+ else
218
+ output.push md_xml_instr(target, code)
219
+ end
194
220
  end
195
221
 
196
222
  def read_raw_html(src)
@@ -84,8 +84,7 @@ Conversion happens using the `iconv` library.
84
84
  end
85
85
 
86
86
  =begin maruku_doc
87
- Attribute: exec
88
- Scope: document
87
+ Variable: Maruku::Globals[:unsafe_features]
89
88
  Summary: Enables execution of XML instructions.
90
89
 
91
90
  Disabled by default because of security concerns.
@@ -115,8 +114,10 @@ Disabled by default because of security concerns.
115
114
  already.push v
116
115
  expand_attribute_list(self.ald[v], result)
117
116
  else
118
- maruku_error "Circular reference: #{v} already seen\n"+
119
- already.inspect
117
+ already.push v
118
+ maruku_error "Circular reference between labels.\n\n"+
119
+ "Label #{v.inspect} calls itself via recursion.\nThe recursion is "+
120
+ (already.map{|x| x.inspect}.join(' => '))
120
121
  end
121
122
  else
122
123
  if not result[:unresolved_references]
@@ -133,31 +134,32 @@ Disabled by default because of security concerns.
133
134
  end
134
135
  end
135
136
 
137
+ def safe_execute_code(object, code)
138
+ begin
139
+ return object.instance_eval(code)
140
+ rescue Exception => e
141
+ maruku_error "Exception while executing this:\n"+
142
+ add_tabs(code, 1, ">")+
143
+ "\nThe error was:\n"+
144
+ add_tabs(e.inspect+"\n"+e.caller.join("\n"), 1, "|")
145
+ rescue RuntimeError => e
146
+ maruku_error "2: Exception while executing this:\n"+
147
+ add_tabs(code, 1, ">")+
148
+ "\nThe error was:\n"+
149
+ add_tabs(e.inspect, 1, "|")
150
+ rescue SyntaxError => e
151
+ maruku_error "2: Exception while executing this:\n"+
152
+ add_tabs(code, 1, ">")+
153
+ "\nThe error was:\n"+
154
+ add_tabs(e.inspect, 1, "|")
155
+ end
156
+ nil
157
+ end
158
+
136
159
  def execute_code_blocks
137
160
  self.each_element(:xml_instr) do |e|
138
161
  if e.target == 'maruku'
139
- puts e.attributes.inspect
140
- code = e.code
141
- result = nil
142
- begin
143
- e.instance_eval(code)
144
- rescue Exception => e
145
- maruku_error "Exception while executing this:\n"+
146
- add_tabs(code, 1, ">")+
147
- "\nThe error was:\n"+
148
- add_tabs(e.inspect+"\n"+e.caller.join("\n"), 1, "|")
149
- next
150
- rescue RuntimeError => e
151
- maruku_error "2: Exception while executing this:\n"+
152
- add_tabs(code, 1, ">")+
153
- "\nThe error was:\n"+
154
- add_tabs(e.inspect, 1, "|")
155
- rescue SyntaxError => e
156
- maruku_error "2: Exception while executing this:\n"+
157
- add_tabs(code, 1, ">")+
158
- "\nThe error was:\n"+
159
- add_tabs(e.inspect, 1, "|")
160
- end
162
+ result = safe_execute_code(e, e.code)
161
163
  if result.kind_of?(String)
162
164
  puts "Result is : #{result.inspect}"
163
165
  end
@@ -186,54 +186,26 @@ module MaRuKu; module In; module Markdown; module SpanLevelParser
186
186
  con.push_char src.shift_char
187
187
  end
188
188
  end
189
- when ?{ # inline attribute list
190
- if new_meta_data?
191
- src.ignore_char # opening {
192
- ial = md_ial(al=read_attribute_list(src, con, [?}]))
193
- src.ignore_char # closing }
194
-
195
- con.push_element ial
196
- else # normal text
197
- con.push_char src.shift_char
198
- end
189
+ when ?{ # extension
190
+ src.ignore_char # {
191
+ interpret_extension(src, con, [?}])
192
+ src.ignore_char # }
193
+
199
194
  when nil
200
195
  maruku_error ("Unclosed span (waiting for %s"+
201
196
  "#{exit_on_strings.inspect})") % [
202
197
  exit_on_chars ? "#{exit_on_chars.inspect} or" : ""],
203
198
  src,con
204
-
205
199
  break
206
200
  else # normal text
207
201
  con.push_char src.shift_char
208
202
  end # end case
209
203
  end # end while true
210
204
  con.push_string_if_present
211
-
212
- # Now we handle the IAL stuff
213
- # We need a helper
214
- def is_ial(e); e.kind_of? MDElement and e.node_type == :ial end
215
- # A IAL at the beginning is strange and we ignore it
216
- if false && is_ial(e=con.elements[0])
217
- "Attribute list at the beginning of span {#{e.to_md}}"
218
- tell_user "Ignoring {#{e.to_md}}"
219
- con.elements.shift
220
- end
221
- # Apply each IAL to the element before
222
- con.elements.each_with_index do |e, i| if is_ial(e) && i>= 1 then
223
- before = con.elements[i-1]
224
- if before.kind_of? MDElement
225
- #puts "Assigning #{e.ial} to #{before}"
226
- before.al = e.ial
227
- else
228
- maruku_error "This IAL: {#{e.ial.to_md}} seems to"+
229
- " refer to a string:\n"+
230
- before.inspect, src, con
231
- maruku_recover "Ignoring IAL: {#{e.ial.to_md}}", src, con
232
- end
233
- end end
234
205
 
235
- # Remove all IAL
236
- # con.elements.delete_if { |e| is_ial(e) }
206
+ # Assign IAL to elements
207
+ merge_ial(con.elements, src, con)
208
+
237
209
 
238
210
  # Remove leading space
239
211
  if (s = con.elements.first).kind_of? String
@@ -251,7 +223,8 @@ module MaRuKu; module In; module Markdown; module SpanLevelParser
251
223
 
252
224
  educated
253
225
  end
254
-
226
+
227
+
255
228
  def read_xml_instr_span(src, con)
256
229
  src.ignore_chars(2) # starting <?
257
230
 
@@ -274,6 +247,46 @@ module MaRuKu; module In; module Markdown; module SpanLevelParser
274
247
  con.push_element md_xml_instr(target, code)
275
248
  end
276
249
 
250
+ # Start: cursor on character **after** '{'
251
+ # End: curson on '}' or EOF
252
+ def interpret_extension(src, con, break_on_chars)
253
+ case src.cur_char
254
+ when ?:
255
+ src.ignore_char # :
256
+ extension_meta(src, con, break_on_chars)
257
+ when ?#, ?.
258
+ extension_meta(src, con, break_on_chars)
259
+ else
260
+ stuff = read_simple(src, escaped=[?}], break_on_chars, [])
261
+ if stuff =~ /^(\w+\s|[^\w])/
262
+ extension_id = $1.strip
263
+ if false
264
+ else
265
+ maruku_recover "I don't know what to do with extension '#{extension_id}'\n"+
266
+ "I will threat this:\n\t{#{stuff}} \n as meta-data.\n", src, con
267
+ extension_meta(src, con, break_on_chars)
268
+ end
269
+ else
270
+ maruku_recover "I will threat this:\n\t{#{stuff}} \n as meta-data.\n", src, con
271
+ extension_meta(src, con, break_on_chars)
272
+ end
273
+ end
274
+ end
275
+
276
+ def extension_meta(src, con, break_on_chars)
277
+ if m = src.read_regexp(/(\w)+\:/)
278
+ name = m[1]
279
+ content = m[2]
280
+ al = read_attribute_list(src, con, break_on_chars)
281
+ self.doc.ald[name] = al
282
+ con.push md_ald(name, al)
283
+ else
284
+ al = read_attribute_list(src, con, break_on_chars)
285
+ self.doc.ald[name] = al
286
+ con.push md_ial(al)
287
+ end
288
+ end
289
+
277
290
  def read_url_el(src,con)
278
291
  src.ignore_char # leading <
279
292
  url = read_simple(src, [], [?>])
@@ -340,14 +353,16 @@ module MaRuKu; module In; module Markdown; module SpanLevelParser
340
353
  # Reads a simple string (no formatting) until one of break_on_chars,
341
354
  # while escaping the escaped.
342
355
  # If the string is empty, it returns nil.
343
- # Raises on error.
344
- def read_simple(src, escaped, exit_on_chars, exit_on_strings=nil)
356
+ # Raises on error if the string terminates unexpectedly.
357
+ # # If eat_delim is true, and if the delim is not the EOF, then the delim
358
+ # # gets eaten from the stream.
359
+ def read_simple(src, escaped, exit_on_chars, exit_on_strings=nil)
345
360
  text = ""
346
361
  while true
347
362
  # puts "Reading simple #{text.inspect}"
348
363
  c = src.cur_char
349
364
  if exit_on_chars && exit_on_chars.include?(c)
350
- # puts (" breaking on "<<c)+" contained in "+exit_on_chars.inspect
365
+ # src.ignore_char if eat_delim
351
366
  break
352
367
  end
353
368
 
@@ -625,7 +640,8 @@ module MaRuKu; module In; module Markdown; module SpanLevelParser
625
640
  @elements << e
626
641
  nil
627
642
  end
628
-
643
+ alias push push_element
644
+
629
645
  def push_elements(a)
630
646
  for e in a
631
647
  if e.kind_of? String
@@ -62,14 +62,15 @@ module MaRuKu; module Strings
62
62
  return :metadata if l =~ /^@/
63
63
  # if @@new_meta_data?
64
64
  return :ald if l =~ AttributeDefinitionList
65
- return :ial if l =~ /^\s{0,3}\{.*\}/
65
+ return :ial if l =~ InlineAttributeList
66
66
  # end
67
67
  return :text # else, it's just text
68
68
  end
69
69
 
70
70
  # $1 = id $2 = attribute list
71
71
  AttributeDefinitionList = /^\s{0,3}\{([\w\d\s]+)\}:\s*(.*)\s*$/
72
-
72
+ #
73
+ InlineAttributeList = /^\s{0,3}\{(.*)\}\s*$/
73
74
  # Example:
74
75
  # ^:blah blah
75
76
  # ^: blah blah
@@ -346,7 +346,7 @@ module MaRuKu; module Out; module HTML
346
346
  end
347
347
 
348
348
  def to_html_code_using_pre(source)
349
- pre = Element.new 'pre'
349
+ pre = create_html_element 'pre'
350
350
  code = Element.new 'code', pre
351
351
  s = source
352
352
 
@@ -368,7 +368,7 @@ module MaRuKu; module Out; module HTML
368
368
  end
369
369
 
370
370
  def to_html_inline_code;
371
- pre = Element.new 'code'
371
+ pre = create_html_element 'code'
372
372
  source = self.raw_code
373
373
  pre << source2html(source)
374
374
 
@@ -381,7 +381,7 @@ module MaRuKu; module Out; module HTML
381
381
  end
382
382
 
383
383
  def to_html_immediate_link
384
- a = Element.new 'a'
384
+ a = create_html_element 'a'
385
385
  url = self.url
386
386
  text = url.gsub(/^mailto:/,'') # don't show mailto
387
387
  a << Text.new(text)
@@ -439,7 +439,7 @@ module MaRuKu; module Out; module HTML
439
439
 
440
440
  def to_html_email_address
441
441
  email = self.email
442
- a = Element.new 'a'
442
+ a = create_html_element 'a'
443
443
  #a.attributes['href'] = Text.new("mailto:"+obfuscate(email),false,nil,true)
444
444
  #a.attributes.add Attribute.new('href',Text.new(
445
445
  #"mailto:"+obfuscate(email),false,nil,true))
@@ -453,7 +453,7 @@ module MaRuKu; module Out; module HTML
453
453
  ##### Images
454
454
 
455
455
  def to_html_image
456
- a = Element.new 'img'
456
+ a = create_html_element 'img'
457
457
  id = self.ref_id
458
458
  if ref = @doc.refs[id]
459
459
  url = ref[:url]
@@ -478,7 +478,7 @@ module MaRuKu; module Out; module HTML
478
478
  return wrap_as_element('span')
479
479
  end
480
480
  title = self.title
481
- a = Element.new 'img'
481
+ a = create_html_element 'img'
482
482
  a.attributes['src'] = url
483
483
  a.attributes['title'] = title if title
484
484
  return a