Last Dutch Forth meeting we discussed a challenge in
recognizing Roman numbers.
It show off the power of the PREFIX word.
Remark:
A word marked PREFIX is found in the dictionary also
if it is immediately followed by another word.
0r (zero-r) M C CM are all prefixes. Making them also IMMEDIATE
made the Roman denotation work also in compilation
mode. It suffices to add POSTPONE LITERAL to the
denotation prefix, (that does nothing in interpret mode).
This is ciforth specific code.
--------------------------------------------------
\ $Id: paas.frt,v 1.4 2025/06/02 12:23:48 albert Exp $
\ Copyright (2012): Albert van der Horst {by GNU Public License}
\ Make an interpreter of Roman numerals.
\ 0rMCMXLVIII is like 0X7FFF000
\ The idea is to have a Roman thingy M C also CM IV
\ that add a constant 1000 100 or 900 4 to what is
\ already on the stack.
\ It is dangerous to let loose a PREFIX D , for
\ example DROP is no longer understood, so the
\ Roman thingies are tucked away in a ROMAN wordlist.
\ ERROR 1001 : The components of a Roman numeral
\ must be in descending order.
\ This detects error 1001, but this is not the subject.
: !ERR ; : ?ERR? ;
NAMESPACE ROMANS
: 0r !ERR 0 NAME ROMANS EVALUATE PREVIOUS POSTPONE LITERAL ;
PREFIX IMMEDIATE
: rdigit CREATE , PREFIX IMMEDIATE DOES> @ ?ERR? + ;
\ Define Roman thingies starting with number the ten times smaller
: _row BEGIN DUP rdigit 10 / DUP 0= UNTIL DROP ;
ROMANS DEFINITIONS
1000 _row M C X I
900 _row CM XC IX
500 _row D L V
400 _row CD XL IV
PREVIOUS DEFINITIONS
Because conflicts can be avoided, there is no need to use a prefix
like your Or, so I do not use that. Here are some examples:
MCMXLVIII . \ 1948
mcmxlviii . \ error: undefined word
MIM \ error: undefined word
L . \ 50
LLL \ error: undefined word
MCMXLVIII LXXVII + . \ 2025
And here's the code: >------------------------------------------------------------------
0
value: rdigit-value
2value: rdigit-string
constant rdigit-size
: romandigit ( u "romandigit" -- )
, parse-name save-mem 2, ;
create romandigits
\ this table contains variants with 4 repetitions, you can comment
\ them out if desired
900 romandigit CM
500 romandigit D
400 romandigit CD
400 romandigit CCCC
300 romandigit CCC
200 romandigit CC
100 romandigit C
90 romandigit XC
50 romandigit L
40 romandigit XL
40 romandigit XXXX
30 romandigit XXX
20 romandigit XX
10 romandigit X
9 romandigit IX
5 romandigit V
4 romandigit IV
4 romandigit IIII
3 romandigit III
2 romandigit II
1 romandigit I
here constant end-romandigits
: roman>n? ( c-addr u -- n f )
\ if c-addr u contains a roman numeral, f is true and n is the value,
\ otherwise f is false.
dup >r 'M' skip r> over - 1000 *
romandigits case {: d: str1 n1 rd1 :}
rd1 end-romandigits = ?of n1 str1 nip 0= endof
str1 rd1 rdigit-string string-prefix? ?of
str1 rd1 rdigit-string nip /string
n1 rd1 rdigit-value +
rd1 rdigit-size + contof
str1 n1 rd1 rdigit-size + next-case ;
: rec-roman ( c-addr u -- n translate-num | 0 )
roman>n? if ['] translate-num else drop 0 then ;
' rec-roman action-of forth-recognize >stack
0
value: rdigit-value
2value: rdigit-string
constant rdigit-size
: romandigit ( u "romandigit" -- )[...]
, parse-name save-mem 2, ;
create romandigits
\ this table contains variants with 4 repetitions, you can comment
\ them out if desired
900 romandigit CM
500 romandigit D
400 romandigit CD
400 romandigit CCCC
300 romandigit CCC
200 romandigit CC
100 romandigit C
90 romandigit XC
50 romandigit L
40 romandigit XL
40 romandigit XXXX
30 romandigit XXX
20 romandigit XX
10 romandigit X
9 romandigit IX
5 romandigit V
4 romandigit IV
4 romandigit IIII
3 romandigit III
2 romandigit II
1 romandigit I
here constant end-romandigits
: roman>n? ( c-addr u -- n f )
\ if c-addr u contains a roman numeral, f is true and n is the value,
\ otherwise f is false.
dup >r 'M' skip r> over - 1000 *
romandigits case {: d: str1 n1 rd1 :}
rd1 end-romandigits = ?of n1 str1 nip 0= endof
str1 rd1 rdigit-string string-prefix? ?of
str1 rd1 rdigit-string nip /string
n1 rd1 rdigit-value +
rd1 rdigit-size + contof
str1 n1 rd1 rdigit-size + next-case ;
For ROMAN>N? I first tried an orthodox approach with data and return
stack only, and BEGIN etc., but with 4 stack items that have to be
updated possibly at every iteration that was somewhat unwieldy, and I >produced a buggy version. Then I tried this approach with the
extended CASE and locals, and I got it right on first try, despite its
bulk. I leave it to dxf to show how much better this becomes in
orthodox Forth.
Because conflicts can be avoided, there is no need to use a prefix
like your Or, so I do not use that. Here are some examples:
- anton
On 9/06/2025 2:23 am, LIT wrote:
I prefer ITC - the processors nowadays are so fast,
that DTC/STC aren't as much advantageous, as they
used to be — and I'm frequently tinkering with that
old fig-Forth I once found on forth.org pages. It's
fun. :)
AFAICS ITC presents no impediment to finding a credible solution
for FigForth's lack of a credible CREATE.
In article <a281de112697213c8d16ce5eee1d08dbff2fb293@i2pn2.org>,
dxf <dxforth@gmail.com> wrote:
On 9/06/2025 2:23 am, LIT wrote:
I prefer ITC - the processors nowadays are so fast,
that DTC/STC aren't as much advantageous, as they
used to be — and I'm frequently tinkering with that
old fig-Forth I once found on forth.org pages. It's
fun. :)
AFAICS ITC presents no impediment to finding a credible solution
for FigForth's lack of a credible CREATE.
Remember ciforth is actually fig.forth 5.5.1.
<BUILDS DOES> is nearly as powerful as CREATE DOES>
I merely made vocabularies and <BUILDS DOES> iso standard .
I also introduced PREFIX.
(PREFIX is actually a two line addition to a properly designed Forth.)
I made more improvements but the above should be sufficient to reproduce
the example
in fig-Forth.
Groetjes Albert--
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 505 |
Nodes: | 16 (2 / 14) |
Uptime: | 51:47:39 |
Calls: | 9,921 |
Calls today: | 8 |
Files: | 13,804 |
Messages: | 6,347,797 |