Spec-Zone.ru › OCaml
☰Язык OCaml
  • Язык OCaml
  • Расширения языка

Глава 11. Язык OCaml

1 Лексические соглашения

Кодировка исходных файлов

Предполагается, что исходные файлы OCaml представляют собой корректный текст в кодировке UTF-8. Интерпретация исходных файлов, не закодированных в UTF-8, не определена. В будущем такие исходные файлы могут быть отклонены.

Пробельные символы

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

Комментарии

Комментарии начинаются с двух символов (* без пробелов между ними и заканчиваются символами *), также без пробелов между ними. Комментарии обрабатываются как пробельные символы. Комментарии не могут находиться внутри строковых и символьных литералов. Вложенные комментарии обрабатываются корректно.

(* 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... *)
*)

Идентификаторы

ident ::= (letter ∣ _) { letter ∣ 0…9 ∣ _ ∣ ' }
capitalized-ident ::= uppercase-letter { letter ∣ 0…9 ∣ _ ∣ ' }
lowercase-ident ::= (\#)? (lowercase-letter ∣ _) { letter ∣ 0…9 ∣ _ ∣ ' }
letter ::= uppercase-letter ∣ lowercase-letter
lowercase-letter ::= a…z ∣ U+00DF … U+00F6 ∣ U+00F8 … U+00FF ∣ U+0153 ∣ U+0161 ∣ U+017E
uppercase-letter ::= A…Z ∣ U+00C0 … U+00D6 ∣ U+00D8 … U+00DE ∣ U+0152 ∣ U+0160 ∣ U+017D ∣ U+0178 ∣ U+1E9E

Идентификаторы представляют собой последовательности букв, цифр, _ (символа подчёркивания) и ' (апострофа), начинающиеся с буквы или символа подчёркивания. К буквам относятся 52 строчные и прописные буквы из набора ASCII, буквы ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ из блока Latin-1 Supplement, буквы ŠšŽžŒœŸ из блока Latin Extended-A и прописная буква ẞ (U+189E). Также поддерживается любая последовательность байтов, эквивалентная одному из этих символов Unicode в соответствии с NFC1.

Все символы идентификатора значимы. Текущая реализация допускает идентификаторы длиной до 16000000 символов.

Во многих случаях OCaml различает идентификаторы, начинающиеся с прописной буквы, и идентификаторы, начинающиеся со строчной буквы. Для этой цели символ подчёркивания считается строчной буквой.

Идентификатор, начинающийся с \#, называется необрабатываемым идентификатором (raw identifier) и эквивалентен идентификатору, полученному удалением префикса \#, за исключением того, что полученный идентификатор никогда не интерпретируется как ключевое слово (см. раздел 11.1).

Целочисленные литералы

integer-literal ::= [-] (0…9) { 0…9 ∣ _ }
∣ [-] (0x ∣ 0X) (0…9 ∣ A…F ∣ a…f) { 0…9 ∣ A…F ∣ a…f ∣ _ }
∣ [-] (0o ∣ 0O) (0…7) { 0…7 ∣ _ }
∣ [-] (0b ∣ 0B) (0…1) { 0…1 ∣ _ }
int32-literal ::= integer-literal l
int64-literal ::= integer-literal L
nativeint-literal ::= integer-literal n

Целочисленный литерал — это последовательность из одной или нескольких цифр, перед которой может стоять знак минус. По умолчанию целочисленные литералы записываются в десятичной системе счисления (основание 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}

Литералы с плавающей точкой

float-literal ::= [-] (0…9) { 0…9 ∣ _ } [. { 0…9 ∣ _ }] [(e ∣ E) [+ ∣ -] (0…9) { 0…9 ∣ _ }]
∣ [-] (0x ∣ 0X) (0…9 ∣ A…F ∣ a…f) { 0…9 ∣ A…F ∣ a…f ∣ _ } [. { 0…9 ∣ A…F ∣ a…f ∣ _ }] [(p ∣ P) [+ ∣ -] (0…9) { 0…9 ∣ _ }]

Десятичные литералы с плавающей точкой состоят из целой части, дробной части и части с показателем степени. Целая часть представляет собой последовательность из одной или нескольких цифр, перед которой может стоять знак минус. Дробная часть — это десятичная точка, за которой следует ноль или несколько цифр. Часть с показателем степени начинается с символа 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

Символьные литералы

char-literal ::= ' regular-char '
∣ ' escape-sequence '
escape-sequence ::= \ (\ ∣ " ∣ ' ∣ n ∣ t ∣ b ∣ r ∣ space)
∣ \ (0…9) (0…9) (0…9)
∣ \x (0…9 ∣ A…F ∣ a…f) (0…9 ∣ A…F ∣ a…f)
∣ \o (0…3) (0…7) (0…7)

Символьные литералы заключаются в символы ' (апострофы). Между двумя апострофами находится либо один символ, отличный от ' и \, либо одна из следующих escape-последовательностей:

Последовательность Обозначаемый символ
\\ обратная косая черта (\)
\" двойная кавычка (")
\' апостроф (')
\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'

Строковые литералы

string-literal ::= " { string-character } "
∣ { quoted-string-id | { newline
∣ any-char } | quoted-string-id }
quoted-string-id ::= { lowercase-letter ∣ _ }
string-character ::= regular-string-char
∣ escape-sequence
∣ \u{ { 0…9 ∣ A…F ∣ a…f }+ }
∣ newline
∣ \ newline { space ∣ tab }

Строковые литералы заключаются в символы " (двойные кавычки). Между двумя двойными кавычками находится последовательность символов, каждый из которых отличается от " и \, либо escape-последовательностей из приведённой выше таблицы символьных литералов, либо escape-последовательность символа Unicode.

Escape-последовательность символа Unicode заменяется на UTF-8-представление указанного скалярного значения Unicode. Скалярное значение Unicode — целое число из диапазонов 0x0000...0xD7FF или 0xE000...0x10FFFF — задаётся от одной до шести шестнадцатеричными цифрами; начальные нули допускаются.

# let greeting = "Hello, World!\n"
  let superscript_plus = "\u{207A}";;

val greeting : string = "Hello, World!\n"
val superscript_plus : string = "⁺"

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

Чтобы разбивать длинные строковые литералы на несколько строк, внутри строковых литералов игнорируется последовательность \newline spaces-or-tabs (обратная косая черта в конце строки, за которой в начале следующей строки следует любое количество пробелов и горизонтальных табуляций).

# 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."

Строковые литералы в кавычках предоставляют альтернативный лексический синтаксис для строковых литералов. Они позволяют представлять строки с произвольным содержимым без экранирования. Строки в кавычках ограничены совпадающей парой { quoted-string-id | и | quoted-string-id }, где с обеих сторон используется один и тот же quoted-string-id. В строках в кавычках ни один символ не имеет специального значения2, однако сама строка не должна содержать последовательность | quoted-string-id }. Идентификатор quoted-string-id — это (возможно, пустая) последовательность строчных букв и символов подчёркивания, которую можно выбрать произвольно, чтобы избежать такой проблемы.

# let quoted_greeting = {|"Hello, World!"|}
  let nested = {ext|hello {|world|}|ext};;

val quoted_greeting : string = "\"Hello, World!\""
val nested : string = "hello {|world|}"

Текущая реализация практически не ограничивает длину строковых литералов.

Именование меток

Чтобы избежать неоднозначности, именованные метки в выражениях нельзя определить только синтаксически как последовательность трёх токенов ~, ident и :; их необходимо определять на лексическом уровне.

label-name ::= lowercase-ident
label ::= ~ label-name :
optlabel ::= ? label-name :

Именованные метки бывают двух видов: label для обычных аргументов и optlabel для необязательных. Они различаются только первым символом: ~ или ?.

Хотя label и optlabel являются лексическими сущностями в выражениях, в грамматиках для удобства чтения используются их развёрнутые формы: ~ label-name : и ? label-name :. Также обратите внимание, что в типовых выражениях это развёртывание можно понимать буквально, т. е. оно действительно состоит из 3 токенов, между которыми могут находиться пробелы.

Префиксные и инфиксные символы

infix-symbol ::= (core-operator-char ∣ % ∣ <) { operator-char }
∣ # { operator-char }+
prefix-symbol ::= ! { operator-char }
∣ (? ∣ ~) { operator-char }+
operator-char ::= ~ ∣ ! ∣ ? ∣ core-operator-char ∣ % ∣ < ∣ : ∣ .
core-operator-char ::= $ ∣ & ∣ * ∣ + ∣ - ∣ / ∣ = ∣ > ∣ @ ∣ ^ ∣ |

См. также следующие языковые расширения: операторы расширений, расширенные операторы индексирования и связывающие операторы.

Последовательности «символов операторов», такие как <=> или !!, считываются как один токен класса infix-symbol или prefix-symbol. Эти символы разбираются как префиксные и инфиксные операторы внутри выражений, но в остальном ведут себя как обычные идентификаторы.

Ключевые слова

Приведённые ниже идентификаторы зарезервированы как ключевые слова; чтобы использовать их в другом качестве, перед ними необходимо поставить \# (см. раздел 11.1):

      and         as          assert      asr         begin       class
      constraint  do          done        downto      effect      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


Следующие последовательности символов также являются ключевыми словами (и, в отличие от указанных выше идентификаторов, всегда зарезервированы):

    !=    #     &     &&    '     (     )     *     +     ,     -
    -.    ->    .     ..    .~    :     ::    :=    :>    ;     ;;
    <     <-    =     >     >]    >}    ?     [     [<    [>    [|
    ]     _     `     {     {<    |     |]    ||    }     ~

Обратите внимание, что точный набор ключевых слов можно изменить с помощью флага компилятора командной строки -keywords (см. раздел 13.2). Наконец, обратите внимание, что следующие идентификаторы являются ключевыми словами ныне не поддерживаемой системы Camlp4, и их следует избегать для обеспечения обратной совместимости.

    parser    value    $     $$    $:    <:    <<    >>    ??

Неоднозначности

Лексические неоднозначности разрешаются по правилу «наиболее длинного совпадения»: если последовательность символов можно несколькими способами разбить на два токена, выбирается разбиение с наиболее длинным первым токеном.

Директивы номеров строк

linenum-directive ::= # { 0…9 }+ " { string-character } "

Препроцессоры, генерирующие исходный код OCaml, могут вставлять в результат директивы номеров строк, чтобы сообщения об ошибках компилятора содержали номера строк и имена файлов исходного файла до препроцессинга, а не после него. Директива номера строки начинается в начале строки и состоит из # (знака решётки), за которым следует положительное целое число (номер строки в исходном файле), а затем символьная строка (имя исходного файла). При лексическом анализе директивы номеров строк рассматриваются как пробелы.

1
Нормализационная форма C
2
За исключением упомянутого ранее преобразования последовательностей символов новой строки в один перевод строки.
Язык OCamlЗначения »
Авторское право © 2026 Institut National de Recherche en Informatique et en Automatique

© 1995-2026 INRIA.
https://ocaml.org/manual/5.5/lex.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API