Лексическая структура
Документы
Документ M представляет собой упорядоченную последовательностью символов Юникода. M допускает использование различных классов символов Юникода в разных частях документа M. Сведения о классах символов Юникода см. в разделе Стандарт Юникод, версия 3.0, подраздел 4.5.
Документ состоит либо только из одного выражения, либо из групп определений, упорядоченных в разделы. Разделы подробно описаны в Главе 10. По существу, для считывания выражения из документа используются следующие шаги.
Документ декодируется в соответствии со схемой кодировки символов в последовательность символов Юникода.
Затем выполняется лексический анализ, в результате которого поток символов Юникода преобразуется в поток токенов. В оставшихся подразделах этого раздела рассматривается лексический анализ.
После выполняется синтаксический анализ, целью которого является преобразование потока токенов в форму, которую можно вычислить. Этот процесс рассматривается в последующих разделах.
Грамматические соглашения
Лексическая и синтаксическая грамматика представлены с помощью грамматических правил. Каждое грамматическое правило определяет неконечный символ и возможные расширения этого неконечного символа в последовательности неконечных или конечных символов. В грамматических правилах неконечные символы обозначаются курсивом, а конечные символы — шрифтом с фиксированной шириной.
Первая строка грамматического правила — это имя определяемого неконечного символа, за которым следует двоеточие. Каждая последующая строка с отступом содержит возможное расширение неконечного символа, представленная в виде последовательности неконечных или конечных символов. Например, следующее правило:
выражение-if:
if
условие-ifthen
выражение-trueelse
выражение-false
определяет, что выражение-if состоит из токена if
, за которым следуют условие-if, токен then
, выражение-true, токен else
и выражение-false.
При наличии более одного возможного расширения неконечного символа альтернативные варианты перечисляются в разных строках. Например, следующее правило:
список переменных:
Переменная
список-переменных,
переменная
определяет, что список-переменных состоит либо из переменной, либо из списка-переменных, за которым следует переменная. Иными словами, определение является рекурсивным и указывает, что список переменных состоит из одной или нескольких переменных, разделенных запятыми.
Суффикс с индексом "необязательно" используется для указания необязательного символа. Правило:
спецификация-поля:
optional
необязательно имя-поля=
тип-поля
является сокращенным вариантом следующего:
спецификация-поля:
имя-поля=
тип-поля
optional
имя-поля=
тип-поля
Это правило определяет, что спецификация-поля при необходимости может начинаться с конечного символа optional
, за которым следуют имя-поля, конечный символ =
и тип-поля.
Альтернативные варианты обычно перечисляются в отдельных строках, но в тех случаях, когда существует много альтернатив, списку расширений, заданных в одной строке, может предшествовать фраза "один из". Это простое сокращение для перечисления всех альтернативных вариантов в отдельной строке. Например, следующее правило:
десятичный-знак: один из
0 1 2 3 4 5 6 7 8 9
является сокращенным вариантом следующего:
десятичный-знак:
0
1
2
3
4
5
6
7
8
9
Лексический анализ
Правило лексическая-единица в производстве определяет лексическую грамматику для документа M. Каждый допустимый документ M следует этой грамматике.
лексическая-единица:
лексические-элементынеобязательно
лексические-элементы:
лексический-элемент
лексический-элемент
лексические-элементы
лексический-элемент:
whitespace
комментарий токена
На лексическом уровне документ M состоит из потока элементов пробел, комментарий и токен. Каждое из этих правил описываются в следующих разделах. В синтаксической грамматике учитываются только элементы токен.
Пробел
Пробелы используются для разделения комментариев и токенов в документе M. Пробелы включают символ пробела (который является частью класса Zs Юникода), а также горизонтальную и вертикальную табуляции, перевод страницы и последовательности символов новой строки. Последовательности символов новой строки включают возврат каретки, перевод строки, возврат каретки, за которым следует символ перевода строки, следующей строки и разделителя абзацев.
пробел:
Любой символ с классом Zs Юникода
Символ горизонтальной табуляции (U+0009
)
Символ вертикальной табуляции (U+000B
)
Символ перевода страницы (U+000C
)
Символ возврата каретки (U+000D
), за которым следует символ перевода строки (U+000A
)
символ-новой-строки
символ-новой-строки:
Символ возврата каретки (U+000D
)
Символ перевода строки (U+000A
)
Символ следующей строки (U+0085
)
Символ разделителя строк (U+2028
)
Символ разделителя абзацев (U+2029
)
Для обеспечения совместимости со средствами редактирования исходного кода, которые добавляют маркеры конца файла и позволяют просматривать документ в виде последовательности правильно завершенных строк, к документу M применяются следующие преобразования (по порядку).
Если последний символ документа является символом сочетания клавиш CTRL+Z (
U+001A
), этот символ удаляется.Если этот документ не является пустым и если последний символ документа не является символом возврата каретки (
U+000D
), символом перевода строки (U+000A
), разделителем строк (U+2028
) или разделителем абзацев (U+2029
), то в конец документа добавляется символ возврата каретки (U+000D
).
Комментарии
Поддерживаются две формы комментариев: однострочные комментарии и комментарии с разделителями. Однострочные комментарии начинаются с символов //
и дополняются до конца исходной строки. Комментарии с разделителями начинаются с символов /*
и заканчиваются символами */
.
Комментарии с разделителями могут включать несколько строк.
комментарий:
однострочный-комментарий
комментарий-с-разделителями
однострочный-комментарий:
//
символы-однострочного-комментариянеобязательно
символы-однострочного-комментария:
символ-однострочного-комментария символы-однострочного-комментариянеобязательно
символ-однострочного-комментария:
Любой символ Юникода, кроме символа-новой-строки
комментарий-с-разделителями:
/*
текст-комментария-с-разделителяминеобязательно звездочки/
текст-комментария-с-разделителями:
раздел-комментария-с-разделителями текст-комментария-с-разделителяминеобязательно раздел-комментария-с-разделителями:
/
звездочкинеобязательно не-косая-черта-или-звездочка
звездочки:
*
звездочкинеобязательно
не-косая-черта-или-звездочка:
Любой символ Юникода, кроме *
или /
Комментарии не предусматривают вложения. Последовательности символов /*
и */
не имеют специального значения в однострочном комментарии, а последовательности символов //
и /*
не имеют специального значения в комментариях с разделителями.
Комментарии не обрабатываются в текстовых литералах. Пример
/* Hello, world
*/
"Hello, world"
содержит комментарий с разделителями.
Пример
// Hello, world
//
"Hello, world" // This is an example of a text literal
содержит несколько однострочных комментариев.
Токены
Токен — это идентификатор, ключевое слово, литерал, оператор или знак препинания. Пробелы и комментарии используются для разделения токенов, но не считаются токенами.
токен:
идентификатор
ключевое слово
литерал
оператор-или-знак-препинания
Escape-последовательности
Текстовые значения M могут содержать произвольные символы Юникода. Однако текстовые литералы ограничены графическими символами и требуют использования escape-последовательностей для неграфических символов. Например, чтобы включить в текстовый литерал символ возврата каретки, перевода строки или табуляции, можно использовать escape-последовательности #(cr)
, #(lf)
и #(tab)
соответственно. Чтобы внедрить символы начала escape-последовательности #(
в текстовый литерал, необходимо экранировать сам символ #
.
#(#)(
Escape-последовательности также могут содержать короткие (четыре шестнадцатеричные цифры) или длинные (восемь шестнадцатеричных цифр) значения кодовых точек Юникода. Таким образом, следующие три escape-последовательности эквивалентны:
#(000D) // short Unicode hexadecimal value
#(0000000D) // long Unicode hexadecimal value
#(cr) // compact escape shorthand for carriage return
В одну escape-последовательность можно добавить несколько escape-кодов, разделенных запятыми; таким образом, следующие две последовательности эквивалентны:
#(cr,lf)
#(cr)#(lf)
Ниже описан стандартный механизм экранирования символов в документе M.
escape-последовательность:
#(
список-escape-последовательностей)
список-escape-последовательностей:
одиночная-escape-последовательность
одиночная-escape-последовательность,
список-escape-последовательностей
одиночная-escape-последовательность:
длинная-управляющая-последовательность-Юникода
короткая-управляющая-последовательность-Юникода
escape-последовательность-с-управляющим-символом
escape-escape
длинная-управляющая-последовательность-Юникода:
шестнадцатеричный-знак шестнадцатеричный-знак шестнадцатеричный-знак шестнадцатеричный-знак шестнадцатеричный-знак шестнадцатеричный-знак шестнадцатеричный-знак шестнадцатеричный-знак
короткая-управляющая-последовательность-Юникода:
шестнадцатеричный-знак шестнадцатеричный-знак шестнадцатеричный-знак шестнадцатеричный-знак
escape-последовательность-с-управляющим-символом:
управляющий-символ
управляющий-символ:
cr
lf
tab
escape-escape:
#
Литералы
Литерал является представлением исходного кода значения.
литерал:
логический-литерал
числовой-литерал
текстовый-литерал
литерал-NULL
буквальный-литерал
Литералы NULL
Литерал NULL используется для записи значения null
. Значение null
представляет отсутствующее значение.
литерал-NULL:
null
Логические литералы
Логический литерал используется для записи значений true
и false
и создает логическое значение.
логический-литерал:
true
false
Числовые литералы
Числовой литерал используется для записи числового значения и дает числовое значение.
числовой-литерал:
десятичный-числовой-литерал
шестнадцатеричный-числовой-литерал
десятичный-числовой-литерал:
десятичные-знаки.
десятичные-знаки показатель-степенинеобязательно
.
десятичные-знаки показатель-степенинеобязательно
десятичные-знаки показатель-степенинеобязательно
десятичные-знаки:
десятичный-знак десятичные-знакинеобязательно
десятичный-знак: один из
0 1 2 3 4 5 6 7 8 9
экспоненциальная-часть:
e
знакнеобязательно десятичные-знаки
E
знакнеобязательно десятичные-знаки
знак: один из
+ -
шестнадцатеричный-числовой-литерал:
0x
Шестнадцатеричные знаки
0X
Шестнадцатеричные знаки
шестнадцатеричные-знаки:
шестнадцатеричный-знак шестнадцатеричные-знакинеобязательно
шестнадцатеричный-знак: один из
0 1 2 3 4 5 6 7 8 9 A B C D E F a b c d e f
Число может быть представлено в шестнадцатеричном формате (шестнадцатеричные-знаки перед символами 0x
). Например:
0xff // 255
Обратите внимание, что если в числовой литерал включена десятичная запятая, то после нее должна содержаться хотя бы одна цифра. Например, 1.3
является числовым литералом, но 1.
и 1.e3
не являются таковыми.
Текстовые литералы
Текстовый литерал используется для записи последовательности символов Юникода и создает текстовое значение.
текстовый-литерал:
"
символы-текстового-литераланеобязательно"
символы-текстового-литерала:
символ-текстового-литерала символы-текстового-литераланеобязательно
символ-текстового-литерала:
одиночный-текстовый-символ
escape-последовательность-символов
escape-последовательность-двойных-кавычек
одиночный-текстовый-символ:
Любой символ, кроме "
(U+0022
) или #
(U+0023
), за которым следует (
(U+0028
)
escape-последовательность-двойных-кавычек:
""
(U+0022
, U+0022
)
Чтобы включить кавычки в текстовое значение, знак кавычек повторяется следующим образом:
"The ""quoted"" text" // The "quoted" text
Правило символ-escape-последовательности можно использовать для записи символов в текстовые значения без необходимости непосредственно кодировать их как символы Юникода в документе. Например, символ возврата каретки и перевода строки можно записать в текстовое значение следующим образом:
"Hello world#(cr,lf)"
Буквальные литералы
Буквальный литерал используется для хранения последовательности символов Юникода, которые введены пользователем как код, но не могут быть обработаны должным образом в виде кода. Во время выполнения он выдает значение ошибки.
буквальный-литерал:
#!"
символы-текстового-литераланеобязательно"
Идентификаторы
Идентификатор — это имя, используемое для ссылки на значение. Идентификаторы могут быть либо обычными идентификаторами, либо нестандартными идентификаторами.
идентификатор:
обычный-идентификатор
нестандартный-идентификатор
обычный-идентификатор:
доступный-идентификатор
доступный-идентификатор символ-точки обычный-идентификатор
доступный-идентификатор:
ключевое-слово-или-идентификатор, не являющееся ключевым словом
ключевое-слово-или-идентификатор:
начальный-символ-идентификатора символы-части-идентификаторанеобязательно
начальный-символ-идентификатора:
буквенный-символ
символ-подчеркивания
символы-части-идентификатора:
символ-части-идентификатора символы-части-идентификаторанеобязательно
символ-части-идентификатора:
буквенный-символ
символ-десятичного-знака
символ-подчеркивания
символ-соединения
символ-объединения
символ-форматирования
символ-точки:
.
(U+002E
)
символ-подчеркивания:
_
(U+005F
)
буква:
Символ Юникода класса Lu, Ll, Lt, Lm, Lo или Nl
символ-объединения:
Символ Юникода класса Mn или Mc
десятичный-знак:
Символ Юникода класса Nd
символ-соединения:
Символ Юникода класса Pc
символ-форматирования:
Символ Юникода класса Cf
Нестандартный идентификатор можно использовать, чтобы разрешить использование любой последовательности из нулей или других символов Юникода в качестве идентификатора, включая ключевые слова, пробелы, комментарии, операторы и знаки препинания.
нестандартный-идентификатор:
#"
символы-текстового-литераланеобязательно"
Обратите внимание, что в нестандартном идентификаторе можно использовать escape-последовательности и двойные кавычки для экранирования так же, как в текстовых литералах.
В следующем примере используется идентификатор, указывающий на имя, содержащее символ пробела:
[
#"1998 Sales" = 1000,
#"1999 Sales" = 1100,
#"Total Sales" = #"1998 Sales" + #"1999 Sales"
]
В следующем примере используется идентификатор, указывающий на включение оператора +
в идентификатор:
[
#"A + B" = A + B,
A = 1,
B = 2
]
Обобщенные идентификаторы
В M есть два места, где в идентификаторах, содержащих пробелы, ключевые слова или числовые литералы, нет неоднозначностей. Это имена полей записей в литерале записи и в операторе доступа к полю ([ ]
). M позволяет использовать такие идентификаторы без необходимости использования нестандартных идентификаторов.
[
Data = [ Base Line = 100, Rate = 1.8 ],
Progression = Data[Base Line] * Data[Rate]
]
Идентификаторы, используемые для именования полей и доступа к ним, называются обобщенными идентификаторами и определяются следующим образом:
обобщенный-идентификатор:
часть-обобщенного-идентификатора
обобщенный-идентификатор разделяется только пробелами (U+0020
)
часть-обобщенного-идентификатора
часть-обобщенного-идентификатора:
сегмент-обобщенного-идентификатора
символ-десятичного-знака сегмент-обобщенного-идентификатора
сегмент-обобщенного-идентификатора:
ключевое-слово-или-идентификатор
ключевое-слово-или-идентификатор символ-точки ключевое-слово-или-идентификатор
Ключевые слова
Ключевое слово является последовательностью символов, аналогичных идентификатору, которая зарезервирована и не может использоваться в качестве идентификатора, кроме случаев, когда используется нестандартный идентификатор или разрешено использование обобщенного идентификатора.
ключевое-слово: одно из
and as each else error false if in is let meta not null or otherwise
section shared then true try type #binary #date #datetime
#datetimezone #duration #infinity #nan #sections #shared #table #time
Операторы и знаки препинания
Существует несколько видов операторов и знаков препинания. Операторы используются в выражениях для описания операций, включающих один или несколько операндов. Например, выражение a + b
использует оператор +
для добавления двух операндов a
и b
. Знаки препинания предназначены для группирования и разделения.
оператор-или-знак-препинания: один из
, ; = < <= > >= <> + - * / & ( ) [ ] { } @ ! ? ?? => .. ...