Roman numeral printer

50 views
Skip to first unread message

David Meyer

unread,
Jul 2, 2019, 9:39:30 AM7/2/19
to 4tH-compiler
I've been using Roman numerals as a step beyond hello programs in learning programming languages. 4tH comes with a sample program by Leo Brodie, but I've come up with the following program that uses 4tH's ," string compiler word to make a nice little routine. I think it's a demonstration of how lookup tables can be used to embody logic in data structures instead of code.


( romnma - Roman numerals from symbol-value table )

[needs lib\enter.4th]

12 constant maxsym

create symbol
," M" ," CM" ," D" ," CD"
             
," C" ," XC" ," L" ," XL"
             
," X" ," IX" ," V" ," IV" ," I"

create symval
1000 ,  900 , 500 ,  400 ,
               
100 ,   90 ,  50 ,   40 ,
               
10 ,    9 ,   5 ,    4 ,   1 ,

: roman ( n -- )
 
1 max 3999 min
  maxsym
1+ 0 do
   
begin dup symval i th @c >= while
      symbol i th
@c count type
      symval i th
@c -
    repeat
  loop
  drop
;

: prompt ( -- )
 
." Enter number:  " enter
 
." Roman numeral: " roman cr
;

prompt





The Beez

unread,
Jul 2, 2019, 4:09:12 PM7/2/19
to 4tH-compiler
Hi David!

That's some pretty clean code, I must say! How long have you been developing for Forth?

BTW, there was some pretty hefty comment when I implemented some heavy support for "lookup tables" in the Forth community. "We don't need no stinking tables". It was Wil Baden who saw the benefit of this and he wrote the code to emulate it in "ordinary" Forth. We still use it. I also co-wrote an article on lookup tables for "Forth Dimensions" in the old days.

I pretty much like table-oriented programming. As you say, you let the data decide - not the code. The entire 4tH compiler is essentially built on three tables. uBasic also features a lot of tables. PP4TH (the preprocessor, not the program) is also built on tables. These programs are a breeze to maintain.

BTW, talking of maintaining..

Today I needed a (written in 4tH) PHP generator I hadn't touched since 2011. But when I used it all it produced was a *LOT* of garbage. Ok, I thought. Maintained, compiles fine, but unused for 8-some years - this thing is fragging dead. Note when it was built we didn't have structures yet, so everything was blindly poked into plain buffers. I was on the verge of ripping the code out and building it again, when I half-heartedly tried to debug it. I found the error:


-  else ." drop Datatype '" type ." ' not supported" cr abort
+  else drop ." Datatype '" type ." ' not supported" cr abort

A misplaced quote that masked a DROP. Darn. After that, it ran just fine. Lesson: murky code, no matter how well made, is hard to understand and change after so many years. Better use libs - when available - and (after the great Herman Brood) "never be clever". 

Hans Bezemer

David Meyer

unread,
Jul 2, 2019, 6:19:47 PM7/2/19
to 4tH-compiler


On Wednesday, July 3, 2019 at 5:09:12 AM UTC+9, The Beez wrote:

That's some pretty clean code, I must say! How long have you been developing for Forth?

Thank you! I am pleased that the code came out so short, straightforward and understandable (hopefully to others besides myself), and doesn't require any arithmetic harder than subtraction.

How long I've been playing with Forth is a good question. The oldest source files I can find are dated 2010, but one of them contains a comment mentioning that I'd written another Forth program almost five years earlier. I can't think of any Forth coding I might have done before that, so that would make it thirteen or fourteen years. It has been an on-again-off-again love affair.

I have always enjoyed Forth's concision, economy, and RPN exoticism, but having done most of my earlier hobby programming in Perl or Bourne shell, Forth's even-less-than-C support for stings was a frustration. Over time I have been taming my string addiction, but I think 4tH's approach of adding support for C (ASCIIZ) strings is an elegant middle-way solution that avoids excessive resource consumption while providing the programmer with a lot of utility. I can see how the innovation might have been controversial in the Forth community, but I'm glad you stuck with it. I think the experiment was a success.

Today I needed a (written in 4tH) PHP generator I hadn't touched since 2011. But when I used it all it produced was a *LOT* of garbage. Ok, I thought. Maintained, compiles fine, but unused for 8-some years - this thing is fragging dead. Note when it was built we didn't have structures yet, so everything was blindly poked into plain buffers. I was on the verge of ripping the code out and building it again, when I half-heartedly tried to debug it. I found the error:


-  else ." drop Datatype '" type ." ' not supported" cr abort
+  else drop ." Datatype '" type ." ' not supported" cr abort

A misplaced quote that masked a DROP. Darn. After that, it ran just fine. Lesson: murky code, no matter how well made, is hard to understand and change after so many years. Better use libs - when available - and (after the great Herman Brood) "never be clever". 

"Even Homer sometimes nods." ;)

As an example of how NOT to program in Forth, here's the hot mess of a Roman numeral implementation I did a year ago using Gforth. I don't even want to try reading through this. ;)



\ numeral9 - superstring and accessor/printer for roman numerals "9";
\            u = power of 10 [0, 2]
: "numeral9" c" IXXCCM" ;
: numeral9 ( u -- ) 2 * "numeral9" 1+ + 2 type ;

\ numeral5 - superstring and accessor/printer for roman numerals "5";
\            u = power of 10 [0, 2]
: "numeral5" c" VLD" ;
: numeral5 ( u -- ) "numeral5" 1+ + 1 type ;

\ numeral4 - superstring and accessor/printer for roman numerals "4";
\            u = power of 10 [0, 2]
: "numeral4" c" IVXLCD" ;
: numeral4 ( u -- ) 2 * "numeral4" 1+ + 2 type ;

\ numeral1 - superstring and accessor/printer for roman numerals "1";
\            u = power of 10 [0, 3]
: "numeral1" c" IXCM" ;
: numeral1 ( u -- ) "numeral1" 1+ + 1 type ;

\ pvnumeral - type roman numeral for given power of 10 [0, 3] and place value
\             [1, 9]
: pvnumeral ( upow uval -- )
    dup
9 = if
        drop numeral9
   
else
        dup
4 = if
            drop numeral4
       
else
            dup
4 > if
                over numeral5
               
5 -
           
then
           
0 u+do dup numeral1 loop
            drop
       
then
   
then
;

\ romnumeral - print Roman numeral for given number [1, 3999]
: romnumeral ( u -- )
   
-1 3 -do
       
10 i u** /mod                        ( u%10^i u/10^i )
        dup
if
            i swap pvnumeral
       
else drop
       
then
   
1 -loop
    drop
;
\ range check
: romnumeral ( u -- )
    dup
0>
    over
4000 <
   
and if
        romnumeral
   
else
       
." ERROR romnumeral: argument out of range: " .
   
then
;


 

thebeez

unread,
Jul 3, 2019, 5:13:22 AM7/3/19
to 4th-co...@googlegroups.com
On 2019-07-03 00:19, David Meyer wrote:
> It has been an on-again-off-again love affair.
Tell me about it! I've been abandoning Forth for close to 10 years!
Until I thought Ï think I can do better.."

> I have always enjoyed Forth's concision, economy, and RPN exoticism,
> but
> having done most of my earlier hobby programming in Perl or Bourne
> shell,
> Forth's even-less-than-C support for stings was a frustration. Over
> time I
> have been taming my string addiction, but I think 4tH's approach of
> adding
> support for C (ASCIIZ) strings is an elegant middle-way solution that
> avoids excessive resource consumption while providing the programmer
> with a
> lot of utility. I can see how the innovation might have been
> controversial
> in the Forth community, but I'm glad you stuck with it. I think the
> experiment was a success.
Note that the real breakthrough came with 4tH v3.5 when I adopted
ANS-Forth's convention of addr/count. Most of 4tH works that way (that's
why WORD is no longer supported). The only place you have to use COUNT
is when you get the contents of a string variable or after using @C. I
could have prevented the latter, but I don't like words that return one
bunch of items one time and another bunch of items the other time (like
?DUP - also unsupported in 4tH).

> As an example of how NOT to program in Forth, here's the hot mess of a
> Roman numeral implementation I did a year ago using Gforth. I don't
> even
> want to try reading through this. ;)

You know, I don't even think that this is your fault. It's the LANGUAGE
that doesn't support the construct you need to solve this elegantly. And
I don't mean you have to add entire abstraction layers like Python, but
a simple "I wanna store a string constant and get it back without
breaking my back". You haven't even discovered my implementation of
compiling character constants and getting stuff back. INFO.4TH (my
version of 'man') is a nice example.
BTW, I don't think 4tH's string support is that bad. A lot of my work
concerns data manipulation and conversion, so I've got a whole slew of
string functions. I think the compiler itself already covers all the
basics like S", S|, COUNT, PLACE, ," , ,"" , +PLACE, NUMBER (much easier
than >NUMBER). Then you got the usual suspects like COMPARE, ICOMPARE,
SEARCH and then a whole bunch of libs like PARSING.4TH, BREAKQ.4TH,
ISTYPE.4TH upto entire regular expression libs - not to mention the
classic COMUS words.

You know, because I use it for work, every time I find something
lacking, I build it and add it to the libs. I started writing utilities
for work, because - you know - it was fun to use my compiler. Nowadays I
use it, because I can outperfom almost any othe programming language.

Once there was a guy at work who had to integrate a dozen dumps every
month, make error reports and resolve inconsistencies. That took an
entire day and didn't work out so well, So I told my boss "Lemme work on
this for a week". In a week I had written a piece of work that
recognized the different dumps (all had a different layout), selected
the proper loading routine, analyzed the stuff, wrote error reports in
Excel, fixed any inconsistencies by a set of predefined rules, was able
to read any date format used (including raw Excel) and dumped out a CSV,
ready to be loaded in the database.

Well, they didn't like 4tH - who was gonna maintain it when they fired
me (4tH is always good for job security) - so they took my (detailed)
specs and gave 'em to a C programmer - assuming he could do the same job
in a week. The estimate came out: 3 MONTHS!!

So it's untrue that 4tH has a low productivity - quite the contrary in
my experience. Another story. There was a database with THOUSANDS of
names that had to be matched with the HR database. Unfortunately, they
had used no standard whatsoever to enter them so you would find
"Bezemer, Hans", "Hans Bezemer", "Bezemer J.L.", "JL Bezemer",
Bezemer*Hans", etc. etc. etc.

So they estimated they needed three guys for three months to clean that
up. I wrote a simple program in a DAY that analyzed them and rewrite
them in separate fields, like "First name", "Last name", "Initials". It
fixed about 80% of the entries in less than 0.1s I've used it a lot of
times ever since.

The trick was dead easy: anything not alphabetic is a delimiter. I
connected a DB with several thousand first names, "Tussenvoegsels"
(https://en.wikipedia.org/wiki/Tussenvoegsel - which is a typical Dutch
phenomena to complicate things). Any 3 characters that were not a first
name were initials. Whatever was left had to be the last name.

So, I think it's a nice workhorse - and yes, it does strings pretty
nicely (IMHO) ;-)

Hans Bezemer

David Meyer

unread,
Jul 3, 2019, 8:54:18 AM7/3/19
to 4tH-compiler
> You know, because I use it for work, every time I find something
> lacking, I build it and add it to the libs.

You have a cool job. :)

The Beez

unread,
Jul 3, 2019, 3:23:23 PM7/3/19
to 4tH-compiler
"Don't judge a man until you walked in his shoes" ;-)

I can't deny that at times I get to do cool things - however, after 35 years on the job I must say I'm looking forward to be retired, because then I get do do something really useful.
Man, I can tell you stories... <shaking head>

Hans Bezemer

David Meyer

unread,
Jul 3, 2019, 6:07:12 PM7/3/19
to 4tH-compiler
I sympathize with you. I have been working for the same company for almost thirty years. I'm grateful for the job, but can hardly wait to retire.

Still, the coolest thing they ever let me program was a single awk script many years ago for a long-since retired system. There hasn't been any programming in my job description for maybe 20 years :_( , and all programming has since been outsourced. So you win. ;)

The Beez

unread,
Jul 4, 2019, 5:00:22 AM7/4/19
to 4tH-compiler


On Thursday, July 4, 2019 at 12:07:12 AM UTC+2, David Meyer wrote:

 There hasn't been any programming in my job description for maybe 20 years :_( , and all programming has since been outsourced. So you win. ;)


You know what? Mine neither! ;-)

But software development is so abysmal nowadays (apart from a few descent colleagues, which I value. One hacks emulated mainframes and does COBOL, but doesn't shy down for PHP and SQL) that these insane development time estimates have to be countered by some old fashioned honest real work every now and then.

In the bank where I used to work we had development times for entire subsystem like a week or two. When I tell people that, they don't believe me..

We decided to go web programming in the early naughties. Not that we liked the environment, but it was much easier to administer than client-server (ever tries to distribute a front end to a few thousand people after a minor update?). And it was sheer HORROR. So we developed some smart layers and code generators. We developed at least four screens (webpages) per developer per DAY. That's 20 per week. That's 80 per month. That's a LARGE application.

We also wrote a single signon system. The application sent a token (to identify itself), the user credentials and it got the DB credentials for that user. No more hard coded passwords in apps. We could interface it with NTLM, Apache, NOVELL ZEN - and then it was fully transparent.

A few remnants of that system (the code generators) are still within the 4tH repository. Yeah, ok, I admit - I designed and wrote most of that stuff. I was actually a Service Management Configuration Manager, but I'd automated myself into a half time job. When I  started my boss said "What I really want is to check the entire CMDB with the push of a button." So when I was done playing with Compaq Inside Manager and Tivoli Inventory (took a few years) I made a webpage with one single large button in the middle that said "PUSH ME".  That cascaded the whole machine into motion. You should have seen the look on his face. "You said: "a single button"" I remarked dryly.

That was fun. But the bankers wiped the bank out of existence and there it went 2008. If not, I'd still work there.

Hans Bezemer

Ron K. Jeffries

unread,
Jul 4, 2019, 12:17:30 PM7/4/19
to 4th-co...@googlegroups.com
Hans,

Have you looked over the specs for the recently announced Raspberry Pi 4? The high end option ($55 USD) has 4 GB of RAM. Has GigE and USB 3 ports and much improved compute performance.

Add a hard drive or better yet a cheap SSD and its a nice development machine.

Runs a Debian Linux flavor.

RasPi 4 seems like a really lovely home for 4Th. They sell cargo containers of these boards, so it's a popular, widely distributed platform that would be perfect for folks who use 4Th and for the many yet to discover your fine software craftsmanship.

Ron K Jeffries
 
---
Ron K. Jeffries






--
You received this message because you are subscribed to the Google Groups "4tH-compiler" group.
To unsubscribe from this group and stop receiving emails from it, send an email to 4th-compiler...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/4th-compiler/792e533a-4cfe-43e7-98ee-71a25e76681c%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

thebeez

unread,
Jul 4, 2019, 4:19:21 PM7/4/19
to 4th-co...@googlegroups.com
Hi Ron!

> Have you looked over the specs for the recently announced Raspberry Pi
> 4?
> The high end option ($55 USD) has 4 GB of RAM. Has GigE and USB 3 ports
> and
> much improved compute performance.
> Add a hard drive or better yet a cheap SSD and its a nice development
> machine.

Oh yes, I was one of the first to report on this machine. And I agree
fully with you that it is a lovely machine. I even predicted that in a
few years, who'll need an Intel machine for most common tasks. Currently
I'm working on an 8-core 1.8 GHz Raspi clone with 2 megs. It had some
issues, but I managed to handle them. See my report here:
https://www.linkedin.com/pulse/polishing-turd-hans-bezemer/

> RasPi 4 seems like a really lovely home for 4Th. They sell cargo
> containers
> of these boards, so it's a popular, widely distributed platform that
> would
> be perfect for folks who use 4Th and for the many yet to discover your
> fine
> software craftsmanship.
<blush> Thank you! I don't say that I'll never buy one, but buying a
completely new workstation so I can get this release out has my utmost
priority now. Note I got to customize it with lots of special s/w first
for all VM and X-compilers. I barely got this thingy going. Today I did
my first work on the manual in 1.5(!) years. I was able to retrieve it
only weeks ago from a crashed drive I salvaged from the old computer
that gave you all these releases for almost ten years.

But I promise that anyone who donates me a gadget with a viable Linux
platform, I'd either bring and continue to bring out a release for that
platform for the lifetime of that machine or get sent it back without
any cost.

Hans Bezemer

thebeez

unread,
May 27, 2020, 5:26:53 AM5/27/20
to 4th-co...@googlegroups.com
Hi David!

I'm cleaning up all the remarks and comments that came in and found this
nifty little program here, which I intend to add to 4tH's codebase with
your permission:

( romnma - Roman numerals from symbol-value table )

[needs lib/enter.4th]

create symbol ," M" ," CM" ," D" ," CD"
," C" ," XC" ," L" ," XL"
," X" ," IX" ," V" ," IV" ," I"

create symval 1000 , 900 , 500 , 400 ,
100 , 90 , 50 , 40 ,
10 , 9 , 5 , 4 , 1 ,
here symval - constant /symval

: roman ( n -- )
1 max 3999 min
/symval 0 do
begin dup symval i th @c < except
symbol i th @c count type
symval i th @c -
repeat
loop
drop
;

: prompt ( -- )
." Enter number: " enter
." Roman numeral: " roman cr
;

prompt

I've cleaned up the code a little. I don't know where "maxsym" came up,
but I'm kind of allergic to magic numbers - especially when I can get
the compiler to calculate them for me. Which is what I did here. It was
listed "12" where the tables contain 13 entries - which is also the loop
index. Did I assume correctly that this is where this constant came
from?

Furthermore I used the word "EXCEPT" which is new in v3.64 and a kind of
inverted "WHILE" so I can get rid of all those pesky "0=". If you want
to get that running in 3.62, you'll need the preprocessor:

:macro except 0= while ;

Same for the following entries, BTW:

:macro unless 0= if ;
:macro ;then exit then ;
:macro ?exit if ;then ;
:macro rdrop r> drop ;
:macro 2rdrop rdrop rdrop ;
:macro >zero dup xor ;
:macro stow over swap ;

So, in short my questions are:
- Were my changes correct or did I miss anything?
- Do you allow me to add it to the codebase?
- If so, which license/attribution do you want to add to it?

I'd love to hear from you. Thank you in advance - and send more ;-)

Hans Bezemer

David Meyer

unread,
May 28, 2020, 8:59:11 AM5/28/20
to 4tH-compiler
Hi!

When did you change from "The Beez" to "Beestje"?

On Wednesday, May 27, 2020 at 6:26:53 PM UTC+9, Beestje wrote:
I've cleaned up the code a little. I don't know where "maxsym" came up,
but I'm kind of allergic to magic numbers - especially when I can get
the compiler to calculate them for me. Which is what I did here. It was
listed "12" where the tables contain 13 entries - which is also the loop
index. Did I assume correctly that this is where this constant came
from?

maxsym is not a magic number. It is the maximum index value for the arrays symbol and symval. I thought its name, the location of the definition, and its use in the loop made that clear. I guess I should have added a comment. Since the number of symbols used to represent Roman numerals is very unlikely to change in the future, I thought it was preferable to define the maximum array index as a literal instead of calculating it as you did for /symval.
 
Furthermore I used the word "EXCEPT" which is new in v3.64 and a kind of
inverted "WHILE" so I can get rid of all those pesky "0=". If you want
to get that running in 3.62, you'll need the preprocessor:

A good reason to update the compiler. However, I am embarrassed to find that I can no longer compile either the original version romnma.4th or your revised version with either the 3.62 or 3.64 compiler version. In all cases it gives me the error "Word 0: I/O error". Am I forgetting something simple about how to use 4tH?

So, in short my questions are:
- Were my changes correct or did I miss anything?

I would like to be able to test your revised version for myself, but assuming it works, I have no objection to using EXCEPT instead of WHILE. Regarding maxsym, (again without testing) your change appears to work equivalently to the original. Style-wise, I prefer the way I used maxsym, with the addition of an explanatory comment (like, "\ maximum array index for symbol, symval"), but if you prefer to use the code as you modified it, it doesn't bother me.
 
- Do you allow me to add it to the codebase?

Yes. I'm flattered that you find my little toy useful.
 
- If so, which license/attribution do you want to add to it?

Is the following too long?

\ Copyright 2019 David Meyer +JMJ
\ Licensed under the Apache License, Version 2.0 (the "License");
\ you may not use this file except in compliance with the License.
\ You may obtain a copy of the License at
\     http://www.apache.org/licenses/LICENSE-2.0
\ Unless required by applicable law or agreed to in writing, software
\ distributed under the License is distributed on an "AS IS" BASIS,
\ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\ See the License for the specific language governing permissions and
\ limitations under the License.

--
David Meyer
Takarazuka, Japan

thebeez

unread,
May 28, 2020, 3:30:50 PM5/28/20
to 4th-co...@googlegroups.com
On 2020-05-28 14:59, David Meyer wrote:
> A good reason to update the compiler. However, I am embarrassed to find
> that I can no longer compile either the original version romnma.4th or
> your
> revised version with either the 3.62 or 3.64 compiler version. In all
> cases
> it gives me the error "Word 0: I/O error". Am I forgetting something
> simple
> about how to use 4tH?
I know the problem. Psst.. insider tip: use the preprocessor to find the
problem:

pi@bpi-iot-ros-ai:~$ pp4th -i romans2.4th
romans2.4th
lib/enter.4th
romans2.4th

This will give you a listing of all files included. It should work with
v3.62. The most obvious error I can think of is that you haven't set the
DIR4TH environment variable properly - or forgot the trailing slash. A
workaround is to make the directory above the "lib" directory your
working directory and try again. If it continues to play up, don't
hestitate to contact me.

> I would like to be able to test your revised version for myself, but
> assuming it works, I have no objection to using EXCEPT instead of
> WHILE.
> Regarding maxsym, (again without testing) your change appears to work
> equivalently to the original. Style-wise, I prefer the way I used
> maxsym,
> with the addition of an explanatory comment (like, "\ maximum array
> index
> for symbol, symval"), but if you prefer to use the code as you modified
> it,
> it doesn't bother me.
It prevents bugs when you forget to update the table. You're right,
after about 2000 years it's quite unlikely, but still..

>> - Do you allow me to add it to the codebase?
> Yes. I'm flattered that you find my little toy useful.
It's a nifty little program, very clear, good style. I like it a lot.
Keep your eye on SVN, ;-)

> Is the following too long?
>
> \ Copyright 2019 David Meyer +JMJ
> \ Licensed under the Apache License, Version 2.0 (the "License");
> \ you may not use this file except in compliance with the License.
> \ You may obtain a copy of the License at
> \ http://www.apache.org/licenses/LICENSE-2.0
> \ Unless required by applicable law or agreed to in writing, software
> \ distributed under the License is distributed on an "AS IS" BASIS,
> \ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> \ See the License for the specific language governing permissions and
> \ limitations under the License.

No problem, I had longer ones ;-) Consider it done.

Thanks! I'll get back to work!

Hans Bezemer
Reply all
Reply to author
Forward
0 new messages