28、如何修改 GNAT 前端以适配 Ada 扩展语言 Drago

如何修改 GNAT 前端以适配 Ada 扩展语言 Drago

1. Drago 语言简介

Drago 是一种作为 Ada 扩展的语言,用于实验容错分布式编程的主动复制模型。在 Drago 中,进程组是一组代理的集合,代理类似于 Ada 任务,有内部状态、独立的控制流和公共操作。一个 Drago 程序由多个位于不同节点的代理组成。

2. 添加新关键字

Drago 新增了四个保留关键字: agent group intragroup replicated 。将这些关键字集成到 GNAT 环境需要以下步骤:
- 第一步:添加新关键字
- GNAT 的预定义标识符列表存储在 Snames 包中,每个预定义标识符有一个常量声明记录其在名称表中的位置。
- 为引入 Drago 关键字,添加了第三种 GNAT 模式(Drago 模式)和一个新的 Drago 专属关键字组:

-- Drago keywords.
First_Drago_Reserved_Word : constant Name_Id := N + 475;
Name_Agent : constant Name_Id := N + 475;
Name_Group : constant Name_Id := N + 476;
Name_Intragroup : constant Name_Id := N + 477;
Name_Replicated : constant Name_Id := N + 478;
Last_Drago_Reserved_Word : constant Name_Id := N + 478;
subtype Drago_Reserved_Words is Name_Id range First_Drago_Reserved_Word .. Last_Drago_Reserved_Word;
- 同时更新 `Snames` 包体中 `Preset Names` 常量的值,保持之前声明的顺序。
  • 第二步:声明新令牌
    • 令牌列表在 Scans 包中声明,是一个枚举类型,元素按类分组。
    • 按如下方式引入新令牌:
type Token_Type is (
    -- Token name Token type Class(es)
    ...
    Tok_If, -- IF Eterm, Sterm, After_SM
    Tok_Intragroup, -- INTRAGROUP Eterm, Sterm, After_SM
    Tok_Pragma, -- PRAGMA Eterm, Sterm, After_SM
    ...
    Tok_Agent, -- AGENT Eterm, Sterm, Cunit, Declk, After_SM
    Tok_Function, -- FUNCTION Eterm, Sterm, Cunit, Declk, After_SM
    Tok_Generic, -- GENERIC Eterm, Sterm, Cunit, Declk, After_SM
    Tok_Group, -- GROUP Eterm, Sterm, Cunit, Declk, After_SM
    Tok_Package, -- PACKAGE Eterm, Sterm, Cunit, Declk, After_SM
    ...
    Tok_Private, -- PRIVATE Eterm, Sterm, Cunit, After_SM
    Tok_Replicated, -- REPLICATED Eterm, Sterm, Cunit, After_SM
    Tok_With, -- WITH Eterm, Sterm, Cunit, After_SM
    ...
    No_Token);
- 根据以下准则为令牌关联类:
    - `intragroup` 必须出现在分号之后。
    - `agent` 和 `group` 可开始一个编译单元和新声明。
    - `replicated` 用于限定组,类似 Ada 95 中的私有包。
- 更新 `Tok_Class_Unit` 子类型声明,使 `Tok_Agent` 成为 `Cunit` 类的第一个令牌,并修改 `Is_Reserved_Keyword` 表。
  • 第三步:修改扫描器初始化
    • 扫描器初始化子程序 Scn.Initialization 负责用对应令牌的字节码标记名称表中的关键字。
    • 添加以下语句到扫描器初始化:
Set_Name_Table_Byte (Name_Agent, Token_Type'Pos (Tok_Agent));
Set_Name_Table_Byte (Name_Group, Token_Type'Pos (Tok_Group));
Set_Name_Table_Byte (Name_Intragroup, Token_Type'Pos (Tok_Intragroup));
Set_Name_Table_Byte (Name_Replicated, Token_Type'Pos (Tok_Replicated));
- 修改扫描器 `Scn.Scan`,使其仅在编译 Drago 程序时识别新关键字。
3. 添加一个编译指示和一个属性

Drago 提供了一个新属性 Member_Identifier 和一个新编译指示 Drago 。将它们集成到 GNAT 需要对 Snames 包进行以下修改:
1. 按字母顺序将它们的声明添加到预定义标识符列表中。 Drago 编译指示属于非配置编译指示组, Member_Identifier 属性属于其他属性组。
2. 将它们的声明插入枚举类型 Pragma_Id Attribute_Id 中,保持顺序。
3. 将它们的字面量添加到 Preset Names 中。
4. 使用 GNAT 实用程序 xsnames 自动更新 C 文件 a-snames.h

4. 添加新语法规则

以 Drago 组的规范为例,其语法类似于 Ada 包规范:

group declaration ::= group specification
group specification ::=
    [replicated] group defining identifier is
        {basic declarative item}
        [intragroup
            {basic declarative item}]
        [private
            {basic declarative item}]
    end [group identifier];

添加此语法到 GNAT 解析器的步骤如下:
- 第一步:添加新节点类型
- GNAT 将源程序的语义和语法信息存储在抽象语法树(AST)中,节点分为扩展节点和非扩展节点,按节点类型分类。
- 为 Drago 组的规则添加两种新节点类型: N_Group_Declaration N_Group_Specification ,并将它们分别放置在与 N_Package_Declaration N_Package_Specification 相关的类中。
- 第二步:添加新模板
- Sinfo 包规范中的模板定义了每种节点类型处理的语法和语义信息。
- N_Group_Declaration N_Group_Specification 的模板如下:

-- N_Group_Declaration
-- Sloc points to GROUP
-- Specification (Node1)
-- N_Group_Specification
-- Sloc points to GROUP
-- Defining_Identifier (Node1)
-- Visible_Declarations (List2)
-- Intragroup_Declarations (List3) (set to No_List if no intragroup part present)
-- Private_Declarations (List4) (set to No_List if no private part present)
- 为每个别名声明两个子程序(一个过程和一个函数),并更新相应的断言编译指示。
  • 第三步:自动修改前端
    • 使用三个 GNAT 实用程序( xsinfo xtreeprs xnmake )根据上一步的模板自动生成四个前端源文件: a-sinfo.h treeprs.ads nmake.ads nmake.adb
graph LR
    A[xnmake] --> B[nmake.ads, nmake.adb]
    C[xsinfo] --> D[a-sinfo.h]
    E[xtreeprs] --> F[treeprs.ads]
    G[sinfo.ads] --> A
    G --> C
    G --> E
  • 第四步:更新解析器
    • GNAT 解析器使用递归下降技术实现,代码在函数 Par 中,按 ARM 章节分为多个子单元。
    • 添加子单元 Par.Drag 用于分析 Drago 规则。
    • 为组规范解析关联的函数命名为 P_Group ,参考分析包规范的解析器片段进行开发。
    • 修改编译单元的解析,使其在分析 Drago 源文件并检测到 group 令牌时调用 P_Group 函数。
5. 语义验证

解析器完成工作后,语义分析器对 AST 进行自上而下的遍历,根据节点类型调用相应的子程序进行语义验证。对 Drago 组规范进行语义验证需要对 GNAT 源文件进行以下修改:
1. 添加一个新包 Sem_Drag ,包含所有 Drago 语义验证子程序。
2. 添加一个新的语义实体 E_Group 到枚举类型 Entity_Info 中,用于表示组名称实体。
3. 使用 GNAT 实用程序 xeinfo 自动更新 C 文件 a-einfo.h

graph LR
    A[xeinfo] --> B[a-einfo.h]
    C[einfo.ads] --> A
    D[einfo.adb] --> A
  1. 为解析器中声明的每种新节点类型编写一个子程序,将其放在 Sem_Drag 包中,参考分析包规范的子程序进行编写。
  2. 修改 Sem 包,使其在分析组规范(或声明)节点时调用这些子程序。
6. 树扩展

树扩展与语义阶段交织。GNAT 验证节点语义时,若需要会调用一个过程对节点进行扩展,即将一个高级节点替换为一个包含更低抽象级别节点的子树。在我们的情况下,创建树的镜像,将 Drago 节点转换为调用多个运行时子程序的 Ada 95 节点,最后调用原始的 GNAT 扩展器将这个 Ada 95 树扩展为 GIGI 源树。

7. 当前工作

目前已经有一个修改后的 GNAT 版本,可对 Drago 语言进行所有静态验证(语法和语义)。接近完成 Drago 运行时系统的集成,该实现重用了 GNAT 运行时库,旨在运行于 Linux 网络上,还使用了一个 Ada 包库,通过原创的共识协议提供可靠的原子广播。当前的发行版要求用户按照 GNAT 发行版的步骤重新编译和安装系统,后续打算修改 gnat 二进制发行版的脚本以方便安装。

8. 总结

本文介绍了将 Drago 集成到 GNAT 前端的过程,重点关注了词汇、语法和语义方面的集成。由于篇幅限制,抽象语法树扩展和代码生成未作讨论。

如何修改 GNAT 前端以适配 Ada 扩展语言 Drago

9. 关键操作步骤总结

为了更清晰地展示修改 GNAT 前端以适配 Drago 语言的过程,下面将关键操作步骤进行总结:
|操作类型|具体步骤|
| ---- | ---- |
|添加新关键字|1. 在 Snames 包中添加新关键字声明,定义 Drago 模式和专属关键字组。
2. 更新 Preset Names 常量的值。
3. 在 Scans 包中声明新令牌,并为其关联类。
4. 更新 Tok_Class_Unit 子类型声明和 Is_Reserved_Keyword 表。
5. 在扫描器初始化中添加标记关键字的语句,修改扫描器使其仅在编译 Drago 程序时识别新关键字。|
|添加编译指示和属性|1. 按字母顺序将编译指示和属性声明添加到 Snames 包的预定义标识符列表中。
2. 将声明插入枚举类型 Pragma_Id Attribute_Id 中。
3. 将字面量添加到 Preset Names 中。
4. 使用 xsnames 自动更新 a-snames.h 文件。|
|添加新语法规则|1. 在 Sinfo.Node_Kind 中添加新节点类型。
2. 在 Sinfo 包规范中添加新模板,并为别名声明子程序。
3. 使用 xsinfo xtreeprs xnmake 自动生成前端源文件。
4. 添加 Par.Drag 子单元,开发 P_Group 函数,修改编译单元解析以调用该函数。|
|语义验证|1. 添加 Sem_Drag 包。
2. 在 Entity_Info 中添加新语义实体 E_Group
3. 使用 xeinfo 自动更新 a-einfo.h 文件。
4. 为新节点类型编写子程序并放在 Sem_Drag 包中。
5. 修改 Sem 包以调用这些子程序。|
|树扩展|创建树的镜像,将 Drago 节点转换为 Ada 95 节点,调用原始 GNAT 扩展器将 Ada 95 树扩展为 GIGI 源树。|

10. 操作流程可视化

下面通过 mermaid 流程图展示整个修改 GNAT 前端以适配 Drago 语言的操作流程:

graph TD
    A[开始] --> B[添加新关键字]
    B --> C[添加编译指示和属性]
    C --> D[添加新语法规则]
    D --> E[语义验证]
    E --> F[树扩展]
    F --> G[当前工作:集成运行时系统]
    G --> H[结束]
11. 技术点分析
  • 关键字和令牌的处理 :通过添加新的 GNAT 模式和关键字组,以及更新相关的常量和表,确保扫描器能够正确识别 Drago 关键字。在声明新令牌时,根据不同的规则为其关联类,使得解析器能够更好地处理这些关键字。
  • 语法规则的添加 :利用抽象语法树(AST)和节点类型的概念,为 Drago 语言的新语法规则添加对应的节点类型和模板。通过 GNAT 实用程序自动生成前端源文件,减少手动编写代码的工作量。
  • 语义验证 :通过添加新的包和语义实体,以及编写相应的子程序,对 Drago 语言的语义进行验证,确保代码的正确性。
  • 树扩展 :将 Drago 节点转换为 Ada 95 节点,并利用原始的 GNAT 扩展器进行树扩展,实现了 Drago 语言与 GNAT 前端的集成。
12. 实际应用建议
  • 开发环境配置 :在进行 Drago 语言开发时,确保使用修改后的 GNAT 版本,并按照当前发行版的要求进行编译和安装。
  • 代码编写规范 :遵循 Drago 语言的语法规则和语义要求进行代码编写,注意关键字、编译指示和属性的正确使用。
  • 调试和测试 :在开发过程中,利用 GNAT 提供的工具进行语法和语义的静态验证,及时发现和解决问题。同时,对运行时系统进行充分的测试,确保其稳定性和可靠性。
13. 总结回顾

通过以上一系列的操作,我们成功地将 Drago 语言集成到了 GNAT 前端。从添加新关键字、编译指示和属性,到添加新语法规则、进行语义验证和树扩展,每一个步骤都紧密相连,共同实现了对 Ada 扩展语言 Drago 的支持。目前,虽然已经完成了大部分工作,但仍需对运行时系统进行进一步的优化和完善,以提高开发效率和代码质量。希望本文能够为相关开发者提供有价值的参考,帮助他们更好地进行 Drago 语言的开发和应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值