marker 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +674 -0
- data/README +98 -0
- data/bin/marker +59 -0
- data/lib/marker/common.rb +40 -0
- data/lib/marker/headings.rb +79 -0
- data/lib/marker/language.treetop +679 -0
- data/lib/marker/links.rb +153 -0
- data/lib/marker/lists.rb +188 -0
- data/lib/marker/markup.rb +81 -0
- data/lib/marker/templates.rb +105 -0
- data/lib/marker/text.rb +96 -0
- data/lib/marker/verbatim.rb +48 -0
- data/lib/marker.rb +80 -0
- metadata +77 -0
data/README
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
== Marker
|
2
|
+
|
3
|
+
Marker is a markup language parser designed for two needs:
|
4
|
+
# Need to mimic MediaWiki syntax
|
5
|
+
# Need to provide multiple output formats
|
6
|
+
|
7
|
+
Mimicing MediaWiki syntax is not exact. One reason is that the MediaWiki
|
8
|
+
parser itself is very complicated and handles many cases specially. It would
|
9
|
+
be very difficult to exactly copy the MediaWiki parser and it probably wouldn't
|
10
|
+
be worth the time because MediaWiki is intended for a wiki and needs to be
|
11
|
+
adapted to be used as a markup lanaguage--especially for multiple output
|
12
|
+
formats. The purpose of mimicing MediaWiki syntax is so that users don't have
|
13
|
+
to learn more than one markup language, so the implementation doesn't *need* to
|
14
|
+
be exact anyway.
|
15
|
+
|
16
|
+
Marker differs from MediaWiki in several ways, because it is a grammar-based
|
17
|
+
implementation. The grammar is written as a
|
18
|
+
Treetop[http://treetop.rubyforge.org/] parsing expression grammar (PEG).
|
19
|
+
|
20
|
+
Not implemented:
|
21
|
+
# Table of contents
|
22
|
+
# Tables
|
23
|
+
|
24
|
+
== Use
|
25
|
+
|
26
|
+
Parsing is done with either Marker.parse or Marker.parse_file. Both parse
|
27
|
+
methods will return a parse tree that has to_html and to_s methods that
|
28
|
+
"render" the markup. Both render methods will accept an options hash.
|
29
|
+
|
30
|
+
Example:
|
31
|
+
>> require 'marker'
|
32
|
+
=> true
|
33
|
+
>> m = Marker.parse "== heading ==\nparagraph with '''bold''' text"
|
34
|
+
=> Markup+...
|
35
|
+
>> puts m.to_s
|
36
|
+
heading
|
37
|
+
--------------------------------------------------------------------------------
|
38
|
+
paragraph with *bold* text
|
39
|
+
=> nil
|
40
|
+
>> puts m.to_html
|
41
|
+
<h2>heading</h2>
|
42
|
+
<p>paragraph with <b>bold</b> text</p>
|
43
|
+
=> nil
|
44
|
+
|
45
|
+
=== Templates
|
46
|
+
|
47
|
+
Templates are implemented as method calls to a templates module. Each method
|
48
|
+
in the templates module is considered a template and can be called using the
|
49
|
+
"{{ name }}" syntax. Each template method is expected to take three arguments:
|
50
|
+
the render format (:html or :text), an array of positional parameters, and a
|
51
|
+
hash of named parameters. For example,
|
52
|
+
module MyTemplates
|
53
|
+
def logo( format, pos_params, name_params )
|
54
|
+
case format
|
55
|
+
when :html
|
56
|
+
'<img src="/images/logo.png" />'
|
57
|
+
else
|
58
|
+
''
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
Template modules are passed to Marker by setting the +templates+ property:
|
64
|
+
require 'my_templates'
|
65
|
+
require 'marker'
|
66
|
+
|
67
|
+
Marker.templates = Templates
|
68
|
+
|
69
|
+
If no template method is found, the template call is printed for debugging:
|
70
|
+
>> puts Marker.parse( '{{t|one|two|name=val}}' ).to_s
|
71
|
+
render:t( :text, ["one", "two"], {"name"=>"val"} )
|
72
|
+
|
73
|
+
=== Internal Links
|
74
|
+
|
75
|
+
Internal links are implemented as links with default prefixes. The link prefix
|
76
|
+
is specified by setting the +link_base+ property:
|
77
|
+
require 'marker'
|
78
|
+
|
79
|
+
Marker.link_base = 'http://example.com/pages/'
|
80
|
+
|
81
|
+
>> puts Marker.parse( '[[target|name]]' ).to_html
|
82
|
+
<p><a href='http://example.com/pages/target'>name</a></p>
|
83
|
+
|
84
|
+
The link target is appended to the link prefix, along with a beginning '/'. If
|
85
|
+
no link base is given, links are just the link target with a beginning '/'.
|
86
|
+
The link base can also be given as a render option.
|
87
|
+
|
88
|
+
=== Unlabelled Links
|
89
|
+
|
90
|
+
(write me)
|
91
|
+
|
92
|
+
== Command Line Program
|
93
|
+
|
94
|
+
== License
|
95
|
+
|
96
|
+
Marker is copyright 2009 Ryan Blue and distributed under the terms of the GNU
|
97
|
+
General Public License (GPL). See the LICENSE file for further information on
|
98
|
+
the GPL, or visit http://creativecommons.org/licenses/GPL/2.0/.
|
data/bin/marker
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'ostruct'
|
5
|
+
require 'rubygems'
|
6
|
+
|
7
|
+
begin
|
8
|
+
require 'marker'
|
9
|
+
rescue LoadError
|
10
|
+
$: << File.join( File.dirname(__FILE__), '..', 'lib' )
|
11
|
+
require 'marker'
|
12
|
+
end
|
13
|
+
|
14
|
+
options = OpenStruct.new
|
15
|
+
options.format = :text
|
16
|
+
OptionParser.new do |opts|
|
17
|
+
opts.on( '--format FORMAT', [:text, :html],
|
18
|
+
'Specify the output format (text, html)' ) do |format|
|
19
|
+
options.format = format
|
20
|
+
end
|
21
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
22
|
+
puts opts
|
23
|
+
exit
|
24
|
+
end
|
25
|
+
end.parse!
|
26
|
+
|
27
|
+
files = {}
|
28
|
+
|
29
|
+
if ARGV.empty? or ARGV.include? '-'
|
30
|
+
s = ''
|
31
|
+
# the parser expects a full block of text, grab it all
|
32
|
+
$stdin.each_line { |l| s << l }
|
33
|
+
files[:stdin] = Marker.parse( s )
|
34
|
+
end
|
35
|
+
|
36
|
+
ARGV.each do |f|
|
37
|
+
next if f == '-'
|
38
|
+
files[f] = Marker.parse_file( f )
|
39
|
+
end
|
40
|
+
|
41
|
+
if files.size > 1
|
42
|
+
files.each do |f, t|
|
43
|
+
puts "\n==> #{f} <=="
|
44
|
+
case options.format
|
45
|
+
when :html
|
46
|
+
puts t.to_html
|
47
|
+
else
|
48
|
+
puts t
|
49
|
+
end
|
50
|
+
end
|
51
|
+
else
|
52
|
+
# only one, so leave out the file name
|
53
|
+
case options.format
|
54
|
+
when :html
|
55
|
+
puts files.values.first.to_html
|
56
|
+
else
|
57
|
+
puts files.values.first
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2009 Ryan Blue.
|
3
|
+
# Distributed under the terms of the GNU General Public License (GPL).
|
4
|
+
# See the LICENSE file for further information on the GPL.
|
5
|
+
#++
|
6
|
+
|
7
|
+
require 'treetop'
|
8
|
+
|
9
|
+
module Treetop #:nodoc:
|
10
|
+
module Runtime #:nodoc:
|
11
|
+
class SyntaxNode #:nodoc:
|
12
|
+
# returns whether the ParseNode matched any text
|
13
|
+
def present?
|
14
|
+
text_value.any?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module Marker #:nodoc:
|
21
|
+
# used to add methods to all parse nodes.
|
22
|
+
class ParseNode < Treetop::Runtime::SyntaxNode
|
23
|
+
end
|
24
|
+
|
25
|
+
# implements collection methods on a list using a recursive grammar definition
|
26
|
+
# requires defining +h+ and +r+
|
27
|
+
class RecursiveList < ParseNode
|
28
|
+
def to_a
|
29
|
+
if r
|
30
|
+
[h] + r.to_a
|
31
|
+
else
|
32
|
+
[h]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def r
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2009 Ryan Blue.
|
3
|
+
# Distributed under the terms of the GNU General Public License (GPL).
|
4
|
+
# See the LICENSE file for further information on the GPL.
|
5
|
+
#++
|
6
|
+
|
7
|
+
require 'marker/common'
|
8
|
+
|
9
|
+
module Marker #:nodoc:
|
10
|
+
class Heading < ParseNode
|
11
|
+
# HTML-rendered headings, using <h#> tags.
|
12
|
+
def to_html( options = {} )
|
13
|
+
# find out how deep it is
|
14
|
+
d = [s.text_value.size, e.text_value.size].min
|
15
|
+
sn = s.text_value.size - d
|
16
|
+
en = e.text_value.size - d
|
17
|
+
if sn > 0
|
18
|
+
"<h#{d}>#{'=' * sn} #{label( :html, options )}</h#{d}>"
|
19
|
+
elsif en > 0
|
20
|
+
"<h#{d}>#{label( :html, options )} #{'=' * en}</h#{d}>"
|
21
|
+
else
|
22
|
+
"<h#{d}>#{label( :html, options )}</h#{d}>"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Text-rendered headings. Should look like this:
|
27
|
+
#
|
28
|
+
# Heading Level 1
|
29
|
+
# ==========================================================================
|
30
|
+
#
|
31
|
+
# Heading Level 2
|
32
|
+
# --------------------------------------------------------------------------
|
33
|
+
#
|
34
|
+
# - Heading Level 3 --------------------------------------------------------
|
35
|
+
#
|
36
|
+
# --- Heading Level 4 ------------------------------------------------------
|
37
|
+
#
|
38
|
+
def to_s( options = {} )
|
39
|
+
width = options[:width] || 80
|
40
|
+
d = [s.text_value.size, e.text_value.size].min
|
41
|
+
sn = s.text_value.size - d
|
42
|
+
en = e.text_value.size - d
|
43
|
+
|
44
|
+
l = if sn > 0
|
45
|
+
"#{'=' * sn} #{label( :text, options )}"
|
46
|
+
elsif en > 0
|
47
|
+
"#{label( :text, options )} #{'=' * en}"
|
48
|
+
else
|
49
|
+
label( :text, options )
|
50
|
+
end
|
51
|
+
|
52
|
+
case d
|
53
|
+
when 1
|
54
|
+
"#{l}\n" + ('=' * width)
|
55
|
+
when 2
|
56
|
+
"#{l}\n" + ('-' * width)
|
57
|
+
else
|
58
|
+
l = " #{l} "
|
59
|
+
h = '-'*width
|
60
|
+
h[2*(d-3)+1, l.size] = l # slice substitution
|
61
|
+
h
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def label( format, options = {} )
|
66
|
+
case format
|
67
|
+
when :html
|
68
|
+
l.to_html( options )
|
69
|
+
else
|
70
|
+
l.to_s( options )
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
#-- defaults ++
|
75
|
+
def l #:nodoc:
|
76
|
+
nil
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|