summaryrefslogtreecommitdiff
path: root/web/clojure-pamphlet/pamphletangler
blob: 50c384cc98eddc030d43105529b285061ce3f339 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#!/usr/bin/perl -w

=head1 NAME

pamphletangler - Extract code bits from LaTeX pamphlet files.

=cut

=head1 SYNOPSIS

B<pamphletangler> [OPTION...] I<FILE> I<CHUNKNAME>

=cut

=head1 DESCRIPTION

The  clojure  pamphlet  system is a system based on the clojure literate
system.  In the clojure's pamphlet system you have your main LaTeX file, which
can be compiled regularly. This file contains documentation and source code
(just like in other forms of literate programming). This code snippets are
wrapped in the 'chunk' environment, hence they can be recognized by the tangler
in order  to  extract  them.  Chunks can be included inside each other by the
'getchunk' command (which will be typesetted acordingly). Finally, you run your
LaTeX file through the tangler and get your desired chunk of code.

=cut

=head1 OPTIONS

=over 29

=item -L, --line=linetext

If set, this text will be inserted after jumps of lines, changing the string
inside "changetext" for the line number. This is used so that error report
refers to pamphlet line number instead of output line number

=item -C, --change=changetext

Defaults to "{}" this is the text that will be replaced with the line number if
the linetext option is specified

=item -?, --help

Give this help list

=item --usage

Give a short usage message

=item --man

Print manual page

=item -V, --version

Print program version

=back

=head1 LICENSE

Copyright (C) 2019 Ernesto Lanchares Sanchez.

   The clojure-pamphlet tagler is free software; you can redistribute
   it and/or modify it under the terms of the GNU Lesser General
   Public License as published by the Free Software Foundation;
   either version 3 of the License, or (at your option) any later
   version.

  The clojure-pamphlet tangler is distributed in the hope that it
  will be useful, but WITHOUT ANY WARRANTY; without even the implied
  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  See the GNU Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this program; if not, see
  L<http://www.gnu.org/licenses/>.


=head1 AUTHOR

Ernesto Lanchares <e.lancha98@gmail.com>

=head1 BUGS

Report bugs to e.lancha98@gmail.com

=cut

use strict;
use warnings;
use Getopt::Long;
use Pod::Usage qw(pod2usage);
use vars qw/$VERSION/;

BEGIN { $VERSION = '1.3' }

my $linetext = "";
my $changetext = "{}";
my $filename;
my $chunkname;

OPTS: {
    GetOptions(
        'usage' => sub { pod2usage( -verbose => 99, -sections => [ qw(SYNOPSIS) ]); },
        'help|?' => sub { pod2usage( -verbose => 99, -sections => [ qw(SYNOPSIS DESCRIPTION OPTIONS BUGS) ]); },
        'man' => sub { pod2usage( -verbose => 2) },
        'version|V' => sub { print "$VERSION\n"; exit; },
        'line|L=s' => \$linetext,
        'change|C=s' => \$changetext
    );

    pod2usage( -message => "[ERROR]: Invalid number of arguments." )
        if scalar(@ARGV) != 2;
    $filename = shift;
    $chunkname = shift;
}

open(my $file, "<", $filename) or die "Can't open $filename: $!";

my $file_contents = do { local $/; <$file> };
my %codeblocks;

while ($file_contents =~ /\\begin\{chunk\}\{([\w.]+)\}[\s\h\t]*\n?(.*?)\n?[\s\t\h]*\\end\{chunk\}/sg) {
    $codeblocks{$1} = { source => $2, printing => undef };
}

open($file, "<", $filename) or die "Can't open $filename: $!";
my $index = 1;
while (my $line = <$file>) {
    while ($line =~ /\\begin\{chunk\}\{([\w.]+)\}/g) {
        $codeblocks{$1}{line} = $index+1;
    }
    $index++;
}

sub printchunk {
    my ($chunk, $indent) = @_;

    die "Chunk $chunk not found." if (!exists($codeblocks{$chunk}));

    die "Cyclic reference found: $chunk" if $codeblocks{$chunk}{printing};
    $codeblocks{$chunk}{printing} = 1;

    my $localines = 1;
    print $linetext =~ s/$changetext/$codeblocks{$chunk}{line}/gr, "\n";
    foreach my $line (split(/\n/, $codeblocks{$chunk}{source})) {
        if ($line =~ /([\s\t\h]*)\\getchunk\{([\w.]+)\}/) {
            eval {
                printchunk($2, $1); #$1 contains indent and $2 contains chunkname
                print $linetext =~ s/$changetext/@{[$codeblocks{$chunk}{line} + $localines]}/gr, "\n";
                1;
            } or do {
                if (rindex($@, "Cyclic reference found:", 0) == 0) {
                    $@ =~ /(.+?)( at .*)/s;
                    die "$1 <- $chunk";
                }
                die $@;
            }
        } else {
            print $indent, $line, "\n";
        }
        $localines++;
    }

    $codeblocks{$chunk}{printing} = undef;
    return 0;
}
printchunk($chunkname, "");