Вычислительная среда

Вычислительная среда системы предназначена для осуществления различных преобразований данных, необходимых для формирования предъявляемого пользователю содержания публикаций. Она включает:
язык программирования;
базовые функции;
функции расширений.
Встроенный язык программирования имеет исключительно вспомогательное значение и используется для соединения воедино той или иной последовательности встроенных функций.
Все базовые функции и функции представленных на данном сайте расширений могут быть безопасно с точки зрения системы и её пользователей применяться при создании любых публикаций. Они устроены таким образом, что обеспечивают доступ только к тем данным, которые могут использовать составитель и читатель публикации, в которой эти функции применяются. Изменить или удалить чужие данные невозможно, если составитель публикации или её читатель не имеют на это права. Также с помощью встроенных функций нельзя прочитать данные, полномочий на доступ к которым составитель публикации и/или её читатель не имеют. В то же время, возможна ситуация, когда читатель публикации не имеет права доступа к определённым данным, но имеет возможность читать публикацию, составленную пользователем, у которого такие права есть и при формировании данной публикации были использованы.
Наличие вычислительной среды позволяет легко подключать к ядру системы любые её расширения, которые могут создавать любые заинтересованные лица на языке программирования PHP. Решение о подключение того или иного расширения к системе принимает администратор хостинга, на котором размещён сайт, самостоятельно анализируя представленные ему на "утверждение" исходные коды расширения. Все его действия сводятся лишь к размещению соответствующих PHP-скриптов в определённом каталоге и правке конфигурационного файла вычислительной среды, устанавливающего соответствие имён PHP-функций расширения и одного или нескольких внешних имён, по которым эти функции будут доступны создателям публикаций и владельцам личных данных. Также может быть необходимо дополнение файла текстовых констант новыми значениями, используемыми в программах устанавливаемого расширения.
С точки зрения пользователей системы любое расширение представляет собой набор функций, выполняющих те или иные действия с определёнными данными. Обращение к подключённым расширениям производится через внешние имена функций, сопоставленные с именами функций расширения (API-интерфейсы расширений). Обращение к конкретной функции производится по её внешнему имени прямо из текста публикации. При обработке текста публикации система устанавливает соответствие функции, записанной в тексте публикации и вызывает соответствующую ей PHP-функцию расширения. Благодаря такому подходу возможности системы могут неограниченно развиваться за счёт несложно встраиваемых в неё новых расширений. Без наличия вычислительной среды это было бы довольно сложно.

Встроенный язык программирования

Для организации вычислений и реализации сложных действий по формированию выходного документа в систему встроен простой процедурный язык программирования. Он включает все основные возможности процедурных языков:
ведение системы переменных (включая ассоциативные массивы произвольной размерности);
вычисление выражений;
организацию условных вычислений с несколькими уровнями вложенности;
организацию циклических вычислений с несколькими уровнями вложенности;
большую библиотеку встроенных функций;
возможность вызова функций (модулей) пользователя с произвольным уровнем вложенности.
Программы могут размещаться в отдельных секциях той же публикации, либо импортироваться из других источников данных (секций других публикаций или текстовых файлов). Каждый такой источник является отдельным модулем.
Модули могут иметь формальные параметры, замещаемые фактическими при вызове, и могут возвращать результаты своих действий вызвавшей программе.
Порядок написания программ исходит из принципа: одна строка - один оператор (логически завершенная часть оператора для if и while). Если первый непустой символ строки *, то строка игнорируется (комментарий).

Переменные и константы

Переменные предназначены для хранения информации, которая может измениться в процессе выполнения программы или обработки документа. Имена переменных должны содержать только строчные буквы (латиница и кириллица), цифры, а также символ подчёркивания (_). Имя должно начинаться с буквы.
Переменные не нужно объявлять заранее, а хранимая в них информация не имеет предопределённого типа. Если переменной ранее не было присвоено никакое значение, то считается, что она не имеет значения (NULL). Проверить наличие или отсутствия значения у переменной можно с помощью функции nz() (см. далее).
Значения переменных могут иметь тип строка или число. По сути все значения - это строки. Числом является строка, которая может быть интерпретирована как правильно записанное число - строка, которая состоит из цифр с необязательным знаком + (плюс) или - (минус) впереди, необязательным наличием символа . (точка) как разделителя целой и дробной частей, а также модификатора E<число> в конце, как экспоненциальной части (степени десяти). Система написана на языке программирования PHP и потому в ней числом является такая строка, которая является числом с точки зрения PHP (is_numeric(x)==true). Подробнее см. документацию по PHP. Других типов, кроме "строка" и "число" в системе нет. Логическое значение false (неверно) - это число равное нулю или пустая строка (строка, которая содержит ноль символов). Логическое значение true (верно) - это число НЕ равное нулю или НЕпустая строка (строка, в которой есть хотя бы один символ).
Константа в системе - это любая последовательность символов. Константа, которая должна интерпретироваться как строка, должна быть заключена в одинарные или двойные кавычки. Если строковая константа заключена в одинарные (') кавычки, то внутри неё может быть любое число двойных (") кавычек. Если строковая константа заключена в двойные (") кавычки, то внутри неё может быть любое число одинарных (') кавычек. Строки, которые должны интерпретироваться как числа, указываются без кавычек. Если по мнению системы такая строка не является правильно записанным числом, то при участии в арифметических действиях её значение интерпретируется как ноль.
Если после имени переменной следует открывающая круглая скобка "(", то система рассматривает его как имя встроенной функции или массива. Если встроенная функция с таким именем не определена в конфигурации, то считается, что переменная указывает на массив текущего модуля. Поэтому надо очень осторожно подходить к именованию массивов: имя массива не должно совпадать с именем какой-либо встроенной функции. Если это правило будет нарушено, то система попытается выполнить функцию, а не обратиться к массиву. В любом модуле с помощью функции omm() (см. далее) можно принудительно заставить систему считать определённые имена массивами, а не функциями. Это может обезопасить модуль от появления в системе новых функций, поскольку имена функций глобальны, а имена массивов локальны для данного модуля. Но в этом случае обратиться к функции с этим именем в данном модуле уже будет нельзя.
Обращение ко всему массиву осуществляется указанием его имени (без скобок), а к элементам массива - с помощью серии разделённых запятыми (,) индексов, заключённых в круглые скобки. Индексом может быть либо любое целое число, либо строка (в одинарных или двойных кавычках), либо скалярная переменная, содержащая целое число или строку. Для многомерных массивов указание части первых индексов интерпретируется как обращение к подмассиву массива. Например, если переменная х является двумерным массивом (матрицей), то обращение x('ab') возвратит строку матрицы x c индексом ab, если она существует и неопределённое значение (NULL) в противном случае.
Каждый модуль имеет своё пространство имен (контекст). Отдельным контекстом является также уровень публикации (документа). Переменные каждого контекста независимы. Попросту говоря, переменные с одинаковыми именами в разных контекстах могут иметь разные значение. С помощью функции ppm() можно получить, а с помощью функции ipm() - изменить переменную в любом другом контексте.
Все переменные в любом контексте являются статическими. То есть, при выходе из модуля, созданные в нем переменные, включая значения, присвоенные формальным параметрам, сохраняют свои значения. Поэтому, если повторный вход в данный модуль должен начинаться с "чистого листа", то перед выходом из него или сразу после входа следует предусмотреть уничтожение нежелательных переменных. Для этого можно использовать встроенную функцию usp() (см. далее).

Выражения и однострочные операторы

Выражения строятся по правилам, аналогичным традиционным системам программирования. Допускаются следующие знаки операций:
+ сложение;
- вычитание или унарный минус;
* умножение;
/ деление;
| логическое ИЛИ;
& логическое И;
= равно;
# не равно;
<> не равно.
> больше;
< меньше;
>= больше или равно;
<= меньше или равно.
Логическим значением "истина" является любое число не равное нулю или непустая строка. Значением операций сравнения или логических операций является 1, если условие истинно, и 0, если условие ложно.
Операндами в выражениях могут быть константы, скалярные переменные, массивы, элементы массивов, обращения к встроенным функциям. Отдельные фрагменты выражения могут объединяться круглыми скобками, устанавливая нужный порядок вычислений по стандартному правилу - сначала вычисляется всё, что в скобках и то, что получено внутри скобок, является одним из операндов для последующих вычислений. Приоритеты операций:
унарный минус;
сравнения (=,#,<>,>,>=,<,<=);
умножение, деление, логическое И;
сложение, вычитание, логическое ИЛИ.
Некорректные с точки зрения математики операции не приводят к остановке выполнения программы (если иное не предусмотрено установкой режимов выполнения программы встроенными функциями). При делении на ноль происходит запись соответствующего сообщения в виде комментария (пользователь не видит) в HTML-код выходного документа, а результатом выполнения операции является NULL. Числовым значением строки, которая не может быть преобразована в число является ноль. Поэтому, если такое значение участвует в арифметической операции, то это эквивалентно тому, что один из операндов - ноль. Если какая-то часть выражения не может быть вычислена и получает неопределённое значение, то в дальнейших вычислениях оно фигурирует со значением ноль. Важно помнить, что неопределённое значение (NULL) с точки зрения системы - это тоже значение, которое при арифметических вычислениях интерпретируется как ноль, а при преобразованиях строк как пустая строка.
Все операторы, кроме if и while являются однострочными. Для них действует правило: одна строка - один оператор. Ими могут быть:
оператор присваивания;
вызов встроенной функции;
операторы loop и exit (см. далее).
Оператор присваивания строится по обычным правилам:
имя_переменной=выражение

Условный оператор

Для организации условных вычислений в языке используется оператор: if elseif else end. Структура оператора:
if выражение0
   операторы
elseif выражение1
   операторы
elseif выражение2
   операторы
 ...
elseif выражениеN
   операторы
else
   операторы
end
Если выражение0 верно, то выполняются последующие операторы (до elseif (если есть), else (если есть) или end (если нет elseif и else)), в противном случае они пропускаются и вычисляется выражение1. Если оно верно, то выполняются последующие операторы до следующего elseif или else, в противном случае проверяется выражение2 и т.д. Если ни одно из выражений 0-N неверно, выполняются операторы, следующие после else. Фрагменты elseif и else необязательны и могут использоваться по необходимости. Условные вычисления могут иметь сколько угодно уровней вложенности.

Оператор цикла

Для организации циклических вычислений в языке используется оператор: while выражение. Структура оператора:
while выражение
    операторы
    loop
    операторы
    exit
    операторы
end
Операторы между while и end выполняются до тех пор, пока выражение (условие продолжения цикла) является истинным (не равно нулю). Оператор loop инициирует переход к началу цикла и проверке условия продолжения цикла в обход последующих операторов. Оператор exit осуществляет немедленный выход из цикла к следующему после end оператору. Циклы могут иметь неограниченное число уровней вложенности. Операторы loop и exit необязательны и могут использоваться по необходимости.
Для принудительного выхода из цикла может использоваться также функция vz(), осуществляющая возврат к вызвавшему модулю.

Пример программы и её выполнения


В данной статье имеется секция p01 следующего содержания:
# n
* Если при вызове модуля значение не передано
n=sif(nz(n),10,n)
*Начальное значение переменной цикла
i=1
*Последовательный перебор переменной цикла
while i<n
    *Вывод сообщений о чётности и нечётности переменной цикла
    if ostatok(i,2)=0
        vzp(i,"чётное число")
    else
        vzp(i,"нечётное число")
    end
    if ostatok(i,3)=0
        vzp(i,"делится на три")
    end
    i=i+1
end
*Вывод сообщения в зависимости от статуса пользователя
k=sp()
if k=0
    vzp('Вы не авторизованы.','Статус = ',k)
elseif k=1
    vzp('Вы - Читатель.','Статус = ',k)
elseif k=2
    vzp('Вы - Писатель.','Статус = ',k)
elseif k=3
    vzp('Вы - Эксперт.','Статус = ',k)
else
    vzp('Вы - Редактор или Администратор.','Статус = ',k)
end
Она представляет собой программу, которую можно выполнить из текста статьи с помощью функции vp().
Разметка
@vp("*~p01")
Вывод
1 нечётное число
2 чётное число
3 нечётное число
3 делится на три
4 чётное число
5 нечётное число
6 чётное число
6 делится на три
7 нечётное число
8 чётное число
9 нечётное число
9 делится на три
Вы не авторизованы. Статус = 0
Строки, начинающиеся с символа * - комментарии. При выполнении они игнорируются.
Программа имеет один формальный параметр n (строка # n). Если параметр не передан (нет значения), то используется его значение по умолчанию, устанавливаемое функцией sif():
n=sif(nz(n),10,n)
Далее устанавливается начальное значение переменной цикла (i=1). Потом программа выполняет цикл (while ... end), в котором операторы if анализируют остаток (функция ostatok()) от целочисленного деления переменной цикла, функцией vzp (вывести значения переменных и констант через пробел с переводом строки в конце) выводятся соответствующие сообщения и переменная цикла увеличивается на единицу.
После завершения цикла переменной k присваивается значение функции sp(), возвращающей статус читающего статью пользователя. В зависимости от его уровня функцией vzp() выводится сообщение о статусе и его значение.

Библиотека системных функций

Системные функции предназначены для:
запуска и завершения выполнения программ;
компиляции модулей, используемых программами;
доступа к переменным других модулей;
управления состоянием переменных;
установке соглашений об именах массивов.

Функция vp - выполнить программу

Функция vp(ud) используется для запуска программы из источника данных по указателю ud (см. Указатели данных ) В такую программу нельзя передать параметры. Пример использования функции см. выше.

Функция kd - компилировать данные по указателю данных

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

Функция km - компилировать модуль из списка строк

Функция km(im,ss) осуществляет компиляцию модуля из списка строк исходного кода, определяемого параметром ss, и помещает его в список скомпилированных модулей под именем, определяемым параметром im. Параметр ss может быть массивом строк или несколькими строками, разделёнными символом перевода строки. Скомпилированный функцией km() модуль может быть вызван по его имени из любой другой программы (модуля) и ему могут быть переданы параметры.

Функция vm - выполнить модуль с параметрами

Функция vm(im,p1,p2,...,pn) выполняет модуль с именем im, передавая ему параметры p1,p2,...,pn. Модуль im предварительно должен быть скомпилирован функциями kd() или km().

Пример совместного использования функций kd() и vm().

Разметка
@kd('primer','*~p01')
@vm('primer',13)
Вывод
1 нечётное число
2 чётное число
3 нечётное число
3 делится на три
4 чётное число
5 нечётное число
6 чётное число
6 делится на три
7 нечётное число
8 чётное число
9 нечётное число
9 делится на три
10 чётное число
11 нечётное число
12 чётное число
12 делится на три
Вы не авторизованы. Статус = 0
Функция kd('primer','*~p01') компилирует исходный текст программы, размещённой в секции p01 данной статьи, под именем primer. Далее, функция vm('primer',13) выполняет модуль primer, передавая ему параметр 13.

Функция vz - вернуть значение

Функция vz(p) осуществляет выход из модуля и возвращает в вызвавший модуль значение переменной p, установленное в этом модуле. Переменная p может быть скаляром, массивом, числовой или строковой константой. Синоним функции - return.

Функция ppm - получить значение переменной другого модуля

Функция ppm(im,ip) возвращает значение переменной с именем ip модуля im. Все переменные любого модуля являются статическими и не теряют своих значений при выходе из модуля. Поэтому любой модуль может обратиться к любому другому за получением тех или иных скрытых в нём значений.

Функция ipm - изменить переменную другого модуля в текущем

Функция ipm(im,ip,z) присваивает значение z переменной с именем ip модуля im. Благодаря этому можно не передавать другому модулю множество параметров, а изменить нужные из них извне.

Мы не разделяем паранойю поборников инкапсуляции и строгой типизации! Бывает, что так намного удобней. Если уж насильственно изменяешь переменную в другом модуле, то знаешь зачем это делаешь!

Пример совместного использования функций vz(), ppm(), ipm().

В данной статье имеется секция p02 следующего содержания.
vzp('Программа из секции p02')
*Компилируем модуль, содержащийся в секции p03 данной статьи под именем px
kd('px','*~p03')
*Выполняем модуль px, передавая параметры
u1=vm('px',1,3,5)
*Вынимаем из модуля px значения переменных x2 и x3
u2=ppm('px','x2')
u3=ppm('px','x3')
*Выводим полученные значения
vzp('Основная программа')
vzp(stroka('u1=',u1,' u2=',u2,' u3=',u3))
*Изменяем содержимое переменных x2, x3
ipm('px','x2',10)
ipm('px','x3',20)
*Опять получаем x1,x2,x3
u1=ppm('px','x1')
u2=ppm('px','x2')
u3=ppm('px','x3')
*Опять выводим полученные значения
vzp(stroka('u1=',u1,' u2=',u2,' u3=',u3))
Она содержит программу, компилирующую и выполняющую модуль из секции p03, которая имеет следующее содержание.
# p1 p2 p3
vzp('Модуль px из секции p03 вызван с параметрами ',p1,p2,p3)
x1=p1
x2=p2
x3=p3
vz(x1)
Выполняем программу из секции p02.
Разметка
@vp('*~p02')
Вывод
Программа из секции p02
Модуль px из секции p03 вызван с параметрами 1 3 5
Основная программа
u1=1 u2=3 u3=5
u1=1 u2=10 u3=20

Функция nz - проверить отсутствие значения у переменной

Функция nz(p) возвращает значение 1, если переменная p не имеет значения и 0 в противном случае. С точки зрения вычислительной среды существуют все возможные переменные. Но одни имеют значения, а другие не имеют.

Функция upm - удалить значения списка переменных из контекста текущего модуля

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

Функция opm - оставить значения списка переменных в контексте текущего модуля

Функция opm(sp) является противоположностью функции upm. Она оставляет в контексте модуля значения только тех переменных, которые имеются в списке sp и удаляет значения всех остальных переменных текущего модуля. Список sp может быть массивом или строкой, содержащей перечень имён, разделённых пробелами.

Пример использования функции upm().

В данной статье имеется секция p04 следующего содержания.
vzp('Программа из секции p04')
*Компилируем модуль, содержащийся в секции p05 данной статьи под именем py
kd('py','*~p05')
*Выполняем модуль py, передавая параметры
vm('py',10,20,30)
*Второй вход в модуль py c новыми параметрами
vm('py',100,200,300)
*Третий вход в модуль py c новыми параметрами
vm('py','A','B','C')
Она содержит программу, компилирующую и выполняющую модуль из секции p05, которая имеет следующее содержание.
# p1 p2 p3
*Подсчёт числа входов
v=v+1
vzp('<b>Модуль из секции p05.','Вход ',v,'</b>')
*Вывод текущих значений переменных x1, x2, x3
vzp('Значения при входе: ',stroka('x1=',x1,' x2=',x2,' x3=',x3))
*Установка значений переменных x1, x2, x3
x1=p1
x2=p2
x3=p3
*Вывод текущих значений переменных x1, x2, x3
vzp('Установленные значения: ',stroka('x1=',x1,' x2=',x2,' x3=',x3))
*При входах, начиная со второго, переменные x2, x3 стираются
if v>1
    upm("x2 x3")
end
Выполняем программу из секции p04.
Разметка
@vp('*~p04')
Вывод
Программа из секции p04
Модуль из секции p05. Вход 1
Значения при входе: x1= x2= x3=
Установленные значения: x1=10 x2=20 x3=30
Модуль из секции p05. Вход 2
Значения при входе: x1=10 x2=20 x3=30
Установленные значения: x1=100 x2=200 x3=300
Модуль из секции p05. Вход 3
Значения при входе: x1=100 x2= x3=
Установленные значения: x1=A x2=B x3=C
При первом входе в модуль py его переменные не имеют значений. Поэтому значения при входе не выводятся (пустые). Далее модуль устанавливает значения своих переменных в соответствии с переданными ему параметрами. Они сохраняют значения при выходе из модуля. При повторном входе в модуль они уже имеют значения, которые и выводятся при втором входе. Однако, начиная со второго входа в модуль, в конце его выполнения значения переменных x2 и x3 удаляются. Поэтому при третьем входе переменная x1 значение сохранила, а переменные x2 и x3 - нет. Для подсчёта числа входов модуль py использует переменную v. При первом входе у неё нет значения. Однако, если переменная не имеет значения, то при её использовании в выражении, она считается равной нулю.

Функция omm - объявление массивов модуля

Функция omm(sp) выполняет идентификацию переменных, которые следует считать массивами, а не функциями.
Если после имени переменной следует открывающая скобка, то система по умолчанию рассматривает его как имя встроенной функции. Если встроенная функция с таким именем не определена в конфигурации, то считается, что переменная указывает на массив. Поэтому имя массива не должно совпадать с именем какой-либо встроенной функции. Если это правило будет нарушено, то система попытается выполнить функцию, а не обратиться к массиву. С помощью функции omm() можно принудительно заставить систему считать определённые имена данного модуля массивами, а не функциями. Это может обезопасить модуль от появления в системе новых функций, поскольку имена функций глобальны, а имена массивов локальны для данного модуля. Но в этом случае обратиться к функции с этим именем в данном модуле уже будет нельзя.

Пример использования функции omm().

В данной статье имеется секция p06 следующего содержания.
*Компилируем модуль, содержащийся в секции p07 данной статьи под именем pz
kd('pz','*~p07')
vzp('<b>Основная программа</b>')
vzp('Синус 10=',sin(10))
vm('pz',10)
vzp('<b>Основная программа</b>')
vzp('Синус 20=',sin(20))
vm('pz',20)
vzp('<b>Основная программа</b>')
vzp('Синус 30=',sin(30))
vm('pz',30)
Она содержит программу, компилирующую и выполняющую модуль из секции p07, которая имеет следующее содержание.
# p
omm("sin")
i=sif(nz(i),0,i+1)
vzp('<b>Модуль</b>')
sin(i)=p
vms(sin)
Выполняем программу из секции p06.
Разметка
@vp('*~p06')
Вывод
Основная программа
Синус 10= -0.54402111088937
Модуль
Array
(
    [0] => 10
)
Основная программа
Синус 20= 0.91294525072763
Модуль
Array
(
    [0] => 10
    [1] => 20
)
Основная программа
Синус 30= -0.98803162409286
Модуль
Array
(
    [0] => 10
    [1] => 20
    [2] => 30
)
Можно видеть, что основная программа считает, что sin() - это функция, а модуль считает, что sin() - это его локальный массив.

Функция vv() - вычислить выражение.

Функция vv(e) вычисляет выражение, содержащееся в строке e.
Разметка
~x=2
~y=3
~r=vv('x*x+y*y')
@vzp(r)
Вывод
13

Функция sif() - выбор значения в зависимости от условия

Функция sif(a,x,y) возвращает значение x, если a не равно нулю или пустой строке, и y - в противном случае. По сути дела - это сокращённая запись оператора if ... else ... end с тем исключением, что в функции sif() вычисляются все выражения: а,x и y, а возвращаемое значение формируется в зависимости от значения выражения a, а в операторе if ... else ... end только выражение a и, в зависимости от его значения, либо выражение x, либо выражение y.

Функция vzp - вывод значений переменных

Функция vzp() выводит в текст публикации значения своих аргументов - констант, переменных или массивов. Число выводимых значений неограничено. Если одним из аргументов является массив, то последовательно выводятся все его значения, соответствующие первому измерению. Выводимые значения разделяются пробелами, а в завершении выводится тег перевода строки [br]. Множество примеров использования функции vzp приведено в предыдущих примерах.

Библиотеки функций

Помимо рассмотренных функций, обеспечивающих выполнение программ на встроенном языке, имеется множество функций, имеющих прикладное значение. Часть из них встроены в ядро системы, а часть является принадлежностью её расширений, которые, по сути дела, являются всего лишь дополнительными библиотеками функций, выполняющих те или иные предусмотренные расширением действия. Поэтому, если расширение установлено, то с точки зрения пользователя все его функции также становятся встроенными и могут без ограничений применяться при формировании публикаций и личных данных.
Подробнее см.
Оцените публикацию по предложенной шкале
-5  -4  -3  -2  -1  0  +1  +2  +3  +4  +5

Статистика Код