Golf meu C "pré-golfe"

12

fundo

Para meus envios de em C, preciso de uma ferramenta de processamento. Como em muitas outras línguas, o espaço em branco é principalmente irrelevante na fonte C (mas nem sempre!) - ainda torna o código muito mais compreensível para os seres humanos. Um programa C totalmente desenvolvido que não contém um único espaço em branco redundante geralmente é pouco legível.

Portanto, eu gosto de escrever meu código em C para um envio de incluindo espaço em branco e, às vezes, comentários, para que o programa mantenha uma estrutura compreensível enquanto escreve. O último passo é remover todos os comentários e espaços em branco redundantes. Esta é uma tarefa tediosa e irracional que realmente deve ser realizada por um estagiário de um programa de computador.

Tarefa

Escreva um programa ou função que elimine comentários e espaços em branco redundantes de alguma fonte C "pré-golfada" de acordo com as seguintes regras:

  • A \(barra invertida) como o último caractere de uma linha é uma continuação de linha . Se você encontrar isso, deverá tratar a seguinte linha como parte da mesma linha lógica (você pode, por exemplo, remover completamente a (nova linha) \e a seguinte \n(nova linha) antes de fazer qualquer outra coisa)
  • Os comentários usarão apenas o formato de uma linha, começando com //. Portanto, para removê-los, você ignora o restante da linha lógica sempre que encontrar //fora de uma literal de cadeia de caracteres (veja abaixo).
  • Os caracteres de espaço em branco são (espaço), \t(tabulação) e \n(nova linha, então aqui o final de uma linha lógica).
  • Quando você encontrar uma sequência de espaço em branco, examine os caracteres que não são de espaço em branco ao seu redor. E se

    • ambos são alfanuméricos ou sublinhados (intervalo [a-zA-Z0-9_]) ou
    • ambos são +ou
    • ambos são -ou
    • o anterior é /e o seguinte é*

    substitua a sequência por um único caractere espaço ( ).

    Caso contrário, elimine a sequência completamente.

    Esta regra tem algumas exceções :

    • As diretivas de pré-processador devem aparecer em suas próprias linhas na saída. Uma diretiva de pré-processador é uma linha que começa com #.
    • Dentro de uma string literal ou literal de caracteres , você não deve remover nenhum espaço em branco. Qualquer "(aspas duplas) / '(aspas simples) que não seja precedido diretamente por um número ímpar de barras invertidas ( \) inicia ou termina uma string literal / literal de caracteres . Você tem certeza de que os literais de sequência e caractere terminam na mesma linha em que foram iniciados. strings literais e literais de caracteres não podem ser aninhados, por isso, um 'dentro de um literal de cadeia , bem como "dentro de um caractere literal não tem nenhum significado especial.

Especificação de E / S

A entrada e a saída devem ser sequências de caracteres (cadeias), incluindo caracteres de nova linha ou matrizes / listas de cadeias que não contêm caracteres de nova linha. Se você optar por usar matrizes / listas, cada elemento representará uma linha; portanto, as novas linhas estarão implícitas após cada elemento.

Você pode assumir que a entrada é um código fonte válido do programa C. Isso também significa que ele contém apenas caracteres ASCII, guias e novas linhas imprimíveis. Comportamento indefinido na entrada malformada é permitido.

Linhas em branco à esquerda e à esquerda / linhas vazias não são permitidas .

Casos de teste

  1. entrada

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

    resultado

    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));
    }
    

    resultado

    #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));
    }
    

    resultado

    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");
    }
    

    resultado

    x;
    #include<stdio.h>
    int main(){puts("hello // there");}
    
  5. input (um exemplo do 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();
            }
        }
    }
    

    resultado

    #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();}}}
    

Isso é , então a resposta mais curta (em bytes) válida vence.

Felix Palmen
fonte
relacionado , relacionado
HyperNeutrino 9/17/17

Respostas:

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]

Os bytes são contados no CP-1252 , portanto, e ·são um byte cada. Observe que isso espera o código C como um único argumento de linha de comando, que (em uma linha de comando real) exigiria o uso de sequências de escape abundantes. É muito mais fácil em Experimente online!

Explicação da versão ligeiramente não destruída

O código realiza várias operações de substituição, com alguns truques.

Continuações de barra invertida

Nós RMtodas as ocorrências da string literal

"\
"

isto é, barra invertida seguida por nova linha.

Literais de cadeia e caracteres

Usamos uma substituição de regex com uma função de retorno de chamada:

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

{
 lPBa
 C(++i + 191)
}

A regex corresponde a uma citação simples ou dupla, seguida por uma não gananciosa .*?que corresponde a 0 ou mais caracteres, o mínimo possível. Temos uma visão negativa para garantir que o personagem anterior não seja uma barra invertida; em seguida, correspondemos a um número par de barras invertidas seguidas pelo delimitador de abertura novamente.

A função de retorno de chamada pega o literal de string / caractere e o envia para o final da lista l. Em seguida, ele retorna um caractere começando com o código de caractere 192 ( À) e aumentando com cada literal substituído. Assim, o código é transformado da seguinte maneira:

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

printf(À, Á);

É garantido que esses caracteres de substituição não ocorram no código fonte, o que significa que podemos substituí-los sem ambiguidades posteriormente.

Comentários

`//.*`

x

O regex corresponde //mais tudo até a nova linha e substitui por x(predefinido para a sequência vazia).

Diretivas de pré-processador

`#.*`

_WR'¶

Agrupa séries de caracteres que não são de nova linha, começando com um sinal de libra .

Espaços que não devem ser eliminados

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

{
 a R w '·
}

Há muita coisa acontecendo aqui. A primeira parte gera esta lista de regexes para substituir:

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

Observe o uso de lookaheads para corresponder, por exemplo, apenas ao e in define P printf. Dessa forma, essa partida não consome o P, o que significa que a próxima partida pode usá-lo.

Geramos essa lista de expressões regulares mapeando uma função para uma lista, onde a lista contém

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

e a função faz isso para 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

Depois que tivermos nossas expressões regulares, substituímos suas ocorrências por esta função de retorno de chamada:

{aRw'·}

que substitui a execução do espaço em branco em cada correspondência por ·.

Eliminação e limpeza de espaço em branco

[w `¶+` '·]

[x n s]

Três substituições sucessivas substituem as execuções restantes do espaço em branco ( w) pela sequência vazia ( x), as execuções pela nova linha e ·pelo espaço.

Substituição traseira de literais de cadeia e caracteres

C(192+,#l)

l

Construímos uma lista de todos os caracteres que usamos como substitutos de literais, obtendo 192 + range(len(l))e convertendo em caracteres. Em seguida, podemos substituir cada um deles pelo literal associado l.

E é isso! A sequência resultante é impressa automaticamente.

DLosc
fonte
Ótimo, estou impressionado (+1)! Incluindo um// literal interno de uma string é definitivamente uma boa ideia para um caso de teste. Acrescentarei um amanhã.
Felix Palmen
Uhm ... agora eu encontrei um bug sutil aqui também ...
Felix Palmen
Vou escolher um vencedor depois de 14 dias (final da próxima semana) e sua solução seria o primeiro candidato se você encontrar tempo para corrigir esse bug. No momento, você tem a pontuação mais baixa :)
Felix Palmen
1
@FelixPalmen Fixed!
DLosc 16/11/19
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

Experimente online!

Foi muito divertido escrever! Primeiro, a ffunção aparece e remove todas as barras invertidas no final das linhas e depois a linesdivide em uma lista de strings nas novas linhas. Em seguida, mapeamos várias funções nas linhas e concatenamos todas juntas novamente. Essas funções: tira o espaço em branco da esquerda ( t) e da direita ( r.t.ronde restá reverse); remova os espaços em branco do meio, ignorando os literais de string e de caracteres, além de remover comments ( w); e, finalmente, adiciona um caractere de nova linha ao final se a linha começar com um #. Depois que todas as linhas são concatenadas novamente, gprocura por # caracteres e garante que eles sejam precedidos por uma nova linha.

wé um pouco complexo, então vou explicar melhor. Primeiro, verifico "//", pois wsei que não estou em uma string literal. Sei que isso é um comentário, então deixo o resto da linha. Em seguida, verifico se o cabeçalho é um delimitador para uma string ou caractere literal. Se for, eu o prefixo e passo o bastão para o lqual os personagens passam, rastreando o estado de "escape" com o nqual será verdadeiro se houver um número par de barras consecutivas. Quando ldetecta um delimitador e não está no estado de escape, ele passa o bastão de volta para w, aparando para eliminar o espaço em branco após o literal, porque wespera que o primeiro caractere não seja em branco. Quandownão encontra um delimitador, usa span para procurar espaços em branco na cauda. Se houver, verifica se os caracteres ao seu redor não podem ser colocados em contato e, se houver, insere um espaço. Em seguida, ele ocorre novamente após o término do espaço em branco. Se não houvesse espaço em branco, nenhum espaço é inserido e ele continua assim mesmo.

EDIT: Muito obrigado ao @DLosc por apontar um erro no meu programa que realmente levou a uma maneira de reduzi-lo também! Viva a correspondência de padrões!

EDIT2: Eu sou um idiota que não terminou de ler as especificações! Mais uma vez obrigado DLosc por apontar isso!

EDIT3: Acabei de notar uma coisa chata de redução de tipo que virou e=elem transformou Char->[Char]->Boolpor algum motivo, interrompendo assim e[a,q]. Eu tive que adicionar uma assinatura de tipo para forçá-la a estar correta. Alguém sabe como eu poderia consertar isso? Eu nunca tive esse problema em Haskell antes. TIO

EDIT4: correção rápida para bug @FelixPalmen me mostrou. Eu posso tentar jogar no golfe mais tarde, quando tiver algum tempo.

EDIT5: -24 bytes graças a @Lynn! Obrigado! Eu não sabia que você poderia atribuir coisas no escopo global usando a correspondência de padrões, como n:c:z=...isso é muito legal! Também é uma boa ideia fazer um operador por elemdesejo, pensei nisso.

user1472751
fonte
1
Eu espremi 24 bytes .
Lynn
2
Você está enfrentando a temida restrição de monomorfismo ; definir e x y=elem x y(ou até e x=elem x) resolve seu problema. (I renomeado epara um operador, (!).)
Lynn
3

C, 497 494 490 489 bytes

Como estamos processando C, vamos fazê-lo usando C! A função f()recebe a entrada do ponteiro de char pe as saídas para o ponteiro qe assume que a entrada está em 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)();}

Assumimos que o arquivo esteja bem formado - os literais de string e de caracteres estão fechados e, se houver um comentário na linha final, deve haver uma nova linha para fechá-lo.

Explicação

A versão pré-golfe é apenas um pouco mais legível, receio:

#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)();}

Ele implementa uma máquina de estado por recursão da cauda. As macros e variáveis ​​auxiliares são

  • Opara o utput
  • Ra r de entrada em EADr
  • Vpara determinar v caracteres identificador alid (uma vez !isalnum('_'))
  • pe q- ponteiros de E / S, conforme descrito
  • r- último caractere a ser r ead
  • s- s caractere recente não-espaço em branco recente
  • t- t ag quando se trabalha em um pré-processador diretiva

Nossos estados são

  • a() - código C normal
  • b() - literal da string
  • c() - Comente
  • d() - código C normal, depois de ler r
  • e() - sequência de fuga
  • f() - estado inicial (função principal)
  • g() - em branco
  • h()- em branco - expedição para g()oui()
  • i() - imediatamente após o espaço em branco - precisamos inserir um caractere de espaço?
  • j() - espaço em branco inicial - nunca insira um caractere de espaço

Programa de teste

#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";);
}

Isso produz

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();}}}

Limitação

Isso quebra definições como

#define A (x)

removendo o espaço que separa o nome da expansão, fornecendo

#define A(x)

com um significado completamente diferente. Este caso está ausente nos conjuntos de teste, por isso não vou abordá-lo.

Eu suspeito que talvez eu consiga produzir uma versão mais curta com uma conversão no local com várias passagens - talvez eu tente na próxima semana.

Toby Speight
fonte
Você pode salvar um byte removendo o =no final da definição Oe alterando o espaço que segue cada chamada para Opara =.
Zachary
Isso é ótimo;) Sobre a "limitação", veja também o meu comentário sobre a pergunta em si - detectar isso adicionaria muita complexidade.
Felix Palmen
@ Zachary - obrigado por isso - eu esqueci quando mudei o código geral para ASCII-specific que O'\\'e O' 'ambos adquiriram um espaço.
Toby Speight
464 bytes
roofcat
2

C,  705   663  640 bytes

Obrigado a @ Zacharý por jogar 40 bytes e a @Nahuel Fouilleul por jogar 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;}

Experimente online!

Steadybox
fonte
Pode for(;W;C++){}se tornar for(;W;C++);?
Zachary
@ Zacharý que nunca foi solicitado. É uma ferramenta para o último passo: remova espaços em branco e comentários redundantes.
Felix Palmen
Eu estava me referindo ao código dele, não ao desafio.
Zachary
@ Zachary haha eu vejo ... estranho quando código e de entrada são a mesma língua;)
Felix Palmen
Isso funcionaria 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/./

Experimente online

Nahuel Fouilleul
fonte
Sim, eu apenas coloquei uma solução não ideal. Acabei de adicionar o link tio, procurarei jogar quando tiver tempo.
Nahuel Fouilleul 10/11
pré-processador directiva estão em sua própria linha quando são colocados antes do código, como nos casos de teste no entanto se for necessário eu vou adicionar a mudança
Nahuel FOUILLEUL
1
corrigido, ver atualização
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'++ -- /*']

Experimente online!

Edit: Corrigido para incluir - -, + +e/ *

TFeld
fonte