О, а так и вправду лучше =) Хмм… И расширение удобное — добавил себе замену и всех остальных шрифтов на нормальные, т.к. те, что используются на фрилансим/тостере в опере 12 постоянно отваливаются и выдают квадратики.
Вот-вот! И это при том, что там в среднем один-два заказа в день добавляются (разработка ПО). И за это платить 300р? Вот если бы там было как на некоторых других крупных биржах по 20-30-50 в день заказов — я еще может быть понял бы. Но вот так силой заставлять платить — ИМХО, это как-то перебор. Сильно похоже на вымогательство — поэтому не буду платить принципиально.
Но есть нюансы. Оказалось, что на горшке можно просидеть довольно неподвижно и более трех минут, а тогда придется махать руками. Это, конечно, минус. Поэтому я думаю о том, что нужен еще датчик веса — сами понимаете, для чего. Хотя это лишние провода в ванной.
Датчик на закрытие двери изнутри: например на блокиратор дверного замка или какой-то сенсор, который определяет, что дверь была закрыта изнутри. Плюс немого логики: если в ванной есть движение, то свет/музыка остаются включенными до тех пор, пока кто-то не закроет дверь снаружи / откроет изнутри и в ванной после этого не будет движения.
Отличная демонстрация того, что современные компиляторы(интерпретатор, ЯП — нужное подчеркнуть или вставить что-то другое) настолько сложны, что даже безобидный на первый взгляд код вызывает неимоверное количество ошибок, а то и вовсе вводит в ступор сам компилятор.
Да, gForth в целом довольно неплох и вполне юзабелен. Но и у него есть свои особенности, плюсы и недостатки. Один из немногих х64 фортов. Вот его репозиторий на гитхабе: github.com/forthy42/gforth — там всегда самая свежая версия. В официальных репозиториях линуксов отставание в год и более. На данный момент главное преимущество данной версии в полной поддержке стандартной либы ptheard — т.е. многопоточности. А вот с юзер-переменными там немного печальнее.
Андроидная версия все таки еще сырая, но в целом работает.
Уже пройденный этап — так неудобно. Удобнее всего с браузером взаимодействовать через веб-сокеты: использовал в одном проекте форт сокет-сервер для точных и быстрых вычислений, а GUI был на веб-ките. Вся графика на jQuery+Html, а ядро программы и основная логика — на форте (СПФ).
Кто был достаточно внимателен, то заметил что мы переопределили базовые слова двоеточие и минус. Как быть с этим? Ответ ниже.
Эмм… Зачем же так извращаться-то? А дальше-то как писать? Таки немного покритикую выбранный подход: для демонстрации тех или иных способов решения задачи в целом пример годится. Но вот в продакшене данный код бесполезен. Во-первых: совершенно ненужное переопределение стандартных и привычных слов. Во-вторых, это нужно было делать в отдельный словарь и просто переключать поиск на него в тот момент, когда это нужно. В третьих: существует куча библиотек для строковых операций в подкаталоге ~devel, в т.ч. для сложения строк/чисел, так же существуют опенсорсные либы для работы со строками (например плагины к ннкрон). Достаточно подключить и можно использовать что-то типа такого: " %DD%-%MM%-%YYYY%/%HH%:%MM%:%SS%"
В четвертых: можно использовать встроенный механизм конвертации чисел для получения строки с нужным форматированием.
Пример(код одноразовый — написал и забыл, приоритет — скорость):
Код из реального проекта для отдачи времени в заголовках браузеру по стандарту RFC 822
WINAPI: GetSystemTime Kernel32.dll
0
2 -- -Year \ Указывает текущий год
2 -- -Month \ Текущий месяц; Январь = 1, Февраль = 2, и так далее
2 -- -DayOfWeek \ Текущий день недели; Воскресенье = 0, Понедельник = 1, и так далее
2 -- -Day \ Текущий день месяца
2 -- -Hour \ Час
2 -- -Minute \ Минуты
2 -- -Second \ Секунды
2 -- -Milliseconds \ Миллисекунды
CONSTANT /SYSTEMTIME
USER-CREATE SYSTEMTIME-BUF /SYSTEMTIME USER-ALLOT
USER-CREATE WebTime[] 30 USER-ALLOT
29 CONSTANT WebTime#
CREATE WebDays S" Sun, Mon, Tue, Wed, Thu, Fri, Sat, " HERE SWAP DUP ALLOT 0 C, CMOVE
CREATE WebMonths S" Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec " HERE SWAP DUP ALLOT 0 C, CMOVE
: WebTime! \ ( addr -- ) \ Записать по адресу addr строку с текущей датой вида "Fri, 04 Jul 2008 08:42:36 GMT"
>R SYSTEMTIME-BUF GetSystemTime \ Получаем текущее системное время
DUP -DayOfWeek W@ 5 * WebDays + R@ 5 CMOVE R> 5 + >R \ и конвертируем его в формат RFC 822
DUP -Day W@ 0 <# # # #> R@ SWAP CMOVE R> 2 + >R \ Код хоть и немного жуткий, зато быстрый — а это главное
BL R> DUP 1+ >R C!
DUP -Month W@ CELLS WebMonths + R@ 4 CMOVE R> 4 + >R
DUP -Year W@ 0 <# # # # # #> R@ SWAP CMOVE R> 4 + >R
BL R> DUP 1+ >R C!
DUP -Hour W@ 0 <# # # #> R@ SWAP CMOVE R> 2 + >R
[CHAR] : R> DUP 1+ >R C!
DUP -Minute W@ 0 <# # # #> R@ SWAP CMOVE R> 2 + >R
[CHAR] : R> DUP 1+ >R C!
-Second W@ 0 <# # # #> R@ SWAP CMOVE R> 2 + >R
S" GMT" R> SWAP CMOVE
;
: GetWebTime \ ( -- a u )
WebTime[] WebTime!
WebTime[] 29
;
В пятых:: оптимальнее сначала собирать все данные для отправки браузеру в один буфер и одним вызовом передавать в сокеты, а не делать овер 9000 апи-вызовов.
В шестых: можно использовать вектор NOTFOUND для сбора строк, не являющихся словами. Т.е. создаем словарь с нужным набором слов, которые просто пишут в текущую позицию в буфере. Далее, когда нужно сделать форматированную строку, то просто переключаем словарь и записываем в вектор NOTFOUND слово, которое будет неизвестные слова складывать в буфер. Но это уже ближе к экзотическим способам решения задачи.
Под R обычно обозначает стек возвратов. И он всегда один, т.к. на этот стек сохраняется следующий адрес для перехода перед тем, как происходит вызов слова. Использовать его для временного хранения данных можно, но не желательно, т.к. неосторожное обращение с ним может все поломать. С другой стороны, его часто используют для временных данных с целью ускорения работы программы, т.к. его вершина тоже обычно представлена отдельным регистром или несколькими процессора или он реализован аппаратно (в форт процессорах, например). Кроме того, существуют еще и другие виды стеков: например стек структур управления, стеки циклов, стек для флоат чисел(в х86 он аппаратный), локальные стеки, даже бывают отдельные стеки для адресов, строк и вообще любых других типов данных.
А так, стеков можно наделать сколько душе угодно:
1024 CONSTANT NewStackSize
CREATE NewStack /NewStack NewStackSize ALLOT 0 ,
0 VALUE NewStackDepth
: -TH \ ( addr n -- addr+n*cell )
CELLS +
;
: NewStack! \ ( n -- )
NewStackDepth 1024 < IF
NewStack NewStackDepth -TH !
NewStackDepth 1+ TO NewStackDepth
ELSE
ABORT" Переполнение стека NewStack"
THEN
;
: ClearNewStack
0 TO NewStackDepth
;
: NewStack@ \ ( -- n )
NewStackDepth IF
NewStackDepth 1- TO NewStackDepth
NewStack NewStackDepth -TH @
ELSE
ABORT" Опустошение стека NewStack"
THEN
;
: NewStackGet \ ( -- n )
NewStackDepth IF
NewStack NewStackDepth 1- -TH @
ELSE
0
THEN
;
Не так. Слово forget переносит текущий указатель области кода на адрес, соответствующий указанному слову. Т.е. затираются все слова, определенные после указанного слова. Т.о. система возвращается в состояние, предшествующее определению данному слову и следующее слово будет записано по адресу «забываемого» слова. Это если в общих чертах. А если чуть углубиться: существуют форт системы с раздельным хранением кода и данных (код и строки, структуры, константы и отеделены друг от друга) и совместным (код и данные перемешаны), есть еще системы с поддержкой многопоточности и т.н. «пользовательских переменных » — переменных, которые для каждого потока разные, плюс существуют разные варианты структуры словарей. Т.о. в какой-то простой системе forget сводится к записи адреса слова в переменную указатель текущего адреса для компиляции, а в какой-то более сложной системе forget настолько сложно сделать, что лучше заняться чем-нибудь полезным и никогда об этом думать.
Да. Можно переопределить даже компилятор и вообще практически все что угодно, а при использовании хаков вообще можно сделать все захочется.
Сидишь пишешь что-то на форте — вдруг надоело или еще чего: взял написал пару символов и прям тут же пишешь на си.
В качестве наглядного примера приведу реальный кусок кода из gForth:
Скрытый текст
c-library socket
\c #include <netdb.h>
c-function gethostbyname gethostbyname a -- a ( name -- hostent )
\c #include <unistd.h>
c-function gethostname gethostname a n -- n ( c-addr u -- ior )
\c #include <errno.h>
\c #define get_errno() errno
c-function errno get_errno -- n ( -- value )
\c #include <sys/types.h>
\c #include <sys/socket.h>
c-function socket socket n n n -- n ( class type proto -- fd )
c-function closesocket close n -- n ( fd -- ior )
c-function connect connect n a n -- n ( fd sock size -- err )
c-function send send n a n n -- n ( socket buffer count flags -- size )
c-function recv recv n a n n -- n ( socket buffer count flags -- size )
c-function listen() listen n n -- n ( socket backlog -- err )
c-function bind bind n a n -- n ( socket sockaddr socklen --- err )
c-function accept() accept n a a -- n ( socket sockaddr addrlen -- fd )
\c #include <stdio.h>
c-function fdopen fdopen n a -- a ( fd fileattr -- file )
\c #include <fcntl.h>
c-function fcntl fcntl n n n -- n ( fd n1 n2 -- ior )
\c #include <arpa/inet.h>
c-function htonl htonl n -- n ( x -- x' )
c-function htons htons n -- n ( x -- x' )
c-function ntohl ntohl n -- n ( x -- x' )
\c #define fileno1(file) fileno((FILE*)(file))
c-function fileno fileno1 a -- n ( file* -- fd )
end-c-library
4 4 2Constant int%
2 2 2Constant short%
struct
cell% field h_name
cell% field h_aliases
int% field h_addrtype
int% field h_length
cell% field h_addr_list
end-struct hostent
struct
short% field family
short% field port
int% field sin_addr
cell% 2* field padding
end-struct sockaddr_in
В данном случае, си код сохраняется в отдельный файл, к нему дописывается еще один инклюд с частью кода самого gForth, он скармливается gcc и на выходе получается so библиотека, которая подключается и используется уже в форт-коде.
А вот пример хака:
: IFNOT \ ( flag -- )
?COMP \ Проверка на режим компиляции
[COMPILE] IF
0x85 HERE 5 - C! \ Хак: подменяем скомпилированную ранее инструкцию JE(0x84) на JNE(0x85)
\ Высокоуровневый вариант:
\ POSTPONE 0= [COMPILE] IF
; IMMEDIATE
Да, все верно. Логика обычного литерала в общем случае сводится к следующему: поместить в стек число из области памяти для статических данных. Обычно это следующий адрес для исполнения (да, прямо там же где и код) и пропуск данного адреса (прибавление размера ячейки) или смещение в области пользовательских данных. Говорю так размыто по причине того, что реализаций очень много и у каждого автора на все есть своё мнение =)
А положить ноль или единицу на вершину стека — это по сути одна-две процессорных команды.
: INTERPRET ( -> ) \ интерпретировать входной поток
BEGIN
PARSE-NAME DUP \ PARSE-NAME тупо перебирает символы из входного потока в поиске разделителя (пробел и меньше) и возвращает найденное слово
WHILE
SFIND \ Ищем слово в словаре/словарях
IF
STATE @ \ Проверка режима работы системы: компиляция или исполнение
IF COMPILE, ELSE EXECUTE THEN \ Компилируем слово или исполняем его
ELSE
NOTFOUND \ Зависит от реализации, но в общем случае тут обрабатываем исключение "слово не найдено", обычно тут еще бывает проверка "число?" -> компилируем числовой литерал или кладем число на стек
THEN
REPEAT
2DROP \ Выкидываем уже не нужную строку со стека
;
Датчик на закрытие двери изнутри: например на блокиратор дверного замка или какой-то сенсор, который определяет, что дверь была закрыта изнутри. Плюс немого логики: если в ванной есть движение, то свет/музыка остаются включенными до тех пор, пока кто-то не закроет дверь снаружи / откроет изнутри и в ванной после этого не будет движения.
Отличная демонстрация того, что современные компиляторы(интерпретатор, ЯП — нужное подчеркнуть или вставить что-то другое) настолько сложны, что даже безобидный на первый взгляд код вызывает неимоверное количество ошибок, а то и вовсе вводит в ступор сам компилятор.
Андроидная версия все таки еще сырая, но в целом работает.
Эмм… Зачем же так извращаться-то? А дальше-то как писать? Таки немного покритикую выбранный подход: для демонстрации тех или иных способов решения задачи в целом пример годится. Но вот в продакшене данный код бесполезен. Во-первых: совершенно ненужное переопределение стандартных и привычных слов. Во-вторых, это нужно было делать в отдельный словарь и просто переключать поиск на него в тот момент, когда это нужно. В третьих: существует куча библиотек для строковых операций в подкаталоге ~devel, в т.ч. для сложения строк/чисел, так же существуют опенсорсные либы для работы со строками (например плагины к ннкрон). Достаточно подключить и можно использовать что-то типа такого:
" %DD%-%MM%-%YYYY%/%HH%:%MM%:%SS%"
В четвертых: можно использовать встроенный механизм конвертации чисел для получения строки с нужным форматированием.
Пример(код одноразовый — написал и забыл, приоритет — скорость):
В пятых:: оптимальнее сначала собирать все данные для отправки браузеру в один буфер и одним вызовом передавать в сокеты, а не делать овер 9000 апи-вызовов.
В шестых: можно использовать вектор NOTFOUND для сбора строк, не являющихся словами. Т.е. создаем словарь с нужным набором слов, которые просто пишут в текущую позицию в буфере. Далее, когда нужно сделать форматированную строку, то просто переключаем словарь и записываем в вектор NOTFOUND слово, которое будет неизвестные слова складывать в буфер. Но это уже ближе к экзотическим способам решения задачи.
А так, стеков можно наделать сколько душе угодно:
Сидишь пишешь что-то на форте — вдруг надоело или еще чего: взял написал пару символов и прям тут же пишешь на си.
В качестве наглядного примера приведу реальный кусок кода из gForth:
В данном случае, си код сохраняется в отдельный файл, к нему дописывается еще один инклюд с частью кода самого gForth, он скармливается gcc и на выходе получается so библиотека, которая подключается и используется уже в форт-коде.
А вот пример хака:
Впрочем, слово «обычно» зачастую сложно применимо относительно того или иного аспекта в форте.
А положить ноль или единицу на вершину стека — это по сути одна-две процессорных команды.
В общем случае все сводится вот к этому:
Если же интересуют подробности, то можно заглянуть сюда (SP-Forth) или сюда (gForth), или даже вот сюда (форт форум).