Nim

Материал из Национальной библиотеки им. Н. Э. Баумана
Последнее изменение этой страницы: 01:07, 24 мая 2016.
Nim
Парадигма мультипарадигмальный: императивный, объектно-ориентированный, функциональный, обобщённое программирование
Разработчики Andreas Rumpf
Первый   появившийся 2008
Стабильная версия 0.13.0 / 18 января 2016
Печать дисциплины статическая
Лицензия MIT
Портал: http://nim-lang.org/
Под влиянием
Lisp, Python, C, Object Pascal, Ada, Модула-3

Nim (или Nimrod) — язык программирования со статической типизацией, поддерживающий процедурный, объектно-ориентированный, функциональный и обобщённый стили программирования.

Язык сочетает в себе мощь Lisp, простоту и понятность Python и высокую производительность C. Особенность от Lisp: абстрактное синтаксическое дерево (AST) является частью спецификации языка, позволяющей поддерживать мощную систему макросов и обеспечивать создание предметно-ориентированных языков (DSL).

Первоначально, компилятор Nim был записан в Паскале. В 2008 была выпущена версия компилятора, написанного на Nim. Компилятор - открытый исходный код и разрабатывается группой добровольцев, дополняющих Андреаса Румпфа. Компилятор генерирует оптимизированный код C и задерживает компиляцию внешним компилятом (большой спектр компиляторов включая clang, и GCC поддерживаются), что усиливает оптимизацию и возможности мобильности. Компилятор может также генерировать C++ и код Objective C, чтобы допускать простое взаимодействие через интерфейс с APIs, что позволяет использовать Nim для написания приложения Android, а также iOS.

Описание

Nim является статически типизированным языком с простым синтаксисом. Он поддерживает функции метапрограммирования времени компиляции, такие как синтаксические макросы и макросы перезаписи термина. Макросы перезаписи термина позволяют реализациям библиотеки структур общих данных, таким как сверхбольшие числа и матрицы, быть реализованными с эффективностью, как будто они были бы встроенными средствами языка. Итераторы поддерживаются и могут использоваться в качестве объектов базового класса, как функции, и эти функции допускают функциональное программирование. Объектно-ориентированное программирование поддерживается посредcтвом наследования и многократной отправки. Функции могут быть универсальными и могут также быть перегружены, обобщения далее улучшены поддержкой классов типа. перегруженный оператор также поддерживается. Nim включает автоматическую сборку "мусора" на основе задержанного подсчета ссылок с обнаружением цикла

Определения

Программа на Nim определяет вычисление, которое действует на память, состоящую из вызванных расположений компонентов. Переменная - в основном имя для расположения. Каждая переменная и расположение имеют определенный тип. Тип переменной вызывают статическим типом, тип расположения вызывают динамическим типом. Если статический тип не совпадает с динамическим, это - супертип или подтип динамического типа.

Идентификатор - символ, объявленный как имя для переменной, типа, процедуры, и т.д. Область программы, по которой применяется объявление, вызывают объемом объявления. Объемы могут быть вложены. Значение идентификатора определено самым маленьким объемом включения, в котором объявлен идентификатор, если перегружающиеся правила разрешения не предлагают иначе.

Выражение определяет вычисление, которое производит значение или расположение. Выражения, произведенные расположениями, вызывают l-значениями. L-значение может обозначить или расположение или значение, которое расположение содержит, в зависимости от контекста. Выражения, значения которых могут быть определены статически, вызывают константными выражениями; они никогда не являются l-значениями.

Статическая ошибка - ошибка, которую реализация обнаруживает перед выполнением программы. Если ошибка явно не классифицирована, она является статической.

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

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

var a: array[0..1, char]
let i = 5
try:
  a[i] = 'N'
except IndexError:
  echo "invalid index"

Ошибка периода выполнения - непроверяемая ошибка, которая, как гарантируют, не будет обнаружена и может спровоцировать произвольные вычисления. Такие ошибки периода выполнения не произойдут, если использовать только безопасные функции языка

Лексический Анализ

Кодирование

Все исходные файлы Nim находятся в UTF-8 (или в ASCII). Другие кодировки не поддерживаются. Может использоваться любая из стандартных последовательностей завершения строки - форма Unix, используя ASCII LF (перевод строки), форма Windows, используя ASCII упорядочивает CR LF (возврат, сопровождаемый переводом строки), или старая форма Macintosh, используя ASCII CR (возврат) символ. Все эти формы могут использоваться одинаково, независимо от платформы.

Добавление отступа

Стандартная грамматика Nim описывает добавление отступа особым образом: все управляющие структуры распознаются добавлением отступа. Добавление отступа состоит только из пробелов; табуляторы не разрешены.

Обработка добавления отступа реализована следующим образом: лексический анализатор аннотирует следующий маркер предыдущим числом пробелов; добавление отступа - не отдельный маркер. Этот прием позволяет анализировать Nim только с одним маркером предвидения.

Синтаксический анализатор использует стек уровней отступа: стек состоит из целых чисел. Информация о добавлении отступа запрошена в стратегических местах в синтаксическом анализаторе, иначе игнорируется: IND псевдотерминала {>} обозначает добавление отступа, которое состоит из большего количества пробелов, чем запись наверху стека; IND {=} добавление отступа, у которого есть то же число пробелов. DED - другой псевдотерминал, который описывает действие изъятия значения из стека, IND {>} подразумевает занесение в стек.

С этой нотацией мы можем теперь легко определить ядро грамматики: блок операторов (упрощенный пример):

ifStmt = 'if' expr ':' stmt
         (IND{=} 'elif' expr ':' stmt)*
         (IND{=} 'else' ':' stmt)?

simpleStmt = ifStmt / ...

stmt = IND{>} stmt ^+ IND{=} DED  # list of statements
     / simpleStmt                 # or a simple statement

Комментарии

Комментарии могут использоваться где угодно вне последовательности или символьного литерала с символом хеша #. Комментарии состоят из связанных частей комментария. Комментарий начинается с # и работает до конца строки. Символ конца строки принадлежит комментарию. Если следующая строка состоит только из части комментария без других маркеров между ним и предыдущим, новый комментарий не начинается:

i = 0     # This is a single comment over multiple lines.
  # The scanner merges these two pieces.
  # The comment continues here.

Комментарии для документации - комментарии, которые начинаются с двух ##. Комментарии для документации - маркеры; они разрешены в определенных местах во входном файле, поскольку они принадлежат дереву синтаксиса!

Идентификаторы и Ключевые слова

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

letter ::= 'A'..'Z' | 'a'..'z' | '\x80'..'\xff'
digit ::= '0'..'9'
IDENTIFIER ::= letter ( ['_'] (letter | digit) )*

В настоящее время любой символ Unicode с порядковым номером > 127 (не ASCII) классифицирован как буква и может таким образом быть частью идентификатора, но более поздние версии языка могут присвоить некоторые символы Unicode, чтобы относиться к символам оператора.

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

addr and as asm atomic
bind block break
case cast concept const continue converter
defer discard distinct div do
elif else end enum except export
finally for from func
generic
if import in include interface is isnot iterator
let
macro method mixin mod
nil not notin
object of or out
proc ptr
raise ref return
shl shr static
template try tuple type
using
var
when while with without
xor
yield

Некоторые ключевые слова не использованы; они зарезервированы для будущих разработок языка.

Равенство идентификатора

Два идентификатора считают равными, если следующий алгоритм возвращает true:

proc sameIdentifier(a, b: string): bool =
  a[0] == b[0] and
    a.replace(re"_|–", "").toLower == b.replace(re"_|–", "").toLower

Это означает, что только первые буквы сравнены c учетом регистра . Другие буквы сравнены без учета регистра, а подчеркивания и короткое тире (точка Unicode U+2013) проигнорированы.

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

Это позволяет программистам главным образом использовать свой собственный предпочтенный стиль, будь то humpStyle, snake_style или стиль тире и библиотеки, записанные различными программистами, не будут иметь проблем несовместимости соглашений. Поддерживающий Nim редактор или IDE могут показать идентификаторы предпочтительным способом. Другое преимущество состоит в том, что это освобождает программиста от запоминания точного написания идентификатора. Исключение относительно первой буквы позволяет проанализировать однозначно такой код, как var foo: Foo.

Исторически, Nim был полностью нечувствительным к стилю языком. Это означало, что не было чувствительности к регистру, подчеркивания были проигнорированы и было не даже различие между Foo и foo.

Числовые константы

Числовые константы имеют единственный тип и имеют форму:

hexdigit = digit | 'A'..'F' | 'a'..'f'
octdigit = '0'..'7'
bindigit = '0'..'1'
HEX_LIT = '0' ('x' | 'X' ) hexdigit ( ['_'] hexdigit )*
DEC_LIT = digit ( ['_'] digit )*
OCT_LIT = '0' ('o' | 'c' | 'C') octdigit ( ['_'] octdigit )*
BIN_LIT = '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )*

INT_LIT = HEX_LIT
        | DEC_LIT
        | OCT_LIT
        | BIN_LIT

INT8_LIT = INT_LIT ['\''] ('i' | 'I') '8'
INT16_LIT = INT_LIT ['\''] ('i' | 'I') '16'
INT32_LIT = INT_LIT ['\''] ('i' | 'I') '32'
INT64_LIT = INT_LIT ['\''] ('i' | 'I') '64'

UINT_LIT = INT_LIT ['\''] ('u' | 'U')
UINT8_LIT = INT_LIT ['\''] ('u' | 'U') '8'
UINT16_LIT = INT_LIT ['\''] ('u' | 'U') '16'
UINT32_LIT = INT_LIT ['\''] ('u' | 'U') '32'
UINT64_LIT = INT_LIT ['\''] ('u' | 'U') '64'

exponent = ('e' | 'E' ) ['+' | '-'] digit ( ['_'] digit )*
FLOAT_LIT = digit (['_'] digit)* (('.' (['_'] digit)* [exponent]) |exponent)
FLOAT32_SUFFIX = ('f' | 'F') ['32']
FLOAT32_LIT = HEX_LIT '\'' FLOAT32_SUFFIX
            | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] FLOAT32_SUFFIX
FLOAT64_SUFFIX = ( ('f' | 'F') '64' ) | 'd' | 'D'
FLOAT64_LIT = HEX_LIT '\'' FLOAT64_SUFFIX
            | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] FLOAT64_SUFFIX

Как видно на практике, числовые константы могут содержать подчеркивания для удобочитаемости. Целочисленные и литералы с плавающей точкой могут быть даны в десятичном числе (нулевой префикс), двоичный файл (префикс 0b), восьмеричный (префикс 0o, или 0c) и шестнадцатеричный (префикс 0x), нотация.

Типы

У всех выражений есть тип, который известен во время компиляции. Nim имеет статический контроль типов. Можно объявить новые типы, который в сущности определяет идентификатор, который может использоваться, чтобы обозначить этот пользовательский тип. Это главные классы типа:

  • перечисляемые типы (состоят из целого числа, bool, символа, перечисление (и поддиапазоны)),
  • типы с плавающей точкой
  • строковые типы
  • структурированные типы
  • ссылка (указатель)
  • процедурный тип
  • универсальный тип

Примеры

Следующие примеры кода относятся к Nim 0.10.2. Синтаксис и семантика могут измениться в последующих версиях.

Hello world

echo "Hello World!"

Реверсивная строка

proc reverse(s: string): string =
  result = "" # implicit result variable
  for i in countdown(high(s), 0):
    result.add s[i]

var str1 = "Reverse This!"
echo "Reversed: ", reverse(str1)

Этот пример показывает многие функции Nim, один из самых экзотических - неявная result переменная: у каждой процедуры в Nim с непустым типом возврата есть неявная переменная результата, которая представляет значение, которое будет возвращено.В цикле for мы видим вызов countdown</code, который является итератором, а если итератор будет опущен, то компилятор попытается использовать <code>items итератор, если он будет определен для типа, который был определен в цикле for.

Метапрограммирование

template genType(name, fieldname: expr, fieldtype: typedesc) =
  type
    name = object
      fieldname: fieldtype

genType(Test, foo, int)

var x = Test(foo: 4566)
echo(x.foo) # 4566

This is an example of metaprogramming in Nim using its template facilities. The genType is invoked at compile-time and a Test type is created.

Обертывание C функций

proc printf(formatstr: cstring)
  {.header: "<stdio.h>", varargs.}

printf("%s %d\n", "foo", 5)

Код C может может непосредственно использоваться в Nim. В этом коде известная функция printf импортируется в Nim и впоследствии используется.

Библиотеки

Программа Нима может пользоваться любой библиотекой, которая может использоваться в программе C. Привязки к языку существуют для многих библиотек, например GTK+2, SDL2, Cairo, OpenGL, WinAPI, zlib, libzip, OpenSSL и cURL. Nim работает с PostgreSQL, базами данных MySQL и SQLite. Nim может взаимодействовать через интерфейс с интерпретатором Lua и Python. Инструмент c2nim помогает генерировать новую привязку от кода C.

Сообщество

У языка есть средство отслеживания ошибки с Wiki, размещенной на GitHub и форумом. Презентация в O'Reilly Open Source Convention (OSCON) запланирована в 2015. Сообщество О'Райли: Существенные Языки: Nim, Scala, Python. Existing C code can directly be used in Nim. In this code the well known printf function is imported into Nim and subsequently used.

Ссылки

  1. Official Nim documentation
  2. Nim on Githab