-
Notifications
You must be signed in to change notification settings - Fork 0
/
prettyprint
139 lines (122 loc) · 5.64 KB
/
prettyprint
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
#!/usr/bin/env perl
# Usage: prettyprint [files]
#
# Quick and dirty prettyprinter for LISP-like expressions.
# Prettyprints all the input, except lines that start with # (i.e., comments).
# No assumption about input whitespace except that it separates sister atoms.
#
# Author: Jason Eisner <[email protected]>,
# 2001-09-08: Created for 600.465 HW1.
# 2001-10-20: Modified for 600.465 HW3 to allow first list element
# to be a list itself. Not sure why - maybe to accept
# the output of the simplify script?
# 2002-06-28: Modified original so it could be used as an interactive
# filter rather than reading a whole file before printing
# anything.
# 2002-10-24: Merged the two previous modifications.
# 2003-10-21: Skip comment lines.
# 2011-08-31: Instead, pass comment lines (and blank lines) through
# as soon as possible.
# 2011-08-31: Start the 'die' messages on a new line.
#
# TO DO: Perhaps when the indent level is 0, we should preserve whitespace
# (or perhaps even all lines that don't start with \s\*\(, even if
# they contain parens).
use warnings;
use IO::Handle
STDOUT->autoflush(); # ensure that output reaches the user immediately
sub pp;
sub peektoken;
sub gettoken;
sub getcomments;
sub myeof;
pp(0); # prettyprint tokens at indent level 0
die "$0: Unexpected right parenthesis; didn't finish printing\n" if defined peektoken;
######################################################################
sub pp { # prettyprints expressions from @tokens,
# at the indent level given by the argument,
# until it runs out of input or runs into an extra right paren.
# Note that there's some special handling at indentation level 0.
# This was added ONLY to improve our printing of comment lines.
#
# At level 0, we want to pass comment lines through immediately rather
# than waiting for the next newline. This enables us to print comments
# before the first expression (i.e., before the first newline). It
# also lets us print comments "hot" as they arrive between expressions
# 1 and 2, rather than waiting till expression 2 actually arrives.
#
# To make this work, we must print "\n" AFTER each level-0
# expression. I used to treat level 0 in exactly the same way as
# the other levels (print "\n" BEFORE each expression other than
# the first, which required the main routine to print a final
# "\n" before quitting). But I've modified the code so that we
# suppress "\n" before level-0 expressions and add it after them.
my($indent) = @_;
my $i=0;
while (defined peektoken($indent) && peektoken ne ")") {
print "\n", getcomments, " " x $indent if $i++ && $indent!=0; # newline before any expr but 1st
print my($token) = &gettoken; # print word or (
if ($token eq "(") { # if ( then
my $LHS = peektoken;
die "\n$0: Unexpected EOF\n" unless defined $LHS;
if ($LHS eq ")") { # no subitems, so nothing to do
;
} elsif ($LHS eq "(") { # first item is a subexpression
&pp($indent+length("(")); # print all items including it in an aligned column just to the right of the previously printed "("
} else { # first subitem is a simple word
&gettoken; # consume it
print "$LHS "; # print it (plus " " even if no more subitems)
&pp($indent+length("($LHS ")); # print all remaining items in an aligned column just to its right
}
my $closeparen = &gettoken;
die "\n$0: Unexpected EOF\n" unless defined $closeparen;
die "\n$0: internal error" unless $closeparen eq ")";
print $closeparen; # print )
}
print "\n" if $indent==0; # special handling
}
}
######################################################################
# Manages the stream of tokens.
######################################################################
BEGIN {
my @tokens = (); # buffer of remaining tokens from most recently read input line
my $comments = ""; # block of saved up comments
my $firsttime = 1; # this is to fix the problem with Perl versions < 5.6,
# where eof() returns 1 when called before <> has been read.
sub peektoken { # returns undef if no more tokens
# if optional argument is 0, comments are printed "hot"
while ($firsttime || @tokens==0 && !myeof) {
$_ = <>; $firsttime=0;
last if !defined $_; # in case we tried to read because it was firsttime, but shouldn't have because input was empty
if (/^#|^\s*$/) { # comment or blank line
if (defined $_[0] && $_[0]==0) {
print $_; # print comment "hot"
} else {
$comments .= $_; # buffer comment till next newline
}
redo;
}
s/[()]/ $& /g; # put space around parens so they get treated as tokens
@tokens=split; # tokenize input by splitting at spaces
}
$tokens[0];
}
sub gettoken { # remove and return next token
my $t = peektoken;
shift(@tokens) if defined $t;
$t;
}
sub getcomments { # remove and return current block of saved up comments; should print this after any newline
my $t = $comments;
$comments = "";
$t;
}
}
# Version of eof() that is careful to keep returning 1 once it has reached eof.
BEGIN {
my $myeof = 0;
sub myeof {
$myeof || ($myeof = eof());
}
}