mirror of
https://github.com/russolsen/rforth
synced 2024-12-26 09:58:27 +01:00
Moved prim methods into a module, cleaned up compiling words
This commit is contained in:
parent
834406e9c1
commit
df1a324bd6
1 changed files with 165 additions and 52 deletions
219
src/rforth.rb
219
src/rforth.rb
|
@ -1,5 +1,74 @@
|
||||||
require 'pp'
|
require 'pp'
|
||||||
|
|
||||||
|
module PrimitiveWords
|
||||||
|
|
||||||
|
def dup
|
||||||
|
@stack << @stack.last
|
||||||
|
end
|
||||||
|
|
||||||
|
def q_dup
|
||||||
|
@stack << @stack.last unless @stack.last == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def drop
|
||||||
|
@stack.pop
|
||||||
|
end
|
||||||
|
|
||||||
|
def swap
|
||||||
|
@stack += [@stack.pop, @stack.pop]
|
||||||
|
end
|
||||||
|
|
||||||
|
def over
|
||||||
|
a = @stack.pop
|
||||||
|
b = @stack.pop
|
||||||
|
@stack << b << a << b
|
||||||
|
end
|
||||||
|
|
||||||
|
def rot
|
||||||
|
a = @stack.pop
|
||||||
|
b = @stack.pop
|
||||||
|
c = @stack.pop
|
||||||
|
@stack << b << a << c
|
||||||
|
end
|
||||||
|
|
||||||
|
def plus
|
||||||
|
@stack << (@stack.pop + @stack.pop)
|
||||||
|
end
|
||||||
|
|
||||||
|
def mult
|
||||||
|
@stack << (@stack.pop * @stack.pop)
|
||||||
|
end
|
||||||
|
|
||||||
|
def subtract
|
||||||
|
a = @stack.pop
|
||||||
|
b = @stack.pop
|
||||||
|
@stack << b - a
|
||||||
|
end
|
||||||
|
|
||||||
|
def divide
|
||||||
|
a = @stack.pop
|
||||||
|
b = @stack.pop
|
||||||
|
@stack << b / a
|
||||||
|
end
|
||||||
|
|
||||||
|
def dot
|
||||||
|
@s_out.print( @stack.pop )
|
||||||
|
end
|
||||||
|
|
||||||
|
def cr
|
||||||
|
@s_out.puts
|
||||||
|
end
|
||||||
|
|
||||||
|
def dot_s
|
||||||
|
@s_out.print( "#{@stack}\n" )
|
||||||
|
end
|
||||||
|
|
||||||
|
def dot_d
|
||||||
|
pp @dictionary
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
class Dictionary
|
class Dictionary
|
||||||
def initialize( &block )
|
def initialize( &block )
|
||||||
@entries = {}
|
@entries = {}
|
||||||
|
@ -16,64 +85,85 @@ class Dictionary
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def alias_word( name, old_name )
|
||||||
|
entry = self[old_name]
|
||||||
|
raise "No such word #{old_name}" unless entry
|
||||||
|
new_entry = entry.dup
|
||||||
|
new_entry[:name] = name
|
||||||
|
@entries[name] = entry
|
||||||
|
end
|
||||||
|
|
||||||
def []( name )
|
def []( name )
|
||||||
@entries[name]
|
@entries[name]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class RForth
|
class RForth
|
||||||
|
include PrimitiveWords
|
||||||
|
|
||||||
def initialize( s_in = $stdin, s_out = $stdout )
|
def initialize( s_in = $stdin, s_out = $stdout )
|
||||||
@s_in = s_in
|
@s_in = s_in
|
||||||
@s_out = s_out
|
@s_out = s_out
|
||||||
@dictionary = initial_dictionary
|
@dictionary = Dictionary.new
|
||||||
@stack = []
|
@stack = []
|
||||||
|
initialize_dictionary
|
||||||
end
|
end
|
||||||
|
|
||||||
def initial_dictionary
|
# Create all of the initial words.
|
||||||
Dictionary.new do |d|
|
def initialize_dictionary
|
||||||
d.word('dup') { @stack << @stack.last }
|
PrimitiveWords.public_instance_methods(false).each do |m|
|
||||||
d.word('?dup') { @stack << @stack.last unless @stack.last == 0 }
|
method_clojure = method(m.to_sym)
|
||||||
d.word('drop') { @stack.pop }
|
word( m.to_s, &method_clojure )
|
||||||
d.word('swap') { @stack += [@stack.pop, @stack.pop] }
|
|
||||||
d.word('over') do
|
|
||||||
a = @stack.pop
|
|
||||||
b = @stack.pop
|
|
||||||
@stack << b << a << b
|
|
||||||
end
|
|
||||||
d.word('rot') do
|
|
||||||
a = @stack.pop
|
|
||||||
b = @stack.pop
|
|
||||||
c = @stack.pop
|
|
||||||
@stack << b << a << c
|
|
||||||
end
|
|
||||||
d.word(':') { define_word }
|
|
||||||
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.word('.') { @s_out.print( @stack.pop ) }
|
|
||||||
d.word('.S') { @s_out.print( "#{@stack}\n" ) }
|
|
||||||
d.word('.D') { pp @dictionary }
|
|
||||||
d.word('cr') { @s_out.puts }
|
|
||||||
d.word('bye') { exit }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def define_word
|
alias_word( '?dup', 'q_dup' )
|
||||||
name = read_word
|
alias_word( '+', 'plus' )
|
||||||
|
alias_word( '*', 'mult' )
|
||||||
|
alias_word( '-', 'subtract' )
|
||||||
|
alias_word( '/', 'divide' )
|
||||||
|
alias_word( '.', 'dot' )
|
||||||
|
alias_word( '.S', 'dot_s' )
|
||||||
|
alias_word( '.D', 'dot_d' )
|
||||||
|
|
||||||
|
word(':') { read_and_define_word }
|
||||||
|
word('bye') { exit }
|
||||||
|
|
||||||
|
immediate_word( '\\' ) { @s_in.readline }
|
||||||
|
end
|
||||||
|
|
||||||
|
# Convience method that takes a word and a closure
|
||||||
|
# and defines the word in the dictionary
|
||||||
|
def word( name, &block )
|
||||||
|
@dictionary.word( name, &block )
|
||||||
|
end
|
||||||
|
|
||||||
|
# Convience method that takes a word and a closure
|
||||||
|
# and defines an immediate word in the dictionary
|
||||||
|
def immediate_word( name, &block )
|
||||||
|
@dictionary.immediate_word( name, &block )
|
||||||
|
end
|
||||||
|
|
||||||
|
# Convience method that takes an existing dict.
|
||||||
|
# word and a new word and aliases the new word to
|
||||||
|
# the old.
|
||||||
|
def alias_word( name, old_name )
|
||||||
|
@dictionary.alias_word( name, old_name )
|
||||||
|
end
|
||||||
|
|
||||||
|
# Given the name of a new words and the words
|
||||||
|
# that make up its definition, define the
|
||||||
|
# new word.
|
||||||
|
def define_word( name, *words )
|
||||||
|
@dictionary.word( name, &compile_words( *words ) )
|
||||||
|
end
|
||||||
|
|
||||||
|
# Give an array of (string) words, return
|
||||||
|
# A block which will run all of those words.
|
||||||
|
# Executes all immedate words, well, immediately.
|
||||||
|
def compile_words( *words )
|
||||||
blocks = []
|
blocks = []
|
||||||
while (word = read_word)
|
words.each do |word|
|
||||||
break if word == ';'
|
entry = resolve_word( word )
|
||||||
entry = @dictionary[word]
|
|
||||||
raise "no such word: #{word}" unless entry
|
raise "no such word: #{word}" unless entry
|
||||||
if entry[:immediate]
|
if entry[:immediate]
|
||||||
entry[:block].call
|
entry[:block].call
|
||||||
|
@ -81,22 +171,49 @@ class RForth
|
||||||
blocks << entry[:block]
|
blocks << entry[:block]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
proc do
|
||||||
@dictionary.word(name) do
|
|
||||||
blocks.each {|b| b.call}
|
blocks.each {|b| b.call}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Read a word definition from input and
|
||||||
|
# define the word
|
||||||
|
# Definition looks like:
|
||||||
|
# new-word w1 w2 w3 ;
|
||||||
|
def read_and_define_word
|
||||||
|
name = read_word
|
||||||
|
words = []
|
||||||
|
while (word = read_word)
|
||||||
|
break if word == ';'
|
||||||
|
words << word
|
||||||
|
end
|
||||||
|
@dictionary.word(name, &compile_words( *words ))
|
||||||
|
end
|
||||||
|
|
||||||
|
# Given a (string) word, return the dictionary
|
||||||
|
# entry for that word or nil.
|
||||||
|
def resolve_word( word )
|
||||||
|
return @dictionary[word] if @dictionary[word]
|
||||||
|
x = to_number(word)
|
||||||
|
if x
|
||||||
|
block = proc { @stack << x }
|
||||||
|
return { :name => word, :block => block, :immediate => false }
|
||||||
|
end
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# Evaluate the given word.
|
||||||
def forth_eval( word )
|
def forth_eval( word )
|
||||||
if @dictionary[word]
|
entry = resolve_word(word)
|
||||||
@dictionary[word][:block].call
|
if entry
|
||||||
elsif (x = to_number(word))
|
entry[:block].call
|
||||||
@stack << x
|
|
||||||
else
|
else
|
||||||
@s_out.puts "#{word} ??"
|
@s_out.puts "#{word} ??"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Try to turn the word into a number, return nil if
|
||||||
|
# conversion fails
|
||||||
def to_number( word )
|
def to_number( word )
|
||||||
begin
|
begin
|
||||||
return Integer( word )
|
return Integer( word )
|
||||||
|
@ -111,15 +228,11 @@ class RForth
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_char
|
|
||||||
@s_in.readchar
|
|
||||||
end
|
|
||||||
|
|
||||||
def read_word
|
def read_word
|
||||||
result = nil
|
result = nil
|
||||||
ch = nil
|
ch = nil
|
||||||
until @s_in.eof?
|
until @s_in.eof?
|
||||||
ch = read_char
|
ch = @s_in.readchar
|
||||||
if result and is_space?(ch)
|
if result and is_space?(ch)
|
||||||
break
|
break
|
||||||
elsif result.nil?
|
elsif result.nil?
|
||||||
|
|
Loading…
Reference in a new issue