summaryrefslogtreecommitdiff
path: root/re
diff options
context:
space:
mode:
authorJohn Ankarstr\xf6m <john@ankarstrom.se>2021-06-02 12:57:24 +0200
committerJohn Ankarstr\xf6m <john@ankarstrom.se>2021-06-02 14:14:13 +0200
commitb61fcab3baf91ecd4b2ad4185e591cf339ad2b34 (patch)
tree9091676b4b07be854408b31e0be1b6d5eb5028e6 /re
downloadref-b61fcab3baf91ecd4b2ad4185e591cf339ad2b34.tar.gz
First commit
Diffstat (limited to 're')
-rwxr-xr-xre123
1 files changed, 123 insertions, 0 deletions
diff --git a/re b/re
new file mode 100755
index 0000000..8e0677e
--- /dev/null
+++ b/re
@@ -0,0 +1,123 @@
+#!/usr/bin/perl
+
+# re -- 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}++;
+ if ($rx->match($_)) {
+ $loc = $bytes{$file};
+ last;
+ }
+ $bytes{$file} += length($_);
+ }
+ } 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 "\n$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;
+ print;
+ }
+ next;
+ }
+normal:
+ print;
+}
+
+close $_ for values %handles;
+close $fh;