Уважаемые коллеги!
 
Сейчас в этом разделе есть только АНОНС! ;-)

Сейчас уже можно сказать, что работа над первой версией компилятора Janus Forth подходит к завершению. В течение двух недель я планирую выложить ее на сайте forth.ru. Как показывает опыт, оценка сроков выпуска со стороны разработчиков продукта часто немного страдает субъективностью, но реальный срок публикации точно не будет отличаться от недели-двух на порядок! ;-)


Полное название компилятора - Janus Constructor, краткое - "просто" Janus или Janus Forth. В отличие от SMAL32 (который можно было использовать бесплатно лишь для некоммерческих проектов), в лицензионное соглашение для Janus'а я планирую включить разрешение и для бесплатного коммерческого использования этого продукта при соблюдении некоторых условий. Итак...

  1. Janus будет бесплатен, если система используется для разработки некоммерческих проектов, не перчисленных в пункте 3
  2. Janus будет бесплатен, если система используется для разработки коммерческих проектов физическими или юридическими лицами, на территории Росии или стран СНГ, кроме проектов, перечисленных в пункте 3. Кроме того, я полагаю, разработчикам коммерческих проектов не жалко будет поделиться со мной одной бесплатной версией их продукта... ;-)
  3. Janus не будет "автоматически бесплатным" при его использовании для разработки следующих типов проектов:
    • Форт-систем или Форт-компиляторов
    • Серверных Internet-приложений
    • Систем тестирования (психологического, контроля знаний, etc...) а также систем разрабтки тестов
  4. Возможно, условием использования Janus будет простая, бесплатная регистрация копии системы на сервере forth.ru

Формулировки условий еще будут уточнены и дополнены.


Janus задумывался не как "самоценная" система. Целью разработки для себя лично я ставил создание эффективного инструмента для решения конкретной и достаточно масштабной прикладной задачи. Кстати, название "Janus" было выбрано также, в значительной степени, "с оглядкой" на этот самый масштабный проект. Что из решения этой задачи получится - покажет время, если ничего не выйдет, то останется "просто" Janus. :-)

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

Не существует нескольких версий Janus'а. Есть единый модуль, запускаемый из различных операционных сред при помощи загрузчиков.

По скорости работы (прежде всего, из-за использования свернутого шитого кода) Janus, по всей видимости, не будет столь быстрым как SMAL32 или Small/32. Но, во-первых, он задумывался и не для скоростных проектов, во-вторых везде, где это возможно, я попытался скомпенсировать эту особенность эффективностью используемых алгоритмов и оптимизацией, в меру своих возможностей, кода наиболее часто используемых слов под процессор Pentium. И наконец, в-третьих, остается перспектива перевода критичных секций в более быстрые типы шитого кода.

Janus использует очень сложную структуру словаря, расчитанную, прежде всего, на поддержку еще не реализованной в полной мере, объектно-ориентированной модели. Все еще впереди... ;)


Из возможностей Janus'а:

  1. Многоплатформенность: Win32, Linux, DOS
  2. ANS - совместимость, в перспективе 100% слов из проекта стандарта
  3. Свернутый шитый код, отсутствие "статического" кода, не принадлежащего словарным статьям
  4. SMAL32 и Small/32 совместимость
  5. Исправлены все известные ошибки в коде, унаследованном со времен SMAL32
  6. Heap: эффективная динамическая память: ALLOCATE, FREE (использование AVL-деревьев для быстрого поиска в списках, минимизация вызовов операционной системы, минимизация запрашиваемой у операционной системы памяти).
  7. MASM-style ассемблер, набор команд Pentium, MMX, сопроцессор, макроопределения
  8. Локальные переменные разлчных типов, массивы, метки, переходы, вложенные области действия локальных имен
  9. Обработка исключений: THROW-CATCH, дополнительный механизм TRY-TRAP-ENDTRY
  10. Управление контекстом выполнения слов: WITH-REPLACE-RESTORE
  11. Вызовы Linux-библиотек и Windows DLL-библиотек
  12. BUILD - построение компактных исполняемых модулей, настройка на любой адрес, обработка множественной рекурсии
  13. Расширенный синтаксис управляющих контрукций, дополнительные управляющие конструкции
  14. Встроенные слова для работы с числами с плавающей запятой (множество слов FLOAT)
  15. Расширенная работа со строками, строки двух типов, длиной до 2^32-1 символов
  16. Работа с виртуальными экранами, запись в GIF-формате
  17. Условная компиляция, многострочные комментарии
  18. Быстрый поиск в словарях - использование HASH-функции
  19. Гибкая структура словаря, позволяющая реализовать большое количество схем работы слов
  20. Расширенное управление контекстом определения новых слов
  21. Функции расчета и обращения (подбора) CRC16 и CRC32
  22. Сортировка
  23. ...

Планируется реализовать в ближайшем будущем (в порядке убывания значимости):

  1. Berkley-style (или что-то более Forth-like) сокеты
  2. Объектно-ориентированная модель, классы
  3. Thread'ы - многозадачность, в перспективе все слова - реентерабельны
  4. Документация - ее не бывает мало
  5. Отладчик, дизассемблер. За основу, возможно, будет взят Code Digger и дизассемблер Тома Циммера
  6. Графика, шрифты
  7. Встроенный макроязык для работы с текстом: TRAC - функционально похож на Perl, структурно - на Lisp. В принципе, реализация уже давно готова, осталось поработать над компенсацией сложности синтаксиса за счет редактора, учитывающего особенности языка.
  8. Отображение GPF и ситуации деления на 0 на механизм исключений
  9. Порт DOS-библиотек из SMAL32 (предже всего, графических), некоторых возможностей из Small/32
  10. Страничка поддержки на forth.ru, FAQ-сервер, публикация удачных авторских проектов для Janus и содействие их распространению по договоренности с авторами
  11. Англоязычная документация (возможно, на основе текста Брюса Хойта)
  12. Доводка до соответствия стандарту
  13. Доводка текстового интерпретатора с целью отслеживания различных ситуаций и генерации оптимального кода
  14. ...

Планируется реализовать в перспективе:

  1. BUILD - построение "быстрых" выполняемых модулей в стиле SMAL32 (на основе прямого шитого кода)
  2. Средства контроля ошибок
  3. Порт под BeOS, OS/2, Free BSD, Solaris (конечно, не все сразу ;-)
  4. ...

Самое "прогрессивное" в Janus'е - это динимическая память. Код, обеспечивающий работу с "кучей" (heap'ом) по сложности вполне может сравниться со сложностью BUILD'а. Как я понял благодаря "приватным" консультациям, красивую и эффективную объектно-ориентированную модель без продуманной реализации динамической памяти написать просто не удастся, а написать ее для развития проекта просто необходимо. При реализации слов ALLOCATE и FREE я планировал решить одновременно несколько трудносовместимых задач:

  1. Увеличение скорости работы за счет минимизации количества системных вызовов и оптимизации поиска наиболее подходящего блока из списка свободных.
  2. Минимизация количества памяти, запрашиваемой у системы.
  3. Минимизация памяти, отводимой для размещения системной информации о состоянии "кучи".
  4. Унификация механизмов работы с целью сделать их независимыми от операционного окружения.

Для рещения этих задач используются алгоритмы для работы с двоичными сбалансированными деревьями (AVL-деревьями). Мне было очень приятно узнать, что для решения аналогичных задач в ядре Linux'а используются те же самые модели. ;)


Еще одна весьма удобная возможность в Janus'е - это управление контекстом выполнения кода. Управляющая конструкция WITH-REPLACE-RESTORE может использоваться в случаях, когда:

  1. Возникает необходимость временного, в пределах одного thread'а и в пределах одного блока кода, переопределения действия уже скомпилированного слова.
  2. Необходимо передать в слово параметры, не предусмотренные на этапе программирования
  3. Установки временной "заглушки" на какое-то действие в уже скомпилированной программе
  4. Использование разного контекста работы в разных thread'ах (задачах) в рамках одной программы.
  5. ...еще не придумал. :)

Примеры:


\ Буферизация ввода/вывода

\ Возвращяет следующий символ из входного буфера
: GETCHAR ( --> char ) ... ;

\ Сохраняет выводимый символ символ в выходном буфере
: PUTCHAR ( char --> ) ... ;

: MAIN
   ...
   WITH
      KEY GETCHAR
      EMIT PUTCHAR
   REPLACE
      ... \ здесь ввод/вывод буферизирован
          \ вместо KEY вызывается GETCHAR, вместо EMIT - PUTCHAR
   RESTORE
   ... \ здесь KEY и EMIT опять работают "нормально"
;

\ Переопределение слова WORDS, заглушка на слово ?KEY
\ Новая версия слова не вызывает ?KEY для постраничной выдачи
: WORDS
   WITH
      ?KEY FALSE
   REPLACE
      WORDS
   RESTORE
;

Иными словами, эта конструкция временно подменяет все вызовы (как прямые, так и косвенные) слов внутри блока REPLACE-RESTORE на вызовы других слов. При этом заменяемое слово сохраняет работоспособность, и может вызываться из других thread'ов (задач) в неизменном виде. Замедления при вызове "подмененных" слов нет.

При помощи конструкции смены контекста выполнения можно переопределять не только вызовы слов, но и, даже, обращения к переменным при помощи слов TO

К примеру, конструкция:


WITH VAR1 VAR2 REPLACE FALSETO VAR1 RESTORE

заносит 0 в переменную VAR2


Пример определения ассемблерного макроса:


Macro testmacro param1,param2,param3
        push    param1
        xchg    param1,param2
        call    subroutine
        pop     param1
EndMacro

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


Расширенная обработка исключений:


: MAIN
   \ Ловим только 4 исключения с номерами -23 -2 -33 и 12
   \ (Если оставить на стеке 0, то будем ловить все исключения)
   -23 -2 -33 12 4
   TRY
      \ код, возможно, генерирующий исключение
   TRAP
      \ если исключение произошло, в этом блоке
      \ сохранено состояние всех стеков в момент исключения

      \ получим номер исключения (0 - исключения не было)
      CAUGHT ( --> n )
      \ если = -23, то генерируем исключение 12
      \ его обработает обработчик на более высоком уровне
      -23 = IF 12 THROW THEN

   ENDTRY
   \ здесь уже стеки находятся в исходном состоянии
;

Вызовы DLL-библиотек из ассемблерных определений:


Code DLLTEST ; ( mem_block_size --> mem_address|error_code )
                pop     eax
                push    40h
                push    1000h
                push    eax
                push    0

                wincall "VirtualAlloc","kernel32.dll"
                ; при последующих вызовах VirtualAlloc
                ; второй параметр (имя библиотеки)
                ; можно уже не указывать

                or      eax,eax
                jz      short get_err
                push    eax
                next
        get_err:
                wincall "GetLastError","kernel32.dll"
                push    eax
                next
EndCode

Linux shared-библиотеки вызываются таким же образом при помощи макроса SHLCALL. Единственное отличие - параметры со стека необходимо удалить после вызова (проанализировав?). Система (ассемблер) отслеживает вызовы библиотек и предотвращает повторное их подключение, если они уже подключены.


Наконец, локальные переменные, использование слов TO, FROM, PTR:


: MAIN ( param1 param2 param3 --> )
    \ Обычное стандартное объявление переменных
    FALSE LOCALS| PARAM4 PARAM3 PARAM2 PARAM1

    \ Их использование
    PARAM1 PARAM2 * PARAM3 + TO PARAM4

    \ PARAM3 := PARAM3 + 4;
    CELL+TO PARAM3

    \ Или так:
    PTR PARAM3 CELL+!

    \ Обычные переменные
    INT VAR1
    INT VAR2

    \ Массив длиной 4096 байт
    [ 4096 ] MEMBLOCK ARRAY1

    \ Его инициализация
    ARRAY1 4096 ERASE

    \ Присваивание
    15 VAR1 !

    \ или так:
    VAR1 @ TO VAR2

    \ или так:
    FROM VAR1 TO VAR2

    {
       \ если объявить переменные здесь,
       \ можно работать как с ними, так и с переменными
       \ внешнего блока

       \ Переход за пределы блока (объявление L1 - см. ниже):
       \ все переменные, не существующие
       \ в месте, в которое осуществляестся переход,
       \ будут автоматически удалены
       GOTO L1

       100 0
       DO
          ...
          IF EXIT THEN          \ EXIT выполнится нормально
          ...

          15 TO I               \ такое тоже возможно

          16 TO J               \ а вот это вызовет ошибку:
                                \ J пока еще не определено
       LOOP
    }

    LABEL L1

;

Текстовый интерпретатор отслеживает использование локальных переменных в слове, и если они отсутствуют, генерирует более быстрый код, не требующий создания и удаления фрейма переменных. Впрочем, сами локальные переменные тоже не должны быть слишком медленными. Возможно, они будет работать, даже, быстрее переменных глобальных, и уж точно быстрее локальных переменных в SMAL32.


И это, конечно же, не все, что есть в Janus'е...

И последнее - с выходом Janus'а я более не вижу смысла ограничивать распространение исходных текстов SMAL32. Они будут опубликованы. Возможно, в полном объеме.

Пока все!

Александр Ларионов, laric@forth.ru, 31 июля 2000 г.
Powered by SYSTRAN.