mirror of
https://github.com/russolsen/rforth
synced 2024-12-26 09:58:27 +01:00
Initial import of Russ's first version
This commit is contained in:
commit
8ef30fbc11
2 changed files with 141 additions and 0 deletions
13
README.md
Normal file
13
README.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
russ forth
|
||||
==========
|
||||
|
||||
A simple Forth interpreter in Ruby (1.9).
|
||||
|
||||
1 2 dup + +
|
||||
.
|
||||
5
|
||||
|
||||
3 2 1 + *
|
||||
.
|
||||
9
|
||||
|
128
src/rforth.rb
Normal file
128
src/rforth.rb
Normal file
|
@ -0,0 +1,128 @@
|
|||
require 'pp'
|
||||
|
||||
class Dictionary
|
||||
def initialize
|
||||
@entries = {}
|
||||
end
|
||||
|
||||
def word( name, &block )
|
||||
@entries[name] = { :name => name, :block => block, :immediate => false }
|
||||
end
|
||||
|
||||
def immediate_word( name, &block )
|
||||
@entries[name] = { :name => name, :block => block, :immediate => true }
|
||||
end
|
||||
|
||||
def []( name )
|
||||
@entries[name]
|
||||
end
|
||||
end
|
||||
|
||||
class RForth
|
||||
|
||||
def initialize( s_in = $stdin, s_out = $stdout )
|
||||
@s_in = s_in
|
||||
@s_out = s_out
|
||||
@dictionary = initial_dictionary
|
||||
@stack = []
|
||||
end
|
||||
|
||||
def initial_dictionary
|
||||
d = Dictionary.new
|
||||
d.word(':') { define_word }
|
||||
d.word('.'){ @s_out.print( @stack.pop ) }
|
||||
d.word('cr') { @s_out.puts }
|
||||
d.word('+') { @stack << (@stack.pop + @stack.pop) }
|
||||
d.word('*') { @stack << (@stack.pop * @stack.pop) }
|
||||
d.word('-') do
|
||||
a = @stack.pop
|
||||
b = @stack.pop
|
||||
@stack << b - a
|
||||
end
|
||||
|
||||
d.word('/') do
|
||||
a = @stack.pop
|
||||
b = @stack.pop
|
||||
@stack << b / a
|
||||
end
|
||||
d
|
||||
end
|
||||
|
||||
def define_word
|
||||
name = read_word
|
||||
blocks = []
|
||||
while (word = read_word)
|
||||
break if word == ';'
|
||||
entry = @dictionary.word(word)
|
||||
raise "no such word: #{word}" unless entry
|
||||
if entry[:immediate]
|
||||
entry[:block].call
|
||||
else
|
||||
blocks << entry[:block]
|
||||
end
|
||||
end
|
||||
|
||||
@dictionary.word(name) do
|
||||
blocks.each {|b| b.call}
|
||||
end
|
||||
end
|
||||
|
||||
def forth_eval( word )
|
||||
if @dictionary[word]
|
||||
@dictionary[word][:block].call
|
||||
elsif (x = to_number(word))
|
||||
@stack << x
|
||||
else
|
||||
@s_out.puts "#{word} ??"
|
||||
end
|
||||
end
|
||||
|
||||
def to_number( word )
|
||||
begin
|
||||
return Integer( word )
|
||||
rescue
|
||||
puts $!
|
||||
end
|
||||
begin
|
||||
return Float( word )
|
||||
rescue
|
||||
puts $!
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
def read_char
|
||||
@s_in.readchar
|
||||
end
|
||||
|
||||
def read_word
|
||||
result = nil
|
||||
ch = nil
|
||||
until @s_in.eof?
|
||||
ch = read_char
|
||||
if result and is_space?(ch)
|
||||
break
|
||||
elsif result.nil?
|
||||
result = ch
|
||||
else
|
||||
result << ch
|
||||
end
|
||||
end
|
||||
return result if result
|
||||
nil
|
||||
end
|
||||
|
||||
def is_space?( ch )
|
||||
/\W/ =~ ch
|
||||
end
|
||||
|
||||
def run
|
||||
until $stdin.eof?
|
||||
@s_out.flush
|
||||
word = read_word
|
||||
forth_eval( word )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
RForth.new.run
|
Loading…
Reference in a new issue