Глава 11 Язык OCaml
1 Лексические соглашения
Пробелы
Следующие символы считаются пробелами: пробел, горизонтальная табуляция, возврат каретки, перевод строки и подача страницы. Пробелы игнорируются, но они разделяют смежные идентификаторы, литералы и ключевые слова, которые в противном случае могли бы быть восприняты как один идентификатор, литерал или ключевое слово.
Комментарии
Комментарии начинаются с двух символов (* без промежуточных пробелов и завершаются символами *) без промежуточных пробелов. Комментарии обрабатываются как пробелы. Комментарии не встречаются внутри строковых или символьных литералов. Вложенные комментарии обрабатываются корректно.
(* single line comment *)
(* multiple line comment, commenting out part of a program, and containing a
nested comment:
let f = function
| 'A'..'Z' -> "Uppercase"
(* Add other cases later... *)
*)
Идентификаторы
|
Идентификаторы — это последовательности букв, цифр, символов _ (символ нижнего подчеркивания) и ' (одиночная кавычка), начинающиеся с буквы или нижнего подчеркивания. Буквы включают в себя по меньшей мере 52 строчные и прописные буквы из набора ASCII. Текущая реализация также распознает некоторые символы из набора ISO 8859-1 (символы 192–214 и 216–222 как прописные буквы; символы 223–246 и 248–255 как строчные буквы). Эта функция устарела и должна быть избегаема для обеспечения совместимости в будущем.
Все символы в идентификаторе значимы. Текущая реализация принимает идентификаторы длиной до 16000000 символов.
Во многих местах OCaml различает идентификаторы, начинающиеся с заглавной буквы, и идентификаторы, начинающиеся со строчной буквы. Символ нижнего подчеркивания считается строчной буквой в этих целях.
Целочисленные литералы
|
Целочисленный литерал — это последовательность одной или нескольких цифр, необязательно предваряемая знаком минус. По умолчанию целочисленные литералы находятся в десятичной системе (основание 10). Следующие префиксы выбирают другую систему счисления:
| Префикс | Система счисления |
| 0x, 0X | шестнадцатеричная (основание 16) |
| 0o, 0O | восьмеричная (основание 8) |
| 0b, 0B | двоичная (основание 2) |
(Начальная 0 — это цифра ноль; O для восьмеричной системы — это буква O.) Целочисленный литерал может быть дополнен одной из букв l, L или n, чтобы указать, что этот целое имеет тип int32, int64 или nativeint соответственно, вместо значения по умолчанию int для целочисленных литералов. Интерпретация целочисленных литералов, которые выходят за пределы диапазона представимых целочисленных значений, не определена.
Для удобства и читаемости символы нижнего подчеркивания (_) принимаются (и игнорируются) внутри целочисленных литералов.
# let house_number = 37
let million = 1_000_000
let copyright = 0x00A9
let counter64bit = ref 0L;;
val house_number : int = 37
val million : int = 1000000
val copyright : int = 169
val counter64bit : int64 ref = {contents = 0L} Литералы с плавающей точкой
|
Числовые литералы с плавающей точкой в десятичном формате состоят из целой части, дробной части и показателя степени. Целая часть — это последовательность одной или нескольких цифр, необязательно с предшествующим знаком минус. Дробная часть — это десятичная точка, за которой следует ноль, одна или более цифр. Часть показателя степени — это символ e или E, за которым следует необязательный знак + или -, а затем одна или более цифр. Она интерпретируется как степень числа 10. Дробная часть или показатель степени могут быть опущены, но не оба одновременно, чтобы избежать неоднозначности с целыми литералами. Интерпретация числовых литералов с плавающей точкой, выходящих за пределы диапазона представляемых значений с плавающей точкой, не определена.
Числовые литералы с плавающей точкой в шестнадцатеричном формате обозначаются префиксом 0x или 0X. Синтаксис похож на синтаксис числовых литералов с плавающей точкой в десятичном формате, с следующими отличиями. Целая часть и дробная часть используют шестнадцатеричные цифры. Часть показателя степени начинается с символа p или P. Она записывается в десятичном формате и интерпретируется как степень числа 2.
Для удобства и читаемости в числовых литералах с плавающей точкой допускаются (и игнорируются) символы нижнего подчеркивания (_).
# let pi = 3.141_592_653_589_793_12 let small_negative = -1e-5 let machine_epsilon = 0x1p-52;; val pi : float = 3.14159265358979312 val small_negative : float = -1e-05 val machine_epsilon : float = 2.22044604925031308e-16
Литералы символов
|
Литералы символов ограничены символами ' (одинарная кавычка). Две одинарные кавычки заключают либо один символ, отличный от ' и \, либо одну из следующих последовательностей экранирования:
| Последовательность | Обозначаемый символ |
| \\ | обратная косая черта (\) |
| \" | двойная кавычка (") |
| \' | одинарная кавычка (') |
| \n | новая строка (LF) |
| \r | возврат каретки (CR) |
| \t | горизонтальная табуляция (TAB) |
| \b | ввод обратного символа (BS) |
| \space | пробел (SPC) |
| \ddd | символ с ASCII-кодом ddd в десятичном формате |
| \xhh | символ с ASCII-кодом hh в шестнадцатеричном формате |
| \oooo | символ с ASCII-кодом ooo в восьмеричном формате |
# let a = 'a' let single_quote = '\'' let copyright = '\xA9';; val a : char = 'a' val single_quote : char = '\'' val copyright : char = '\169'
Литералы строк
|
Литералы строк ограничены символами " (двойная кавычка). Две двойные кавычки заключают последовательность символов, отличных от " и \, или последовательностей экранирования из таблицы выше для литералов символов, или последовательности экранирования для символов Юникода.
Последовательность экранирования для символа Юникода заменяется кодировкой UTF-8 указанного скалярного значения Юникода. Скалярное значение Юникода, целое число в диапазонах 0x0000...0xD7FF или 0xE000...0x10FFFF, определяется с помощью от 1 до 6 шестнадцатеричных цифр; разрешаются ведущие нули.
# let greeting = "Hello, World!\n"
let superscript_plus = "\u{207A}";;
val greeting : string = "Hello, World!\n"
val superscript_plus : string = "⁺" Последовательность новой строки — это символ новой строки, необязательно предваряемый символом возврата каретки. С OCaml 5.2 последовательность новой строки в строковом литерале нормализуется в один символ новой строки.
Для разрешения разделения длинных строковых литералов по строкам последовательность \новая-строка пробелы-или-табуляции (обратная косая черта в конце строки, за которой следует любое количество пробелов и горизонтальных табуляций в начале следующей строки) игнорируется внутри строковых литералов.
# let longstr =
"Call me Ishmael. Some years ago — never mind how long \
precisely — having little or no money in my purse, and \
nothing particular to interest me on shore, I thought I\
\ would sail about a little and see the watery part of t\
he world.";;
val longstr : string =
"Call me Ishmael. Some years ago — never mind how long precisely — having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world." Экранированные новые строки обеспечивают более удобное поведение, чем неэкранированные новые строки, поскольку отступ не считается частью строкового литерала.
# let contains_unexpected_spaces =
"This multiline literal
contains three consecutive spaces."
let no_unexpected_spaces =
"This multiline literal \n\
uses a single space between all words.";;
val contains_unexpected_spaces : string =
"This multiline literal\n contains three consecutive spaces."
val no_unexpected_spaces : string =
"This multiline literal \nuses a single space between all words." Литералы строк в кавычках предоставляют альтернативный лексический синтаксис для строковых литералов. Они полезны для представления строк произвольного содержимого без экранирования. Строки в кавычках ограничены соответствующей парой { идентификатор-строки | и | идентификатор-строки } с тем же идентификатор-строки с обеих сторон. Строки в кавычках не интерпретируют никакой символ особым образом1, но требуют, чтобы последовательность | идентификатор-строки } не встречалась в самой строке. Идентификатор идентификатор-строки — это (возможно, пустая) последовательность строчных букв и нижних подчеркиваний, которые можно свободно выбирать для избежания такой проблемы.
# let quoted_greeting = {|"Hello, World!"|}
let nested = {ext|hello {|world|}|ext};;
val quoted_greeting : string = "\"Hello, World!\""
val nested : string = "hello {|world|}" Текущая реализация практически не накладывает ограничений на длину строковых литералов.
Именование меток
Чтобы избежать неоднозначностей, имена меток в выражениях не могут быть просто определены синтаксически как последовательность трёх токенов ~, идентификатор и :, и должны быть определены на лексическом уровне.
|
Имена меток бывают двух видов: метка для обычных аргументов и необяз-метка для необязательных. Они просто различаются своим первым символом, либо ~, либо ?.
Несмотря на то, что метка и необяз-метка являются лексическими сущностями в выражениях, их расширения ~ имя-метки : и ? имя-метки : будут использоваться в грамматиках для повышения читаемости. Обратите также внимание, что внутри выражений типов это расширение можно понимать буквально, то есть действительно есть 3 токена с необязательными пробелами между ними.
Символы префикса и инфикса
|
См. также следующие расширения языка: расширенные операторы, расширенные операторы индексирования и операторы связывания.
Последовательности «символов операторов», таких как <=> или !!, считываются как один токен из класса infix-symbol или prefix-symbol. Эти символы анализируются как префиксные и инфиксные операторы внутри выражений, но в остальном ведут себя как обычные идентификаторы.
Ключевые слова
Нижеприведенные идентификаторы зарезервированы как ключевые слова и не могут использоваться иначе:
and as assert asr begin class
constraint do done downto else end
exception external false for fun function
functor if in include inherit initializer
land lazy let lor lsl lsr
lxor match method mod module mutable
new nonrec object of open or
private rec sig struct then to
true try type val virtual when
while with
Следующие последовательности символов также являются ключевыми словами:
!= # & && ' ( ) * + , -
-. -> . .. .~ : :: := :> ; ;;
< <- = > >] >} ? [ [< [> [|
] _ ` { {< | |] || } ~
Обратите внимание, что следующие идентификаторы являются ключевыми словами устаревшей системы Camlp4 и следует избегать их по соображениям обратной совместимости.
parser value $ $$ $: <: << >> ??
Неясности
Лексические неоднозначности разрешаются в соответствии с правилом «наибольшего совпадения»: когда последовательность символов может быть разложена на два токена несколькими различными способами, сохраняется разложение с самым длинным первым токеном.
Директивы номера строки
|
Препроцессоры, которые генерируют исходный код OCaml, могут вставлять директивы номера строки в свой вывод, чтобы сообщения об ошибках, создаваемые компилятором, содержали номера строк и имена файлов, относящиеся к исходному файлу до препроцессинга, а не после препроцессинга. Директива номера строки начинается с начала строки, состоит из # (знак решетки), за которым следует положительное целое число (номер исходной строки), за которым следует строка символов (имя исходного файла). Директивы номера строки обрабатываются как пробелы во время лексического анализа.
- 1
- За исключением нормализации последовательностей символов новой строки в один символ новой строки, упомянутой ранее.
© 1995-2024 INRIA.
https://ocaml.org/manual/5.2/lex.html