Ada è un linguaggio progettato dal DoD statunitense per sistemi safety-critical e real-time. Ogni revisione espande le capacità mantenendo la retrocompatibilità e la verificabilità formale.
| Versione | Anno | Standard | Novità Principali |
|---|---|---|---|
| Ada 83 | 1983 | MIL-STD-1815 / ISO 8652:1987 | L'originale. Strong typing, packages, tasking nativo, generics, exception handling, representation clauses. Progettato per embedded DoD. |
| Ada 95 | 1995 | ISO/IEC 8652:1995 | OOP: tagged types, ereditarietà, polimorfismo con dispatching dinamico. Protected objects. Child packages. Annexes specializzati (Real-Time, Distributed, Systems Programming, Safety & Security). |
| Ada 2005 | 2007 | ISO/IEC 8652:1995/Amd 1:2007 | Interfacce (multiple inheritance of interfaces). Prefixed notation Obj.Method. Ada.Containers (Vectors, Maps, Sets). Access su subprograms migliorato. Limited aggregates. Nested generics semplificati. |
| Ada 2012 | 2012 | ISO/IEC 8652:2012 | Design by Contract: Pre, Post, Type_Invariant come aspect. Expression functions. Conditional/quantified expressions (if/case expressions, for all/for some). Flexible containers migliorati. Aspect specifications unificate. |
| Ada 2022 | 2023 | ISO/IEC 8652:2023 | Parallel blocks/loops. Iteratori generalizzati e procedural iterators. Light-weight user-defined literals. Delta aggregates. Target name @. 'Reduce attribute. Dichiarazione variabili in-place. Contratti rafforzati. Miglioramenti containers. |
Ogni programma Ada ha un sottoprogramma principale (procedura senza package obbligatorio).
Il linguaggio è case-insensitive. I statement terminano con ;.
with Ada.Text_IO; -- importa il package di I/O testuale procedure Hello is begin Ada.Text_IO.Put_Line ("Hello, Ada World!"); end Hello;
with Ada.Text_IO; use Ada.Text_IO; -- rende visibili i nomi direttamente with Ada.Integer_Text_IO; use Ada.Integer_Text_IO; procedure Main is X : Integer := 42; begin Put ("Valore: "); Put (X, Width => 0); -- stampa senza padding New_Line; end Main;
-- Questo è un commento (unico stile: doppio trattino fino a fine riga) -- Ada NON ha commenti multi-riga: si usano più righe con --
#include in C).
use rende i nomi visibili senza qualifica (opzionale, preferire la qualifica per chiarezza).
Ada ha un sistema di tipi fortemente tipizzato: non esistono conversioni implicite tra tipi diversi.
| Tipo | Descrizione | Esempio |
|---|---|---|
Integer |
Intero con segno (almeno 16 bit, tipicamente 32) | X : Integer := -42; |
Natural |
Subtype di Integer: 0 .. Integer'Last | N : Natural := 0; |
Positive |
Subtype di Integer: 1 .. Integer'Last | P : Positive := 1; |
Float |
Virgola mobile (almeno 6 cifre significative) | F : Float := 3.14; |
Long_Float |
Virgola mobile a doppia precisione | D : Long_Float := 3.14159_26535; |
Character |
Singolo carattere (Latin-1) | C : Character := 'A'; |
Wide_Character |
Carattere Unicode BMP | W : Wide_Character := 'Ω'; |
Boolean |
True / False | B : Boolean := True; |
String |
Array di Character a lunghezza fissa | S : String (1..5) := "Hello"; |
Duration |
Tipo fixed-point per intervalli di tempo | D : Duration := 1.5; |
-- Tipo intero con range esplicito (il compilatore sceglie la rappresentazione) type Day_Number is range 1 .. 31; -- Tipo floating-point con precisione minima (cifre significative) type Velocity is digits 8; -- almeno 8 cifre type Probability is digits 6 range 0.0 .. 1.0; -- Tipo fixed-point (aritmetica esatta, usato in sistemi finanziari/embedded) type Voltage is delta 0.001 range 0.0 .. 5.0; -- ordinary fixed type Money is delta 0.01 digits 12; -- decimal fixed -- Tipo modular (aritmetica unsigned con wrapping) type Byte is mod 256; -- 0..255, overflow fa wrap type Word is mod 2**16; -- 0..65535
32 a un Day_Number solleva
Constraint_Error. Questo previene intere classi di bug tipiche di C/C++.
procedure Vars_Demo is -- Variabili (dichiarate nella parte dichiarativa, prima di begin) Count : Integer := 0; -- inizializzata Name : String (1..10); -- non inizializzata (attenzione!) Active : Boolean := False; -- Costanti (valore immutabile) Pi : constant Float := 3.14159_26535; Max_Len : constant Integer := 1024; -- Costante con tipo dedotto dal contesto (named number) Factor : constant := 2 * 3; -- universal_integer, nessun tipo specifico -- Rinomina (alias, zero-cost) C : Integer renames Count; begin C := C + 1; -- modifica Count tramite alias end Vars_Demo;
_ è ammesso nei letterali numerici per leggibilità:
1_000_000, 3.14_159, 16#FF_FF#.
-- Letterali in base diversa: base#valore# Hex : Integer := 16#FF#; -- 255 in esadecimale Oct : Integer := 8#77#; -- 63 in ottale Bin : Integer := 2#1010_0011#; -- 163 in binario
| Op | Significato |
|---|---|
+ - | Somma, sottrazione |
* / | Moltiplicazione, divisione |
mod | Modulo (segno del divisore) |
rem | Resto (segno del dividendo) |
** | Elevamento a potenza |
abs | Valore assoluto |
| Op | Significato |
|---|---|
= /= | Uguale, diverso |
< > <= >= | Confronto |
and or xor | Logici (booleani e bit-a-bit) |
not | Negazione logica |
and then | Short-circuit AND |
or else | Short-circuit OR |
& | Concatenazione stringhe |
and/or valutano ENTRAMBI gli operandi. Usare and then/or else
per la valutazione cortocircuitata (essenziale per guardie null-check).
with Ada.Text_IO; use Ada.Text_IO; with Ada.Integer_Text_IO; use Ada.Integer_Text_IO; with Ada.Float_Text_IO; use Ada.Float_Text_IO; procedure IO_Demo is N : Integer; Line : String (1..80); Last : Natural; begin -- Output Put ("Testo senza a-capo"); Put_Line ("Testo con a-capo"); New_Line; -- riga vuota Put (N, Width => 5); -- intero con padding Put (3.14, Fore => 1, Aft => 2, Exp => 0); -- "3.14" -- Input Get (N); -- legge un intero Skip_Line; -- consuma il newline residuo Get_Line (Line, Last); -- legge una riga, Last = ultimo carattere usato Put_Line (Line (1..Last)); -- stampa solo la parte letta end IO_Demo;
| Package | Uso |
|---|---|
Ada.Text_IO | Stringhe e caratteri |
Ada.Integer_Text_IO | Integer standard |
Ada.Float_Text_IO | Float standard |
Ada.Text_IO.Unbounded_IO | Unbounded_String I/O |
Ada.Sequential_IO | File binari sequenziali |
Ada.Direct_IO | File binari ad accesso diretto |
Ada.Streams.Stream_IO | I/O basato su stream |
with Ada.Text_IO; use Ada.Text_IO; procedure Write_File is F : File_Type; begin Create (F, Out_File, "output.txt"); Put_Line (F, "Prima riga"); Put_Line (F, "Seconda riga"); Close (F); end Write_File;
with Ada.Text_IO; use Ada.Text_IO; procedure Read_File is F : File_Type; Line : String (1..256); Last : Natural; begin Open (F, In_File, "input.txt"); while not End_Of_File (F) loop Get_Line (F, Line, Last); Put_Line (Line (1..Last)); end loop; Close (F); end Read_File;
Open (F, Append_File, "log.txt"); -- apre in modalità append Put_Line (F, "Nuova riga in coda"); Close (F);
| Modo | Descrizione |
|---|---|
In_File | Solo lettura |
Out_File | Solo scrittura (sovrascrive) |
Append_File | Scrittura in coda |
with Ada.Sequential_IO; procedure Bin_Sequential is package Int_IO is new Ada.Sequential_IO (Integer); use Int_IO; F : Int_IO.File_Type; V : Integer; begin -- Scrittura Create (F, Out_File, "data.bin"); Write (F, 42); Write (F, 99); Close (F); -- Lettura Open (F, In_File, "data.bin"); while not End_Of_File (F) loop Read (F, V); -- legge un Integer alla volta end loop; Close (F); end Bin_Sequential;
with Ada.Direct_IO; procedure Bin_Direct is type Sensor is record Id : Integer; Value : Float; end record; package Sensor_IO is new Ada.Direct_IO (Sensor); use Sensor_IO; F : Sensor_IO.File_Type; S : Sensor; begin Create (F, Inout_File, "sensors.bin"); Write (F, (Id => 1, Value => 23.5)); Write (F, (Id => 2, Value => 18.0)); -- Lettura per posizione (indice parte da 1) Read (F, S, 2); -- legge il secondo record Set_Index (F, 1); -- riposiziona all'inizio Close (F); end Bin_Direct;
Is_Open (F) per verificare se il file è aperto.
Le operazioni su file possono sollevare Name_Error (file non trovato),
Status_Error (file già aperto/chiuso) e Use_Error (operazione non valida per il modo).
if Temp > 100.0 then Put_Line ("Ebollizione"); elsif Temp > 0.0 then Put_Line ("Liquido"); else Put_Line ("Ghiaccio"); end if;
-- Espressione condizionale (simile al ternario di C) Status : String := (if Active then "ON " else "OFF");
case Day is when Mon => Put_Line ("Lunedì"); when Tue .. Thu => Put_Line ("Infrasettimanale"); when Fri => Put_Line ("Venerdì!"); when Sat | Sun => Put_Line ("Weekend"); end case;
case deve coprire TUTTI i valori possibili del tipo.
Il compilatore genera un errore se manca un valore. Usa when others => come default obbligatorio
se non elenchi tutti i valori.
Msg : String := (case Level is when 0 => "Off", when 1..5 => "Low", when others => "High");
loop Do_Something; exit when Done; -- uscita condizionale end loop;
while Count < 10 loop Count := Count + 1; end loop;
for I in 1 .. 10 loop Put (I, Width => 0); end loop; -- I non è visibile fuori dal loop
for I in reverse 1 .. 10 loop Put (I, Width => 0); end loop;
type Color is (Red, Green, Blue); for C in Color loop -- itera su tutti i valori dell'enum Put_Line (Color'Image (C)); -- stampa "RED", "GREEN", "BLUE" end loop; for C in Color'Range loop -- equivalente null; end loop;
Outer : for I in 1 .. 10 loop Inner : for J in 1 .. 10 loop exit Outer when I * J > 50; -- esce dal loop esterno end loop Inner; end loop Outer;
parallel for I in 1 .. 1000 loop Result (I) := Compute (Data (I)); -- iterazioni indipendenti eseguite in parallelo end loop;
Un subtype restringe il range di un tipo esistente senza creare un tipo nuovo. Valori di un subtype e del tipo padre sono compatibili senza conversione.
subtype Small_Int is Integer range -100 .. 100; subtype Percentage is Integer range 0 .. 100; subtype Upper_Char is Character range 'A' .. 'Z'; A : Small_Int := 50; B : Integer := A; -- OK: compatibili (stesso tipo base) C : Percentage := A; -- OK a compile-time, check a runtime (50 è nel range)
Percentage non può MAI contenere 101: il runtime lo garantisce con Constraint_Error.
Questo elimina interi bug di validazione rispetto a un semplice int.
-- Già definiti nello standard: subtype Natural is Integer range 0 .. Integer'Last; subtype Positive is Integer range 1 .. Integer'Last;
Un derived type crea un tipo completamente nuovo. Valori del tipo derivato e del padre sono incompatibili: servono conversioni esplicite. Questo previene errori di confusione tra unità di misura.
type Meters is new Float; type Seconds is new Float; type MPS is new Float; -- metri al secondo function Speed (D : Meters; T : Seconds) return MPS is begin return MPS (Float (D) / Float (T)); -- conversioni esplicite obbligatorie end Speed; Distance : Meters := 100.0; Time : Seconds := 9.58; V : MPS := Speed (Distance, Time); -- OK -- V := Distance; -- ERRORE di compilazione! Meters /= MPS
type Pounds is new Float; e type Newtons is new Float; avrebbero reso
questa confusione un errore di compilazione.
type Temperature_C is new Float range -273.15 .. 1_000_000.0; type Temperature_K is new Float range 0.0 .. 1_000_000.0; -- Non puoi assegnare Celsius a Kelvin senza convertire esplicitamente
type Direction is (North, South, East, West); type Weekday is (Mon, Tue, Wed, Thu, Fri, Sat, Sun); subtype Work_Day is Weekday range Mon .. Fri; Dir : Direction := North; Dir := Direction'Succ (Dir); -- South (successore) Dir := Direction'Pred (Dir); -- North (predecessore) Put_Line (Direction'Image (Dir)); -- "NORTH" (stringa) Dir := Direction'Value ("EAST"); -- parsing da stringa N := Direction'Pos (East); -- 2 (posizione ordinale) Dir := Direction'Val (3); -- West (valore da posizione)
'First, 'Last, 'Range, 'Succ, 'Pred,
'Pos, 'Val, 'Image, 'Value. Funzionano su tutti i tipi discreti.
-- Array a indice fisso (constrained) type Vector is array (1 .. 3) of Float; V : Vector := (1.0, 2.0, 3.0); -- Array a indice variabile (unconstrained) type Int_Array is array (Integer range <>) of Integer; Data : Int_Array (1 .. 100) := (others => 0); -- inizializza tutto a 0 -- Array indicizzato da enumerazione type Score_Table is array (Weekday) of Natural; Scores : Score_Table := (Mon => 10, Fri => 20, others => 0); -- Array multidimensionale type Matrix is array (Positive range <>, Positive range <>) of Float; M : Matrix (1..3, 1..3) := (others => (others => 0.0)); -- Slicing Sub : Int_Array := Data (5 .. 10); -- slice: copia elementi 5..10
A'First, A'Last, A'Range, A'Length.
Usali sempre nei loop: for I in A'Range loop — previene off-by-one.
type Point is record X : Float := 0.0; -- valore di default Y : Float := 0.0; end record; P1 : Point; -- (0.0, 0.0) per default P2 : Point := (X => 3.0, Y => 4.0); -- aggregate con nomi P3 : Point := (1.0, 2.0); -- aggregate posizionale P1.X := 5.0; -- accesso ai campi con notazione punto
type Shape_Kind is (Circle, Rectangle); type Shape (Kind : Shape_Kind) is record case Kind is when Circle => Radius : Float; when Rectangle => Width, Height : Float; end case; end record; S1 : Shape := (Kind => Circle, Radius => 5.0); S2 : Shape := (Kind => Rectangle, Width => 3.0, Height => 4.0); -- S1.Width sarebbe Constraint_Error: il discriminante è Circle
Ada chiama i puntatori access types. Sono fortemente tipizzati e di default inizializzati a null.
type Int_Ptr is access Integer; P : Int_Ptr; -- automaticamente null P := new Integer'(42); -- allocazione su heap Put (P.all, Width => 0); -- dereference con .all → stampa 42 P.all := 99; -- modifica il valore puntato -- Access a subprogram (function pointer) type Math_Func is access function (X : Float) return Float;
with Ada.Unchecked_Deallocation; procedure Free is new Ada.Unchecked_Deallocation (Object => Integer, Name => Int_Ptr); -- Dopo: Free (P); → P diventa null
null solleva
Constraint_Error. Usare not null access per
forzare la non-nullità a livello di tipo.
Il package è l'unità fondamentale di modularità in Ada. La specifica (.ads) dichiara l'interfaccia pubblica. Il body (.adb) contiene l'implementazione.
package Stack is Max : constant := 100; procedure Push (Val : in Integer); function Pop return Integer; function Is_Empty return Boolean; Stack_Overflow : exception; Stack_Underflow : exception; end Stack;
package body Stack is Data : array (1..Max) of Integer; Top : Natural := 0; procedure Push (Val : in Integer) is begin if Top = Max then raise Stack_Overflow; end if; Top := Top + 1; Data (Top) := Val; end Push; function Pop return Integer is Val : Integer; begin if Top = 0 then raise Stack_Underflow; end if; Val := Data (Top); Top := Top - 1; return Val; end Pop; function Is_Empty return Boolean is (Top = 0); end Stack;
package_name.ads (specifica), package_name.adb (body).
Un file per unità di compilazione. Il compilatore impone la corrispondenza nomi.
package Sensor is -- Parte PUBBLICA: visibile ai client type Reading is private; -- tipo opaco type Handle is limited private; -- opaco + no copia/confronto function Get_Value (R : Reading) return Float; function Create (Id : Positive) return Handle; private -- Parte PRIVATA: completamento del tipo, non visibile ai client type Reading is record Value : Float := 0.0; Timestamp : Duration := 0.0; end record; type Handle is record Sensor_Id : Positive; FD : Integer := -1; end record; end Sensor;
| Dichiarazione | Assegnamento | =, /= | Campi visibili |
|---|---|---|---|
is private | Sì (:=) | Sì | No (solo nel body) |
is limited private | No | No | No (solo nel body) |
package Sensor.Calibration is ... vede i campi interni di Sensor.
I generics Ada sono simili ai template C++ ma con controllo formale: il contratto del parametro generico è verificato al punto di definizione, non solo all'istanziazione.
-- Procedura generica: scambia due valori di qualsiasi tipo generic type Element is private; -- qualsiasi tipo con := e = procedure Generic_Swap (A, B : in out Element); procedure Generic_Swap (A, B : in out Element) is Tmp : Element := A; begin A := B; B := Tmp; end Generic_Swap; -- Istanziazione esplicita (nessuna deduzione implicita come in C++) procedure Swap_Int is new Generic_Swap (Integer); procedure Swap_Float is new Generic_Swap (Float);
generic type Item is private; Size : Positive; package Generic_Stack is procedure Push (Val : Item); function Pop return Item; end Generic_Stack; -- Istanziazione package Int_Stack is new Generic_Stack (Item => Integer, Size => 50); package Char_Stack is new Generic_Stack (Character, 256);
| Parametro Formale | Significato |
|---|---|
type T is private | Qualsiasi tipo con := e = |
type T is limited private | Qualsiasi tipo (anche senza := e =) |
type T is (<>) | Tipo discreto (intero o enum) |
type T is range <> | Tipo intero con segno |
type T is mod <> | Tipo modular |
type T is digits <> | Tipo floating-point |
type T is delta <> | Tipo fixed-point |
type T is tagged private | Tipo tagged (per OOP) |
with function F (...) return T | Parametro funzione |
with package P is new G (<>) | Parametro package istanziato |
-- Procedura (nessun valore di ritorno) procedure Greet (Name : in String) is begin Put_Line ("Hello, " & Name & "!"); end Greet; -- Funzione (deve sempre ritornare un valore) function Max (A, B : Integer) return Integer is (if A > B then A else B); -- expression function (Ada 2012)
| Modo | Lettura | Scrittura | Uso tipico |
|---|---|---|---|
in (default) | Sì | No | Input sola lettura |
out | Sì* | Sì | Output (valore di ritorno multiplo) |
in out | Sì | Sì | Modifica in-place |
*In Ada 2012+ i parametri out sono leggibili fin dall'inizio del sottoprogramma (hanno il valore di default del tipo o il valore passato). In Ada 95/2005 la lettura prima della prima assegnazione è erronea.
procedure Log (Message : String; Level : Natural := 0; -- valore di default To_File : Boolean := False) is ... -- Chiamate equivalenti: Log ("test"); Log ("test", Level => 2); Log (Message => "err", To_File => True, Level => 3);
Ada supporta overloading per nome, tipo dei parametri e tipo di ritorno (unico tra i linguaggi mainstream).
function Convert (X : Integer) return Float is (Float (X)); function Convert (X : Float) return Integer is (Integer (X)); function Convert (X : Integer) return String is (Integer'Image (X)); -- Overloading di operatori function "+" (A, B : Vector) return Vector is ((A(1)+B(1), A(2)+B(2), A(3)+B(3)));
Ada 95+ implementa l'OOP tramite tagged types: record con tag implicito che identifica il tipo a runtime (simile alla vtable del C++).
package Shapes is type Shape is tagged record X, Y : Float := 0.0; -- posizione end record; function Area (S : Shape) return Float; -- "metodo" procedure Move (S : in out Shape; DX, DY : Float); end Shapes; package body Shapes is function Area (S : Shape) return Float is (0.0); procedure Move (S : in out Shape; DX, DY : Float) is begin S.X := S.X + DX; S.Y := S.Y + DY; end Move; end Shapes;
S.Move (1.0, 2.0) invece di Move (S, 1.0, 2.0)
— la prefixed notation rende il codice più leggibile in stile OOP.
with Shapes; use Shapes; package Circles is type Circle is new Shape with record -- estende Shape Radius : Float := 1.0; end record; -- Override del metodo Area overriding -- keyword opzionale ma raccomandata (Ada 2005) function Area (S : Circle) return Float; -- Nuovo metodo (solo per Circle) function Circumference (S : Circle) return Float; end Circles; package body Circles is Pi : constant := 3.14159_26535; function Area (S : Circle) return Float is (Pi * S.Radius ** 2); function Circumference (S : Circle) return Float is (2.0 * Pi * S.Radius); end Circles;
type Abstract_Shape is abstract tagged record X, Y : Float; end record; function Area (S : Abstract_Shape) return Float is abstract; -- Non si possono creare istanze di Abstract_Shape -- Le sottoclassi DEVONO implementare Area
with Shapes; use Shapes; with Circles; use Circles; procedure Poly_Demo is -- Shape'Class = tutti i tipi derivati da Shape (class-wide type) procedure Print_Area (S : Shape'Class) is begin -- Dispatching dinamico: chiama la versione corretta di Area Put_Line ("Area = " & Float'Image (S.Area)); end Print_Area; S : Shape := (X => 0.0, Y => 0.0); C : Circle := (X => 1.0, Y => 2.0, Radius => 5.0); begin Print_Area (S); -- chiama Shape.Area → 0.0 Print_Area (C); -- chiama Circle.Area → 78.54 end Poly_Demo;
type Shape_Access is access Shape'Class; -- punta a qualsiasi Shape o derivato Ptr : Shape_Access := new Circle'(X => 0.0, Y => 0.0, Radius => 3.0); A : Float := Ptr.Area; -- dispatching dinamico tramite puntatore
T'Class.
Un parametro di tipo specifico T produce una chiamata statica (risolta a compile-time).
package Drawable is type Drawable_Interface is interface; -- nessun campo, solo operazioni procedure Draw (Self : Drawable_Interface) is abstract; end Drawable; package Printable is type Printable_Interface is interface; function To_String (Self : Printable_Interface) return String is abstract; end Printable; -- Ereditarietà multipla di interfacce + ereditarietà singola di tipo type Widget is new Shape and Drawable.Drawable_Interface and Printable.Printable_Interface with record Label : String (1..20); end record; -- Widget deve implementare Draw, To_String e (opzionalmente override) Area
Ada ha il supporto alla concorrenza integrato nel linguaggio (non in libreria). I task sono thread leggeri gestiti dal runtime Ada.
procedure Task_Demo is task Background; task body Background is begin for I in 1..5 loop Put_Line ("BG: " & Integer'Image (I)); delay 1.0; end loop; end Background; begin -- Background parte automaticamente Put_Line ("Main attivo"); delay 3.0; Put_Line ("Main finito"); -- attende la terminazione di -- Background prima di uscire end Task_Demo;
task type Worker (Id : Positive); task body Worker is begin Put_Line ("Worker" & Positive'Image (Id) & " avviato"); delay 2.0; Put_Line ("Worker" & Positive'Image (Id) & " completato"); end Worker; -- Crea 3 worker paralleli W1 : Worker (1); W2 : Worker (2); W3 : Worker (3);
delay 0.5; -- pausa di almeno 0.5 secondi with Ada.Real_Time; use Ada.Real_Time; Next : Time := Clock; loop Do_Periodic_Work; Next := Next + Milliseconds (100); delay until Next; -- timing deterministico (no drift) end loop;
parallel do Part_A := Compute_A (Data); and Part_B := Compute_B (Data); and Part_C := Compute_C (Data); end do; -- Tutte le sezioni eseguite in parallelo, si sincronizzano alla fine
I protected objects forniscono mutua esclusione e sincronizzazione senza lock espliciti — il runtime garantisce le invarianti.
protected type Shared_Counter is procedure Increment; -- accesso esclusivo (read-write) procedure Decrement; function Value return Integer; -- accesso concorrente (read-only) entry Wait_Until_Zero; -- entry: blocca il chiamante finché la guardia è False private Count : Integer := 0; end Shared_Counter; protected body Shared_Counter is procedure Increment is begin Count := Count + 1; end Increment; procedure Decrement is begin Count := Count - 1; end Decrement; function Value return Integer is (Count); entry Wait_Until_Zero when Count = 0 is -- barrier/guardia begin null; -- si sblocca quando Count = 0 end Wait_Until_Zero; end Shared_Counter;
| Componente | Accesso | Semantica |
|---|---|---|
function | Multipli lettori concorrenti | Read-only, nessuna modifica allo stato |
procedure | Un solo writer alla volta | Read-write, mutua esclusione |
entry | Come procedure + guardia booleana | Il chiamante si blocca se la guardia è False |
Il rendezvous è il meccanismo di comunicazione sincrona tra task:
il chiamante si blocca finché il task accettante non esegue l'accept.
task Server is entry Request (Data : in Integer; Result : out Integer); entry Shutdown; end Server; task body Server is Running : Boolean := True; begin while Running loop select accept Request (Data : in Integer; Result : out Integer) do Result := Data * 2; -- elaborazione sincrona end Request; or accept Shutdown; Running := False; or delay 5.0; -- timeout: nessuna richiesta in 5s Put_Line ("Timeout"); end select; end loop; end Server; -- Uso dal chiamante: R : Integer; Server.Request (21, R); -- R = 42 dopo il rendezvous Server.Shutdown;
Ada 2012 introduce aspect specifications per precondizioni, postcondizioni e invarianti di tipo — verificabili a runtime e/o staticamente con SPARK.
function Divide (A, B : Float) return Float with Pre => B /= 0.0, -- precondizione: B non zero Post => Divide'Result * B = A; -- postcondizione function Sqrt (X : Float) return Float with Pre => X >= 0.0, Post => Sqrt'Result >= 0.0 and then abs (Sqrt'Result ** 2 - X) < 1.0E-6;
package Dates is type Date is private with Type_Invariant => Is_Valid (Date); -- verificato dopo ogni modifica pubblica function Is_Valid (D : Date) return Boolean; function Create (Y, M, D : Positive) return Date; private type Date is record Year : Positive; Month : Positive range 1 .. 12; Day : Positive range 1 .. 31; end record; end Dates;
subtype Even is Integer with Dynamic_Predicate => Even mod 2 = 0; subtype Prime is Positive with Dynamic_Predicate => Is_Prime (Prime); subtype Weekday_Only is Weekday with Static_Predicate => Weekday_Only in Mon .. Fri; X : Even := 4; -- OK -- X := 3; -- Assertion_Error a runtime!
-- "per tutti gli elementi..." / "esiste un elemento..." All_Positive : Boolean := (for all I in Data'Range => Data (I) > 0); Has_Zero : Boolean := (for some I in Data'Range => Data (I) = 0);
with Ada.Text_IO; use Ada.Text_IO; with Ada.Exceptions; use Ada.Exceptions; with Ada.Integer_Text_IO; use Ada.Integer_Text_IO; procedure Exception_Demo is My_Error : exception; -- dichiarazione eccezione custom N : Integer; begin Put ("Inserisci un numero: "); Get (N); if N < 0 then raise My_Error with "Numero negativo non ammesso"; end if; Put (N / (N - 5), Width => 0); -- possibile divisione per zero exception when E : My_Error => Put_Line ("Errore custom: " & Exception_Message (E)); when Constraint_Error => Put_Line ("Errore: overflow, divisione per zero o range violato"); when Data_Error => Put_Line ("Input non valido"); when E : others => Put_Line ("Eccezione imprevista: " & Exception_Name (E)); Put_Line (Exception_Information (E)); raise; -- ri-solleva l'eccezione end Exception_Demo;
| Eccezione | Quando viene sollevata |
|---|---|
Constraint_Error | Range violato, overflow, divisione per zero, accesso null, indice fuori bounds |
Program_Error | Errori strutturali: funzione senza return, elaborazione errata, access before elab. |
Storage_Error | Memoria esaurita (heap o stack overflow) |
Tasking_Error | Errori di comunicazione tra task (task abortito durante rendezvous) |
Data_Error | Input malformato durante I/O |
I pragma sono direttive al compilatore (stile Ada 83–2005). Gli aspect (Ada 2012+) sono la sintassi moderna e unificata per le stesse informazioni.
| Pragma (vecchio stile) | Aspect (Ada 2012+) | Significato |
|---|---|---|
pragma Inline (F); | with Inline | Suggerisce inlining |
pragma Pure (Pkg); | with Pure | Package senza stato (idempotente) |
pragma Preelaborate; | with Preelaborate | Elaborazione prima dell'esecuzione |
pragma Suppress (Range_Check); | with Suppress => ... | Disabilita check runtime (unsafe!) |
pragma Convention (C, T); | with Convention => C | Calling convention C |
pragma Import (C, F, "f"); | with Import, Convention => C | Importa simbolo esterno |
pragma Volatile (V); | with Volatile | Accesso memory-mapped |
pragma Atomic (V); | with Atomic | Accesso atomico |
pragma Pack (T); | with Pack | Compatta la rappresentazione |
| — | with Pre => ... | Precondizione (solo aspect) |
| — | with Post => ... | Postcondizione (solo aspect) |
| — | with Type_Invariant => ... | Invariante di tipo (solo aspect) |
pragma Unreferenced (X); | — | Sopprime warning variabile non usata |
pragma SPARK_Mode (On) si abilita l'analisi formale SPARK,
che può dimostrare l'assenza di runtime errors senza eseguire il codice.
Ada permette di controllare la rappresentazione in memoria a livello di bit, essenziale per protocolli hardware e registri embedded.
-- Dimensione esplicita di un tipo type Status_Byte is mod 256; for Status_Byte'Size use 8; -- esattamente 8 bit -- Enumerazione con valori di rappresentazione espliciti type Command is (Reset, Start, Stop, Read_Data); for Command use (Reset => 0, Start => 1, Stop => 2, Read_Data => 16#FF#); -- Record layout a livello di bit (per registri hardware) type Control_Register is record Enable : Boolean; Mode : Integer range 0..3; Reserved : Integer range 0..0; Interrupt : Boolean; Data_Ready : Boolean; end record; for Control_Register use record Enable at 0 range 0 .. 0; -- bit 0 Mode at 0 range 1 .. 2; -- bit 1-2 Reserved at 0 range 3 .. 4; -- bit 3-4 Interrupt at 0 range 5 .. 5; -- bit 5 Data_Ready at 0 range 6 .. 6; -- bit 6 end record; for Control_Register'Size use 8; -- Indirizzo specifico (memory-mapped I/O) Ctrl : Control_Register; for Ctrl'Address use System.Storage_Elements.To_Address (16#4000_0000#); pragma Volatile (Ctrl);
with Interfaces.C; use Interfaces.C; with Interfaces.C.Strings; -- Importa funzione C function C_Strlen (S : Interfaces.C.Strings.chars_ptr) return size_t with Import => True, Convention => C, External_Name => "strlen"; -- Esporta procedura Ada per chiamata da C procedure Ada_Callback (Value : in int) with Export => True, Convention => C, External_Name => "ada_callback";
| Ada (Interfaces.C) | C equivalente |
|---|---|
int | int |
unsigned | unsigned int |
char | char |
C_float | float |
double | double |
size_t | size_t |
Strings.chars_ptr | char* |
-- Conversione di tipo unsafe (come reinterpret_cast in C++) with Ada.Unchecked_Conversion; function To_Byte is new Ada.Unchecked_Conversion (Source => Character, Target => Byte); -- Deallocazione manuale (free) with Ada.Unchecked_Deallocation; procedure Free is new Ada.Unchecked_Deallocation (Node, Node_Ptr);
Unchecked_* bypassano il sistema dei tipi.
Usarle solo quando strettamente necessario e documentare il motivo.
GNAT (GNU Ada Translator) è il compilatore Ada open-source, parte di GCC. Alire è il package manager moderno per l'ecosistema Ada/SPARK.
# ─── Compilazione diretta ────────────────────────── $ gnatmake main.adb # compila + linka in un passo $ gnatmake -O2 -gnatn main.adb # ottimizzato + inlining $ gnatmake -gnata main.adb # abilita assertions (Pre/Post) $ gnatmake -gnatwa main.adb # tutti i warning attivi $ gnatmake -gnatVa main.adb # tutti i validity checks # ─── Compilazione separata ──────────────────────── $ gcc -c stack.adb # compila solo il body (.o) $ gcc -c main.adb $ gnatbind main.ali # risolve dipendenze $ gnatlink main.ali # linka # ─── Con GPRbuild (progetto complesso) ──────────── $ gprbuild -P myproject.gpr # usa file di progetto GNAT $ gprbuild -P myproject.gpr -Xmode=release # ─── Con Alire (package manager) ────────────────── $ alr init my_project --bin # crea progetto eseguibile $ alr init my_lib --lib # crea progetto libreria $ alr build # compila $ alr run # compila e esegui $ alr with gnatcoll # aggiungi dipendenza
| Flag | Significato |
|---|---|
-gnata | Abilita assertions, Pre/Post, Type_Invariant |
-gnatwa | Tutti i warning |
-gnatwe | Warning come errori |
-gnato | Overflow check (default in GNAT) |
-gnatp | Sopprime TUTTI i check (pericoloso, solo produzione ottimizzata) |
-gnatVa | Tutti i validity check |
-gnatn | Abilita inlining inter-unità |
-gnatl | Listing con codice sorgente |
-gnatR | Mostra representation info (layout memoria) |
-gnat2022 | Attiva features Ada 2022 |
-gnat2012 | Attiva features Ada 2012 (default in GNAT recenti) |
-gnateDX=Y | Definisce simbolo preprocessore |
project My_Project is for Source_Dirs use ("src"); for Object_Dir use "obj"; for Main use ("main.adb"); package Compiler is for Default_Switches ("Ada") use ("-gnatwa", "-gnata", "-O2", "-gnat2022"); end Compiler; end My_Project;
Gli attributi ('Attribute) sono proprietà/operazioni predefinite su tipi e oggetti.
| Attributo | Si applica a | Risultato |
|---|---|---|
T'First | Tipo scalare / Array | Primo valore / primo indice |
T'Last | Tipo scalare / Array | Ultimo valore / ultimo indice |
T'Range | Tipo scalare / Array | T'First .. T'Last |
T'Length | Array | Numero di elementi |
T'Image (V) | Tipo scalare | Conversione valore → String |
T'Value (S) | Tipo scalare | Conversione String → valore |
T'Succ (V) | Tipo discreto | Successore |
T'Pred (V) | Tipo discreto | Predecessore |
T'Pos (V) | Tipo discreto | Posizione ordinale |
T'Val (N) | Tipo discreto | Valore dalla posizione |
T'Size | Tipo / Oggetto | Dimensione in bit |
T'Address | Oggetto | Indirizzo in memoria |
T'Access | Oggetto (aliased) | Access value (puntatore) |
T'Min (A,B) | Tipo scalare | Minimo tra A e B |
T'Max (A,B) | Tipo scalare | Massimo tra A e B |
T'Digits | Floating-point | Cifre di precisione |
T'Modulus | Tipo modular | Valore del modulo |
T'Reduce | Array/Iterabile (2022) | Riduzione (fold) |
| Package | Contenuto | Da |
|---|---|---|
Ada.Text_IO | I/O testuale (Put, Get, file) | 83 |
Ada.Strings.Unbounded | Stringhe a lunghezza variabile | 95 |
Ada.Strings.Fixed | Operazioni su String a lunghezza fissa | 95 |
Ada.Strings.Maps | Character mapping e set | 95 |
Ada.Numerics.Elementary_Functions | Sin, Cos, Sqrt, Log, Exp... | 95 |
Ada.Numerics.Float_Random | Generatore numeri casuali Float | 95 |
Ada.Numerics.Discrete_Random | Generatore numeri casuali discreti | 95 |
Ada.Calendar | Data e ora | 83 |
Ada.Real_Time | Orologio monotono ad alta risoluzione | 95 |
Ada.Containers.Vectors | Array dinamico (come std::vector) | 2005 |
Ada.Containers.Doubly_Linked_Lists | Lista bidirezionale | 2005 |
Ada.Containers.Hashed_Maps | HashMap (come unordered_map) | 2005 |
Ada.Containers.Ordered_Maps | Map ordinata (come std::map) | 2005 |
Ada.Containers.Hashed_Sets | HashSet | 2005 |
Ada.Containers.Ordered_Sets | Set ordinato | 2005 |
Ada.Directories | Navigazione filesystem | 2005 |
Ada.Environment_Variables | Variabili d'ambiente | 2005 |
Ada.Command_Line | Argomenti da riga di comando | 95 |
Ada.Exceptions | Info su eccezioni (nome, messaggio) | 95 |
Ada.Streams | Streaming di dati | 95 |
Interfaces | Tipi C, COBOL, Fortran | 95 |
System | Info sistema, Address, Storage_Elements | 83 |
GNAT.Sockets | Networking TCP/UDP (GNAT-specifico) | — |
Le String Ada sono a lunghezza fissa (array di Character).
Per stringhe a lunghezza variabile si usa Unbounded_String.
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; S : Unbounded_String; S := To_Unbounded_String ("Hello"); S := S & ", World!"; -- concatenazione -- Conversioni Len : Natural := Length (S); Fix : String := To_String (S); -- Operazioni Ch : Character := Element (S, 1); Replace_Element (S, 1, 'h'); S := Head (S, 5); -- primi 5 char S := Tail (S, 3); -- ultimi 3 char
with Ada.Strings.Fixed; use Ada.Strings.Fixed; S : String (1..20); -- Ricerca Pos := Index (S, "abc"); -- 0 se non trovato Cnt := Count (S, "x"); -- Trim spazi T := Trim (S, Ada.Strings.Both); -- Sostituzione R := Replace_Slice (S, 3, 5, "XY"); -- Padding e allineamento H := Head ("Hi", 10); -- "Hi " T := Tail ("Hi", 10); -- " Hi"
with Ada.Characters.Handling; use Ada.Characters.Handling; Is_Letter ('A'); -- True Is_Digit ('3'); -- True Is_Alphanumeric ('_'); -- False To_Upper ('a'); -- 'A' To_Lower ("HELLO"); -- "hello"
S : String := "Hello, World!"; -- Slicing (gli indici seguono il range della stringa) Sub : String := S (1..5); -- "Hello" End_Part : String := S (8..13); -- "World!" -- Concatenazione con & Full : String := "Ada" & " " & "2022"; -- "Ada 2022" WithChar : String := "X" & '!'; -- "X!" (String & Character) -- Confronto (case-sensitive, ordine lessicografico) if S = "Hello, World!" then ... -- uguaglianza if "abc" < "abd" then ... -- True
-- Numero → String S1 : String := Integer'Image (42); -- " 42" (spazio iniziale per il segno) S2 : String := Float'Image (3.14); -- " 3.14000E+00" -- String → Numero N : Integer := Integer'Value ("42"); -- 42 F : Float := Float'Value ("3.14"); -- 3.14 -- Ada 2022: 'Image senza spazio iniziale per Integer S3 : String := 42'Image; -- "42" (Ada 2022+)
with Ada.Strings.Fixed; use Ada.Strings.Fixed; with Ada.Strings.Maps; use Ada.Strings.Maps; -- Definire una mappa di traduzione Vowel_Set : Character_Set := To_Set ("aeiouAEIOU"); Pos : Natural := Index (Source => "Hello", Set => Vowel_Set); -- 2 ('e') -- Translate: sostituisce caratteri Map : Character_Mapping := To_Mapping (From => "aeiou", To => "AEIOU"); R : String := Translate ("ciao mondo", Map); -- "cIAO mOndO"
with Ada.Strings.Bounded; package Name_Strings is new Ada.Strings.Bounded.Generic_Bounded_Length (Max => 50); use Name_Strings; S : Bounded_String := To_Bounded_String ("Ada"); S := S & " Lovelace"; -- "Ada Lovelace" L : Natural := Length (S); -- 12 F : String := To_String (S); -- conversione a String fissa
String fissa per dimensioni note a compile-time;
Bounded_String per lunghezza variabile con tetto massimo (no heap);
Unbounded_String per lunghezza totalmente dinamica (usa heap).
with Ada.Containers.Vectors; package Int_Vectors is new Ada.Containers.Vectors (Index_Type => Natural, Element_Type => Integer); use Int_Vectors; V : Vector; V.Append (10); -- aggiunge in coda V.Append (20); V.Append (30); V.Prepend (5); -- aggiunge in testa (Ada 2022) Put (V.Length); -- 4 Put (V.Element (0)); -- 5 (primo elemento) Put (V (2)); -- 20 (Ada 2012: indexing notation) V.Replace_Element (1, 99); -- V(1) := 99 V.Delete_Last; -- rimuove ultimo V.Insert (2, 42); -- inserisce 42 alla posizione 2 -- Iterazione for E of V loop -- Ada 2012: iterazione per elemento Put (E, Width => 0); end loop; for C in V.Iterate loop -- iterazione per cursore Put (Element (C)); end loop; V.Clear; -- svuota
with Ada.Containers.Indefinite_Hashed_Maps; with Ada.Strings.Hash; package Name_Map is new Ada.Containers.Indefinite_Hashed_Maps (Key_Type => String, Element_Type => Integer, Hash => Ada.Strings.Hash, Equivalent_Keys => "="); use Name_Map; M : Map; M.Insert ("alice", 30); M.Insert ("bob", 25); M.Include ("alice", 31); -- Insert o Replace (nessuna eccezione se esiste) if M.Contains ("alice") then Put (M.Element ("alice")); -- 31 end if; M.Delete ("bob"); for C in M.Iterate loop Put_Line (Key (C) & " => " & Integer'Image (Element (C))); end loop;
-- Map ordinata per chiave (albero bilanciato) with Ada.Containers.Ordered_Maps; package Score_Map is new Ada.Containers.Ordered_Maps (Key_Type => Integer, Element_Type => Float); -- Set ordinato with Ada.Containers.Ordered_Sets; package Int_Sets is new Ada.Containers.Ordered_Sets (Element_Type => Integer); use Int_Sets; S : Set; S.Insert (10); S.Insert (5); S.Insert (10); -- nessun effetto (duplicato) -- S contiene {5, 10}
| Container | Definite | Indefinite | Accesso |
|---|---|---|---|
| Vector | Vectors | Indefinite_Vectors | O(1) per indice |
| Lista | Doubly_Linked_Lists | Indefinite_Doubly_Linked_Lists | O(n) sequenziale |
| Hash Map | Hashed_Maps | Indefinite_Hashed_Maps | O(1) medio |
| Ordered Map | Ordered_Maps | Indefinite_Ordered_Maps | O(log n) |
| Hash Set | Hashed_Sets | Indefinite_Hashed_Sets | O(1) medio |
| Ordered Set | Ordered_Sets | Indefinite_Ordered_Sets | O(log n) |
String). Le versioni Definite sono più efficienti
per tipi a dimensione nota a compile-time.
I Controlled Types permettono di eseguire codice alla creazione, assegnamento e distruzione di un oggetto — il pattern RAII (Resource Acquisition Is Initialization) di Ada.
with Ada.Finalization; package Smart_Ptrs is type Smart_Ptr is new Ada.Finalization.Controlled with private; -- Operazioni pubbliche function Create (Value : Integer) return Smart_Ptr; function Get (Self : Smart_Ptr) return Integer; private type Int_Ptr is access Integer; type Smart_Ptr is new Ada.Finalization.Controlled with record Ptr : Int_Ptr := null; end record; -- Override delle primitive di finalizzazione overriding procedure Initialize (Self : in out Smart_Ptr); -- alla creazione overriding procedure Adjust (Self : in out Smart_Ptr); -- dopo assegnamento (:=) overriding procedure Finalize (Self : in out Smart_Ptr); -- alla distruzione end Smart_Ptrs;
| Primitiva | Quando viene chiamata | Uso tipico |
|---|---|---|
Initialize | Alla creazione (se non inizializzato con aggregate) | Allocare risorse |
Adjust | Dopo un assegnamento := (sulla copia) | Deep copy di risorse |
Finalize | Alla fine dello scope o prima di riassegnamento | Rilasciare risorse (free, close) |
limited (non copiabile), estendere
Ada.Finalization.Limited_Controlled — ha solo Initialize e Finalize (niente Adjust).
Ada 2022 (ISO/IEC 8652:2023) introduce diverse funzionalità che rendono il linguaggio
più espressivo mantenendo la sicurezza. Compilare con -gnat2022.
-- Variabili locali in un'espressione (senza statement) Area : Float := (declare R : constant Float := Radius (Shape); Pi : constant := 3.14159_26535; begin Pi * R ** 2);
-- Copia un record/array modificando solo alcuni campi type Config is record Port : Positive := 8080; Debug : Boolean := False; Timeout : Duration := 30.0; end record; Default_Cfg : Config; Debug_Cfg : Config := (Default_Cfg with delta Debug => True, Port => 9090); -- Funziona anche con array A : array (1..5) of Integer := (1, 2, 3, 4, 5); B : array (1..5) of Integer := (A with delta 3 => 99, 5 => 0); -- B = (1, 2, 99, 4, 0)
-- @ si riferisce alla variabile a sinistra dell'assegnamento Count := @ + 1; -- equivale a: Count := Count + 1 Name := To_Upper (@); -- equivale a: Name := To_Upper (Name) Rec.Field := @ * 2; -- equivale a: Rec.Field := Rec.Field * 2 Map.Reference ("key") := @ & "!"; -- evita doppia valutazione della chiave
-- Riduzione (fold) su array e iterabili Sum : Integer := [for I in 1..100 => I]'Reduce ("+", 0); -- 5050 Prod : Integer := [1, 2, 3, 4, 5]'Reduce ("*", 1); -- 120 -- Con funzione custom Max_Val : Integer := Data'Reduce (Integer'Max, Integer'First);
-- Sintassi [ ] per aggregati (Ada 2022) Squares : array (1..10) of Integer := [for I in 1..10 => I ** 2]; -- [1, 4, 9, 16, ..., 100] Evens : array (1..5) of Integer := [for I in 1..5 => I * 2]; -- [2, 4, 6, 8, 10]
-- Variabili dichiarate ovunque in sequenza di statement (non più solo dopo is/declare) begin Put_Line ("Inizio"); X : constant Integer := Compute_Value; -- dichiarazione in-place Put (X); end;
-- Loop parallelo (iterazioni indipendenti) parallel for I in 1..1_000 loop Result (I) := Heavy_Compute (I); end loop; -- Blocco parallelo (sezioni indipendenti) parallel do A := Compute_A; and B := Compute_B; end do; -- Riduzione parallela (Ada 2022 + 'Reduce) Total : Integer := [parallel for I in 1..1_000_000 => F(I)]'Reduce("+", 0);
gnatmake -gnat2022
o -gnat2022 nel file .gpr. GNAT 13+ supporta la maggior parte delle features.
SPARK è un sottoinsieme verificabile di Ada. Con gli strumenti GNATprove, si può provare matematicamente l'assenza di runtime errors (overflow, divisione per zero, bounds check).
package Safe_Math with SPARK_Mode => On is function Safe_Add (A, B : Integer) return Integer with Pre => (A >= 0 and then B >= 0 and then A <= Integer'Last - B), Post => Safe_Add'Result = A + B; function Abs_Value (X : Integer) return Natural with Pre => X > Integer'First, -- evita overflow di abs(Integer'First) Post => (if X >= 0 then Abs_Value'Result = X else Abs_Value'Result = -X); end Safe_Math;
# Verifica formale $ gnatprove -P myproject.gpr --level=2 # Se tutte le prove passano → assenza GARANTITA di runtime errors nel codice SPARK