summaryrefslogtreecommitdiff
path: root/ref
diff options
context:
space:
mode:
Diffstat (limited to 'ref')
-rwxr-xr-xref136
1 files changed, 136 insertions, 0 deletions
diff --git a/ref b/ref
new file mode 100755
index 0000000..7322246
--- /dev/null
+++ b/ref
@@ -0,0 +1,136 @@
+#!/usr/bin/perl
+
+# ref -- reference-based literate programming system
+
+use strict;
+use warnings;
+use POSIX::Regex qw/:all/;
+
+my $bytes;
+my $count;
+my %bytes; # file => current byte
+my %file_locations; # file => [location, ...]
+my %files; # byte => file
+my %handles; # file => handle
+my %lines; # file => current line
+my %locations; # byte => location (i.e. byte in source file)
+my @references;
+
+die "usage: $0 file\n" if @ARGV != 1;
+open my $fh, '<', $ARGV[0] or die "could not open $ARGV[0]: $!\n";
+
+# collect references
+$bytes = 0;
+while (<$fh>) {
+ $bytes += length($_);
+ push @references, [$bytes, $1] if /^\.\s*Re\s+(.*)/;
+}
+
+# find referenced locations
+for (@references) {
+ my ($bytes, $ref) = @$_;
+ my $loc = -1;
+
+ goto invalid if not $ref =~ /^([^:]+):(.*)/;
+ my ($file, $ident) = ($1, $2);
+
+ if (not exists $handles{$file}) {
+ open my $fh, '<', $file or die "could not open $file: $!\n";
+ $handles{$file} = $fh;
+ $bytes{$file} = 0;
+ $lines{$file} = 0;
+ }
+
+ if ($ident =~ /^(\d+)$/) {
+ my $line = $1;
+ if ($ident <= $lines{$file}) {
+ seek $handles{$file}, 0, 0;
+ $bytes{$file} = 0;
+ $lines{$file} = 0;
+ }
+ local $_;
+ while ($_ = readline $handles{$file}) {
+ $lines{$file}++;
+ if ($lines{$file} == $line) {
+ $loc = $bytes{$file};
+ last;
+ }
+ $bytes{$file} += length($_);
+ }
+ } elsif ($ident =~ m{^/(.*?)/?$}) {
+ my $rx = new POSIX::Regex($1);
+ seek $handles{$file}, 0, 0;
+ $bytes{$file} = 0;
+ $lines{$file} = 0;
+ local $_;
+ while ($_ = readline $handles{$file}) {
+ $lines{$file}++;
+ chomp;
+ if ($rx->match($_)) {
+ $loc = $bytes{$file};
+ last;
+ }
+ $bytes{$file} += length($_)+1;
+ }
+ } else {
+ goto invalid;
+ }
+
+ die "could not find location $ident in $file\n" if $loc == -1;
+ $locations{$bytes} = $loc;
+ $files{$bytes} = $file;
+ if (exists $file_locations{$file}) {
+ push @{$file_locations{$file}}, $loc;
+ } else {
+ $file_locations{$file} = [];
+ }
+
+ next;
+invalid:
+ die "invalid syntax: $ref at $bytes\n";
+}
+
+# intertwine
+seek $fh, 0, 0;
+$bytes = 0;
+$count = 0;
+while (<$fh>) {
+ $bytes += length($_);
+ goto normal if not @references;
+ my $ref_bytes = $references[0][0];
+ if ($bytes == $ref_bytes) {
+ shift @references;
+ my $file = $files{$bytes};
+ my $end = shift @{$file_locations{$file}} || '';
+ my $loc = $locations{$bytes};
+
+ print ".Sr \"$file\" $loc $end\n";
+
+ seek $handles{$file}, $loc, 0;
+ local $_;
+ my $bytes = $loc;
+ while ($_ = readline $handles{$file}) {
+ $bytes += length($_);
+ last if $end and $bytes > $end;
+ /^(\s*)/; my $indent = $1;
+ my $max = 64;
+ for (split //, $indent) {
+ $max -= 7 if /\t/;
+ }
+ s/(.{$max})(.{6,})/$1\\fR...\\fP\n$indent\t$2/g;
+ s/\\/\\\\/g;
+ s/\\\\fR...\\\\fP/\\fR...\\fP/g;
+ s/^\./\\&./m;
+ print;
+ }
+
+ print ".Se\n";
+
+ next;
+ }
+normal:
+ print;
+}
+
+close $_ for values %handles;
+close $fh;