第7课|基本语法元素

11 阅读9分钟

本课目标

完成本课后,你将能够:

  • 掌握Ada字符集与词法单元的完整规范

  • 熟练使用各种数值字面量表示形式

  • 正确处理字符串字面量与宽字符类型

  • 理解编译指示(Pragma)的基础与高级用法

  • 应用分隔符与特殊符号编写规范代码


一、Ada字符集

1.1 基本字符集

Ada 2012定义了两级字符集:

级别字符范围用途
基本字符集ISO-646(ASCII子集)保证所有平台可移植
完整字符集ISO-10646(Unicode)扩展标识符与字符串

基本字符集包含

  • 大写字母: A-Z

  • 小写字母: a-z

  • 数字: 0-9

  • 特殊字符: !"#$%&'()*+,-./:;<=>?@[]^_ {|}~

  • 空白字符:空格、水平制表符、换行、回车


1.2 扩展标识符字符

GNAT支持Unicode标识符(其他编译器可能不支持):

-- 基本字符集标识符(标准,强烈推荐)
 procedure Calculate_Area is ...

-- 扩展标识符(GNAT特定,可移植性差)
 procedure 计算面积 is ...  -- 中文字符
 procedure Εμβαδόν is ...  -- 希腊字母

工业建议:生产代码严格使用基本字符集,确保跨平台编译器兼容性。


二、词法单元(Lexical Elements)

Ada源代码被分解为以下词法单元:

源代码 → 分隔符 / 标识符 / 保留字 / 数值字面量 / 
         字符字面量 / 字符串字面量 / 注释

三、分隔符(Delimiters)

3.1 单字符分隔符

符号名称用途
&与号字符串连接
'撇号字符字面量、属性
( )括号分组、参数列表
*星号乘法、幂运算(**
+加号加法、正号
,逗号分隔列表元素
-减号减法、负号
.点号选择组件、小数点
/斜杠除法
:冒号类型标记、case 语句
;分号语句/声明终止
< >尖括号关系运算、泛型参数
=等号相等比较、赋值(:=

3.2 双字符分隔符

符号名称用途
=>箭头case/when关联、参数映射
..范围范围运算符(1..10)
**双星号幂运算(2**10 = 1024)
:=赋值变量赋值
/=不等不等比较
>=大于等于比较运算
<=小于等于比较运算
<< >>标号括号goto标号(极少使用)
<>泛型形参默认、离散范围

3.3 分隔符使用示例

-- 各类分隔符综合展示
procedure Delimiter_Demo is
   A, B : Integer range 1 .. 100;  -- 逗号、范围..
   Result : Float;
begin
   A := 10;
   B := 20;
   
   -- 算术运算符
   Result := Float (A + B) * 2.5 / 3.0;  -- + * / := ()
   
   -- 比较与逻辑
   if A /= B and then Result >= 15.0 then  -- /= >= and then
      case A is
         when 1 .. 5  => Put_Line ("Small");   -- => 箭头
         when 6 .. 10 => Put_Line ("Medium");  -- | 隐式or
         when others  => Put_Line ("Large");
      end case;
   end if;
   
   -- 字符串连接
   Put_Line ("Value: " & Result'Image);  -- & 连接, ' 属性
   
end Delimiter_Demo;

四、数值字面量

4.1 整数字面量

-- 十进制整数
Decimal : constant := 1234;
Negative : constant := -5678;

-- 下划线增强可读性(编译器忽略)
Million : constant := 1_000_000;
Hex_Mask : constant := 16#FF_FF_FF_FF#;  -- 十六进制,见下文

4.2 基数值(基于的整数)

Ada支持2-16任意进制的整数表示:

-- 语法:基#数字# [指数]
-- 基范围:2 .. 16

Binary      : constant := 2#1111_0000#;     -- 240
Octal       : constant := 8#377#;           -- 255
Hexadecimal : constant := 16#FF#;           -- 255
Base_7      : constant := 7#666#;           -- 7^3 - 1 = 342

-- 带指数的基数值
Mega : constant := 10#1#E6;     -- 1 * 10^6 = 1_000_000
Kibi : constant := 2#1#E10;     -- 1 * 2^10 = 1024

基数值规则

基数允许数字示例
20-12#1010# = 10
80-78#77# = 63
100-910#255# = 255
160-9, A-F16#FF# = 255
N (2–16)0 到 N-112#BB# = 11×12 + 11 = 143

4.3 实数字面量

-- 十进制实数(必须有小数点)
Pi_Approx : constant := 3.14159;
Negative : constant := -0.001;

-- 科学计数法
Avogadro : constant := 6.022_140_76E23;  -- 6.022... × 10^23
Tiny : constant := 1.0E-10;

-- 基数值实数(基数#尾数#E指数)
Hex_Float : constant := 16#1.FFFF#E2;  -- 1.FFFF(16) × 16^2

实数字面量规则

  • 必须包含小数点指数(或两者)

  • 1..5 都是合法的(但 .5 可读性较差,推荐 0.5


4.4 数值字面量类型

数值字面量本身无类型,根据上下文确定:

X : Integer := 100;      -- 字面量100解释为Integer
Y : Float := 100.0;      -- 字面量100.0解释为Float
Z : Duration := 100.0;   -- 字面量100.0解释为Duration

-- 显式限定(Qualified Expression)
A := Integer'(100);      -- 明确指定为Integer
B := Float'(100);        -- 将整数字面量转为Float

五、字符与字符串字面量

5.1 字符字面量

-- 单引号包围单个字符
A : Character := 'A';
Digit : Character := '0';
Space : Character := ' ';
Newline : Character := ASCII.LF;  -- 控制字符使用ASCII包

转义序列(GNAT支持):

序列含义
ASCII.NUL空字符
ASCII.BEL响铃
ASCII.BS退格
ASCII.HT水平制表
ASCII.LF换行
ASCII.CR回车
ASCII.ESC退出

5.2 字符串字面量

-- 双引号包围
Message : String := "Hello, Ada!";

-- 连接长字符串
Long_Msg : constant String := 
   "This is a very long message " &
   "that spans multiple lines " &
   "using the concatenation operator.";

-- 包含引号的字符串
Quote : String := "She said ""Hello"" to me.";  -- 双引号转义

5.3 宽字符与宽字符串

Ada支持16位(Wide)和32位(Wide_Wide)字符:

-- Wide_Character(16位,UTF-16)
W_Char : Wide_Character := '中';  -- 中文字符

-- Wide_String
W_String : Wide_String := "Hello 世界";

-- Wide_Wide_Character(32位,完整Unicode)
WW_Char : Wide_Wide_Character := '囍';  -- Emoji

-- 类型转换
S : String := To_String (W_String);  -- 可能丢失信息

输出宽字符串

with Ada.Wide_Text_IO;

procedure Wide_Demo is
   Msg : Wide_String := "Hello 世界";
begin
   Ada.Wide_Text_IO.Put_Line (Msg);
end Wide_Demo;

六、编译指示(Pragma)

6.1 Pragma基础

Pragma是向编译器传递特殊指令的语法结构:

pragma 指示名 [(参数)];

常见分类

类别作用示例
配置控制编译行为pragma Optimize (Time)
接口外部语言/表示pragma Import (C, Func)
任务并发控制pragma Priority (10)
程序运行时检查pragma Assert (X > 0)
预定义标准定义的指示pragma Pure, pragma Preelaborate

6.2 常用预定义Pragma

优化控制

pragma Optimize (Time);     -- 优化执行速度
pragma Optimize (Space);    -- 优化代码体积
pragma Optimize (Off);      -- 关闭优化(调试用)

pragma Inline (Max);        -- 请求内联展开函数Max
pragma No_Inline (Slow);    -- 禁止内联函数Slow

运行时检查

pragma Assert (X >= 0, "X must be non-negative");  -- 断言检查

pragma Suppress (Range_Check);      -- 关闭范围检查(危险!)
pragma Unsuppress (Range_Check);    -- 强制开启范围检查

pragma Debug (Put_Line ("Debug: " & X'Image));  -- 调试代码

程序性质声明

pragma Pure (Math_Utils);           -- 纯包:无状态、无副作用
pragma Preelaborate (Config);       -- 预 elaborable:启动时初始化
pragma Elaborate_Body (Package_1);  -- 强制体elaborate

pragma Convention (C, My_Record);   -- 使用C调用约定
pragma Pack (Color_Array);          -- 紧凑存储,最小化填充

6.3 配置Pragma(项目级设置)

可在项目文件( .gpr )或配置文件中设置:

-- config.adc 配置文件
pragma Profile (Ravenscar);         -- Ravenscar实时配置
pragma Partition_Elaboration_Policy (Sequential);
pragma Detect_Blocking;
pragma Restrictions (
   No_Allocators,                   -- 禁止动态分配
   No_Task_Hierarchy,               -- 禁止任务层次
   No_Protected_Type_Allocators
);

6.4 自定义Pragma(GNAT扩展)

-- 定义自定义Pragma
pragma My_Company_Reviewed (Unit_Name => "Critical_Module");

-- 在代码中使用
pragma My_Company_Reviewed (Unit_Name => "Navigation");

七、特殊语法元素

7.1 标号(极少使用)

procedure Label_Demo is
begin
   <<Start>>  -- 标号定义
   
   for I in 1 .. 10 loop
      if Some_Condition then
         goto Start;  -- 跳转到标号(避免使用)
      end if;
   end loop;
   
end Label_Demo;

警告goto 在Ada中受限,禁止跳入 ifloopblock 内部。


7.2 空语句与空声明

procedure Empty_Examples is
begin
   null;  -- 空语句:语法需要但无操作
   
   declare
      -- 空声明部分(合法)
   begin
      null;
   end;
   
end Empty_Examples;

八、完整示例:配置寄存器操作

------------------------------------------------------------------------------
--                         Hardware_Register                                --
--                                                                          --
--              演示数值字面量与Pragma在嵌入式开发中的应用                  --
------------------------------------------------------------------------------

with System;
with System.Storage_Elements;

package Hardware_Register is

   pragma Preelaborate;
   -- 此包可在启动时初始化

   ---------------------------------------------------------------------------
   --  寄存器地址定义(使用基数值增强可读性)
   ---------------------------------------------------------------------------

   Base_Address : constant System.Address :=
      System'To_Address (16#4000_0000#);  -- 外设基地址

   GPIO_Offset  : constant := 16#1000#;   -- GPIO寄存器偏移
   TIMER_Offset : constant := 16#2000#;   -- 定时器偏移

   ---------------------------------------------------------------------------
   --  控制寄存器位掩码(二进制字面量直观展示位模式)
   ---------------------------------------------------------------------------

   type Control_Flags is record
      Enable   : Boolean;  -- 位0
      Interrupt: Boolean;  -- 位1
      Mode     : Bits_2;   -- 位2-3
      Reserved : Bits_4;   -- 位4-7
   end record with Size => 8;

   for Control_Flags use record
      Enable    at 0 range 0 .. 0;
      Interrupt at 0 range 1 .. 1;
      Mode      at 0 range 2 .. 3;
      Reserved  at 0 range 4 .. 7;
   end record;

   -- 常用配置常量
   CONFIG_ENABLE_INT : constant := 2#0000_0011#;  -- 启用+中断
   CONFIG_FAST_MODE  : constant := 2#0000_1101#;  -- 启用+中断+快速模式

   ---------------------------------------------------------------------------
   --  操作子程序
   ---------------------------------------------------------------------------

   procedure Write_Control (Value : in Control_Flags)
      with Inline,
           Global => (Output => Hardware_Register);

   function Read_Status return Control_Flags
      with Inline,
           Volatile_Function;

private

   pragma Volatile (Control_Register);
   -- 告诉编译器此变量可能被硬件异步修改

   Control_Register : Control_Flags
      with Address => System'To_Address (16#4000_1000#),
           Import;

end Hardware_Register;

九、本课总结

  • 基本字符集保证可移植,扩展Unicode标识符慎用

  • 分隔符包括单字符( + - * 等)和双字符( => .. := 等)

  • 数值字面量支持2-16任意进制,使用 基#值# 语法,下划线增强可读性

  • 字符串String / Wide_String / Wide_Wide_String 三级,支持UTF-16/32

  • Pragma向编译器传递指令,涵盖优化、检查、接口、任务等多领域


十、课后练习

1.进制转换:将以下数值转为Ada字面量:

  • 二进制 10110101 (十进制181)

  • 十六进制 DEADBEEF

  • 8兆字节(8 × 1024 × 1024)

2.Pragma应用:为前几课的 Math_Utils 包添加 pragma Purepragma Inline

3.宽字符实践:编写程序输出"Hello 囍 Ada 世界",正确处理Emoji。

4.寄存器定义:使用基数值定义一个32位状态寄存器,包含使能位、错误位、模式字段(2位)。

5.代码审查:找出以下代码中的词法错误:

X := 16#GG#;  -- ?
Y := 2#1012#; -- ?
Z := 1.0E;    -- ?

十一、下节预告

第8课|数据类型概述

我们将:

  • 理解Ada类型系统的哲学:强类型与类型安全

  • 学习标量类型与复合类型的分类体系

  • 掌握类型声明、子类型、派生类型的区别

  • 建立Ada类型系统的整体认知框架


关键术语表

词法单元:源代码分解的最小有意义单元(标识符、字面量、分隔符等)

基数值:任意进制(2-16)表示的整数,语法为 基#数字#

Pragma:向编译器传递特殊指令的语法结构

Wide_String:16位字符字符串,支持UTF-16编码

Elaborate:Ada的初始化阶段,包体和任务的启动执行


提示警告:本课程内容(包括但不限于文字、图片、音频、视频等)版权归原作者所有,未经授权严禁转载、复制、翻录、传播或以任何方式用于商业用途。本课程仅供个人学习使用,请尊重知识产权,共同维护良好的创作环境。如有疑问或需授权合作,请联系版权方。感谢您的理解与支持!