Golf mój „pre-golf” C

12

tło

Do przesłania w C potrzebuję narzędzia do przetwarzania. Podobnie jak w wielu innych językach, białe znaki są przeważnie nieistotne w źródle C (ale nie zawsze!) - nadal sprawiają, że kod jest znacznie bardziej zrozumiały dla ludzi. W pełni golfowy program C, który nie zawiera ani jednej nadmiarowej białej spacji, często jest ledwo czytelny.

Dlatego lubię pisać mój kod w C dla tym białych znaków i czasami komentarzy, więc program zachowuje zrozumiałą strukturę podczas pisania. Ostatnim krokiem jest usunięcie wszystkich komentarzy i zbędnych białych znaków. Jest to żmudne i bezmyślne zadanie, które naprawdę powinien wykonać stażysta programu komputerowego.

Zadanie

Napisz program lub funkcję, która eliminuje komentarze i zbędne białe znaki z jakiegoś „wstępnie golfowego” źródła C zgodnie z następującymi zasadami:

  • A \(ukośnik odwrotny) jako ostatni znak w linii jest kontynuacją linii . Jeśli to znajdziesz, musisz traktować następujący wiersz jako część tego samego wiersza logicznego (możesz na przykład całkowicie usunąć \następny i następujący \n(nowy wiersz) przed zrobieniem czegoś innego)
  • Komentarze będą używać tylko formatu jednowierszowego, zaczynając od //. Tak, aby je usunąć, zignorować resztę logicznej linii gdziekolwiek spotkać //poza ciągiem znaków (patrz poniżej).
  • Znaki białych znaków to (spacja), \t(tab) i \n(nowa linia, więc tutaj koniec linii logicznej).
  • Gdy znajdziesz sekwencję białych znaków, sprawdź otaczające ją znaki niebiałe. Jeśli

    • oba są alfanumeryczne lub podkreślone (zakres [a-zA-Z0-9_]) lub
    • oba są +lub
    • oba są -lub
    • poprzedni jest, /a następny jest*

    następnie zastąp sekwencję pojedynczym znakiem spacji ( ).

    W przeciwnym razie całkowicie wyeliminuj sekwencję.

    Ta reguła ma kilka wyjątków :

    • Dyrektywy preprocesora muszą pojawiać się w osobnych wierszach na wydruku. Dyrektywa preprocesora to linia zaczynająca się od #.
    • Wewnątrz literału łańcuchowego lub literału znakowego nie należy usuwać żadnych białych znaków. Dowolny "(podwójny cudzysłów) / '(pojedynczy cudzysłów), który nie jest poprzedzony nieparzystą liczbą odwrotnych ukośników ( \), rozpoczyna lub kończy literał łańcuchowy / literał znakowy . Masz gwarancję, że literały ciągów i znaków kończą się w tym samym wierszu, w którym zaczęły. literałów ciągów i literałów znakowych nie można zagnieżdżać, więc 'wewnątrz literału ciągowego , jak również "wewnątrz literału znakowego , nie ma żadnego specjalnego znaczenia.

Specyfikacja we / wy

Dane wejściowe i wyjściowe muszą być albo ciągami znaków (ciągami znaków), w tym znakami nowej linii, albo tablicami / listami ciągów znaków, które nie zawierają znaków nowej linii. Jeśli zdecydujesz się użyć tablic / list, każdy element reprezentuje linię, więc nowe linie są ukryte po każdym elemencie.

Możesz założyć, że dane wejściowe są poprawnym kodem źródłowym programu C. Oznacza to również, że zawiera tylko drukowalne znaki ASCII, tabulatory i znaki nowej linii. Niezdefiniowane zachowanie na nieprawidłowo wprowadzonych danych jest dozwolone.

Wiodące i końcowe białe znaki / puste linieniedozwolone .

Przypadki testowe

  1. Wejście

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

    wynik

    main(){printf("Hello, World!");}
    
  2. Wejście

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

    wynik

    #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. Wejście

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

    wynik

    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. Wejście

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

    wynik

    x;
    #include<stdio.h>
    int main(){puts("hello // there");}
    
  5. wejście (przykład z prawdziwego świata)

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

    wynik

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

To jest , więc wygrywa najkrótsza (w bajtach) poprawna odpowiedź.

Felix Palmen
źródło

Odpowiedzi:

4

Pip , 148 135 133 138 bajtów

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

Bajty są liczone w CP-1252 , tak i ·to jeden bajt każda. Zauważ, że oczekuje to kodu C jako pojedynczego argumentu wiersza poleceń, który (w rzeczywistym wierszu poleceń) wymagałby użycia obfitych sekwencji specjalnych. O wiele łatwiej w Wypróbuj online!

Wyjaśnienie nieco nie golfowej wersji

Kod wykonuje kilka operacji wymiany z kilkoma sztuczkami.

Kontynuacje odwrotnego ukośnika

Wszyscy RMwystępujemy dosłowny ciąg

"\
"

to jest ukośnik odwrotny, po którym następuje nowa linia.

Literały ciągów i znaków

Używamy zamiennika wyrażenia regularnego z funkcją wywołania zwrotnego:

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

{
 lPBa
 C(++i + 191)
}

Wyrażenie regularne pasuje do pojedynczego lub podwójnego cudzysłowu, po którym następuje chciwość, .*?która pasuje do 0 lub więcej znaków, tak mało jak to możliwe. Mamy negatywny wygląd, aby upewnić się, że poprzednia postać nie była odwrotna; następnie dopasowujemy parzystą liczbę odwrotnych ukośników, a następnie ponownie ogranicznik otwarcia.

Funkcja zwrotna przyjmuje literał ciąg / znak i przesuwa go na koniec listy l. Następnie zwraca znak zaczynający się od kodu znaku 192 ( À) i rosnący z każdym zastąpieniem literału. Tak więc kod jest transformowany w następujący sposób:

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

printf(À, Á);

Te znaki zastępcze z pewnością nie występują w kodzie źródłowym, co oznacza, że ​​możemy je później jednoznacznie zastąpić.

Komentarze

`//.*`

x

Wyrażenia regularne dopasowują //plus wszystko aż do nowej linii i zastępuje je x(ustawione na pusty ciąg).

Dyrektywy preprocesora

`#.*`

_WR'¶

Zawija przebiegi znaków innych niż nowa linia, zaczynając od znaku funta .

Miejsca, których nie należy eliminować

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

{
 a R w '·
}

Dużo się tu dzieje. Pierwsza część generuje tę listę wyrażeń regularnych do zastąpienia:

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

Zwróć uwagę na użycie lookaheads dopasować, na przykład, tylko e in define P printf. W ten sposób ten mecz nie zużywa P, co oznacza, że ​​może go użyć następny mecz.

Generujemy tę listę wyrażeń regularnych poprzez mapowanie funkcji na listę, na której znajduje się lista

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

a funkcja robi to z każdym elementem:

(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

Kiedy mamy nasze wyrażenia regularne, zastępujemy ich wystąpienia tą funkcją wywołania zwrotnego:

{aRw'·}

który zamienia ciąg białych znaków w każdym dopasowaniu na ·.

Eliminacja i czyszczenie białych znaków

[w `¶+` '·]

[x n s]

Trzy kolejne zamiany zastępują pozostałe serie białych znaków ( w) pustym ciągiem ( x), nowymi znakami i ·spacją.

Podstawienie wsteczne literałów ciągów i znaków

C(192+,#l)

l

Tworzymy listę wszystkich znaków, których używaliśmy jako zamienników literałów, pobierając 192 + range(len(l))i konwertując na znaki. Następnie możemy zastąpić każdy z nich odpowiednim literałem w l.

I to wszystko! Powstały ciąg jest automatycznie drukowany.

DLosc
źródło
Świetnie, jestem pod wrażeniem (+1)! Dołączenie //literału ciągu jest zdecydowanie dobrym pomysłem na przypadek testowy, dodam go jutro.
Felix Palmen,
Uhm ... teraz też znalazłem tutaj subtelny błąd ...
Felix Palmen,
Wybiorę zwycięzcę po 14 dniach (koniec przyszłego tygodnia), a twoje rozwiązanie będzie pierwszym kandydatem, jeśli znajdziesz czas na naprawienie tego błędu. W tej chwili masz najniższy wynik :)
Felix Palmen
1
@FelixPalmen Naprawiono!
DLosc
7

Haskell , 327 360 418 394 bajtów

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

Wypróbuj online!

Pisanie to była świetna zabawa! Najpierw fpojawia się funkcja i usuwa wszystkie ukośniki odwrotne na końcu wierszy, a następnie linesdzieli je na listę ciągów znaków na nowej linii. Następnie odwzorowujemy kilka funkcji na linie i łączymy je wszystkie razem. Funkcje te: usuwanie białych znaków z lewej ( t) i z prawej ( r.t.rgdzie rjest reverse); usuń białe znaki ze środka, ignorując literały ciągów i znaków, a także usuwając komentarze ( w); i na końcu dodaje znak nowej linii na końcu, jeśli linia zaczyna się od #. Po połączeniu wszystkich linii z powrotem gszuka znaków # i upewnia się, że poprzedza je nowa linia.

wjest trochę skomplikowany, więc wyjaśnię to dokładniej. Najpierw sprawdzam „//”, ponieważ wwiem, że nie jestem literałem ciągiem, wiem, że to komentarz, więc zostawiam resztę wiersza. Następnie sprawdzam, czy głowa jest ogranicznikiem literału łańcucha lub znaku. Jeśli tak, przygotowuję go i przekazuję pałeczkę, do lktórej biegną postacie, śledząc stan „ucieczki”, z nktórym będzie to prawdą, jeśli wystąpi parzysta liczba kolejnych cięć. Kiedy lwykryje ogranicznik i nie znajduje się w stanie ucieczki, przekazuje pałeczkę z powrotem w, przycinając w celu wyeliminowania białych znaków po literałach, ponieważ woczekuje, że pierwszy znak nie będzie białymi znakami. Kiedywnie znajduje separatora, używa span do szukania białych znaków w ogonie. Jeśli istnieje, sprawdza, czy nie można nawiązać kontaktu z otaczającymi go postaciami, i jeśli tak, wstawia spację. Następnie powtarza się po zakończeniu spacji. Jeśli nie było białych znaków, nie jest wstawiana spacja i i tak się przesuwa.

EDYCJA: Bardzo dziękuję @DLosc za wskazanie błędu w moim programie, który faktycznie doprowadził mnie do tego, że mogę go również skrócić! Brawo dla dopasowania wzoru!

EDIT2: Jestem idiotą, który nie skończył czytać specyfikacji! Jeszcze raz dziękuję DLosc za wskazanie tego!

EDYCJA 3: Właśnie zauważyłem jakąś irytującą rzecz polegającą e=elemna redukcji typu, która Char->[Char]->Boolz jakiegoś powodu zmieniła się , powodując awarię e[a,q]. Musiałem dodać podpis typu, aby wymusić poprawność. Czy ktoś wie, jak to naprawić? Nigdy wcześniej nie miałem tego problemu w Haskell. TIO

EDIT4: pokazała mi szybka poprawka dla błędu @ FelixPalmen. Mogę spróbować zagrać w golfa później, kiedy będę miał trochę czasu.

EDIT5: -24 bajtów dzięki @Lynn! Dziękuję Ci! Nie wiedziałem, że możesz przypisywać rzeczy do globalnego zasięgu za pomocą dopasowywania wzorców, jakby n:c:z=...to było naprawdę fajne! Również dobrym pomysłem jest stworzenie operatora na elemżyczenie, o którym myślałem.

użytkownik 1472751
źródło
2
Wpadasz na przerażające ograniczenie monomorfizmu ; zdefiniowanie e x y=elem x y(a nawet e x=elem x) rozwiązuje problem. (Zmieniłem nazwę ena operatora (!).)
Lynn
3

C, 497 494 490 489 bajtów

Ponieważ przetwarzamy C, zróbmy to za pomocą C! Funkcja f()pobiera dane wejściowe ze wskaźnika char pi dane wyjściowe do wskaźnika qi zakłada, że ​​dane wejściowe są w 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)();}

Zakładamy, że plik jest poprawnie sformułowany - literały ciągów i znaków są zamknięte, a jeśli w ostatnim wierszu jest komentarz, musi być nowy wiersz, aby go zamknąć.

Wyjaśnienie

Obawiam się, że wersja z golfem jest tylko trochę bardziej czytelna:

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

Implementuje maszynę stanu przez rekurencję ogona. Makra pomocnicze i zmienne są

  • Oo O utput
  • Rdo R EaD wejścia dor
  • Vw celu ustalenia v różnych znaków identyfikatora (od !isalnum('_'))
  • poraz q- wskaźniki I / O, jak opisano
  • r- ostatni znak być r EAD
  • s- s ostatni niedawny znak spacji
  • t- t ag podczas pracy nad dyrektywą preprocesora

Nasze stany są

  • a() - normalny kod C.
  • b() - dosłowny ciąg znaków
  • c() - komentarz
  • d() - normalny kod C po odczytaniu r
  • e() - sekwencja ewakuacyjna
  • f() - stan początkowy (główna funkcja)
  • g() - w białych znakach
  • h()- w białych znakach - wysyłka do g()lubi()
  • i() - natychmiast po spacji - czy musimy wstawić znak spacji?
  • j() - początkowa biała spacja - nigdy nie wstawiaj spacji

Program testowy

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

To produkuje

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

Ograniczenie

To łamie definicje takie jak

#define A (x)

usuwając spację oddzielającą nazwę od rozwinięcia, dając

#define A(x)

o zupełnie innym znaczeniu. Ten przypadek jest nieobecny w zestawach testowych, więc nie zamierzam się nim zajmować.

Podejrzewam, że być może uda mi się stworzyć krótszą wersję z wieloprzebiegową konwersją lokalną - mógłbym spróbować w przyszłym tygodniu.

Toby Speight
źródło
Możesz zapisać jeden bajt, usuwając =na końcu definicji Oi zmieniając miejsce po każdym wywołaniu Ona =.
Zacharý
To wspaniale;) O „ograniczeniu” patrz także mój komentarz do samego pytania - wykrycie tego spowodowałoby zbyt dużą złożoność.
Felix Palmen,
@Zachary - dziękuję za to - zapomniałem, kiedy zmieniłem ogólny kod na specyficzny dla ASCII O'\\'i O' 'oba uzyskały spację.
Toby Speight,
464 bajty
ceilingcat
2

C  705   663  640 bajtów

Dzięki @ Zacharý za grę w golfa 40 bajtów i dzięki @Nahuel Fouilleul za grę w golfa 23 bajty!

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

Wypróbuj online!

Steadybox
źródło
Może for(;W;C++){}zostać for(;W;C++);?
Zacharý
@ Zacharý, o który nigdy nie pytano. To narzędzie dla ostatniego kroku: usuń zbędne białe znaki i komentarze.
Felix Palmen,
Miałem na myśli jego kod, a nie wyzwanie.
Zacharý
@ Zacharý haha ​​Widzę ... dziwne, gdy kod i dane wejściowe są w tym samym języku;)
Felix Palmen
Czy zadziałałoby to na 665 bajtów? goo.gl/E6tk8V
Zacharý
2

Perl 5, 250 + 3 (-00n) , 167 + 1 (-p) bajtów

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

Wypróbuj online

Nahuel Fouilleul
źródło
Tak, właśnie wprowadziłem nieoptymalne rozwiązanie. Właśnie dodałem link tio, będę szukał gry w golfa, kiedy będę miał czas.
Nahuel Fouilleul
dyrektywa preprocesora znajduje się we własnej linii, gdy jest umieszczana przed kodem, podobnie jak w przypadkach testowych, ale jeśli to konieczne, dodam zmianę
Nahuel Fouilleul
1
naprawiono patrz aktualizacja
Nahuel Fouilleul
0

Python 2 , 479 456 445 434 502 497 bajtów

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'++ -- /*']

Wypróbuj online!

Edycja: Fixed zawierać - -, + +oraz/ *

TFeld
źródło