Гольф мой "предварительно гольф" C

12

Фон

Для моего представления на C мне нужен инструмент обработки. Как и во многих других языках, пробел в основном не имеет отношения к исходному тексту на Си (но не всегда!) - все еще делает код намного более понятным для людей. Полностью играющая в гольф C-программа, которая не содержит ни одного лишнего пробела, часто едва читаема.

Поэтому я люблю писать свой код на C для отправки включая пробелы и иногда комментарии, чтобы программа сохраняла понятную структуру при написании. Последний шаг - удалить все комментарии и лишние пробелы. Это утомительно и бессмысленная задача , которая на самом деле должно быть сделано стажером компьютерной программой.

задача

Напишите программу или функцию, которая исключает комментарии и лишние пробелы из какого-либо «предварительно сыгранного» источника C в соответствии со следующими правилами:

  • \(Обратный слэш) в качестве последнего символа в строке является продолжением строки . Если вы обнаружите это, вы должны обработать следующую строку как часть одной и той же логической строки (например, вы можете полностью удалить \следующие \nсимволы и следующую (новую строку ), прежде чем делать что-либо еще)
  • Комментарии будут использовать только однострочный формат, начиная с //. Таким образом, чтобы удалить их, вы игнорируете остальную часть логической строки, где бы вы ни находились, //вне строкового литерала (см. Ниже).
  • Символами пробела являются (пробел), \t(табуляция) и \n(перевод строки, поэтому здесь конец логической строки).
  • Когда вы найдете последовательность пробелов, изучите непробельные символы, окружающие ее. Если

    • оба они являются буквенно-цифровыми или подчеркивания (диапазон [a-zA-Z0-9_]) или
    • оба являются +или
    • оба являются -или
    • предыдущий /и следующий*

    затем замените последовательность одним символом пробела ( ).

    В противном случае полностью исключите последовательность.

    Это правило имеет некоторые исключения :

    • Директивы препроцессора должны появляться в своих строках в выходных данных. Директива препроцессора - это строка, начинающаяся с #.
    • Внутри строкового литерала или символьного литерала вы не должны удалять пробелы. Любой "(двойная кавычка) / '(одинарная кавычка), которому непосредственно не предшествует нечетное число обратных косых черт ( \), начинает или заканчивает строковый литерал / символьный литерал . Вам гарантировано, что строковые и символьные литералы заканчиваются на той же строке, с которой они начинались.Строковые литералы и символьные литералы не могут быть вложенными, поэтому 'внутренний строковый литерал , а также "внутренний символьный литерал не имеют никакого особого значения.

Спецификация ввода / вывода

Входные и выходные данные должны представлять собой либо последовательности символов (строки), включая символы новой строки, либо массивы / списки строк, которые не содержат символов новой строки. Если вы решите использовать массивы / списки, каждый элемент представляет собой линию, поэтому перевод строки неявно после каждого элемента.

Вы можете предположить, что ввод является допустимым исходным кодом C-программы. Это также означает, что он содержит только печатные символы ASCII, вкладки и переводы строк. Неопределенное поведение при неправильном вводе допускается.

Ведущие и ведомые пробелы / пустые строки являются не допускаются .

Контрольные примеры

  1. вход

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

    выход

    main(){printf("Hello, World!");}
    
  2. вход

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

    выход

    #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. вход

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

    выход

    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. вход

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

    выход

    x;
    #include<stdio.h>
    int main(){puts("hello // there");}
    
  5. вход (реальный пример)

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

    выход

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

Это , поэтому самый короткий (в байтах) правильный ответ выигрывает.

Феликс Палмен
источник

Ответы:

4

Пип , 148 135 133 138 байт

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

Байты учитываются в CP-1252 , поэтому и· составляют по одному байту каждый. Обратите внимание, что это предполагает, что код C является единственным аргументом командной строки, который (в реальной командной строке) потребует использования обильных escape-последовательностей. Попробовать онлайн гораздо проще !

Объяснение версии слегка безглого

Код выполняет кучу операций замещения с парой трюков.

Продолжение с обратной косой чертой

У нас RMвсе вхождения буквальной строки

"\
"

то есть обратная косая черта с последующим переводом строки.

Строковые и символьные литералы

Мы используем замену регулярного выражения с функцией обратного вызова:

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

{
 lPBa
 C(++i + 191)
}

Регулярное выражение соответствует одинарной или двойной кавычке, за которой следует не жадный .*? который соответствует 0 или более символов, как можно меньше. У нас есть негативный взгляд на то, чтобы предыдущий символ не был обратной косой чертой; затем мы сопоставляем четное число обратных косых черт, за которыми снова следует разделитель.

Функция обратного вызова берет строковый / символьный литерал и помещает его в конец списка l. Затем он возвращает символ, начинающийся с кода символа 192 ( À) и увеличивающийся с каждым замененным литералом. Таким образом, код преобразуется так:

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

printf(À, Á);

Эти замещающие символы гарантированно не встречаются в исходном коде, что означает, что мы можем однозначно заменить их позже.

Комментарии

`//.*`

x

Регулярное совпадение //плюс все до новой строки и заменяется наx (предустановлено пустой строкой).

Директивы препроцессора

`#.*`

_WR'¶

Обтекания запускаются без символов перевода строки, начинающихся со знака решетки .

Пространства, которые не должны быть устранены

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

{
 a R w '·
}

Здесь много чего происходит. Первая часть генерирует этот список регулярных выражений для замены:

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

Обратите внимание на использование lookaheads для соответствия, например, только e in define P printf. Таким образом, это совпадение не использует P, что означает, что следующее совпадение может использовать его.

Мы генерируем этот список регулярных выражений, отображая функцию в список, где список содержит

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

и функция делает это с каждым элементом:

(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

Получив наши регулярные выражения, мы заменим их вхождения этой функцией обратного вызова:

{aRw'·}

который заменяет пробел в каждом матче на ·.

Устранение и очистка пробелов

[w `¶+` '·]

[x n s]

Три последовательные замены заменяют оставшиеся прогоны пробела ( w) на пустую строку ( x), прогоны символа новой строки и ·пробела.

Обратная замена строковых и символьных литералов

C(192+,#l)

l

Мы строим список всех символов, которые мы использовали в качестве замены для литералов, беря 192 + range(len(l))и преобразовывая в символы. Затем мы можем заменить каждый из них соответствующим литералом в l.

Вот и все! Полученная строка автоматически печатается.

DLosc
источник
Отлично, я впечатлен (+1)! Включение //внутреннего строкового литерала - определенно хорошая идея для тестового случая, я добавлю его завтра.
Феликс Пальмен
Хм ... теперь я также нашел здесь
Феликс Палмен
Я собираюсь выбрать победителя через 14 дней (конец следующей недели), и ваше решение будет первым кандидатом, если вы найдете время, чтобы исправить эту ошибку. Прямо сейчас у вас самый низкий счет :)
Феликс Пальмен
1
@FelixPalmen Исправлено!
DLosc
7

Haskell , 327 360 418 394 байта

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

Попробуйте онлайн!

Это было очень весело писать! Сначала fфункция проходит и удаляет все обратные слеши в конце строк, а затем linesразбивает ее на список строк в новых строках. Затем мы отображаем несколько функций на линии и объединяем их все вместе. Эти функции: убрать пробелы слева ( t) и справа ( r.t.rгде rесть reverse); удалить пробел из середины, игнорируя строковые и символьные литералы, а также удаляя комментарии ( w); и, наконец, добавляет символ новой строки в конец, если строка начинается с #. После того, как все строки соединены вместе, gищет символы # и гарантирует, что им предшествует символ новой строки.

wнемного сложнее, поэтому я объясню это дальше. Сначала я проверяю «//», так как wя знаю, что не в строковом литерале, я знаю, что это комментарий, поэтому я отбрасываю оставшуюся часть строки. Затем я проверяю, является ли заголовок разделителем строки или символьного литерала. Если это так, я добавляю его и передаю эстафету, lкоторой управляют персонажи, отслеживая состояние «выхода», с nкоторым будет верно, если было четное количество последовательных слешей. Когда lобнаруживает разделитель и не находится в состоянии перехода, он передает эстафету обратноw , обрезая, чтобы исключить пробел после литерала, потому что wожидает, что первый символ не будет пробелом. когдаwне находит разделитель, он использует span для поиска пробелов в хвосте. Если он есть, он проверяет, нельзя ли привести символы вокруг него в контакт, и вставляет пробел, если так. Затем это повторяется после того, как пропущен пробел. Если пробелов не было, пробел не вставляется, и он все равно перемещается.

РЕДАКТИРОВАТЬ: Большое спасибо @DLosc за указание на ошибку в моей программе, которая фактически привела к тому, что я тоже смог ее сократить! Ура для сопоставления с образцом!

EDIT2: я идиот, который не закончил читать спецификации! Еще раз спасибо DLosc за то, что указал на это!

EDIT3: Просто заметил некоторые раздражающие сокращения типа вещь , которая превратилась e=elemв Char->[Char]->Boolпо какой - то причине, что нарушения на e[a,q]. Мне пришлось добавить сигнатуру типа, чтобы она была правильной. Кто-нибудь знает, как я мог это исправить? У меня никогда не было этой проблемы в Хаскеле. TIO

EDIT4: быстрое исправление ошибки @FelixPalmen показал мне. Я мог бы попытаться сыграть в гольф позже, когда у меня будет время.

EDIT5: -24 байта благодаря @ Линн! Спасибо! Я не знал, что вы можете назначить вещи в глобальной области видимости, используя сопоставление с образцом, как n:c:z=...это действительно круто! Также хорошая идея сделать оператора для elemжелания, я бы подумал об этом.

user1472751
источник
2
Вы сталкиваетесь с ужасным ограничением мономорфизма ; определение e x y=elem x y(или даже e x=elem x) решает вашу проблему. (Я переименовал eв оператора (!).)
Линн
3

C 497 494 490 489 байт

Поскольку мы обрабатываем C, давайте сделаем это с помощью C! Функция f()принимает входные данные от указателя символа pи выводит указатель q, и предполагает, что вход находится в 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)();}

Мы предполагаем, что файл правильно сформирован - строковые и символьные литералы закрыты, и если в заключительной строке есть комментарий, должна быть новая строка, чтобы закрыть его.

объяснение

Боюсь, что версия для игры в гольф немного более разборчива:

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

Он реализует конечный автомат с помощью хвостовой рекурсии. Вспомогательные макросы и переменные

  • Oдля о utput
  • Rчтобы R EAD ввода вr
  • Vдля определения V Алид символов идентификатора (с !isalnum('_'))
  • pи q- указатели ввода / вывода, как описано
  • r- последний символ будет г Свинец
  • s- S aved недавнего характера непробельного
  • t- т аг при работе над директивой препроцессора

Наши штаты

  • a() - нормальный код C
  • b() - строковый литерал
  • c() - комментарий
  • d() - нормальный код C, после прочтения r
  • e() - escape-последовательность
  • f() - начальное состояние (основная функция)
  • g() - в пробелах
  • h()- в пустом месте - отправка g()илиi()
  • i() - сразу после пробела - нам нужно вставить пробел?
  • j() - начальный пробел - никогда не вставлять пробел

Тестовая программа

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

Это производит

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

ограничение

Это нарушает определения, такие как

#define A (x)

удаляя пробел, отделяющий имя от расширения, давая

#define A(x)

с совершенно другим значением. Этот случай отсутствует в тестовых наборах, поэтому я не буду его рассматривать.

Я подозреваю, что смогу создать более короткую версию с многоходовым преобразованием на месте - я мог бы попробовать это на следующей неделе.

Тоби Спейт
источник
Вы можете сохранить один байт, удалив =в конце определения Oи изменив пространство, следующее за каждым вызовом, Oна a =.
Захари
Это замечательно;) Об «ограничении» см. Также мой комментарий к самому вопросу - обнаружение этого добавило бы слишком много сложности.
Феликс Пальмен
@Zachary - спасибо за это - я забыл, когда я изменил общий код на ASCII-специфичный, O'\\'и O' 'оба получили пробел.
Тоби Спейт
464 байта
floorcat
2

C  705   663  640 байт

Спасибо @ Zacharý за гольф 40 байтов и благодаря @Nahuel Fouilleul за гольф 23 байта!

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

Попробуйте онлайн!

Steadybox
источник
Может for(;W;C++){}стать for(;W;C++);?
Захари
@ Захари, о котором никогда не просили. Это последний шаг для самого последнего шага: удалите лишние пробелы и комментарии.
Феликс Пальмен
Я имел в виду его код, а не вызов.
Захари
@ Zacharý haha ​​Я вижу ... странно, когда код и ввод - один и тот же язык;)
Феликс Пальмен
Будет ли это работать на 665 байтов? goo.gl/E6tk8V
Захари
2

Perl 5, 250 + 3 (-00n) , 167 + 1 (-p) байтов

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

Попробуйте онлайн

Науэль Фуйе
источник
Да я просто поставил неоптимальное решение. Я только что добавил ссылку tio, я посмотрю в гольф, когда у меня будет время.
Науэль Фуйе
Директива препроцессора находится на отдельной строке, когда помещается перед кодом, как в тестовых
примерах,
1
исправлено: см. обновление
Науэль Фуйе
0

Python 2 , 479 456 445 434 502 497 байт

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

Попробуйте онлайн!

Edit: Фиксированный включить - -, + +и/ *

TFeld
источник