Skip to content

Instantly share code, notes, and snippets.

@usure
Forked from deadprogram/forth.rb
Last active August 29, 2015 14:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save usure/f39a6e6fe47b9ff62baa to your computer and use it in GitHub Desktop.
Save usure/f39a6e6fe47b9ff62baa to your computer and use it in GitHub Desktop.
Forth interpreter in 64 lines of Ruby
#!/usr/bin/env ruby
require 'pp'
def pop; $stack.pop || raise(StackUnderflow); end
def push(expression); $stack << expression; end
def unary; -> { push(yield pop) }; end
def binary; -> { push(yield pop, pop) }; end
def unary_boolean; -> { push(if yield pop then 1 else 0 end) }; end
def binary_boolean; -> { push(if yield pop, pop then 1 else 0 end) }; end
def functions; -> { push(yield pop, pop)}; end
def swap; $stack[-2,2] = $stack[-2,2].reverse; end
$stack = []
#def new_word(word, expr)
#raise EmptyWord if word.size < 1
#raise NestedDefinition if word.include? ':'
#name, expression = word, expr
#expression = Array(expression)
#puts expression.class
# $dictionary[name] = -> {
#expression.each { |x| parse(x) }
# quick_expr = Array(.rchomp('"').chomp('"'))
# quick_expr = parse(Array(expression.rchomp('"').chomp('"')))
# puts quick_expr.class
# puts expression
#quick_expr.length
#quick_expr.each { |x| parse(x) }
# }
#end
def new_word
raise EmptyWord if $word.size < 1
raise NestedDefinition if $word.include? ':'
name, expression = $word.shift, $word.join(' ')
$dictionary[name] = -> { parse(expression) }
$word = nil
end
def parse(expression)
puts "=> #{expression}"
begin
expression.split.each do |statement|
case
when !$skip.nil? && statement != 'fi'; next
when !$word.nil? && statement != ';'; $word << statement
when $dictionary.has_key?(statement); $dictionary[statement].call
when statement == Integer
push statement.to_i
else
push statement.to_str
end
end
rescue
puts "Error: #{$!}"
end
end
$dictionary = {
'+' => binary { |a, b| a.to_i + b.to_i },
'-' => binary { |a, b| a.to_i - b .to_i},
'*' => binary { |a, b| a.to_i * b.to_i },
'/' => binary { |a, b| a.to_i * b.to_i },
'%' => binary { |a, b| a.to_i * b.to_i },
'<' => binary_boolean { |a, b| a.to_i < b.to_i },
'>' => binary_boolean { |a, b| a.to_i > b.to_i },
'=' => binary_boolean { |a, b| a == b ? (puts "true") : (puts "false")},
'&' => binary_boolean { |a, b| a && b },
'|' => binary_boolean { |a, b| a || b },
'not' => binary_boolean { |a, b| a == 0 },
'neg' => binary { |a| -a },
'.' => -> { puts(pop) },
'..' => -> { puts($stack) },
'.,' => -> { pp($dictionary)},
':' => -> { $word = [] },
';' => -> { new_word },
'pop' => -> { pop },
'\\' => -> {},
'fi' => -> { $skip = nil },
'words' => -> { p $dictionary.keys.sort },
'if' => -> { $skip = true if pop == 0 },
'dup' => -> { push($stack.last || raise(StackUnderflow)) },
'over' => -> { push($stack.last(2).first || raise(StackUnderflow)) },
'swap' => -> { begin swap rescue raise(StackUnderflow) end }
}
puts $dictionary.class
puts "frb."
while ARGV.size > 0; open(ARGV.shift).each { |line| parse(line) }; end
while true; print "> "; break unless gets; parse $_; end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment