ALGOL 68
编程范型 | 多范型:指令式,过程式,结构化,并发 |
---|---|
設計者 | 阿德里安·范·韦恩加登, B.J. Mailloux, J.E.L. Peck, C.H.A. Koster等人 |
发行时间 | 最终报告: 1968年 修订报告: 1973年 |
型態系統 | 静态、强类型、安全、结构式 |
網站 | Revised Report on the Algorithmic Language ALGOL 68 |
主要實作產品 | |
Algol68toC[1], ALGOL 68 Genie[2], ALGOL 68-R, ALGOL 68C, ALGOL 68RS, ALGOL 68S, FLACC | |
衍生副語言 | |
ALGOL 68r0 (最终报告:1968年), ALGOL 68r1 (修订报告:1973年) | |
啟發語言 | |
ALGOL 60, ALGOL X, ALGOL Y | |
影響語言 | |
C[3]、C++[4]、Bourne shell、KornShell、Bash、Steelman、Ada、Python[5]、Seed7、Mary、S3 |
ALGOL 68(源自英語:ALGOrithmic Language 1968的縮寫),一種指令式程式語言,作為ALGOL家族的成員,是官方上的ALGOL 60後繼者。它的設計目標,是提供更廣泛的應用,以及更嚴格的語法定義。
ALGOL 68的特征包括基于表达式的语法,用户声明的类型和结构与标签联合类型,变量与引用参数的引用模型,可变长数组和字符串、数组与矩阵的分片,用户定义的运算符和运算符重载,高阶函数与匿名函数,以及并发。
概論
[编辑]ALGOL 68由IFIP工作组2.1負責設計。1968年12月20日,IFIP工作组2.1通過了這個語法規範,並提交IFIP大會通過且出版。
ALGOL 68的定义使用了阿德里安·范·韦恩加登发明的一种数学形式主义的两级形式文法。Van Wijngaarden文法使用分别称为“超规则”和“元产生式规则”的两组上下文无关文法规则,生成形成一个可能无限的产生式集合,而这些常规的产生式将识别特定的ALGOL 68程序;值得注意的是,它们能够表达在很多其他编程语言的技术标准中被标记为“语义”的那种要求,其他语言的语义必须用易致歧义的自然语言叙述来表达,并接着在编译器中实现为附加到形式语言解析器的“特设”代码。
ALGOL 68设计的主要目标和原则为:
在1970年,ALGOL 68-R成为了第一个投入工作的ALGOL 68编译器。在1973年9月确定的修订版本中,省略了特定特征比如过程化、gomma和形式边界[6]。Stephen R. Bourne是ALGOL 68修订委员会成员,他选取了它的一些想法到他的Bourne shell之中。
ALGOL 68曾受到严厉批评[8],最突出的是来自其设计委员会的一些成员比如C. A. R. Hoare[9],还有ALGOL 60编译器作者比如Edsger Dijkstra[10],它获评为抛弃了ALGOL 60的简单性,成为了复杂或过于笼统的想法的载体。ALGOL 68与刻意保持简单的同时代(竞争)者如C、S-algol和Pascal形成了鲜明对比。
ALGOL 68的语言定义出版后的文本长达两百多页并充斥着非标准术语,这种复杂性使得编译器实现任务变得困难,故而它曾被称为“没有实现也没有用户”。这么说只是部份真实的,ALGOL 68曾应用于一些小众市场,特别是流行于英国的国际计算机有限公司(ICL)的机器之上,还有在教学角色之上。在这些领域之外,其使用相对有限。
尽管如此,ALGOL 68对计算机科学领域的贡献是深刻、广泛而持久的,虽然这些贡献大多只是在它们于后来开发的编程语言中重现之时才被公开认可。很多语言是为了应对设计这门语言时所采用的形式化方法导致的复杂性而专门开发的,其中最著名的是Niklaus Wirth的ALGOL W及其后继者Pascal[11];或者是针对特定角色而重新实现的,比如有些人认为Ada可以看作ALGOL 68的后继者[12]。
1970年代的很多语言可以追溯其设计至ALGOL 68,选取一些特征,并放弃被认为太复杂或超出给定角色范围的其他特征。其中就有C语言,它受到ALGOL 68的直接影响,特别是它的强类型和结构。多数现代语言都至少可以追溯其部份语法至要么C语言要么Pascal,因而很多语言可直接或间接的经由C语言而追溯至ALGOL 68。
规定和实现时间线
[编辑]名称 | 年 | 国家 | 描述 | 目标CPU/平台 | 属主/许可证 | 实现语言 |
---|---|---|---|---|---|---|
广义ALGOL | 1962 | 荷蘭 | 广义文法的ALGOL[13] | |||
ALGOL 68DR | 1968 | IFIP WG 2.1草案报告[14] | ||||
ALGOL 68r0 | 1968 | IFIP WG 2.1最终报告[15] | ||||
ALGOL 68-R | 1970 | 英国 | GEORGE 3之下的ALGOL 68 | ICL 1900 | 皇家雷达研究所 | ALGOL 60 |
DTSS ALGOL 68 | 1970 | 美国 | Dartmouth分时系统的ALGOL 68[16] | GE-635 | 达特茅斯学院 | |
Mini ALGOL 68 | 1973 | 荷蘭 | 针对简单ALGOL 68程序的解释器[17] | 可移植解释器 | 荷兰数学中心 | ALGOL 60 |
OREGANO | 1973 | 美国 | 实践“实现模型的重要性。”[18] | 加州大学洛杉矶分校 | ||
M-220 ALGOL 68 | 1974 | 苏联 | 使用EPSILON在M-220上实现ALGOL 68[19] | M-220 | EPSILON | |
ALGOL 68C | 1975 | 英国 | 剑桥大学ALGOL 68实现[20] | ICL,IBM System/360,PDP-10和Unix,Telefunken TR440/TR445,Tesla 200和Z80(1980年)[21] | 剑桥大学 | ALGOL 68C |
ALGOL 68r1 | 1975 | IFIP WG 2.1修订报告[22] | ||||
ALGOL 68 H | 1975 | 荷蘭, 加拿大 | ALGOL 68编译器[23] | 阿尔伯塔大学,荷兰数学中心 | ALGOL W | |
CDC ALGOL 68 | 1975 | 美国 | 完全实现的ALGOL 68[24] | CDC 6000系列,CDC Cyber | 控制数据公司 | |
Odra ALGOL 68 | 1976 | 苏联, 波蘭 | Odra 1204/IL | ALGOL 60 | ||
Oklahoma ALGOL 68 | 1976 | 美国 | 俄克拉何马州立大学ALGOL 68实现[25] | IBM 1130和System/370 Model 158 | 俄克拉何马州立大学 | ANSI Fortran 66 |
Berlin ALGOL 68 | 1977 | 德国 | 柏林工业大学ALGOL 68实现[26] | 基于抽象ALGOL 68机器的机器无关编译器 | 柏林工业大学 | CDL 2 |
FLACC | 1977 | 加拿大 | 具有调试特征的修订报告完整实现 | System/370 | 租用,Chion公司 | 汇编 |
ALGOL 68RS | 1977 | 英国 | 可移植编译器 | ICL 2900系列,Multics,VMS和C生成器(1993年) | 皇家信号与雷达研究所 | ALGOL 68RS |
ALGOL 68-RT | 1979 | 英国 | 并行ALGOL 68-R | |||
ALGOL 68+ | 1980 | 荷蘭 | 提议的ALGOL 68的超语言[27] | |||
Leningrad ALGOL 68 | 1980 | 苏联 | 列宁格勒国立大学开发的完全语言加上模块系统 | IBM,DEC,CAMCOH,PS 1001和PC | ||
交互式ALGOL 68 | 1983 | 英国 | 增量编译,1992年再版MK2[28] | PC | 非商业共享软件 | |
ALGOL 68S | 1985 | 英国, 美国 | ALGOL 68的子集语言[29][30] | Sun-3,Sun SPARC(在SunOS 4.1和Solaris 2下),Atari ST(在GEMDOS下),Acorn Archimedes(在RISC OS下),VAX-11(在Ultrix-32下) | 利物浦大学,卡内基·梅隆大学,曼彻斯特大学 | BLISS,Pascal |
Rutgers ALGOL 68 | 1990 | 美国, 匈牙利 | Mini ALGOL 68解释器后续版本[31] | 可移植解释器 | 写于罗格斯大学的非商业开源软件 | C |
Algol68toC | 1993 | 英国 | 基于源自1985年ELLA[32] ALGOL 68RS的ctrans[33][1] | 可移植C生成器 | 国防研究局的皇冠版权开源软件 | ALGOL 68RS |
ALGOL 68 Genie | 2001 | 荷蘭 | 完全语言包括标准的并立子句[2] | 可移植解释器;2010年版本2之后,具有可选的选定单元的编译 | GNU GPL | C |
样例代码
[编辑]下面是Hello World样例代码,采用了ALGOL 68RS的程序结构,严格的说只有在BEGIN
与END
之间的诸行是ALGOL 68程序,第一行、第二行和最后一行都特定于a68toc
编译器。
PROGRAM helloworld CONTEXT VOID
USE standard
BEGIN
print(("hello, world!", newline))
END
FINISH
第一行给出这个程序的标识为helloworld
而且这个名字的文件包含了这个程序;CONTEXT VOID
短语指定这个程序独立而非嵌入到其他部份之中。第二行的USE
加载了标准预置(prelude),print
就在其中。将上述代码保存入文本文件helloworld.a68
中,然后使用ALGOL 68RS编译器a68toc
执行它:
$ ca -u ASDFGHJ helloworld.a68
$ ./helloworld
hello, world!
下面的样例代码实现了埃拉托斯特尼筛法来找到小于等于100的所有素数。ALGOL 68中NIL
类似于其他语言中的空指针,x OF y
表示访问STRUCT y
的成员x
。
BEGIN # ALGOL68的素数筛法,基于链表实现 #
MODE
NODE = STRUCT (INT h, REF NODE t),
LIST = REF NODE;
OP CONS = (INT n, LIST l) LIST: HEAP NODE := NODE (n, l);
PRIO CONS = 9;
PROC one to = (INT n) LIST:
(PROC fn = (INT m) LIST: (m > n | NIL | m CONS fn(m + 1));
fn(1));
PROC error = (STRING s) VOID:
(print((newline, " error: ", s, newline)); stop);
PROC hd = (LIST l) INT:
(l IS NIL | error("hd NIL"); SKIP | h OF l);
PROC tl = (LIST l) LIST:
(l IS NIL | error("tl NIL"); SKIP | t OF l);
PROC show = (LIST l) VOID:
(l ISNT NIL | print((" ", whole(hd(l), 0))); show(tl(l))
| print(newline));
PROC filter = (PROC (INT) BOOL p, LIST l) LIST:
BEGIN
PROC fn = (LIST m) LIST:
IF m IS NIL THEN NIL
ELIF p(hd(m)) THEN hd(m) CONS fn(tl(m))
ELSE fn(tl(m))
FI;
fn(l)
END;
PROC sieve = (LIST l) LIST:
IF l IS NIL THEN NIL
ELSE hd(l) CONS sieve(filter(
(INT n) BOOL: n MOD hd(l) NE 0, tl(l)))
FI;
PROC primes = (INT n) LIST: sieve(tl(one to(n)));
show(primes(100))
END
将上述代码保存入文本文件primes.a68
中,然后使用ALGOL 68 Genie解释器执行它:
$ a68g primes.a68
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
显著的语言元素
[编辑]符号和保留字
[编辑]标准语言包含六十一个保留字,典型的用粗体字打印,并且其中一些还具有“简短”符号等价者:
MODE,OP,PRIO,PROC,FLEX,HEAP,LOC,LONG,REF,SHORT,STRUCT,UNION,VOID, BITS,BOOL,BYTES,CHAR,COMPL,INT,REAL,SEMA,STRING,CHANNEL,FILE,FORMAT, AT @,IS :=:, ISNT :/=:,OF →r0,TRUE,FALSE,EMPTY,NIL ○,SKIP ~, COMMENT CO # ¢,PRAGMAT PR,BEGIN ~ END ( ~ ), IF ~ THEN ~ ELIF ~ THEN ~ ELSE ~ FI ( ~ | ~ |: ~ | ~ | ~ ), CASE ~ IN ~ OUSE ~ IN ~ OUT ~ ESAC ( ~ | ~ |: ~ | ~ | ~ ), FOR ~ FROM ~ TO ~ BY ~ WHILE ~ DO ~ OD,PAR,GO TO GOTO,EXIT □r0。
GO TO
被当作两个保留字,未修订报告的语言还有保留字EITHER
和IS NOT
结合。
符号表示
[编辑]“臻选字符”(worthy character)是1977年出版的ALGOL 68标准硬件表示报告出于可移植性而推荐的字符集中的字符[34]:
- A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9
这反映了当年一些硬件不支持在ANSI的ASCII或IBM的EBCDIC之外其他字符的事实。
传输表示采用“臻选字符”时,“加上乘以”符号⊥
替代为I
,“乘以的幂”符号⏨
和\
替代为E
。
ALGOL的“特殊”字符:
- × ÷ ≤ ≥ ≠ ≡ ¬ ∧ ∨ ⊃ ↑ ⏨ ␣ ↓ → ⊥ ○ □ ⌊ ⌈ ⎩ ⎧ ¢
多数都可以在1965年出现的配备APL打字球与键帽后的IBM 2741终端的键盘上找到。这些字符也是Unicode标准的一部份,并且在一些流行的字体中可获得到。
单元
[编辑]基本语言构造是“单元”(unit)。单元可以是“公式”、“封闭子句”、“例程正文”这样的构造,也可以是某个技术上需要的构造诸如赋值、跳转、越过和空无。
技术术语“封闭子句”(enclosed clause)统一了某些固有的加括号的构造,它们在其他当代语言中被称为“块”、“do语句”、“switch语句”等等。在使用关键字的时候,通常使用介入关键字的反转字符序列来终结这个包围,比如:IF ~ THEN ~ ELSE ~ FI
、CASE ~ IN ~ OUT ~ ESAC
和FOR ~ WHILE ~ DO ~ OD
。这种守卫命令语法被Stephen Bourne重新使用在常用的Unix Bourne shell之中。
一个表达式还可以产生多元值(multiple value)即有多个元素的一个值,它是通过“并立子句”(collateral clause)从其他一些值构造出来的。这种构造看起来就像过程调用的参数包(pack)。
模态
[编辑]数据类型在ALGOL 68用语中叫做“模态”(mode),其原始模态的声明符为:INT
、REAL
、COMPL
(复数)、BOOL
、CHAR
、BITS
、BYTES
和STRING
。ALGOL 68不再定义long
、short
和double
这样的类型,而是提供了修饰符(modifier)LONG
和SHORT
。
BITS
–BOOL
值的包装向量(packed vector)。BYTES
–CHAR
值的固定大小的紧致多元组(数组)。STRING
–CHAR
值的FLEX [1 : 0]
(声明时无元素)的灵活多元组(数组)。LONG
– 声明INT
、REAL
或COMPL
的大小为LONG
。SHORT
– 声明INT
、REAL
或COMPL
的大小为SHORT
。
ALGOL 68提供了预置常量(prelude constant)如max real
和long max real
等来将程序适配到不同实现之中。
复杂模态可以使用模态构造子REF
、STRUCT
、UNION
和PROC
来创建自更简单的模态:
REF mode
– 到模态mode
的值的引用,类似于C/C++中和Pascal中的指针。STRUCT
– 用来建造结构,类似于C/C++中的struct
和Pascal中的recode
。UNION
– 用来建造联合,类似于C/C++和Pascal中的union
。PROC
– 用来指定过程,类似于C/C++中的函数和Pascal中的过程/函数。
在一个代码块中,诸声明不必须都先于诸语句,但所有名字都必须在使用前声明。ALGOL 68提供了恒等声明来将标识符绑定到常量值之上。在声明给名字的标识符之时,这个名字可以被初始化为立即引用一个值;还可以将名字声明为等同于以前声明的名字而使得二者互为别名。例如:
INT n = 2; # n是新建的常量,它被固定为2 # INT m := 3; # m是新建的局部变量的名字,这个变量的值被初始化为3 # INT p = m; # p是新建的常量,它被固定为同于m当前的值 # INT q := m; # q是新建的局部变量的名字,这个变量的值被初始化为同于m当前的值 # REF INT r = m; # r是新建的变量名字,它与m引用的是同一个变量 # REAL avogadro = 6.02214076E23; # 阿伏伽德罗常数 # LONG REAL long pi = 3.14159 26535 89793 23846 26433 832795; COMPL imaginary unit = 0 I 1; # 虚数单位i即复数0 + 1i #
其他声明符号包括:FLEX
、HEAP
和LOC
。
FLEX
– 所声明的名字是灵活的,就是说它可以引用任何数量元素的多元组(数组)。HEAP
– 为变量从全局堆中分配一些空闲空间。LOC
– 为变量分配这个局部栈的一些空闲空间。
声明REAL x
只是REF REAL x = LOC REAL
的语法糖,LOC REAL
是生成REF REAL
模态的名字的单元。在REF REAL x = LOC REAL := 0
中,赋值LOC REAL := 0
导致整数0
在被赋值给这个生成器产生的名字之前就被扩充(widen)为实数0.0
,声明的名字x
与生成器产生的名字二者所引用的是同一个变量。
原始声明符还包括:FORMAT
、FILE
、CHANNEL
、SEMA
、COMPLEX
G和PIPE
G。
SEMA
– 可以通过运算符LEVEL
初始化的信号量。
ALGOL 68使用MODE
声明来给模态声明名字,它类似于C/C+中的typedef
和Pascal中的type
:
INT max = 99; MODE NEWMODE = [0:9][0:max]STRUCT ( LONG REAL a, b, c, SHORT INT i, j, k, REF REAL r);
这类似于如下C99代码:
const int max = 99;
typedef struct {
double a, b, c; short i, j, k; float *r;
} newmode[9+1][max+1];
对于ALGOL 68,只有模态标示(indicant)NEWMODE
出现在等号的左侧,最值得注意的是,构造的制造和读取,是从左至右而不顾及优先级的。还有ALGOL 68多元组(数组)的下界缺省的是1
,但也可以是从-max int
到max int
的任何整数。
模态声明允许递归模态,就是说模态可直接或间接的依据自身来进行定义。这要服从一些限制,例如下列声明是非法的:
MODE A = REF A; MODE A = STRUCT (A a, B b); MODE A = PROC (A a) A;
而下列声明是有效的:
MODE A = STRUCT (REF A a, B b); MODE A = PROC (REF A a) REF A;
注释和编译指导
[编辑]注释可以按各种方式插入:
¢ 最初方式是在程序中增加两个美分符号 ¢ COMMENT "粗体"注释 COMMENT CO 风格i注释 CO # 风格ii注释 # £ 针对英国键盘的横杠/英镑注释 £
一般而言,注释在ALGOL 68中不能嵌套。这个限制可以使用不同的注释分界符来绕过。
语用指定(pragmat)是在程序中的编译指导,典型的提示给编译器;在新近的语言中叫做“pragma”。例如:
PRAGMAT heap=32 PRAGMAT PR heap=32 PR
表达式和复合语句
[编辑]ALGOL 68成为了面向表达式编程语言,赋值语句所返回的值是到目的地的引用。因此下列代码在ALGOL 68中是有效的:
REAL half pi, one pi; one pi := 2*(half pi := 2*arc tan(1))
这种表示法也出现在C语言和Perl以及其他一些语言之中。注意在早期语言比如ALGOL 60和FORTRAN中,在标识符中的空格是允许的,所以half pi
是一个单一的标识符,因而无需采用蛇形命名法或驼峰式大小写。
另一个例子,要表达数学中f(i)
从i=1
到n
的总和,下列ALGOL 68整数表达式就可充任:
(INT sum := 0; FOR i TO n DO sum +:= f(i) OD; sum)
注意,作为整数表达式,上述的代码块可以用在“整数值可以使用的任何上下”之中。代码块在ALGOL 68称为“系列子句”(serial clause),它返回其中被求值的最后一个表达式的值;这个概念也出现在LISP以及其他一些语言之上。系列子句加上封闭它的一对圆括号或BEGIN
与END
叫做闭合子句(closed-clause)。
复合语句都终止于独特的闭括号:
IF选择子句
[编辑]IF 条件 THEN 诸语句 [ ELSE 诸语句 ] FI 简短形式:( 条件 | 诸语句 | 诸语句 )
IF 条件1 THEN 诸语句 ELIF 条件2 THEN 诸语句 [ ELSE 诸语句 ] FI 简短形式:( 条件1 | 诸语句 |: 条件2 | 诸语句 | 诸语句 )
这种方案不仅避免了悬摆else问题,还避免了必须对嵌入的语句序列使用BEGIN
和END
。
CASE选择子句
[编辑]CASE 开关 IN 诸语句, 诸语句, ... [ OUT 诸语句 ] ESAC 简短形式:( 开关 | 诸语句, 诸语句, ... | 诸语句 )
CASE 开关1 IN 诸语句, 诸语句, ... OUSE 开关2 IN 诸语句, 诸语句, ... [ OUT 诸语句 ] ESAC 简短形式:( 开关1 | 诸语句, 诸语句, ... |: 开关2 | 诸语句, 诸语句, ... | 诸语句 )
采用粗体符号的选择子句的例子:
PROC days in month = (INT year, month) INT: CASE month IN #一月# 31, #二月# IF year MOD 4 EQ 0 AND year MOD 100 NE 0 OR year MOD 400 EQ 0 THEN 29 ELSE 28 FI, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 # 直到十二月 # ESAC;
采用简短符号的选择子句的例子:
PROC days in month = (INT year, month) INT: (month| 31, (year%*4 = 0 AND year%*100 /= 0 OR year%*400 = 0 | 29 | 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
粗体和简短符号的选择子句可以混合使用。
ALGOL 68允许开关(switch)具有的类型,要么是INT
要么是(独有的)UNION
。后者允许在UNION
变量上施加强类型,参见后面的联合例子。
[ FOR 索引 ] [ FROM 第一个 ] [ BY 增量 ] [ TO 最后一个 ] [ WHILE 条件 ] DO 诸语句 OD
循环语句的极小化形式是:DO 诸语句 OD
。
下面是这种“通用”循循的完整例子:
FOR i FROM 1 BY -22 TO -333 WHILE i*i /= 4444 DO ~ OD
这个构造有一些不寻常的方面:
- 只有
DO ~ OD
部份是强制性的,在这种情况下循环将无限迭代。 - 因此子句
TO 100 DO ~ OD
,将只迭代100次。 WHILE
“语法元素”允许编程者更早的从FOR
循环中破出,例如:
INT sum sq := 0; FOR i WHILE print(("So far:", i, newline)); sum sq /= 70**2 DO sum sq +:= i**2 OD
后续的对标准ALGOL 68的扩展,允许TO
语法元素被替代为UPTO
和DOWNTO
来达成小优化。这些编译器还结合了:
UNTIL
C – 用于更晚的循环终止。FOREACH
S – 用于并行的运算于多元组(数组)之上。
结构与联合
[编辑]ALGOL 68支持多字段结构STRUCT
和标签联合UNION
。REF
可以前导于任何MODE
,包括多元组分片和结构字段。
作为递归模态的例子,下面是传统的链表的声明:
MODE VALUE = UNION (VOID, REAL, INT, COMPL, STRING), NODE = STRUCT (VALUE val, REF NODE next), LIST = REF NODE;
对上述VALUE
的UNION
使用共形子句(conformity clause)的CASE
的例子:
VALUE v := "1234"; # 或者 v := EMPTY; # CASE v IN (VOID): print(("void:", "EMPTY")), (REAL r): print(("real:", r)), (INT i): print(("int:", i)), (COMPL c): print(("compl:", c)), (STRING s): print(("string:", s)) OUT print("undefined value") ESAC
在未修订报告的语言中共形子句的功能通过在CASE
子句中采用共形关系CTAB
来实现。
多元组
[编辑]在ALGOL 68中,不再采用术语数组或阵列(array),转而基于数学中的元组(-tuple)定义了多元值(multiple value)亦简称多元组(multiple)。多元组由一定数量的元素组成,每个元素都有相同的模态,有时称之为基础模态。多元组的模态由前导着一对方括号的针对每个元素的模态标示组成,它被读作“成行的模态”(row of mode),它不同于函数式编程语言如ML中的对应于代数数据类型里积类型的“元组”(tuple)。
ALGOL 68多元组的属性之一是它的维度数目。可以声明任何数目维度的多元组,整数的二维多元组拥有模态[,]INT
,它被读作“行套行的整数”(row-row-of-int),而实数的三维多元组拥有模态[,,]REAL
。
MODE VECTOR = [1:3]REAL; # 向量MODE声明 # MODE MATRIX = [1:3,1:3]REAL; # 矩阵MODE声明 # VECTOR v1 := (1,2,3); # 向量变量初始化为 (1,2,3) # []REAL v2 = (4,5,6); # 常量元组,类型等价于VECTOR,边界是隐含的 # OP + = (VECTOR a, b) VECTOR: # 二元OP即运算符定义 # (VECTOR out; FOR i FROM LWB a TO UPB a DO out[i] := a[i]+b[i] OD; out); MATRIX m := (v1, v2, v1+v2);
这里的冒号分隔构造:第一个元素的下标 : 最后一个元素的下标
,被称为裁剪器(trimmer)。
ALGOL 68的分片,在概念上涵盖了向多元组的各个维度加下标(subscript)从而访问其中特定元素的常规情况,但这个术语更常用的保留给至少一个维度不加下标的情况,比如从矩阵中切分出部份横行(row)或纵列(column)。例如:
REF VECTOR row = m[2,]; # 定义一个REF至矩阵的第二个横行 # REF VECTOR col = m[,2]; # 定义一个REF至矩阵的第二个纵列 # print ((m[,2:])); # 打印矩阵的从第二个至最后一个的纵列 #
省略了裁剪器中第一个下标则假定其为此维度的下界,而省略了第二个下标则将假定其为对应的上界,两个下标都省略则产生下界为1
的全部分片。
过程
[编辑]过程(procedure)的PROC
声明对参数和结果值二者要求类型指定,如果没有参数则省略参数列表,如果没有结果值则指定结果模态为VOID
:
PROC max of real = (REAL a, b) REAL: (a > b | a | b);
过程的返回值是在过程中求值的最后一个的表达式的值。当进行如下过程调用之时:
a := max of real(x, y)
得到等价于进行了如下变换后的结果:
a := REAL (REAL a = x, b = y; (a > b | a | b))
在这个过程之内对变量a
和b
的赋值是非法的。针对需要变更形式参数所提及的值的情况,ALGOL 68提供了传引用调用参数,用来在形式参数列表中指定引用,比如REF REAL
。到过程的引用REF PROC
也是允许的。下列例子定义一个过程,它将作为参数而指定的一个函数应用于一个数组的每个元素:
PROC apply = (REF []REAL a, PROC (REAL) REAL f) VOID: FOR i FROM LWB a TO UPB a DO a[i] := f(a[i]) OD;
这种代码的简单性是在ALGOL 68的前身ALGOL 60中不能达成的。
运算符与关联名字的单元
[编辑]过程和运算符(operator)合称例程(routine),编程者可以定义新的运算符,并且这些自定义的和预定义的运算符二者都可以重载,并且它们的优先级可以由编码者变更。下列例子定义的运算符MAX
具有二元和一元形式二者,一元形式在一个数组的元素之上进行扫描。
PRIO MAX = 9; OP MAX = (INT a, b) INT: (a > b | a | b); OP MAX = (REAL a, b) REAL: (a > b | a | b); OP MAX = (COMPL a, b) COMPL: (ABS a > ABS b | a | b); OP MAX = ([]REAL a) REAL: (REAL out := a[LWB a]; FOR i FROM LWB a + 1 TO UPB a DO (a[i] > out | out := a[i]) OD; out);
初等和二等单元
[编辑]初等单元(primary)涵盖封闭子句,此类属自身包括:所应用的标识符、调用、铸型、除了例程指示之外的指示和分片。
所应用的标识符(applied-identifier)意味着标识符在一个上下文之中被使用,并非处在其定义之中。
指示(denotation)是其所产生与任何行动都无关的构造,比如实数指示3.14
或字符串指示"abc"
,模态VOID
只有一个值,其指示是EMPTY
。在其他语言中,指示有时叫做“文字”(literal)或“常值”(constant)。
铸型(cast)构成自一个模态标示(indicant)和随后的通常为闭合子句的封闭子句。铸型可被用来提供强位置,在强上下文中能获得所有的强制。例如在REF REAL (xx) := 1
中的REF REAL (xx)
。
调用(call)引起(invoke)一个过程,调用在ALGOL 68 Genie中可以被部份参数化(partial parameterisation):就是说增加实际参数到一个过程的语境(locale)中,当这个语境完全之时调用这个过程,否则发生柯里化。
优先级 | ALGOL68r1“臻选字符” | ALGOL68G |
---|---|---|
相当于12 | 加下标[~] ,分片[~,~] ,裁剪[~:~] ,AT @
|
DIAG ,TRNSP ,ROW ,COL
|
二等单元(secondary)包括生成器(generator)和选取(selection)。
优先级 | ALGOL68r1“臻选字符” | ALGOL68r0−r1 | ALGOL68G |
---|---|---|---|
相当于11 | OF ,LOC ,HEAP
|
→
|
NEW
|
有关的符号在技术上不是运算符,它们转而被当作“关联名字的单元”[35]。
三等单元
[编辑]三等单元(tertiary)包括公式和NIL
(可替代为○
)。公式构成自运算符和运算元。
在ALGOL 68中对于名字只有一个指示,那就是含义为不引用任何值的NIL
。NIL
的模态依赖于上下文,例如:REF INT k = NIL
,这里的NIL
拥有模态REF INT
。尽管NIL
是一个名字却不可以对它赋值。
一元运算符
[编辑]优先级 | ALGOL68r1“臻选字符” | ALGOL68r1替代符号 | ALGOL68C,G | ALGOL68r0-r1 |
---|---|---|---|---|
相当于10 | NOT ,- ,UP ,DOWN ,LWB ,UPB ,ABS ,ARG ,BIN ,ENTIER ,LENG ,LEVEL ,ODD ,REPR ,ROUND ,SHORTEN
|
¬ ~ ,↑ ,↓ ,⌊ ,⌈
|
NORM ,TRACE ,T ,DET ,INV
|
LWS ⎩ ,UPS ⎧ ,BTB ,CTB
|
二元运算符及其关联的优先级
[编辑]优先级 | ALGOL68r1“臻选字符” | ALGOL68r1替代符号 | ALGOL68r0−r1 |
---|---|---|---|
9 | I +* |
⊥ +× |
!
|
8 | UP ** ,DOWN ,LWB ,UPB ,SHL ,SHR |
↑ ,↓ ,⌊ ,⌈ |
^ ×× ,LWS ⎩ ,UPS ⎧
|
7 | * ,/ ,OVER % ,MOD %* ,ELEM |
× ,÷ ,÷× ÷* %× ,□ |
÷:
|
6 | - ,+ |
||
5 | LT < ,LE <= ,GE >= ,GT > |
≤ ,≥ |
|
4 | EQ = ,NE /= |
≠ ¬= ~= |
|
3 | AND |
∧ & |
/\
|
2 | OR |
∨ |
\/
|
1 | MINUSAB -:= ,PLUSAB +:= ,TIMESAB *:= ,DIVAB /:= ,OVERAB %:= ,MODAB %*:= ,PLUSTO +=:
|
×:= ,÷:= ,÷×:= ÷*:= %×:= |
MINUS ,PLUS ,TIMES ,DIV ,OVERB ,MODB ÷::= ,PRUS
|
特殊细节:
LWS
和UPS
:在ALGOL 68r0中,它们分别在一个多元组(数组)的这个维度的“下界状态”和“上界状态”为固定的(fixed)的情况下返回TRUE
。LWB
和UPB
:这两个运算符所运算的多元组运算元的模态是ROW
,它是指称任何横行的模态。
四等单元
[编辑]四等单元(quaternary)包括:赋值、同一关系、例程指示(也称作例程正文)和SKIP
(可替代为~
)。四等单元并非语言报告中的术语,它是给从单元这个类属中除去封闭子句、初等单元、二等单元和三等单元所余下的只称为“单元”的类属的别名。
SKIP
产生任何模态的未定义的值,并且只能出现在强上下文中。
优先级 | ALGOL68r1“臻选字符” | ALGOL68r1替代符号 | ALGOL68C,R | ALGOL68r0−r1 |
---|---|---|---|---|
相当于0 | := ,IS :=: ,ISNT :/=: |
:≠: :¬=: :~=:
|
:=:= C,=:= R |
..= .= ,IS NOT ,CT :: ,CTAB ::=
|
在ALGOL 68中除了恒等声明之外的所有构造都有一个值,它允许链式赋值,例如a := b := c
,这些赋值是从右至左执行的。
同一关系包括:IS
(可替代为:=:
)测试两个引用是否相等;ISNT
(可替代为:/=:
)测试两个引用是否不相等。同一关系的一侧是可以在强制时解引用来匹配另一侧的强侧,而另一侧则为软侧,不允许两侧都解引用。
强制
[编辑]强制(coercion)依据三个要件,从被强制者(coercend)产生已强制者(coercee):在应用任何强制之前作为要被强制者的一个先验模态,在这些强制之后作为已被强制者的一个后验模态,已强制者的的语法位置(position)或类属(sort)。强制是可以级联的(cascaded)。
有7种可能的强制,其术语为解过程化(deproceduring),解引用(dereferencing)和弱解引用(weakly-dereferencing),联合化(uniting),扩充(widening)、入行(rowing)和弃置(voiding)。除了联合化之外,每种强制都在所关联的值之上,规制(prescribe)一个相应的动态效果。因此,可以使用强制来编程很多原始行动。
允许特定强制的境况(circumstance)叫做上下文(context)。下面列出每种上下文所固有的强度及其允许的强制:
- 软(soft)– 解过程化。
- 弱(weak) – 软强制,随后弱解引用而产生一个名字。
- 柔(meek)– 软强制,随后解引用。
- 硬(firm)– 柔强制,随后联合化。
- 强(strong)– 硬强制,随后扩充、入行或弃置。
在任何上下文中,都有拥有或产生某种模态的值的一个单元,并且在这个上下文中亦有所需的一个模态。如果两个模态不同而现有模态的值能够强制成所需模态的值,则这种强制是合法的。
平衡是将条件、case
和共形子句中的交替者(alternative)即可供选择者,和同一关系的两侧,都强制成共同(common)的模态的手段,这使得所涉及构造的上下文会被提升(promote)从而获得这种强制。
强制层级及例子
[编辑]ALGOL 68有着上下文层级,它确定在程序中特定点之上可获得的强制的种类。这种上下文分为五个层次:
上下文 | 上下文位置 | 可获得的强制 | 在此上下文中强制例子 | ||||
---|---|---|---|---|---|---|---|
软 | 弱 | 柔 | 硬 | 强 | |||
强 |
|
解过程化 | 只在弱上下文时软强制再弱解引用 | 软强制再解引用 | 柔强制再联合化 | 硬强制再扩充或入行或弃置 |
扩充出现在没有精度损失的情况下。例如:
变量可以入行为长度为1的多元组。例如:
|
硬 |
|
例子:
UNION(INT, REAL) var := 1 | |||||
柔 |
|
例子:
| |||||
弱 |
|
例子:
| |||||
软 |
|
例子:
|
正交性
[编辑]ALGOL 68文法的表达依据了一些原始概念:值、模态、上下文、强制和短语(phrase)。短语是声明和单元二者之一。共有5种上下文、7种强制、22种不同的单元,和潜在无穷数量的值和模态。在每种上下文中都有可获得的强制。
正交性所称谓的是基本概念可以被没有副作用的组合。原始概念:值、模态、上下文、强制和短语,是相互独立的,它们的组合给予了ALGOL 68少有编程语言拥有的灵活性。举个例子,如果需要模态INT
的一个值,比如在多元组的声明的裁剪器或边界中,那么在这个上下文中任何会产生一个整数的单元都可充任。其结果是ALGOL 68程序可以用宽广多样的风格来书写。例如打印从键盘读取的两个数的总和,用常规风格可以写为:
INT a, b; read((a, b)); print((a + b, newline))
也可以等价的写为:
print(((INT a, b; read((a, b)); a + b), newline))
只要所书写的是合法的ALGOL 68代码,可以采用编程者喜好的任何方式。这种独立性的另一个结果是语言的规则极少有例外。
传输
[编辑]传输(transput)是ALGOL 68用来称谓输入和输出设施的术语。它包括针对非格式化、格式化和二进制传输的预定义的过程。文件和其他传输设备以机器无关的方式来处理。下面的例子打印出一些非格式化的输出到“标准输出”设备:
print ((newpage, "Title", newline, "Value of i is ", i, "and x[i] is ", x[i], newline))
注意预定义的过程newpage
和newline
作为实际参数而传送。
print
和read
接受可能具有各种所需模态的实际参数,这些例程的形式参数具有成行的联合模态:
PROC print = ([]SIMPLOUT items) VOID: …… ; MODE SIMPLOUT = UNION ( INT, REAL, BOOL, CHAR, []INT, []REAL, []BOOL, []CHAR, [,]INT, [,]REAL, [,]BOOL, [,]CHAR, …… ); PROC read = ([]SIMPLIN items) VOID: …… ; MODE SIMPLIN = UNION ( REF INT, REF REAL, REF BOOL, REF CHAR, REF []INT, REF []REAL, REF []BOOL, REF []CHAR, REF [,]INT, REF [,]REAL, REF [,]BOOL, REF [,]CHAR, …… );
read
使用的模态SIMPLIN
是来自各种名字的模态的联合。print
使用共形子句来从参数行中每个元素提取出实际的值。
在ALGOL 68中“格式化传输”拥有自己的语法和模式(函数),具有嵌入在两个$
字符之间的FORMAT
。例如:
printf (($2l"The sum is:"x, g(0)$, m + n)); # 其打印相同于下列: # print ((new line, new line, "The sum is:", space, whole(m + n, 0))
并行处理
[编辑]ALGOL 68支持并行处理编程。使用关键字PAR
,可以将“并立子句”转换成“并行子句”,这里的行动的同步使用信号量来控制。并行行动在ALOGL 68 Genie中被映射到线程上,如果它们在宿主操作系统上能获得到的话。例如:
PROC eat = VOID: (muffins -:= 1; print(("Yum!", newline))), speak = VOID: (words -:= 1; print(("Yak...", newline))); INT muffins := 4, words := 8; SEMA mouth = LEVEL 1; PAR BEGIN WHILE muffins > 0 DO DOWN mouth; eat; UP mouth OD, WHILE words > 0 DO DOWN mouth; speak; UP mouth OD END
未修订报告的语言
[编辑]原本依据1968年最终报告的语言在模态铸型的语法上有所不同,并且它拥有过程化(proceduring)这个特征,就是说,将一个项目的值强制成求值这个项目的一个过程。过程化意图进行惰性求值。最有用的应用是实现布尔运算符的短路求值:
OP ANDF = (BOOL a, PROC BOOL b) BOOL: (a | b | FALSE); OP ORF = (BOOL a, PROC BOOL b) BOOL: (a | TRUE | b);
在ANDF
中,b
只在a
为TRUE
的情况下才求值。预期下面例子中的print
不会被执行:
IF FALSE ANDF (print("Should not be executed"); TRUE) THEN …… FI
语言修订之后,已经不再允许这种从模态BOOL
到模态PROC BOOL
的强制和铸型。ALGOL 68 Genie将ANDF
和ORF
作为扩展而实现为伪运算符。
在语言修订之前,编程者可以通过使用叫做“gomma”的分号替代逗号(comma),从而决定一个过程的参数串行而非并立的求值。例如:
PROC test = (REAL a; REAL b): …… ; test (x +:= 1, x);
这里保证第一个实际参数求值在第二个实际参数之前,但是在平常情况:
PROC test = (REAL a, b): …… ; test (x +:= 1, x);
这时编译器可以按它喜好的次序来求值实际参数。
参见
[编辑]註釋
[编辑]- ^ 1.0 1.1 Neil Matthew. The RSRE Algol-68RS Compiler. May 2021.
An update of the original port by Sian Mountbatten of a68toc (ctrans) from Algol-68RS/ELLA2000 updated to run on Intel and ARM processors (32- and 64-bit) Linux and macOS systems.
- ^ 2.0 2.1 ALGOL 68 Genie. [2020-04-22]. (原始内容存档于2020-08-14).
- ^ Dennis Ritchie. The Development of the C Language (PDF). April 1993.
The scheme of type composition adopted by C owes considerable debt to Algol 68, although it did not, perhaps, emerge in a form that Algol’s adherents would approve of. The central notion I captured from Algol was a type structure based on atomic types (including structures), composed into arrays, pointers (references), and functions (procedures). Algol 68’s concept of unions and casts also had an influence that appeared later. …… a notation for type conversions (called ‘casts’ from the example of Algol 68) was invented to specify type conversions more explicitly.
- ^ A History of C++: 1979−1991 (PDF). Page 12, 2nd paragraph: Algol68 [gave] operator overloading(§3.3.3), references (§3.3.4), and the ability to declare variables anywhere in a block (§3.3.1). March 1993.
- ^ Interview with Guido van Rossum. July 1998 [2007-04-29]. (原始内容存档于2007-05-01).
String slicing came from Algol-68 and Icon.
- ^ 6.0 6.1 6.2 6.3 Revised Report on the Algorithmic Language Algol 68.
- ^ Adriaan van Wijngaarden. Orthogonal design and description of a formal language (PDF). 1965.
- ^ signed by Edsger Dijkstra, Fraser Duncan, Jan Garwick, Tony Hoare, Brian Randell, Gerhard Seegmüller, Wlad Turski, Mike Woodger. ALGOL Bulletin 30.1.1.1 "Minority Report". March 1970 [2007-03-01]. (原始内容存档于2007-09-30).
More than ever it will be required from an adequate programming tool that it assists, by structure, the programmer in the most difficult aspects of his job, viz. in the reliable creation of sophisticated programs. In this respect we fail to see how the language proposed here is a significant step forward: on the contrary, we feel that its implicit view of the programmer's task is very much the same as, say, ten years ago. This forces upon us the conclusion that, regarded as a programming tool, the language must be regarded as obsolete.
- ^ Hoare, C. a. R. Critique of ALGOL 68. ALGOL Bulletin. November 1968, 29: 27–29.
I believe that the essential problem in the design of self-extending languages is in the design of the nucleus of built-in features, which the implementor will be expected to represent within the machine code of his computer. It is essential that this nucleus should have the properties: 1. Extreme simplicity and small size …… 2. Extreme efficiency of implementation …… I would therefore reckon that a language at about the level of FORTRAN would be a suitable choice for a nucleus. The language nucleus described in MR93 is far too elaborate; and some of its defects are listed in Appendix II.
- ^ Dijkstra, E. W. To the Editor ALGOL 68 Mathematische Centrum. [2007-04-28]. (原始内容存档于2007-04-21).
On account of the draft report my faith in WG.2.1 (at least in its present constitution) is very low. The draft report is thick and difficult, in fact too thick and too difficult to inspire much confidence. …… Size and complexity of the defining apparatus you needed terrify me. Being well-acquainted with your ingenuity I think it a safe assumption that ALGOL 68 as conceived can hardly be defined by significantly more concise and transparent means. …… I feel inclined to put the blame on the language you tried to define. If this is correct, WG.2.1 should return to its proper subject matter, viz. programming languages.
- ^ Niklaus Wirth. ALGOL Colloqium – Closing Word. 1968.
The implied and rather vague goal was the specification of a universal language, a sensible goal in 1960, even 1964, but an utopia in 1968; a goal which if pursued faithfully, invariably lead towards a monster language, a species of which there already exists a sample hardly worth nor possible to compete with.
- ^ Learning Algol 68 Genie (PDF).
Some claim that Ada is Algol 68’s successor but many dispute that.
- ^ Adriaan van Wijngaarden. Generalized ALGOL (PDF). Symbolic Languages in Data Processing, Proc. Symp. Intl, Computation Center Rome, Gordon & Beach, New York. 1962.
- ^ Van Wijngaarden, A.; Mailloux, B. J.; Peck, J.; Koster, C. H. A. Draft Report on the Algorithmic Language ALGOL 68. ALGOL Bulletin. 1 March 1968, (Sup 26): 1–84 [7 April 2023] –通过Mar. 1968.
- ^ Report on the algorithmic language ALGOL 68. 1969.
- ^ Sidney Marshall. J. E. L. Peck , 编. ALGOL 68 Implementation (PDF). Proceedings of the IFIP Working Conference on ALGOL 68 Implementation, Munich,: 239–243. July 20–24, 1970.
Sidney Marshall. On the implementation of ALGOL 68 (PDF). PhD Thesis, Dartmouth College. 1972. - ^ L. Ammeraal. An interpreter for simple Algol 68 Programs (PDF). 1973.
- ^ Daniel M. Berry. The importance of implementation models in ALGOL 68: or how to discover the concept of necessary environment.
- ^ "Application of the Machine-Oriented Language Epsilon to Software Development", I.V. Pottosin et al., in Machine Oriented Higher Level Languages, W. van der Poel, N-H 1974, pp. 417–434
- ^ Algol68C Release 1.3039 for IBM 360/370/etc mainframes (or their emulations) running MVT or MVS. March 2013.
- ^ Liverpool Software Gazette March 1980 (PDF). [2010-03-20]. (原始内容 (PDF)存档于2010-04-15).
- ^ Revised report on the algorithmic language ALGOL 68 (PDF). 1976.
- ^ Hendrik Boom. 1978 branch of Algol 68 H development tree. 2008.
- ^ ALGOL 68 Version I Reference Manual (PDF). Control Data Services B.V., Rijswijk, Netherlands. 1976.
- ^ ALGOL68 instruction at Oklahoma State University.
- ^ "The Berlin ALGOL 68 implementation"
An abstract ALGOL 68 machine and its application in a machine independent compiler – Springer. Springerlink.com. Retrieved on 2013-07-21. - ^ Algol 68+, a superlanguage of Algol 68 for processing the standard-prelude (PDF). 1981.
- ^ a68mk2.zip. (原始内容存档于2006-08-29).
- ^ Hibbard, P.G. A Sublanguage of ALGOL 68. SIGPLAN Notices. May 1977, 12 (5): 71–79. S2CID 37914993. doi:10.1145/954652.1781177 .
- ^ Dr C. H. Lindsey. The ALGOL 68S System. 1988.
- ^ Laci Csirmaz. An ALGOL 68 interpreter, lots of sample programs. You can learn the usage of UNION types. 2000.
- ^ ELLA is a Hardware description language and support toolset.
- ^ Dr Sian Mountbatten. A68ToC port to Debian Linux. Version 1.15. Sep 13 2012.
- ^ Report on the Standard Hardware Representation of Algol 68. 16 May 1977.
- ^ Units associated with names.
参考文献
[编辑]- Brailsford, D. F. and Walker, A. N. Introductory ALGOL 68 Programming. Ellis Horwood/Wiley. 1979.
- Lindsey, C. H. and van der Meulen, S. G. Informal Introduction to ALGOL 68 : Revised Edition. (PDF). North-Holland. 1981.
- Lindsey, C. H. A History of ALGOL 68. ACM SIGPLAN Notices. 1993-03-02, 28 (3): 97–132. doi:10.1145/155360.155365 .
- McGettrick, A. D. ALGOL 68, A First and Second Course. Cambridge Univ. Press. 1978.
- Peck, J. E. L. An ALGOL 68 Companion. Univ. of British Columbia. October 1971.
- Tanenbaum, A. S. A Tutorial on ALGOL 68. Computing Surveys 8, 155-190, June 1976 and 9, 255-256. September 1977.
- Woodward, P. M. and Bond, S. G. ALGOL 68-R Users Guide. London, Her Majesty's Stationery Office. 1972.
外部链接
[编辑]- Revised Report on the Algorithmic Language ALGOL 68(页面存档备份,存于互联网档案馆) The official reference for users and implementors of the language (large pdf file, scanned from Algol Bulletin)
- Revised Report on the Algorithmic Language ALGOL 68(页面存档备份,存于互联网档案馆) Hyperlinked HTML version of the Revised Report
- A Tutorial on Algol 68, by Andrew S. Tanenbaum, in Computing Surveys, Vol. 8, No. 2, June 1976, with Corrigenda (Vol. 9, No. 3, September 1977)
- Algol 68 Genie – a GNU GPL Algol 68 compiler-interpreter(页面存档备份,存于互联网档案馆)
- Open source Algol 68 implementations, on SourceForge(页面存档备份,存于互联网档案馆)
- Algol68 Standard Hardware representation (.pdf)(页面存档备份,存于互联网档案馆)
- Из истории создания компилятора с Алгол 68(页面存档备份,存于互联网档案馆)
- Algol 68 – 25 Years in the USSR(页面存档备份,存于互联网档案馆)
- Система программ динамической поддержки для транслятора с Алгол 68
- C history with Algol68 heritage
- McJones, Paul, "Algol 68 implementations and dialects"(页面存档备份,存于互联网档案馆), Software Preservation Group, Computer History Museum, 2011-07-05
- Web enabled ALGOL 68 compiler for small experiments