commit 8ef30fbc113f40b1b213cb350c10c0952287ba57 Author: fogus Date: Thu Sep 9 09:01:38 2010 -0400 Initial import of Russ's first version diff --git a/README.md b/README.md new file mode 100644 index 0000000..c273870 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +russ forth +========== + +A simple Forth interpreter in Ruby (1.9). + + 1 2 dup + + + . + 5 + + 3 2 1 + * + . + 9 + diff --git a/src/rforth.rb b/src/rforth.rb new file mode 100644 index 0000000..4423a16 --- /dev/null +++ b/src/rforth.rb @@ -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