nnCron и язык программирования Форт

nnCron написан на языке программирования Форт (SP-Forth), поэтому использование этого языка гармонично "вплетено" в идеологию программы. Пользователю разрешено прямо в рамках задачи создавать переменные и массивы, использовать любые существующие форт-слова и создавать новые. Фактически, внутри задачи можно (при желании) использовать "чистый" Форт. Синтаксис расширенного режима тоже во многом наследован от Форта.

Форт - очень мощный инструмент и поэтому довольно уязвим. Неумелое использование Форта может привести к "падению" nnCron и даже к нестабильной работе системы. nnCron позволяет принудительно ограничить возможности программирования при написании задач вручную: см. описание переменной nncron.ini SyntaxRestriction.

Документация nnCron содержит ответы на часто задаваемые вопросы по Форту, здесь же я привожу только базовую (начальную) информацию, те "азы", без которых трудно обойтись при активном использовании nnCron. Описание работы с переменными, константами и массивами выделено в специальную главу.

Во время своих экспериментов с Фортом вы можете пользоваться встроенной в nnCron форт-консолью: это сильно облегчит вам понимание того, как "устроен" Форт. Примеры задач nnCron с использованием ветвления, переменных и форт-слов можно посмотреть в кронтаб-файле example.tab.



Синтаксис Форта

Синтаксис Форта очень прост - он считается одним из самых простых в истории программирования.

Вкратце его можно описать так: "Программа на языке программирования Форт - это набор слов, разделенных пробелами". "Словом" в Форте называется имя определения. Говоря иначе, каждое слово в Форте - это аналог функции (метода) в других языках программирования. Выполняются слова в том же порядке, в каком они указаны в исходном тексте. Вот (для примера) как может выглядеть на Форте гипотетическая программа, эмулирующая "работоголика" (человека, который только работает и совсем не отдыхает):

WAKE_UP BREAKFAST EAT WORK DINNER EAT WORK SLEEP

По сути, работа с Фортом - это операции со словами. Даже знаки арифметических действий (например, +, -, *), знак строкового комментария (\) и даже знаки начала и окончания описания задачи (#( и )#) являются полноценными форт-словами. В связи с этим следите, чтобы слова были разделены пробелами.

Примеры:

\ следующая строка ошибочна: 
\ нет пробела после "слова-комментария"
\ошибка!

\ в первой строке следующего задания тоже 
\ допущена ошибка: имя задачи не отделено 
\ пробелом от слова, означающего 
\ начало описания задачи
#(task_name \ ошибка!
\ ...
)#

Постфиксная нотация

В языке программирования Форт принята т. н. "постфиксная нотация" (ее еще иногда называют "обратной польской записью"). Это означает, что знак операции (логической или арифметической) в нем располагается после чисел.

Например, операция сложения двух чисел записывается в постфиксной нотации так:

2 3 +

Чтобы постфиксная запись выглядела для вас более логичной, вы можете читать вышеуказанный пример так: "Берем число 2, число 3 и складываем". (Результат, как вы, наверное, уже догадались - 5). :)


Использование стека

Форт использует постфиксную нотацию из-за ее удобства в обращении со "стеком" - участком памяти в который данные помещаются и удаляются по принципу LIFO (последним занесен - первым выбран). Проще всего представить себе стек как "стопку" чисел, расположенных друг над другом.

Например, строка

2 3 +

фактически означает следующее - числа 2 и 3 помещаются на стек, складываются и в результате на стеке остается число 5.

Вот немного более сложный пример:

4 3 2 1 + - +

Объясняю пошагово: слова + и - (а в Форте эти привычные нам знаки арифметических действий тоже являются полноценными словами) работают с двумя верхними элементами стека. Мы поместили на стек числа 4, 3, 2 и 1. Соответственно, верхними элементами стека сейчас являются числа 2 и 1. Их и складывает первый "постфиксный" плюс.

 4 3 2 1 + - +

После первого сложения на стеке остаются три числа: 4, 3 и только что полученная сумма: 3. "Постфиксный" минус тоже работает с верхними элементами стека, соответственно, мы вычитаем 3 - 3.

4 3 3 - +

На стеке остается два числа: 4 и 0, которые и складываются последним плюсом. Результат: 4.

4 0 +

Ну и как это вам? "Тяжело в учении - легко в бою!". :)


Управляющие конструкции (ветвление)

Постфиксная нотация проявляет себя и в распространенной конструкции IF THEN. Вместо традиционного применения:

IF <условие> THEN <действие_если_TRUE>
IF <условие> THEN <действие_если_TRUE> ELSE <действие_если_FALSE>

мы имеем:

<условие> IF <действие_если_TRUE> THEN
<условие> IF <действие_если_TRUE> ELSE <действие_если_FALSE> THEN

Общее правило ветвления в Форте таково: после IF пишем все, что должно выполняться, если пpовеpка условия веpнет TRUE (-1), после ELSE пишем все, что должно выполняться, если пpовеpка условия веpнет FALSE (0).
THEN - пpосто обозначает конец констpукции.
ELSE - необязательная часть условия, можно использовать пpосто IF THEN.

Пpимеp:

FILE-EXIST: "test.txt"  \ условие
IF MSG: "exist!"        \ выполнится, если файл существует
ELSE MSG: "not exist!"  \ выполнится, если файл не существует
THEN

Иногда вместо IF ... ELSE ... THEN удобнее использовать конструкцию CASE ... OF ... ENDOF ... ENDCASE, которая обычно применяется, когда надо делать выбор из нескольких, заранее известных значений:

<значение_на_стеке>
CASE
    <возможное_значение> OF действие (значение на стеке совпало с возможным значением) ENDOF 
    <возможное_значение> OF действие (значение на стеке совпало с возможным значением) ENDOF 
    \ ...
    <возможное_значение> OF действие (значение на стеке совпало с возможным значением) ENDOF 
    <возможное_значение> OF действие (значение на стеке совпало с возможным значением) ENDOF
    DUP OF действие (значение на стеке не совпало ни с одним из проверяемых значений) ENDOF
ENDCASE

Пример:

#( test_case
NoActive
Action:
    START-APPW: program.exe
    \ кладем на стек код возврата программы
    ExitCodeProc
    \ выводим сообщение в зависимости от кода возврата
    CASE
        \ код возврата 0
        0 OF MSG: "Все в порядке!" ENDOF
        \ код возврата 1
        1 OF MSG: "Лень запускаться!" ENDOF
        \ код возврата 2
        2 OF MSG: "Отстаньте от меня!" ENDOF
        \ любые остальные коды возврата
        DUP OF MSG: "Неизвестная ошибка!" ENDOF
    ENDCASE
)#

Создание и использование новых слов

Создание новых форт-слов очень напоминает создание переменных, только используется при этом другой синтаксис:

: <имя_слова> <тело слова> ;

Вы обратили внимание на пробелы после ':' и перед ';'? Да-да! Это тоже форт-слова. :) Надеюсь, теперь вам понятно почему в имени переменных, новых слов и имени задач нельзя использовать пробелы? (На всякий случай, отвечу на поставленный вопрос - с точки зрения Форта, пробел - это разделитель слов.Форт воспримет имя, содержащее пробел как два разных идентификатора).

Пример:

\ создаем новое слово 'x2', которое будет удваивать 
\ верхний элемент стека
: x2 2 * ;
\ теперь попробуем 'x2' в действии:
2 x2   \ теперь на стеке хранится результат умножения 2 * 2
16 x2  \ теперь на стеке хранится результат умножения 16 * 2

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

Пример:

\ если слово 'x2' уже определено, то мы можем 
\ создать новое слово,  которое будет, скажем, 
\ отнимать '4' от результата работы слова 'x2'. 
\ назовем новое слово 'x2minus4'
: x2minus4 x2 4 - ;
\ пробуем 'x2minus4' в действии:
8 x2minus4  \ теперь на стеке хранится результат 
            \ выражения '(8 * 2) - 4'
1 x2minus4  \ теперь на стеке хранится результат 
            \ выражения '(1 * 2) - 4'

Новые слова создаются в начале задачи (до раздела Action:), а использовать их можно внутри самого раздела Action:. Превратим предыдущий пример в задачу nnCron:

#( words_task
NoActive \ будем запускать задание вручную
\ создаем два новых слова
: x2 2 * ;
: x2minus4 x2 4 - ;
Action:
    \ выводим на экран результат работы '3 x2'
    MSG: "3 x2 = %3 x2%"
    \ выводим на экран результат работы '6 x2minus4'
    MSG: "6 x2minus4 = %6 x2minus4%"
    \ выводим на экран результат работы '1 x2minus4'
    MSG: "1 x2minus4 = %1 x2minus4%"
)# 

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


Числа двойной длины

Некоторые слова Форта и nnCron возвращают числа двойной длины, т. е. число, представление которого занимает 64 бита вместо 32. Число двойной длины занимает в стеке место двух чисел одинарной длины.

Для работы со числами двойной длины в Форте есть специальные слова, например:

D. выведение числа двойной длины на консоль
D+ сложение двух чисел двойной длины
D- вычитание одного числа двойной длины из другого (d1 - d2)
DNEGATE изменение знака числа двойной длины на противоположный
D< занесение в стек истины, если d1 меньше d2
D= занесение в стек истины в случае равенства d1 и d2
D0= занесение в стек истины, если d равно нулю

Константы двойной длины записываются с точкой на конце. Чтобы конвертировать число одинарной длины в число двойной длины используется слово S>D. Чтобы конвертировать число двойной длины в число одинарной длины используется слово D>S.

Примеры:

\ складываем два числа двойной длины
1000000. 2000000. D+
\ сравниваем два числа двойной длины и выдаем сообщение 10240. 90000. D< IF MSG: "<" ELSE MSG: ">" THEN
\ проверяем два числа двойной длины на равенство
150000. 150000. D=
IF MSG: "равны!"
ELSE MSG: "не равны"
THEN