maruku 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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