Как стать автором
Обновить

От MUMPS к MSH

Время на прочтение 26 мин
Количество просмотров 3.4K
В предыдущей статье я уже пытался рассказать народу о достоинствах такого малоизвестного языка программирования как MUMPS. Но наряду с его достоинствами у него имеются и недостатки о которых я и хотел бы поделиться в данной статье. Некоторые комментаторы которые удосужились взглянуть на этот язык кстати обратили на них внимание. Кроме того, я хочу предложить способы устранения этих недостатков в новом языке MSH.

Первое впечатление, которое производит язык MUMPS, это то что он архаичен. Он был создан достаточно давно и будучи закрепленным в стандарте мало изменился по сравнению со своей 2-й редакцией. В MUMPS сообществе существует довольно сильное неприятие изменений этого языка. Но в реализациях пытаются вводить расширения для компенсации его недостатков, правда иногда крайне неудачно. Но несмотря на его недостатки идеология MUMPS очень продуктивна.
По большому счету основной недостаток этого языка, это отсутствие объектов и системы обработки событий. Он не объектно ориентирован. Во первых, причина, как всегда, лежит в его истории. В момент его создания объектное программирование не было распространено. Язык был сразу стандартизован, что препятствовало его коренному изменению. Во вторых, включению в язык объектов препятствует отсутствие в языке декларации переменных.
Короче, объекты необходимо в язык добавлять. Кроме того, я считаю, что современный язык программирования высокого уровня обязательно должен включать в себя развитую систему обработки событий. Из всех известных мне языков только ассемблер имеет средства обработки событий. Методология обработки событий широко применяется в практике программирования, как в визуальных библиотеках (Delphi, GTK, да и в других), так и для построения операционных систем. Операционные системы на уровне библиотек имеют различные средства обработки событий. Обработка событий в MUMPS крайне неразвита и фактически сводится к обработке ошибок, правда команда ZTRAP может создать пользовательское событие.
В качестве отправной точки для создания языка MSH я взял MUMPS не случайно. Это совершенный язык. Он в каком то смысле идеален. В нем нет противоречий, лишних конструкций. Небольшая, но мощная библиотека встроенных функций языка. Невероятная гибкость языка.
Основным, но не единственным достоинством этого языка является организация и управление данными. Описание данных отсутствует. Поэтому любая переменная может выступать в различных видах, в зависимости от контекста. В одном контексте она может трактоваться как строка, в другом как число. Структурой любой переменной является дерево, однако переменная может быть и простой. Расположена переменная может быть как в оперативной памяти, так и на любом блочном устройстве, даже на другом континенте.
Представление данных, как в оперативной памяти, так и на внешних носителях, почти одинаково и поэтому данные могут обрабатываться одними и теме же средствами языка. Язык имеет развитую систему обеспечения целостности данных. То есть, это полнофункциональная распределенная база данных. Конкретные реализации также имеют системы журналирования и дублирования данных. Язык имеет встроенную многозадачность со средствами синхронизации. Косвенный синтаксис и команда Xecute позволяют обрабатывать внешние команды, поступающие из вне программы.

Первый недостаток устраняется очень просто. Так как в языке принципиально отсутствует декларативность, то декларативная часть описания объекта отбрасывается и остается только реализация объекта в виде стандартного модуля. А в язык добавляется точечный синтаксис для обращения к свойствам объектов. Имя публичного свойства объекта ассоциируется с точкой входа в модуле. Для организации наследования вводим дополнительную команду Extend, в аргументах которой перечисляем всех предков данного класса.
А вот систему обработки событий надо разрабатывать полностью. Для этого в язык MSH были добавлены команды обработки событий:

1. EventCall — команда привязывает к событию программу обработки,
2. EventWait — команда ждет наступления заданного события,
3. EventDelete – команда удаляет событие,
4. EventTrap – команда порождает событие.

Этих команд должно быть достаточно для обработки событий. События могут быть системными, это те, что порождены внешним окружением. Например, асинхронное открытие устройства или ошибки RunTime в программе. События могут быть порождены и приложением с помощью команды EventTrap, тогда это пользовательское событие. Необходимые параметры передаются программе обработки событий в качестве аргументов.

Остальные изменения языка MSH по сравнению с MUMPS не столь принципиальны, но тем не менее должны улучшить его возможности.
Начнем с локализации данных. В MUMPS принята своя терминология обозначения областей видимости данных. Аналогом глобалей MUMPS в других языках программирования являются данные расположенные в базах данных. Аналогом локалей MUMPS в языке Си являются глобальные и автоматические переменные. По умолчанию все переменные MUMPS являются глобальными переменными в терминологии языка Си. Для создания автоматических переменных по терминологии Си в MUMPS существует команда New в двух формах. Первая форма соответствует примерно декларированию переменных в языке Си. Все переменные перечисленные в этой команде становятся автоматическими переменными до конца подпрограммы. Вторая форма команды New аналогов в Си не имеет. Это так называемая исключительная форма команды New. В этом случае все переменные не перечисленные в этой команде становятся автоматическими в смысле Си до конца выполнения подпрограммы. Самих понятий глобальных и автоматических переменных в MUMPS нет, все они называются локалями. Такое положение вещей в MUMPS меня всегда напрягало. Неудобно в каждую подпрограмму включать команду New чтобы защитить переменные. Поэтому я решил отказаться от этого способа локализации данных.
В MSH существует несколько областей видимости переменных.
1. Переменные локализованные внутри вызова подпрограммы,
2. Переменные локализованные внутри задания,
3. Переменные локализованные внутри приложения,
4. Переменные расположенные на внешних устройствах.

А так как в MSH как и в MUMPS отсутствует декларирование переменных, для разграничения областей видимости пришлось использовать единственный известный и проверенный метод древнего Бейсика по префиксу переменной.
Переменные локализованные внутри приложения имеют префикс %%.
Переменные локализованные внутри задания имеют префикс %.
Переменные расположенные на внешних устройствах имеют префикс ^.
Все остальные переменные локализованы внутри вызова подпрограммы.
Это позволило исключить чудесную команду New.

Теперь по поводу структуры данных. В MUMPS одна структура данных для локалей и глобалей — это дерево. Переменные могут быть индексироваными и не индексироваными. Но в глобалях существует такое понятие, как сокращенная ссылка. Сокращенная ссылка сильно затрудняет как понимание программы, так и ее отладку. Кроме того, если в подпрограмме используется сокращенная ссылка, то такая подпрограмма не может быть использована для обработки локальных переменных, что нарушает целостность языка MUMPS. Поэтому в MSH сокращенная ссылка отсутствует. Чтобы язык MUMPS не потерял гибкости, в нем присутствует такая, очень сомнительная конструкция, как косвенный синтаксис. Который позволяет использовать в качестве имени переменной другую переменную, в которой находится имя первой переменной. Звучит несколько коряво, но столь же неудобно им и пользоваться. Эта конструкция языка вызывает много нареканий, поэтому в MSH эта конструкция исключена. А для того чтобы не потерять гибкость языка было решено отказаться от имени переменной и оставить только индекс. Первый индекс трактовать как имя переменной. Конечно, деревянная структура данных очень универсальна. Но она заведомо не оптимальна по скорости доступа для простых, не индексированных переменных. Поэтому в MSH добавлена еще одна структура данных — одномерный массив данных.

Перейдем к командам языка.
Аргументы отделяются от команды пробелом. В MUMPS есть 2 типа синтаксически разных команд. В командах первого типа следующая команда отделяется от предыдущей пробелом. В этих командах пробелы имеют существенное значение и лишние пробелы недопустимы. Их наличие приводит к синтаксическим ошибкам. Если в команде нет аргументов, то за командой следуют 2 пробела. Команды второго типа завершаются концом строки. Таких команд 3: For, If, Else. Фактически эти команды используются в блоке с другими командами и поэтому их синтаксис отличается. Кроме этого, эти команды в отличии от команд первого типа не имеют условия их выполнения. Все это в совокупности приводит к тому что писать наглядные программы неудобно.
В MSH применена несколько другая стратегия синтаксиса.
Первое, введен признак конца команды-';'. Пробел сохранен как разделитель между командой и аргументами, но их количество теперь значения не имеет.
Второе, введено понятие блочных команд. Команда этого типа является началом блока, а концом блока является команда End. Условие выполнения команды применяется ко всем командам. Синтаксис становится более однородным.
Команды блокировки.
В MUMPS команда блокировки Lock используется в 2х формах: для блокировки и разблокировки. В MSH команда блокировки разделена на блокировку по чтению, блокировку по записи и разблокировку. Кроме того, для блокировок со временем ожидания используются системные функции. Блокировки используются в основном для синхронизации процессов и синхронизации обращения к данным в разных заданиях. В MSH синхронизация обращения к данным на внешних устройствах и данным уровня приложения выполнены на уровне языка и дополнительного использования команд блокировки не требуют.
Команда If стала блочной и должна завершаться либо Else, либо End. Аргументов у этой команды нет, а условие выполнения команды распространяется на весь блок. Команда Else используется вместе с командой If и также может иметь условие выполнения. Тогда эта команда превращается в команду ElseIf. Коменда Else — блочная и должна завершаться либо командой Else, либо командой End. Аргументов у команды Else нет.
Другой блочной командой является команда For. Эта команда реализует цикл. По содержанию команда For MSH близка к команде for языка Си. Команда for Си имеет 3 аргумента. 1-й аргумент инициализация цикла. В MSH этот аргумент исключен из команды For. Необходимая инициализация переменных выполняется перед циклом командой Set. 2-й аргумент в Си задает условие окончания цикла. В MSH условие окончания цикла перенесено в условие выполнения команд For и End. Это позволило исключить команды цикла While и do While, так как команда цикла без аргументов реализует эти циклы. 3-й аргумент в команде for Си изменение параметров цикла является аргументом команды For в MSH.
В связи с циклами в MUMPS есть одна неудобная особенность. Внутри цикла его можно завершить с помощью команды Quit. Но вся беда в том, что для завершения подпрограммы используется эта же команда, и завершить подпрограмму внутри цикла невозможно. В MSH в качестве команды завершения цикла используется команда Break, а для завершения подпрограммы применена команда Return. Команда Quit исключена.
Опыт программирования в MUMPS мне показал что в 90% случаев команда For используется для организации обхода дерева. А это не самая удобная команда. Она требует дополнительных обращений за данными. Поэтому в MSH введены 3 команды итераторов и системная функция обхода дерева.
1. команда Next- обход одного уровня дерева от начала до конца,
2. команда Back- обход одного уровня дерева от конца до начала,
3. команда Query- обход узла дерева на всю его глубину.

В MUMPS отсутствует такая полезная команда как Case. В MSH она добавлена в нотации языка Pascal, которая показалась мне более удачной, чем в Си. При этом переменная выбора может быть любого типа. Команда Case MSH блочная и завершается командой End. Метки внутри команды локализованы внутри блока.

Нет в MUMPS и такого понятия как константа, а вещь эта очень полезна, поэтому в MSH добавлена команда Const.

Для организации наследования в MSH используется команда Extend.
Кроме того из Си взята команда Include позволяющая компоновать текст программы из различных кусков.

В MSH изменена система передачи параметров в подпрограммы и функции. В MUMPS точки входа в подпрограмму и метки отличаются тем, что у точек входа присутствует иногда список формальных параметров. Но вызвать подпрограмму по любой метке, а возможно и по метке + смещение ничего не мешает, правда передать параметры в этом случае не удастся. То есть, в общем метка от точки входа может ничем не отличаться. Этот подход несколько не логичен и некрасив. Кроме того, список фактических параметров фиксирует их число, что не есть хорошо. В Си вынуждены были с помощью костылей добавить возможность передачи переменного числа фактических параметров. В MSH я поступил по другому. Никакого списка формальных параметров в MSH нет. Обращение к подпрограммам и функциям происходит по меткам в модуле с передачей фактических параметров в традиционной форме. В подпрограмму фактические параметры попадают в специальном массиве аргументов, количество которых сохраняется в нулевом элементе массива.

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

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

Далее я привожу формальное описание этого языка.
Описание языка MSH
Содержание
1. Введение
Цель.
2. Лексика языка
Алфавит
Комментарии
3. Операции
4. Константы
5. Переменные
6. Выражения
7. Команды
8. Функции
9. Предопределенные свойства.
10. Списки.
11. Структура модуля.
12. Объекты.
13. Заключение.

1. Введение
Цель
Цель: -Сократить время обслуживания цикла жизни программного обеспечения.

Сокращение времени разработки достигается следующими средствами:
-Язык должен быть синтаксически простым лаконичным и минимально возможным.
-Конструкции языка должны быть однозначными и не допускать необычного использования.
-Набор команд языка должен быть достаточным что бы поддерживать все современные модели программирования.
-Язык должен быть максимально гибким. Достигается отсутствием декларирования переменных.

Сокращение времени отладки достигается следующими средствами:
-Простотой и наглядностью программ.
-Минимально возможных ошибок в программе.
-Программы должны содержать минимум строк.

Сокращение времени сопровождения достигается следующими средствами:
-Надежностью языка.
-Язык должен иметь средства обеспечения целостности данных.
-Минимально возможных ошибок в программе.

Ни один из современных языков не соответствует поставленной цели. Сейчас имеется большой выбор различных языков каждый из которых содержит какие либо идеи. Но идей не так уж много. Попытаюсь самые ценные идеи использовать в языке.
Для достижения максимальной гибкости язык должен обладать определенными свойствами в частности он не должен быть декларативным. Все декларативные языки немедленно теряют гибкость. Программы на них сразу раздуваются в объеме. Сами деклараци переменных кроме помощи транслятору никакого другого смысла не имеют. Язык должен быть ориентирован на манипулирование данными и не содержать средств построения диалогов с пользователями. Средства построения диалогов с пользователем должен обеспечивать специализированный язык HTML. Язык должен быть ориентирован на Web разработку.
Языком наиболее полно отвечающим поставленной цели является язык программирования MUMPS. Его и берем за основу. Последним стандартом этого языка является стандарт 1995 года он и будет отправной точкой.

2. Лексика языка

Алфавит
Алфавитом является набор символов в кодировке UTF-8.

Комментарии
однострочный комментарий //
многострочный комментарий /* */

При описании языка необязательные параметры помещаются внутри скобок < >

3. Операции
Операндами могут быть любые переменные, константы и функции.
Унарные операции:
'-' унарный минус Признак отрицательного числа.
Результат: Арифметический тип.
'~' Логическая операция НЕ
Результат: целое число 0 или 1
Бинарные операции:
Операции могут быть 1-2 символьными.
Арифметические операции: Результат Арифметический тип
'+' плюс
'-' минус
'*' умножить
'**' возведение в степень
'/' деление
'\' деление нацело Результат целое число
'#' модуль числа Результат целое число
Логические операции: Результат целое число 0 или 1
'&' операция И
'|' операция ИЛИ
Операции сравнения: Результат целое число 0 или 1
'>' больше
'<' меньше
'==' равно
'~=' не равно
'~>' не больше
'~<' не меньше
'>=' больше или равно
'<=' меньше или равно
'<>' не равно
Строковые операции: Результат строка символов
'_' сцепление строк
'_>' строка следует
'_<' строка содержит

Побитовые операции над строками:
'_&' операция И
'_|' операция ИЛИ
'_^' операция исключающее ИЛИ
'_~' операция НЕ

Операция выбора:
'?'-почти соответствует стандарту языка Си.
ExpYcl? ExpTrue: expFalse
expYcl-условие выбора,
expTrue-выражение присваиваемое при выполнении условия,
expFalse-выражение присваиваемое при Не выполнении условия.

Встроенные операции имеют вид операций с префиксом `
Arg1 `name Arg2

4. Константы
Константы состоят из имени и значения. Имя есть идентификатор. Значение константы
есть любое количество алфавитно цифровых символов на любых языках. Значение константы может быть в кавычках или в двойных кавычках. В этом случае внутри кавычек могут присутствовать любые символы.
Пример значений констант: 123 ABC 15Нгш '75&56+»()»' «wq^erНГ'»
5. Переменные
В языке отсутствуют объявления переменных. Переменная возникает в момент когда ей присваивается значение. При обращении к неопределенной переменной возвращается пустая строка.
Переменные могут находиться как в оперативной памяти так и во внешней памяти. Переменные находящиеся во внешней памяти имеют префикс ^.
Например: ^abc[15,gh8,42] abc- имя глобали.

Переменные находящиеся в памяти разделяются по области видимости и структуре хранения.
1. Глобали. Переменные находящиеся на внешних устройствах. Префиксом этих переменных является ^.
2. Переменные задания. Они являются общими для всех программ в задании Job. Эти переменные имеют префикс %
3. Остальные переменные. Они видны только внутри блока Do.
4. Псевдомассивы. Они имеют префикс @.

Структура хранения может быть следующая:
1. Структура дерева. Такие переменные выглядят в виде произвольной индексной структуры заключенной в квадратные скобки. Индексы разделены запятыми. Любой индекс может иметь любой тип. Число, строку и др. Например: ldb[abc,125,5,9]
Индексы в переменных могут быть произвольными выражениями.
В имени может быть использована предопределенная константа this. В этом случае идет обращение к закрытым свойствам объекта. Такое обращение возможно только внутри вызова метода или свойства объекта.
Например:
[%this].Age

2. Структура сплошного массива.
Эта структура имеет один целочисленный индекс. Все элементы массива расположены подряд. Эта структура применяется для быстрого доступа к данным.
Индекс массива следует за знаком $
Например: $25
Индексация массива начинается с 1. Нулевой элемент массива является служебным и содержит индекс последнего элемента. Итераторы игнорируют этот элемент.

3. Псевдомассивы.
В них передается служебная информация. Обращение к ним соответствует обращению к массиву.
Перечень псевдомассивов:
префикс @ — значения переданных в функцию аргументов,
префикс do@ стек вызовов подпрограмм для текущего задания,
префикс job@ стек вызовов заданий для приложения,
префикс data@ стек данных блока Do,
префикс ind@ индексный стек блока Do.

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

7. Команды
Аргументы в различных командах могут быть разными. Аргумент может состоять из подполей разделенных либо символом = либо символом:
Для дальнейших объяснений примем следующие соглашения:
exp — префикс обозначающий выражение
expYcl — выражение задающее условие выполнения команды
ref — префикс обозначающий ссылку на переменную.
Prog — ссылка на программу в виде .expLabel[expArg <,expArg> ]
expMod — выражение значение которого есть имя модуля
expLabel — выражение значение которого есть метка в модуле
expArg — выражение значение которого является аргументом. Аргумент передается по значению.

Интервалы времени задаются в микросекундах 10e-6 сек.

Декларативные команды:
Extend, Constant, Include.
Декларативные команды имеют формат:
CMD Arg <,Arg>;
CMD — код команды.
Arg — аргумент команды.
Декларативные команды выполняются в момент трансляции по мере появления в исходном тексте программы. Их выполнение не зависит от хода выполнения программы.

Команда Extend (Ext).
Аргументами команды являются предки данного модуля. Команда используется для организации наследования.
Extend expParent <,expParent>;
expParent- константное имя родителя для данного модуля. Как видно из синтаксиса имеет место множественное наследование. Приоритет выбора родителя определяется порядком следования в списке наследования. Чем раньше встречается родитель тем его приоритет выше.

Команда Constant (Const).
Constant nameConst=valConst <,nameConst=valConst>;
nameConst- имя константы есть идентификатор и его длинна не должна превышать 18 байт,
valConst- значение константы. Может состоять из алфавитно цифровых символов или любых символов заключенных в кавычки или двойные кавычки.

Команда Include.
Include fileName <,fileName>;
fileName- имя вставляемого файла. Эта команда будет заменена содержимым перечисленных файлов.

Команды выполнения:
Break, Back, Back1, Copy, Do, Else, End, Case, Loop, GoTo, If, Job, KiLL, LockW, LockR, LockUn, Move, Next, Next1, Query, Query1, Return, Set, TimeOut,Transaction, EventTrap, EventCall, EventWait, EventDelete, Open, Close, Write, Read.

Команды состоят из:
— кода команды,
— необязательного условия выполнения команды,
— обязательного пробела,
— необязательных аргументов перечисленных через запятую.
Конец команды отмечается точкой с запятой. Код команды не зависит от регистра и может быть сокращен. Полный код команды может быть расширен любым количеством латинских символов или цифр. Например команда End может быть записана как EndIf. Это же касается и других команд.
Условие выполнения команды отделяется от кода команды знаком? Если результат вычисления условия выполнения равен 0 то команда не выполняется. Если результат вычисления условия выполнения не равен 0 то команда выполняется.
CMD<?expYcl> <Arg1 <,ArgN>>;

Команда вычисления выражения Set.
Set (S) — имеет вид:
Set<?expYcl> ref=exp <,ref=exp>;
S<?expYcl> ref=exp <,ref=exp>;
переменой ref присваивается значение выражения exp.

Команда удаления данных Kill.
Команда KiLL (K).
Kill<?expYcl> ref <,ref>;
K<?expYcl> ref <,ref>;
Команда удаляет потомков узла ref и сам узел и свойства объекта.
Имеется еще модификации этой команды.
Удаляет данные в узле ref.
KillD<?expYcl> ref <,ref>; KD <?expYcl> ref <,ref>;
Удаляет потомков узла ref.
KillN<?expYcl> ref <,ref>; KN <?expYcl> ref <,ref>;
Удаляет свойства объекта узла ref.
KillP<?expYcl> ref <,ref>; KP <?expYcl> ref <,ref>;
Возможны комбинации постфиксов например
Удаляет данные и свойства объекта узла ref.
KillDP<?expYcl> ref <,ref>; KP <?expYcl> ref <,ref>;
Удаляет данные и потомков узла ref.
KillDN<?expYcl> ref <,ref>; KN <?expYcl> ref <,ref>;

Команды копирования данных:
Узел данных refFrom сливается с узлом refTo
Copy<?expYcl> refTo=refFrom <,refTo=refFrom>;
Узел данных refFrom перемещается в узел refTo
Move<?expYcl> refTo=refFrom <,refTo=refFrom>;

Команды блокировки задания.
LockW (LW).
LockW<?expYcl> expID <,expID>;
LW<?expYcl> expID <,expID>;
Устанавливает блокировку по записи на идентификатор заданный выражением expID.

LockR (LR).
LockR<?expYcl> expID <,expID>;
LR<?expYcl> expID<,expID>;
Устанавливает блокировку по чтению на идентификатор заданный выражением expID.

LockUn (LU).
LockUn<?expYcl> expID <,expID>;
LU<?expYcl> expID <,expID>;
Снимает блокировку с идентификатора заданного выражением expID.

Команды изменения хода выполнения программы.

Команда TimeOut выполняет задержку выполнения текущей программы.
TimeOut<?expYcl> expTime<,expTime>;
expTime — выражение задающее задержку вычислений в текущей программе.

Команда GOTO (G) переход в новую точку выполнения программы.
GOTO<?expYcl> expMod.expLabel; G<?expYcl> expMod.expLabel;
переход на метку в том же модуле.
GOTO<?expYcl> expLabel; G<?expYcl> expLabel;
expMod — результат вычисления этого выражения является именем модуля.
expLabel — результат вычисления этого выражения является точкой продолжения выполнения программы.

Команда Do.
Команда выполняет вызов подпрограммы с локализацией переменных внутри этого блока выполнения.
Do<?expYcl> <expMod.>expLabel< [<arg <,arg> >] >< ,<expMod.>expLabel< [<arg <,arg> > ] > >;

Команда Job (J).
Команда выполняет программу в новом задании. Для каждого такого вызова создается новый блок Run и блок Do.
Job<?expYcl> ref=<expMod.>expLabel< [<arg <,arg>> ] >< ,ref=<expMod.>expLabel< [<arg <,arg>> ] >;
ref- в эту переменную помещается номер задания.

Команда Return (Re) завершает выполнение текущей программы и возвращает значение в вызывающую программу.
Return<?expYcl> expRet;
Re<?expYcl> expRet;
expRet- выражение вычисляется и возвращается вызывающей программе.

Блочные Команды.
Программа может содержать блоки. Блок начинается блочной командой и заканчивается командой END;

Блочная команда If (I).
Если условие выполнения этой команды не равно нулю то выполняются команды идущие за командой If вплоть до команды Else или End. Внутри блока If End могут встречаться команды Else.

Команда Else без условия выполнения может быть только одна и должна быть последней в цепочке команд Else. Если команда Else имеет условие выполнения и это условие не равно нулю то выполняются команды следующие за ней до команд Else или End.
Эти команды не имеют аргументов.
If?expYcl; I?expYcl;
любые команды
Else<?expYcl;> E<?expYcl>;
любые команды
END;
Команда Else в формате с условием выполнения фактически является командой ElseIf, а без условия выполнения является командой Else.
В общем виде блок If может выглядеть так:
If?expYcl;
comand…
< Else?expYcl; comand… >
< Else; comand… >
End;

Блочная команда выбора Case.
Команда обеспечивает выполнение участка кода в зависимости от заданного выражения. Команда используется в нотации Pascal.
Результат вычисления выражения должен давать строку.
Если эта строка равна одной из меток внутри блока Case то выполняются команды идущие после этой метки до следующей метки или команды End.
Если строка не равна ни одной метке то выполняются команды <comandElse...>.

Case<?expYcl> expLabel;
<comandElse...>
Label: comand…
<Label: comand...>
End;

Блочная команда Try.
Команда Try создает защищенный блок кода. Предназначена для обработки ошибок возникающих в программе в момент исполнения программы.
Try<?expYcl> <Prog [arg<,arg>] >;
comand…
End;
Аргументом этой команды является ссылка на программу обработки ошибки. Этот Аргумент может отсутствовать. При возникновении ошибки внутри блока вызывается программа обработки, если она присутствует и блок завершается.

Блочная команда Transaction(Tr).
Команда Transaction является началом блока выполнения транзакций глобалей. Действие этой команды распространяется только на текущее задание. Если внутри этого блока встречается команда Job то на выполнение задания Job текущая транзакция распространяться не будет. Завершается блок командой End с одним аргументом. Если этот аргумент имеет значение =0, то производится откат транзакции. Аргумент может отсутствовать тогда транзакция проводится. Если условие выполнения команды ложно то весь блок не выполняется.
Transaction<?expYcl>;
comand…
End ;

Блочные Команды цикла.
Команда Loop (L).
Команда не содержит инициализации цикла. Инициализация цикла выполняется другими командами перед циклом. Если условие выполнения команды Loop становится равным 0 то цикл завершается. Если условие выполнения команды End становится не равным 0 то цикл завершается.

Loop<?expYclFor>;
comand…
End<?expYclEnd>;

L<?expYclFor>;
comand…
End<?expYclEnd>;

expYclFor — условие продолжения цикла.
expYclEnd — условие завершения цикла.

Блочные итераторы цикла.
Эти команды используются для обхода данных. На каждом шаге становится доступной следующая вершина с данными.
*Замечание по реализации для команд Next, Back и Query. Эти замечания не касаются команд Next1, Back1 и Query1:
Внутри блока итератора опорный индекс меняться не должен. Выражения типа $$2 или [[3]] недопустимы в случае если $2 или [3] меняются внутри блока итератора. Изменение этих переменных не будет учтено.
Во время обхода структура данных не должна меняться.

Команда обхода потомков узла на одном уровне с начала до конца Next (N).
Next<?expYclEnd> refY<,refSaveInd>;
comand…
End;
N<?expYclEnd> refY<,refSaveInd>;
comand…
End;

refY — ссылка на узел потомки которого будут обходится. Опорный индекс.
RefSaveInd — ссылка на узел в который будут сохранен подиндекс потомка. Сохраняться будет только последний индекс потомка. Параметр необязательный.
Если необходимо обойти свойства узла, то к индексу добавляется символ ':'
Например Next [4:],$1

Команда обхода потомков узла на одном уровне с конца до начала Back (Ba).
Back<?expYclEnd> refY<,refSaveInd>;
comand…
End;
Ba<?expYclEnd> refY<,refSaveInd>;
comand…
End;

refY — ссылка на узел потомки которого будут обходится.
RefSaveInd — ссылка на узел в который будут сохранен подиндекс потомка. Сохраняться будет только последний индекс потомка. Параметр необязательный.

Команда обхода всех потомков узла Query (Q).
Команда Query обходит ветвь дерева сверху вниз и слева направо.
Query<?expYclEnd> refY<,refSaveInd>;
comand…
End;
Q<?expYclEnd> refY<,refSaveInd>;
comand…
End;

refY — ссылка на узел потомки которого будут обходится.
RefSaveInd — ссылка на узел в который будут сохранен подиндекс потомка. Сохраняться будет только последний индекс потомка. Параметр необязательный.

Не блочные команды.

Команды обхода дерева данных.
Команда Next1 (N1) дает следующую вершину на том же уровне.
Next1<?expYclEnd> refY,refSaveInd;

Команда Back1 (B1) дает предыдущую вершину на том же уровне.
Back1<?expYclEnd> refY,refSaveInd;

refY — ссылка на узел потомки которого будут обходится.
RefSaveInd — ссылка на узел в который будут сохранен подиндекс потомка. Сохраняться будет только последний индекс потомка. Параметр обязательный.

Команда Query1 (Q1) дает следующую вершину ветви дерева при обходе всего узла сверху вниз и слева направо.
Query1<?expYclEnd> refY,refSaveInd;

refY — ссылка на узел потомки которого будут обходится.
RefSaveInd — ссылка на узел в который будут сохранен подиндекс потомка. Сохраняться будет только последний индекс потомка. Параметр обязательный.

Выход из команд цикла может осуществляться в любой точке цикла по команде Break (Br).
Break<?expYclFor>; Br<?expYclFor>;

События.
Команда вызывает обработку пользовательского события EventTrap (EvT).
EventTrap<?expYcl> expName<(arg1<,argN> )> <,expName<(arg1<,argN> )>
EvT<?expYcl> expName<(arg1<,argN> )> <,expName<(arg1<,argN> )>
expName — выражение задающее имя события. Имя события не может превышать 18 байт.
arg — аргументы которые будут переданы обработчикам этих событий.
По этой команде потоки ожидающие наступления этого события будут продолжены. С этого момента им будут доступны аргументы события.
По этой команде будут запущены в отдельных потоках все обработчики этого события и им будут переданы аргументы.
После завершения работы всех обработчиков события оно будет удалено.
Если нет ни одного обработчика этого события то событие будет ждать появления обработчика, затем будет обработано и удалено.

Команды обработки события.
Команда назначает обработчик завершения события EventCall (EvC).
EventCall<?expYcl> expName=Prog<,expName=Prog>
EvC<?expYcl> expName=Prog<,expName=Prog>
expName — выражение задающее имя события.
Prog — ссылка на программу обработчик события.
Если в момент выполнения этой команды событие существует то обработчик будет вызван и событие удалено.

Команда ожидать завершения события EventWait (EvW).
EventWait<?expYcl> expName<,expName>
EvW<?expYcl> expName<,expName>
Ожидает наступления события и когда оно происходит то возобновляется выполнение задания. С этого момента в программе становятся доступными аргументы переданные командой EventTrap. Если в момент выполнения команды EventWait событие существует то выполнение будет продолжено.

Команда удаления события EventDelete(EvD).
EventDelete<?expYcl> expName<,expName>
EvD<?expYcl> expName<,expName>

Удаляет события с заданными именами.

События могут быть вызваны процессами происходящими в системе. Это системные события они имеют префикс sys. События так же могут быть вызваны командой EventTrap в программе. Это пользовательские события и они имеют префикс usr.

Системные события:
sysJobEnd — Завершение задания, в передаваемом аргументе номер завершенного задания. Это событие возникает в момент завершения любого задания.

Системные события работы с внешними устройствами.
Эти события инициируются командой EventTrap. Но обработчики будут вызваны только тогда когда событие будет завершено. При вызове обработчиков события в 1-ом аргументе возвращается идентификатор устройства и он будет положительным целым числом. Если произошла ошибка то возвращается в этом аргументе код ошибки, и он будет отрицательным целым числом. 2-й аргумент, если он есть, зависит от события.
sysRead — Чтение с устройства. Данные прочитанные с устройства передаются во 2-ом аргументе.
В качестве аргумента в команде EventTrap передается идентификатор файла.
sysOpenReadClose — Событие возникает при любом из этих событий.
В качестве аргумента в команде EventTrap передается имя файла.

Команды работы с файлами.
Писать в файл команда:
Write<?expYcl> pathFile;
pathFile — выражение строковое значение которого является именем файла.
Выводимые значения должны быть предварительно помещены в массив аргументов.

Читать из файла: команда
Read<?expYcl> pathFile;
pathFile — выражение строковое значение которого является именем файла.
Введенные значения будут помещены в массив аргументов.

8. Функции.
Функции могут быть встроенными и пользовательскими. Встроенные функции определены языком. Пользовательские разрабатываются программистом. Имя встроенной функции задается константой. В имени встроенной функции имеется префикс sys.
Фактические параметры передаются в функцию по значению и по имени. Если параметр передается по имени то имя переменно дополняется сзади символом!..

Встроенные функции.
Функция обхода данных.
%Query(refArrInd,<expMod.>expLabel)
refArrInd – индекс входа,
expMod.expLabel – функция обработки вершины.
Этой функции в качестве аргументов передаются Данные и дополнительный индекс вершины.

Функции блокировки.
%LockR(exp,time)— блокировка по чтению с временем ожидания,
%LockW(exp,time)— блокировка по записи с временем ожидания,
%LockSt(exp)— статус блокировки

exp — выражение значение которого и будет Идентификатором блокировки
результат выражения не должен превышать 18 байт.
Если длинна больше то учитываться будут только первые 18 байт
:time -выражение значение которого задает TimeOut в микросекундах (usleep)
Возвращаемое значение ref= результат блокировки
0- блокировка выполнена текущим заданием
<0-блокировка не выполнена
>0 —№ задания которое ранее установило блокировку. Блокировка не выполнена.

Функции Чтения записи Ini файлов
%ReadIni(expFile,refYZL)
%WriteIni(expFile,refYZL)
%WriteIni(OUT,refYZL)
%WriteIni(WWW,refYZL)
функции Чтения записи текстовых файлов
%ReadTxt(expFile,refYZL)
%WriteTxt(expFile,refYZL)
%WriteTxt(OUT,refYZL)
%WriteTxt(WWW,refYZL)
expFile-выражение задающее имя файла
refYZL — ссылка задающее вершину куда поместятся строки файла.
стандартные открытые устройства:
OUT — стандартный поток вывода
WWW — канал Браузера

Содержание файловой директории.
%ReadDir(expFile,refYZL)
expFile-выражение задающее имя директории
refYZL — ссылка задающее вершину куда поместятся строки файла.

Рекурсивное содержание файловой директории.
%ReadDirR(expFile,refYZL)
expFile-выражение задающее имя директории
refYZL — ссылка задающее вершину куда поместятся строки файла.

Создать файловую директорию.
%CreateDir(expFile)
expFile-выражение задающее имя директории

Удалить файловую директорию.
%DeleteDir(expFile)
expFile-выражение задающее имя директории

Удалить файл.
%DeleteFile(expFile)
expFile-выражение задающее имя файла

Функции возвращают количество обработанных файлов.

Компиляция файла.
Функция компиляции файла:
%Comp(pathFile);
pathFile — выражение строковое значение которого является именем файла.
В результате трансляции будет создан файл команд виртуальной MSH машины с расширением mvm.
Возвращаемое значение код возврата=0 или сообщение об ошибке в формате:
код_ошибки, строка, позиция ошибки.

Пользовательские функции.
Пользовательские функции представляют из себя обычные подпрограммы и имеют формат аргументов команды Do но с обязательными скобками.
expMod.expLabel( <expArg <,expArg>> )
.expLabel( <expArg <,expArg>> )
expLabel( <expArg <,expArg>> )
expMod — выражение задающее имя модуля.
ExpLabel — выражение задающее точку входа (имя подпрограммы)
expArg — выражение задающее аргумент.

9. Предопределенные свойства.
Системные свойства.
Системные свойства дают дополнительную информацию о системе.
%this — Индекс Объекта.
%idJob — ID выполняющегося задания.
%idDo — ID блока Do.
%idDiv — текущее устройство ввода-вывода.
%statDo — Статус выполнения программы.
%statData — состояние переменной к которой было последнее обращение.
%isTr- статус транзакции. Выполняется ли код внутри транзакции.
%Key — ссылка последнего обращения к данным.
%Data — значение последнего обращения к данным.
%nameMod — имя исполняемого модуля.

Свойства любых переменных.
У каждой переменной есть предопределенные свойства
.%type -тип переменной
.%byte — размер переменной в байтах в строковой форме
.%size — размер переменной в символах в строковой форме
.%stat — состояние переменной.
Тип результата устанавливается в соответствии с типом свойства.

Строковые свойства.
Перед выполнением этих свойств переменная преобразуется к строковому формату.
Действия над символами строки.
StartInd- индекс символа с которого надо выполнять указанное действие.
Count — количество символок учавствующих в действии.

1. Получить Count символов строки начиная с символа с индексом startInd. Если Count не задан то считается что он равен 1:
.%GetStr(startInd <,Count> )
например: [j1,j2]=[i1,i2,i3].%GetStr(5,3)
2. Получить код символа строки с индексом startInd
.%CodeStr(startInd)
например: [j1,j2]=[i1,i2,i3].%CodeStr (5)
3. Снять с вершины строки Count символов
.%PopStr(Count )
например: [j1,j2]=[i1,i2,i3].%PopStr(2)
4. Вставить в строку символы начиная с символа с индексом startInd
.%InsStr(startInd)
например: [i1,i2,i3].%InsStr(7)=[j1,j2]
или $2=[i1,i2,i3].%InsStr(7,[j1,j2])
5. Заменить в строке Count символов:
.%SetStr( startInd <,Count> )
например: [i1,i2,i3].%SetStr(4,3)=[j1,j2]
6. Удалить из массива Count символов начиная с символа с индексом startInd
.%DelStr( startInd <,Count> )
например:
[j1,j2]=[i1,i2,i3].%DelStr(5,2)
7. Найти подстроку начиная с символа с индексом startInd если startInd не задан то поиск начинать с начала строки:
.%FindStr( subStr<,startInd> )
Результат: позиция первого искомого символа.
например:
[j1,j2]=[i1,i2,i3].%FindStr(ABC,2)
8. В строке символы charSource заменить символами charRepl если charRepl не задан то символы charSource удалить из строки:
.%ReplStr(charSource<,charRepl> )
например:
[j1,j2]=[i1,i2,i3].%ReplStr( ABC,123)

Действия над полями строки.
Если Count не задан то считать его равным 1.
1. Получить Count полей строки начиная с поля startInd, если Count не задан то считать Count=1:
.%GetField(delimiter,startInd <,Count> )
например:
[j1,j2]=[i1,i2,i3].%GetField( ',',5,3
2. Добавить поля в строку:
.%PushField(delimiter)
например
[i1,i2,i3].%PushField('.')=[j1,j2]
3. Снять с вершины строки Count полей если Count не задан то считать Count=1:
.%PopField( delimiter<,Count> )
например
[j1,j2]=[i1,i2,i3].%PopField('AB',2)
4. Вставить в строку поля
.%InsField(delimiter,startInd)
например:
[i1,i2,i3].%InsField('/',7)=['-',j1,j2]
5. Заменить в строке Count полей начиная с поля startInd
.%SetField( delimiter,startInd <,Count> )
например
[i1,i2,i3].%SetField('-',4,3)=[j1,j2]

6. Удалить в строке Count полей начиная с поля startInd
.%DelField( delimiter,startInd <,Count> )
например
[j1,j2]=[i1,i2,i3].%DelField('-',5,2)

10. Списки.
Любая переменная может быть списком. Список состоит из одного или нескольких значений. Значения могут быть любых типов в том числе и списками.
Создать cписок можно конструкцией:
ref={exp<,exp>}
ref — ссылка на переменную куда будет записан список.
exp — выражение которое станет соответствующим элементом списка.
Список может быть также возвращен командой Query.

Если ссылка ref является списком то в индексе она будет развернута в соответствующее количество индексов.
[ref]
Каждый элемент списка станет соответствующим индексом. В ссылке обычные индексы могут чередоваться со списками. Например переменная может выглядеть так:
[Af,[B,2],125,$1,5.6]
если переменные[B,2] и $1 являются списками то они будут развернуты в индексы в соответствии с размером списка.

Действия над списками:
1. Получить Count элементов списка начиная с startInd
.%GetList(startInd <,Count> )
например [j1,j2]=[i1,i2,i3].%GetList(5,3)
2. Добавить к списку другой список
.%PushList
например
[i1,i2,i3].%PushList=[j1,j2]
3. Снять с вершины списка Count элементов списка
.%PopList(Count )
.%PopList — Снять с вершины списка 1 элемент
например
[j1,j2]=[i1,i2,i3].%PopList(2)
4. Вставить в список на место элемента startInd другой список
.%InsList(startInd)
например
[i1,i2,i3].%InsList( 7)=[j1,j2]
5. Заменить в списке Count элементов начиная с элемента startInd
.%SetList( startInd <,Count> )
например
[i1,i2,i3].%SetList(4,3)=[j1,j2]
6. Удалить из списка Count элементов начиная с элемента startInd
.%DelList(startInd <,Count> )
например
[j1,j2]=[i1,i2,i3].%DelList(5,2)

11. Структура модуля.
Модуль состоит из строк. Строка может начинаться с метки. Тогда после метки стоит двоеточие. Между меткой и двоеточием пробелов быть не должно. Далее через пробел могут следовать команды. Команды отделяются друг от друга точкой с запятой ';'.

12. Объекты.
Модули могут выступать в качестве Типа(Класса) объекта. Имя модуля соответствует типу объекта и не должно превышать 18 байтов. Имя свойства по чтению должно быть меткой в модуле. Свойство по записи должно быть меткой совпадающей с именем свойства с префиксом '.'. Наследование обеспечивается командой Extend. Любая переменная может стать объектом для этого достаточно присвоить ее предопределенному свойству .TYPE имя типа (модуля). Например имеется тип (модуль) Org и свойство этого типа Arg.
Создаем объект этого типа:
Set [An,12].TYPE=Org; Тогда обращение к свойству Arg по записи и по чтению будет выглядеть так:
[An,12].Arg=[An,12].Arg+1;

13. Заключение.
Считаю что описанный язык будет отвечать поставленным целям. Приведенное множество команд позволяет реализовать все современные концепции программирования. Наличие команд создания заданий и обработки событий позволяет упростить процесс программирования. Управление данными в языке позволяет исключить большое количество ошибок при разработке программ.

Автор: Шарымов Михаил Алексеевич. Email: misha_shar53@mail.ru
При использовании данного материала ссылка на источник и автора обязательна.
Теги:
Хабы:
-4
Комментарии 11
Комментарии Комментарии 11

Публикации

Истории

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн