编辑“︁
Modula-3
”︁(章节)
跳转到导航
跳转到搜索
警告:
您没有登录。如果您进行任何编辑,您的IP地址会公开展示。如果您
登录
或
创建账号
,您的编辑会以您的用户名署名,此外还有其他益处。
反垃圾检查。
不要
加入这个!
==基础性语法和语义== Modula-3承袭了[[ALGOL]]家族传统的[[强类型]],并且为了使[[类型系统]]尽可能的统一,采用了如下原则: *首先,类型或目标类型确定是没有歧义的,所有的[[表达式]]的类型由它的子表达式来确定,而非由它的使用来确定。 *其次,没有自动[[类型转换|转换]]。 *第三,可赋值性和类型兼容性,是依据单一的在语法上用[[算子 (編程)|算符]]<code><:</code>指定的[[子类型]][[关系 (数学)|关系]]来定义的,它具有如下性质:如果<code>T</code>是<code>U</code>的[[子类型]],则所有的<code>T</code>的成员都是<code>U</code>的[[元素 (数学)|成员]],子类型关系是[[自反关系|自反]]和[[传递关系|传递]]的;子类型关系是处理具有[[继承 (计算机科学)|继承]]的[[对象 (计算机科学)|对象]]所需要的,但也能用于确定常规类型的兼容性规则。 一个典型的不安全运行时间错误,是释放仍可由活跃引用([[迷途指针|悬空指针]])触及的数据结构。避免这个问题的唯一可靠方式,是不再触及存储的自动释放,或称为[[垃圾收集 (计算机科学)|垃圾收集]]。Modula-3因而提供了有跟踪的[[參照|引用]],它类似于[[Modula-2]]的[[指针]],除了它们所指向的存储被保存在有跟踪的堆之中之外,在这里它们将在到它们的引用都消失时自动释放。垃圾收集的另一个巨大利益是简化了接口,没有垃圾收集,接口就必须规定客户或实现是否有责任释放每个分配的引用,和在什么条件下可以安全的去这么做。 [[例外处理 (编程)|例外]]是一次退出多层[[作用域]]的[[控制结构]]。一个例外被重复的发起退出当前活跃的作用域,直到找到给这个例外的处理器(handler),并将控制权移交给这个处理器,如果没有处理器,则计算以某种依赖于系统的方式终止,比如进入[[调试器]]。在Modula-3中,<code>EXIT</code>和<code>RETURN</code>语句的语义,分别使用退出例外<code>exit-exception</code>和返回例外<code>return-exception</code>来定义。Modula-3的设计者认为语言实现,除了这种返回例外和退出例外之外,更应当以各种例外为代价,加速正常的结果产出;为每个引发的例外花费一千条指令,来为每个过程节约一条指令都是合理的。 ===块与声明=== [[声明 (编程)|声明]]介入一个常量、类型、变量、例外或过程的一个名字。这个名字的[[作用域]]是包含这个声明的[[块 (编程)|块]]。一个块可以出现为一个模块或过程的本体,或一个块[[语句 (计算机科学)|语句]],[[Pascal语言|Pascal]]和[[Modula-2]]没有块语句<ref>{{cite web|title=Modula-2 Reference - Statements|url=https://www.modula2.org/reference/statements.php|access-date=2023-02-21|archive-date=2023-02-21|archive-url=https://web.archive.org/web/20230221111935/https://www.modula2.org/reference/statements.php|dead-url=no}}</ref>。一个块有如下形式: <syntaxhighlight lang="modula2"> Decls BEGIN S END </syntaxhighlight> 这里的<code>Decls</code>是成序列的声明,而<code>S</code>是一个语句,经常是顺序复合语句即成序列的语句,它是这个块的执行部份。这里的<code>Decls</code>与<code>S</code>在[[缩进风格|缩进]]上对齐,是因为在[[Pascal语言|Pascal]]语言家族中,可能含有嵌套过程声明的声明序列的位置相较于[[ALGOL 60]],从紧随在关键字<code>BEGIN</code>之后转移到了紧靠在其前面。一个块的声明可以介入一个名字最多一次,然而一个名字可以在嵌套的块中重新声明。在块中这些声明的次序,除了确定变量的初始化次序之外无关紧要。 一个Modula-3程序规定了一个计算,它作用在一序列的叫做[[記憶體位址|地点]](location)的数字元件之上。[[变量 (程序设计)|变量]]是地点的一个集合,它依据这个变量的[[类型系统|类型]]所确定的约定,表示一个数学上的{{le|值 (数学)|Value (mathematics)|值}}。如果一个值可以由类型<code>T</code>的某个变量来表示,则称谓这个值是<code>T</code>的一个成员,并且<code>T</code>包含这个值。 [[声明 (编程)|声明]]为一个[[变量 (程序设计)|变量]]、[[类型系统|类型]]、[[子程序|过程]]、{{le|常量 (计算机编程)|Constant (computer programming)|常量}}或[[例外处理 (编程)|例外]]指定一个被称为[[标识符 (计算机语言)|标识符]]的{{le|符号 (编程)|Symbol (programming)|符号}}作为名字。一个声明应用于其上的程序区域,叫做这个声明的[[作用域]]。作用域是可以嵌套的。一个标识符的含义,由这个标识符在其中声明的最小的包围作用域来确定。 [[类型系统|类型]]声明有如下形式:{{code|2="modula2"|1=TYPE T = U}},这里的<code>T</code>是一个标识符,而<code>U</code>是一个类型或类型表达式。在需要类型的各种地方,通常允许类型表达式。 [[变量 (程序设计)|变量]]声明有如下形式:{{code|2="modula2"|1=VAR id: T := E}},这里的<code>id</code>是一个标识符,<code>T</code>是除了开放数组之外的一个非空类型,而<code>E</code>是一个表达式。它声明了<code>id</code>是类型<code>T</code>的一个变量,其初始值是<code>E</code>的值。<code>:= E</code>和<code>: T</code>任何一个都可省略,但二者不能都省略。如果省略了<code>T</code>,它采用<code>E</code>的类型。如果省略了<code>E</code>,初始值是类型<code>T</code>的任意值。如果二者都存在,<code>E</code>必须可赋值给<code>T</code>。初始值是一种简写,等价于在随后的[[块 (编程)|块]]的可执行部份开始处插入赋值<code>id := E</code>,如果多个变量有初始值,按它们声明的次序插入它们的赋值。形式{{code|2="modula2"|1=VAR v_1, ..., v_n: T := E}},是{{code|2="modula2"|1=VAR v_1: T := E; ...; VAR v_n: T := E}}的简写。 {{le|常量 (计算机编程)|Constant (computer programming)|常量}}声明有如下形式:{{code|2="modula2"|1=CONST id: T = C}},这里的<code>id</code>是一个标识符,<code>T</code>是一个类型,而<code>C</code>是一个常量表达式。它声明<code>id</code>为具有类型<code>T</code>和值<code>C</code>的一个常量。<code>: T</code>可以省略,在这种情况下<code>id</code>的类型是<code>C</code>的类型。如果<code>T</code>存在,则它必须包含<code>C</code>。 ===值与类型=== Modula-3的[[类型系统]]使用{{le|结构类型系统|Structural type system|结构等价}},而非[[Modula-2]]的{{le|名称类型系统|Nominal type system|名称等价}}即类型构造子的每次出现产生一个新类型。在结构等价中,在如果两个类型的定义在展开的时候,也就是在所有常量表达式都被替换为它们的[[值 (计算机科学)|值]],并且所有类型名字都被替代为它们的定义的时候,变成相同的,则这两个类型是相同的。在[[递归数据类型]]的情况下,这个展开是部份展开的[[无穷]][[极限 (数学)|极限]]。 有三种[[序数]]类型:[[枚举]]、[[区间|子范围]]和[[整数 (计算机科学)|整数]]类型,预定义了如下序数类型:<code>INTEGER</code>、<code>LONGINT</code>、<code>[[基数 (数学)|CARDINAL]]</code>(如同子范围<code>[0..LAST(INTEGER)]</code>)、<code>[[真值|BOOLEAN]]</code>(枚举<code>{FALSE, TRUE}</code>)和<code>[[字符 (计算机科学)|CHAR]]</code>(包含至少256个元素表示[[ISO/IEC 8859-1|ISO-Latin-1]]代码的枚举)。[[整数]]和[[枚举]][[元素 (数学)|元素]]合起来叫做序数[[值 (计算机科学)|值]]。序数值<code>v</code>如果是整数或扩展精度整数,则<code>v</code>的基础类型分别是<code>INTEGER</code>或<code>LONGINT</code>,否则<code>v</code>的基础类型是包含它的那个唯一性的枚举类型。两个序数如果有相同的值则二者相等。 有三种浮点类型:<code>[[实数|REAL]]</code>、<code>LONGREAL</code>和<code>EXTENDED</code>,这些类型的属性规定在对应的必要接口中。两个[[浮点数]]如果定义它们的底层实现相同则二者相等。 一个[[参照|引用]]值要么是<code>[[空指针|NIL]]</code>,要么是一个叫做所引用者(referent)的变量的[[地址空间|地址]]。预定义了如下[[参照|引用]]类型:<code>REFANY</code>,它包含所有的有跟踪的引用,<code>ADDRESS</code>,它包含所有的无跟踪的引用,和<code>[[空指针|NULL]]</code>,它只包含<code>[[空指针|NIL]]</code>。Modula-3支持在[[执行期|运行时间]]的数据[[内存分配|分配]]。分配操作有如下形式:{{code|2="modula2"|1=NEW(T, ...)}},这里的<code>T</code>是除了预定义的<code>REFANY</code>、<code>ADDRESS</code>或<code>NULL</code>之外的引用类型。两个[[参照|引用]]如果[[地址空间|寻址]]相同的[[記憶體位址|地点]]则二者相等。 [[记录]]是叫做这个记录的[[字段]]的命名[[变量 (程序设计)|变量]]的一个[[序列]],记录类型的声明有如下形式:{{code|2="modula2"|1=TYPE T = RECORD FieldList END}},在这里的类型表达式中,{{le|算符 (计算机科学)|Operator (computer programming)|算子}}<code>RECORD</code>是[[类型构造子]],而它的实际参数是字段声明的一个列表<code>FieldList</code>,其中每个字段都有如下形式:{{code|2="objectpascal"|1=fieldName: Type := default}},这里的字段名字<code>fieldName</code>是一个标识符,而字段类型<code>Type</code>是除了开放数组之外的非空类型,而字段缺省值<code>default</code>是一个常量表达式。形式{{code|2="objectpascal"|1=f_1, ..., f_m: Type := default}},是{{code|2="objectpascal"|1=f_1: Type := default; ...; f_m: Type := default}}的简写。两个记录如果有相同的字段,并且对应的字段都相等则二者相等。 在Modula-3中的[[对象 (计算机科学)|对象]],非常类似于[[Simula|Simula 67]]中的对象:它们总是引用,它们拥有数据[[字段]]和[[方法 (计算机科学)|方法]]二者,并且它们有着单一[[继承 (计算机科学)|继承]]而非[[多重继承]]。预定义了如下[[对象 (计算机科学)|对象]]类型:<code>ROOT</code>,它是没有字段或方法的有跟踪的对象类型,和<code>UNTRACED ROOT</code>,它是没有字段或方法的无跟踪的对象类型。 [[不透明数据类型|不透明类型]]是一个名字,它指示某个给定[[引用类型]]的未知[[子类型]]。预定义了两个[[不透明数据类型|不透明类型]]:<code>TEXT <: REFANY</code>和<code>MUTEX <: ROOT</code>,它们分别表示[[文本]][[字符串|串]]和[[互斥锁|互斥]][[信号量]],它们的属性分别规定于必要接口<code>Text</code>和<code>Thread</code>之中。 [[数组]]是叫做这个数组的[[元素 (数学)|元素]]的组成[[变量 (程序设计)|变量]]的有索引的[[集合 (计算机科学)|搜集]]。有两种数组类型,固定的和开放的。固定数组的长度是在编译时间确定的,开放数组的长度是在运行时间分配确定的,这个长度此后不能改变。两个数组如果有相同的长度,并且对应的元素都相等则二者相等。 [[位段|压缩]]类型的变量出现在记录、对象或数组中,占据指定数量的[[位元]],并毗邻于前导的字段或元素紧压打包。压缩类型的声明有如下形式:{{code|2="modula2"|1=TYPE T = BITS n FOR Base}},这里的<code>Base</code>是一个基础类型,而<code>n</code>是整数取值的常量表达式。 [[集合 (数学)|集合]]是取自某个序数类型的[[值 (计算机科学)|值]]的[[集合 (计算机科学)|搜集]]。集合类型声明有如下形式:{{code|2="modula2"|1=TYPE T = SET OF Base}},这里的基础类型<code>Base</code>是一个序数类型。两个集合如果有相同的元素则二者相等。 不包含值的一个类型是空的,例如<code>[1..0]</code>是一个空类型。空类型可以用来建造非空类型,比如<code>SET OF [1..0]</code>。声明一个空类型的一个变量是静态错误。 所有的{{le|表达式 (计算机科学)|Expression (computer science)|表达式}}都有一个唯一的类型,但是一个[[值 (计算机科学)|值]]可以是很多类型的一个[[元素 (数学)|成员]],例如值<code>6</code>是<code>[0..9]</code>和<code>INTEGER</code>二者的成员,因此论述“一个值的类型”将会是有歧义的。短语“<code>x</code>的类型”,意味着“表达式<code>x</code>的类型”;然而短语“<code>x</code>是<code>T</code>的成员”,意味着“<code>x</code>的值是<code>T</code>的成员”。但在有一种情况下,一个值可以称为有着一个类型:所有的[[对象 (计算机科学)|对象]]或有跟踪的[[参照|引用]]值,包括了一个类型代码,它叫做这个引用值的分配类型。分配类型通过<code>TYPECASE</code>语句来测试。 语言规定提供了类型运算<code>BITSIZE()</code>、<code>BYTESIZE()</code>和<code>ADRSIZE()</code>,分别返回变量<code>x</code>或者类型<code>T</code>的那些变量的[[位元]]数目、8位[[字节]]的数目和可[[地址空间|寻址]][[記憶體位址|地点]]的数目。在所有情况下,<code>x</code>必须是指定式,而<code>T</code>必须不是开放数组类型,指定式<code>x</code>只在它的类型是开放数组的时候求值。 ====序数类型==== [[枚举]]类型声明有如下形式:{{code|2="modula2"|1=TYPE T = {id_1, id_2, ..., id_n} }},这里的<code>id</code>是各不相同的标识符,类型<code>T</code>是<code>n</code>个值的[[全序关系|有序集合]],表达式<code>T.id_i</code>指示这个类型在递增次序下的第<code>i</code>个值,空枚举<code>{ }</code>是允许的。 [[区间|子范围]]类型声明有如下形式:{{code|2="modula2"|1=TYPE T = [Lo..Hi]}},这里的<code>Lo</code>和<code>Hi</code>是有相同的基础类型的两个序数值,<code>T</code>的值是从<code>Lo</code>到<code>Hi</code>含二者所有的值,它们必须是常量表达式,如果<code>Lo</code>超过<code>Hi</code>,则子范围是空的。 每个不同的枚举类型介入新的一组值,而子范围类型重复使用底层类型的值。例如: <syntaxhighlight lang="modula2"> TYPE T1 = {A, B, C}; T2 = {A, B, C}; U1 = [T1.A..T1.C]; U2 = [T1.A..T2.C]; (* sic *) V = {A, B} </syntaxhighlight> 这里的<code>T1</code>和<code>T2</code>是相同类型,因为它们有相同的展开定义。<code>U1</code>和<code>U2</code>也是相同的类型,因为<code>T1.C = T2.C</code>。类型<code>T1</code>和<code>U1</code>却是不同的,尽管它们包含相同的值,但是<code>T1</code>的展开定义是枚举,而<code>U1</code>的展开定义是子范围。类型<code>V</code>是第三个类型,它的值<code>V.A</code>和<code>V.B</code>无关于值<code>T1.A</code>和<code>T1.B</code>。 语言规定提供了支持[[序数]]类型的一些类型运算:对于序数类型<code>T</code>,<code>NUMBER(T)</code>返回在<code>T</code>中元素的数目,<code>FIRST(T)</code>返回<code>T</code>的最小值,<code>LAST(T)</code>返回<code>T</code>的最大值。<code>ORD(T.id)</code>将一个[[枚举]]的元素<code>T.id</code>,转换成表示它在枚举次序中所处位置的[[整数]],如果元素的类型是枚举<code>T</code>的子范围,结果是元素在<code>T</code>内的位置,而非在子范围内。<code>VAL(i, T)</code>将表示在枚举类型<code>T</code>中位置的[[整数]]<code>i</code>,转换成在这个[[枚举]]中此位置上的那个元素,如果<code>T</code>是枚举类型的子范围,结果是在源出枚举类型的位置<code>i</code>上的元素,而非在子范围内。如果<code>n</code>是类型<code>T</code>的一个[[整数]],则有[[平凡 (数学)|平凡]]的:<code>ORD(n) = VAL(n, T) = n</code>。 ====数组类型==== 固定[[数组]]类型声明有如下形式:{{code|2="modula2"|1=TYPE T = ARRAY Index OF Element}},这里的索引类型<code>Index</code>是序数类型,而元素类型<code>Element</code>是除了开放数组之外的任何类型。开放[[数组]]类型声明有如下形式:{{code|2="modula2"|1=TYPE T = ARRAY OF Element}},这里的元素类型<code>Element</code>是任何类型。 [[数组]]类型<code>T</code>的值,是元素类型为<code>Element</code>的数组;对于固定数组类型,这个数组的长度是索引类型<code>Index</code>的元素数目;对于开放数组类型,这个数组的长度是任意的;开放数组的索引类型是<code>INTEGER</code>子范围<code>[0..n-1]</code>,这里的<code>n</code>是这个数组的长度。开放数组类型只能用作形式参数的类型,引用类型的所引用者,另一个开放数组的元素类型,或作为数组[[构造器|构造子]]中的类型。 多维数组的形状是它每一维的长度的序列。更精确的说,一个数组的形状是它的长度跟随着任何它的元素的形状,非数组的形状是空序列。数组如果有相同的类型和形状,则它们是可赋值的。如果赋值的要么来源要么目标是开放数组,则需要运行时间检查。形如{{code|2="modula2"|1=ARRAY Index_1, ..., Index_n OF Element}}的表达式,是{{code|2="modula2"|1=ARRAY Index_1 OF ... OF ARRAY Index_n OF Element}}的简写。 如果<code>a</code>有数组类型<code>T</code>,则<code>a[i]</code>指示<code>a</code>的一个指定元素,它在数组中的位置对应于<code>i</code>在索引类型中位置。形如<code>a[i_1, ..., i_n]</code>的表达式,是<code>a[i_1]...[i_n]</code>的简写。索引的解释由一个数组的类型确定,而非它的[[值 (计算机科学)|值]];数组赋值改变数组变量的[[值 (计算机科学)|值]],而非它的类型。例如: <syntaxhighlight lang="modula2"> VAR a := ARRAY [1..3] OF REAL {1.0, 2.0, 3.0}; b: ARRAY [-1..1] OF REAL := a; </syntaxhighlight> 这里在<code>a</code>或<code>b</code>做元素值变更之前,<code>a = b</code>为<code>TRUE</code>,尽管<code>a[1] = 1.0</code>而<code>b[1] = 3.0</code>。 可以使用<code>NUMBER(A)</code>,得到一个固定数组类型的索引类型中的元素数目,使用<code>FIRST(A)</code>和<code>LAST(A)</code>,分别得到一个固定数组类型的索引类型的最小元素和最大元素。可以使用<code>NUMBER(a)</code>,得到一个数组的索引类型中的元素数目,使用<code>FIRST(a)</code>和<code>LAST(a)</code>,分别得到一个数组的索引类型的最小元素和最大元素,在这种情况下,表达式<code>a</code>只在它指示一个开放数组的时候求值。 ====引用类型==== 一个[[引用类型]]要么是有跟踪的要么是无跟踪的。当到已分配的一段存储的所有的有跟踪的引用都消失时,系统对这段存储进行[[垃圾回收 (计算机科学)|垃圾回收]]。有跟踪的[[引用类型]]以如下形式来声明:{{code|2="modula2"|1= TYPE T = REF Type}},这里的<code>Type</code>是任何类型。<code>T</code>的值是到类型<code>Type</code>的变量的有跟踪的引用,<code>Type</code>叫做<code>T</code>的所引用类型。无跟踪的引用类型以如下形式来声明:{{code|2="modula2"|1=TYPE T = UNTRACED REF Type}},这里的<code>Type</code>是任何无跟踪的类型(这个限制在不安全模块中解除)。 两个引用类型如果都是有跟踪的或都是无跟踪的,那么它们有相同的引用类。一个一般性的类型是有跟踪类型的条件为,它是有跟踪的引用类型,其任何字段类型是有跟踪类型的记录类型,其元素类型是有跟踪类型的数组类型,或其底层未压缩类型是有跟踪类型的压缩类型。 在有跟踪的和无跟踪的二者情况下,关键字<code>REF</code>可选的可以前导上<code>BRANDED b</code>,这里的<code>b</code>是叫做铭牌(brand)的文本常量。铭牌用来区分原本上相同的类型,它们没有其他语义效果。在一个程序中所有铭牌都必须是唯一性的。如果<code>BRANDED</code>出现而<code>b</code>缺席,系统自动的为<code>b</code>提供一个唯一的值。 [[动态内存分配|分配]]操作{{code|2="modula2"|1=NEW(T, ...)}}返回<code>T</code>的所引用类型的一个新分配的变量的地址,如果<code>T</code>是对象类型,则返回到有配对的方法套件的一个新分配的数据记录的引用。<code>NEW()</code>返回的引用不同于所有的现存引用。这个新引用的分配类型是<code>T</code>。如果<code>T</code>的所引用类型为空则是一个静态错误。如果<code>T</code>是到有<code>k</code>个开放维度的数组的引用,<code>NEW(T)</code>操作有如下形式:{{code|2="modula2"|1=NEW(T, n_1, ..., n_k)}},这里的这些<code>n</code>是指定新数组在它的前<code>k</code>个维度上的长度的整数取值表达式。 所有的对象类型和有跟踪的引用类型(包括<code>NULL</code>)都有一个关联的整数代码。不同的类型有不同的代码。给一个类型的代码对程序的任何单一执行都是常量,但对不同的执行可以不同。类型运算<code>TYPECODE(T)</code>,返回类型<code>T</code>的这个代码,而<code>TYPECODE(r)</code>,返回引用<code>r</code>的分配类型的这个代码。如果<code>T</code>是<code>REFANY</code>,或者不是对象类型或有跟踪的引用类型,则是一个静态错误。类型运算<code>ISTYPE(x, T)</code>,测试<code>x</code>是否为<code>T</code>的成员,这里的<code>T</code>必须是对象类型或有跟踪的引用类型,而<code>x</code>必须可赋值给<code>T</code>。 ===表达式=== {{le|表达式 (计算机科学)|Expression (computer science)|表达式}}规定产生一个值或变量的一个计算。所有的表达式都有一个静态确定的类型,它包含这个表达式可以产生的所有的值。在语法上,一个表达式要么是一个[[运算数]],要么是应用于自身也是表达式的实际参数的一个[[运算]]。运算数是[[标识符 (计算机语言)|标识符]]、{{le|文字 (计算机编程)|Literal (computer programming)|文字}}或[[类型系统|类型]]。通过递归的求值一个运算的实际参数并应用这个[[运算]],来[[求值策略|求值]]一个表达式。实际参数的求值次序,对除了[[短路求值]]的<code>AND</code>和<code>OR</code>之外的所有运算都是未定义的。 对一个[[子程序|过程]]的调用,在这个过程是返回一个结果的函数过程的时候,是称为函数应用的一个表达式,这个表达式的类型是过程的结果类型。 Modula-3语言规定提供了前缀算术运算:<code>+</code>、<code>-</code>,中缀算术运算:<code>+</code>、<code>-</code>、<code>*</code>、<code>/</code>、<code>DIV</code>、<code>MOD</code>,数值函数运算:<code>ABS(x)</code>、<code>FLOAT(x, T)</code>、<code>FLOOR(x)</code>、<code>CEILING(x)</code>、<code>ROUND(r)</code>、<code>TRUNC(r)</code>、<code>MAX(x, y)</code>、<code>MIN(x, y)</code>,关系运算:<code>=</code>、<code>#</code>、 <code><=</code>、<code>>=</code>、<code>></code>、<code><</code>,逻辑运算:<code>NOT p</code>、<code>p AND q</code>、<code>p OR q</code>,文本[[串接]]运算:<code>a & b</code>,内存分配运算:<code>NEW(T, ...)</code>,集合运算:[[并集]]<code>+</code>、[[补集#相对补集|差集]]<code>-</code>、[[交集]]<code>*</code>、[[对称差|对称差集]]<code>/</code>和[[元素 (数学)|隶属]]关系测试<code>e IN s</code>,它在序数<code>e</code>是集合<code>s</code>的元素的时候返回<code>TRUE</code>。 表达式可建构自如下{{le|文字 (计算机编程)|Literal (computer programming)|文字}}:{{le|整数文字|Integer literal}}、实数文字、{{le|字符文字|Character literal}}和{{le|字符串文字|String literal|文本文字}}。集合、数组和记录有[[构造器|构造子]]表达式: *<code>S{e_1, ..., e_n}</code>,这里的<code>S</code>是一个集合类型,而这些<code>e</code>是表达式或<code>lo..hi</code>形式的范围。这个构造子指示类型为<code>S</code>的一个值,它包含列出的那些值和在列出的范围中的那些值,这些<code>e</code>、<code>lo</code>和<code>hi</code>必须可赋值给类型<code>S</code>的元素类型。 *<code>A{e_1, ..., e_n}</code>,这里的<code>A</code>是一个数组类型,而这些<code>e</code>是表达式。这个构造子指示类型为<code>A</code>的一个值,它包含遵循列出次序的这些列出元素,这些<code>e</code>必须可赋值给<code>A</code>的元素类型。如果<code>A</code>是多维数组,则这些<code>e</code>必须自身也是数组取值表达式。如果<code>A</code>是固定数组类型,并且<code>n</code>至少为<code>1</code>,则<code>e_n</code>可以跟随着<code>, ..</code>,指出<code>e_n</code>的值按照需要重复任意多次来填满这个数组。 *<code>R{Bindings}</code>,这里的<code>R</code>是一个记录类型,而<code>Bindings</code>是完全同于过程调用的关键字或位置绑定的一个列表。这个绑定列表会被重写来适合<code>R</code>的字段和缺省值的列表,完全同于过程调用中的那些做为,记录字段名字扮演了过程形式参数的角色。这个构造子指示类型为<code>R</code>的一个值,它的字段值由重写后的这些绑定来指定。 {{le|常量 (计算机编程)|Constant (computer programming)|常量}}表达式是一般性的表达式类的子集,所要求的限制使得可以静态的求值这个表达式。在常量表达式中,除了<code>NEW()</code>、解引用(显式的或隐式的)、<code>SUBARRAY()</code>、<code>TYPECODE()</code>、<code>NARROW()</code>、<code>ISTYPE()</code>、<code>ADR()</code>和<code>LOOPHOLE()</code>之外的所有运算都是合法的,唯独可运用的过程,是在提供无符号[[字 (计算机)|字]]运算的<code>Word</code>接口中的函数。变量在常量表达式中,只能作为给<code>FIRST()</code>、<code>LAST()</code>、<code>NUMBER()</code>、<code>BITSIZE()</code>、<code>BYTESIZE()</code>或<code>ADRSIZE()</code>的实际参数出现,并且这个变量必须不是开放数组类型的。文字和顶层过程常量在常量表达式中是合法的。 ====指定式==== 产生变量的表达式叫做指定式(designator)。常量表达式永远不是指定式。指定式的类型是它产生的变量的类型。一个指定式依赖于[[上下文 (计算机)|上下文]],可以指示要么一个变量,要么这个变量的值。比如,它作为赋值的[[左值]]时是变量,作为[[右值]]时是这个变量的值。一些指定式是只读的,它们不能用在可能改变这个变量的值的上下文中,并非只读的指定式就是可写的指定式。 一个标识符是可写的指定式的条件为,它被声明为变量,是<code>VAR</code>或<code>VALUE</code>形式参数,<code>TYPECASE</code>或<code>TRY-EXCEPT</code>语句的局部变量,或绑定到可写指定式的<code>WITH</code>局部变量。一个标识符是只读的指定式的条件为,它是<code>READONLY</code>形式参数,<code>FOR</code>语句的局部变量,或绑定到非指定式或只读的指定式的<code>WITH</code>局部变量。只有如下[[运算]]可以产生指定式: *<code>r^</code>,这个运算叫做{{le|解引用算符|Dereference operator|解引用}}(dereferencing),它表示<code>r</code>的所引用者。表达式<code>r^</code>总是可写的指定式。如果<code>r</code>的类型是<code>REFANY</code>、<code>ADDRESS</code>、<code>NULL</code>、对象类型或不透明类型,则是一个静态错误;如果<code>r</code>是<code>NIL</code>,则是一个必查的运行时间错误。 *<code>a[i]</code>,这个运算叫做下标,它表示数组<code>a</code>的值的第<code>i + 1 - FIRST(a)</code>个元素,比如说<code>a</code>的索引类型是<code>[-1..1]</code>,<code>a[1]</code>是这个数组的值的第<code>1 + 1 - (-1)</code>个即第<code>3</code>个元素。表达式<code>a[i]</code>是否为指定式进而是否可写同于<code>a</code>。表达式<code>i</code>必须可赋值给<code>a</code>的索引类型。如果<code>a</code>是到数组的引用,则这里有隐含的解引用,即这里的<code>a[i]</code>是<code>a^[i]</code>的简写。 *<code>r.f</code>、<code>o.f</code>、<code>I.x</code>、<code>o.m</code>、<code>T.m</code>和<code>E.id</code>,这种[[点表示法]]运算叫选取(selection)。 **<code>r.f</code>表示记录<code>r</code>的指名字段<code>f</code>,表达式<code>r.f</code>是否为指定式进而是否可写同于<code>r</code>。如果<code>r</code>是到记录的引用,则这里有隐含的解引用,即<code>r.f</code>是<code>r^.f</code>的简写。 **<code>o.f</code>表示对象<code>o</code>的指名字段<code>f</code>,表达式<code>o.f</code>是可写的指定式。 **<code>I.x</code>表示导入的接口<code>I</code>的指名实体<code>x</code>,如果<code>x</code>被声明为变量,则表达式<code>I.x</code>是指定式并且总是可写的。 **<code>o.m</code>表示对象<code>o</code>的指名方法<code>m</code>,<code>o.m</code>不产生指定式。 **<code>T.m</code>表示对象类型<code>T</code>的指名方法<code>m</code>,<code>T.m</code>不产生指定式。 **<code>E.id</code>表示枚举类型<code>E</code>的值<code>id</code>,<code>E.id</code>不产生指定式。 *<code>SUBARRAY(a, from, for)</code>,它产生数组<code>a</code>的越过前<code>from</code>个元素并包含下<code>for</code>个元素的子数组。<code>SUBARRAY()</code>不复制数组,它是否为指定式进而是否可写同于<code>a</code>。如果<code>a</code>是多维数组,<code>SUBARRAY()</code>只应用于顶层数组。如果<code>from + for</code>超过了<code>NUMBER(a)</code>,则是一个必查的运行时间错误。 ===子类型规则=== 使用<code>T <: U</code>指示<code>T</code>是<code>U</code>的[[子类型]],也就是<code>U</code>是<code>T</code>的超类型。子类型规则是:如果<code>T <: U</code>,则类型<code>T</code>的所有值,都是类型<code>U</code>的值,反过来则不成立。 对于[[序数]]类型<code>T</code>和<code>U</code>,<code>T <: U</code>的条件为,它们拥有相同的基础类型,并且所有的<code>T</code>的成员都是<code>U</code>的成员。就是说序数类型上的子类型,反映了在值[[集合 (数学)|集合]]上的[[子集]]关系。 对于[[数组]]类型,一个数组类型<code>A</code>是一个数组类型<code>B</code>的子类型的条件为,它们有相同的最终元素类型,相同的维度数,并且对于每个维度,要么二者都是开放的,要么<code>A</code>是固定的而<code>B</code>是开放的,要么它们都是固定的并有着相同的大小。 对于[[子程序|过程]]类型,<code>T <: U</code>的条件为,它们除了形式参数名字、缺省值和<code>RAISES</code>集合之外是相同的,并且<code>T</code>的<code>RAISES</code>集合包含在<code>U</code>的<code>RAISES</code>集合中。<code>NIL</code>是所有的过程类型的一个成员。 对于[[位段|压缩]]类型,<code>BITS n FOR T</code>和<code>T</code>有相同的值,也就是说:<code>BITS n FOR T <: T</code>并且<code>T <: BITS n FOR T</code>。 对于[[集合 (数学)|集合]]类型<ref name="type" />,<code>SET OF A <: SET OF B</code>的条件是<code>A <: B</code>,就是说集合的子类型规则简单的使用值集合规则。 对于[[参照|引用]]类型有: <syntaxhighlight lang="objectpascal"> NULL <: REF T <: REFANY NULL <: UNTRACED REF T <: ADDRESS </syntaxhighlight> 就是说,<code>REFANY</code>和<code>ADDRESS</code>分别包含所有的有跟踪的和无跟踪的引用,<code>NIL</code>是所有的引用类型的一个成员。这两个规则也适用于加铭牌的类型。 对于[[对象 (计算机科学)|对象]]类型有: <syntaxhighlight lang="objectpascal"> ROOT <: REFANY UNTRACED ROOT <: ADDRESS NULL <: T OBJECT ... END <: T </syntaxhighlight> 就是说,所有对象都是引用,<code>NIL</code>是所有的对象类型的一个成员,而所有的子类型都包括在它的超类型之中。第三个规则也适用于加铭牌的类型。 对于所有的<code>T</code>,都有<code>T <: T</code>。对于所有的<code>T, U, V</code>,<code>T <: U</code>[[逻辑与|并且]]<code>U <: V</code>,[[蕴涵]]<code>T <: V</code>。就是说<code><:</code>是[[自反关系|自反]]和[[传递关系|传递]]的。但是<code>T <: U</code>并且<code>U <: T</code>,不蕴涵<code>T</code>与<code>U</code>是相同的,因为子类型关系不受形式参数名字、缺省值和压缩的影响。 ====可赋值性==== 一个类型<code>T</code>可[[赋值语句|赋值]](assignable)给类型<code>U</code>的条件是: *<code>T <: U</code>,或者 *<code>U <: T</code>并且<code>T</code>是一个数组或除了<code>ADDRESS</code>之外的一个引用类型(这个限制在不安全模块中解除),或者 *<code>T</code>和<code>U</code>是至少有一个共同成员的序数类型。 在第一种情况下,没有运行时间错误是有可能的。在第二种情况下,允许赋值一个<code>REFANY</code>到一个<code>REF T</code>,还允许赋值一个<code>ARRAY OF T</code>到一个<code>ARRAY I OF T</code>,这里分别要求对引用的类型和数组长度的运行时间检查;可以使用类型运算<code>NARROW(x, T): T</code>,将对象类型或有跟踪的引用类型的超类型变量当作其子类型的成员,这时需要运行时间检查,{{le|Modula-2+}}介入它就是为了以安全的方式,将<code>REFANY</code>变量赋值给确知的实际类型<code>REF T</code>。在第三种情况下,常规的范围检查就能确保特定的<code>T</code>的成员也是<code>U</code>的成员。 一个表达式<code>e</code>可赋值给一个变量<code>v</code>的条件是: *<code>e</code>的类型可赋值给<code>v</code>的类型,并且 *<code>e</code>的值是<code>v</code>的类型的一个成员,它不是一个局部过程,并且如果它是一个数组,则有同<code>v</code>一样的形状。 一个表达式<code>e</code>可赋值给类型<code>T</code>,如果<code>e</code>可赋值给类型<code>T</code>的某个变量。如果<code>T</code>不是一个开放数组,这等同声称<code>e</code>可赋值给类型<code>T</code>的任何变量。 ===语句=== 执行一个[[语句 (计算机科学)|语句]]产生一个计算,它可以正常产出结果(在[[可计算理论]]中称为[[停机问题|停机]]),发起一个例外,导致一个必查的运行时间错误,或[[无限循环]]。如果结果是一个例外,它可以可选的配对上一个实际参数。如果一个表达式作为一个语句的执行部份而求值,这个求值引发一个例外,则这个例外成为这个语句的结果。空语句是无操作的(no-op),在语言报告中写为{{code|2="modula2"|1=(*skip*)}}。 [[子程序|过程]]调用在这个过程是真正过程的时候是一个语句。调用一个函数过程并丢弃它的结果使用<code>EVAL</code>语句,它有如下形式:<code>EVAL e</code>,这里的<code>e</code>是一个表达式。它的效果是求值<code>e</code>并忽略其结果。<code>RETURN</code>语句用于从过程中返回。 [[赋值语句]]有如下形式:{{code|2="modula2"|1=v := e}},这里的<code>v</code>是可写的指定式,而<code>e</code>是可赋值给<code>v</code>所指示变量的表达式。赋值语句将<code>v</code>设置为<code>e</code>的值,<code>v</code>和<code>e</code>的求值次序是未定义的,但<code>e</code>会在<code>v</code>更新之前求值。特别是,如果<code>v</code>和<code>e</code>是有交叠的子数组,以没有元素在被用作来源之前被用作目标的方式进行赋值。 顺序复合语句有如下形式:<code>S_1; S_2</code>,它在[[Modula-2]]中称为“语句序列”。一些编程者使用分号作为语句终结符(terminator),另一些编程者将它用作分隔符(separator);Modula-3允许这两种风格,语言报告将其用作分隔符。 [[块 (编程)|块]]语句有如下形式:{{code|2="modula2"|1=Decls BEGIN S END}},这里的<code>Decls</code>是一序列的声明,S是一个语句。块介入在<code>Decls</code>中声明的常量、类型、变量和过程,并接着执行<code>S</code>。声明的名字的作用域是这个块。 ====选择控制结构==== Modula-3提供了选择[[控制结构]]<code>IF</code>和<code>CASE</code>语句。<code>IF</code>语句有如下形式: <syntaxhighlight lang="modula2"> IF B_1 THEN S_1 ELSIF B_2 THEN S_2 ... ELSIF B_n THEN S_n ELSE S_0 END </syntaxhighlight> 这里的这些<code>B</code>是布尔表达式,而这些<code>S</code>是语句。<code>ELSE S_0</code>和每个<code>ELSIF B_i THEN S_i</code>都是可选的。<code>IF</code>语句依次求值<code>B</code>,直到某个<code>B_i</code>求值为<code>TRUE</code>,则接着执行<code>S_i</code>。如果没有表达式求值为<code>TRUE</code>,并且<code>ELSE S_0</code>存在,则执行<code>S_0</code>。如果没有表达式求值为<code>TRUE</code>,并且<code>ELSE S_0</code>缺席,则<code>IF</code>语句是无操作的(no-op)。Modula-3的<code>CASE</code>语句,不同于[[Modula-2]]的同名语句,它有如下形式: <syntaxhighlight lang="modula2"> CASE Expr OF L_1 => S_1 | ... | L_n => S_n ELSE S_0 END </syntaxhighlight> 这里的<code>Expr</code>是类型为序数类型的一个表达式,而每个<code>L</code>是一个列表,这个列表构成自常量表达式,或用<code>e_1..e_2</code>指示的常量表达式的范围,它表示从<code>e_1</code>到<code>e_2</code>含二者的值。如果<code>e_1</code>超出了<code>e_2</code>,这个范围为空。如果两个<code>L</code>表示的[[集合 (数学)|集合]]有交叠,或者任何常量表达式不是类型<code>Expr</code>的一个成员,则是一个静态错误。<code>ELSE S_0</code>是可选的。 <code>CASE</code>语句求值<code>Expr</code>。如果结果的值在任何<code>L_i</code>之中,则执行<code>S_i</code>。如果这个值不在任何<code>L_i</code>之中,并且<code>ELSE S_0</code>存在,则执行它。如果这个值不在任何<code>L_i</code>中,并且<code>ELSE S_0</code>缺席,则发生一个必查的运行时间错误。一些编程者使用竖杠作为初始符(initiator),另一些编程者将它用作分隔符。Modula-3允许这两种风格,语言报告将其用作分隔符。 ====分配类型测试==== <code>TYPECASE</code>语句有如下形式: <syntaxhighlight lang="modula2"> TYPECASE Expr OF T_1 (v_1) => S_1 | ... | T_n (v_n) => S_n ELSE S_0 END </syntaxhighlight> 这里的<code>Expr</code>是类型为引用类型的一个表达式,这些<code>S</code>是语句,这些<code>T</code>是引用类型,而这些<code>v</code>是标识符。如果<code>Expr</code>有类型<code>ADDRESS</code>,或者任何<code>T</code>不是<code>Expr</code>的类型的子类型,则为一个静态错误。<code>ELSE S_0</code>和每个<code>(v)</code>都是可选的。如果<code>(v_i)</code>缺席,形式<code>T_1, ..., T_n => S</code>,是<code>T_1 => S | ... | T_n => S</code>的简写。 <code>TYPECASE</code>语句求值<code>Expr</code>,如果结果的引用值,是任何列出的类型<code>T_i</code>的成员,则针对其中最小的<code>i</code>,执行<code>S_i</code>。<code>NIL</code>是所有引用类型的一个成员,因此<code>NULL</code>情况只有首先出现才能起作用。如果这个值不是列出的这些类型的一个成员,并且<code>ELSE S_0</code>存在,则执行它。如果这个值不是列出的这些类型的一个成员,并且<code>ELSE S_0</code>缺席,则发生一个必查的运行时间错误。每个<code>(v_i)</code>声明类型为<code>T_i</code>的一个变量,并且它的[[作用域]]是<code>S_i</code>。如果<code>v_i</code>存在,它在执行<code>S_i</code>之前被初始化为<code>Expr</code>的值。 可以使用<code>TYPECASE</code>语句测试类型为<code>REFANY</code>或对象的一个表达式的值的引用类型,比如写一个过程<code>ToText(r: REFANY): TEXT</code>,在其中测试<code>r</code>的值是<code>NULL</code>、<code>REF BOOLEAN</code>和<code>REF INTEGER</code>中的哪一个类型。 ====循环控制结构==== Modula-3提供了循环[[控制结构]]<code>FOR</code>、<code>LOOP</code>、<code>WHILE</code>和<code>REPEAT</code>语句,和必须在字面上包围在这四个循环语句中,用于从循环中退出的<code>EXIT</code>语句。语句{{code|2="modula2"|1=LOOP S END}},循环直到<code>S</code>中有一个{{code|2="modula2"|1=EXIT}}发生。语句{{code|2="modula2"|1=WHILE B DO S END}},被定义为: <syntaxhighlight lang="modula2"> LOOP IF B THEN S ELSE EXIT END END </syntaxhighlight> 语句{{code|2="modula2"|1=REPEAT S UNTIL B}},被定义为: <syntaxhighlight lang="modula2"> LOOP S; IF B THEN EXIT END END </syntaxhighlight> <code>FOR</code>语句有如下形式:{{code|2="modula2"|1=FOR id := first TO last BY step DO S END}},这里的<code>id</code>是一个标识符,而<code>first</code>和<code>last</code>是有相同基础类型的序数表达式,<code>step</code>是整数取值的表达式。而<code>S</code>是一个语句。<code>BY step</code>是可选的,如果省略,<code>step</code>缺省为<code>1</code>。标识符<code>id</code>指示其[[作用域]]是<code>S</code>的一个只读变量,其类型是<code>first</code>和<code>last</code>共同的基础类型。<code>FOR</code>语句的语义,可如下这样用<code>WHILE</code>和<code>WITH</code>语句精确的定义为一个块: <syntaxhighlight lang="modula2"> VAR i := ORD(first); done := ORD(last); delta := step; BEGIN IF delta >= 0 THEN WHILE i <= done DO WITH id = VAL(i, T) DO S END; INC(i, delta) END ELSE WHILE i >= done DO WITH id = VAL(i, T) DO S END; INC(i, delta) END END END </syntaxhighlight> 这里的<code>T</code>是<code>id</code>的类型,<code>i</code>、<code>done</code>和<code>delta</code>代表不出现在<code>FOR</code>语句中的变量。从<code>FOR</code>语句的这个定义可以看出,如果一个<code>FOR</code>循环的上界是<code>LAST(INTEGER)</code>或<code>LAST(LONGINT)</code>,就应当将它改写为<code>WHILE</code>循环来避免溢出。 ====With语句==== Modula-3的<code>WITH</code>语句,无关于[[Modula-2]]的同名语句,它有如下形式:{{code|2="modula2"|1=WITH id = e DO S END}},这里的<code>id</code>是一个标识符,<code>e</code>是一个表达式,而<code>S</code>是一个语句。<code>WITH</code>语句声明了具有[[作用域]]<code>S</code>的<code>id</code>,作为变量<code>e</code>的别名,或作为值<code>e</code>的只读名字。表达式<code>e</code>在进入<code>WITH</code>语句的时候被求值一次。一个单一的<code>WITH</code>语句可以包含多个绑定,它们顺序的求值,就是说{{code|2="modula2"|1=WITH id_1 = e_1, id_2 = e_2, ...}},等价于{{code|2="modula2"|1=WITH id_1 = e_1 DO WITH id_2 = e_2 DO ....}}。 <code>WITH</code>语句可类比于某种过程调用<code>P(e)</code>,这里的<code>P</code>被声明为: <syntaxhighlight lang="modula2"> PROCEDURE P(mode id: typeof e) = BEGIN S END P </syntaxhighlight> 这里的算符<code>[[typeof]]</code>只是个示意,它不在语言规定之中;如果<code>e</code>是可写的指定式,则<code>mode</code>是<code>VAR</code>,否则<code>mode</code>是<code>READONLY</code>。在<code>WITH</code>语句和调用<code>P(e)</code>之间唯一的区别为,出现在<code>WITH</code>语句内的[[自由变量]]、<code>RETURN</code>和<code>EXIT</code>,是在<code>WITH</code>语句所在的[[上下文 (计算机)|上下文]]中解释的,而非在新增加的<code>P</code>的上下文中。 Modula-3语言报告使用<code>WITH</code>语句定义其他语句,比如,<code>INC</code>和<code>DEC</code>语句分别有如下形式:{{code|2="modula2"|1=INC(v, n)}}和{{code|2="modula2"|1=DEC(v, n)}},这里的<code>v</code>指示一个[[序数]]类型的变量,而<code>n</code>是可选的一个[[整数]]取值表达式,如果省略<code>n</code>则缺省为<code>1</code>,这两个语句分别等价于{{code|2="modula2"|1=WITH x = v DO x := VAL(ORD(x) + n, T) END}}和{{code|2="modula2"|1=WITH x = v DO x := VAL(ORD(x) - n, T) END}},这里的<code>T</code>是<code>v</code>的类型,而<code>x</code>代表不出现在<code>n</code>中的变量。 ===例外=== 例外声明有如下形式:<code>EXCEPTION id(T)</code>,这里的<code>id</code>是一个标识符,而<code>T</code>是除了开放数组之外的一个类型,它声明了<code>id</code>是具有实际参数类型<code>T</code>的例外。如果省略了<code>(T)</code>,这个例外不接受实际参数。例外声明只允许出现在接口和模块的最外层作用域之中。所有的声明的例外都是各不相同的。 <code>RAISE</code>语句有两种形式,没有实际参数的形式:{{code|2="objectpascal"|1=RAISE e}},这里的<code>e</code>是不接受实际参数的一个例外,它的结果是发起例外<code>e</code>;具有实际参数的形式:{{code|2="objectpascal"|1=RAISE e(x)}},这里的<code>e</code>是接受实际参数的一个例外,而<code>x</code>是可赋值给<code>e</code>的实际参数类型的一个表达式,它的结果是引发具有配对实际参数<code>x</code>的例外<code>e</code>。 Modula-3的[[例外处理 (编程)|例外处理]],基于了<code>TRY-EXCEPT</code>语句。采用类似的块系统的有[[Delphi]]、[[Python]]<ref>{{cite web|url= https://www.python.org/doc/faq/general/#why-was-python-created-in-the-first-place|title= Why was Python created in the first place?|quote= I had some experience with using Modula-2+ and talked with the designers of Modula-3 and read the Modula-3 report. Modula-3 is the origin of the syntax and semantics used for exceptions, and some other Python features.|access-date= 2021-06-22|archive-date= 2008-02-23|archive-url= https://web.archive.org/web/20080223222507/http://www.python.org/doc/faq/general/#why-was-python-created-in-the-first-place}}</ref>、[[Scala]]<ref>{{Cite web |url=http://scala.epfl.ch/ |title=Scala Center at EPFL. |access-date=2022-05-15 |archive-date=2020-09-23 |archive-url=https://web.archive.org/web/20200923124042/https://scala.epfl.ch/ }}</ref>和[[Visual Basic.NET]]。 <code>TRY-EXCEPT</code>语句有如下形式: <syntaxhighlight lang="objectpascal"> TRY Body EXCEPT id_1 (v_1) => Handler_1 | ... | id_n (v_n) => Handler_n ELSE Handler_0 END </syntaxhighlight> 这里的<code>Body</code>和每个<code>Handler</code>都是语句,每个<code>id</code>指名一个例外,而每个<code>v_i</code>是一个标识符。<code>ELSE Handler_0</code>和每个<code>(v_i)</code>都是可选的。每个<code>(v_i)</code>声明一个变量,其类型是例外<code>id_i</code>的实际参数类型,而它的[[作用域]]是<code>Handler_i</code>。如果<code>(v_i)</code>缺席,形式<code>id_1, ..., id_n => Handler</code>,是<code>id_1 => Handler; ...; id_n => Handler</code>的简写。 <code>TRY</code>子句执行<code>Body</code>。如果结果是正常的,则忽略<code>EXCEPT</code>等子句。如果<code>Body</code>引发任何列出的例外<code>id_i</code>,则执行<code>Handler_i</code>。在处理具有配对实际参数<code>x</code>的例外<code>id_i</code>的时候,<code>v_i</code>在执行<code>Handler_i</code>之前被初始化为<code>x</code>。如果<code>Body</code>引发任何其他例外,并且<code>ELSE Handler_0</code>存在,则执行它。在这两种情况的任何之一下,<code>TRY-EXCEPT</code>语句的结果是选出的处理器的结果。如果<code>Body</code>引发未列出的例外,而<code>ELSE Handler_0</code>缺席,则<code>TRY-EXCEPT</code>语句的结果是<code>Body</code>引发的例外。 Modula-3还提供<code>TRY-FINALLY</code>语句,它有如下形式:{{code|2="objectpascal"|1=TRY S_1 FINALLY S_2 END}},它执行语句<code>S_1</code>接着执行语句<code>S_2</code>。如果<code>S_1</code>的结果是正常的,则<code>TRY</code>语句的结果等价于<code>S_1; S_2</code>。如果<code>S_1</code>的结果是例外,并且<code>S_2</code>的结果是正常,则在<code>S_2</code>执行之后,重新发起来自<code>S_1</code>的例外。如果二者的结果都是例外,则<code>TRY</code>语句的结果是来自<code>S_2</code>的例外。 在Modula-3中,{{code|2="modula2"|1=EXIT}}和{{code|2="modula2"|1=RETURN}}语句的语义,分别被定义为发起叫做<code>exit-exception</code>和<code>return-exception</code>的例外。<code>exit-exception</code>不接受实际参数,<code>return-exception</code>接受任意类型的实际参数,程序不能显式的命名这些例外。语句{{code|2="modula2"|1=LOOP S END}},非正式的等价于: <syntaxhighlight lang="objectpascal"> TRY S; S; S; ... EXCEPT exit-exception => (*skip*) END </syntaxhighlight> ===过程=== ====过程类型==== 一个[[子程序|过程]]要么是<code>[[空指针|NIL]]</code>,要么是有如下构成的[[三元组]]: *过程体,它是一个语句。 *[[类型签名|签名]],它有如下形式<code>(formal_1; ...; formal_n): R RAISES S</code>,这里的每个<code>formal_i</code>是形式参数,<code>R</code>是结果类型,<code>S</code>是<code>RAISES</code>集合(这个过程可以引发的例外的集合)。 *环境,它是在解释过程体中变量名字方面有关的[[作用域]]。 形式参数有如下形式:{{code|2="objectpascal"|1=Mode Name: Type := Default}},这里的: *<code>Mode</code>是参数模态,它可以是<code>VALUE</code>、<code>VAR</code>或<code>READONLY</code>。如果省略了<code>Mode</code>,它缺省为<code>VALUE</code>。 *<code>Name</code>是命名这个形式参数的标识符。形式参数名字必须是各不相同的。 *<code>Type</code>是这个形式参数的类型。 *<code>Default</code>是一个常量表达式,它是这个形式参数的缺省值。如果<code>Mode</code>是<code>VAR</code>,则<code>:= Default</code>必须省略,否则<code>:= Default</code>和<code>: Type</code>中任何一个都可以省略,但不能二者都省略。如果省略了<code>Type</code>,它采用<code>Default</code>的类型。如果二者都存在,<code>Default</code>的值必须是<code>Type</code>的一个成员。 形式{{code|2="objectpascal"|1=Mode v_1, ..., v_n: Type := Default}},是{{code|2="objectpascal"|1=Mode v_1: Type := Default; ...; Mode v_n: Type := Default}}的简写。 <code>VAR</code>形式参数绑定到对应的实际参数所指示的变量上,也就是说它是别名。对于<code>VAR</code>形式参数,实际参数必须是可写的指定式,它的类型同于形式参数的类型,或者在<code>VAR</code>数组形式参数的情况下,它可赋值给这个形式参数。 <code>VALUE</code>形式参数绑定到具有未用的位置的一个变量上,并且它被初始化为实际参数对应的值。对于<code>VALUE</code>或<code>READONLY</code>形式参数,实际参数是可赋值给形式参数类型的任何表达式,并且放开了针对赋值局部过程的禁止。 <code>READONLY</code>形式参数,如果实际参数是指定式并且有与形式参数相同的类型,或者是可赋值给形式参数的类型的一个数组类型,则被当作<code>VAR</code>形式参数对待,它的只读语义体现在指定这个指定式为只读的,否则被当作<code>VALUE</code>形式参数对待。 在签名中,如果省略了<code>: R</code>,则这个过程是真正(proper)过程,否则为函数过程;如果省略了<code>RAISES S</code>,则假定它为<code>RAISES {}</code>。一个过程发起未包括在<code>RAISES</code>集合中的一个例外,是一个必查的运行时间错误,如果语言实现将这个运行时间错误映射成一个例外,则这个例外隐含的包括在所有的<code>RAISES</code>子句中。 ====过程声明==== 有两种形式的过程声明:只允许出现在接口中的{{code|2="modula2"|1=PROCEDURE id sig}},和只允许出现在模块中的{{code|2="modula2"|1=PROCEDURE id sig = B id}},这里的<code>id</code>是一个标识符,<code>sig</code>是过程[[类型签名|签名]],而<code>B</code>是一个块。在一个模块的最外层作用域内声明的过程是顶层过程,其他过程是局部过程。局部过程可以作为形式参数传递但不能被赋值,因为在栈实现下,局部过程在包含它的[[调用栈|栈桢]]被弹出后会成为无效的。 过程类型声明有如下形式:{{code|2="modula2"|1=TYPE T = PROCEDURE sig}},这里的<code>sig</code>是[[类型签名|签名]]规定。一个过程<code>P</code>是过程类型<code>T</code>的一个成员,或称为是它的一个值的条件为,它是<code>NIL</code>,或者它的签名被<code>T</code>的签名所含盖(cover)。这里的签名<code>sig_A</code>含盖签名<code>sig_B</code>的条件是: *它们有相同数目的形式参数,并且对应的形式参数有相同的类型和模态。 *它们有相同的结果类型,或者都没有结果类型。 *<code>sig_A</code>的<code>RAISES</code>集合包含<code>sig_B</code>的<code>RAISES</code>集合。 过程常量是声明为过程的一个标识符。过程变量是声明为具有过程类型的一个变量。例如: <syntaxhighlight lang="modula2"> PROCEDURE P(txt: TEXT := "P") = BEGIN IO.Put(txt) END P; VAR q: PROCEDURE(txt: TEXT := "Q") := P; </syntaxhighlight> 声明了过程常量<code>P</code>和过程变量<code>q</code>。两个过程如果有一致的[[闭包 (计算机科学)|闭包]],就是说它们涉及相同的过程体和环境,则二者相等即有相同的值。形式参数名字和缺省值,影响一个过程的类型即签名,而非这个过程的值。将一个过程常量赋值给一个过程变量,改变这个它的值,而非它的类型。比如上例中有:<code>P = q</code>为<code>TRUE</code>。缺省的形式参数的解释,由一个过程的类型来确定,而非这个过程的值。比如上例子中:<code>P()</code>打印<code>P</code>,而<code>q()</code>打印<code>Q</code>。 ====过程调用==== 过程调用有如下形式:<code>P(Bindings)</code>,这里的<code>P</code>是一个过程取值的表达式,而<code>Bindings</code>是关键字或位置绑定的一个列表。关键字绑定有如下形式:<code>name := actual</code>,这里的<code>actual</code>是一个表达式,而<code>name</code>是一个标识符。位置绑定有如下形式:<code>actual</code>,这里的<code>actual</code>是一个表达式。当关键字和位置绑定混合在一个调用中的时候,位置绑定必须前导于关键字绑定。如果绑定列表为空,圆括号仍然是需要的。 绑定列表要如下这样重写,来适合<code>P</code>的类型签名:首先,每个位置绑定<code>actual</code>,若为<code>Bindings</code>中的第<code>i</code>个绑定,则通过补充上第<code>i</code>个形式参数的名字,转换成关键字绑定增加到关键字绑定列表中。其次,对于有缺省值并且在第一步之后没有被绑定的每个形式参数,将它的形式参数的名字<code>name</code>和它的缺省值<code>default</code>,形成关键字绑定<code>name := default</code>增加到关键字绑定列表。重写成的关键字绑定列表必须只绑定形式参数,并且必须只绑定每个形式参数正好一次。 执行过程调用,需要求值过程取值表达式<code>P</code>和它的实际参数,绑定形式参数,并执行过程体。<code>P</code>和它的实际参数的求值次序是未定义的。调用一个未定义或<code>NIL</code>过程,是一个必查的运行时间错误。调用具有过程体<code>B</code>的真正过程,在绑定了实际参数之后等价于: <syntaxhighlight lang="objectpascal"> TRY B EXCEPT return-exception => (*skip*) END </syntaxhighlight>而调用具有过程体<code>B</code>的函数过程等价于: <syntaxhighlight lang="objectpascal"> TRY B; (错误:没有返回值) EXCEPT return-exception (v) => (结果成为v) END </syntaxhighlight> ===递归定义=== 对于常量、类型或过程声明<code>N = E</code>、变量声明<code>N: E</code>、例外声明<code>N(E)</code>或揭示<code>N = E</code>,如果<code>N</code>出现在<code>E</code>的任何部份展开之中,则它是[[递归 (计算机科学)|递归]]的。对于省略了类型的变量声明<code>N := I</code>,如果<code>N</code>出现在<code>I</code>的类型<code>E</code>的任何部份展开中,则它是[[递归 (计算机科学)|递归]]的。允许这种声明的条件,是<code>N</code>在<code>E</code>的任何部份展开中所有的出现为如下三者之一:在[[类型构造子]]<code>REF</code>或<code>PROCEDURE</code>的某个出现之内,在[[类型构造子]]<code>OBJECT</code>的一个字段或方法类型之内,或者在一个过程体之内。下面是合法的递归声明的例子: <syntaxhighlight lang="objectpascal"> TYPE RealList = REF RECORD val: REAL; link: RealList END; ListNode = OBJECT link: ListNode END; IntList = ListNode OBJECT val: INTEGER END; T = PROCEDURE(n: INTEGER; p: T); EXCEPTION E(PROCEDURE() RAISES {E}); PROCEDURE P(b: BOOLEAN) = BEGIN IF b THEN P(NOT b) END END P; </syntaxhighlight>
摘要:
请注意,所有对Local Chinese Wikipedia的贡献均可能会被其他贡献者编辑、修改或删除。如果您不希望您的文字作品被随意编辑,请不要在此提交。
您同时也向我们承诺,您提交的内容为您自己所创作,或是复制自公共领域或类似自由来源(详情请见
Project:著作权
)。
未经许可,请勿提交受著作权保护的作品!
取消
编辑帮助
(在新窗口中打开)
导航菜单
个人工具
未登录
讨论
贡献
创建账号
登录
命名空间
页面
讨论
大陆简体
不转换
简体
繁體
大陆简体
香港繁體
澳門繁體
大马简体
新加坡简体
臺灣正體
查看
阅读
编辑
查看历史
更多
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
工具
链入页面
相关更改
特殊页面
页面信息