октября 16, 2011

Пространства имён в DEmbro

Недавно в DEmbro появилась конструкция, которую я называл «короткое пространство имён», и я решил написать о пространствах имён в DEmbro и словарях вообще.


Обычные пространства имён

Пространства имён объявляются при помощи конструкции
namespace name ... \namespace

Все команды, объявленные внутри многоточия, будут командами пространства имён name. Например, рассмотрим код
: command ." Global command" ;
namespace X
  : command ." X command" ;
\namespace


Если теперь вызвать command, то будет напечатано "Global command". Чтобы вызвать слово из прострнства имён, нужно подключить прострнство X, просто вызвав его имя, затем вызвать команду, затем отключить пространство имён командой «^»:
X command ^

Можно между «X» и «^» вызывать несколько команд из пространства имён X, а так же глобальные команды, если они не были перекрыты внутри X. Возможно объявлять пространства имён внутри пространств имён. Вот достаточно крупный пример с комментариями:
: command1 ." Global command 1" ;
namespace X
  command1 // напечатает "Global command 1", потому что команда ещё не перекрыта
  : command1 ." X command 1" ;
  command1 // напечатает "X command 1" — теперь это перекрытая команда
  : command2 ." X command 2" ;
  namespace Y // создаём подпространство имён
    : command1 ." Y command 1" ;
    command1 // напечатает "Y command 1"
  \namespace
  command1 // напечатает "X command 1" — мы уже вне пространства имён Y
\namespace
X command1 ^ // напечатает "X command 1" — мы подключили пространство имён X
X Y command1 ^ ^ // напечатает "Y command 1" — мы подключили подпространство имён Y
X X command1 ^ ^ // напечатает "X command 1": X можно вызвать внутри, т.к. оно определено глобально
X Y command2 ^ ^ // напечатает "X command 2" — внутри Y нет команды command2
Y command1 ^ // ошибка: не объявлена команда Y


Конструкция uses

Если нужно подключить сразу много пространств имён, то можно утомиться закрывать их все командой «^»:
X Y Z W Q
  .....
^ ^ ^ ^ ^


Чтобы сделать это автоматически, можно воспользоваться конструкцией «uses ... \uses». Эта конструкция автоматически отключает все пространства, подключённые внутри многоточия. Например, предыдущий фрагмент перепишется в виде:
uses X Y Z W Q
  .....
\uses


Расширения прострнства имён

Иногда может возникнуть необходимость расширить пространство имён новыми словами вне конструкции «namespace ... \namespace». В этом случае можно воспользоваться конструкцией «extend-namespace .. \extend-namespace». Она берёт последнее подключённое пространство имён и делает так, что все команды, объявляемые внутри многоточия, будут создаваться внутри него (если это поведение не будет явно изменено другими действиями с пространствами имён):
X extend-namespace
  ...
\extend-namespace


«Короткие» пространства имён

Помимо механизма «uses» есть и другой механизм упрощения отключения пространства имён: короткие пространства имён. Единственное их отличие от обычных в том, что они ищут только следующую команду в пространстве, и не требуют закрывающего слова. Проще такое понять на примере:
: command ." Global command" ;
shortnamespace X
  : command ." X command" ;
  shortnamespace Y
    : command ." Y command" ;
  \shortnamespace
\shortnamespace
command // Global command
X command // X command
command // Global command — короткие пространства имён воздействуют только на следующую команду
X Y command // Y command
X X X X X X command // X command


Если проводить аналогии с языком C++, то поведение обычных пространств имён напоминает то, что делает слово «using», а поведение коротких пространств имён — оператор «::». Можно даже в коде на DEmbro объявлять короткие пространства имён с таким суффиксом для большей наглядности:
shortnamespace X::
  shortnamespace Y::
    : command ." Y command" ;
  \shortnamespace
\shortnamespace

X:: Y:: command


Словари, context и target стеки

Расскажу теперь о том, как это всё организовано внутри.

Первое важное понятие — словарь. Словарь — это просто список, сопоставляющей имени команды её код. Когда создаётся новое слово, оно добавляется в конец словаря. Когда ищется слово — оно ищется в словаре с конца.

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

Отдельный стек target используется для в каком-то смысле обратного действия — добавления команд. При добавлении команд DEmbro-машина берёт верхний словарь со стека target, и добавляет в него команду.

Изначально в обоих стеках лежит словарь GLOBAL (на самом деле, это не совсем правда, но пока что предполагается так).

Всю работу на DEmbro со словарями и стеками context и target можно выразить пятью основными словами:
vocabulary-new ( -- voc) создаёт новый словарь и кладёт его в стек
context< ( voc --) переносит словарь с основного стека в context
context> ( -- voc) переносит словарь из стека context в основной
target< ( voc --) переносит словарь с основного стека в target
target> ( -- voc) переносит словарь из стека target в основной


Реализация пространств имён через словари

Напоследок я покажу как реализуются все слова по работе с пространствами имён через эти пять. Реализацию вы можете посмотреть в файле core/default/voc.de. Тут я опишу немного упрощённую версию, но полностью соответствующую описанному выше в посте.

Сперва объявим вспомогательное слово vocabulary:
: vocabulary ( "name") create vocabulary-new ,
  does> ( -- voc) @ ;


Оно полезно для создания именованных словарей. Например, если теперь написать
vocabulary X
то в дальнейшем вызов команды X будет приводить к тому, что на стек будет класться словарь, созданный командой vocabulary-new внутри vocabulary.

Теперь объявим конструкцию «namespace»:
: namespace ( "name") create vocabulary-new dup context< dup target< ,
  does> @ context< ;
: \namespace context> drop target> drop ;


Команда «namespace» создаёт и кладёт словарь в стеки context и target, команда «\namespace» скидывает верхние значения этих стеков. Кроме того, команда namespace переопределяет действие создаваемого слова: после строки
namespace X
вызов X будет приводить к тому, что словарь кладётся в стек context, а не в обычный, как в случае с vocabulary.

Легко объявить закрывающую конструкцию «^». Сперва я ещё объявляю конструкцию «^^^», явлюящуюся не-immediate версией «^»:
: ^^^ context> drop ;
' ^^^ alias ^ immediate

В последней строке команда «^» объявляется как копия «^^^», только делается ещё и immediate.

Объявим конструкцию расширения пространства имён:
: extend-namespace context> dup context< target< ;
' \namespace alias \extend-namespace 

Тут всё элементарно.

Конструкция «uses» использует («задокументированную») фичу: в стеки словарей можно класть ноль:
: uses 0 context< 0 target< ;
: \uses begin context> 0= until   begin target> 0= until ;


Остались короткие пространства имён. Короткое пространство имён кладёт словарь в стек context, выполняет один шаг DEmbro-машины, после этого снимает словарь со стека. Для того, чтобы выполнить один шаг, используется команда «dembro-step»:
: shortnamespace ( "name") namespace
  does> @ context< dembro-step context> drop ;
' \namespace alias \shortnamespace

Конструкция отличается от «namespace» только поведением создаваемого ей словом.


Комментариев нет:

Отправить комментарий

Постоянные читатели

Обо мне

Моя фотография
Мой e-mail: vitek_03(at)mail(dot)ru