Golf my "pre-golfed" C

12

Hintergrund

Für meine Einsendungen in C benötige ich ein Verarbeitungswerkzeug. Wie in vielen anderen Sprachen ist Whitespace in C-Quellen meistens irrelevant (aber nicht immer!) - macht den Code dennoch für den Menschen viel verständlicher. Ein vollwertiges C-Programm, das häufig kein redundantes Leerzeichen enthält, ist kaum lesbar.

Aus diesem Grund schreibe ich meinen Code gerne in C für eine Einreichung mit Leerzeichen und manchmal Kommentaren, damit das Programm beim Schreiben eine nachvollziehbare Struktur beibehält. Der letzte Schritt besteht darin, alle Kommentare und überflüssigen Leerzeichen zu entfernen. Dies ist eine mühsame und sinnlose Aufgabe, die eigentlich ein Praktikant eines Computerprogramms erledigen sollte .

Aufgabe

Schreiben Sie ein Programm oder eine Funktion, die Kommentare und überflüssige Leerzeichen aus einer C-Quelle "vor dem Golfen" gemäß den folgenden Regeln entfernt:

  • Ein \(Backslash) als letztes Zeichen in einer Zeile ist eine Zeilenfortsetzung . Wenn Sie dies feststellen, müssen Sie die folgende Zeile als Teil derselben logischen Zeile behandeln (Sie können beispielsweise die \und die folgenden \nZeilen (newline) vollständig entfernen, bevor Sie etwas anderes tun).
  • Kommentare verwenden nur das einzeilige Format, beginnend mit //. Um sie zu entfernen, ignorieren Sie den Rest der logischen Zeile, wo immer Sie //außerhalb eines Zeichenfolgenliterals vorfinden (siehe unten).
  • Leerzeichen sind (Leerzeichen), \t(Tabulator) und \n(Zeilenvorschub, hier also das Ende einer logischen Zeile).
  • Wenn Sie eine Folge von Leerzeichen finden, untersuchen Sie die Nicht-Leerzeichen-Zeichen, die sie umgeben. Wenn

    • beide sind alphanumerisch oder unterstrichen (Range [a-zA-Z0-9_]) oder
    • beide sind +oder
    • beide sind -oder
    • das vorhergehende ist /und das folgende ist*

    Ersetzen Sie dann die Sequenz durch ein einzelnes Leerzeichen ( ).

    Andernfalls beseitigen Sie die Reihenfolge vollständig.

    Diese Regel hat einige Ausnahmen :

    • Präprozessor-Direktiven müssen in Ihrer Ausgabe in eigenen Zeilen erscheinen. Eine Präprozessor-Direktive ist eine Zeile, die mit beginnt #.
    • In einem String-Literal oder Zeichen-Literal sollten Sie keine Leerzeichen entfernen. Jedes "(doppelte Anführungszeichen) / '(einfache Anführungszeichen), dem keine ungerade Anzahl von umgekehrten Schrägstrichen ( \) vorangestellt ist, beginnt oder endet ein Zeichenkettenliteral / Zeichenliteral . Es ist garantiert, dass Zeichenketten- und Zeichenliterale in derselben Zeile enden, in der sie begonnen haben. Stringliterale und Zeichenliterale können nicht verschachtelt werden, so dass ein 'in einem Stringliteral , sowie ein "in einem Zeichenliteral haben keine besondere Bedeutung.

E / A-Spezifikation

Eingabe und Ausgabe müssen entweder Zeichenfolgen (Strings) einschließlich Zeilenumbrüchen oder Arrays / Listen von Strings sein, die keine Zeilenumbrüche enthalten. Wenn Sie Arrays / Listen verwenden, stellt jedes Element eine Zeile dar, sodass die Zeilenumbrüche nach jedem Element implizit sind.

Sie können davon ausgehen, dass die Eingabe ein gültiger C-Programm-Quellcode ist. Dies bedeutet auch, dass es nur druckbare ASCII-Zeichen, Tabulatoren und Zeilenumbrüche enthält. Undefiniertes Verhalten bei fehlerhafter Eingabe ist zulässig.

Führende und nachfolgende Leerzeichen / Leerzeilen sind nicht erlaubt .

Testfälle

  1. Eingang

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

    Ausgabe

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

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

    Ausgabe

    #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. Eingang

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

    Ausgabe

    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. Eingang

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

    Ausgabe

    x;
    #include<stdio.h>
    int main(){puts("hello // there");}
    
  5. Eingabe (ein Beispiel aus der Praxis)

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

    Ausgabe

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

Dies ist , also gewinnt die kürzeste (in Bytes) gültige Antwort.

Felix Palmen
quelle
verwandt , verwandt
HyperNeutrino
Lassen Sie uns diese Diskussion im Chat fortsetzen .
DLosc

Antworten:

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 werden gezählt , in CP-1252 , so und ·sind jeweils ein Byte. Beachten Sie, dass dies den C-Code als einzelnes Befehlszeilenargument erwartet, das (in einer tatsächlichen Befehlszeile) die Verwendung zahlreicher Escape-Sequenzen erfordert. Bei Try it online ist es viel einfacher !

Erklärung der leicht ungolften Version

Der Code führt eine Reihe von Ersetzungsvorgängen mit ein paar Tricks aus.

Backslash-Fortsetzungen

Wir RMalle Vorkommen der wörtlichen Zeichenfolge

"\
"

Das heißt, Backslash, gefolgt von Newline.

Zeichenketten- und Zeichenliterale

Wir verwenden einen Regex-Ersatz mit einer Rückruffunktion:

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

{
 lPBa
 C(++i + 191)
}

Der reguläre Ausdruck entspricht einem einfachen oder doppelten Anführungszeichen, gefolgt von einem nicht gierigen Ausdruck .*?, der 0 oder mehr Zeichen entspricht, so wenig wie möglich. Wir haben ein negatives Aussehen, um sicherzustellen, dass das vorherige Zeichen kein Backslash war. dann passen wir eine gerade Anzahl von Backslashes an, gefolgt vom öffnenden Begrenzer.

Die Rückruffunktion nimmt das Zeichen / Zeichen-Literal und verschiebt es an den Ende der Liste l. Anschließend wird ein Zeichen zurückgegeben, das mit dem Zeichencode 192 ( À) beginnt und mit jedem ersetzten Literal zunimmt. So wird Code wie folgt transformiert:

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

printf(À, Á);

Es wird garantiert, dass diese Ersatzzeichen im Quellcode nicht vorkommen, was bedeutet, dass wir sie später eindeutig zurücksetzen können.

Bemerkungen

`//.*`

x

Der //reguläre Ausdruck stimmt mit allem bis zum Zeilenumbruch überein und wird durch x(voreingestellt auf den leeren String) ersetzt.

Präprozessor-Direktiven

`#.*`

_WR'¶

Zeilenumbrüche von Zeichen, die keine Zeilenumbrüche sind, beginnen mit einem Doppelkreuz .

Leerzeichen, die nicht beseitigt werden sollten

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

{
 a R w '·
}

Hier ist viel los. Der erste Teil generiert diese Liste der zu ersetzenden regulären Ausdrücke:

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

Beachten Sie die Verwendung von Lookaheads anzupassen, beispielsweise nur die e in define P printf. Auf diese Weise verbraucht dieses Match nicht das P, was bedeutet, dass das nächste Match es verwenden kann.

Wir generieren diese Liste der regulären Ausdrücke, indem wir eine Funktion einer Liste zuordnen, in der die Liste enthalten ist

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

und die Funktion tut dies für jedes Element:

(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

Sobald wir unsere regulären Ausdrücke haben, ersetzen wir ihre Vorkommen durch diese Rückruffunktion:

{aRw'·}

Das ersetzt die Leerzeichenfolge in jedem Spiel mit ·.

Leerzeichen entfernen und bereinigen

[w `¶+` '·]

[x n s]

Drei aufeinanderfolgende Ersetzungen ersetzen die verbleibenden Leerzeichenfolgen ( w) für leere Zeichenfolgen ( x), Zeilenumbrüche (Newline) und ·Leerzeichen.

Rückersetzung von Zeichenketten- und Zeichenliteralen

C(192+,#l)

l

Wir erstellen eine Liste aller Zeichen, die wir als Ersetzungen für Literale verwendet haben, indem wir Zeichen nehmen 192 + range(len(l))und in Zeichen konvertieren. Wir können dann jedes dieser Elemente durch das zugehörige Literal in ersetzen l.

Und das ist es! Die resultierende Zeichenfolge wird automatisch gedruckt.

DLosc
quelle
Großartig, ich bin beeindruckt (+1)! Das Einfügen eines //Strings in ein Literal ist definitiv eine gute Idee für einen Testfall, ich werde morgen eines hinzufügen.
Felix Palmen
Ähm ... jetzt habe ich auch hier einen kleinen Fehler gefunden ...
Felix Palmen
Ich werde nach 14 Tagen (Ende nächster Woche) einen Gewinner auswählen und Ihre Lösung wäre der erste Kandidat, wenn Sie Zeit finden, diesen Fehler zu beheben. Im Moment haben Sie die niedrigste Punktzahl :)
Felix Palmen
1
@FelixPalmen behoben!
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

Probieren Sie es online!

Es hat viel Spaß gemacht, dies zu schreiben! Zuerst kommt die fFunktion durch und entfernt alle Backslashes am Ende der Zeilen, dann lineszerlegt sie sie in eine Liste von Strings an den Zeilenumbrüchen. Dann ordnen wir den Zeilen eine Reihe von Funktionen zu und verknüpfen sie wieder miteinander. Diese Funktionen: Leerzeichen von links ( t) und von rechts ( r.t.rwo rist reverse) entfernen ; Leerzeichen aus der Mitte entfernen, Zeichenketten- und Zeichenliterale ignorieren sowie Kommentare entfernen ( w); und fügt am Ende ein Zeilenumbruchzeichen hinzu, wenn die Zeile mit einem # beginnt. Nachdem alle Zeilen wieder zusammengefügt wurden, wird gnach # Zeichen gesucht und sichergestellt, dass vor ihnen eine neue Zeile steht.

wist ein wenig komplex, deshalb erkläre ich es weiter. Zuerst überprüfe ich auf "//", da wich in weiß, dass ich kein String-Literal bin. Ich weiß, dass dies ein Kommentar ist, also lasse ich den Rest der Zeile fallen. Als nächstes überprüfe ich, ob der Kopf ein Begrenzer für eine Zeichenkette oder ein Zeichenliteral ist. Wenn es so ist, stelle ich es voran und übergebe den Taktstock, lder durch die Charaktere läuft, wobei ich den "Escape" -Zustand verfolge, der nwahr ist, wenn es eine gerade Anzahl von aufeinanderfolgenden Schrägstrichen gegeben hat. Wenn lein Trennzeichen erkannt wird und es sich nicht im Escape-Status befindet, wird der Taktstock an zurückgegeben w, um Leerzeichen nach dem Literal wzu entfernen, da erwartet wird, dass das erste Zeichen kein Leerzeichen ist. Wannwfindet kein Trennzeichen, das span verwendet, um nach Leerzeichen im Schwanz zu suchen. Wenn es welche gibt, prüft es, ob die Charaktere um es herum nicht in Kontakt gebracht werden können und fügt ein Leerzeichen ein, wenn dies der Fall ist. Dann wiederholt es sich, nachdem das Leerzeichen beendet ist. Wenn es kein Leerzeichen gab, wird kein Leerzeichen eingefügt und es geht trotzdem weiter.

EDIT: Vielen Dank an @DLosc für den Hinweis auf einen Fehler in meinem Programm, der dazu geführt hat, dass ich ihn auch verkürzen konnte! Ein Glück für den Pattern Matching!

EDIT2: Ich bin ein Idiot, der die Spezifikation nicht zu Ende gelesen hat! Nochmals vielen Dank, DLosc, dass Sie darauf hingewiesen haben!

EDIT3: Habe gerade eine nervige Typreduktionssache bemerkt, die sich aus irgendeinem Grund e=elemin eine solche verwandelt hat Char->[Char]->Boolund damit aufbrach e[a,q]. Ich musste eine Typensignatur hinzufügen, um die Richtigkeit zu erzwingen. Weiß jemand, wie ich das beheben könnte? Ich hatte dieses Problem noch nie in Haskell. TIO

EDIT4: Schnellkorrektur für Fehler @FelixPalmen zeigte mir. Ich könnte versuchen, es später zu spielen, wenn ich etwas Zeit habe.

EDIT5: -24 Bytes dank @Lynn! Vielen Dank! Ich wusste nicht, dass man Dinge auf globaler Ebene mit Pattern Matching zuordnen kann, wie n:c:z=...das wirklich cool ist! Auch eine gute Idee, einen Operator für den elemWunsch zu machen, an den ich gedacht hatte.

user1472751
quelle
1
Ich habe 24 Bytes herausgepresst .
Lynn
2
Sie stoßen auf die gefürchtete Monomorphismus-Einschränkung . Definieren e x y=elem x y(oder sogar e x=elem x) löst Ihr Problem. (Ich wurde in eeinen Operator umbenannt (!).)
Lynn
3

C 497 494 490 489 Bytes

Da wir C verarbeiten, machen wir es mit C! Die Funktion f()nimmt Eingaben vom Zeichenzeiger pund Ausgaben vom Zeichenzeiger an qund geht davon aus, dass die Eingabe in ASCII erfolgt:

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

Wir gehen davon aus, dass die Datei wohlgeformt ist - Zeichenketten- und Zeichenliterale sind geschlossen, und wenn die letzte Zeile einen Kommentar enthält, muss eine neue Zeile eingefügt werden, um sie zu schließen.

Erläuterung

Die Pre-Golf-Version ist leider nur geringfügig besser lesbar:

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

Es implementiert eine Zustandsmaschine durch Schwanzrekursion. Die Hilfsmakros und Variablen sind

  • Ofür o utput
  • Rzu r ead Eingabe inr
  • Vum zu bestimmen , v alid Identifikator Zeichen (da !isalnum('_'))
  • pund q- E / A-Zeiger wie beschrieben
  • r- letztes Zeichen seine r ead
  • s- s aved neuer Nicht-Whitespace-Charakter
  • t- t ag bei der Arbeit an einer Präprozessor-Direktive

Unsere Staaten sind

  • a() - normaler C-Code
  • b() - String-Literal
  • c() - Kommentar
  • d() - normaler C-Code nach dem Lesen r
  • e() - Fluchtabfolge
  • f() - Ausgangszustand (Hauptfunktion)
  • g() - In Leerzeichen
  • h()- im Leerzeichen - Versand an g()oderi()
  • i() - Unmittelbar nach dem Leerzeichen - müssen wir ein Leerzeichen einfügen?
  • j() - Anfängliches Leerzeichen - Fügen Sie niemals ein Leerzeichen ein

Testprogramm

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

Dies erzeugt

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

Verjährung

Dies bricht Definitionen wie

#define A (x)

durch Entfernen des Leerzeichens, das den Namen von der Erweiterung trennt, geben

#define A(x)

mit einer ganz anderen Bedeutung. Dieser Fall fehlt in den Testsätzen, daher werde ich ihn nicht ansprechen.

Ich vermute, dass ich mit einer In-Place-Konvertierung mit mehreren Durchläufen eine kürzere Version erstellen kann - das werde ich vielleicht nächste Woche versuchen.

Toby Speight
quelle
Sie können ein Byte speichern, indem Sie das =am Ende der Definition von stehende Zeichen entfernen Ound das Leerzeichen, das jedem Aufruf folgt, Oin a ändern =.
Zacharý
Das ist großartig;) Über die „Einschränkung“, siehe auch mein Kommentar auf die Frage selbst - Erfassung dieser einfach zu viel Komplexität würde hinzufügen.
Felix Palmen
@ Zachary - danke dafür - Ich habe vergessen, als ich den allgemeinen Code in ASCII-spezifisch änderte, O'\\'und O' 'beide haben ein Leerzeichen erhalten.
Toby Speight
464 Bytes
Ceilingcat
2

C  705   663  640 Bytes

Danke an @ Zacharý für das Golfen mit 40 Bytes und danke an @Nahuel Fouilleul für das Golfen mit 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;}

Probieren Sie es online!

Steadybox
quelle
Kann for(;W;C++){}werden for(;W;C++);?
Zacharý
@ Zacharý das wurde nie gefragt. Es ist ein Werkzeug für den allerletzten Schritt: Entfernen Sie überflüssige Leerzeichen und Kommentare.
Felix Palmen
Ich bezog mich auf seinen Code, nicht auf die Herausforderung.
Zacharý
@ Zacharý haha ​​Ich sehe ... seltsam, wenn Code und Eingabe die gleiche Sprache sind;)
Felix Palmen
Würde dies für 665 Bytes funktionieren? 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/./

Probieren Sie es online aus

Nahuel Fouilleul
quelle
Ja, ich habe gerade eine nicht optimale Lösung. Ich habe gerade den tio-Link hinzugefügt, ich werde es versuchen, Golf zu spielen, wenn ich Zeit habe.
Nahuel Fouilleul
Präprozessor-Direktiven stehen in einer eigenen Zeile, wenn sie vor dem Code stehen, wie in den Testfällen, aber wenn es erforderlich ist, werde ich eine Änderung hinzufügen
Nahuel Fouilleul
1
Behoben siehe Update
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'++ -- /*']

Probieren Sie es online!

Edit: Feste zu schließen - -, + +und/ *

TFeld
quelle