...

четверг, 5 июня 2014 г.

FORTH: Самоопределяющиеся слова

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

Например: x, y, z, x', y', z', x'', y'', z'', s, m и так далее…

Для их определения придется каждый раз выписывать слово VARIABLE, а это громоздко, уныло и некрасиво. Можно ли как-то сделать повеселее?

Указать, что дальше будут определены переменные и выписать их имена.

Что-то вроде:



VARIABLES:
x y z
x' y' z'
x'' y'' z''
s m
;VARIABLES




Вспомним, что интерпретатор языка Форт реализованный в SP-Forth, если не находит слово в контекстном словаре ищет в том же контекстном словаре слово NOTFOUND, которое и исполняет. Входными параметрами NOTFOUND являются адрес и счетчик подстроки из входного потока. Значит, необходимо переопределить NOTFOUND так, чтобы оно делало то, что нам нужно.


Что же нужно?

Взять это не найденное слово и скомпилировать его на вершину текущего словаря как переменную. Напомню определение




: VARIABLE CREATE 0 , ;



Но слово CREATE само выбирает из входного потока следующее слово, а нам надо создать словарную статью из строки с адресом и счетчиком на стеке. Благо на такой случай есть слово CREATED, как раз и принимающее адрес и счетчик строки со стека и создающее словарную статью. К сожалению, оно, как и NOTFOUND, не входит в стандартный ANSI-94 набор слов.

Таким образом



: NOTFOUND (addr u -- ) CREATED 0 , ;



Но, если мы поместим такое определение в базовый список FORTH, то лишимся возможности вводить числа. Значит надо спрятать этот новый NOTFOUND в какой-то иной контекст. Заведем словарь variables.



VOCABULARY variables



и сделаем его текущим.



ALSO variables DEFINITIONS



поместим туда определение NOTFOUND

вернем текущий контекст




PREVIOUS DEFINITIONS

Таким образом слово VARIABLES: переключает контекст на variables и делает доступным нужный нам NOTFOUND




: VARIABLES: ALSO variables ;



Закрывающее слово ;VARIABLES будет возвращать контекст. Находится оно должно, естественно, в контексте variables.

То-есть, итого:




VOCABULARY variables
ALSO variables DEFINITIONS

: NOTFOUND (addr u -- ) CREATED 0 , ;
: ;VARIABLES PREVIOUS ;

PREVIOUS DEFINITIONS

: VARIABLES: ALSO variables ;



Вот так, буквально в четыре строки мы расширили интерпретатор SP-Forth и упростили себе описание переменных.

Но ведь аналогичный подход можно использовать и для VALUE-переменных, и констант, и вообще любых слов с общей семантикой исполнения. Тех слов, которые определяются с помощью определяющего слова. В принципе полезно иметь пары определяющих слов. Одно для единственного определения, и парное для группового определения. Собственно, определяющее слово создается ради возможности создавать группы слов с общей семантикой. И удобно если эти определения будут не размазаны по тексту, а собраны в одном блоке.

Попробуем реализовать подобное для VALUE-переменных.




VOCABULARY values
ALSO values DEFINITIONS
: NOTFOUND ...



А вот тут мы натыкаемся на некоторую неприятность. Определяющее слово VALUE не определено через CREATE. Оно определено так:



: VALUE
HEADER
['] _CONSTANT-CODE COMPILE, ,
['] _TOVALUE-CODE COMPILE,
;



На счастье слову HEADER, берущему строку из входного потока есть пара в виде слова SHEADER, синонимичного слову CREATED.

Просто заменим одно на другое и получим необходимый вариант слова.



: VALUED ( n addr u --- )
SHEADER
['] _CONSTANT-CODE COMPILE, ,
['] _TOVALUE-CODE COMPILE,
;

Итак:




VOCABULARY values
ALSO values DEFINITIONS

: ;VALUES PREVIOUS DROP ;
: NOTFOUND VALUED 0 ;

PREVIOUS DEFINITIONS

: VALUES: ALSO values 0 ;



Но здесь присутствует один недостаток. Все VALUE инициализируются нулем. Было бы неплохо устранить это.

Вариантов реализации может быть несколько.

Можно записывать просто



VALUES:
11 aa
22 bb
33 cc
;VALUES



Это неудобочитаемо.

Попробуем писать так:




VALUES:
aa = 11
bb = 22
cc = 33
;VALUES



выглядит красиво.

Очевидно, что слово «равно» должно присутствовать в контексте values. Одно должно выбирать следующее слово и интерпретировать его как число. То-есть быть почти синонимом LITERAL. Еще «равно» должно присваивать это значение последней определенной VALUE-переменной.


Пишем




VOCABULARY values
ALSO values DEFINITIONS

: ;VALUES PREVIOUS DROP ;
: = BL WORD ?LITERAL LATEST NAME> 9 + EXECUTE ;
: NOTFOUND VALUED 0 ;

PREVIOUS DEFINITIONS

: VALUES: ALSO values 0 ;

Такой вариант




VALUES:
11 TO aa
22 TO bb
33 TO cc
;VALUES

Ценен тем, что не выпадает из парадигмы языка, кроме того позволяет инициализировать VALUE-переменные вычисляемыми значениями.



VALUES:
11 TO aa
22 1980 * TO bb
aa bb + TO cc
;VALUES



Для его реализации не потребуется переопределять NOTFOUND. Будет изменен только смысл слова TO. Между словами-ограничителями VALUES: ;VALUES TO должно действовать как обычное VALUE.



VOCABULARY values
ALSO values DEFINITIONS

: ;VALUES PREVIOUS ;
: TO VALUE ;

PREVIOUS DEFINITIONS

: VALUES: ALSO values ;

Можно сделать аналогичный способ записи и для констант.




CONSTANTS:
11 IS aa
22 1980 * IS bb
aa bb + IS cc
;CONSTANTS



Реализация этого способа, думаю, очевидна.

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

Существенно более приятным дополнением SP- SP-Forth может стать групповая реализация слова WINAPI:. В частности, в библиотеке Winctl определения WINAPI: разбросаны по всему тексту, что выглядит бардачно.

Как вариант:




WINAPIS:
LIB: USER32.DLL
PostQuitMessage
PostMessageA
SetActiveWindow
LIB: GDI32.DLL
CreateFontA
GetDeviceCaps
DeleteDC
LIB: COMCTL32.DLL
InitCommonControlsEx
;WINAPIS



Чтобы это сделать, подглядим как реализовано слово WINAPI:

spf_win_defwords.f




: __WIN: ( params "ИмяПроцедуры" "ИмяБиблиотеки" -- )
HERE >R
0 , \ address of winproc
0 , \ address of library name
0 , \ address of function name
, \ # of parameters
IS-TEMP-WL 0=
IF
HERE WINAPLINK @ , WINAPLINK ! ( связь )
THEN
HERE DUP R@ CELL+ CELL+ !
PARSE-NAME CHARS HERE SWAP DUP ALLOT MOVE 0 C, \ имя функции
HERE DUP R> CELL+ !
PARSE-NAME CHARS HERE SWAP DUP ALLOT MOVE 0 C, \ имя библиотеки
LoadLibraryA DUP 0= IF -2009 THROW THEN \ ABORT" Library not found"
GetProcAddress 0= IF -2010 THROW THEN \ ABORT" Procedure not found"
;

: WINAPI: ( "ИмяПроцедуры" "ИмяБиблиотеки" -- )
( Используется для импорта WIN32-процедур.
Полученное определение будет иметь имя "ИмяПроцедуры".
Поле address of winproc будет заполнено в момент первого
выполнения полученной словарной статьи.
Для вызова полученной "импортной" процедуры параметры
помещаются на стек данных в порядке, обратном описанному
в Си-вызове этой процедуры. Результат выполнения функции
будет положен на стек.
)
NEW-WINAPI?
IF HEADER
ELSE
-1
>IN @ HEADER >IN !
THEN
['] _WINAPI-CODE COMPILE,
__WIN:
;






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

Для того, чтобы переделать этот код под наши пожелания, определимся с тем, что будет делать каждое слово.

;WINAPIS — просто восстанавливает контекст.

LIB: — вводит из входного потока следующее слово и запоминает его во временном буфере. Можно совместить с валидацией.

Остальные слова воспринимаются как имена процедур.

Итак:


string to stack.f




SP@ VALUE spstore
: sp-save SP@ TO spstore ;
: sp-restore spstore SP! ;
: s-allot ( n bytes -- addr ) sp-save spstore SWAP - ALIGNED DUP >R CELL- CELL- SP! R> ;
: s-s ( -- addr u ) NextWord 2>R R@ s-allot DUP DUP R@ + 0! 2R> >R SWAP R@ CMOVE R> ;
: s-free spstore CELL+ SP! ;
: 3DUP 2 PICK 2 PICK 2 PICK ;






winapis.f




VOCABULARY winlibs
ALSO winlibs DEFINITIONS

: ;WINAPIS s-free PREVIOUS ;

: LIB: ( -- addr u id ) s-free s-s CR OVER LoadLibraryA DUP 0= IF -2009 THROW THEN ;

: NOTFOUND ( addr u id addr u -- addr u id )
2>R 3DUP 2R>
2DUP SHEADER
['] _WINAPI-CODE COMPILE,
HERE >R
0 , \ address of winproc
0 , \ address of library name
0 , \ address of function name
-1 , \ # of parameters
IS-TEMP-WL 0=
IF
HERE WINAPLINK @ , WINAPLINK ! ( связь )
THEN
HERE DUP R@ CELL+ CELL+ ! >R
CHARS HERE SWAP DUP ALLOT MOVE 0 C, R> \ имя функции
HERE R> CELL+ ! 2>R
CHARS HERE SWAP DUP ALLOT MOVE 0 C, 2R> \ имя библиотеки
SWAP GetProcAddress 0= IF -2010 THROW THEN \ ABORT" Procedure not found"
;

PREVIOUS DEFINITIONS

: WINAPIS: sp-save 1 2 3 ALSO winlibs ;





This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


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

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