Пишу на псевдокоде, работает в PHP

    Я могу писать программы на псевдокоде и превращать их в PHP-код. Или в переносимый Си. Или ещё во что-нибудь. Список будет пополняться.

    Часть проекта, которая «превращать в PHP», готова не полностью. Но я пишу статью уже сейчас, чтобы:

    • узнать, кому ещё интересен проект;
    • спросить у опытных пхпшников, как улучшить мой PHP-код и как померять, что именно тормозит.

    Пример и технические детали под хаброкатом.

    Пример программы:

    (display "Hello, world!\n")

    Или посложней:

    (define (сделать-писателя что-писать)
      (lambda () (display что-писать)))
    (define пиши-привет (сделать-писателя "Привет!\n"))
    (пиши-привет)


    В этом примере функция возвращает функцию. На PHP код выглядел бы так (не работает):

    function сделать_писателя($что_писать) {
      return function () {
        print $что_писать;
      }
    }
    $пиши_привет = сделать_писателя("Привет!\n");
    $пиши_привет();


    На остальные примеры можно посмотреть на github или в архиве. Тесты проверяют, что работает

    * хоть что-то (пример “Hello, world”)
    * рекурсия (факториал, фибоначчи, аккерманн)
    * замыкания
    * продолжения

    Все важные вещи работают. До полной реализации стандарта R5RS остаётся только наделать примитивов. Ах да, совершенно случайно мой псевдокод совпадает с языком Scheme R5RS.

    Для своего проекта я использую компилятор Схемы Gambit. Внутри него — регистрово-стековая виртуальная машина GVM (A Parallel Virtual Machine for Efficient Scheme Compilation). Вот, во что превращается наш пример, когда оказывается в GVM:

    ; +N: регистр номер N
    ; -N: ячейка N фрейма стека
    ; Параметры функций: первый в +1, второй в +2 и т.д.
    ; Результат функции возвращается в +1
    ; Используется continuation-passing style, адрес перехода-возврата лежит в +0
    ; Здесь и далее, escape sequences заменены на русские буквы
    ;
    ; Метка #1 плюс всякие полезности

    #1 0 entry-point 0 ()
      ; Глобальная переменная "сделать-писателя" указывает на функцию
      |~#сделать-писателя| = '#<procedure |~#сделать-писателя|>
      -1 = +0
      +1 = '"Привет!\n"
      +0 = #3
     ; Переход на метку #2 плюс всякие полезности
      jump* 4 #2
    #2 4
      ; Вызывает функцию. Так как в +0 лежит адрес #3, то туда и вернёмся, а в +1 будет лежать анонимная функция (замыкание)
      jump$ 4 |~#сделать-писателя| 1
    #3 4 return-point
      |~#пиши-привет| = +1
      +0 = -1
      jump* 4 #4
    #4 4
      jump$ 0 |~#пиши-привет| 0

    **** #<procedure |~#сделать-писателя|> =
    #1 0 entry-point 1 ()
      ; Замыкание -- это функция плюс параметры
      close -1 = (#2 +1)
      +1 = -1
      jump 0 +0
      ; Начало анонимной функции
    #2 0 closure-entry-point 0 ()
      ; В +4 лежит замыкание, +4(1) -- первый параметр
      +1 = +4(1)
      jump* 0 #3
    #3 0
      jump$ 0 display 1


    Результат трансляции GVM на PHP:

    function glo_x20hellowr() {
      global $reg0, $reg1, $reg2, $reg3, $reg4, $pc, $fp, $stack, $nargs;
      $GLOBALS['glo_сделать-писателя'] = 'glo_сделать_писателя';
      $stack[$fp+1] = $reg0;
      $reg1 = "Привет!\n";
      $reg0 = 'lbl_x20hellowr_3';
      $pc = 'lbl_x20hellowr_2';
      $fp = $fp+4;
    }
    function lbl_x20hellowr_2() {
      global $reg0, $reg1, $reg2, $reg3, $reg4, $pc, $fp, $stack, $nargs;
      $nargs = 1;
      $pc = $GLOBALS['glo_сделать-писателя'];
    }
    function lbl_x20hellowr_3() {
      global $reg0, $reg1, $reg2, $reg3, $reg4, $pc, $fp, $stack, $nargs;
      $GLOBALS['glo_пиши-привет'] = $reg1;
      $reg0 = $stack[$fp-3];
      $pc = 'lbl_x20hellowr_4';
    }
    function lbl_x20hellowr_4() {
      global $reg0, $reg1, $reg2, $reg3, $reg4, $pc, $fp, $stack, $nargs;
      $nargs = 0;
      $pc = $GLOBALS['glo_пиши-привет'];
      $fp = $fp-4;
    }
    // procedure сделать-писателя =
    function glo_сделать_писателя() {
      global $reg0, $reg1, $reg2, $reg3, $reg4, $pc, $fp, $stack, $nargs;
      $stack[$fp+1] = array('lbl_сделать_писателя_2', $reg1);
      $reg1 = $stack[$fp+1];
      $pc = $reg0;
    }
    function lbl_сделать_писателя_2() {
      global $reg0, $reg1, $reg2, $reg3, $reg4, $pc, $fp, $stack, $nargs;
      $reg1 = $reg4[1];
      $pc = 'lbl_сделать_писателя_3';
    }
    function lbl_сделать_писателя_3() {
      global $reg0, $reg1, $reg2, $reg3, $reg4, $pc, $fp, $stack, $nargs;
      $nargs = 1;
      $pc = $GLOBALS['glo_display'];
    }
    exec_scheme_code('glo_x20hellowr');


    1) Как реализовать goto?


    Каждый блок GVM (от метки до перехода) становится функцией PHP, которая возвращает, куда надо goto. Исполнитель программы «exec_scheme_code($pc)» выглядит примерно так:

    $reg0 = 'glo_exit';
    while(1) {
      $pc = $pc()
    }

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

    Альтернативы?

    2) Superglobals


    В каждой функции приходится перечислять одни и те же глобальные переменные. Может, есть способ сказать PHP, что они глобальные по умолчанию?

    3) Стек


    Вместо аппаратного стека надо использовать программный. Естественно, я взял массив. И немного не угадал. Оказывается, они всегда ассоциативные, с кучей любопытных побочных эффектов, что тоже замедляет работу.

    Как сделать быстрый стек?

    4) Производительность


    Естественно, такой PHP-код тормозит. Например, по сравнению с ручной реализацией функции Аккерманна, он в 30 раз медленнее. Я подозреваю, почему (недо-goto и стек), но хочу убедиться.

    Какими инструментами мерять, что тормозит?
    Share post

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 30

      +30
      зачем это?
        +3
        Чтобы написать код один раз, и использовать его в разных окружениях: Си, Java, PHP и прочих.
          +4
          У вас часто возникала такая необходимость?
            0
            Всего несколько раз, но достачто сильно.
            +2
              –3
              +1000
              всяко лучше скобочного говна.
                0
                Спасибо за ссылку, очень интересно.
                +2
                Facebook для этой цели придумал Apache Thrift.
                • UFO just landed and posted this here
                    0
                    А я и не говорил, что это метаязык. Если я правильно понял комментарий, на который ответил, то речь там шла именно о ре-использовании кода в различных окружениях, а не о природе подхода.
                  +1
                  ostatic.com/c2java
                  www.mibsoftware.com/php2cpp/
                  Конвертер C в PHP не нашел, но не думаю, что его (в упрощенном виде) написать сложнее, чем транслятор из метаязыка.
                  +7
                  Подозреваю, что человек любит схему, а работать приходится с php :)
                  +3
                  Синтаксис у Вас — это жесть. Если бы не комментарии, никогда бы не разобрался.

                  И зачем Вам городить свои велосипеды? Сделайте ретранслятор Scheme -> PHP — я думаю найдется не мало людей которые будут Вам благодарны.
                    +2
                    Именно это (транслятор Scheme -> PHP) и сделано.
                      +1
                      Сбила фраза «совершенно случайно»

                      Теперь все вижу. Отличная разработка. Так держать.

                      Отвечу на вопросы, которые в статье (на что могу)

                      В PHP в последних версиях введены анонимные функции и замыкания.

                      В качестве Superglobals попробуйте облачать PHP-код в один большой класс, и использовать $this или self (для статических)

                      То, что написано про стек в статье, это про функции array_push и array_pop? Если да, то Вы открыли мне глаза на данных товарищей.
                    +1
                    Перечитал статью. Синтаксис GVM ужасен, Ваш синтаксис вполне удобен.
                      0
                      Да, я до сих пор не могу привыкнуть к синтаксису GVM. Но он оправдан тем, что это просто распечатка внутреннего состояния компилятора.
                    +6
                    Зачем скрывать в начале статьи, что псевдокод — это Scheme? Сначала думал что-то новое и необычное, потом понял что просто транслятор.
                      +6
                      Небольшая маркетинговая уловка. Если бы Scheme, или упаси б-г Lisp, были бы упомянуты в самом начале, дальше хабраката никто бы и не посмотрел.
                      +6
                      переместите в «ненормальное программирование»
                      • UFO just landed and posted this here
                          +1
                          $GLOBALS
                            +3
                            >>сделать-писателя
                            *facepalm*
                              0
                              Про быстрый стек: а если использовать список? Т.е. Node — массив или объект с полями value и next, где в next записана ссылка на следующий такой элемент.
                                0
                                Немного побенчмаркил на вставки элементов: обычные массивы и SplStack показывают примерно одинаковые результаты (последний все же чуть быстрее), мой же способ гораздо хуже.
                                  0
                                  Интересно, как SplStack добивается производительности. Мне так показалось, что он просто надстройка на обычными PHP-структурами.

                                  Жаль, что с Node не получилось. Я надеялся, что это хорошее решение.
                                +1
                                Стек можно попробовать через SplStack
                                  +1
                                  брейнфак будет поддерживаться?
                                    0
                                    Думаю, что если захотеть, то и брейнфак можно сделать. Но я вряд ли захочу, ибо проект не столько развлекательный, сколько для практического применения.

                                  Only users with full accounts can post comments. Log in, please.