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

Глава 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... *)
*)

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

ident ::= (letter ∣ _) { letter ∣ 0…9 ∣ _ ∣ ' }
capitalized-ident ::= (A…Z) { letter ∣ 0…9 ∣ _ ∣ ' }
lowercase-ident ::= (a…z ∣ _) { letter ∣ 0…9 ∣ _ ∣ ' }
letter ::= A…Z ∣ a…z

Идентификаторы — это последовательности букв, цифр, символов _ (символ нижнего подчеркивания) и ' (одиночная кавычка), начинающиеся с буквы или нижнего подчеркивания. Буквы включают в себя по меньшей мере 52 строчные и прописные буквы из набора ASCII. Текущая реализация также распознает некоторые символы из набора ISO 8859-1 (символы 192–214 и 216–222 как прописные буквы; символы 223–246 и 248–255 как строчные буквы). Эта функция устарела и должна быть избегаема для обеспечения совместимости в будущем.

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

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

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

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)

Литералы символов ограничены символами ' (одинарная кавычка). Две одинарные кавычки заключают либо один символ, отличный от ' и \, либо одну из следующих последовательностей экранирования:

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

Литералы строк

строка-литерал ::= " { символ-строки } "
∣ { идентификатор-строки | { новая-строка
∣ любой-символ } | идентификатор-строки }
идентификатор-строки ::= { a…z ∣ _ }
символ-строки ::= обычный-символ-строки
∣ последовательность-экранирования
∣ \u{ { 0…9 ∣ A…F ∣ a…f }+ }
∣ новая-строка
∣ \ новая-строка { пробел ∣ табуляция }

Литералы строк ограничены символами " (двойная кавычка). Две двойные кавычки заключают последовательность символов, отличных от " и \, или последовательностей экранирования из таблицы выше для литералов символов, или последовательности экранирования для символов Юникода.

Последовательность экранирования для символа Юникода заменяется кодировкой 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    $     $$    $:    <:    <<    >>    ??

Неясности

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

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

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

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

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

© 1995-2024 INRIA.
https://ocaml.org/manual/5.2/lex.html

Spec-Zone.ru

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