Progetto

L'obiettivo del progetto è scrivere un inteprete per una versione ridotta del linguaggio Ruby.

Ruby è stato sviluppato da Y. Matsumoto nel 1995.

La motivazione principale della creazione di tale linguaggio è proporre un linguaggio di script simile a Perl o Python che utilizza un modello orientato agli oggetti.

Ruby è un linguaggio orientato agli oggetti. Per esempio, il codice seguente

class Count
  def initialize
     @count = 0
  end
  def get
    @count
  end
  def incr(n)
    @count = @count + n
  end
  def Count.test
    count = Count.new
    count.incr(1)
    count.incr(2)
  end
end
definisce una class Count. Dentro la class sono definiti 4 metodi. Il primo metodo initialize
  def initialize
     @count = 0
  end
ha uno status particolare. Sarà chiamato automaticamente alla creazione di un oggetto della class Count. Ruby utilizza il prefisso @ per indicare un campo di un oggetto e il prefisso @@ per indicare un campo statico di un oggetto. I campi non hanno bisogno di essere dichiarati (la stesso vale per le variabili locali). Dunque il metodo initialize mette a zero il campo count.

Il secondo metodo get

  def get
    @count
  end
permette di avere il valore di un campo. Un tale metodo è necessario in Ruby perché i campi non sono visibili fuori della class. Questa definizione illustra due aspetti importanti di Ruby. Primo, Ruby non è tipato. La dichiarazione non ha bisogno di dare il tipo del valore restituito dal metodo get. Secondo, tutte le istruzioni di Ruby sono considerate comme delle espressioni. Dunque il body di un metodo restituisce sempre un valore. Quindi un modo equivalente di definire lo stesso metodo sarebbe
  def get
    return @count
  end
Il terzo metodo incr
  def incr(n)
    @count = @count + n
  end
permette di incrementare il campo count. L'assegnazione in Ruby è un'espressione. Dunque il metodo incr restituisce il nuovo valore del campo count.

Il quarto metodo Count.test

  def Count.test
    count = Count.new
    count.incr(1)
    count.incr(2)
  end
definisce un metodo di class (l'equivalente di un metodo statico in Java). Il body prima definisce una variabile locale count che inizializza con un nuovo oggetto di tipo Count. In Ruby, gli oggetti sono creati chiamando il metodo new. Dopo l'inizializzazione della variabile count, il codice chiama due volte il metodo incr. Dunque il valore restituito è 3.

In Ruby tutti i valori manipolati sono degli oggetti e ci sono solamente chiamate di metodi. Per esempio, l'espressione

1 + 2 * 3
è interpretata come
1.plus(2.mult(3))

Una proprietà importante di Ruby è la sua dinamicità. In qualsiasi momento, si può aggiungere un metodo ad una class. La verifica che una chiamata di un metodo è valida è effettuata solamente a tempo di esecuzione. Per esempio, un modo equivalente di definire la class precedente sarebbe

class Count
  def initialize
     @count = 0
  end
  def get
    @count
  end
  def incr(n)
    @count = @count + n
  end
end

class Count
  def Count.test
    count = Count.new
    count.incr(1)
    count.incr(2)
  end
end
La ridichiarazione della class Count non è considerata come una ridefinizione ma come un'aggiunta.

Ruby non permette l'overloading dei metodi. Dunque con il codice seguente

class Count
  def initialize
     @count = 0
  end
  def get
    @count
  end
  def incr(n)
    @count = @count + n
  end
end

class Count
  def incr
    @count = @count + 1
  end
end
La seconda dichiarazione del metodo incr è considerata come una ridichiarazione. Un oggetto Count avrà in seguito un solo metodo incr senza parametri. Come in Java, Ruby permette di estendere una class per crearne un'altra. Una class può estendere una sola class. Per esempio,
class A 
  def initialize
     @a = 0
  end
end

class B < A 
  @@c = 0
  def initialize
     super()
     @b = 0
  end
end
crea una estensione B della class A aggiungendo un campo b ed un campo statico c.

Un meccanismo molto potente di Ruby è la possibilità di passare una chiusura nella chiamata di un metodo. Tale meccanismo permette non solo di passare una funzione come parametro di un metodo ma anche di assicurarsi che l'esecuzione di tale funzione (tramite la parola chiave yield) si farà nel contesto della creazione della funzione. Quindi una chiusura è composta da una funzione e dal suo contesto. Per esempio,

class A 
  def twice(n)
     yield(n)
     yield(n+1)
  end
  def test
    a = 0
    twice(5) do |m| 
         a = a + m
    end
    a
  end
end
il metodo test chiama il metodo twice con il parametro 5 e la chiusura parametrizzata da m
do |m| 
  a = a + m
end
che fa un'assegnazione. Nella dichiariazione del metodo twice il parametro n è passato in modo esplicito, invece la chiusura è passata in modo implicito. Quando il metodo twice eseguirà la chiusura, la variabile locale a di test sarà modificata in conseguenza. Nel nostro caso, il metodo test è equivalente a
  def test
    a = 0
    a = a + 5
    a = a + 6
    a
  end
Quindi la chiamata del metodo test ritornerà sempre 11.

Un'introduzione più completa del linguaggio può essere trovata all'indirizzo http://ruby-doc.com/docs/ProgrammingRuby/.

Progetto

La base del progetto consiste nell'avere una rappresentazione di un programma Ruby come un oggetto Java. Per esempio, al programma

class A
  def initialize
    @a = 0
  end
end
potrebbe corrispondere l'oggetto
new Programma(
  new ClassDef(new ClassName("A"), new ClassName("Object"),
    new ClassBody(
      new MethodDef(new MethodName("initialize"), new Parameter[]{},
         new Body(new Assign(new FieldName("a"), new Int(0)))
                   )
                 )
              )
             )
La base deve permettere

Esempi di programmi che si dovrebbe essere capace di scrivere sono qui. Un esempio d'inizio di implementazione è qui

La base del progetto dovrà obbligatoriamente essere consegnata prima della prova scritta dell'esame.

A partire da questa base, si può completare il progetto realizzando per esempio:

Lo studente dovrebbe seguire le convenzioni di codifica date durante il corso. Per verificare la conformità del progetto, si può usare l'archivio checkstyle.jar. Eseguendo

java -jar checkstyle.jar File.java
si genera un file report che indica la conformità del codice.
Laurent Théry
Last modified: Sun Jan 25 03:52:36 MET 2004