[ANN] ratlast 0.1 -- embedded FORTH in Ruby

Rubyists,

Later on today, I will be uploading the first release of
ratlast to RAA. You can get the system at

http://www3.sympatico.ca/mark.probert/download/files/ratlast_0_1.tar.gz

(This is my home account which I don’t have access to at work.
I will upload the file around 19.00 EST.)

*** What is ratlast?

ratlast is an extension gives Ruby the power and flexibility of FORTH,
using John Walker’s ATLAST system.

ATLAST is a F-83 style system that includes floating point
and string primitives, as well as systems FORTH standards such
as CREATE and DOES>. It is very powerful, flexible, fast, and
easy to extend. On the fly compiling is trivial.

Most of all, it is a -lot- of fun to use!

*** Example

Here is a little magic :-). We create a linear equation ax+b and
give it the name ‘linear’. For the example, we know the slope (3)
and intercept (17) and the x varies. We then evaluate the function
and print the result.

(For the FORTHists in the audience, the stack is 4-bytes wide for
all integers, hence the 4 + …)

$ cat ex1.rb
require 'Atlast’
t = Atlast.new

t.eval(": linear ( a b – ) create swap , , does> dup >r @ * r> 4 + @ + ;")
t.eval(“3 17 linear aline”)
x = 1
until x >= 10
t.eval("#{x} aline .")
puts "x=#{x} : 3x + 17 = #{t.capture[0]}"
x += 1
t.clear # clear the capture buffer
end

*** Output

$ ruby ex1.rb
x=1 : 3x + 17 = 20
x=2 : 3x + 17 = 23
x=3 : 3x + 17 = 26
x=4 : 3x + 17 = 29
x=5 : 3x + 17 = 32
x=6 : 3x + 17 = 35
x=7 : 3x + 17 = 38
x=8 : 3x + 17 = 41
x=9 : 3x + 17 = 44

-mark.

Mark,

I'm not nocking ya and I don't have a clue about FORTH... so... why

would I use FORTH instead of Ruby?

Where can I go to learn FORTH?

-Rich

···

----- Original Message -----
From: “Mark Probert” probertm@nortelnetworks.com
To: “ruby-talk ML” ruby-talk@ruby-lang.org
Sent: Thursday, December 05, 2002 1:57 PM
Subject: [ANN] ratlast 0.1 – embedded FORTH in Ruby

Rubyists,

Later on today, I will be uploading the first release of
ratlast to RAA. You can get the system at

http://www3.sympatico.ca/mark.probert/download/files/ratlast_0_1.tar.gz

(This is my home account which I don’t have access to at work.
I will upload the file around 19.00 EST.)

*** What is ratlast?

ratlast is an extension gives Ruby the power and flexibility of FORTH,
using John Walker’s ATLAST system.

ATLAST is a F-83 style system that includes floating point
and string primitives, as well as systems FORTH standards such
as CREATE and DOES>. It is very powerful, flexible, fast, and
easy to extend. On the fly compiling is trivial.

Most of all, it is a -lot- of fun to use!

*** Example

Here is a little magic :-). We create a linear equation ax+b and
give it the name ‘linear’. For the example, we know the slope (3)
and intercept (17) and the x varies. We then evaluate the function
and print the result.

(For the FORTHists in the audience, the stack is 4-bytes wide for
all integers, hence the 4 + …)

$ cat ex1.rb
require ‘Atlast’
t = Atlast.new

t.eval(“: linear ( a b – ) create swap , , does> dup >r @ * r> 4 + @ +
;”)
t.eval(“3 17 linear aline”)
x = 1
until x >= 10
t.eval(“#{x} aline .”)
puts “x=#{x} : 3x + 17 = #{t.capture[0]}”
x += 1
t.clear # clear the capture buffer
end

*** Output

$ ruby ex1.rb
x=1 : 3x + 17 = 20
x=2 : 3x + 17 = 23
x=3 : 3x + 17 = 26
x=4 : 3x + 17 = 29
x=5 : 3x + 17 = 32
x=6 : 3x + 17 = 35
x=7 : 3x + 17 = 38
x=8 : 3x + 17 = 41
x=9 : 3x + 17 = 44

-mark.

Ah, yes, I can see how that’s so much more intuitive than …

def linear(x)
3 * x + 17
end

Only ribbing you, there, but you have to admit the FORTH code tends to obscure
what’s going on just a little :-).

It reminds me of Postscript code (which I’ve only ever written maybe 1000
lines of altogether). Probably because of the direct stack-based approach.

I bought a book on FORTH a few years ago, but never got around to playing with
it, because something else distracted me.

I seem to recall reading a rumour that the boot code for Sun workstations was
written in FORTH, but never found out whether that was true.

H.

···

On Fri, 6 Dec 2002 07:57, Mark Probert wrote:

Most of all, it is a -lot- of fun to use!

$ cat ex1.rb
require ‘Atlast’
t = Atlast.new

t.eval(“: linear ( a b – ) create swap , , does> dup >r @ * r> 4 + @ +
;”) t.eval(“3 17 linear aline”)

Mark
Getting bounces… anyway

Ruby-1.6.8 woks fine with your CFLAGS
Ruby-1.7.3 (2002-12-04) [i686-linux] produces a Makefile line

CFLAGS = -fPIC -g -O2-Wall -fwritable-strings -DALIGNMENT -DEXPORT

-02 fails gcc make Invalid option, remove -02 makes ok

Hope this is helpful

···


Neil Walker
http://www.quill.plus.com

Hi, Rich.

Thank for your interest.

Mark,

I'm not nocking ya and I don't have a clue about FORTH... so... why

would I use FORTH instead of Ruby?

FORTH is good for some things that Ruby is less strong on. One of the
main “advantages” of FORTH is that when you define a word, you are
actually extending the language. The mechanism for doing this is
very efficient.

The main idea that I have for ratlast is as a user-extensible command
language for Ruby applications. For example, what do you do if you have
a running Ruby system that you can’t take down, yet you have to add some
functionality? That is very easy to do using ratlast.

Not convinced? Me either ;-). I did this cause I could, really. FORTH
is an excellent language and unusual language, and, as I said, a lot
of fun to work with. It has a very quick test cycle that encourages
on-the-spot testing. Try it and you’ll see.

Where can I go to learn FORTH?

Have a look at

     http://www.taygeta.com/forthlit.html

Julian Noble’s primer is a good start.

-mark.

···

At 06:19 AM 12/6/2002 +0900, you wrote:

Hi, Harry.

Harry Ohlsen harryo@zip.com.au wrote:

Most of all, it is a -lot- of fun to use!

$ cat ex1.rb
require ‘Atlast’
t = Atlast.new

t.eval(": linear ( a b – ) create swap , , does> dup >r @ * r> 4 + @

  • ;") t.eval(“3 17 linear aline”)

Ah, yes, I can see how that’s so much more intuitive than …

def linear(x)
3 * x + 17
end

hmmmmm.

Not quite. The colon definition of linear is more like a lambda
function in lisp, it defines a series of functions, for “a*x + b”.
The binding of a and b happens in the “linear aline” part. I think
you can do this in Ruby using closures.

Only ribbing you, there, but you have to admit the FORTH code tends to
obscure what’s going on just a little :-).

Beauty is in the eye … :wink:

It reminds me of Postscript code

Postscript was based on FORTH.

I seem to recall reading a rumour that the boot code for Sun
workstations was written in FORTH, but never found out whether that was
true.
Type L1-A

4 5 + .
: 2* 2 * . ;
7 2*

-laugh-

It is (was) FORTH. I think there is an open firmware project
that is based on FORTH.

-mark.

···

On Fri, 6 Dec 2002 07:57, Mark Probert wrote:

LOADER(8) FreeBSD System Manager’s Manual LOADER(8)
NAME
loader - kernel bootstrapping final stage

DESCRIPTION
The program called loader is the final stage of FreeBSD’s kernel boot-
strapping process. On IA32 (i386) architectures, it is a BTX client. It
is linked statically to libstand(3) and usually located in the directory
/boot.

 It provides a scripting language that can be used to automate tasks, do
 pre-configuration or assist in recovery procedures.  This scripting lan-
 guage is roughly divided in two main components.  The smaller one is a
 set of commands designed for direct use by the casual user, called
 "builtin commands" for historical reasons.  The main drive behind these
 commands is user-friendlyness.  The bigger component is an ANS Forth com-
 patible Forth interpreter based on ficl, by John Sadler.
···

Harry Ohlsen (harryo@zip.com.au) wrote:

I seem to recall reading a rumour that the boot code for Sun
workstations was written in FORTH, but never found out whether that
was true.

H.


Eric Hodel - drbrain@segment7.net - http://segment7.net
All messages signed with fingerprint:
FEC2 57F1 D465 EB15 5D6E 7C11 332A 551C 796C 9F04

Hi, Neil,

Mark
Getting bounces… anyway

Me, too. Thanks for the post.

Ruby-1.6.8 woks fine with your CFLAGS
Ruby-1.7.3 (2002-12-04) [i686-linux] produces a Makefile line

CFLAGS = -fPIC -g -O2-Wall -fwritable-strings -DALIGNMENT -DEXPORT

If you put the lines:

$CFLAGS << " -DALIGNMENT -DEXPORT"
if Config::CONFIG[“CC”] =~ /gcc/
$CFLAGS += " -Wall -fwritable-strings"
end

into extconf.rb then it should make okay. It did under cygwin
though I have yet to check it against linux (later today, if I
get the time).

I almost have ratlast 0.2 ready to release. This adds a number
of control and performance items, like breaking loops, memory
stats for the interpreter, and so on. Also, I plan on having
Atlast#eval return the first element in the capture array so
you could do something like:

t = Atlast.new
n = t.eval(“342 406 gcd .”)
puts “gcd = #{n}”

Regards,

-mark.

···

At 03:56 AM 12/8/2002 +0900, you wrote:

Mark Probert wrote:

The main idea that I have for ratlast is as a user-extensible command
language for Ruby applications. For example, what do you do if you have
a running Ruby system that you can’t take down, yet you have to add some
functionality? That is very easy to do using ratlast.

I like this idea. It’s possible to generate C code, compile it, and load
the dynamic lib, but that requires a C compiler, takes a fair amount of
cpu time, etc. If you want to trade run speed for compile speed,
Forth-like code is worth a look.

What kind of code-threading model do you use? Token, direct, indirect,
jump? If you’re using jump-threaded code, you can have bits of inline
assembly code, so performance could approach compiled C.

Do you plan to provide any kind of API to ruby objects, so you can
access instance vars, call methods, etc.?

Mark Probert wrote:

Hi, Harry.

Harry Ohlsen harryo@zip.com.au wrote:

Most of all, it is a -lot- of fun to use!

$ cat ex1.rb
require ‘Atlast’
t = Atlast.new

t.eval(": linear ( a b – ) create swap , , does> dup >r @ * r> 4 + @

  • ;") t.eval(“3 17 linear aline”)

Ah, yes, I can see how that’s so much more intuitive than …

def linear(x)
3 * x + 17
end

hmmmmm.

Not quite. The colon definition of linear is more like a lambda
function in lisp, it defines a series of functions, for “a*x + b”.
The binding of a and b happens in the “linear aline” part. I think
you can do this in Ruby using closures.

But closures can do something quite different–they can share a variable
binding with the enclosing scope:

a = 1
b = 2

linear = proc { proc {|x|a*x+b} }

l1 = linear

p l1[5] # ==> 7

a = 10

p l1[5] # ==> 52

If you want linear to behave more like the Forth example:

linear2 = proc { |aa,bb| proc {|x|aa*x+bb} }

l2 = linear2[1,2]

p l2[5] # ==> 7

aa = 10

p l2[5] # ==> 7

···

On Fri, 6 Dec 2002 07:57, Mark Probert wrote:

In article 5.1.0.14.2.20021205171651.021cce20@zcard04k.ca.nortel.com,

···

Mark Probert probertm@nortelnetworks.com wrote:

Hi, Rich.

Thank for your interest.

At 06:19 AM 12/6/2002 +0900, you wrote:

Mark,

I'm not nocking ya and I don't have a clue about FORTH... so... why

would I use FORTH instead of Ruby?

FORTH is good for some things that Ruby is less strong on. One of the
main “advantages” of FORTH is that when you define a word, you are
actually extending the language. The mechanism for doing this is
very efficient.

The main idea that I have for ratlast is as a user-extensible command
language for Ruby applications. For example, what do you do if you have
a running Ruby system that you can’t take down, yet you have to add some
functionality? That is very easy to do using ratlast.

Actually, that’s easy to do using Ruby. :wink:

Phil

“Or perhaps the truth is less interesting than the facts?”
Amy Weiss (accusing theregister.co.uk of engaging in ‘tabloid journalism’)
Senior VP, Communications
Recording Industry Association of America

Mark Probert wrote:

Hi, Neil,

Mark
Getting bounces… anyway

Me, too. Thanks for the post.

Ruby-1.6.8 woks fine with your CFLAGS
Ruby-1.7.3 (2002-12-04) [i686-linux] produces a Makefile line

CFLAGS = -fPIC -g -O2-Wall -fwritable-strings -DALIGNMENT -DEXPORT

If you put the lines:

$CFLAGS << " -DALIGNMENT -DEXPORT"
if Config::CONFIG[“CC”] =~ /gcc/
$CFLAGS += " -Wall -fwritable-strings"
end

into extconf.rb then it should make okay. It did under cygwin
though I have yet to check it against linux (later today, if I
get the time).

I almost have ratlast 0.2 ready to release. This adds a number
of control and performance items, like breaking loops, memory
stats for the interpreter, and so on. Also, I plan on having
Atlast#eval return the first element in the capture array so
you could do something like:

t = Atlast.new
n = t.eval(“342 406 gcd .”)
puts “gcd = #{n}”

Regards,

-mark.

Thanks working fine now, look forward to 0.2,
Am running in a Tk.Text window with the output fed into
another Tk.Text window.
It would be nice to be have all Atlasts’ error messages available.

neil

···

At 03:56 AM 12/8/2002 +0900, you wrote:


Neil Walker

What kind of code-threading model do you use? Token, direct, indirect,
jump? If you’re using jump-threaded code, you can have bits of inline
assembly code, so performance could approach compiled C.

Indirect threading, I think. To tell you the truth, I haven’t
studied the inner workings of ATLAST, yet. That will be part
of the next adventure.

Do you plan to provide any kind of API to ruby objects, so you can
access instance vars, call methods, etc.?

I hadn’t planned on doing so. Part of the idea was to keep
everything as simple and clean as possible. So, do in Forth
what it does best, and the same in Ruby.

What would you see as the advantages of doing this?

-mark.

···

Joel VanderWerf vjoel@PATH.Berkeley.EDU wrote:

Hi, Joel.

t.eval(": linear ( a b – ) create swap , , does> dup >r @ * r> 4 + @

  • ;") t.eval(“3 17 linear aline”)

But closures can do something quite different–they can share a variable
binding with the enclosing scope:

a = 1
b = 2

linear = proc { proc {|x|a*x+b} }

This is also (almost) true of the Forth code. Here, the binding
happens at the line

3 17 linear aline

I could have written this code:

i = j = 1
until i <= 10
t.eval(“#{i} #{j} linear aline_#{i}”) # rename the fn to avoid conflict
x = rand(100)
t.eval(“#{x} aline_#{i}”)
puts “x=#{x} : #{i}x + #{j} = #{t.caputure[0]}”
i += 1
j += 5
t.clear
end

I think this is the same as closures, or have I misunderstood?

Regards,

-mark.

···

Joel VanderWerf vjoel@PATH.Berkeley.EDU wrote:

Hi, Phil.

···

At 03:58 PM 12/6/2002 +0900, you wrote:

The main idea that I have for ratlast is as a user-extensible command
language for Ruby applications. For example, what do you do if you have
a running Ruby system that you can’t take down, yet you have to add some
functionality? That is very easy to do using ratlast.

Actually, that’s easy to do using Ruby. :wink:

Do tell … :slight_smile:

-mark.

Hi, Neil.

Thanks working fine now, look forward to 0.2,

-laugh-

I think you are the only one …

Am running in a Tk.Text window with the output fed into
another Tk.Text window.

Neat – interactive Forth with Ruby.

It would be nice to be have all Atlasts’ error messages available.

That is one of the To Dos. I’ll add it to 0.2

Regards,
-mark.

···

At 03:57 AM 12/9/2002 +0900, you wrote:

server.rb:

···

require ‘drb’
class Server
def loadscript(file)
load(file)
end
end

$srvr = Server.new
DRb.start_service(‘druby://localhost:1234’,$srvr)
DRb.thread.join

loadscript.rb:

require ‘drb’
DRb.start_service(0
srvr = DRbObject.new(nil,‘druby://localhost:1234’)

ARGV.each do |file|
srvr.loadscript(file)
end

newscript.rb:

puts “New script successfully loaded”

..
ruby server.rb (in shell 1)
ruby loadscript.rb newscript.rb (in shell 2)
=> “New script succesfully loaded” (in shell 1)


Greg Millam
walker at deafcode.com

On Sat, 7 Dec 2002, Mark Probert wrote:

Hi, Phil.

At 03:58 PM 12/6/2002 +0900, you wrote:

The main idea that I have for ratlast is as a user-extensible command
language for Ruby applications. For example, what do you do if you have
a running Ruby system that you can’t take down, yet you have to add some
functionality? That is very easy to do using ratlast.

Actually, that’s easy to do using Ruby. :wink:

Do tell … :slight_smile:

-mark.

Hi, Greg.

···

At 12:52 AM 12/7/2002 +0900, you wrote:

server.rb:

require ‘drb’
class Server
def loadscript(file)
load(file)
end
end

Thank you. I didn’t know that drb existed.

Regards,
-mark.