Яндекс.Метрика

    Песочница

    Пишем двуязычную программу

    Недавно передо мной встала нелёгкая задачка, которую мне подкинул один знакомый. Суть состоит в том, чтобы, написав один исходник, можно было его скомпилировать в gcc и исполнить, также и исполнить через интерпретатор perl. Казалось бы, не так уж и сложно, но не всем программистам придёт в голову, как правильно написать данный исходник. Приступим к рассмотрению изначальных программ на C и Perl.
    Голый Си:
    #include <stdio.h>
    #include <stdlib.h>
    int main(int argc, char *argv[])
    {
       int sum=0;
       int i;
       for (i=1; i<argc; i++)
         sum+=atoi(argv[i]);
       printf("%d\n", sum);
    return 0;
    }
    


    Проверяем:
    cyber@punk:/opt# gcc test.c -o test1
    cyber@punk:/opt# ./test1 -2 15 -13
    0
    cyber@punk:/opt# ./test1 2 4 6
    12


    Теперь аналог на Perl:
    #!/usr/bin/perl
    
    $sum=0;
    foreach (@ARGV) {
      $sum+=$_;
    }
    print "$sum\n";
    


    Снова проверяем:
    cyber@punk:/opt# perl test.pl 3 15 10 20
    48


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

    #include <stdio.h>
    #include <stdlib.h>
    
    #define $ /* */
    
    #define ARGV argv
    
    #define if($x) int main(int argc, char *argv[])
    
    #define $start 1  
    
    #if PERL
      sub atoi { $_[0] }
      $ argc=@ARGV;
      $ start=0;
      $ x=1;
    #endif
    
    if($x)
    {
      int $ sum;
      int $ i;
      
      $ sum=0;
    
      for ( $ i = $start; $ i < $ argc ; $ i++) {
        $ sum += atoi ($ ARGV [$ i]);}
    
      printf("%d\n", $ sum);
    
    exit(0);
    }
    


    Тестируем результат:

    cyber@punk:/opt# perl test4.c 3 15 10 20
    48
    cyber@punk:/opt# gcc test4.c -o test4
    cyber@punk:/opt# ./test4 3 15 10 20
    48


    Давайте рассмотрим хитрости обоих языков, использованные при написании.
    • Perl интерпретирует знак #, как начало комментария до конца строки, это дает возможность использовать директивы препроцессора Си, которые Perl будет игнорировать.
    • Синтаксис языка Perl допускает пробел между знаком $ и именем переменной. Поэтому определение #define $ /* */ позволит в Си интерпретировать переменную вида $ var, как var, а в perl — как $var.
    • В Си параметры командной строки передаются как char *argv[], а в perl — ARGV. Проблему решает директива: #define ARGV argv.
    • В Си нулевой аргумент — это имя программы, а в Perl ARGV — переданный параметр. Обходится это введением переменной start, которая в Си устанавливается в единицу, а в Perl — в ноль. Кусок кода Perl экранируется в Си директивой #if PERL.
    • Синтаксис операторов for и printf в Perl и Си одинаков, поэтому никаких дополнительных мер тут не требуется.

    На этом, в принципе, и все. Возможно у вас есть другие методы написания подобных «двуязычных» программ. С нетерпение жду ваших решений!