mirror of
https://github.com/PeterCamilleri/fOOrth
synced 2024-11-16 07:47:56 +01:00
Day 2
This commit is contained in:
parent
2bfd05ed9b
commit
ada8df9b4e
9 changed files with 319 additions and 30 deletions
|
@ -26,6 +26,7 @@ Gem::Specification.new do |s|
|
|||
s.files += Dir['tests/*.rb']
|
||||
|
||||
s.files += ['rakefile.rb',
|
||||
'sire.rb',
|
||||
'license.txt',
|
||||
'readme.txt',
|
||||
'reek.txt']
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
# The fOOrth Language System implemented via a Ruby gem.
|
||||
|
||||
require_relative 'fOOrth/exceptions'
|
||||
require_relative 'fOORth/core'
|
||||
require_relative 'fOOrth/interpreter'
|
||||
require_relative 'fOOrth/compiler'
|
||||
require_relative 'fOOrth/main'
|
||||
|
@ -13,7 +15,7 @@ module XfOOrth
|
|||
#The version of this module.
|
||||
#<br>Returns
|
||||
#* A version string; <major>.<minor>.<step>
|
||||
def version
|
||||
def self.version
|
||||
"00.06.00"
|
||||
end
|
||||
|
||||
|
@ -25,7 +27,7 @@ module XfOOrth
|
|||
#Create an new instance of a fOOrth virtual machine
|
||||
#<br>Parameters:
|
||||
#* name - An optional string that describes this virtual machine instance.
|
||||
def initialize(name='----')
|
||||
def initialize(name='-')
|
||||
@name = name
|
||||
|
||||
#Bring the major sub-systems to a known state.
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
module XfOOrth
|
||||
|
||||
class VirtualMachine
|
||||
attr_accessor :debug
|
||||
end
|
||||
|
||||
end
|
||||
|
|
16
lib/fOOrth/exceptions.rb
Normal file
16
lib/fOOrth/exceptions.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
# coding: utf-8
|
||||
|
||||
#* exceptions.rb - Exception classes for the fOOrth language interpreter.
|
||||
module XfOOrth
|
||||
#The generalize exception used by all fOOrth specific exceptions.
|
||||
class XfOOrthError < StandardError; end
|
||||
|
||||
#The exception raised to force the fOOrth language system to exit.
|
||||
class ForceExit < StandardError; end
|
||||
|
||||
#The exception raised to silently force the fOOrth language system to exit.
|
||||
class SilentExit < StandardError; end
|
||||
|
||||
#The exception raised to force the fOOrth language system to abort execution.
|
||||
class ForceAbort < StandardError; end
|
||||
end
|
|
@ -6,6 +6,17 @@ require_relative 'interpreter/data_stack'
|
|||
module XfOOrth
|
||||
|
||||
class VirtualMachine
|
||||
|
||||
#The fOOrth control stack. This is mostly used to hold information
|
||||
#relating to control structures during compile and interpretation.
|
||||
attr_reader :ctrl_stack
|
||||
|
||||
#Reset the state of the fOOrth inner interpreter.
|
||||
def interpreter_reset
|
||||
@data_stack = Array.new
|
||||
@ctrl_stack = Array.new
|
||||
@start_time = Time.now
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -2,8 +2,83 @@
|
|||
|
||||
#* data_stack.rb - The fOOrth language system data stack.
|
||||
module XfOOrth
|
||||
|
||||
class VirtualMachine
|
||||
end
|
||||
#The fOOrth data stack. This is the primary means used to hold data
|
||||
#for processing.
|
||||
attr_reader :data_stack
|
||||
|
||||
#Add an entry to the data stack.
|
||||
#<br>Parameters:
|
||||
#* datum - The data to be added to the data stack.
|
||||
def push(datum)
|
||||
@data_stack << datum
|
||||
end
|
||||
|
||||
#Remove the "top" entry from the data stack.
|
||||
#<br>Returns:
|
||||
#* The "top" element of the data stack.
|
||||
#<br>Note:
|
||||
#* If the stack is empty this will raise a XfOOrthError exception.
|
||||
def pop
|
||||
unless @data_stack.length >= 1
|
||||
fail XfOOrthError, "Data Stack Underflow: pop"
|
||||
end
|
||||
|
||||
@data_stack.pop
|
||||
end
|
||||
|
||||
#Remove multiple entries from the "top" of the data stack.
|
||||
#<br>Parameters:
|
||||
#* count - the number of elements to be returned.
|
||||
#<br>Returns:
|
||||
#* An array containing the "top" count elements of the data stack.
|
||||
#<br>Note:
|
||||
#* Raises a XfOOrthError exception if the stack has too few data.
|
||||
def popm(count)
|
||||
unless @data_stack.length >= count
|
||||
fail XfOOrthError, "Data Stack Underflow: popm"
|
||||
end
|
||||
|
||||
@data_stack.pop(count)
|
||||
end
|
||||
|
||||
#Remove the "top" entry from the data stack as a boolean.
|
||||
#<br>Returns:
|
||||
#* The "top" element of the data stack as a boolean
|
||||
#<br>Note:
|
||||
#* If the stack is empty this will raise a XfOOrthError exception.
|
||||
def pop?
|
||||
pop.to_fOOrth_b
|
||||
end
|
||||
|
||||
#Read an entry from the data stack without modify that stack.
|
||||
#<br>Parameters:
|
||||
#* index - The (optional) entry to be retrieved. 1 corresponds to the
|
||||
# "top" of the stack, 2 the next element, etc.
|
||||
# This parameter defaults to 1.
|
||||
#<br>Returns:
|
||||
#* The element specified from the data stack.
|
||||
#<br>Note:
|
||||
#* Attempting to access an element deeper than the number of elements
|
||||
# on the stack will fail with an XfOOrthError exception.
|
||||
def peek(index=1)
|
||||
unless @data_stack.length >= index
|
||||
fail XfOOrthError, "Data Stack Underflow: Peek"
|
||||
end
|
||||
@data_stack[-index]
|
||||
end
|
||||
|
||||
#Read an entry from the data stack as a boolean without modify that stack.
|
||||
#<br>Parameters:
|
||||
#* index - The (optional) entry to be retrieved. 1 corresponds to the "top"
|
||||
# of the stack, 2 the next element, etc. This parameter defaults to 1.
|
||||
#<br>Returns:
|
||||
#* The element specified from the data stack as a boolean.
|
||||
#<br>Note:
|
||||
#* Attempting to access an element deeper than the number of elements on
|
||||
# the stack will fail with an XfOOrthError exception.
|
||||
def peek?(index=1)
|
||||
peek(index).to_fOOrth_b
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,35 +5,124 @@ require 'getoptlong'
|
|||
#* main.rb - The entry point for a stand-alone fOOrth session.
|
||||
module XfOOrth
|
||||
|
||||
class VirtualMachine
|
||||
@library_loaded = false
|
||||
@library_loaded = false
|
||||
|
||||
#Has the fOOrth run time library been loaded?
|
||||
def library_loaded?
|
||||
@library_loaded
|
||||
end
|
||||
#Has the fOOrth run time library been loaded?
|
||||
def self.library_loaded?
|
||||
@library_loaded
|
||||
end
|
||||
|
||||
#The fOOrth run time library has been loaded!
|
||||
def library_loaded
|
||||
@library_loaded = true
|
||||
end
|
||||
#The fOOrth run time library has been loaded!
|
||||
def self.library_loaded
|
||||
@library_loaded = true
|
||||
end
|
||||
|
||||
#The starting point for an interactive fOOrth programming session.
|
||||
#This method only returns when the session is closed.
|
||||
#<br>To launch a fOOrth session, simply use:
|
||||
# XfOOrth::VirtualMachine.new.main
|
||||
def main
|
||||
#The starting point for an interactive fOOrth programming session.
|
||||
#This method only returns when the session is closed.
|
||||
#<br>Returns:
|
||||
#* The virtual machine used to run the session.
|
||||
#<br>To launch a fOOrth interactive session, simply use:
|
||||
# XfOOrth::main
|
||||
def self.main
|
||||
vm = VirtualMachine.new('main')
|
||||
|
||||
begin
|
||||
|
||||
loop do
|
||||
begin
|
||||
running ||= start_up(vm)
|
||||
vm.eceute_console
|
||||
|
||||
rescue ForceAbort => forced_abort
|
||||
vm.display_abort(forced_abort)
|
||||
break unless running
|
||||
end
|
||||
end
|
||||
|
||||
rescue Interrupt
|
||||
puts "\nProgram interrupted. Exiting fOOrth."
|
||||
|
||||
rescue ForceExit
|
||||
puts "\nQuit command received. Exiting fOOrth."
|
||||
|
||||
rescue SilentExit
|
||||
puts
|
||||
|
||||
rescue Exception => err
|
||||
puts "\n#{err.class.to_s.gsub(/.*::/, '')} detected: #{err}"
|
||||
puts err.backtrace
|
||||
|
||||
end
|
||||
|
||||
#Process the command line arguments. A string is returned containing
|
||||
#fOOrth commands to be executed after the dictionary is loaded.
|
||||
#<br>Returns
|
||||
#A string of fOOrth commands to be executed after the dictionary is loaded.
|
||||
def process_command_line_options
|
||||
vm
|
||||
end
|
||||
|
||||
#Perform one time start-up actions.
|
||||
def self.start_up(vm)
|
||||
announcement
|
||||
vm.debug = false
|
||||
vm.exec_str.process_command_line_options
|
||||
true
|
||||
end
|
||||
|
||||
#Display the start-up messages for the interactive session.
|
||||
def self.announcement
|
||||
puts "fOOrth Reference Implementation Version: #{XfOOrth.version}"
|
||||
fmt = '%Y-%m-%d at %I:%M%P'
|
||||
puts "Session began on date: #{Time.now.strftime(fmt)}"
|
||||
end
|
||||
|
||||
#Process the command line arguments. A string is returned containing
|
||||
#fOOrth commands to be executed after the dictionary is loaded.
|
||||
#<br>Returns
|
||||
#A string of fOOrth commands to be executed after the dictionary is loaded.
|
||||
def self.process_command_line_options
|
||||
begin
|
||||
defer, found = "", false
|
||||
|
||||
opts = GetoptLong.new(
|
||||
[ "--help", "-h", "-?", GetoptLong::NO_ARGUMENT ],
|
||||
[ "--load", "-l", GetoptLong::REQUIRED_ARGUMENT ],
|
||||
[ "--debug", "-d", GetoptLong::NO_ARGUMENT ],
|
||||
[ "--quit", "-q", GetoptLong::NO_ARGUMENT ],
|
||||
[ "--words", "-w", GetoptLong::NO_ARGUMENT ])
|
||||
|
||||
# Translate the parsed options into fOOrth.
|
||||
opts.each do |opt, arg|
|
||||
unless found
|
||||
puts; found = true
|
||||
end
|
||||
|
||||
case opt
|
||||
when "--debug"
|
||||
@debug = true
|
||||
when "--load"
|
||||
defer << "load\"#{arg}\" "
|
||||
when "--quit"
|
||||
defer << ")quit "
|
||||
when "--words"
|
||||
defer << ")words "
|
||||
else
|
||||
fail SilentExit
|
||||
end
|
||||
end
|
||||
|
||||
puts if found
|
||||
|
||||
rescue Exception
|
||||
puts
|
||||
puts "fOOrth available options:"
|
||||
puts
|
||||
puts "--help -h -? Display this message and exit."
|
||||
puts "--load -l <filename> Load the specified fOOrth source file."
|
||||
puts "--debug -d Default to debug ON."
|
||||
puts "--quit -q Quit after processing the command line."
|
||||
puts "--words -w List the current vocabulary."
|
||||
puts
|
||||
raise SilentExit
|
||||
end
|
||||
|
||||
defer
|
||||
end
|
||||
|
||||
end
|
28
rakefile.rb
28
rakefile.rb
|
@ -4,21 +4,26 @@
|
|||
require 'rake/testtask'
|
||||
require 'rdoc/task'
|
||||
|
||||
#Generate internal documentation with rdoc.
|
||||
RDoc::Task.new do |rdoc|
|
||||
rdoc.rdoc_dir = "rdoc"
|
||||
|
||||
#List out all the files to be documented.
|
||||
rdoc.rdoc_files = ["lib/fOOrth.rb",
|
||||
"lib/fOOrth/exceptions.rb",
|
||||
"lib/fOOrth/core.rb",
|
||||
"lib/fOOrth/interpreter.rb",
|
||||
"lib/fOOrth/interpreter/data_stack.rb",
|
||||
"lib/fOOrth/compiler.rb",
|
||||
"lib/fOOrth/main.rb",
|
||||
"license.txt", "README.txt"]
|
||||
"license.txt",
|
||||
"README.txt"]
|
||||
|
||||
#Make all access levels visible.
|
||||
rdoc.options << '--visibility' << 'private'
|
||||
end
|
||||
|
||||
#Run the fOOrth test suite.
|
||||
Rake::TestTask.new do |t|
|
||||
#List out all the test files.
|
||||
t.test_files = []
|
||||
|
@ -26,15 +31,12 @@ Rake::TestTask.new do |t|
|
|||
t.verbose = false
|
||||
end
|
||||
|
||||
#Run a scan for smelly code!
|
||||
task :reek do |t|
|
||||
`reek --no-color lib > reek.txt`
|
||||
end
|
||||
|
||||
def eval_puts(str)
|
||||
puts str
|
||||
eval str
|
||||
end
|
||||
|
||||
#Fire up an IRB session with fOOrth preloaded.
|
||||
task :console do
|
||||
require 'irb'
|
||||
require 'irb/completion'
|
||||
|
@ -43,3 +45,17 @@ task :console do
|
|||
ARGV.clear
|
||||
IRB.start
|
||||
end
|
||||
|
||||
#Run the Simple Interactive Ruby Environment.
|
||||
task :sire do
|
||||
require './lib/fOOrth'
|
||||
require './sire'
|
||||
SIRE.new.run_sire
|
||||
end
|
||||
|
||||
#Run an Interactive fOOrth Session.
|
||||
task :run do
|
||||
require './lib/fOOrth'
|
||||
ARGV.clear
|
||||
XfOOrth::main
|
||||
end
|
||||
|
|
78
sire.rb
Normal file
78
sire.rb
Normal file
|
@ -0,0 +1,78 @@
|
|||
# coding: utf-8
|
||||
# A Simple Interactive Ruby Environment
|
||||
|
||||
require 'readline'
|
||||
require 'pp'
|
||||
|
||||
include Readline
|
||||
|
||||
class Object
|
||||
#Generate the class lineage of the object.
|
||||
def classes
|
||||
begin
|
||||
klass = self
|
||||
|
||||
begin
|
||||
klass = klass.class unless klass.instance_of?(Class)
|
||||
print klass
|
||||
klass = klass.superclass
|
||||
print " < " if klass
|
||||
end while klass
|
||||
|
||||
puts
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class SIRE
|
||||
#Set up the interactive session.
|
||||
def initialize
|
||||
@done = false
|
||||
@running = false
|
||||
|
||||
puts "Welcome to a Simple Interactive Ruby Environment\n"
|
||||
puts "Use command 'q' to quit.\n\n"
|
||||
end
|
||||
|
||||
#Quit the interactive session.
|
||||
def q
|
||||
@done = true
|
||||
puts
|
||||
"Bye bye for now!"
|
||||
end
|
||||
|
||||
#Run the interactive session.
|
||||
def run_sire
|
||||
until @done
|
||||
begin
|
||||
line = readline('SIRE>', true)
|
||||
@running = true
|
||||
result = eval line
|
||||
@running = false
|
||||
pp result unless line.length == 0
|
||||
|
||||
rescue Interrupt => e
|
||||
if @running
|
||||
@running = false
|
||||
puts "\nExecution Interrupted!"
|
||||
puts "\n#{e.class} detected: #{e}\n"
|
||||
puts e.backtrace
|
||||
else
|
||||
puts "\nI'm outta here!'"
|
||||
@done = true
|
||||
end
|
||||
|
||||
puts "\n"
|
||||
|
||||
rescue Exception => e
|
||||
puts "\n#{e.class} detected: #{e}\n"
|
||||
puts e.backtrace
|
||||
puts
|
||||
end
|
||||
end
|
||||
|
||||
puts "\n\n"
|
||||
end
|
||||
|
||||
end
|
||||
|
Loading…
Reference in a new issue