跳转到内容

Oberon/ETH Oberon/Tutorial/编译器

来自 Wikibooks,开放世界中的开放书籍

这些教程页面由 André Fischer (afi) 编写,Hannes Marais 提供编辑协助,托管在 ETHZ,并保留在 ETH 许可 下。相关内容可以在系统中通过 Book.Tool 找到。扩展内容也可在 纸质版 上找到。一些教程页面在 WayBack 存档 中。

编译器、构建器和分析器用户指南

教程目标

[编辑 | 编辑源代码]

学习使用 Oberon 编译器及其称为构建器的前端。

预计时间:30 分钟。

编译器的输入是文本。这些文本方便地在文本查看器中使用系统编辑器[1]创建。以下命名约定 (广泛使用) 建议用于存储源程序的文件

name.Mod 其中 name 通常对应于模块的名称 (MODULE name;)。

有关 Oberon 编程语言的介绍,请参阅 有关 Oberon 的文献。但学习一门新的编程语言的唯一方法是阅读和编写该语言的程序。

第一个例子

[编辑 | 编辑源代码]

第一个程序对于所有编程语言都是一样的!在 Oberon 中,编写 hello, world 的程序是

MODULE Hello;
 IMPORT Out;

 PROCEDURE World*;
 BEGIN
  Out.String("hello, world");
  Out.Ln
 END World;

END Hello.

要学习如何使用编译器,请按照以下步骤操作

  1. 标记 上面的文本并执行 Compiler.Compile *[2]
  2. 观察 Oberon 日志中的消息
    正在编译 Hello 33
    模块名称后面的数字是现在存储在当前目录中的目标文件的大小。
  3. 执行 Hello.World 并观察日志中的输出 hello, world。

使用编译器

[编辑 | 编辑源代码]

编译器是 Oberon 系统的重要组成部分。除了内核的一部分,Oberon 系统 3 是用原始的 Oberon 语言实现的。提供的编译器是 Oberon-2 编译器。原则上,任何 Oberon-2 编译器都与系统 3 兼容,并且可以使用 Oberon-2 编写和编译系统 3 程序。 编译器可以生成两种类型的目标文件:包含目标机器代码的经典本地目标文件,或者默认情况下,瘦二进制文件。瘦二进制文件是一种新的目标文件形式,它根本不包含目标代码,而是一个模块内容的便携式描述,这使得这些文件完全独立于最终的目标机器 (平台无关)。目标代码生成是由模块加载器 (取决于底层硬件) 在运行时进行的,并且与加载传统目标文件的时间一样长。

模块 Compiler 只导出两个命令,它们在 Compiler.Tool 中的几个变体中都有文档,并在下面描述。

编译源代码(s) - Compiler.Compile

[编辑 | 编辑源代码]

Compiler.Compile @[\options] 编译从最近的文本选择开始的源代码。

Compiler.Compile *[\options] 编译标记的查看器的源代码。

这两个第一个命令变体允许直接从文本编辑器编译模块:不需要先存储文本。

Compiler.Compile {[\dst=pathName] [\options] {fileName[\options]}}~ | ^ 编译参数列表中命名的文件的文本。pathName 指示编译器将新目标文件存储在指定的子目录中。

options

s - 启用生成新的 symbol 文件
e - 启用生成 extended 符号文件
u - 如果目标文件是最新的,则抑制编译
w - 启用生成 warning 消息

编译一系列模块时,文件名顺序必须符合模块导入层次结构:从底层到顶层;也就是说,模块的客户端必须在模块本身之后编译。您可以通过使用 构建器 功能来避免这种情况。请注意,每个文件名后都可以跟选项。

以下选项仅在编译器必须生成本地目标文件时才有效。这些文件跨平台边界可移植。因此,这些选项应仅在编写使用低级模块 SYSTEM 或内置 SIZE 函数的扩展时使用。示例:Win.Audio.Mod

N - nable 生成本地目标文件
a - 抑制为 ASSERT 函数生成代码
p - 抑制将本地 pointers 初始化为 NIL
x - 禁用 Index 检查
n - 禁用 NIL 检查
t - 禁用 Type 检查
v - 启用 Overflow 检查
c - 禁用 Range Checks
r - 抑制生成 ref-info
d - 生成 MacsBug debugger 信息 (MacOberon)
f - find 对应于所选程序计数器值的文本位置
g - 抑制此次编译后的 garbage 收集

如果抑制垃圾收集,编译运行速度更快,但如果使用了太多内存或太多文件,则可能无法完成编译 (将捕获)。

指定目标文件路径 - Compiler.SetDestPath

[edit | edit source]

Compiler.SetDestPath pathName 指示编译器将新的目标文件存储在指定的子目录中。

指导加载精简二进制文件

[edit | edit source]

以下命令影响精简二进制文件的加载

OMI.AssertsOn / OMI.AssertsOff 启用/禁用为 ASSERTS 函数生成代码。

OMI.ChecksOn / OMI.ChecksOff 启用/禁用索引检查。

OMI.RefsOn / OMI.RefsOff 启用/禁用生成详细引用信息。

这些命令可以方便地放在 Configuration.Text 文件中。

在执行之前卸载模块

[edit | edit source]

如果您对模块进行更改,并在使用(即加载)后重新编译它,您必须使用 System.Free 命令或使用鼠标将其卸载

当鼠标焦点位于命令名称 M.P 上并且按下鼠标中键时,□ ■  左键点击会卸载包含该命令的模块 M 并加载它的新副本,然后执行该命令。

程序员提示和警告: 这在开发和调试 Oberon 过程时非常有用。这是一个方便的快捷方式来执行 System.Free 命令,但只有在模块 M 没有客户端时才会卸载它。因此,请记住 System.Free 以及 Builder.Free 提供了额外的可能性。

如果您的更正没有产生任何影响,请不要感到惊讶:旧的 模块版本仍在控制中!您经常会忘记强制系统使用更新的版本。

以下示例应有助于具体化之前所说的话

MODULE Client;
  IMPORT Hello;

  PROCEDURE MyAction*;
  BEGIN Hello.World
  END MyAction;

END Client.
  1. 选择模块文本的开头
  2. 执行 Compiler.Compile @
  3. 执行 Client.MyAction
  4. 执行 System.Free Hello ~
  5. 观察并解释日志消息:Hello 卸载失败

编译修改后的示例

[edit | edit source]
MODULE Hello;
  IMPORT Out;

  PROCEDURE World*;
  BEGIN
    Out.String("hello, world");
    Out.Ln
  END World;

END HELLO.
  1. 修改文本 "hello, world"(例如 "Hello Oberon")
  2. 执行 Compiler.Compile *(即重新编译程序)
  3. 执行 Hello.World 并查看 Oberon 日志中如何显示相同的 "hello, world"。
  4. 强制系统卸载模块并使用鼠标中键 + 左键点击执行命令 Hello.World 的新版本。

定位和纠正语法错误

[edit | edit source]
MODULE Hello;
  IMPORT Out;

  PROCEDURE World*;
  BEGIN
    Out.String("hello, world");
    Out.Ln
  END World;

END HELLO.
  1. 删除程序中的分号
  2. 将插入符号移动到文本中的另一个位置
  3. 执行 Compiler.Compile *
      然后 Oberon 日志显示
     compiling Hello pos nn err 39
  4. 选择包含错误消息的行,然后单击 Oberon 日志菜单栏中的 [Locate] 按钮。这将把插入符号设置在缺少分号的位置。错误可以很容易地纠正。

调试编译器检测到的语法错误

[edit | edit source]

编译器检测到的错误在 Oberon 日志中以以下形式的消息报告
pos <错误位置> err <错误编号>

含义:错误 <错误编号> 发生在源文本中的位置 <错误位置>。

以下过程将插入符号放置在源文本中的错误位置

  1. 确保源文本出现在查看器中
  2. 标记查看器
  3. 在 Oberon 日志中选择包含错误消息的行
  4. 单击菜单栏中的 [Locate] 按钮或
  5. 执行命令 TextDocs.Locate 或
  6. 执行命令 Edit.Locate.

在某些版本中,Oberon 编译器错误编号及其简短说明列在 OberonErrors.Text 中。在 PlugIn Oberon 中,它运行在各种 MS Windows 系统上,该文件名为 OP2.Errors。在 Oberon 2.3.7 中,类似的列表位于 Oberon.Text 的 Errors 部分[3]

您需要重新编译模块,直到没有报告错误。但是,可能会发出警告,但它们不被视为错误,因此会创建目标文件。

陷阱查看器

[edit | edit source]

如果命令执行失败,运行时错误会导致程序异常终止,并且会在文本查看器 'System.Trap' 中显示错误报告。导致陷阱的错误由陷阱代码标识,显示某些系统寄存器的内容,并显示过程激活堆栈,从调用堆栈顶部的过程开始。所有标量变量和字符串以及它们的值也列出。

MODULE Trap;
  VAR arr: ARRAY 3 OF INTEGER;

  PROCEDURE count(n: INTEGER);
  BEGIN arr[n] := n; count(n+1)
  END count;

  PROCEDURE ForceIt*;
  BEGIN count(0)
  END ForceIt;

END Trap.
  1. 选择模块文本的开头
  2. 执行 Compiler.Compile @
  3. 执行 Trap.ForceIt 并研究查看器的内容。

最初,陷阱代码在 OberonErrors.Text 的末尾进行了解释。随后,当陷阱查看器被创建或增强时,OberonErrors.Text 中的解释变得多余并被删除。

为了找到失败的语句,只需显示源程序并标记其查看器。然后在陷阱查看器中选择包含感兴趣过程名称的行,并使用 "\d" 或 "\f" 选项重新编译程序。

使用构建器

[edit | edit source]

Builder 为上面描述的 Oberon 编译器提供了一个方便的前端。它确保模块文本以正确的顺序呈现给编译器,无论参数列表中文件名的顺序如何。但是,它不是真正的 make 工具:必须指定所有要编译的模块。请注意,参数列表必须包含文件名:这些文件名可能与模块名称不同,但这只是一个约定问题。Builder 模块命令在 Builder.Tool 中有记录。

编译源文本 - Builder.Compile

[edit | edit source]

Builder.Compile [\options] * 编译标记的查看器的源文本。文本中插入的所有错误标记都将被删除。

Builder.Compile [\options] ({fileName}~ | ^ | *) 编译参数列表中命名的文件的文本,自动确定模块的正确编译顺序。必须指定所有要编译的模块,即它不是真正的 make 工具。选项与 Compiler.Compile 命令的选项相同。

有效处理编译器检测到的错误

[edit | edit source]

Builder.MarkErrors [^] 当选择包含编译器在 Oberon 日志中写入的错误消息时,此命令会为该消息以及所有后续消息在标记的文本中插入错误标记。错误标记是一个特殊的(构建器)小工具,它显示在程序文本中该位置发现的错误代码的编号。以下错误消息

pos 111 err 4

将在位置 111 处放置 4

Builder.NextError 将插入符号前进到下一个错误标记。当到达文本末尾时,搜索将环绕回到开头。

Builder.ClearErrors 删除标记文本中的所有错误标记。由 Builder.Compile * 命令自动执行。

卸载模块 - Builder.Free

[edit | edit source]

Builder.Free {fileName}~ | ^ 以正确的顺序卸载参数列表中命名的每个模块。由于文件名必须出现在参数列表中,因此此命令只能释放源文本可用的模块。要卸载其他模块,请使用 System.Free.

插入模块图标 - Builder.InsertHierarchy

[edit | edit source]

Builder.InsertHierarchy {fileName}~ | ^ 在插入符号处为参数列表中命名的每个模块插入一个图标。图标以正确的编译顺序插入。每个图标都带有文件名作为标题,并且它的Cmd 属性值为 "Desktops.OpenDoc '#Caption '". Compiler.Panel 中可以找到此类图标的示例。

使用分析器

[edit | edit source]

分析器为 Oberon 编译器提供了一个便捷的补充,用于在语法正确的程序文本中查找其他潜在错误。编译器错误首先报告。不生成目标代码。分析器定位

  • 未导出的项目(变量/常量/类型/字段),这些项目已声明但从未使用过,在初始化 (*) 之前使用过,从未初始化过,以及初始化过但从未使用过。
  • 未导出的 [类型绑定] 过程,这些过程从未调用过。
  • 从未使用的导入模块。

(*) 对于在不同范围内声明的变量,不会产生警告(但是,请参阅选项 \u)。

分析器模块命令在 Analyzer.Tool. 中有记录。

分析源文本 - Analyzer.Analyze

[编辑 | 编辑源代码]

Analyzer.Analyze @[\options] 分析从最新文本选择开始的源文本。

Analyzer.Analyze *[\options] 分析标记查看器的源文本。

这两个命令的第一个变体允许直接从文本编辑器分析模块:无需先存储文本。

Analyzer.Analyze {fileName[\options]}~ | ^ 分析参数列表中命名的文件的源文本。

Analyzer.Analyze 通过以下形式的消息警告 Oberon 日志中可能出现的错误

pos <error position> warning <warning number>

请参阅 调试编译器检测到的语法错误.

Analyzer.Analyze 还报告模块中语句(赋值、if、while、过程调用等)的数量。这对于确定程序的复杂性(而不是行数)很方便。

错误标记可以插入源文本中,如 有效处理编译器检测到的错误 中所述。

可以通过指定一个或多个 选项 获得更多信息

i (intermediate) - 定位
- 已经在外部范围内声明的项目。
- 对中间项目的使用或赋值,例如在外部范围内声明的局部变量/参数。
s (evaluation sequence) - 定位参数列表中出现函数调用的多参数过程的调用。参数的求值顺序可能会影响这些函数的副作用。
t (type-bound) - 定位
- 类型绑定过程的重新定义。
- 在扩展类型中对类型绑定过程的重新定义。
u (used) - 定位在不同范围内声明并在设置之前使用的变量。
v (VAR parameter) - 定位用作 VAR 参数的变量,因此不能确保已初始化这些变量。
x (exported) - 定位已声明但模块本身未使用、在初始化之前使用、从未初始化或已初始化但从未使用的导出项目。
[ A | B | C | E | N | O | S | T | U ]

A

Analyzer.Analyze
Analyzer.Tool

B

Builder.ClearErrors
Builder.Compile
Builder.Free
Builder.InsertHierarchy
Builder.MarkErrors
Builder.NextError
Builder.Tool

C

Compiler.Compile
Compiler.Panel
Compiler.SetDestPath
Compiler.Tool

E

错误标记

N

本地目标文件

O

OberonErrors.Text

S

精简二进制文件

T

陷阱查看器

U

卸载模块


修订,afi 1996 年 12 月 10 日
安装于 1997 年 05 月 30 日


  1. 在 V2(Ceres Oberon)中 Edit.Open <fileName> 。在 ETH Oberon 中 Edit.Open <fileName>ET.Open <fileName> 。在 V5(FPGA Oberon)中 Edit.Open <fileName>
  2. 可以在 ETH Oberon 中使用 MM 执行的命令是深红色。目标超链接是蓝色。非目标超链接是猩红色。
  3. 邮件列表有一个从 2023-01-07 开始的讨论。
华夏公益教科书