Scheme Pretty-Printing
Scheme Pretty-Printing |
Scheme is an expression language. This means that everything that is entered to the Scheme interpreter/compiler is an expression. Expressions are separated by blank space (blank, tabs, new-lines). Expressions can be in several forms:
- numbers - a sequence of digits, possibly preceded by a `+' or `-', possibly including a single `.' following at least one digit
- strings - a sequence of characters (not including newlines) preceded and followed by `"', with any included double-quote characters preceded by a `
\
', such as"This `\"' is a double-quote"
- special constants - a `
#
' followed by any characters up to blank space - compound expressions - a (possibly empty) sequence of expressions surrounded by parentheses
- identifiers - a sequence of non-blank characters not including the characters:
#
,"
,\
,(
, and)
Input
A sequence of Scheme expressions.
Output
The same sequence of expressions, reformatted to make them more readable. The rules you must follow are:
- All top level expressions will start with no leading blanks on a line.
- A compound expression with the first sub-expression being the identifier
`define
' is a define-form. The second sub-expression will be an identifier and should go on the same line as the word`define
'. If the third (and last) expression is compound it should start on the following line, indented 3 spaces. Otherwise, the whole define-form should be on a single line. - A compound expressions with the first sub-expression being the identifier
`lambda
' is a lambda-form. The second sub-expression will be an identifier or compound expression and should go on the same line as the word`lambda
'. All subsequent expressions should start on a new line, indented by an additional 3 spaces - A compound expressions with the first sub-expression being the identifier
`if
' is an if-form. The second sub-expression will be an identifier or compound expression and should go on the same line as the word`if
'. All subsequent expressions should start on a new line, indented by an additional 4 spaces - All other compound expressions are function applications. If any of the sub-expressions are compound, the first two sub-expressions will be on the same line and all subsequent sub-expressions will be on new lines, indented to align with the second sub-expression.
- In all other cases, all blank space between elements of a compound expression will be replaced by a single space.
Sample Input
(define abc+ (lambda (@1 $f) (if (if $f a b) (@1 3 4) (bcdefg (d e) (f "g"))))) (define a 42) (+ a (- b c))
Sample Output
(define abc+ (lambda (@1 $f) (if (if $f a b) (@1 3 4) (bcdefg (d e) (f "g"))))) (define a 42) (+ a (- b c))
写到我蛋都碎了,一提交竟然一次AC,不知有没有人能写的简洁一点。
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <cstring>
#include <cctype>
void print_spaces (int n)
/*
* print 'n' number of spaces
*/
{
assert (n >= 0);
while (n--)
printf (" ");
}
bool is_define_form (char *exp)
/*
* return true if 'exp' is a 'define form compound',
* otherwise return false.
*/
{
char s1[100], s2[100];
if (sscanf (exp, "%s%s", s1, s2) == EOF)
return false;
return *s1 == '(' && strcmp (s2, "define") == 0;
}
bool is_lambda_form (char *exp)
/*
* return true if 'exp' is a 'lambda from compound'
* otherwise return false.
*/
{
char s1[100], s2[100];
if (sscanf (exp, "%s%s", s1, s2) == EOF)
return false;
return *s1 == '(' && strcmp (s2, "lambda") == 0;
}
bool is_if_form (char *exp)
/*
* return true if 'exp' is a 'if form compound'
* otherwise return false.
*/
{
char s1[100], s2[100];
if (sscanf (exp, "%s%s", s1, s2) == EOF)
return false;
return *s1 == '(' && strcmp (s2, "if") == 0;
}
bool is_compound (char *exp)
/*
* return true if 'exp' is a 'compound expression',
* otherwise return false.
*/
{
char s[100];
if (sscanf (exp, "%s", s) == EOF)
return false;
return *s == '(';
}
bool is_identifier (char *exp)
/*
* return true if 'exp' is a identifier,
* otherwise return false.
*/
{
char s[100];
if (sscanf (exp, "%s", s) == EOF)
return false;
return *s && *s != ')' && *s != '(';
}
bool is_exp (char *exp)
/*
* return true if 'exp' is a expression to be printed,
* otherwise return false
*/
{
return is_compound (exp) || is_identifier (exp);
}
bool has_compound (char *exp)
/*
* return true if any sub-expression is a compound,
* otherwise return false.
*/
{
int bar = 0, n; // brackets
char *p = exp, s[100];
do {
if (sscanf (p, "%s%n", s, &n) == EOF)
return false;
if (p != exp && is_compound (p))
return true;
if (*s == '(')
++bar;
else if (*s == ')')
--bar;
p += n;
} while (*p && bar);
return false;
}
char *identifier (char *exp, int *len = NULL)
/*
* print a identifier
*/
{
#ifdef _DEBUG
printf ("identifier ()\n");
#endif
char s[100];
int n;
if (sscanf (exp, "%s%n", s, &n) == EOF)
return exp;
printf ("%s", s);
if (len)
*len = strlen (s);
return exp += n;
}
char *compound_on_one_line (char *exp, int *len = NULL)
/*
* print a 'compound expression' on one single line
*/
{
#ifdef _DEBUG
printf ("compound_on_one_line ()\n");
#endif
if (len)
*len = 0;
int bar = 0, n;
char *p = exp, s1[100], s2[100] = "";
do {
if (sscanf (p, "%s%n", s1, &n) == EOF)
return p;
if (p != exp && *s2 != '(' && *s1 != ')')
printf (" ");
printf ("%s", s1);
strcpy (s2, s1);
if (*s1 == ')')
++bar;
else if (*s1 == '(')
--bar;
if (len)
*len += strlen (s1) + (p==exp ? 0 : 1);
p += n;
} while (*p && bar);
return p;
}
char *eat (char *exp, char ch)
/*
* eats a 'ch' char in exp.
*/
{
while (*exp && *exp != ch)
++exp;
return ++exp;
}
char *compound (char *exp, int idents)
/*
* print the 'compound expression',
* try to distinguish 'if form', 'lambda form', 'define form' and 'normal form'.
* 'idents' is the number of spaces we will ident
*/
{
#ifdef _DEBUG
printf ("compound (idents = %d)\n", idents);
#endif
char *if_form (char *, int);
char *lambda_form (char *, int);
char *define_form (char *, int);
char *print_one_by_one (char *, int);
if (is_if_form (exp))
return if_form (exp, idents);
else if (is_lambda_form (exp))
return lambda_form (exp, idents);
else if (is_define_form (exp))
return define_form (exp, idents);
// now it's a 'normal form compound'
if (! has_compound (exp))
return compound_on_one_line (exp);
printf ("(");
exp = eat (exp, '(');
int n;
if (is_compound (exp))
exp = compound_on_one_line (exp, &n);
else if (is_identifier (exp))
exp = identifier (exp, &n);
if (is_exp (exp)) {
printf (" ");
if (is_compound (exp))
exp = compound_on_one_line (exp);
else if (is_identifier (exp))
exp = identifier (exp);
else
assert (false);
}
if (is_exp (exp)) {
printf ("\n");
print_spaces (idents + n + 2);
exp = print_one_by_one (exp, idents + n + 2);
}
printf (")");
return eat (exp, ')');
}
char *define_form (char *exp, int idents)
/*
* print the 'define form compound',
* 'idents' specifies the number of spaces we ident.
*/
{
printf ("(");
exp = eat (exp, '(');
char s1[100], s2[100];
int n;
if (sscanf (exp, "%s%s%n", s1, s2, &n) == EOF)
return exp;
exp += n;
printf ("%s %s", s1, s2);
if (is_compound (exp)) {
printf ("\n");
print_spaces (3 + idents);
exp = compound (exp, 3 + idents);
} else {
printf (" ");
exp = identifier (exp);
}
printf (")");
return eat (exp, ')');
}
char *if_form (char *exp, int idents)
/*
* print the 'if form compound',
* 'idents' specifies the number of spaces we ident.
*/
{
printf ("(");
exp = eat (exp, '(');
char *print_one_by_one (char *, int);
char s1[100];
int n;
if (sscanf (exp, "%s%n", s1, &n) == EOF)
return exp;
exp += n;
printf ("%s ", s1);
if (is_compound (exp))
exp = compound (exp, strlen (s1) + 2 + idents);
else
exp = identifier (exp);
printf ("\n");
print_spaces (idents + 4);
exp = print_one_by_one (exp, idents + 4);
printf (")");
return eat (exp, ')');
}
char *lambda_form (char *exp, int idents)
/*
* print the 'lambda form compound',
* 'idents' specifies the number of spaces we ident.
*/
{
printf ("(");
exp = eat (exp, '(');
char *print_one_by_one (char *, int);
char s1[100];
int n;
if (sscanf (exp, "%s%n", s1, &n) == EOF)
return exp;
exp += n;
printf ("%s ", s1);
if (is_compound (exp))
exp = compound (exp, strlen (s1) + 2 + idents);
else
exp = identifier (exp);
printf ("\n");
print_spaces (idents + 3);
exp = print_one_by_one (exp, idents + 3);
printf (")");
return eat (exp, ')');
}
char *print_one_by_one (char *exp, int idents)
/*
* print a sequence of expression whitch may be
* a single identifier or a compound expression.
*/
{
#ifdef _DEBUG
printf ("print_one_by_one (idents = %d)\n", idents);
#endif
if (is_compound (exp))
exp = compound (exp, idents);
else if (is_identifier (exp))
exp = identifier (exp);
else
return exp;
while (*exp) {
if (is_exp (exp)) {
printf ("\n");
print_spaces (idents);
} else {
break;
}
if (is_compound (exp))
exp = compound (exp, idents);
else if (is_identifier (exp))
exp = identifier (exp);
else
assert (false);
}
return exp;
}
void preprocess (char *exp)
/*
* seperate all elements with spaces
*/
{
char *buf = (char *) malloc (strlen (exp) * 3);
char *src = exp;
char *dest = buf;
while (*src) {
if (*src == '(') {
dest = 3 + strcat (dest, " ( ");
} else if (*src == ')') {
dest = 3 + strcat (dest, " ) ");
} else if (isblank (*src) || *src == '\n') {
*dest++ = ' ';
} else if (*src == '#') {
*dest++ = ' ';
while (*src && *src != ' ')
*dest++ = *src++;
*dest++ = ' ';
} else if (*src == '\"') {
*dest++ = ' ';
do {
if (*src == '\\')
*dest++ = *src++;
if (*src == '\n')
++src;
else
*dest++ = *src++;
} while (*src != '\"');
*dest++ = '\"';
*dest++ = ' ';
} else {
*dest++ = *src;
}
++src;
}
*dest = 0;
strcpy (exp, buf);
free (buf);
}
int main ()
{
static char all_lines [1024] = "";
static char line [1024];
while (gets (line)) {
strcat (all_lines, line);
strcat (all_lines, "\n");
}
preprocess (all_lines);
print_one_by_one (all_lines, 0);
printf ("\n");
return 0;
}