Pascal-like language with extremely strong static typing.
Ada provides extremely strong static-typing, sane object-oriented programming, built-in design-by-contract, and formal proofs via SPARK. It is primarily used in resource constrained environments where high-reliability is required.
Hello ada
To do simple text output you could write something like the following:
// hello.adb
with Ada.Text_IO; use Ada.Text_IO;
procedure Hello is
begin
Put_Line("Hello, there");
end Hello;
To build you run:
$ gnat compile hello $ gnat bind hello $ gnat link hello
You can then run the program:
$ ./hello Hello, there
Safe arrays
Ada allows arbitrary ranges to be used for array access, simplifying a lot adding and subtracting that happens in C in order to fix up array bounds. For example when writing an emulator we might have something like:
subtype Memory_Range is Integer range #0#16 .. #FFF#16; PC: Memory_Range; Memory: array (Memory_Range) of Integer;This restricts both
PC values and array access
into Memory to the range specified with the compiler handling
selecting an appropriately sized basic type for the range given. This also
allows for compile time checks and the raising of
a Constraint_Error at runtime if PC overflows during
addition. Overflow issues can also be caught before runtime by running the
SPARK prover.
Safe pointers
Pointers in Ada are called "access types". To define a pointer type you might have something like the following:
Ref: access T; ... Obj: aliased T; Ref := Obj'Access;
We first declare Ref to be an access type and Obj
to be an aliased value of type T. This tells the
compiler than we plan to take pointers to Obj. We then
set Ref to point to Obj using Obj'Access
to get the pointer.
You can also declare pointers that can never be null as follows:
Ref: access not null T := <Initial value>; // Non-null requires an initial value. ... Ref := null; // * Constraint_Error at runtime *
SPARK
SPARK is a subset of the Ada language designed for the development of high-integrity software. It disables language features like dynamic dispatch for object-oriented programming that make programs hard to formally analyze. Despite being restricted, a large enough portion of Ada's features remain to make it quite comfortable to develop within.
SPARK works by analyzing the code you write based on contracts you define within the code. In normal Ada the contracts introduce runtime checks, but with SPARK the pre and post conditions are analyzed by the prover at compile time.
// chip8.ads
with Interfaces; use Interfaces;
package Chip8 with SPARK_Mode is
subtype Nibble is Integer range 0 .. 15;
-- Represents a 4-bit value
subtype Byte is Interfaces.Unsigned_8;
-- Represents an 8-bit byte value
subtype Word is Interfaces.Unsigned_16;
-- Represents a 16-bit word value
function To_Hex_String (Value : in Nibble) return String
with
Post => To_Hex_String'Result'Length = 1;
-- Return the string hexadecimal representation of the given nibble.
function To_Hex_String (Value : in Byte) return String
with
Post => To_Hex_String'Result'Length = 2;
-- Return the string hexadecimal representation of the given byte value.
function To_Hex_String (Value : in Word) return String
with
Post => To_Hex_String'Result'Length = 3;
-- Return the string hexadecimal representation of the given word value.
end Chip8;
// chip8.adb
package body Chip8 with SPARK_Mode is
-------------------
-- To_Hex_String --
-------------------
function To_Hex_String (Value : in Nibble) return String is
Hex_Value_Map : array (Nibble) of String (1 .. 1) :=
(0 => "0",
1 => "1",
2 => "2",
3 => "3",
4 => "4",
5 => "5",
6 => "6",
7 => "7",
8 => "8",
9 => "9",
10 => "A",
11 => "B",
12 => "C",
13 => "D",
14 => "E",
15 => "F");
begin
return Hex_Value_Map (Value);
end To_Hex_String;
function To_Hex_String (Value : in Byte) return String is
Result : String (1 .. 2) := " ";
begin
Result (2 .. 2) := To_Hex_String (Nibble (Value and 16#0F#));
if Value < 16#10# then
Result (1) := '0';
else
Result (1 .. 1) :=
To_Hex_String (Nibble (Shift_Right (Value, 4) and 16#0F#));
end if;
return Result;
end To_Hex_String;
function To_Hex_String (Value : in Word) return String is
Result : String (1 .. 3) := " ";
begin
if Value < 16#100# then
Result (1) := '0';
else
Result (1 .. 1) :=
To_Hex_String (Nibble (Shift_Right (Value, 8) and 16#00F#));
end if;
Result (2 .. 3) := To_Hex_String (Byte (Value and 16#0FF#));
return Result;
end To_Hex_String;
end Chip8;
The above contract enforces, for example, that the hex string returned when
converting a Nibble value should always be 1 character in
length. We don't need a pre-condition check the range of Nibble
since that is implied by its ranged definition. We can run the SPARK prover
over our implementation of this as follows:
$ gnatprove -P./chip8.gpr -j0 --level=4 --report=all Phase 1 of 2: generation of Global contracts ... Phase 2 of 2: flow analysis and proof ... chip8.adb:32:26: info: length check proved chip8.adb:32:55: info: range check proved chip8.adb:38:12: info: length check proved chip8.adb:38:58: info: range check proved chip8.adb:51:12: info: length check proved chip8.adb:51:58: info: range check proved chip8.adb:54:26: info: length check proved chip8.adb:54:53: info: range check proved chip8.ads:16:16: info: postcondition proved chip8.ads:21:16: info: postcondition proved chip8.ads:26:16: info: postcondition proved
incoming(1): how to
Last update on 7E4B18, edited 1 times. 1/1thh