Golf mi C "pre-golf"

12

Antecedentes

Para mis envíos de en C, necesito una herramienta de procesamiento. Al igual que en muchos otros lenguajes, el espacio en blanco es irrelevante en su mayoría en la fuente C (¡pero no siempre!) - aún hace que el código sea mucho más comprensible para los humanos. Un programa C completamente desarrollado que no contiene un solo espacio en blanco redundante a menudo es apenas legible.

Por lo tanto, me gusta escribir mi código en C para un envío de incluye espacios en blanco y, a veces, comentarios, para que el programa mantenga una estructura comprensible mientras escribo. El último paso es eliminar todos los comentarios y espacios en blanco redundantes. Esta es una tarea tediosa y sin sentido que realmente debe hacer un interno en un programa de computadora.

Tarea

Escriba un programa o función que elimine comentarios y espacios en blanco redundantes de alguna fuente de C "pre-golf" de acuerdo con las siguientes reglas:

  • A \(barra invertida) como el último carácter de una línea es una continuación de línea . Si encuentra esto, debe tratar la siguiente línea como parte de la misma línea lógica (podría, por ejemplo, eliminar completamente la \y la siguiente \n(nueva línea) antes de hacer cualquier otra cosa)
  • Los comentarios solo usarán el formato de una línea, comenzando con //. Por lo tanto, para eliminarlos, ignora el resto de la línea lógica siempre que se encuentre //fuera de un literal de cadena (consulte a continuación).
  • Los caracteres de espacio en blanco son (espacio), \t(tabulación) y \n(nueva línea, así que aquí el final de una línea lógica).
  • Cuando encuentre una secuencia de espacios en blanco, examine los caracteres que no lo son. Si

    • ambos son alfanuméricos o subrayados (rango [a-zA-Z0-9_]) o
    • ambos son +o
    • ambos son -o
    • el anterior es /y el siguiente es*

    luego reemplace la secuencia con un solo espacio ( ).

    De lo contrario, elimine la secuencia por completo.

    Esta regla tiene algunas excepciones :

    • Las directivas de preprocesador deben aparecer en sus propias líneas en su salida. Una directiva de preprocesador es una línea que comienza con #.
    • Dentro de un literal de cadena o literal de caracteres , no debe eliminar ningún espacio en blanco. Cualquier "(comilla doble) / '(comilla simple) que no esté precedida directamente por un número impar de barras invertidas ( \) comienza o finaliza una cadena literal / carácter literal . Le garantizamos que los literales de cadena y caracteres terminan en la misma línea que comenzaron. literales de cadena y literales de caracteres no se pueden anidar, por lo que un 'interior de un literal de cadena , así como "el interior de un carácter literal no tienen ningún significado especial.

Especificación de E / S

La entrada y la salida deben ser secuencias de caracteres (cadenas) que incluyen caracteres de nueva línea o matrices / listas de cadenas que no contienen caracteres de nueva línea. Si elige usar matrices / listas, cada elemento representa una línea, por lo que las nuevas líneas están implícitas después de cada elemento.

Puede suponer que la entrada es un código fuente de programa C válido. Esto también significa que solo contiene caracteres ASCII imprimibles, pestañas y líneas nuevas. Se permite un comportamiento indefinido en la entrada con formato incorrecto.

Espacio inicial y final / líneas vacías son no permitidos .

Casos de prueba

  1. entrada

    main() {
        printf("Hello, World!"); // hi
    }
    

    salida

    main(){printf("Hello, World!");}
    
  2. entrada

    #define max(x, y) \
        x > y ? x : y
    #define I(x) scanf("%d", &x)
    a;
    b; // just a needless comment, \
            because we can!
    main()
    {
        I(a);
        I(b);
        printf("\" max \": %d\n", max(a, b));
    }
    

    salida

    #define max(x,y)x>y?x:y
    #define I(x)scanf("%d",&x)
    a;b;main(){I(a);I(b);printf("\" max \": %d\n",max(a,b));}
    
  3. entrada

    x[10];*c;i;
    main()
    {
        int _e;
        for(; scanf("%d", &x) > 0 && ++_e;);
        for(c = x + _e; c --> x; i = 100 / *x, printf("%d ", i - --_e));
    }
    

    salida

    x[10];*c;i;main(){int _e;for(;scanf("%d",&x)>0&&++_e;);for(c=x+_e;c-->x;i=100/ *x,printf("%d ",i- --_e));}
    
  4. entrada

    x;
    #include <stdio.h>
    int main()
    {
        puts("hello // there");
    }
    

    salida

    x;
    #include<stdio.h>
    int main(){puts("hello // there");}
    
  5. input (un ejemplo del mundo real)

    // often used functions/keywords:
    #define P printf(
    #define A case
    #define B break
    
    // loops for copying rows upwards/downwards are similar -> macro
    #define L(i, e, t, f, s) \
            for (o=i; o e;){ strcpy(l[o t], l[o f]); c[o t]=c[s o]; }
    
    // range check for rows/columns is similar -> macro
    #define R(m,o) { return b<1|b>m ? m o : b; }
    
    // checking for numerical input is needed twice (move and print command):
    #define N(f) sscanf(f, "%d,%d", &i, &j) || sscanf(f, ",%d", &j)
    
    // room for 999 rows with each 999 cols (not specified, should be enough)
    // also declare "current line pointers" (*L for data, *C for line length),
    // an input buffer (a) and scratch variables
    r, i, j, o, z, c[999], *C, x=1, y=1;
    char a[999], l[999][999], (*L)[999];
    
    // move rows down from current cursor position
    D()
    {
        L(r, >y, , -1, --)
        r++ ? strcpy(l[o], l[o-1]+--x), c[o-1]=x, l[o-1][x]=0 : 0;
        c[y++] = strlen(l[o]);
        x=1;
    }
    
    // move rows up, appending uppermost to current line
    U()
    {
        strcat(*L, l[y]);
        *C = strlen(*L);
        L(y+1, <r, -1, , ++)
        --r;
        *l[r] = c[r] = 0;
    }
    
    // normalize positions, treat 0 as max
    X(b) R(c[y-1], +1)
    Y(b) R(r, )
    
    main()
    {
        for(;;) // forever
        {
            // initialize z as current line index, the current line pointers,
            // i and j for default values of positioning
            z = i = y;
            L = l + --z;
            C = c + z;
            j = x;
    
            // prompt:
            !r || y/r && x > *C
                ? P "end> ")
                : P "%d,%d> ", y, x);
    
            // read a line of input (using scanf so we don't need an include)
            scanf("%[^\n]%*c", a)
    
                // no command arguments -> make check easier:
                ? a[2] *= !!a[1],
    
                // numerical input -> have move command:
                // calculate new coordinates, checking for "relative"
                N(a)
                    ? y = Y(i + (i<0 | *a=='+') * y)
                        , x = X(j + (j<0 || strchr(a+1, '+')) * x)
                    :0
    
                // check for empty input, read single newline
                // and perform <return> command:
                : ( *a = D(), scanf("%*c") );
    
            switch(*a)
            {
                A 'e':
                    y = r;
                    x = c[r-1] + 1;
                    B;
    
                A 'b':
                    y = 1;
                    x = 1;
                    B;
    
                A 'L':
                    for(o = y-4; ++o < y+2;)
                        o<0 ^ o<r && P "%c%s\n", o^z ? ' ' : '>', l[o]);
                    for(o = x+1; --o;)
                        P " ");
                    P "^\n");
                    B;
    
                A 'l':
                    puts(*L);
                    B;
    
                A 'p':
                    i = 1;
                    j = 0;
                    N(a+2);
                    for(o = Y(i)-1; o<Y(j); ++o)
                        puts(l[o]);
                    B;
    
                A 'A':
                    y = r++;
                    strcpy(l[y], a+2);
                    x = c[y] = strlen(a+2);
                    ++x;
                    ++y;
                    B;
    
                A 'i':
                    D();
                    --y;
                    x=X(0);
                    // Commands i and r are very similar -> fall through
                    // from i to r after moving rows down and setting
                    // position at end of line:
    
                A 'r':
                    strcpy(*L+x-1, a+2);
                    *C = strlen(*L);
                    x = 1;
                    ++y > r && ++r;
                    B;
    
                A 'I':
                    o = strlen(a+2);
                    memmove(*L+x+o-1, *L+x-1, *C-x+1);
                    *C += o;
                    memcpy(*L+x-1, a+2, o);
                    x += o;
                    B;
    
                A 'd':
                    **L ? **L = *C = 0, x = 1 : U();
                    y = y>r ? r : y;
                    B;
    
                A 'j':
                    y<r && U();
            }
        }
    }
    

    salida

    #define P printf(
    #define A case
    #define B break
    #define L(i,e,t,f,s)for(o=i;o e;){strcpy(l[o t],l[o f]);c[o t]=c[s o];}
    #define R(m,o){return b<1|b>m?m o:b;}
    #define N(f)sscanf(f,"%d,%d",&i,&j)||sscanf(f,",%d",&j)
    r,i,j,o,z,c[999],*C,x=1,y=1;char a[999],l[999][999],(*L)[999];D(){L(r,>y,,-1,--)r++?strcpy(l[o],l[o-1]+--x),c[o-1]=x,l[o-1][x]=0:0;c[y++]=strlen(l[o]);x=1;}U(){strcat(*L,l[y]);*C=strlen(*L);L(y+1,<r,-1,,++)--r;*l[r]=c[r]=0;}X(b)R(c[y-1],+1)Y(b)R(r,)main(){for(;;){z=i=y;L=l+--z;C=c+z;j=x;!r||y/r&&x>*C?P"end> "):P"%d,%d> ",y,x);scanf("%[^\n]%*c",a)?a[2]*=!!a[1],N(a)?y=Y(i+(i<0|*a=='+')*y),x=X(j+(j<0||strchr(a+1,'+'))*x):0:(*a=D(),scanf("%*c"));switch(*a){A'e':y=r;x=c[r-1]+1;B;A'b':y=1;x=1;B;A'L':for(o=y-4;++o<y+2;)o<0^o<r&&P"%c%s\n",o^z?' ':'>',l[o]);for(o=x+1;--o;)P" ");P"^\n");B;A'l':puts(*L);B;A'p':i=1;j=0;N(a+2);for(o=Y(i)-1;o<Y(j);++o)puts(l[o]);B;A'A':y=r++;strcpy(l[y],a+2);x=c[y]=strlen(a+2);++x;++y;B;A'i':D();--y;x=X(0);A'r':strcpy(*L+x-1,a+2);*C=strlen(*L);x=1;++y>r&&++r;B;A'I':o=strlen(a+2);memmove(*L+x+o-1,*L+x-1,*C-x+1);*C+=o;memcpy(*L+x-1,a+2,o);x+=o;B;A'd':**L?**L=*C=0,x=1:U();y=y>r?r:y;B;A'j':y<r&&U();}}}
    

Este es el , por lo que gana la respuesta válida más corta (en bytes).

Felix Palmen
fuente
relacionado , relacionado
HyperNeutrino
Continuemos esta discusión en el chat .
DLosc

Respuestas:

4

Pip , 148 135 133 138 bytes

aRM"\
"R`("|').*?(?<!\\)(\\\\)*\1`{lPBaC:++i+191}R[`//.*``#.*`{X*aJw.`(?=`}.')M[A`\w`RL2"++""--""/*"]w`¶+`'·C(192+,#l)][x_WR'¶{aRw'·}xnsl]

Bytes se cuentan en CP-1252 , de modo y ·son de un byte cada uno. Tenga en cuenta que esto espera que el código C sea un argumento de línea de comando único, que (en una línea de comando real) requeriría el uso de copiosas secuencias de escape. ¡Es mucho más fácil probarlo en línea!

Explicación de la versión ungolfed

El código realiza un montón de operaciones de reemplazo, con un par de trucos.

Continuaciones de barra invertida

Todos RMlos casos de la cadena literal

"\
"

es decir, barra invertida seguida de nueva línea.

Literales de cadena y caracteres

Utilizamos un reemplazo de expresiones regulares con una función de devolución de llamada:

`("|').*?(?<!\\)(\\\\)*\1`

{
 lPBa
 C(++i + 191)
}

La expresión regular coincide con una comilla simple o doble, seguida de una no codiciosa .*?que coincide con 0 o más caracteres, la menor cantidad posible. Tenemos una mirada negativa hacia atrás para asegurar que el personaje anterior no sea una barra invertida; luego hacemos coincidir un número par de barras invertidas seguidas por el delimitador de apertura nuevamente.

La función de devolución de llamada toma el literal de cadena / carácter y lo empuja al final de la lista l. Luego devuelve un carácter que comienza con el código de carácter 192 ( À) y aumenta con cada literal reemplazado. Por lo tanto, el código se transforma así:

printf("%c", '\'');

printf(À, Á);

Se garantiza que estos caracteres de reemplazo no aparecerán en el código fuente, lo que significa que podemos reemplazarlos sin ambigüedad más adelante.

Comentarios

`//.*`

x

La expresión regular coincide //más todo hasta la nueva línea y se reemplaza con x(preestablecido en la cadena vacía).

Directivas del pre procesador

`#.*`

_WR'¶

Envuelve ejecuciones de caracteres que no son de nueva línea que comienzan con un inicio de sesión .

Espacios que no deben ser eliminados

{
 (
  X*a J w.`(?=`
 ) . ')
}
M
[
 A`\w` RL 2
 "++"
 "--"
 "/*"
]

{
 a R w '·
}

Están pasando muchas cosas aquí. La primera parte genera esta lista de expresiones regulares para reemplazar:

[
 `(?a)\w\s+(?=(?a)\w)`  Whitespace surrounded by [a-zA-Z_]
 `\+\s+(?=\+)`          Whitespace surrounded by +
 `\-\s+(?=\-)`          Whitespace surrounded by -
 `\/\s+(?=\*)`          Whitespace surrounded by / *
]

Tenga en cuenta el uso de lookaheads para hacer coincidir, por ejemplo, solo el e in define P printf. De esta manera, esta coincidencia no consume el P, lo que significa que la próxima coincidencia puede usarlo.

Generamos esta lista de expresiones regulares asignando una función a una lista, donde la lista contiene

[
 [`(?a)\w` `(?a)\w`]
 "++"
 "--"
 "/*"
]

y la función hace esto a cada elemento:

(X*aJw.`(?=`).')
 X*a              Map unary X to elements/chars a: converts to regex, escaping as needed
                  Regexes like `\w` stay unchanged; strings like "+" become `\+`
    J             Join the resulting list on:
     w             Preset variable for `\s+`
      .`(?=`       plus the beginning of the lookahead syntax
(           ).')  Concatenate the closing paren of the lookahead

Una vez que tenemos nuestras expresiones regulares, reemplazamos sus ocurrencias con esta función de devolución de llamada:

{aRw'·}

que reemplaza la ejecución de espacios en blanco en cada partido con ·.

Eliminación y limpieza de espacios en blanco.

[w `¶+` '·]

[x n s]

Tres reemplazos sucesivos sustituyen las ejecuciones restantes de espacio en blanco ( w) por una cadena vacía ( x), ejecuciones de nueva línea y ·espacio.

Sustitución inversa de cadenas y literales de caracteres

C(192+,#l)

l

Construimos una lista de todos los caracteres que usamos como sustituciones de literales tomando 192 + range(len(l))y convirtiendo en caracteres. Entonces podemos reemplazar cada uno de estos con su literal asociado en l.

¡Y eso es! La cadena resultante se imprime automáticamente.

DLosc
fuente
¡Genial, estoy impresionado (+1)! Incluir un //literal dentro de una cadena es definitivamente una buena idea para un caso de prueba, agregaré uno mañana.
Felix Palmen
Uhm ... ahora encontré un error sutil aquí también ...
Felix Palmen
Voy a elegir un ganador después de 14 días (al final de la próxima semana) y su solución sería el primer candidato si encuentra tiempo para solucionar este error. En este momento, tienes la puntuación más baja :)
Felix Palmen
1
@FelixPalmen fijo!
DLosc
7

Haskell , 327 360 418 394 bytes

g.(m.w.r.r=<<).lines.f
n:c:z="\n#_0123456789"++['A'..'Z']++['a'..'z']
(!)x=elem x
f('\\':'\n':a)=f a
f(a:b)=a:f b
f a=a
m('#':a)=c:a++[n]
m a=a
g(a:'#':b)=a:[n|a/=n]++c:g b
g(a:b)=a:g b
g a=a
s=span(!" \t")
r=reverse.snd.s
l n(a:b)d|a==d,n=a:w(snd$s b)|1>0=a:l(not$n&&a=='\\')b d
w('/':'/':_)=[]
w(a:b)|a!"\"'"=a:l(1>0)b a|(p,q:u)<-s b=a:[' '|p>"",a!z&&q!z||[a,q]!words"++ -- /*"]++w(q:u)
w a=a

Pruébalo en línea!

¡Fue muy divertido escribirlo! Primero faparece la función y elimina todas las barras diagonales inversas al final de las líneas, luego la linesdivide en una lista de cadenas en las nuevas líneas. Luego mapeamos un montón de funciones en las líneas y las concatenamos todas juntas. Esas funciones: eliminar espacios en blanco desde la izquierda ( t) y desde la derecha ( r.t.rdonde restá reverse); eliminar espacios en blanco del medio, ignorando cadenas y literales de caracteres, así como eliminar comentarios ( w); y finalmente agrega un carácter de nueva línea al final si la línea comienza con un #. Después de que todas las líneas se concatenan nuevamente, se gbuscan # caracteres y se asegura de que estén precedidos por una nueva línea.

wes un poco complejo, así que lo explicaré más a fondo. Primero verifico "//" ya que en wSé que no estoy en un literal de cadena Sé que este es un comentario, así que elimino el resto de la línea. A continuación, verifico si la cabeza es un delimitador para una cadena o literal de caracteres. Si es así, lo antepongo y paso el testigo al lque atraviesan los personajes, siguiendo el estado de "escape" con el nque será cierto si ha habido un número par de barras consecutivas. Cuando ldetecta un delimitador y no está en el estado de escape, vuelve a pasar el testigo w, recortando para eliminar espacios en blanco después del literal porque wespera que el primer carácter no sea un espacio en blanco. Cuandowno encuentra un delimitador, usa span para buscar espacios en blanco en la cola. Si hay alguno, comprueba si los personajes que lo rodean no pueden ponerse en contacto e inserta un espacio si es así. Luego vuelve a aparecer una vez finalizado el espacio en blanco. Si no había espacios en blanco, no se inserta ningún espacio y se mueve de todos modos.

EDITAR: ¡Muchas gracias a @DLosc por señalar un error en mi programa que realmente me permitió acortarlo también! ¡Hurra por la coincidencia de patrones!

EDIT2: ¡Soy un idiota que no terminó de leer las especificaciones! Gracias de nuevo DLosc por señalar eso!

Edit3: Apenas notó algo molesto reducción del tipo que se convirtió e=elemen Char->[Char]->Boolpor alguna razón, rompiendo así el e[a,q]. Tuve que agregar una firma de tipo para forzarla a ser correcta. ¿Alguien sabe cómo podría solucionar eso? Nunca he tenido este problema en Haskell antes. TIO

EDIT4: solución rápida para el error @FelixPalmen me mostró. Podría intentar jugar golf más tarde cuando tenga algo de tiempo.

EDIT5: -24 bytes gracias a @Lynn! ¡Gracias! ¡No sabía que podía asignar cosas en el ámbito global utilizando la coincidencia de patrones como n:c:z=...si fuera realmente genial! También es una buena idea hacer un operador por elemdeseo. Pensé en eso.

usuario1472751
fuente
1
Me exprimida 24 bytes .
Lynn
2
Te estás encontrando con la temida restricción del monomorfismo ; definir e x y=elem x y(o incluso e x=elem x) resuelve tu problema. (Cambié el nombre ea un operador (!)
Lynn
3

C, 497 494 490 489 bytes

Como estamos procesando C, ¡hagámoslo usando C! La función f()toma la entrada del puntero de caracteres py las salidas al puntero q, y supone que la entrada está en ASCII:

#define O*q++
#define R (r=*p++)
#define V(c)(isalnum(c)||c==95)
char*p,*q,r,s,t;d(){isspace(r)?g():r==47&&*p==r?c(),g():r==92?e():(O=s=r)==34?b():r==39?O=R,a():r?a():(O=r);}a(){R;d();}b(){((O=R)==34?a:r==92?O=R,b:b)();}c(){while(R-10)p+=r==92;}e(){R-10?s=O=92,O=r,a():h();}j(){(!isspace(R)?r==47&&*p==r?c(),j:(t=r==35,d):j)();}f(){t=*p==35;j();}i(){V(s)&&V(r)||s==47&&r==42||(s==43||s==45)&&r==s&&*p==s?O=32:0;d();}h(){isspace(R)?g():i();}g(){(r==10?t?O=r,j:*p==35?s-10?s=O=r,j:0:h:h)();}

Suponemos que el archivo está bien formado: los literales de cadena y caracteres están cerrados, y si hay un comentario en la línea final, debe haber una nueva línea para cerrarlo.

Explicación

La versión previa al golf es solo un poco más legible, me temo:

#define O *q++=
#define R (r=*p++)
#define V(c)(isalnum(c)||c=='_')
char*p,*q,r,s,t;
d(){isspace(r)?g():r=='/'&&*p==r?c(),g():r=='\\'?e():(O s=r)=='"'?b():r=='\''?O R,a():r?a():(O r);}
a(){R;d();}
b(){((O R)=='"'?a:r=='\\'?O R,b:b)();}
c(){while(R!='\n')p+=r=='\\';}
e(){R!='\n'?s=O'\\',O r,a():h();}
j(){(!isspace(R)?r=='/'&&*p==r?c(),j:(t=r=='#',d):j)();}
f(){t=*p=='#';j();}
i(){V(s)&&V(r)||s=='/'&&r=='*'||(s=='+'||s=='-')&&r==s&&*p==s?O' ':0;d();}
h(){isspace(R)?g():i();}
g(){(r=='\n'?t?O r,j:*p=='#'?s!='\n'?s=O r,j:0:h:h)();}

Implementa una máquina de estados por recursión de cola. Las macros y variables auxiliares son

  • Opara o utput
  • Ra r de entrada ead enr
  • Vpara determinar los caracteres identificadores de v alid (desde !isalnum('_'))
  • py q- punteros de E / S como se describe
  • r- último carácter a r ead
  • s- s reciente personaje no en espacios en blanco
  • t- t ag cuando se trabaja en una directiva de preprocesador

Nuestros estados son

  • a() - código C normal
  • b() - cadena literal
  • c() - comentar
  • d() - código C normal, después de leer r
  • e() - secuencia de escape
  • f() - estado inicial (función principal)
  • g() - en espacios en blanco
  • h()- en los espacios en blanco - envío a g()oi()
  • i() - inmediatamente después del espacio en blanco - ¿necesitamos insertar un carácter de espacio?
  • j() - espacio en blanco inicial: nunca inserte un carácter de espacio

Programa de prueba

#define DEMO(code)                              \
    do {                                        \
        char in[] = code;                       \
        char out[sizeof in];                    \
        p=in;q=out;f();                         \
        puts("vvvvvvvvvv");                     \
        puts(out);                              \
        puts("^^^^^^^^^^");                     \
    } while (0)

#include<stdio.h>
#include<stdlib.h>
int main()
{
    DEMO(
         "main() {\n"
         "    printf(\"Hello, World!\"); // hi\n"
         "}\n"
         );
    DEMO(
         "#define max(x, y)                               \\\n"
         "    x > y ? x : y\n"
         "#define I(x) scanf(\"%d\", &x)\n"
         "a;\n"
         "b; // just a needless comment, \\\n"
         "        because we can!\n"
         "main()\n"
         "{\n"
         "    I(a);\n"
         "    I(b);\n"
         "    printf(\"\\\" max \\\": %d\\n\", max(a, b));\n"
         "}\n"
         );
    DEMO(
         "x[10];*c;i;\n"
         "main()\n"
         "{\n"
         "    int _e;\n"
         "    for(; scanf(\"%d\", &x) > 0 && ++_e;);\n"
         "    for(c = x + _e; c --> x; i = 100 / *x, printf(\"%d \", i - --_e));\n"
         "}\n"
         );
    DEMO(
         "// often used functions/keywords:\n"
         "#define P printf(\n"
         "#define A case\n"
         "#define B break\n"
         "\n"
         "// loops for copying rows upwards/downwards are similar -> macro\n"
         "#define L(i, e, t, f, s) \\\n"
         "        for (o=i; o e;){ strcpy(l[o t], l[o f]); c[o t]=c[s o]; }\n"
         "\n"
         "// range check for rows/columns is similar -> macro\n"
         "#define R(m,o) { return b<1|b>m ? m o : b; }\n"
         "\n"
         "// checking for numerical input is needed twice (move and print command):\n"
         "#define N(f) sscanf(f, \"%d,%d\", &i, &j) || sscanf(f, \",%d\", &j)\n"
         "\n"
         "// room for 999 rows with each 999 cols (not specified, should be enough)\n"
         "// also declare \"current line pointers\" (*L for data, *C for line length),\n"
         "// an input buffer (a) and scratch variables\n"
         "r, i, j, o, z, c[999], *C, x=1, y=1;\n"
         "char a[999], l[999][999], (*L)[999];\n"
         "\n"
         "// move rows down from current cursor position\n"
         "D()\n"
         "{\n"
         "    L(r, >y, , -1, --)\n"
         "    r++ ? strcpy(l[o], l[o-1]+--x), c[o-1]=x, l[o-1][x]=0 : 0;\n"
         "    c[y++] = strlen(l[o]);\n"
         "    x=1;\n"
         "}\n"
         "\n"
         "// move rows up, appending uppermost to current line\n"
         "U()\n"
         "{\n"
         "    strcat(*L, l[y]);\n"
         "    *C = strlen(*L);\n"
         "    L(y+1, <r, -1, , ++)\n"
         "    --r;\n"
         "    *l[r] = c[r] = 0;\n"
         "}\n"
         "\n"
         "// normalize positions, treat 0 as max\n"
         "X(b) R(c[y-1], +1)\n"
         "Y(b) R(r, )\n"
         "\n"
         "main()\n"
         "{\n"
         "    for(;;) // forever\n"
         "    {\n"
         "        // initialize z as current line index, the current line pointers,\n"
         "        // i and j for default values of positioning\n"
         "        z = i = y;\n"
         "        L = l + --z;\n"
         "        C = c + z;\n"
         "        j = x;\n"
         "\n"
         "        // prompt:\n"
         "        !r || y/r && x > *C\n"
         "            ? P \"end> \")\n"
         "            : P \"%d,%d> \", y, x);\n"
         "\n"
         "        // read a line of input (using scanf so we don't need an include)\n"
         "        scanf(\"%[^\\n]%*c\", a)\n"
         "\n"
         "            // no command arguments -> make check easier:\n"
         "            ? a[2] *= !!a[1],\n"
         "\n"
         "            // numerical input -> have move command:\n"
         "            // calculate new coordinates, checking for \"relative\"\n"
         "            N(a)\n"
         "                ? y = Y(i + (i<0 | *a=='+') * y)\n"
         "                    , x = X(j + (j<0 || strchr(a+1, '+')) * x)\n"
         "                :0\n"
         "\n"
         "            // check for empty input, read single newline\n"
         "            // and perform <return> command:\n"
         "            : ( *a = D(), scanf(\"%*c\") );\n"
         "\n"
         "        switch(*a)\n"
         "        {\n"
         "            A 'e':\n"
         "                y = r;\n"
         "                x = c[r-1] + 1;\n"
         "                B;\n"
         "\n"
         "            A 'b':\n"
         "                y = 1;\n"
         "                x = 1;\n"
         "                B;\n"
         "\n"
         "            A 'L':\n"
         "                for(o = y-4; ++o < y+2;)\n"
         "                    o<0 ^ o<r && P \"%c%s\\n\", o^z ? ' ' : '>', l[o]);\n"
         "                for(o = x+1; --o;)\n"
         "                    P \" \");\n"
         "                P \"^\\n\");\n"
         "                B;\n"
         "\n"
         "            A 'l':\n"
         "                puts(*L);\n"
         "                B;\n"
         "\n"
         "            A 'p':\n"
         "                i = 1;\n"
         "                j = 0;\n"
         "                N(a+2);\n"
         "                for(o = Y(i)-1; o<Y(j); ++o)\n"
         "                    puts(l[o]);\n"
         "                B;\n"
         "\n"
         "            A 'A':\n"
         "                y = r++;\n"
         "                strcpy(l[y], a+2);\n"
         "                x = c[y] = strlen(a+2);\n"
         "                ++x;\n"
         "                ++y;\n"
         "                B;\n"
         "\n"
         "            A 'i':\n"
         "                D();\n"
         "                --y;\n"
         "                x=X(0);\n"
         "                // Commands i and r are very similar -> fall through\n"
         "                // from i to r after moving rows down and setting\n"
         "                // position at end of line:\n"
         "\n"
         "            A 'r':\n"
         "                strcpy(*L+x-1, a+2);\n"
         "                *C = strlen(*L);\n"
         "                x = 1;\n"
         "                ++y > r && ++r;\n"
         "                B;\n"
         "\n"
         "            A 'I':\n"
         "                o = strlen(a+2);\n"
         "                memmove(*L+x+o-1, *L+x-1, *C-x+1);\n"
         "                *C += o;\n"
         "                memcpy(*L+x-1, a+2, o);\n"
         "                x += o;\n"
         "                B;\n"
         "\n"
         "            A 'd':\n"
         "                **L ? **L = *C = 0, x = 1 : U();\n"
         "                y = y>r ? r : y;\n"
         "                B;\n"
         "\n"
         "            A 'j':\n"
         "                y<r && U();\n"
         "        }\n"
         "    }\n"
         "}\n";);
}

Esto produce

main(){printf("Hello, World!");}
#define max(x,y)x>y?x:y
#define I(x)scanf("%d",&x)
a;b;main(){I(a);I(b);printf("\" max \": %d\n",max(a,b));}
x[10];*c;i;main(){int _e;for(;scanf("%d",&x)>0&&++_e;);for(c=x+_e;c-->x;i=100/ *x,printf("%d ",i- --_e));}
#define P printf(
#define A case
#define B break
#define L(i,e,t,f,s)for(o=i;o e;){strcpy(l[o t],l[o f]);c[o t]=c[s o];}
#define R(m,o){return b<1|b>m?m o:b;}
#define N(f)sscanf(f,"%d,%d",&i,&j)||sscanf(f,",%d",&j)
r,i,j,o,z,c[999],*C,x=1,y=1;char a[999],l[999][999],(*L)[999];D(){L(r,>y,,-1,--)r++?strcpy(l[o],l[o-1]+--x),c[o-1]=x,l[o-1][x]=0:0;c[y++]=strlen(l[o]);x=1;}U(){strcat(*L,l[y]);*C=strlen(*L);L(y+1,<r,-1,,++)--r;*l[r]=c[r]=0;}X(b)R(c[y-1],+1)Y(b)R(r,)main(){for(;;){z=i=y;L=l+--z;C=c+z;j=x;!r||y/r&&x>*C?P"end> "):P"%d,%d> ",y,x);scanf("%[^\n]%*c",a)?a[2]*=!!a[1],N(a)?y=Y(i+(i<0|*a=='+')*y),x=X(j+(j<0||strchr(a+1,'+'))*x):0:(*a=D(),scanf("%*c"));switch(*a){A'e':y=r;x=c[r-1]+1;B;A'b':y=1;x=1;B;A'L':for(o=y-4;++o<y+2;)o<0^o<r&&P"%c%s\n",o^z?' ' :'>',l[o]);for(o=x+1;--o;)P" ");P"^\n");B;A'l':puts(*L);B;A'p':i=1;j=0;N(a+2);for(o=Y(i)-1;o<Y(j);++o)puts(l[o]);B;A'A':y=r++;strcpy(l[y],a+2);x=c[y]=strlen(a+2);++x;++y;B;A'i':D();--y;x=X(0);A'r':strcpy(*L+x-1,a+2);*C=strlen(*L);x=1;++y>r&&++r;B;A'I':o=strlen(a+2);memmove(*L+x+o-1,*L+x-1,*C-x+1);*C+=o;memcpy(*L+x-1,a+2,o);x+=o;B;A'd':**L?**L=*C=0,x=1:U();y=y>r?r:y;B;A'j':y<r&&U();}}}

Limitación

Esto rompe definiciones como

#define A (x)

quitando el espacio que separa el nombre de la expansión, dando

#define A(x)

con un significado completamente diferente. Este caso está ausente de los conjuntos de prueba, por lo que no voy a abordarlo.

Sospecho que podría ser capaz de producir una versión más corta con una conversión in situ de varios pasos; podría intentarlo la próxima semana.

Toby Speight
fuente
Puede guardar un byte eliminando =al final de la definición de Oy cambiar el espacio que sigue a cada llamada Oen a =.
Zacharý
Esto es genial;) Acerca de la "limitación", vea también mi comentario sobre la pregunta en sí misma: detectar esto agregaría demasiada complejidad.
Felix Palmen
@Zachary, gracias por eso, olvidé cuando cambié el código general a ASCII específico O'\\'y O' 'ambos adquirieron un espacio.
Toby Speight
464 bytes
ceilingcat
2

C,  705   663  640 bytes

¡Gracias a @ Zacharý por jugar 40 bytes y gracias a @Nahuel Fouilleul por jugar 23 bytes!

#define A(x)(x>47&x<58|x>64&x<91|x>96&x<123)
#define K if(*C==47&(C[1]==47|p==47)){if(p==47)--G;for(c=1;c;*C++-92||c++)*C-10||--c;if(d)p=*G++=10,--d;
#define D if(!d&*C==35){d=1;if(p&p-10)p=*G++=10;}
#define S K}if((A(p)&A(*C))|(p==*C&l==43|p==45)|p==47&*C==42|p==95&(A(*C)|*C==95)|*C==95&(A(p)|p==95))p=*G++=32;}
#define W*C<33|*C==92
#define F{for(;W;C++)
c,d,e,p,l;g(char*C,char*G)F;for(;*C;*C>32&&*C-34&&*C-39&&(p=*G++=*C),*C-34&&*C-39&&C++){l=e=0;if(*C==34)l=34;if(*C==39)l=39;if(l)for(*G++=l,p=*G++=*++C;*C++-l|e%2;e=*(C-1)-92?0:e+1)p=*G++=*C;K}D if(d){if(W)F{*C-92||++d;*C-10||--d;if(!d){p=*G++=10;goto E;}}S}else{if(W)F;S}E:D}*G=0;}

Pruébalo en línea!

Steadybox
fuente
Puede for(;W;C++){}convertirse for(;W;C++);?
Zacharý
@ Zacharý que nunca se pidió. Es una herramienta para el último paso: eliminar espacios en blanco redundantes y comentarios.
Felix Palmen
Me refería a su código, no al desafío.
Zacharý
@ Zacharý jaja Ya veo ... raro cuando el código y la entrada son el mismo idioma;)
Felix Palmen
¿Funcionaría para 665 bytes? goo.gl/E6tk8V
Zacharý
2

Perl 5, 250 + 3 (-00n) , 167 + 1 (-p) bytes

$_.=<>while s/\\
//;s,(//.*)|(("|')(\\.|.)*?\3)|/?[^"'/]+,$1|$2?$2:$&=~s@(\S?)\K\s+(?=(.?))@"$1$2"=~/\w\w|\+\+|--|\/\*/&&$"@ger,ge;$d++&&$l+/^#/&&s/^/
/,$l=/^#/m if/./

Pruébalo en línea

Nahuel Fouilleul
fuente
Sí, acabo de poner una solución no óptima. Acabo de agregar el enlace tio, buscaré jugarlo cuando tenga tiempo.
Nahuel Fouilleul
las directivas de preprocesador están en su propia línea cuando se colocan antes del código, como en los casos de prueba, sin embargo, si es necesario,
agregaré el
1
fijo ver actualización
Nahuel Fouilleul
0

Python 2 , 479 456 445 434 502 497 bytes

e=enumerate
import re
u=re.sub
def f(s):
 r=()
 for l in u(r'\\\n','',s).split('\n'):
	s=p=w=0;L=[]
	for i,c in e(l):
	 if(p<1)*'//'==l[i:i+2]:l=l[:i]
	 if c in"'\""and w%2<1:
		if p in(c,0):L+=[l[s:i+1]];s=i+1
		p=[0,c][p<1]
	 w=[0,w+1]['\\'==c]
	r+=L+[l[s:]],
 S=''
 for l in r:s=''.join([u('. .',R,u('. .',R,u('\s+',' ',x))).strip(),x][i%2]for i,x in e(l));S+=['%s','\n%s\n'][s[:1]=='#']%s
 print u('\n\n','\n',S).strip()
def R(m):g=m.group(0);f=g[::2];return[f,g][f.isalnum()or f in'++ -- /*']

Pruébalo en línea!

Editar: Se ha corregido para incluir - -, + +y/ *

TFeld
fuente