Spice (язык программирования)

Материал из Национальной библиотеки им. Н. Э. Баумана
Последнее изменение этой страницы: 13:12, 8 июня 2016.
Spice
Парадигма Функциональный, выражение-ориетнитированный
Спроектировано Дэйв Регетт, Крис Доллин, Стив Лич
Портал: Spice

Spice - (произносится spaɪs) - был создан в 1998 году Дейвом Регеттом (Dave Regett), Крисом Доллином (Chris Dollin) и Стивом Личем (Steve Leach) с целью упростить программистам не самого высокого уровня обработку текстовых документов со сложной иерархией (в первую очередь файлам с метаданными, в частности XML и HTML), а также ускорить обработку таких документов. Включая в себя синтаксис, подобный Algol, и функциональную ориентированность, присущую Common Lisp, Spice является выражение-ориентированным языком. При этом язык обладает особенностями, унаследованными от обоих языков-предшественников, о которых подробно изложено ниже.

Содержание

Особенности

Автоматическое управление размещением в памяти

Программист освобождается от рутинного контроля за использованием памяти: если на объект не указывают ссылки, менеджер памяти вернёт занятые им области памяти для дальнейшего использования.

Типизация данных

Поддерживаются как статическая, так и динамическая типизация. По умолчанию, типы являются сущностями времени исполнения, и их тип определяется динамически. Впрочем, при объявлении переменных можно указать тип явно, что немаловажно для оптимизации. При привязке переменных к выражениям тип первых определяется на этапе компиляции.

Ориентированность языка

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

Уровень языка

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

Многоуправляемость

Доступна перегрузка функций для разных комбинаций типов аргументов.По сравнению со SmallTalk, Java и C++ возможности перегрузки сильно расширены.

Классово-нейтральность

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

Объявления функций и переменных

Языком поддерживается несколько типов процедур: функции, методы, конструкторы и прочие. Для оформления вызова процедуры используется синтаксис вида <codeF(X)</code>, в котором F — имя вызываемой процедуры, а X — передаваемый ей аргумент. При использовании переменных их объявление является обязательным. Строки представляют собой последовательности символов Unicode в каноническом виде. Являются неизменяемыми и могут рассматриваться как массивы с использованием стандартной нотации s[i].

Лексические средства языка

Язык обладает семантикой наподобие Algol, в частности:

  • Все слова языка состоят из букв, цифр и нижних подчёркиваний, при этом начинаясь с буквы. Регистр букв в слове учитывается. Слова имеют неограниченную длину, в отличие от си-подобных языков. Имена типов начинаются с заглавной буквы, прочие имена -- со строчной.
  • комментарии 2 видов:
# однострочный комментарий
/* начало многострочного комментария
   конец многострочного комментария */

В языке есть символы, применяемые для маркировки, а именно:

  • простые маркеры: []{},;
  • составные маркеры, включающие в себя последовательности символов из множества !@%^&*-+=|:.?/<>. Ввиду поддержки XML- и HTML-выражений не допускаются составные маркеры "</>", "</", "><".

Литералы

Числовые литералы

Целые числа и числа с плавающей запятой можно записать тем же способом, что и во всех широко известных языках программирования: в частности, числа с плавающей запятой можно представить как непосредственной записью (с разделителем ".", например 10.034), так и в стандартном виде (экспоненциальной форме записи -1.45e-3. Кроме этого, доступны также следующие представления этих литералов:

  • удобочитаемое представление, влияющее на формат вывода (1_057_048);
  • формат в произвольной позиционной системе счисления от 2 до 36 (2x1001011, 12xA43, 30xIfThenElse), при этом префикс 0x эквивалентен префиксу 16x, а в допустимые в указываемой системе счисления в качестве цифр латинские буквы допускаются в любом регистре;
  • формат с указанием специальной величины (49cm, 143miles, 2x10000bits, 0.5cubemetre), доступен только для систем счисления от 2 до 10. Символическая часть обязана быть объявлена ранее в качестве имени величины и содержать только буквы.

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

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

  • \&stuff;, где stuff -- допустимое HTML-выражение.
  • \(Expression) -- для регулярных выражений; вообще говоря, задают не единственную возможную строку.
  • Если после \ указан один из символов "[]{}|*%?", он не изменяется, а внутри регулярных выражений интерпретируется как обычный символ.
  • Использование после \ иных символов не допускается, если только будущие версии языка не определят строго спецификацию комбинации символов.

Символьные литералы обрамляются одинарными кавычками. Представляют собой набор символов, указанных внутри одинарных кавычек.

Литералы регулярных выражений

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

  • классы символов: [XYZ];
  • группировка: {S};
  • произвольное повторение: E*, понимается как итерация Клина;
  • произвольный символ: ?;
  • выбор одного из выражений: X|Y.

Символы из строки "[]{}|*%?", чтобы участвовать в регулярных выражениях в качестве символов, а не метасимволов, должны быть защищены косой чертой.

Традиционная модель регулярного выражения начинается с // и заканчивается /. Поэтому символ / также должен быть защищён для применения в регулярном выражении. Возможно, в будущем такой синтаксис будет совместим с семантикой библиотеки регулярных выражений Java. Нативная модель регулярного выражения начинается с / и заканчивается двойной кавычкой, поэтому не должна содержать незащищённых двойных кавычек.

Синтаксис высшего уровня

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

Программа

[(Spice)] (Package* | PackageBody)

Модификатор

Параметр MarkedModifier может быть public, protected или private. Синтаксис объявления примерно следующий:

    MarkedModifier
    | '[' Name [(MarkedArgument)]** ',' ']'

Здесь MarkedArgument -- литерал регулярного выражения, обрамлённый символами ().

Пакеты

Определяются следующим образом:

    MarkedModifier 'package' PackageName Facets PackageBody

Имя пакета является словом, заканчивающимся точкой. Количество точек в имени пакета зависит от его вложенности в другие пакеты более высокого уровня.

Включения

Внутри PackageBody можно объявить включения других пакетов:

    MarkedModifier 'import' OpenModifier PackageName [('from' Expr)]

Последнее значение необязательно.

Определения процедур

Полное описание включает в себя:

    'define' Modifier* [(':')] Header ProcedureBody 'enddefine'

Modifier -- это один из литералов: 'method', 'function', 'generic', 'init', 'specific'. Header -- совокупность (возможно, неполная и даже пустая) блоков ('returns' Type), ('extends' CommaExpr), ('super' Header).

Определения переменных

    MarkedModifier NameDecl

MarkedModifier принимает значения 'val' или 'var', NameDecl -- разрешённые незарезервированные имена. Если их несколько, перечисляются через запятую. Если требуется указание типа, записывают его идентификатор после имени переменной через точку с запятой.

Определения классов

    'define' Modifier 'class' Name
    # Далее перечисляем элементы класса.#
    'enddefine'

Синтаксис низшего уровня

Литералы

Строковые, символьные либо числовые.

Операции

Префиксные и инфиксные начинаются с символа '@'. Постфиксные обычно пишутся через точку, а начинаться они могут как с '@', так и с другого разрешённого символа. Присвоение выполняется оператором :=. Обращение к полю класса осуществляется оператором ->. Существуют также и обратные операторы =: и <-.

Вызовы

Вызовы в инфиксной и префиксной записи равноправны, как, например,

    f(x,y)
    x.f(y)

Лямбда-выражения

    '(' Args '=>' StatementSeq ')'

или

    'fun' Args '=>' StatementSeq 'endfun'

XML-подобные выражения

Записываются валидным XML-текстом.

Ветвления, циклы

  • Ветвления: начинаются с if или unless (что значит "if not");
    if
    BoolExpession
    then
    StatementSeq
    else
    StatementSeq
    endif

Если оператор ветвления unless, то и завершаться он будет endunless. Кроме того, допустимо использование elseif Ветвление вместо else. Вместо then допускается также писать do.

  • Циклы:
    • repeat -- с постусловием;
    • for -- с предусловием. Итерации могут выполняться как в теле цикла, так и в блоке входа. Также доступен синтаксис while.
  • Множественный выбор switch.

Стандартная библиотека

Типы данных

Названия всех типов данных начинаются с заглавной буквы. Существует специальный тип Any, с которым совместимы все остальные типы данных. С экземпляром любого класса совместим тип Object.

Числовые типы данных

Обобщённый числовой тип данных -- Number, представляет все нижеперечисленные:

  • целые: Small -- малое целое, BigInt -- большое целое.
  • с плавающей запятой: Float.
  • рациональная дробь: Ratio.
  • комплексные числа: Complex.

Простые типы данных

  • логический тип: Bool.
  • символьный тип: Char. Не интерпретируется в виде короткого целого числа, как это сделано в си-подобных языках.
  • типы даты и времени: Date, Time, DateTime.
  • процедурный тип Procedure.

Строки

  • строковый тип общего назначения String.
  • идентификатор величины Unit.
  • пути к файлам и URL: Pathname.

Контейнеры

  • Таблицы: Table, FatTable.
  • Словари: Lookup, Maplet.
  • Массивы: стандартная скобочная нотация a[i].

Стандартные процедуры и функции

Функции могут записываться как в инфиксной, так и префиксной нотации. Не всегда инфиксную нотацию следует понимать как вызов метода класса или объекта. Приведём наиболее важные с точки зрения начинающего программиста функции и процедуры языка Spice.

Обобщённая работа с данными

  • Выявление типа данных переменной: x.typeOf.
  • Вывод в понятном человеку виде в поток вывода: x.printOn( s ), где s -- поток вывода. Если аргумент -- контейнер или структура, система выведет ячейки или поля со стандартными разделителями.
    • Если поток вывода является стандартным потоком вывода (StandardOutput), то можно вызвать x.print.
    • Если поток вывода является потоком ошибок (StandardReport), то можно вызвать x.report.
    • Для вывода в строку таким же способом используется процедура x.toString.
    • для вывода в стандартный поток вывода нескольких значений используются процедуры:
      • print(x1, x2, ...) -- без перевода строки в конце;
      • println(x1, x2, ...) -- с переводом строки в конце.
  • Копирование объекта: x.copy.
  • Проверка на равенство: equal( x, y ) или x == y. Операция эквивалентности понимается в математическом смысле. Может быть перегружена, ответственность за рефлексивность, симметричность и транзитивность лежит на программисте.
  • Ёмкость экземпляра: x.length. В отличие от си-подобных языков, означает следующее:
    • 0 для атомарных объектов (сущностей простых типов);
    • количество экземпляров в контейнере (строке, массиве и т. д.);
    • для составных структур данных -- количество полей.

Математические операторы, процедуры и функции

  • Числовые операторы (все в инфиксной нотации):
    • +, -, * -- ведут себя ожидаемым образом.
    • Операторы деления:
      • Возвращающее результат с плавающей запятой: /. Если результат гарантированно целый, то он и останется таким.
      • Возвращающее результат в виде рациональной дроби: /:.
      • Целочисленное деление: частное -- div (с округлением результата к нулю), остаток -- rem.
    • Операторы сравнения: <, >, <=, >=, == , / == . Имеют обыкновенное поведение. Сравнивать вещественные числа на предмет точного равенства или неравенства опасно.
  • Некоторые функции:
    • Модуль числа (в том числе комплексного): x.abs или x.magnitude.
    • Минимум среди 2 чисел: x @min y, x @max y.
    • Логарифм левого операнда по основанию в правом: x @logToBase y.
    • Возведение левого операнда в степень правого: x @toPower y.
    • Квадратный корень: x.sqrt. Если результат не может быть вещественным, а комплексные числа недопустимы в коде, будет выброшено исключение.
    • Округление вещественных числе до целого: x.round, x.floor, x.ceiling. Поведение схоже с ожидаемым.
    • Тригонометрические и обратные тригонометрические функции: x.sin, x.cos, x.tan, x.asin, x.acos, x.atan.

Если числовые операции выполняются над числами с величинами, то и операнды, и результаты также должны быть числами с правильными величинами, например,

    # 1culon / 2second == 0.5umper.#
    # 1byte + 3byte == 4byte.#
    # 1byte + 5newton -> error.#

Контейнеры

  • Строки:
    • Длина строки s.length.
    • Перевод копии строки в нижний или верхний регистр: x.toLower, x.toUpper.
  • Массивы:
    • Размер массива a.length.
    • Создание отсортированной с помощью функции f сравнения копии: a @sortWith f. Сортировка на месте осуществляется с помощью процедуры a @sortInPlaceWith f.
    • Создание перевёрнутой копии: a.reverse. На месте эту же операцию выполняет процедура a.reverseInPlace.
  • Таблицы и словари:
    • Возврат значений, записанных в словаре: l.explode.
    • Возврат пар ключ-значение из словаря: l.maplets.
    • Проверка наличия ключа k: l @hasKey k.
    • Пара ключ-значение: key == > value.

Работа с потоками ввода-вывода

Операции ввода/вывода оперируют аргументами типов File, Pathname, Stream. В их числе:

  • s.streamFile -- сообщает, какому файлу соответствует поток s;
  • s.parsePath -- переводит строку в объект Pathname;
  • f.openedOn -- сообщает, по какому пути открыт файл, если он открыт;
  • f.close -- если файл не закрыт, он закрывается. После записи все изменения сохраняются;
  • f @readInto( b, w, l ) -- чтение из открытого файла f в байтовый буфер b со смещением w не более l байтов;
  • f @writeFrom( b, w, l ) -- запись в открытый файл f из байтового буфера b со смещением w не более l байтов.

Перспективы применения языка в разработке ПО

В настоящее время для большинства популярных языков программирования составлены нативные или бинарные библиотеки функций работы с файлами метаданных, поэтому язык Spice в современной разработке практически не применяется. Кроме того, механизм исключений реализован аналогично C++ и Java, что, по мнению Криса Доллина (см. [1], параграф 23.1, English), является не самым удачным решением применительно к стилю языка Spice.

Ссылки

  1. Chris Dollin -- Technical Report for Spice, HP Laboratories Bristol, published on October 30, 2002.
  2. Статья о языке программирования Spice на русском, по состоянию на 17.01.2014.