编程语言Oberon-2
H. Mössenböck, N. Wirth
Institut für Computersysteme, ETH Zürich,
1992-1996
1 引言
2 语法
3 词汇和表示法
4 声明和作用域准则
5 常量声明
6 类型声明
7 变量声明
8 表达式
9 语句
10 过程声明
11 模块

附录A  术语定义
附录B  Oberon的语法
附录C  模块SYSTEM
附录D  Oberon的编程环境

1 引言
    Oberon 是一种从Pascal和Modula-2发展而来的通用语言。最主要的特征是块结构，模块性，分开编译，类型检测（也可跨越模块）严密的静态类型，（类型绑定过程）类型扩展。
    类型扩展使得Oberon成为一种面向对象的语言。一个对象就是一个抽象数据类型（包括私有数据的陈述和对这些数据进行操作的过程）变量。抽象数据类型被定义成可扩展记录类型。Oberon通过建立必要的语言词汇（使相似的概念减少到最少）含盖了大多数面向对象语言的术语。
    这篇报告没有打算作为初学者的指南。而是刻意保持简练，作为程序员的参考、工具和手册。没有说明的部分大多是故意忽略的，要么是因为可以从规定的语法准则衍生而来，要么是因为是通用的约束而没有必要说明的定义。
    附录A详细说明了一些用来表达类型测试准则的Oberon术语。在文中用斜体字显示它们的特殊含义（如相同类型）。

2 语法
    一种扩展的Backus-Naur（巴科斯范式）形式（EBNF）用来描述Oberon的语法规则。| 用来分开选项。括号[ ]表示选择[ ]里面的表达式。括号{}表示重复（也许是0次）。非术语符号以大写字母开头（如Statement）。术语符号或以小写字母开头（如ident），或全部用大写字母（如BEGIN），或用字符串表示（如“：=”）。

3 词汇和表示法
    （术语）符号用ASCII码表示。这些符号是标识符、数字、字符串、算子和分隔符。下列词汇规则必须注意：空格和分隔线不能出现在符号中（特别是注释和字符串中的空格）。除非是分开相邻符号所必须的，否则他们将被忽略。在Oberon中，大写和小写被认为是不同的。
    1）标识符是一串字母和数字，首字符必须是字母。
    标识符：字母{字母 |数字}.
    例如:
    x     Scan     Oberon2     GetSymbol     firstLetter
    2）数字是（无符号）整常量或实常量。整常量型是常量中最小的类型（见6.1）。如果常量带有后缀H则表示是16进制数，否则作为十进制处理。实数通常含有一个小数点，还可含有十进制比例因子。字母E（或D）表示“10的幂指数”。除非含有比例因子字母D（在这种情况下是长实型），实数是实型。
    数字 ：整数 | 实数
    整数 ：数字{数字}| 数字{十六进制数字}“H”
    实数 ：数字{数字}“.”{数字}[幂指数]
    幂指数：（“E”|“D”）[“+”| “—”]数字{数字}
    十六进制数：数字| “A” | “B” | “C” | “D” | “E” | “F”
    数字： “0” | “1” | “2” | “3” | “4” | “5” | “6” | “7” | “8” | “9”
例如:
	1991	INTEGER	1991
	0DH 	SHORTINT	13
	12.3	REAL	12.3
	4.567E8 	REAL	456700000
	0.57712566D-6 	LONGREAL		0.00000057712566
    3)字符常量用带有“X”的十六进制序数字符表示。
字符 ： 数字{十六进制数}“X”
    4）字符串是一列用单引号（‘）或双引号（“）括起来的字符。开放的引用必须与封闭的引用一样，并且不能出现在字符串里。字符的多少称为字符串的长度。长度是1的字符串可以用在任何允许使用字符常量的地方。
字符串：' {char} '  | " {char} "
例如：
	"Oberon"     "Don't worry!"     "x"	'hello world'
    5) 算子和分隔符是特殊的字符、字符组或保留字，列表如下。保留字使用专门的大写字符并且不能用作标识符。
        +		:=	ARRAY	IMPORT	RETURN
        -		^	BEGIN	IN			THEN
        *		=	BY		IS			TO
        /		#	CASE	LOOP		TYPE
        ~		<	CONST	MOD		UNTIL
        &		>	DIV		MODULE	VAR
        .		<=	DO		NIL			WHILE
        ,		>=	ELSE	OF			WITH
        ;		..	ELSIF	OR
        |		:	END	POINTER
        (		)	EXIT	PROCEDURE
        [		]	FOR	RECORD
        {		}	IF	REPEAT
    6）注释（夹在（* 和 *）之间的任意字符序列）可以插在程序的任意两个符号之间，可以嵌套，但不会影响程序。
    
4 声明和作用域准则
    除了预声明标识符，程序中的每个标识符必须声明。声明说明了对象的一些永久属性，如是否是常量、类型、变量或过程。然后标识符用来说明相关对象。
    对象x的作用域从声明开始直到声明所在块的结束（模块，过程，或记录），因此意味着这个对象是一个局部对象。不允许在相同作用域嵌套声明相同名字的对象。作用域准则如下：
1) 标识符不能在指定的作用域表示多个对象（例如标识符不能在同一块中声明两次）。
2) 对象只能在其作用域内引用。
3) 当T1未知时，类型T仍可声明成POINT TO T1（见6.4）。T是局部的，则T1的声明必须与T在同一块中。
4) 标识符指明记录的成员（见6.3）或类型绑定过程， 只在记录指定器中（见10.2）才合法。
    模块中声明的标识符后面可能会跟着调用标记（“*”或“－”）表示被调用。如果其他模块调用Ｍ模块，Ｍ模块的标识符ｘ也可能用在这些模块中。则标识符x在这些模块中写为M.x，称为限制标识符。声明中带“－”的标识符在调用模块是只读的。
    限制标识符　：[标识符“.”]标识符
    公开成员 ：标识符[“*”｜“－”]
    下面是预编译标识符；他们的含意已定义：

    ABS	(10.3)			LEN(10.3)
    ASH	(10.3)			LONG (10.3)
    BOOLEAN (6.1)		LONGINT (6.1)
    CAP	(10.3)			LONGREAL(6.1)
    CHAR (6.1)			MAX (10.3)
    CHR	(10.3)			MIN (10.3)
    COPY (10.3)			 NEW(10.3)
    DEC	(10.3)			ODD (10.3)
    ENTIER (10.3)		ORD(10.3)
    EXCL (10.3)			REAL (6.1)
    FALSE (6.1)			SET (6.1)
    HALT (10.3)			SHORT	(10.3)
    INC	(10.3)			SHORTINT (6.1)
    INCL (10.3)			SIZE (10.3)
    INTEGER (6.1)		TRUE (6.1)
    
５常量声明
    常量声明给标识符赋予一个常量。
    常量声明：公开成员＝常量表达式
    常量表达式：表达式
    例如：
	N = 100
	limit = 2*N - 1
	fullSet = {MIN(SET) .. MAX(SET)}
    常量表达式是一个仅由文本扫描而无需执行程序就可以得出值的表达式。其操作数是常量（8章）或预声明函数（10.3章）在预编译时就可得出值。
    
6  类型声明
    数据类型决定了变量值的范围、适用的算子。类型声明定义了标识符的类型。在结构类型（数组和记录）中也定义了变量的结构。
    类型声明：公开成员 “＝”类型。
    类型：限制标识符|数组类型|记录类型|指针类型|过程类型。
    例如：
    Table = ARRAY N OF REAL
	Tree = POINTER TO Node
	Node =  RECORD
		key : INTEGER;
		left, right: Tree
	END
	
	CenterTree = POINTER TO CenterNode
	CenterNode = RECORD (Node)
		width: INTEGER;
		subnode: Tree
    END
	
	Function = PROCEDURE(x: INTEGER): INTEGER
    6.1 基本类型
    基本类型由预声明标识符表示。相应的算子在8.2中定义，预声明函数过程见10.3。基本类型值的范围如下：
1. 布尔型（BOOLEAN）：逻辑值TURE和FALSE
2. 字符型（CHAR）：扩展ASCII码字符（OX至OFFX）
3. 短整型（SHORTINT）：在最小短整型数和最大短整型数之间的整数
4. 整型（INTEGER）：在最小整型数和最大整型数之间的整数
5. 长整型（LONGINT）：在最小长整型数和最大长整型数之间的整数
6. 实型（REAL）：在最小实型数和最大实型数之间的实数
7. 长实型（LONGREAL）：在最小长实型数和最大长实型数之间的实数
8. 集合型（SET）：在0和最大集合型数之间的整数集合
9. PTR型（PTR）：任意结构指针类型的基础类型
    类型3到5都是整数型，类型6和7都是实型，他们合起来称为数字型。他们构成了一个层次关系；大类型（值）的范围包含小类型（值）的范围：
	LONGREAL  >=  REAL  >=  LONGINT  >=  INTEGER  >=  SHORTINT
    6.2 数组类型
    数组是一列类型（称为元素类型）相同的元素构成的结构。元素的个数称为数组的长度。数组元素由下标指定，下标从0到长度－1。
    数组类型：数组[长度｛“，”长度｝]OF 类型。
    长度：常量表达式。
    例如：
        ARRAY 10, N OF INTEGER
    	ARRAY OF CHAR
形为
	ARRAY L0, L1, ..., Ln OF T
的类型理解为下面形式的缩写
	ARRAY L0 OF
		ARRAY L1 OF
		...
			ARRAY Ln OF T
    未指明长度的数组声明称为开放数组。他们对指针的基本类型(见6.4)、开放数组的元素类型和形参类型（见10.1）是受限的。
    6.3 记录类型
    记录类型是由固定数量的可能不同类型的元素（称为成员）组成。记录类型声明指定了名称和成员类型。成员标识符的作用域从其声明开始直到记录类型结束。但在涉及记录变量元素（见8.1）的指定器里，它们依然可见。如果一个记录类型被调用，成员标识符要在声明模块外部可见的话必须标记。他们称为公开成员；无标记的元素称为私有成员。
    记录类型：RECORD[“（”基本类型“）”]成员列表｛“；”成员列表｝END。
    基本类型：限制标识符。
    成员列表：[标识符列表“：”类型]。
    例如：
    RECORD
		day, month, year: INTEGER
	END

	RECORD
		name, firstname: ARRAY 32 OF CHAR;
		age: INTEGER;
		salary: REAL
	END
    记录类型是可扩展的，如记录类型可以声明为其他记录类型的扩展。例如
	T0 = RECORD x: INTEGER END
    	T1 = RECORD (T0) y: REAL END
    T1是T0的（直接）扩展，T0是T1的（直接）基础类型（见附录.A）.扩展类型T1包含基础类型的成员和在T1中声明的成员（见6章）。所有在扩展记录中声明的标识符必须与基础记录类型中声明的标识符不同。
    6.4指针类型
    假设指针类型变量P指向一些T类型变量。T称为P的指针基础类型并且T必须是记录或数组类型。指针类型继承了其指针基础类型的扩展关系：假如类型T1是T 的扩展，P1是POINT TO T1类型，则P1也是P的扩展。
    指针类型：POINTER TO 类型
    如果p是类型P＝POINTER TO T的一个变量，调用预声明过程NEW(p)（见10.3）给T类型变量分配空的存储空间。如果T是记录类型或固定长度的数组类型，当使用NEW(p)时分配存储空间；如果T是n维开放数组使用NEW(p)时分配给T的存储空间长度由表达式e0,…,en-1给定。无论那种情况指向分配变量的指针赋给p。p属于P类型。引用变量p^（称为p－引用）属于T类型。
    任何指针变量可以被赋予NIL值，不指向任何变量。所有指针变量继承了基础类型PTR的扩展关系并指向初始值NIL。
    6.5 过程类型
    过程类型变量T像含有值一样含有一个过程（或NIL）。如果过程P被赋给T类型变量，P和T的形参表（见10.1）必须匹配。P不能是预声明或类型绑定过程或属于另一个过程的局部变量。
    过程类型：PROCEDURE[形参]
    
7 变量声明
   变量声明给变量赋以标识符和数据类型。
    变量声明：标识符表“：”类型。
    例如：
    i, j, k: INTEGER
	x, y: REAL
	p, q: BOOLEAN
	s: SET
	F: Function
	a: ARRAY 100 OF REAL
	w: ARRAY 16 OF RECORD
		name: ARRAY 32 OF CHAR;
		count: INTEGER
	END
	t, c: Tree
    记录和指针变量既有静态类型（声明类型－简称类型）又有动态类型（在运行时所采用的类型）。记录类型的指针和变量参数的动态类型可能是其静态类型的扩展。静态类型决定哪些记录成员可到达。动态类型用来调用类型绑定过程（见10.2）。
    
8 表达式
    表达式是指明计算法则的结构：常量和变量目前的值组合起来通过使用算子和函数过程算出其他值。表达式由操作数和算子组成。可能使用圆括号来表达算子和操作数之间的特殊关系。
    8.1 操作数
    除了集合构造器和文字常量（数字，字符常量，或字符串），操作数由指定器指定。指定器由一个涉及到常量，变量，或过程的标识符组成。这个标识符可能由一个模块标识符来限制（见4章和11章），也可能跟着一个选择器（如果指定对象是一个结构成员）。
    指定器：限制标识符｛“.”标识符|“[”表达式表“]”|“^”|“（”限制标识符“）”｝；
    表达式列表：表达式｛“，”表达式｝
    例如：
    i	(INTEGER)
	a[i]	(REAL)
	w[3].name[i]	(CHAR)
	t.left.right	(Tree)
    t(CenterTree).subnode	(Tree)
    如果a指向一个数组，则a[e]表示下标是表达式e的值的元素。e的类型必须是整型。a[e0,e1,…,en]形式的指定器表示a[e0][e1]…[en]。假如r指向一个记录，则r.f表示r的f成员或绑定到r动态类型的f过程（10章）。假如p指向指针，p^表示p引用的元素。指定器p^.f和p^[e]可缩写为p.f和p[e]，例如记录和数组选择器就是引用。假如a或r是只读，则a[e]和r.f也是只读。
类型条件语句v(T)表明了v的动态类型是T（或T的扩展类型）。例如程序执行会中止，如果v的动态类型不是T（或T的扩展类型）。通过指定器，v认为是拥有静态类型T。条件语句是可用的，如果
1) v是一个记录类型的变量参数或v是一个指针，和如果
2) T是v的静态类型的扩展。
    假如指定对象是常量或变量，则指定器指向其当前值。假如是一个过程，指定器指向这个过程（除非跟着一个（也许空的）参数表，这种情况下意味着过程的激活并指向其执行的结果值）。在特有过程调用中实参必须与形参匹配（见10.1）。
    8.2 算子
    依照语法，表达式中的算子分为4种不同优先级别（粘结强度）。算子~的优先级最高，然后是乘除算子，加减算子，和关系算子。优先级相同的算子，运算从左到右。如，x-y-z即是(x-y)-z。
表达式：简单表达式[关系算子  简单表达式]
简单表达式：["+" | "-"]乘除结果｛乘除结果的加减运算｝
乘除结果：因子｛因子的乘除运算｝
因子：指定器[实参]|数字|字符|字符串|NIL|集合|“（”表达式“）”|“~”因子
集合：“｛”[元素｛“，”元素｝]“｝”
元素：表达式[“..”表达式]
实参：“（”[表达式]“）”
关系算子：“=” | “#” | “<” | “<=” | “>” | “>=” | IN | IS
加减算子：“＋” | “－” | OR
乘除算子：“*” | “/” | DIV | MOD | “&”
    合法算子列表如下。一些算子可用于多种类型，代表不同的运算。在此情况下，实际的操作由操作数的类型决定。操作数必须是与算子匹配（见附录.A）。
    8.2.1 逻辑算子
OR  逻辑或         p OR q   “p 或 q为TRUE，则表达式为TURE”
&   逻辑与         p & q    “p 和 q都为TURE，则表达式为TURE”
~   逻辑非         ~p        “p取非”
    这些算子用于布尔型操作数，结果也是布尔型。
    8.2.2 算术算子
+   求和
-   求差
*   求积
/   求实商
DIV 求整商
MOD 求模
    算子＋，－，*,和/适用于数字类型。结果的类型与操作数中类型大的一样，除了除法的结果类型是包含两个操作数类型的最小实型。单元算子，－表示符号，＋表示恒等运算。DIV和MOD只适用于整数。他们服从下面的规则，x是任意数，y为正除数，
x = (x DIV y) * y + (x MOD y)
0 <= (x MOD y) < y
例如：
    x	y	x DIV y	x MOD y
	5	3	1	2
	-5	3	-2	1
    8.2.3 集合算子
    +	并
    -	差 (x - y = x * (-y))
    *	交
    /	对称差 (x / y = (x-y) + (y-x))
    集合算子适用于集合类型的操作数，其结果也是集合类型。单元算子负号代表x的补集，如，－x表示不属于x的集合整数（0到最大集合数）。
    集合构造器通过列出花括号内的元素来定义集合的值。元素必须是O到最大集合值之间的整数。a..b代表区间[a,b]内的所有整数。
    8.2.4 关系算子
    =	等于
    #	不等于
    <	小于
    <=	小于等于
    >	大于
    >=	大于等于
    IN	集合成员
    IS	类型测试
    关系算子得出的结果是布尔型。关系算子=, #, <, <=, >, 和 >=适用于数字类型，字符型，（开放）字符数组类型，和字符串型。关系算子＝和#也适用于布尔型和集合型，指针型和过程型（包括NIL）。x IN s代表“x是s的元素”。x必须是整型，s是集合型。v IS s代表“v 的动态类型是T（或T的扩展）”称为一个类型测试。适用于如果
1) v 是记录类型的变量参数或v 是指针，和如果
2) T 是v的静态类型的扩展。
    表达式的例子（7章提及）
    1991	             INTEGER
	i DIV 3	             INTEGER
	~p OR q	             BOOLEAN
	(i+j) * (i-j)	     INTEGER
	s - {8, 9, 13}	     SET
	i + x	             REAL
	a[i+j] * a[i-j]	     REAL
	(0<=i) & (i<100)	 BOOLEAN
	t.key = 0	         BOOLEAN
	k IN {i..j-1}	     BOOLEAN
	w[i].name <= "John"	 BOOLEAN
    t IS CenterTree	     BOOLEAN
    
9 语句
    语句指明了操作，分为基本语句和结构语句。基本语句不包含任何语句作为其一部分，如赋值，过程调用，返回值，和退出语句。结构语句包含有语句作为其成分，通常用于表达顺序和条件，选择，循环的执行。也可有空语句不执行任何操作。空语句是为了适应语句顺序中的标点符号规则。
    语句：[赋值｜过程调用｜If语句｜Case语句｜While语句｜Repest语句｜For语句｜Loop语句｜With语句｜EXIT|RETURN[表达式]]。
9.1 赋值语句
赋值语句由表达式产生的新值代替变量的旧值。表达式必须与变量匹配（见附录.Ａ）。赋值操作符写为“：＝”。
    赋值语句：指定器：＝表达式。
    例如（在７章的例子中提及）:
    i := 0
	p := i = j
	x := i + 1
	k := log2(i+j)
	F := log2		(* see 10.1 *)
	s := {2, 3, 5, 7, 11, 13}
	a[i] := (x+y) * (x-y)
	t.key := i
	w[i+1].name := "John"
    t := c
    如果Ｔｅ类型的表达式ｅ赋给Ｔｖ类型的变量ｖ，会发生：
1) 如果Tv和Te是记录类型，只是Te和Tv中相同的成员才被赋值（投影）；ｖ的动态类型必须与ｖ的静态类型相同并且不因为赋值而改变。
2) 如果Tv和Te是指针类型，ｖ的动态类型变为ｅ的动态类型；
3) 如果Tv是长度为n的字符数组并且ｅ是长度为ｍ＜ｎ的字符串，v[i]变为ｅｉ（ｉ＝０..ｍ－１）v[m]变为0X。
9.2 过程调用
     过程调用激活了一个过程。过程调用可能含有实参表代替了相应在过程声明中的形参表（见10章）。通过实参表和形参表中参数的位置来传值。有两种参数：变量和值参数。
    假如一个形参是变量参数，相应的实参必须是一个代表变量的指定器。如果它代表结构变量的一个元素，当形/实参替换时（如在过程执行之前）才决定成分选择器的值。假如形参是一个值参数，相应的实参必须是一个表达式。在过程激活之前表达式的值已确定，并且把结果值赋给形参（见10.1）。
    过程调用：指定器[实参]。
    例如：
	WriteInt(i*2+1)	(* see 10.1 *)
	INC(w[k].count)
    t.Insert("John")	(* see 11 *)
    为了语言类型安全，PTR类型的形参变量要求实参也是PTR类型。
    9.3　语句顺序
        语句（由分号分开的构成语句）顺序指定了执行顺序。
        语句顺序：语句｛“；“语句｝。
    9.4　If语句
    If语句：IF　表达式　THEN  语句列
        {ELSIF  表达式　THEN语句列}
        [ELSE　语句列]
    END
例如：
    IF (ch >= "A") & (ch <= "Z") THEN ReadIdentifier
	  ELSIF (ch >= "0") & (ch <= "9") THEN ReadNumber
	  ELSIF (ch = " ' ") OR (ch = ' " ') THEN ReadString
	  ELSE SpecialCharacter
	END
    If语句指定了条件语句列的执行顺序。语句列前的布尔型表达式称为条件语句。当条件语句为真时执行相应的语句列。如果条件语句都不满足，（如果有ELSE语句）则执行ELSE后的语句列。
9.5 开关语句
    开关语句通过表达式的值选择执行相应的语句列。首先计算CASE表达式，然后执行开关标记表含有获得值的语句列。CASE表达式必须要么是包含所有开关标记类型的整型，要么CASE表达式和开关标记都为字符型。开关标记是常量，并且标记不准相同。假如表达式的值与任何开关标记都不同，如果有ELSE语句，则执行ELSE语句。否则程序中止。
    CASE语句：CASE 表达式 OF 开关｛“|”开关｝[ELSE 语句列]END。
    开关：[开关标记列表“：”语句列]。
    开关标记列表：[开关标记 “，”开关标记]。
    开关标记：常量表达式[“..”常量表达式]。
    例如：
    CASE ch OF
	"A" .. "Z": ReadIdentifier 
	| "0" .. "9": ReadNumber 
	| " ' ", ' " ': ReadString
	ELSE SpecialCharacter
	END
9.6 While语句
    当（条件语句）表达式布尔值为TRUE时，While语句重复执行一个语句列。条件语句在每次执行语句列之前检验。
    While语句：WHILE表达式 DO 语句列END。
    例如：
	WHILE i > 0 DO i := i DIV 2; k := k + 1 END
    WHILE (t # NIL) & (t.key # i) DO t := t.left END
9.7 Repeat语句
Repeat语句重复执行一个语句列直到条件表达式布尔值为TRUE。
Repeat语句：REPEAT 语句列 UNTIL 表达式。
9.8 For语句
   通过一个整数变量（称为语句的控制变量）值的变化来控制For语句重复执行一个语句列固定的次数。
For语句：FOR 标识符 “：＝”表达式TO表达式[BY常量表达式]DO语句列END。
   例如：
    FOR i := 0 TO 79 DO k := k + a[i] END
   	FOR i := 79 TO 1 BY -1 DO a[i] := a[i-1] END
    语句
    FOR v := low TO high BY step DO statements END
    等同于
    v := low; temp := high;
    IF step > 0 THEN
	  WHILE v <= temp DO statements; v := v + step END
    ELSE
	  WHILE v >= temp DO statements; v := v + step END
    END
    Low必须与v匹配（见附录.A），high也必须是与v相匹配的表达式，步长必须为非0常量的整型表达式。如不指定步长，将被默认为1。
9.9 Loop 语句
Loop 语句重复执行一个语句列直到该语句列中的退出语句被执行。
    Loop 语句：LOOP 语句列END。
例如：
    LOOP
		ReadInt(i);
		IF i < 0 THEN EXIT END;
		WriteInt(i)
	END	
    Loop 语句非常适合于重复执行（语句列中有多个退出点或情况的）语句列。
9.10 Return和Exit语句
    Return语句用符号RETURE指明了过程的终止。如果该过程是一个函数过程，RETURE符号后面跟着一个表达式。表达式类型必须与在过程开头（见10章）表明的结果类型一致（见附录.A）。
    函数过程需要Return语句指明结果值。在特有过程中，Return语句隐含在过程体后。任何显式的Return语句都作为一个（也许是异常的）终止点。
    Exit语句由EXIT符号表明。用于终止一个封闭的Loop语句，然后继续执行Loop语句之后的语句。虽然依照语法Exit语句与相应的循环语句不相干，但实际上Exit语句是与上下语句是紧密联系的。
9.11 With语句
    With语句执行一个语句列取决于类型测试结果和每次语句列的被测变量在类型条件语句中的应用。
With语句：WITH 条件语句 DO 语句列｛“|”条件语句DO语句列｝[ELSE 语句列]END。
条件语句：限制标识符“：” 限制标识符。
    例如：	
    WITH t: CenterTree DO i := t.width; c := t.subnode END
    如果v是记录类型参数变量或指针变量，并假设v属于静态类型T0，则语句
	WITH v: T1 DO S1 | v: T2 DO S2 ELSE S3 END
    意义如下：如果v的动态类型是T1，则执行语句列S1；如果v的动态类型是T2，则执行语句列S2；条件都不满足时，执行S3。T1和T2必须是T0的扩展。如果类型测试都不满足又没有ELSE语句，则程序中止。
    
10 过程声明
    过程声明由过程头和过程体组成。过程头指明了过程名和形参。类型绑定过程也指明了接收器参数。过程体包含了声明和语句。过程名会在过程结束时再出现。
    有两类过程：特有过程和函数过程。后者由函数指定器（表达式的一部分，其结果作为表达式的操作数）激活。特有过程由过程调用激活。如果过程的形参指明了一个结果类型则称为函数过程。函数过程体必须含有一个指定其结果的return语句。
    所有常量，变量，类型，和声明了过程体的过程都可从属于一个过程。因为过程可能也声明成局部对象，过程声明可能嵌套。函数调用自身声明称为递归调用。
    除了形参和局部声明对象，过程外界声明的对象（除了与过程的局部声明对象同名）在过程中也可见。
    过程声明：过程头“；”过程体标识符。
    过程头：PROCEDURE[接收器]公开成员[形参]。
    过程体：声明语句[BEGIN 语句列]END。
    声明语句：｛CONST ｛常量声明“；”｝|TYPE ｛类型声明“；”｝|VAR ｛变量声明“；”｝｝
          ｛过程声明“；”|提前声明“；”｝
    提前声明：PROCEDURE“^”[接收器]公开成员[形参]。
    如果过程声明指明了接收器参数，认为过程绑定到一种类型（见10.2）。提前声明允许提前引用一个过程，而这个过程的实际声明在后文中出现。提前声明的形参表和实际的声明必须匹配（见附录.A）。
    10.1 形参
    形参是过程的形参表中声明的标识符。与过程调用的实参一致。当过程被调用时，形参就和实参建立了联系。有两种参数，值参数和变量参数，由在形参表中是否出现了关键字VAR来区分。值参数（其值作为相应实参的初始值）是局部变量。变量参数与相应的实参变量一致，并且这些变量参数代表了这些实参变量。形参作用域是从其声明到过程结束。没有参数的函数过程必须有一个空的参数表，必须由一个实参表也为为空的函数调用。过程的结果类型可以是记录型或数组型。
    形参：“（”[形参组｛“；”形参组｝]“）”[“：” 限制标识符]。
    形参组：[VAR]标识符｛“；”标识符｝“：”类型。
    例如：
PROCEDURE ReadInt(VAR x: INTEGER);
		VAR i: INTEGER; ch: CHAR;
	BEGIN i := 0; Read(ch);
		WHILE ("0" <= ch) & (ch <= "9") DO
			i := 10*i + (ORD(ch)-ORD("0")); Read(ch)
		END;
		x := i
	END ReadInt

	PROCEDURE WriteInt(x: INTEGER); (*0 <= x <100000*)
		VAR i: INTEGER; buf: ARRAY 5 OF INTEGER;
	BEGIN i := 0;
		REPEAT buf[i] := x MOD 10; x := x DIV 10; INC(i) UNTIL x = 0;
		REPEAT DEC(i); Write(CHR(buf[i] + ORD("0"))) UNTIL i = 0
	END WriteInt

	PROCEDURE WriteString(s: ARRAY OF CHAR);
		VAR i: INTEGER;
	BEGIN i := 0;
		WHILE (i < LEN(s)) & (s[i] # 0X) DO Write(s[i]); INC(i) END
	END WriteString;

	PROCEDURE log2(x: INTEGER): INTEGER;
		VAR y: INTEGER; (*assume x>0*)
	BEGIN
		y := 0; WHILE x > 1 DO x := x DIV 2; INC(y) END;
		RETURN y
	END log2;
    Tf是形参f（非开放数组）的类型，Ta是相应实参a的类型。对于变量参数，Ta必须与Tf相同，或Tf必须是记录类型而Ta是Tf的扩展。对于值参数，a必须与f赋值匹配（见附录.A）。
    如果Tf是开放数组，a必须是与f数组匹配（见附录.A）。f的长度取决于a。
10.2 类型绑定过程
    全局声明过程可能与记录类型在同一个模块中一起声明。称此过程绑定在该记录类型上。绑定由过程声明开始的接收器类型表述。接收器可能要么是记录类型T的变量参数，要么是指向T的指针的值参数类型（在此T是记录类型）。过程绑定到T类型并认为是T的局部变量。
    过程头：PROCEDURE[接收器]公开成员 [形参]。
    接收器:“（”[VAR]标识符“：”标识符“）”。
例如：	
    
    PROCEDURE (t: Tree) Insert (node: Tree);
		VAR p, father: Tree;
	BEGIN p := t;
		REPEAT father := p;
			IF node.key = p.key THEN RETURN END;
			IF node.key < p.key THEN p := p.left ELSE p := p.right END
		UNTIL p = NIL;
		IF node.key < father.key THEN father.left := node ELSE father.right := node END;
		node.left := NIL; node.right := NIL
	END Insert;

	PROCEDURE (t: CenterTree) Insert (node: Tree);  (*redefinition*)
	BEGIN
		WriteInt(node(CenterTree).width);
		t.Insert^ (node)  (* calls the Insert procedure bound to Tree *)
    	END Insert;
    	
    如果过程P绑定到T0类型，则也隐式地绑定到任意（T0的扩展类型）T1类型。过程      P '（与P名字相同）可能显式地绑定到T1，在这种情况下覆盖P的绑定。P '称为在T1上重新定义P。P ’和P的形参必须一致（见附录.A）。如果P和T1被调用（见4章）P ’也被调用。
    如果v是一个指定器并且P是一个类型绑定过程，则v.P表示过程P（绑定到v的动态类型（动态绑定））。注意，这个过程可能不同于绑定到v的静态类型的过程。v传递到P 的接收器依照10.1章讲的参数传递规则。
    如果r是一个声明为T类型的接收器参数，r.P^表示（重新定义）过程P绑定到基础类型T。
    在一个类型绑定过程的提前声明中，接收器参数必须与实际过程声明的类型相同。两者形参表声明必须保持一致（附录.A）。
    10.3 预声明程序
    下表列出了预声明过程。一些是同类过程运用到几种不同类型的操作数。v代表变量，x和n代表表达式，T代表类型。
    函数过程
名称		参数类型			结果类型		作用
ABS(x)			数字型				X的类型			绝对值
ASH(x, n)		x, n:整型				长整型			算术转换 (x * 2n)
CAP(x)			字符型				字符型			x是字母: 相应的大写字母
CHR(x)			整型					字符型			整型x变为字符型
ENTIER(x)		实型					长整型			不大于x的最大整数
LEN(v, n)		v:数组; n: 整常量	长整型			v的第 n 维长度(第一维 = 0)
LEN(v)			v: 数组					长整型			与 LEN(v, 0)相同
LONG(x)		SHORTINT					整型				扩展
				INTEGER					长整型
				REAL						长实型
				
MAX(T)		T =基本类型			T						T类型最大值

				T = 集合型			短整型			集合最大元素
MIN(T)		T = 基本类型		T						T类型最小值

				T = 集合型			整型				0
ODD(x)		整型					BOOLEAN				x MOD 2 = 1
ORD(x)		字符型				整型				字符型 x的序数词
SHORT(x)		长整型				整型				类型缩小
				整型					短整型			类型缩小
				长实型				实型				类型缩小(可能截断)
SIZE(T)			任意类型			整型				T类型所需字节数
    
    
    
    特有过程
名称			参数类型										作用
ASSERT(x)			x: 布尔型表达式								如果不是x终止程序运行
ASSERT(x, n)		x: 布尔型表达式;n: 整常量				如果不是x终止程序运行
COPY(x, v)			x: 字符数组, 字符串;v: 字符数组		v := x
DEC(v)				整型												v := v - 1
DEC(v, n)			v, n: 整型											v := v - n
EXCL(v, x)			v: 集合型; x: 整型								v := v - {x}
HALT(x)			整常量											终止程序运行
INC(v)				整型												v := v + 1
INC(v, n)			v, n: 整型											v := v + n
INCL(v, x)			v: 集合型； x: 整型							v := v + {x}
NEW(v)			指向记录的指针或定长数组		分配 v ^
NEW(v, x0, ..., xn)	v指向开放数组的指针 xi:整型		分配 v ^长度为 x0.. xn

	COPY	允许不同类型的（开放）字符数组间赋值。如果需要，源数组缩短到目标数组长度－1。目标数组总是用字符0X结束。在ASSERT(x,n)和HALT(x)中，x由系统工具来解释。
11  模块
    模块由常量，类型，变量，过程的声明和一系列给变量赋初值的语句所组成。模块组成了一个可编译的文本单元。
    模块：MODULE 标识符“；”[调用列表]声明语句[BEGIN 语句列]END标识符“。”
调用列表：IMPORT 调用模块名｛“，” 调用模块名｝“；”
调用：[标识符“：＝”]标识符
    例如：
    
MODULE Trees; 	(* exports: Tree, Node, Insert, Search, Write, Init *)
	IMPORT Texts, Oberon;	(* exports read-only: Node.name *)

	TYPE
		Tree* = POINTER TO Node;
		Node* = RECORD
			name-: POINTER TO ARRAY OF CHAR;
			left, right: Tree
		END;

	VAR w: Texts.Writer;

	PROCEDURE (t: Tree) Insert* (name: ARRAY OF CHAR);
		VAR p, father: Tree;
	BEGIN p := t;
		REPEAT father := p;
			IF name = p.name^ THEN RETURN END;
			IF name < p.name^ THEN p := p.left ELSE p := p.right END
		UNTIL p = NIL;
		NEW(p); p.left := NIL; p.right := NIL; NEW(p.name, LEN(name)+1); COPY(name, p.name^);
		IF name < father.name^ THEN father.left := p ELSE father.right := p END
	END Insert;

	PROCEDURE (t: Tree) Search* (name: ARRAY OF CHAR): Tree;
		VAR p: Tree;
	BEGIN p := t;
		WHILE (p # NIL) & (name # p.name^) DO
			IF name < p.name^ THEN p := p.left ELSE p := p.right END
		END;
		RETURN p
	END Search;
	PROCEDURE (t: Tree) Write*;
	BEGIN
		IF t.left # NIL THEN t.left.Write END;
		Texts.WriteString(w, t.name^); Texts.WriteLn(w); Texts.Append(Oberon.Log, w.buf);
		IF t.right # NIL THEN t.right.Write END
	END Write;

	PROCEDURE Init* (VAR t: Tree);
		VAR t: Tree;
	BEGIN NEW(t.name, 1); t.name[0] := 0X; t.left := NIL; t.right := NIL
	END Init;
BEGIN Texts.OpenWriter(w)
END Trees.

    调用列表指定了调用模块的名字。假如模块A被模块M调用并且A调用标识符x，则使用A.x在M中调用x。如果使用B:=A调用A，对象x写为B.x。这样就可用使用短的别名。用来被调用的标识符（例如在另一个模块中可见）在声明时必须带上调用标记。
    当模块加载到系统中后执行符号BEGIN后的语句列，这些将调用模块加载后完成。循环调用模块是非法的。特殊（无参数并被调用）过程能被系统激活，这种过程当作命令使用（见附录 D1）。
    
    附录 A：术语解说
    整数型   SHORTINT, INTEGER, LONGINT
    实型   REAL, LONGREAL
    数字型   整数型，实型
相同类型
    两个Ta和Tb型的变量a和b类型相同如果
1) Ta和Tb具有相同类型标识符，或
2) 在类型声明中用Ta=Tb来声明Ta与Tb相同，或
     3) 在变量，记录作用域，或形参（都不是开放数组）声明中a和b出现在同一标识符列表。
相等类型
    类型Ta和Tb相等如果
     1) Ta和Tb是相同类型，或
 2) Ta和Tb是元素类型相同的开放数组，或
 3) Ta和Tb是形参列表一致的过程类型。
    类型包含
    数字型的层次如下：
    LONGREAL >= REAL >= LONGINT >= INTEGER >= SHORTINT
类型扩展
    若类型声明Tb＝RECORD（Ta）...END，Tb是Ta的直接扩展，Ta是Tb的直接基础类型。类型Tb是Ta的扩展（Ta是Tb的基础类型）如果
1) Ta与Tb是相同的类型，或
2) Tb是Ta扩展类型的直接扩展。
    假如Pa = POINTER TO Ta和Pb = POINTER TO Tb， Tb是Ta的扩展，则Pb是Pa的扩展（Pa是Pb的基础类型）。
赋值匹配
    Te类型表达式e被赋予Tv类型的变量v认为是赋值匹配的，如果满足下列条件之一：
1) Te和Tv类型相同；
2) Te和Tv都是数字型并且Tv包含Te；
3) Te和Tv是记录类型，Te是Tv的扩展且Tv是v的动态类型；
4) Te和Tv是指针类型且Te是Tv的扩展；
5) Tv是指针或过程类型且e为NIL；
6) Tv是长度为n的字符数组，e是长度为m的字符串常量，且m<n；
7) Tv是过程类型，e是形参与Tv形参匹配的过程名。

数组匹配
    Ta类型的实参a与Tf类型的形参b数组匹配，如果
1) Tf和Ta类型相同，或
2) Tf是开放数组，Ta是任意数组，他们的元素类型是数组匹配的，或
3) Tf是字符数组且a是字符串。

表达式匹配
    对于给定算子，其操作数类型是表达式匹配的，如果他们符合下表（也显示了表达式结果类型）。T1类型必须是T0的扩展类型：
    
算子			第一个操作数		第二个操作数			结果类型
+ - *				数字型					数字型						包含两个操作数类型的最小数字型
/					数字型					数字型						包含两个操作数类型的最小实型
+ - * /				集合型					集合型						集合型
DIV MOD			整型						整型							包含两个操作数类型的最小整型
OR & ~				布尔型					布尔型						布尔型
= # < <= > >=		数字型					数字型						布尔型
					字符型					字符型						布尔型
					整型数组, 字符串	整型数组, 字符串		布尔型
= #					布尔型					布尔型						布尔型
					集合型					集合型						布尔型
					NIL, T0 或 T1类型指针	NIL, T0 或 T1类型指针		布尔型
					T类型过程, NIL			T类型过程, NIL				布尔型
IN					整型						集合型						布尔型
IS					T0类型						T1类型							布尔型
    
形参表匹配
    两个形参表是匹配的，如果满足以下条件：
1) 有相同数量的参数，
2) 他们有相同的函数结果类型或都不返回值
3) 相应位置的参数有相等类型，
4) 相应位置的参数要么都为值参数要么都为变量参数。

附录B：Oberon的语法规则
    模块：MODULE 标识符“；”[调用列表]声明语句[BEGIN 语句列]END 标识符“.”
    调用列表：IMPORT[标识符“：＝”]标识符｛“，”[标识符“：＝”]标识符｝“；”
声明语句：｛CONST｛常量声明“；”｝|TYPE｛类型声明“；”｝|VAR｛变量声明“；”｝｝｛过程声明“；”|提前声明“；”｝
    常量声明：公开成员“＝”常量表达式
    类型声明：公开成员“＝”类型
    变量声明：标识符列表“：”类型
    过程声明：PROCEDURE[接收器]公开成员[形参]“；”语句声明[BEGIN语句列]END标识符
    提前声明：PROCEDURE“^”[接收器]公开成员[形参]
    形参：“（”[形参组｛“；”形参组｝]“）”[“：” 限制标识符]
    形参组：[VAR]标识符｛“，”标识符｝“：”类型
    接收器：“（”[VAR]标识符“：”标识符“）”
    类型＝限制标识符
     |ARRAY[常量表达式｛“，”常量表达式｝]OF类型
     |RECORD[“（” 限制标识符“）”]成员列表｛“；”成员列表｝END
     |POINT TO类型
     |PROCEDURE[形参]
    成员列表：[标识符列表“：”类型]
    声明语句：语句｛“；”语句｝
    语句：[指定器“：＝”表达式
      |指定器[“（”[表达式]“）”]
      |IF表达式THEN语句列｛ELSEIF表达式THEN语句列｝[ELSE语句列]END
      |CASE表达式OF开关｛“|”开关｝｛ELSE表达式THEN语句列｝[ELSE记忆力]END
      |WHILE表达式DO语句列END
      |REPEAT语句列UNTIL表达式
      |FOR表达式“：＝”表达式TO表达式[BY常量表达式]DO语句列END
      |LOOP语句列END
      |WITH条件语句DO语句列｛“|”条件语句DO语句列｝[ELSE语句列]END
      |EXIT
      |RETURN[表达式]
      ]
    开关：[开关标记｛“，”开关标记｝“：”语句列]
    开关标记：常量表达式[“..”常量表达式]
    条件语句：限制标识符“：”限制标识符
    常量表达式：表达式
    表达式：简单表达式[关系算子  简单表达式]
    简单表达式[“＋”|“－”]乘除结果｛加减算子  乘除结果｝
    乘除结果：因子｛乘除算子  因子｝
    因子：指定器[“（”[表达式列表]“）”]|数字|字符|字符串|NIL|集合|“（”表达式“）”|“~”因子
    集合：“｛”[元素｛“，”元素｝]“｝”
    元素：表达式[“..”表达式]
    关系算子：“=” | “#” | “<” | “<=” | “>” | “>=” | IN | IS
    加减算子：“+” | “-” | OR
    乘除算子：“* ” | “/” | DIV | MOD | “&”
    指定器：限制标识符｛“.”标识符|“[”表达式列表“]”|“^”|“（” 限制标识符“）”｝
    表达式列表：表达式｛“，”表达式｝
    标识符列表：公开成员｛“，”公开成员｝
    限制标识符：[标识符“.”]标识符
    公开成员：标识符[“*”|“－”]
附录C：模块SYSTEM
    模块SYSTEM包含一些类型和过程，他们是必需的底层运行工具（特别是针对计算机和/或执行工具）。包括如计算机控制的存取设备的工具，和打破类型匹配规则的工具等等都在语言定义里利用。强烈要求限制使用特殊模块（底层模块）。这些模块本来不可移动且只能在编译选项\N下编译。下面的说明书提供了在Ceres计算机上的Oberon的工具。
    模块SYSTEM调用有如下特征的BYTE型：字符型和短整型变量能赋给BYTE型变量。假如形参变量是BYTE数组类型，则相应的实参可以是任意类型。
    模块SYSTEM也调用PTR型。任意指针类型变量都可赋给PTR型变量。如果形参变量是PTR型，实参可是任意的指针类型。
模块SYSTEM包含的过程列表如下。大多数符合单指令作为内嵌编码编译。详细地说，给读者提供了过程手册。v代表一个变量，x，y，a和n代表表达式，T代表类型。
    
    函数过程
名称				参数类型								结果类型				作用
ADR(v)					任意类型								短整型					变量v的地址
BIT(a, n)				a: 短整型 n: 整型						布尔型					Mem[a]第n位
CC(n)					整常量									布尔型					条件 n (0 <= n <= 15)
LSH(x, n)				x: 整型, 字符型, BYTE型 n: 整型	x的类型					逻辑转换
ROT(x, n)				x: 整型, 字符型, BYTE型 n: 整型	x的类型					旋转
VAL(T, x)				T, x: 任意类型							T								x 处理成T类型
    
    
    特有过程
名称				参数类型															作用
GET(a, v)				a: 短整型; v: 任意基础类型, 指针型, 过程型		v := Mem[a]
PUT(a, x)				a: 短整型; v: 任意基础类型, 指针型, 过程型		Mem[a] := x		
GETREG(n, v)			n: 整常量 v: 任意基础类型, 指针型, 过程型		v := 寄存器 n
PUTREG(n, x)			n: 整常量 v: 任意基础类型, 指针型, 过程型		寄存器 n := v
MOVE(a0, a1, n)		a0, a1:短整型； n: 整型											Mem[a1.. a1+n-1] := Mem[a0.. a0+n-1]
NEW(v, n)				v: 任意指针型；n: 整型										分配n个字节的存储容量，地址赋给v

    附录D：Oberon运行环境
    Oberon程序通常在提供命令激活，回收站，动态模块加载，和一些运行时的数据结构的环境下运行。虽然不是语言的一部分，环境也对Oberon有相当的贡献（暗含在语言定义）。附录D描述了典型Oberon环境的基本特征和提示了工具的使用。更多描述见[1]，[2]，和[3]。
    D1  命令
    命令是指从一个M模块中调用任意无参过程P。写为M.P，并能被运行系统的命令行使用此符号激活。在Oberon中，用户调用命令（代替了程序或模块的调用），从而给了用户一个有用的控制工具和允许模块有多个进入点。当调用命令M.P时，模块M自动加载（除非已在内存里（见D2））并执行过程P。当P结束时，M仍处在加载状态。所有M的全局指针变量能到达的全局变量和数据结构保留其值。当P（或M中的另一个命令）重新调用时，可能继续使用这些值。
    下面的模块演示了命令的使用。实现了抽象数据结构Counter包含一个counter变量并提供了增加和指定counter变量值的命令。

MODULE Counter;
	IMPORT Texts, Oberon;

	VAR
		counter: LONGINT;
		w: Texts.Writer;

	PROCEDURE Add*;   (* takes a numeric argument from the command line *)
		VAR s: Texts.Scanner;
	BEGIN 
		Texts.OpenScanner(s, Oberon.Par.text, Oberon.Par.pos);
		Texts.Scan(s);
		IF s.class = Texts.Int THEN INC(counter, s.i) END
	END Add;

	PROCEDURE Write*;
	BEGIN
		Texts.WriteInt(w, counter, 5); Texts.WriteLn(w);
		Texts.Append(Oberon.Log, w.buf)
	END Write;

BEGIN 
	counter := 0; Texts.OpenWriter(w)
END Counter.

    用户可用执行以下的两个命令：
    Counter.Add   n 	把n的值加到变量counter
    Counter.Write 	把counter 目前的值写到屏幕
    由于命令没有参数，因此他们不得不从运行系统中得到其值。通常，命令可自由地从任何地方获得参数（如从命令后的正文中，或从最近的筛选中，或从标记的阅读器中）。命令Add使用了扫描仪（Oberon系统提供的数据类型）来读取命令行后的值。
    当Counter.Add第一次被调用时，模块Counter被加载并执行。每次调用Counter.Add n来使变量counter增加n。每次调用Counter.Write把counter的目前值写到屏幕。
    因命令执行后模块仍保持加载状态，因此必须有显式的方法卸载他（如当用户希望用重新编译的版本代替已加载的版本时。），Oberon系统提供了一个命令来解决这个问题。
    D2  动态加载模块
    加载的模块可能因使用一个还没有加载的模块的名称而调用这个模块。则调用的模块会自动加载并执行指定的命令。动态加载允许用户使用简单的基础模块开始一个程序并在执行的时候，在明确需要时加载其他模块来扩展此基础模块。
    模块M0可能引起没有调用就动态加载模块M1。M1自然就会被调用和使用，但M0不必知道M1的存在。M1模块可在M0之后继续调用。
    D3  回收站
    在Oberon中，预声明过程NEW用来在空闲的内存中分配数据。但没有显式处理分配的数据的方法。为此，Oberon环境使用回收站寻找不再使用的的数据使得相应的内存可用重新使用。数据可一直使用到被全局指针变量使用（通过指针链）。剪断这个指针链（如设置一个指向NIL的指针）使得数据可以被收集。
    回收站使得程序员从并非不重要的给数据结构正确地分配存储单元中解脱出来，因而有助于避免错误的发生。但，这需要执行时动态数据的信息（见D5）。
    D4  浏览器
    模块的界面（被调用对象的声明）通过浏览器（Oberon环境中的分离工具）从模块中分离出来。例如，浏览器产生了下面（11章模块Trees）的界面。

DEFINITION Trees; 
	TYPE
		Tree = POINTER TO Node;
		Node = RECORD
			name: POINTER TO ARRAY OF CHAR;
			PROCEDURE (t: Tree) Insert (name: ARRAY OF CHAR);
			PROCEDURE (t: Tree) Search (name: ARRAY OF CHAR): Tree;
			PROCEDURE (t: Tree) Write;
		END;
	PROCEDURE Init (VAR t: Tree);
END Trees.

    在运行时一些记录型的信息必须可用：记录的动态类型用于类型测试和类型条件语句。调用绑定到记录型中的过程的地址表来使用动态绑定。最后，回收站在动态分配记录时需要指针地址的信息。所有信息保存在类型描述器，其中有一个用于运行时存储每个记录类型。下段显示了类型描述器的一个可能的应用。
    D5  运行数据
    在运行时一些记录型的信息必须可用：记录的动态类型用于类型测试和类型条件语句。调用绑定到记录型中的过程的地址表来使用动态绑定。
    记录的动态类型与其类型描述符的地址一致。为了动态分配记录类型，这个地址储存在类型标记（在实际记录数据之前并且对程序员是不可见的）。假如t是CenterTree类型的变量（见6章中的例子）。图D5.1显示一个在运行数据结构时可能使用的运用。
    
								[ drawing missing ]    
    
    
    因为过程地址表和指针偏移表都必须有一个相对类型标记地址的固定偏移量，和两个都会在类型扩展和其他过程和指针加入时增加，因此两个表格固定在类型标记的两端往不同的方向生长。
    类型绑定过程t.p称为t^.tag^.过程标记[下标P]。每个类型绑定过程的过程表下标在编译时都是已知的。类型测试v IS T被翻译成v^.tag^.基本类型[生长链T]＝类型描述符地址T。记录类型的扩展和类型描述符地址在编译时都已知。例如，Node的生长链是0（没有基本类型），和CenterNode的生长链是1。

[1]  	N.Wirth, J.Gutknecht: 
	The Oberon System. Software Practice and Experience 19, 9, Sept. 1989
[2]  	M.Reiser: 
	The Oberon System. User Guide and Programming Manual. Addison-Wesley, 1991
[3]	C.Pfister, B.Heeb, J.Templ: 
	Oberon Technical Notes. Report 156, ETH Zrich, March 1991
    
