7. Язык M++

7.1. Структура программы

Программа на M++ размещается в одном или нескольких текстовых файлах (обычно с раширением .4xx). Один из этих файлов должен быть главным файлом программы - именно его имя передается утилите mxxc и именно с него начинается выполненние программы. Главный файл программы может подключать другие файлы программы через директиву препроцессора #include.

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

В общем виде, программа на M++ имеет следующую структуру:

программа ::= <декларация>
	| <оператор>
	| <программа> <декларация>
	| <программа> <оператор>

декларация ::= <декларация_строковой_переменной>
	| <деларация_переменной_вектора>

оператор ::= <оператор_if>
	| <оператор_выражения>
	| <оператор_for_each>
	| <оператор_halt>
	| <пустой_оператор>
	| <составной_оператор>

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

7.2. Элементы языка

7.2.1. Алфавит и лексические элементы

Программа на языке M++ формируются из символов, входящих в следующий алфавит:

'A' - 'Z'
'a' - 'z'
'_'
'0' - '9'
'{' '}' '[' ']' '(' ')' '"'
',' ';'
'=' '+'
'&' '|' '!'

Лексическими единицами в M++ являются:

7.2.2. Комментарии

В языке M++ допускается применение двух типов комментариев: однострочных и многострочных. Однострочные комментарии начинаются символами // и заканчиваются в конце строки. Многострочные комментарии начинаются символами /* и заканчиваются символами */. Многострочные и однострочные комментарии могут быть вложенными друг в друга. Многострочные комментарии не могут быть вложенными.

7.2.3. Зарезервированные ключевые слова

Следующие ключевые слова в языке M++ зарезервированы и не могут использоваться в качестве идентификаторов:

else
empty
foreach
halt
if
in
string

7.2.4. Строки

В языке M++ существуют два способа записи строк: традиционный и способ записи verbatim-строк. Традиционный способ записи строк аналогичен способу записи строк в языках C/C++ (за исключением т.н. триграфов).

В традиционном способе записи строкой считается последовательность символов, заключенных в кавычки. Строки могут содержать следующие escape-последовательности: \n, \r, \t, \b, \", \\. Так же символы могут кодироваться шестнадцатиричными (escape-последовательность, начинающаяся с префикса \x, за которым следуют две шестнадцатиричные цифры) и восьмиричными (escape-последовательность, начинающаяся с префикса \ за которым следуют три восьмиричные цифры) значениями. Если строка не помещается на одной строке, ее можно продолжить на следующей строке поставив в конце строки символ обратной косой \ (за этим символом может находиться только переход на новую строку). Например:

string t;
t = "String value";
t = "String value '2'";
t = "String value \"3\"";
t = "c:\\home\\eao\\mxx-4";
t = "~/mxx-4/default.4xx";
t = "Multi\tline\nstring";
t = "Spec char\x02";
t = "String on \
multiple lines";

Примечание. Несколько расположенных друг за другом строк (например, "abc" "def" "ghi") считаются различными экземплярами лексического элемента строка. Они объединяются в одну строку только на этапе синтаксического анализа программы.

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

string t;
t = #"
@echo off
set MXX4=-i c:\home\eao\mxx-4\lib -d MSC
set PATH=%path%;c:\home\eao\mxx-4
"c:\program files\far\far"
"#;

Будет неявно преобразована препроцессором в следующую традиционную запись строки:

string t;
t = "\n\
@echo off\n\
set MXX4=-i c:\\home\\eao\\mxx-4\\lib -d MSC\n\
set PATH=%path%;c:\\home\\eao\\mxx-4\n\
\"c:\\program files\\far\\far\"\n\
";

7.3. Переменные

В M++ переменные могут иметь два типа: "строка" и "вектор строк". Каждым элементом вектора строк является строка.

В M++ различаются самостоятельные переменные и переменные-итераторы. Самостоятельные переменные (далее просто переменные) создаются посредством деклараций, могут иметь тип "строка" или "вектор строк". Переменные-итераторы объявляются и создаются в рамках оператора for_each (7.8), имеют тип "строка" и служат для доступа к значениям переменной типа "вектор строк".

Областью видимости переменной является блок в котором она объявлена. Блоком является составной оператор. Для переменных-итераторов блоком является оператор for_each. Если переменная объявляется вне какого-либо блока, то она является глобальной переменной (для нее блоком является вся программа).

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

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

7.3.1. Декларация строковой переменной

Формат:

декларация_строковой_переменной ::= string <идентификатор>;

Имя переменной должно быть уникально в рамках блока.

Примечание. В текущей версии языка M++ переменные должны объявляться по одной. При объявлении нельзя сразу присвоить значение переменной.

Пример:

string a;
string b;

7.3.2. Декларация переменной-вектора

Формат:

деларация_переменной_вектора ::= string[] <идентификатор>;

Имя переменной должно быть уникально в рамках блока.

Примечание. В текущей версии языка M++ переменные должны объявляться по одной. При объявлении нельзя сразу присвоить значение переменной.

Пример:

string[] c;
string[] d;

7.4. Выражения

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

Результат вычисления каждого выражения имеет тип строка или вектор строк, в зависимости от применяемых в нем констант и/или переменных.

Результатом выражения является значение-строка, значение вектор или специальное значение empty. Значение empty означает отсутствие значения (т.е. пустая строка, пустой вектор). Значение empty совместимо как с типом строка, так и с типом вектор строк. Реальный тип значения empty автоматически определяется в зависимости от типа выражения.

Формат

<выражение> ::= <выражение_присваивания>
	| <условное_выражение>

<выражение_присваивания> ::= <идентификатор> = <выражение>
	| <идентификатор> += <выражение>

<условное_выражение> ::= <условное_выражение_И>
	| <условное_выражение> || <условное_выражение_И>

<условное_выражение_И> ::= <выражение_равенства>
	| <условное_выражение_И> && <выражение_равенства>

<выражение_равенства> ::= <выражение_сложения>
	| <выражение_равенства> == <выражение_сложения>
	| <выражение_равенства> != <выражение_сложения>

<выражение_сложения> ::= <унарное_выражение>
	| <выражение_сложения> + <унарное_выражение>

<унарное_выражение> ::= <первичное_выражение>
	| ! <унарное_выражение>

<первичное_выражение> ::= <идентификатор>
	| <строка>
	| <вызов_функции>
	| <выражение_вектор>
	| empty
	| ( <выражение> )

<вызов_функции> ::=  <идентификатор> ()
	| <идентификатор> ( <параметры_функции> )

<параметры_функции> ::= <выражение>
	| <параметры_функции> , <выражение>

<вражение_вектор> ::= []
	| [ <значения_выражения_вектора> ]
	;

<значения_выражения_вектора> ::= <выражение>
	| <значения_выражения_вектора> , <выражение>

Примечание. Особенностью данной грамматики является то, что правым операндом любой бинарной операции, а так же операндом унарной операции, должно быть выражение с операцией более высокого приоритета (7.5.5). Так, нельзя, чтобы правым операндом операции сложения была операция присваивания, например, d = a + b = "3". В языке M++ это вызовет синтаксическую ошибку (в языке C++ это так же ошибка, но диагностируемая как необходимость иметь l-value в левой части операции присваивания). Для преодоления этого необходимо заключать менее приоритетные операции в скобки: d = a + ( b = "3" ).

7.5. Операции

7.5.1. Операции присваивания

переменная = выражение
Присваивает переменной значение выражения. Тип выражения должен совпадать с типом переменной.
переменная += выражение
Добавляет к переменной значение выражения.
Если переменная имеет тип строка, то выражение так же должно иметь тип строка, а результатом операции является склейка (конкатенация) старого значения переменной со значением выражения.
Если переменная имеет тип вектор строк, то выражение может иметь тип строка (добавляется один элемент) или вектор строк (добавляются все элементы). Значение выражение добавляется в конец вектора. Предыдущие элементы вектора не изменяются.

7.5.2. Логические операции

! выражение
Отрицание выражения. Результатом операции отрицания является значение-строка. Если результат выражения empty, то результат операции отрицания - отличное от empty значение. Если результат выражения не empty, то результат операции отрицания - empty.
выражение1 || выражение2
Операция логического ИЛИ. Результатом операции является значение-строка (empty или отличное от empty значение). Значение <выражение2> вычисляется только, если <выражение1> имеет значение empty.
выражение1 && выражение2
Операция логического И. Результатом операции является значение-строка (empty или отличное от empty значение). Значение <выражение2> вычисляется только, если <выражение1> имеет значение, отличное от empty.

7.5.3. Операции равенства

выражение1 == выражение2
Операция равенства. Результатом операции является значение-строка: отличное от empty значение, если значение <выражения1> равно значению <выражения2> или empty, если значение <выражения1> не равно значению <выражения2>. Оба сравниваемых выражения должны иметь одинаковый тип.
выражение1 != выражение2
Операция не равенства. Результатом операции является значение-строка: отличное от empty значение, если значение <выражения1> не равно значению <выражения2> или empty, если значение <выражения1> равно значению <выражения2>. Оба сравниваемых выражения должны иметь одинаковый тип.

7.5.4. Операция сложения

выражение1 + выражение2
Операция сложения. Оба выражения является должны иметь одинаковый тип. Для значений типа строка результатом будет конкатенированная строка. Для значений типа вектор строк результатом будет вектор, содержащий все элементы <выражения1>, за которыми следуют все элементы <выражения2>.

7.5.5. Приоритеты операций и порядок вычисления выражений

Приоритеты операций в языке M++ распределены следующим образом (в порядке убывания приоритета):

() вызов функции


! отрицание

+ сложение

== равенство
!= не равенство

|| логическое ИЛИ
&& логическое И

= присваивание
+= конкатенация и присваивание

Операции присваивания (= и +=) вычисляются справа налево. Остальные операции вычисляются слева направо. Для изменения порядка вычисления элементов выражения, необходимо использовать скобки (). Например, в результате вычисления:

string a;
string b;
string c;

b = ( c = ( a = "begin" ) + " - " ) + ( a += ", end" );

Будет получено:

b: "begin - begin, end"
c: "begin -"
a: "begin, end"

7.6. Оператор if

Формат:

<оператор_if> ::= if( <выражение> ) <оператор1>
	| if( <выражение> ) <оператор1> else <оператор2>

Вычисляет значение <выражения> и, если оно отлично от empty, выполняет <оператор1>. Если значение <выражения> равно empty и используется необязательная часть else, то выполняется <оператор2>.

7.7. Оператор выражения

Формат:

<оператор_выражения> ::= <выражение> ;

Вычисляет значение <выражения>.

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

a = "a" + "b";
c += [ a, "b", "c" ];
io_print( a + "\n" );

7.8. Оператор foreach

Формат:

<оператор_foreach> ::= foreach( <идентификатор> in <выражение> ) <оператор>

Создает переменную с именем <идентификатор> и выполняет цикл по всем значениям <выражение>. На каждом проходе цикла переменная <идентификатор> получает очередное значение и выполняется <оператор>. <выражение> должно иметь тип вектор строк. Если значение <выражение> равно empty, то цикл не выполняется ни разу. После завершения цикла переменная <идентификатор> уничтожается (т.е. блоком для переменной является оператор foreach).

Оператор foreach является единственным способом работы с одиночными значениями векторов строк в языке M++.

Пример.

string[]	v;

v = [ "1", "2", [ "3", "4" ], "5" ];
// Распечатываем все элемены вектора
foreach( i in v )
	io_print( i + "\n" );

// Распечатываем все значения выражения-вектора
foreach( i in [ "1", "2" ] + [ "2", "3" ] + [ "3", "4" ] )
	io_print( i + "\n" );

Примечание. Оператор foreach выполняет цикл по значениям выражения, которое вычисляется перед началом цикла. После этого выражение заново не вычисляется. Поэтому, изменение в цикле значений переменных, входивших в выражение, не влияет на продолжительность цикла. Например, в следующем случае цикл будет выполнен пять раз, не взирая на изменение переменной vect:

string[] vect;

vect = [ "1", "2", "3", "4", "5" ];

foreach( v in vect ) {
	if( "3" == v )
		vect = [ "1", "2", "3", "4" ];
	io_print( v + "\n" );
}

7.9. Оператор halt

Формат:

<оператор_halt> ::= halt;

Завершает выполнение программы.

7.10. Пустой оператор

Формат:

<пустой_оператор> ::= ;

Ничего не выполняет. Предназначен для случаев, когда синтаксис языка требует наличие оператора, но никаких действий выполнять не требуется.

7.11. Составной оператор

Формат:

<составной_оператор> ::= {}
	| { <тело_составного_оператора> }

<тело_составного_оператора> ::= <оператор>
	| <декларация>
	| <тело_составного_оператора> <оператор>
	| <тело_составного_оператора> <декларация>

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

7.12. Препроцессор языка M++

Примечание. Препроцессор языка M++ является сильно упрощенным аналогом препроцессора языка C++. Поэтому здесь дается только краткая сводка грамматики препроцессора M++.

#include <имя_файла>
#include expression

#define ID expressionopt

#undef ID

#error expression

#warrning expression

if: if_part elif_partopt else_partopt endif_part

if_part:	#if cond_expr

elif_part:	#elif cond_expr

else_part:	#else

endif_part:	#endif

expression:	ID
	| STRING
	| expression ID
	| expression STRING

cond_expr:	primary_cond_expr
	| cond_expr || cond_expr
	| cond_expr && cond_expr

primary_cond_expr:	is_defined
	| ! is_defined
	| ( cond_expr )
	| !( cond_expr )

is_defined:	defined( ID )
Hosted by uCoz