Skip to content

Instantly share code, notes, and snippets.

@fogus
Forked from deadprogram/forth.rb
Created January 26, 2012 03:32
Show Gist options
  • Select an option

  • Save fogus/1680793 to your computer and use it in GitHub Desktop.

Select an option

Save fogus/1680793 to your computer and use it in GitHub Desktop.

Revisions

  1. @deadprogram deadprogram revised this gist Jan 26, 2012. 1 changed file with 14 additions and 45 deletions.
    59 changes: 14 additions & 45 deletions forth.rb
    Original file line number Diff line number Diff line change
    @@ -1,27 +1,17 @@
    #!/usr/bin/env ruby

    def pop
    $stack.pop || raise(StackUnderflow)
    end

    def push(expression)
    $stack << expression
    end

    # Word helpers
    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 swap
    $stack[-2,2] = $stack[-2,2].reverse
    end
    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 swap; $stack[-2,2] = $stack[-2,2].reverse; end
    $stack = []

    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
    @@ -32,29 +22,17 @@ def parse(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

    else
    push statement.to_i
    when !$skip.nil? && statement != 'fi'; next
    when !$word.nil? && statement != ';'; $word << statement
    when $dictionary.has_key?(statement); $dictionary[statement].call
    else push statement.to_i
    end
    end
    rescue
    puts "Error: #{$!}"
    end
    end

    # Empty the stack
    $stack = []

    # Setup the initial dictionary
    $dictionary = {
    '+' => binary { |a, b| a + b },
    '-' => binary { |a, b| a - b },
    @@ -81,15 +59,6 @@ def parse(expression)
    'swap' => -> { begin swap rescue raise(StackUnderflow) end }
    }

    # Load all files given in the command line
    puts "Ruby Forth interpreter: enter commands at the prompt"
    while ARGV.size > 0
    open(ARGV.shift).each { |line| parse(line) }
    end

    # Here's the REPL
    while true
    print "> "
    break unless gets
    parse $_
    end
    while ARGV.size > 0; open(ARGV.shift).each { |line| parse(line) }; end
    while true; print "> "; break unless gets; parse $_; end
  2. @deadprogram deadprogram revised this gist Jan 15, 2012. 1 changed file with 0 additions and 2 deletions.
    2 changes: 0 additions & 2 deletions forth.rb
    Original file line number Diff line number Diff line change
    @@ -1,11 +1,9 @@
    #!/usr/bin/env ruby

    # Return and remove the last value of the stack array
    def pop
    $stack.pop || raise(StackUnderflow)
    end

    # Add a expression to the stack
    def push(expression)
    $stack << expression
    end
  3. @deadprogram deadprogram revised this gist Jan 15, 2012. 1 changed file with 9 additions and 33 deletions.
    42 changes: 9 additions & 33 deletions forth.rb
    Original file line number Diff line number Diff line change
    @@ -11,63 +11,44 @@ def push(expression)
    end

    # Word helpers
    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 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 swap
    $stack[-2,2] = $stack[-2,2].reverse
    end

    # Create a new word from the expressions given
    def new_word
    # If there was no word, raise with an EmptyWord error
    raise EmptyWord if $word.size < 1

    # If the definition of the word has a definition, raise NestedDefintion
    raise NestedDefinition if $word.include? ':'

    # Extract the name and expression from the word array
    name, expression = $word.shift, $word.join(' ')

    # Create a new word on the dictionary
    $dictionary[name] = -> { parse expression }

    # Reset the word variable
    $dictionary[name] = -> { parse(expression) }
    $word = nil
    end

    # Parse the expression given
    def parse(expression)
    # Output the return of the expression
    puts "=> #{expression}"
    begin
    # Attempt to split up the expression and go over each statement
    expression.split.each do |statement|
    case

    # When skip is not nil, and the statement is not 'fi' then go
    # to next statement
    when !$skip.nil? && statement != 'fi'
    next

    # When word is not nil and statement is not ';' then add
    # the statement to the word array
    when !$word.nil? && statement != ';'
    $word << statement

    # When the statement is a word, call that associated function
    when $dictionary.has_key?(statement)
    $dictionary[statement].call

    # Otherwise push it onto the stack as an integer
    else
    push statement.to_i
    end
    end
    rescue
    # If something goes wrong, print out the error
    puts "Error: #{$!}"
    end
    end
    @@ -89,16 +70,16 @@ def parse(expression)
    '|' => binary_boolean { |a, b| a || b },
    'not' => binary_boolean { |a, b| a == 0 },
    'neg' => binary { |a| -a },
    '.' => -> { puts pop },
    '..' => -> { puts $stack },
    '.' => -> { puts(pop) },
    '..' => -> { puts($stack) },
    ':' => -> { $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) },
    'dup' => -> { push($stack.last || raise(StackUnderflow)) },
    'over' => -> { push($stack.last(2).first || raise(StackUnderflow)) },
    'swap' => -> { begin swap rescue raise(StackUnderflow) end }
    }

    @@ -110,12 +91,7 @@ def parse(expression)

    # Here's the REPL
    while true
    # Print out the prompt token for the user
    print "> "

    # If nothing is given, break out of the loop
    break unless gets

    # Parse the given text
    parse $_
    end
  4. Kurtis Rainbolt-Greene revised this gist Jan 15, 2012. 1 changed file with 108 additions and 61 deletions.
    169 changes: 108 additions & 61 deletions forth.rb
    Original file line number Diff line number Diff line change
    @@ -1,74 +1,121 @@
    #!/usr/bin/env ruby

    # the stack
    $stack = []
    def pop() $stack.pop || ufe end
    def push(f) $stack<<f end
    # poor man's Exception class
    def ufe() raise("Stack underflow") end
    # lambda constructor helpers
    def mkUnary(&fn) lambda{push(fn.call(pop))} end
    def mkBinary(&fn) lambda{push(fn.call(pop,pop))} end
    def mkBool1(&b) lambda{push(b.call(pop)?1:0)} end
    def mkBool2(&b) lambda{push(b.call(pop,pop)?1:0)} end
    # the dictionary of Forth words (String => Proc)
    $dict = {'+' =>mkBinary{|a,b|a+b}, '-' =>mkBinary{|a,b|a-b},
    '*' =>mkBinary{|a,b|a*b}, '/' =>mkBinary{|a,b|a/b},
    '%' =>mkBinary{|a,b|a%b}, '=' =>mkBool2 {|a,b|a==b},
    '<' =>mkBool2 {|a,b|a<b}, '>' =>mkBool2 {|a,b|a>b},
    '&' =>mkBool2 {|a,b|a&b}, '|' =>mkBool2 {|a,b|a|b},
    'not' =>mkBool1 {|a| a==0}, 'neg' =>mkUnary {|a|-a},
    '.' =>lambda {puts pop}, '..' =>lambda {p $stack},
    ':' =>lambda {$word=[]}, ';' =>lambda {makeword},
    'pop' =>lambda {pop}, 'fi' =>lambda {$skip=nil},
    'words'=>lambda {p $dict.keys.sort},
    'if' =>lambda {$skip=true if pop==0},
    'dup' =>lambda {push $stack[-1] || ufe},
    'over' =>lambda {push $stack[-2] || ufe},
    'swap' =>lambda {begin swap rescue ufe end},
    }
    # Return and remove the last value of the stack array
    def pop
    $stack.pop || raise(StackUnderflow)
    end

    # Add a expression to the stack
    def push(expression)
    $stack << expression
    end

    # Word helpers
    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 swap
    $stack[-2,2] = $stack[-2,2].reverse
    $stack[-2,2] = $stack[-2,2].reverse
    end

    # adds user-defined word to the dictionary
    def makeword
    ($word=nil;raise("Empty word definition")) unless $word && $word.size > 1
    ($word=nil;raise("Nested word definition")) if $word.include? ':'
    name, code = $word.shift, $word.join(' ')
    $dict[name] = lambda{parse(code)}
    $word = nil
    # Create a new word from the expressions given
    def new_word
    # If there was no word, raise with an EmptyWord error
    raise EmptyWord if $word.size < 1

    # If the definition of the word has a definition, raise NestedDefintion
    raise NestedDefinition if $word.include? ':'

    # Extract the name and expression from the word array
    name, expression = $word.shift, $word.join(' ')

    # Create a new word on the dictionary
    $dictionary[name] = -> { parse expression }

    # Reset the word variable
    $word = nil
    end

    # meat and potatoes
    def parse(str)
    begin
    str.split.each do |w|
    if $skip and w != 'fi'
    next # skipping over conditional
    elsif $word and w != ';'
    $word << w # reading word definition
    elsif $dict.has_key? w
    $dict[w].call # calling pre-defined word
    else
    push w.to_i # pushing int literal
    end
    end
    rescue
    puts "Error: #{$!}"
    end
    # Parse the expression given
    def parse(expression)
    # Output the return of the expression
    puts "=> #{expression}"
    begin
    # Attempt to split up the expression and go over each statement
    expression.split.each do |statement|
    case

    # When skip is not nil, and the statement is not 'fi' then go
    # to next statement
    when !$skip.nil? && statement != 'fi'
    next

    # When word is not nil and statement is not ';' then add
    # the statement to the word array
    when !$word.nil? && statement != ';'
    $word << statement

    # When the statement is a word, call that associated function
    when $dictionary.has_key?(statement)
    $dictionary[statement].call

    # Otherwise push it onto the stack as an integer
    else
    push statement.to_i
    end
    end
    rescue
    # If something goes wrong, print out the error
    puts "Error: #{$!}"
    end
    end

    # Empty the stack
    $stack = []

    # Setup the initial dictionary
    $dictionary = {
    '+' => binary { |a, b| a + b },
    '-' => binary { |a, b| a - b },
    '*' => binary { |a, b| a * b },
    '/' => binary { |a, b| a * b },
    '%' => binary { |a, b| a * b },
    '<' => binary_boolean { |a, b| a < b },
    '>' => binary_boolean { |a, b| a > b },
    '=' => binary_boolean { |a, b| a == b },
    '&' => 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 },
    ':' => -> { $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 }
    }
    # Load all files given in the command line
    puts "Ruby Forth interpreter: enter commands at the prompt"
    # read definitions files
    while ARGV.size > 0
    File.open(ARGV.shift).each{|line| parse(line)}
    open(ARGV.shift).each { |line| parse(line) }
    end
    # REPL
    loop do
    ">> ".display
    break unless gets
    parse($_)
    # Here's the REPL
    while true
    # Print out the prompt token for the user
    print "> "

    # If nothing is given, break out of the loop
    break unless gets

    # Parse the given text
    parse $_
    end
  5. @perimosocordiae perimosocordiae created this gist Feb 10, 2010.
    74 changes: 74 additions & 0 deletions forth.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,74 @@
    #!/usr/bin/env ruby

    # the stack
    $stack = []
    def pop() $stack.pop || ufe end
    def push(f) $stack<<f end
    # poor man's Exception class
    def ufe() raise("Stack underflow") end
    # lambda constructor helpers
    def mkUnary(&fn) lambda{push(fn.call(pop))} end
    def mkBinary(&fn) lambda{push(fn.call(pop,pop))} end
    def mkBool1(&b) lambda{push(b.call(pop)?1:0)} end
    def mkBool2(&b) lambda{push(b.call(pop,pop)?1:0)} end
    # the dictionary of Forth words (String => Proc)
    $dict = {'+' =>mkBinary{|a,b|a+b}, '-' =>mkBinary{|a,b|a-b},
    '*' =>mkBinary{|a,b|a*b}, '/' =>mkBinary{|a,b|a/b},
    '%' =>mkBinary{|a,b|a%b}, '=' =>mkBool2 {|a,b|a==b},
    '<' =>mkBool2 {|a,b|a<b}, '>' =>mkBool2 {|a,b|a>b},
    '&' =>mkBool2 {|a,b|a&b}, '|' =>mkBool2 {|a,b|a|b},
    'not' =>mkBool1 {|a| a==0}, 'neg' =>mkUnary {|a|-a},
    '.' =>lambda {puts pop}, '..' =>lambda {p $stack},
    ':' =>lambda {$word=[]}, ';' =>lambda {makeword},
    'pop' =>lambda {pop}, 'fi' =>lambda {$skip=nil},
    'words'=>lambda {p $dict.keys.sort},
    'if' =>lambda {$skip=true if pop==0},
    'dup' =>lambda {push $stack[-1] || ufe},
    'over' =>lambda {push $stack[-2] || ufe},
    'swap' =>lambda {begin swap rescue ufe end},
    }
    def swap
    $stack[-2,2] = $stack[-2,2].reverse
    end
    # adds user-defined word to the dictionary
    def makeword
    ($word=nil;raise("Empty word definition")) unless $word && $word.size > 1
    ($word=nil;raise("Nested word definition")) if $word.include? ':'
    name, code = $word.shift, $word.join(' ')
    $dict[name] = lambda{parse(code)}
    $word = nil
    end
    # meat and potatoes
    def parse(str)
    begin
    str.split.each do |w|
    if $skip and w != 'fi'
    next # skipping over conditional
    elsif $word and w != ';'
    $word << w # reading word definition
    elsif $dict.has_key? w
    $dict[w].call # calling pre-defined word
    else
    push w.to_i # pushing int literal
    end
    end
    rescue
    puts "Error: #{$!}"
    end
    end
    puts "Ruby Forth interpreter: enter commands at the prompt"
    # read definitions files
    while ARGV.size > 0
    File.open(ARGV.shift).each{|line| parse(line)}
    end
    # REPL
    loop do
    ">> ".display
    break unless gets
    parse($_)
    end