|
|
|
package Text::Dokuwiki;
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
use warnings;
|
|
|
|
use feature qw/ switch /;
|
|
|
|
use utf8;
|
|
|
|
|
|
|
|
use Data::Dumper;
|
|
|
|
|
|
|
|
sub new {
|
|
|
|
my ($class) = @_;
|
|
|
|
my $self = {};
|
|
|
|
|
|
|
|
return bless($self, $class);
|
|
|
|
}
|
|
|
|
|
|
|
|
sub parse {
|
|
|
|
my ($self, $text) = @_;
|
|
|
|
my ($tree, $mode, $attrs, $buf) = ([], 'text', '', '');
|
|
|
|
|
|
|
|
my @lines = split /\r?\n/o, $text;
|
|
|
|
my $linenum = 0;
|
|
|
|
|
|
|
|
foreach my $line (@lines) {
|
|
|
|
$linenum++;
|
|
|
|
|
|
|
|
given ($mode) {
|
|
|
|
when (m!block/(file|code|nowiki)!o) {
|
|
|
|
if ($line =~ m{^\s*</$1>}o) {
|
|
|
|
if ($1 eq 'file') {
|
|
|
|
my $dt = [dt => {}, 0 => $attrs->{file}];
|
|
|
|
my $dd = [dt => {}, 0 => [pre => {class => $attrs->{class}}, 0 => $buf]];
|
|
|
|
push @{ $tree }, [dl => {class => 'file'}, [$dt, $dd]];
|
|
|
|
} elsif ($1 eq 'nowiki') {
|
|
|
|
push @{ $tree }, [pre => {}, 0 => $buf];
|
|
|
|
} else {
|
|
|
|
push @{ $tree }, [code => {class => $attrs->{class}}, 0 => $buf];
|
|
|
|
}
|
|
|
|
($buf, $attrs, $mode) = ('', {}, undef); next;
|
|
|
|
}
|
|
|
|
$buf .= $line . "\n";
|
|
|
|
}
|
|
|
|
when ("code") {
|
|
|
|
if ($line =~ m/^\s{2}(.+)/o) {
|
|
|
|
$buf .= $line . "\n"; next;
|
|
|
|
} else {
|
|
|
|
push @{ $tree }, [pre => {}, 0 => $buf];
|
|
|
|
($buf, $attrs, $mode) = ('', {}, undef); continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
when ("list") {
|
|
|
|
if ($line =~ m/^\s{2}([\*-])\s+(.+)/o) {
|
|
|
|
push @{ $buf }, [li => {}, 0 => $2];
|
|
|
|
next;
|
|
|
|
} else {
|
|
|
|
push @{ $tree }, [ul => {}, @$buf]; # TODO: lost list type
|
|
|
|
($buf, $attrs, $mode) = ('', {}, undef); continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
when ("table") {
|
|
|
|
...
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
given ($line) {
|
|
|
|
# header
|
|
|
|
when (m/^\s?(={2,6}) (.+) \g{1}\s*/o) {
|
|
|
|
my $level = $1 =~ tr/=/=/;
|
|
|
|
$level = 7 - $level; # invert
|
|
|
|
push @{ $tree }, ["h$level" => {}, 0 => $2];
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
# code/file block
|
|
|
|
when (m/^\s?<(code|file)(?:\s+(\S+)\s+(\S+))?>\s*$/o) {
|
|
|
|
$mode = "block/$1";
|
|
|
|
$attrs = ($2) ? {class => $2, file => $3} : {};
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
# nowiki block
|
|
|
|
when (m/\s?<nowiki>/o) {
|
|
|
|
$mode = "block/nowiki";
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
# lists
|
|
|
|
when (m/^\s{2}([\*-])\s+(.+)/o) {
|
|
|
|
$mode = 'list';
|
|
|
|
$buf = [];
|
|
|
|
push @{ $buf }, [li => {}, 0 => $2];
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
# quotes
|
|
|
|
when (m/^\s?(>)+\s*(.+)/o) {
|
|
|
|
my $level = $1 =~ tr/>/>/;
|
|
|
|
push @{ $tree }, [blockquote => {level => $level}, 0 => $2];
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
# table
|
|
|
|
when (m/^\s?[\|\^]/o) {
|
|
|
|
$mode = 'table';
|
|
|
|
$buf = $line . "\n"; # render later
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
# code idented with two spaces
|
|
|
|
when (m/^\s{2}(\S.+)/o) {
|
|
|
|
$mode = 'code';
|
|
|
|
$buf = $line . "\n";
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
# nonempty line
|
|
|
|
when (m/^\s?(\S.+)/o) {
|
|
|
|
push @{ $tree }, [p => {}, 0 => $1];
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
# empty lines;
|
|
|
|
when (m/^\s*$/) {
|
|
|
|
push @{ $tree }, [br => {}];
|
|
|
|
$mode = undef;
|
|
|
|
next;
|
|
|
|
}
|
|
|
|
# catchall
|
|
|
|
default {
|
|
|
|
printf "Unmatched % 3d: %s\n", $linenum, $line;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $tree;
|
|
|
|
}
|
|
|
|
|
|
|
|
1;
|
|
|
|
|
|
|
|
__END__
|
|
|
|
|
|
|
|
[div => {class => 'block'}, # <div class='block'>
|
|
|
|
0 => 'text1', # Hello!
|
|
|
|
[strong => {}, 0 => 'bold text'], # <strong>user</strong>
|
|
|
|
0 => ', this is converted text.', # , this is converted text.
|
|
|
|
[br => {}], # <br/>
|
|
|
|
[p => {}, 0 => 'Second paragraph'] # <p>Second paragraph</p>
|
|
|
|
] # </div>
|