Изменение и дополнение механизма форматного преобразования строк

Изменение и дополнение механизма форматного преобразования строк.
(см. предыдущю статью PAD replacement idea)

Устройство механизма форматного преобразования чисел в форте сделан очень удобно, но оно не всегда достаточно. Так, например, когда сталкиваешься с необходимостью распечатать число в фиксированной системе исчисления, приходится делать так:
: name ( N -- )
ВASE @ DECIMAL SWAP
0 <# # # # # #> TYPE
BASE ! ;
Что на самом деле не очень удобно. Кроме этого иногда удобно пользоваться этим механизмом, не для получения строкового представления чисел, а как-то так:
: name ( ASC # -- ASC # )
<# S" s;ldkf" HOLDS HOLDS 0.0 #> ;

К списку недостатков(или неудобностей) стандартного механизма можно добавить невозможность вывести "постороннее" число во время работы внутри <# #>, а так же отсутствие стандартного механизма для преобразования чисел после десятичной точки.

Конечно, из-за этого не обязательно что-то менять в устройстве форматного преобразования чисел. Более того, делать систему несовместимой со старой. Собственно говоря, то бы хотелось получить? В первом случае:

  : name !FixedBase <# #S #> TYPE ;

 это сделать просто:

 User !CurrentBase  \ текущая система исчисления

 \ начать форматное преобразование строки
 : <# ( D BASE -- D )
      !CurrentBase ! ... ;


 : #  ( D -- D )
0 !CurrentBase @ UM/MOD >R
!CurrentBase @ UM/MOD R>
ROT >DIGIT HOLD ;

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

  \ начать форматное преобразование строки: новый вариант.
: {# ( D BASE -- D ) !CurrentBase ! ... ; \ начать форматное преобразование строки: переопределение старого.
: <# BASE @ {# ;
  • таким образом убили сразу двух зайцев. Осталась совместимость со "старыми" исходными текстами наших программ и появилась возможность "налету" менять нашу систему исчисления.

Примеры:

         : name ( N --> ) 10 {# #S #> TYPE ;


- сравните его с первым примером с сохранением текущей базы.

Теперь перейдем ко второму примеру:
: #} ( D -- D ASC # ) ...
;

: #> #} 2SWAP 2DROP ;
тогда:
: name ( ASC # -- ASC # )
<# S" s;ldkf" HOLDS HOLDS #} ;

 по-моему, текст стал прозрачнее и чище. К тому же появляется возможность делать что-то наподобие следующего примера: 
: !PrintCell ( N --> )
0 10 {# # # #} TYPE SPACE
16 {# # # #} TYPE SPACE
32 {# # # #> TYPE ;

Едем дальше. Преобразование чисел после десятичной точки(или запятой). Сначала о формате чисел, с которыми я хотел бы работать:

HIGH(CELL).LOW(CELL) <-- TOS то есть число с фиксированной точкой упаковывается в стандартное двойное число, и ряд операций для работы с двойными числами становится сразу доступен для нас(D+ D- D2/ D2*).

    \ получить очередную цифру
    : $ ( n --> n*base C) !CurrentBase @ UM* >digit ;

и

    USER places \ число отображаемых чисел.

    \ получить все цифры после запятой
    \ Возможен вариант и без рекурсии, но он не так красив. 
: $S ( N -- )
places @
I F $ OVER
IF -1 places +! SWAP RECURSE ELSE NIP THEN
ELSE DROP RDROP
THEN HOLD ;

теперь для вывода таких чисел достаточно написать:

\ колличество отображаемых знаков после десятичной точки

        10 CONSTANT PRECISION

\ преобразовать число в строку (формат с фиксированной точкой) \ с R знаками после "." : (Pr) ( P R --> Asc # )

       places !
       TUCK DABS SWAP
       <# $S [CHAR . HOLD 0 #S ROT SIGN #> ;

: (P) ( P -- ASC # ) PRECISION (pr) ;

: P. ( P -- ) (P) TYPE ;

Слова же >DIGIT в СПФе к сожалению нет в явном виде (хотя оно не помешало бы) можно определить так:

    : >DIGIT ( N --> Char ) DUP 10 > IF 7 + THEN 48 + ;

Остается нерешенной проблема с использованием "вложенных" друг в друга <# .. #>.

Она на мой взгляд не так важна для форта, но все-же не является на мой взгляд лишней. Пример(как хотелось бы):

        : name ( N -- ASC # ) 
0 10 {# # #
DUP IF 8 ELSE 16 THEN
{# # # # #} HOLDS
# # #> ;

конечно, немножечко дутый, но, тем не менее, что-то подобное запросто может возникнуть. Решить эту часть задачи легко, создав стек строк. Реализацию см. в spf_print.f.release и примеры в PAD replacement idea - в примерах заменить <# на {# и #> на #}.

DmitryZyryanov А вот мне случайно понадобилось - и я нашёл выход. Можно относительно портабельно сохранять и восстанавливать состояние разбора формирования строки:

: Save-# ( -- addr )

        0. #>
        OVER 0= OVER 0= OR IF 2DROP 0 EXIT THEN
        DUP CELL+ ALLOCATE THROW >R
        DUP R@ !
        R@ CELL+ SWAP MOVE
        R>

;

: Restore-# ( addr -- )

        ?DUP 0= IF <# EXIT THEN
        >R
        <# R@ CELL+ R@ @ HOLDS
        R> FREE THROW

;

Oleg А чем не понравилось мое решение?

DmitryZyryanov Просто не вспомнил про него, когда оно мне внезапно понадобилось :) Подумал - а вот тут бы сохранить состояние #-разбора. Кроме того у меня нет предположений, что строка лежит в PAD или ещё где-то. Мне нужно было портабельное решение.

Valid XHTML 1.0! Valid CSS!
Page Execution took real: 1.467 seconds