#!/usr/bin/env ruby
#####################################################################
#
# mdt.rb (Implementazione della Macchina di Turing)
#
#
# 06/12/2007 - edited by Invisigoth <njprincess_autobot@yahoo.it>"
#
# mdt.rb is distributed under the terms of the GNU General Public
# License. See the file COPYING for details.
#
######################################################################
$prog = "mdt.rb - ver 1.0 (distributed under the terms of the GNU General Public License)"
$author = "edited by Invisigoth <njprincess_autobot@yahoo.it>"
$hstr = "\n*** #{$prog}\n*** #{$author}"
$CLEAR_STR = "\e[H\e[2J"
require 'command'
def globalM_Authors
puts $hstr
end
class RegolaNonTrovataError < StandardError
end
class FormatError < StandardError
end
class Direzione
SINISTRA = -1
DESTRA = +1
NULLA = 0
def Direzione.to_s(str)
if str.to_i <= Direzione.SINISTRA then
return "Sinistra"
elsif str.to_i >= Direzione.DESTRA then
return "Destra"
else
#if str.to_i == Direzione.NULLA then
return "Nulla"
end
end
def Direzione.equal?(dir1, dir2)
if( ((dir1.to_i * dir2.to_i) == 0) || ((dir1.to_i * dir2.to_i) > 0) )then
return true
elsif( (dir1.to_i * dir2.to_i) < 0) then
return false
end
end
def Direzione.SINISTRA
return SINISTRA
end
def Direzione.DESTRA
return DESTRA
end
def Direzione.NULLA
return NULLA
end
end
class Nastro
attr_accessor :nastro_win
attr_accessor :blank
def initialize(str = "", pos = 0)
@hashTape = {}
@blank = "b"
@nastro_win = 5
@max = 0
@min = 0
@pos = 0
self.pos = pos
upgradeMinMax
end
def read
return getCell(@pos)
end
def write(sim)
setCell(@pos, sim)
end
def muovi(dir)
if Direzione.equal?(dir.to_i, Direzione.DESTRA) then
shiftDestra
elsif Direzione.equal?(dir.to_i, Direzione.SINISTRA) then
shiftSinistra
end
end
def blank=(sim)
@blank = sim.split("")[0] if !sim.nil?
end
def nastro_win=(win)
@nastro_win = win.to_i
end
def pos
@pos
end
def mov
end
def shiftDestra
self.pos = (@pos + Direzione.DESTRA)
end
def shiftSinistra
self.pos = (@pos + Direzione.SINISTRA)
end
def clear
@hashTape = {}
@pos = 0
end
def from_s(str, pos = 0)
i = pos.to_i
str.to_s.split("").each {|c| setCell(i, c); i +=1}
end
def to_s
str = ""
(@min..@max).each do |i|
retCell = getCell(i)
if i != @pos
str += retCell
else
str += "[" + retCell +"]"
end
end
return str
end
def inspect
str = ""
(@min..@max).each do |i|
retCell = getCell(i)
if i != @pos
str += "#{retCell.to_s}"
else
str += "[#{retCell.to_s}]"
end
end
return "#{self.class} |#{str}|"
end
private
def getCell(pos)
retVal = @hashTape[pos.to_i]
if retVal.nil? then
return @blank
else
return retVal #.to_s[0] <--- è gia garantito nel Nastro.setCell(pos, sim)
end
end
def setCell(pos, sim)
@hashTape[pos.to_i] = sim.to_s
end
def pos=(pos)
@pos = pos.to_i if !pos.nil?
upgradeMinMax
end
protected
def upgradeMinMax
upgradeMinMax_slide
end
def upgradeMinMax_center
@max = @pos + @nastro_win
@min = @pos - @nastro_win
end
def upgradeMinMax_slide
if @pos > (@max - @nastro_win) then
@max = @pos + @nastro_win
end
if @pos < (@min + @nastro_win) then
@min = @pos - @nastro_win
end
end
end
class Stato
INIZIALE = 0
FINALE = -1
def initialize(stato = INIZIALE)
self.value = stato.to_i
@haltState = false
end
def value=(stato)
@value = stato.to_i
end
def value
@value
end
def setHalt
@haltState = true
end
def is_halt?
@haltState
end
def is_iniziale?
if @value == INIZIALE then
return true
else
return false
end
end
def is_finale?
if @value <= FINALE then
return true
else
return false
end
end
def is_ordinario?
if @value >= 1 then
return true
else
return false
end
end
def ==(stato)
if(stato.class == Fixnum)
if @value == stato.to_s then
return true
else
return false
end
elsif(stato.class == Stato)
if @value == stato.value.to_i then
return true
else
return false
end
else
return false
end
end
def to_s
str = ""
str += "#{self.class}: "
if is_halt? then
str += "HALT"
else
str += "#{@value} ("
if is_finale? then
str += "FINALE"
elsif is_iniziale? then
str += "INIZIALE"
else
str += "ORDINARIO"
end
end
str +=")"
return str
end
def Stato.INIZIALE
INIZIALE
end
def Stato.FINALE
FINALE
end
end
class RegolaIn
attr_accessor :stato, :simbolo
def initialize
@stato = Stato.new
@simbolo = ""
end
def ==(r2)
raise ArgumentError.new if r2.class != RegolaIn
if (r2.stato == @stato) && (r2.simbolo == @simbolo) then
return true
else
return false
end
end
def to_s
"<Regola in: #{@stato.to_s}, simbolo: #{@simbolo.to_s}>"
end
end
class RegolaOut
attr_accessor :id, :stato, :simbolo, :movimento
def initialize
@stato = Stato.new
#@simbolo = Simbolo.new
@simbolo = ""
@movimento = Direzione.NULLA
end
def ==(r2)
raise ArgumentError.new if r2.class != RegolaIn
if (r2.stato == @stato) && (r2.simbolo == @simbolo) && (r2.movimento == @movimento) then
return true
else
return false
end
end
def to_s
"#{self.class}: #{@stato.to_s}, simbolo: #{@simbolo}, movimento: #{Direzione.to_s(@movimento)}"
end
end
class RegolaTransizione
attr_accessor :id, :regolaIn, :regolaOut
def initialize(regolaIn = nil, regolaOut = nil)
@id = 0
@regolaIn = regolaIn
@regolaOut = regolaOut
end
def from_s(str)
regolaTransizione = RegolaTransizione.getRegolaFromS(str)
@id = regolaTransizione.id
@regolaIn = regolaTransizione.regolaIn
@regolaOut = regolaTransizione.regolaOut
end
def to_s
return "#{@id}: #{@regolaIn.stato.value} x #{@regolaIn.simbolo} --> #{@regolaOut.stato.value} x #{@regolaOut.simbolo} x #{@regolaOut.movimento.to_s}"
end
def RegolaTransizione.getRegolaFromS(str)
raise FormatError.new(":String is nil?") if str.nil?
if str.downcase =~ /(\d+):\s*(\d|-\d)\s*x\s*(\w)\s*->\s*(\d|-\d)\s*x\s*(\w)\s*x\s*(sinistra|destra|nulla|sx|dx|null|nil|s|d|n)/ then
regolaIn = RegolaIn.new
regolaIn.stato = Stato.new $2
#regolaIn.simbolo = Simbolo.new $3
regolaIn.simbolo = $3
if ($6== "sinistra") || ($6 == "sx") || ($6 == "s") then
dir = Direzione.SINISTRA
elsif ($6 == "destra") || ($6 == "dx") || ($6 == "d") then
dir = Direzione.DESTRA
elsif ($6 == "nulla") || ($6 == "null") || ($6 == "nil") || ($6 == "n") then
dir = Direzione.NULLA
else
raise FormatError.new
end
regolaOut = RegolaOut.new
regolaOut.stato = Stato.new $4
#regolaOut.simbolo = Simbolo.new $5
regolaOut.simbolo = $5
regolaOut.movimento = dir
regolaTransizione = RegolaTransizione.new(regolaIn, regolaOut)
#Questo esula dalla concezione OO ma essendo superflua come cosa la lasciamo cosi -- INIZIO
regolaTransizione.id = $1.to_i
regolaTransizione.regolaOut.id = $1.to_i
#Questo esula dalla concezione OO ma essendo superflua come cosa la lasciamo cosi -- FINE
return regolaTransizione
else
raise FormatError.new(str)
end
end
end
class Programma
attr_reader :numRegole
def initialize
@matriceTransizione = []
@numRegole = 1
end
def addRegola(regola)
raise ArgumentError.new if regola.class != RegolaTransizione
@matriceTransizione << regola
@numRegole += 1
end
def procRegola(regolaIn)
raise ArgumentError.new if regolaIn.class != RegolaIn
@matriceTransizione.each do |regolaTransizione|
if regolaTransizione.regolaIn == regolaIn then
return regolaTransizione.regolaOut
end
end
raise RegolaNonTrovataError.new(regolaIn.to_s)
end
def to_s
str = ""
str += "#{self.class}:\n"
@matriceTransizione.each do |regola|
str += regola.to_s + "\n"
end
return str
end
end
class CaricatoreProgramma
def load(str)
raise ArgumentError.new if (str.class != Array) || (str.class != String)
if str.class == String then
str = str.split "\n"
end
programma = Programma.new
a.each do |r|
regolaTransizione = RegolTransizione.new
regolaTransizione.from_s(r)
programma.addRegola(regolaTransizione)
end
return programma
end
end
class MdT
attr_accessor :programma, :stato, :nastro, :logger, :step_time
def initialize(programma, stato, nastro, logger)
raise ArgumentError.new if (programma.class != Programma)
raise ArgumentError.new if (stato.class != Stato)
raise ArgumentError.new if (nastro.class != Nastro)
raise ArgumentError.new if (logger.class != Logger)
@programma = programma
@stato = stato
@nastro = nastro
@logger = logger
@RegolaIn = RegolaIn.new
@RegolaOut = RegolaOut.new
@step = 0
@step_time = 0.4
end
def step_time=(time)
@step_time = time.to_f
end
def run
@logger.puts "\nINIZIO COMPUTAZIONE"
@logger.puts self.inspect
@logger.puts @nastro
while( (!@stato.is_finale?) && (!@stato.is_halt?) )do
procPasso
end
@logger.puts "\nFINE COMPUTAZIONE"
@logger.puts self.to_s
@logger.puts @nastro
end
def procPasso
@regolaIn = getRegolaIn
begin
@regolaOut = @programma.procRegola(@regolaIn)
procRegolaOut @regolaOut
@step +=1
printComputazione
stepAction
rescue RegolaNonTrovataError => e
@logger.puts "Regola non trovata: <#{@regolaIn.stato.value} x #{@regolaIn.simbolo.to_s}>. La MdT và in HALT"
@stato.setHalt
end
end
def to_s
"La MdT si trova in stato [#{@stato}], simbolo [#{@nastro.read}], posizione [#{@nastro.pos}], numero passi [#{@step}]"
end
def inspect
"#<#{self.class}: [#{@stato}, testina: [ pos: #{@nastro.pos}, simbolo: #{@nastro.read}]>"
end
def close
@logger.closeAll
end
private
def getRegolaIn
regolaIn = RegolaIn.new
regolaIn.stato = @stato
#regolaIn.simbolo = @nastro.getCell(@testina.pos)
regolaIn.simbolo = @nastro.read
return regolaIn
end
def getRegolaOut(regolaIn)
raise ArgumentError if regolaIn.class != RegolaIn
regolaOut = @programma.procRegola(regolaIn)
return regolaOut
end
def procRegolaOut(regolaOut)
raise ArgumentError if regolaOut.class != RegolaOut
@stato = regolaOut.stato
#@nastro.setCell(@testina.pos, regolaOut.simbolo)
@nastro.write(regolaOut.simbolo)
#@testina.muovi(regolaOut.movimento)
@nastro.muovi(regolaOut.movimento)
end
def printComputazione
@logger.puts "-"*30
@logger.puts "Passo ##{@step}"
@logger.puts "[Regola n. #{@regolaOut.id}] #{@regolaIn.stato.value} x #{@regolaIn.simbolo} --> #{@regolaOut.stato.value} x #{@regolaOut.simbolo} x #{@regolaOut.movimento.to_s}"
#@logger.puts "NASTRO : #{@nastro.to_s}"
@logger.puts "#{@nastro.to_s}"
@logger.puts "-"*30
end
def stepAction
sleepStepTime()
end
def sleepStepTime()
sleep @step_time
end
end
class MainController
def initialize(mdtConfFile = nil)
@logger = Logger.new
@logger.addLog $stdout
@console = Console.new($stdin)
@programma = Programma.new
@stato = Stato.new
@nastro = Nastro.new
@mdtBase = MdT.new @programma, @stato, @nastro, @logger
@mdt = MdT.new @programma, @stato, @nastro, @logger
if !mdtConfFile.nil? then
readConfFile(mdtConfFile)
end
@mdt = MdT.new @programma, @stato, @nastro, @logger
end
def mainLoop
loop do
print "comando: "
cmd = @console.gets.chomp!
procCmd(cmd)
end
end
private
def readConfFile(pathname)
begin
f = File.open pathname
ar = f.readlines
f.close
ar = ar - [""]
ar.each { |cmd| procCmd(cmd.chomp!) }
rescue FormatError => e
Logger.puts "Errore di Formato del file #{pathname}. [#{e.inspect}"
rescue StandardError => e
Logger.puts "Errore nella lettura del file #{pathname}. [#{e.inspect}]"
end
end
def procCmd(cmd)
raise FormatError.new(":String is nil") if cmd.nil?
cmd_downcase = cmd.downcase
if cmd_downcase =~ /add\s+(.+)/ then
addRegola($1)
elsif cmd_downcase =~ /set\s+(stato|nastro|nastro_win|step_time|blank)\s+(.+)/
setComponent($1,$2)
elsif cmd_downcase =~ /view\s+(programma|stato|nastro|nastro_win|step_time|blank|all)/ then
viewComponent($1)
#QUESTO VA DOPO PERCHE ALTRIMENTI INTERCETTA I COMANDI "VIEW" CON ARGOMENTO
elsif cmd_downcase =~ /view/ then
viewComponent("all")
##########################################################################
elsif cmd_downcase =~ /reset\s+(programma|stato|nastro|testina|all)/ then
resetComponent($1)
elsif cmd_downcase == "run" then
@mdt.run
elsif cmd_downcase == "step" then
@mdt.procPasso
elsif cmd_downcase == "exit" then
@mdt.close
controllerExit(0, $hstr)
elsif cmd_downcase =~ /output (.+)/ then
begin
f = File.new $1, "w"
rescue
Logger.puts "Errore nell'apertura del file"
end
@mdt.logger.addLog(f)
elsif cmd_downcase == "about" then
globalM_Authors
elsif cmd_downcase == "help" then
@mdt.logger.puts "Lista comandi:"
@mdt.logger.puts " add <stato> x <simbolo> -> <stato> x <simbolo> x <movimento> - Aggiunge una regola al programma"
@mdt.logger.puts " set <componente> <valore> - cambia le configurazioni del componente"
@mdt.logger.puts " view / view <componente> - visualizza il componente, o tutti se si omette il parametro"
@mdt.logger.puts " load <file> - usa il file come sorgente per l'immissione di comandi"
@mdt.logger.puts " output <file> - aggiunge il file alla lista di output"
@mdt.logger.puts " step - esegue un singolo passo computazionale"
@mdt.logger.puts " run - avvia il processo computazionale fino a giungere allo stato finale o HALT"
@mdt.logger.puts " reset <componente> / reset all - resetta il componente / resetta tutta la MdT"
@mdt.logger.puts " about - informazioni sul programma e l'autore"
@mdt.logger.puts " exit - esce dal programma"
elsif cmd_downcase =~ /rload\s+(\w+)/ then
resetComponent("all")
readConfFile($1)
elsif cmd_downcase =~ /load\s+(\w+)/ then
readConfFile($1)
else
@mdt.logger.puts "Comando sconosciuto. 'help' per lista comandi."
end
end
def controllerExit(code, str = "")
Logger.print str +"\n"
exit code
end
private
def setComponent(strDef, par)
lstrDef = strDef.to_s
lpar = par.to_s
if (lstrDef == "stato") && (lpar =~ /\d+/) then
@mdt.stato.value = tmpStr
#@mdt.logger.puts "updated: #{@mdt.stato}"
elsif (lstrDef == "nastro") then
if(lpar =~ /(.+)\s+(\d+)/) then
@mdt.nastro.from_s($1, $2)
elsif(lpar =~ /(.+)/) then
@mdt.nastro.from_s($1)
end
#@mdt.logger.puts "updated: #{@mdt.nastro}.inspect"
elsif (lstrDef == "blank") && (lpar =~ /(.)/) then
@mdt.nastro.blank = lpar
#@mdt.logger.puts "Blank = #{@mdt.nastro.blank}"
elsif (lstrDef == "nastro_win") && (lpar =~ /(\d+)/) then
@mdt.nastro.nastro_win = lpar
elsif (lstrDef == "step_time") && (lpar =~ /(\d+)/) then
@mdt.step_time = lpar
end
end
def viewComponent(strDef)
lstrDef = strDef.to_s
if (lstrDef == "stato") then
@mdt.logger.puts @mdt.stato
elsif (lstrDef == "nastro") then
@mdt.logger.puts @mdt.nastro
elsif (lstrDef == "programma") then
@mdt.logger.puts @mdt.programma
elsif (lstrDef == "blank") then
@mdt.logger.puts "Blank: #{@mdt.nastro.blank}"
elsif (lstrDef == "step_time") then
@mdt.logger.puts "Step time: #{@mdt.step_time}"
elsif (lstrDef == "nastro_win") then
@mdt.logger.puts "Nastro Win: #{@mdt.nastro.nastro_win}"
elsif (lstrDef == "all") then
@mdt.logger.puts @mdt.stato
@mdt.logger.puts @mdt.programma
@mdt.logger.puts @mdt.nastro.inspect
end
end
def resetComponent(strDef)
lstrDef = strDef.to_s
if (lstrDef == "stato") then
@mdt.stato = Stato.new
#@mdt.logger.puts "Reset done: #{@mdt.stato}"
elsif (lstrDef == "nastro") then
@mdt.nastro = MemoriaNastro.new
#@mdt.logger.puts "Reset done: #{@mdt.nastro}"
elsif (lstrDef == "programma") then
@mdt.programma = Programma.new
#@mdt.logger.puts "Reset done: #{@mdt.programma}"
elsif (lstrDef == "all") then
@mdt.stato = Stato.new
@mdt.nastro = Nastro.new
@mdt.programma = Programma.new
@mdt.logger.puts "Reset totale"
end
end
def addRegola(str)
lstr = str.to_s
regolaTransizione = RegolaTransizione.new
begin
regolaTransizione.from_s("#{@mdt.programma.numRegole.to_s}: #{lstr}")
@mdt.programma.addRegola(regolaTransizione)
@mdt.logger.puts "Regola aggiunta."
rescue FormatError => e
@mdt.logger.puts "Formato regola errato. 'help' per lista comandi."
end
end
end
begin
#mainLoop
mainController = MainController.new(ARGV.first)
mainController.mainLoop
rescue Interrupt
puts "Interrupt catched...exiting"
exit -1
end