分类:
2006-05-25 15:10:56
第二章 CL程序设计
这章的重点是ILE 而不是OPM。因此,在这章中用‘过程’来代替‘程序’。但在讨论一般的CL命令时,仍用程序这个术语。
一个CL过程是一组CL命令,它告诉系统从哪得到输入,怎样处理,结果放在哪儿。过程要有一个名字,其它过程用此名来调用它,也用这个名字联编到程序中。与其它类型过程一样,CL过程也要先输入源语句、编译、联编后才能运行。
在进入单个CL命令时,每个命令是分开处理的。当进入做为过程源语句的CL命令时,源语句可保留做以后修改用。命令编译形成模块,这个模块保留做永久的系统目标能连编到其它程序及运行。这样,CL就相当于一个系统功能的高级程序设计语言。CL过程保证一组命令处理的一致性,能用CL过程完成单个命令实现不了的功能,且能提供比单个命令运行好得多的运行性能。
CL过程在交互和批处理中都可使用,某些命令或过程只能在交互或批作业中处理。
CL源语句由CL命令组成,不是所有CL命令都能做为源语句,有些只能用在CL过程或OPM程序中,可在CL参考手册中每个命令语法图右上角的标识来确定使用限制。如下图:
PGM:B,I |
(P) |
|
(I) |
|
|
注:(I) 最多反复40次。
(P) 这点前以所有参数都可以按位置参数格式规定。
右上角的‘PGM:B,I’表示这个命令可用在交互和批作业中,但仅能用在CL程序或过程。如果此处没有PGM字样,说明此命令不能用做CL程序或过程的源语句。
源语句可从工作站交互地输入到源成员中,也可从设备上做为批作业的输入流。要用CL源语句生成一个程序,必须把源语句输入到数据库的源成员中,然后编译这些源语句生成ILE模块,再连编这个模块形成程序。
CL过程可以做:
控制操作顺序及调用其它程序或过程
根据菜单选项的选择显示菜单及运行命令
读数据库文件
处理命令、程序或过程发布的错误条件,管理特定信息
为了控制应用程序的操作建立一些变量,例如日期、时间及外部指针。
为程序员提供预先定义的功能,例如启动子系统、备份文件,这样能减少经常
使用的命令数,保证系统操作的性能一致。
CL过程不可以做:
增加或修改数据库文件中的记录
使用打印机和ICF文件
在显示文件中使用子文件
使用程序描述的显示文件
2.1 生成CL程序
所有程序都按下列步骤来生成:
1. 输入源码,在大多情况下,源语句按应用程序设计要求的逻辑顺序输入到数据库文件中。
2. 生成模块,用CRTCLMOD命令和源语句生成系统目标,生成的CL模块可连编到程序中,一个CL模块由一个CL过程组成,其它的HLL语言的每个模块可包括多个过程。
3. 生成程序,用CRTPGM命令,这个模块(以及其它模块或服务程序)用于生成一个程序。
注:如果生成的程序仅有一个模块,那么可用CRTBNDCL命令,把第2步和第3步合成一步。
2.1.1 交互式输入
AS/400提供许多菜单和显示来帮助程序员,象程序员菜单、命令入口显示、命令提示和PDM菜单,也能由用户配置文件来控制使用这些显示和菜单的权限。用户配置文件是由系统安全管理人员建立和维护的。
经常使用的源语句录入工具是SEU。
2.1.2 批方式输入
可从软盘上用一个批输入流来建立CL源语句。下面给出它的基本部分,它用SBMDKTJOB命令提交给作业队列,输入流使用下面的格式:
// BCHJOB
CRTBNDCL PGM(QGPL/EDUPGM) SRCFILE(PERLIST)
// DATA FILE(PERLIST) FILETYPE(*SRC)
.
. (CL Procedure Source)
.
//
/*
// ENDINP
用这个输入流从联机源语句生成程序。如果要保留这些源语句,用CPYF命令把软盘上的内容复制到数据库文件中,然后用此文件生成程序,也可用外部介质上的CL源语句用IBM支持的设备文件直接生成CL模块。IBM的软盘源文件为QDKTSRC,带文件为QTAPSRC。假定软盘上的源文件名为PGMA,第一步用下面命令标识软盘上源文件的位置:
OVRDKTF FILE(QDKTSRC) LABEL(PGMA)
第二步用CRTCLMOD命令生成模块:
CRTCLMOD MODULE(QGPL/PGMA) SRCFILE(QDKTSRC)
在命令处理时,它把QDKTSRC当做数据库文件源文件。由于第一条命令,它到软盘上去找源文件,PGMA生成在QGPL中,源语句仍留在软盘上。
2.1.3 CL过程的组成部分
做为CL过程,每部分的源语句实际上都是一个CL命令,在典型的CL过程中它可以分成下列几个基本部分:
PGM命令 PGM PARM(&A)
这是一个可选值,表示过程的开始及可接收的参数
说明命令 (DCL,DCLF)
在使用变量时,说明命令要放在除PGM命令之外的所有命令前面
CL处理命令 CHGVAR,SNDPGMIUSR,OVRDBF,DLTF……
用来管理常量及变量
逻辑控制命令 IF,THEN,ELSE,DO,ENDDO,GOTO
用来控制过程内部的处理
内部函数 %SUB,%SWICH,%BIN
用于计算、关系或逻辑表达式
程序控制命令 CALL,RETURN
用来把控制传递给其它程序
过程控制命令 CALLPRC,RETURN
用来把控制传递给其它过程
ENDPGM命令 ENDPGM
这是可选的,表示程序的结束
以上命令使用的顺序、组合及扩展都依据应用程序的逻辑和设计。在生成过程、处理命令时,引用的目标必须已经存在。
在大多数情况下,要成功地运行一个过程,必须有:
一个显示文件:用来给出设备显示信息。如果过程使用显示,在生成过程前必须
先生成显示文件,且在过程中用DCLF说明它,详细内容看本书5.12及数据管理一书。
一个数据库文件:CL过程可以读数据库中的记录。如果用数据库文件,必须在生成过程前生成物理文件或逻辑文件,可用DDS、SQL或IDDU来定义文件中的记录格式。在过程中,要用DCLF命令来说明它。详细内容,请看本书5.2及DB2数据库程序设计一书。
其它程序:如果用CALL命令,那么在CALL运行前,调用的程序必须存在,但在编译使用CALL命令的程序时,它可以不存在。
其它过程:如果用CALLPRC命令,在过程运行时被调用的过程必须存在,但编译时可以不存在。
2.1.4 简单的CL程序例子
一个CL程序可简单、可繁杂,这要看你设计的想法。下面的例子给出在一天开始时操作员正常要做的几件事情(调用程序A、B、C),可用下列编码生成一个名为STARTUP的CL过程:
PGM /* STARTUP */
CALL PGM(A)
CALL PGM(B)
CALL PGM(C)
ENDPGM
在此例中,用程序员菜单生成程序,也可用PDM。下图给出输入、生成及使用程序的步骤:
物理文件成员 0001.00 PGM /*STARTUP*/ 0002.00 CALL PGM(A) 0003.00 CALL PGM(B) 0004.00 CALL PGM(C) 0005.00 ENDPGM
输入CL源语句 |
|
STARTUP |
生成后的目标 程序员菜单选项3 | ||
|
程序员菜单选项4 | |
|
要输入CL源语句:
在程序员菜单选8,在PARM字段写STARTUP,在TYPE字段规定CLLE,按执行键,在SEU显示中,用I行命令进入CL语句。
Columns........: 1 71 Edit QGPL/QCLSRC
Find......: _____________________________________________ STARTUP
FMT A* .....A*. 1 ...+... 2 ...+... 3 ...+... 4 ...+... 5 ...+... 6 ...+... 7
************** Beginning of data ***********************************
.......
.......
.......
.......
.......
.......
在输入完后,用F3键结束SEU,用结束屏的缺省值,按执行键回到程序员菜单,选3(生成目标),不用修改此显示中的任何信息。
注:此时引用的程序(A、B、C)不必须已存在。
在程序生成后,用程序员菜单中的选项4调用它,如果要运行它,则程序A、B、C必须都存在。
2.2 CL过程中使用的命令
一个CL过程仅包括CL命令。它包括IBM支持的及自己定义的命令。某些CL命令,象TFRJOB和SBMJOB都有RQSDTA或CMD参数,它用另外的CL命令做参数值。仅能用在CL过程中的命令不能用做RQSDTA或CMD参数的值。
下表给出在CL过程中经常使用的命令,从中你可选择这方面命令来满足需要。这些命令以功能分组,帮助你理解这章的其余部分内容。图中的(1)指出这些命令仅能用在CL过程中。
系统功能 |
命 令 |
命令功能 |
修改过程控制 |
CALL (Call) |
调用一个程序 |
CALLPRC (Call Procedure) (1) |
调用一个过程 | |
RETURN (Return) |
返回到引起程序或过程运行的下一条命令 | |
CL过程界限 |
PGM (Program) (1) |
指出CL过程源码的开始 |
ENDPGM (End Program) (1) |
指出CL过程源码的结束 | |
CL过程逻辑 |
IF (If) (1) |
根据逻辑表达式的值执行命令 |
ELSE (Else) (1) |
对IF命令为假条件定义采取的动作 | |
DO (Do) (1) |
指出DO组的开始 | |
|
ENDDO (End Do) (1) |
指出DO组的结束 |
GOTO (Go To) (1) |
转移到另外的命令 | |
CL过程变量 |
CHGVAR (Change Variable)(1) |
修改CL变量的值 |
DCL (Declare) (1) |
说明一个变量 | |
替换 |
CHGVAR (Change Variable)(1) |
修改CL变量的值 |
CVTDAT (Convert Date) (1) |
修改日期格式 | |
数据区 |
CHGDTAARA (Change Data Area) |
修改数据区 |
CRTDTAARA (Create Data Area) |
生成一个数据区 | |
DLTDTAARA (Delete Data Area) |
删除一个数据区 | |
DSPDTAARA (Display Data Area) |
显示一个数据区 | |
RTVDTAARA (Retrieve Data Area) |
把数据区的内容复制到一个CL变量中 | |
文件 |
ENDRCV (End Receive) (1) |
取消由前面的RCVF,SNDF或SNDRCVF命令对一个显示文件发出的输入请求 |
DCLF (Declare File) (1) |
说明一个显示文件或数据库文件 | |
RCVF (Receive File) (1) |
从显示文件和数据库文件中读记录 | |
RTVMBRD (Retrieve Member Description) (1) |
取得数据库文件成员的描述 | |
SNDF (Send File) (1) |
往显示文件中写记录 | |
SNDRCVF (Send/Receive File) (1) |
往显示文件中写记录,在用户回答后读记录 | |
WAIT (Wait) (1) |
等待从显示文件发出的SNDF,RCVF或SNDRCVF命令接收数据 | |
信息 |
MONMSG (Monitor Message) (1) |
监控送往程序信息队列的逃逸、状态和通知信息 |
RCVMSG (Receive Message) (1) |
把信息从信息队列复制到一个CL变量中 | |
RMVMSG (Remove Message) (1) |
从信息队列取消信息 | |
RTVMSG (Retrieve Message) (1) |
把预先定义的信息从信息文件复制到CL变量中 | |
SNDPGMMSG (Send Program Message) (1) |
往信息队列发送程序信息 | |
SNDRPY (Send Reply) (1) |
给查询信息的发送者发送回答信息 | |
SNDUSRMSG (Send User Message) |
给显示工作站或系统操作员发送消息或查询信息 | |
混杂命令 |
CHKOBJ (Check Object) |
检查目标是否存在及使用目标必须有的权限 |
PRTCMDUSG (Print Command Usage) |
产生一个用在某组CL过程中的一组命令中的交叉引用表 | |
RTVCFGSRC (Retrieve Configuration Source) |
对生成的已存在的配置目标建立一个CL命令源码且把它放在源文件成员中 | |
RTVCFGSTS (Retrieve Configuration Status) (1) |
从三个配置目标(线路、控制器和设备)中取得配置状态 | |
RTVJOBA (Retrieve Job Attributes) (1) |
取得一个或多个作业属性的值且把它们放到CL变量中 | |
RTVSYSVAL (Retrieve System Value) (1) |
取得系统值并且把它放到一个CL变量中 | |
RTVUSRPRF (Retrieve User Profile) (1) |
取得用户配置文件属性并把它放到CL变量中 | |
程序生成命令 |
CRTCLMOD (Create CL Module) |
生成一个CL模块 |
DLTMOD (Delete Module) |
删除一个模块 | |
DLTPGM (Delete Program) |
删除一个程序 | |
CRTBNDCL (Create Bound Control Language Program) |
生成一个联编的CL程序 | |
CRTPGM (Create Program) |
生成一个程序 | |
CRTSRVPGM (Create Service Program) |
生成一个服务程序 |
2.3 使用CL过程
CL程序设计是一个很灵活的工具,能完成不同的功能。一般的,用CL过程能:
使用变量、逻辑控制、表达式和内部函数来维护和处理CL过程中的数据:
PGM
DCL &C *LGL
DCL &A *DEC VALUE(22)
DCL &B *CHAR VALUE(ABCDE)
.
.
.
CHGVAR &A (&A + 30)
.
.
.
IF (&A < 50) THEN(CHGVAR &C '1')
.
DSPLIB ('Q' || &B)
.
IF (%SST(&B 5 1)=E) THEN(CHGVAR &A 12)
.
.
.
ENDPGM
在过程中使用系统值做变量:
PGM
DCL &TIME *CHAR 6
.
系统值 .
QDATE .
. .
. .
. ENDPGM
在过程中用作业属性做变量:
PGM
DCL &USER *CHAR 10
作业属性 .
Job Name .
Job Number .
. .
. .
. ENDPGM
用显示文件传输数据:
DDS
PGM
Display DCL &OPTION *CHAR 2
.
.
.
RCVF ...
.
.
.
ENDPGM
用CL过程监控作业的错误信息,如需要,采取相应的动作:
PGM
MONMSG MSGID(CPF0001) EXEC(GOTO ERROR)
CALL PROGA
CALL PROGB
RETURN
ERROR: SNDPGMMSG MSG('A CALL command failed') MSGTYPE(*ESCAPE)
ENDPGM
控制过程和程序间的操作且在之间传递参数和替换文件:
PROCA PROCB
DCL ... DCL ...
. .
. .
.
. .
. .
. .
ENDPGM RETURN
ENDPGM
PROCC
DCL ...
.
.
.
OVRDBF FILE(INPUT) TOFILE(&FILEX)
.
.
.
.
.
RETURN
ENDPGM
作为一个控制过程,CL过程也能调用用其它语言写的过程,下面解释如何在程序里的CL过程和RPG IV及ILE COBOL过程中传递控制。要用这个程序,工作站用户要请求PGMA,它控制整个应用程序。
PGMA包括:
一个CL过程(PGMA),调用RPG 过程(PGMB)
RPG过程(PGMB)调用另外一个RPG过程(PGMC)
PGMB调用CL过程(PGMD)
PGMA调用COBOC过程(PGME)
PGME调用CL过程PGMF
PGMA (CL) |
CALL PGMC CALL PGMD RETURN |
END |
CALL PGMB CALL PGME ENDPGM
RETURN |
PGMF (CL) |
PGME (COBOL) |
CALL PGMF EXIT PROGRAM RETURN
这些过程能用下面给出的命令来生成,可把各自的源语句输入到不同的源成员中:
CRTCLMOD PGMA
CRTRPGMOD PGMB
CRTRPGMOD PGMC
CRTCLMOD PGMD
CRTCBLMOD PGME
CRTCLMOD PGMF
CRTPGM PGM(PGMA) +
MODULE(PGMA PGMB PGMC PGMD PGME PGMF) +
ENTMOD(*FIRST)
2.4 处理变量
CL过程由命令组成,命令本身又有命令语句、参数及参数值。参数值可以是变量、常量和表达式。变量是一个命名的可变的值,用它的名字来修改和访问。变量可用在CL命令多数的参数中,当变量做参数值且运行命令时,变量的值做为参数值,命令每次运行时,变量可有不同的值。仅在过程和程序中能用变量及其表达式做参数值。变量不是放在库中,它们不是目标,它们的值在使用它们的程序不被调用时会破坏掉。变量名以&号开始后跟10个以内的字符。&后的第一个字符必须是字母。使用变量让CL程序设计变得更灵活,这是由于目标的高级管理可由特定的应用修改其内容。例如,可写CL程序来指挥其它程序的处理或几个工作站的操作,而不用规定要控制哪个程序或工作站,这些都可做为CL过程中的变量,在过程运行时可以定义变量的值。
所有的变量在使用之前都要说明:
说明变量:用DCL命令定义变量及其属性。属性包括类型、长度和初值。
DCL VAR(&AREA) TYPE(*CHAR) LEN(4) VALUE(BOOK)
说明文件:如果CL程序使用文件,必须在DCLF命令中的FILE参数中规定文件
名,文件由记录格式及字段组成。在编译时,DCLF命令隐含地说明
字段的变量和指示器。
如果文件的DDS中有一个记录,记录中有两个字段(F1和F2),那么在程序中
自动地说明两个变量&F1和&F2。
DCLF FILE(MCGANN/GUIDE)
如果文件不是用DDS生成的物理文件,说明的变量为整个记录所用,变量名与文件同名、长度与文件的记录长相同。
DCL命令必须放在PGM后、其它命令前,多个说明命令可用任何顺序排列。
变量也可做:
在作业和过程间传递信息,请看第三章。
在过程和设备显示间传递信息,请看5.2.7。
条件处理命令,请看2.5。
生成目标,一个变量可用来放目标名或库名,下面的例子中,第一行是用规定
的库生成物理文件,第二行是用变量代替库名。
CRTPF FILE(DSTPRODLB/&FILE)
CRTPF FILE(&LIB/&FILE)
不能用变量修改命令名,键字段用作CALLPRC命令的过程名,但在处理CL过程时可用提示功能来修改命令的参数,请看6.5。也可用QCAPCMD功能把命令的键字和参数汇编在一起来进行处理,有关QCAPCMD的详细内容请看6.1。
2.4.1 说明变量
用最简单的格式 ,DCL命令有下列参数。
*DEC LEN(长度) VALUE(初值)
*LGL
使用DCL命令,要遵守下列规定:
CL变量名要以&号开始后跟10个以内的字符。&后的第一个字符必须是字母,其余可是字母数字。
CL变量的值须是以下之一:
—5000以内个字符串
—最多15个数字,9位小数的压缩十进制值
—逻辑值‘1’或‘0’
如果没有规定初值,则假定下列值为初值:
—十进制变量为0
—字符变量为空格
—逻辑变量为‘0’
对十进制或字符类型,如果规定了初值但没给出长度,缺省长度为与初值等长,对字符类型,长度最长为5000。
用DCL语句说明参数为变量。
2.4.2 用变量规定列表或限定名
参数的值可以是一个列表,例如,CHGLIBL命令中的LIBL参数就需要一个库列表,列表中的各项可以是变量。
CHGLIBL LIBL(&LIB1 &LIB2 &LIB3)
这时,每个变量必须分开用DCL说明:
DCL VAR(&LIB1) TYPE(*CHAR) LEN(10) VALUE(QTEMP)
DCL VAR(&LIB2) TYPE(*CHAR) LEN(10) VALUE(QGPL)
DCL VAR(&LIB3) TYPE(*CHAR) LEN(10) VALUE(DISTLIB)
CHGLIBL LIBL(&LIB1 &LIB2 &LIB3)
变量的各项不能象下面那样规定为字符串的列表:
DCL VAR(&LIBS) TYPE(*CHAR) LEN(20) +
VALUE('QTEMP QGPL DISTLIB')
CHGLIBL LIBL(&LIBS)
这时,系统不把它做为分开各项的列表处理,那么要出错。
也能用变量规定一个限定名,每个限定名要用DCL来分别说明:
DCL VAR(&PGM) TYPE(*CHAR) LEN(10)
DCL VAR(&LIB) TYPE(*CHAR) LEN(10)
CHGVAR VAR(&PGM) VALUE(MYPGM)
CHGVAR VAR(&LIB) VALUE(MYLIB)
.
.
.
DLTPGM PGM(&LIB/&PGM)
ENDPGM
在此例中,程序和库是分别说明的,它俩的名字不能用一个变量说明,象下面那样是错误的:
DCL VAR(&PGM) TYPE(*CHAR) LEN(10)
CHGVAR VAR(&PGM) VALUE('MYLIB/MYPGM')
DLTPGM PGM(&PGM)
这时系统把它做为一个字符串,而不是当作两个目标,如果限定名非要做为一个字符串,要用内部函数%SUB和*TCAT功能分配一个目标或库名来给各自的变量,详细内容请看2.5.8及第九章。
2.4.3 变量中的小写字符
一些保留值,比如*LIBL,做变量时要用大写字符,它们做字符串时,要用引号括起。例如,在命令中要把一个库名做为变量,正确的写法写:
DCL VAR(&LIB) TYPE(*CHAR) LEN(10) VALUE('*LIBL')
DLTPGM (&LIB)/MYPROG;
不正确的写法如:
DCL VAR(&LIB) TYPE(*CHAR) LEN(10) VALUE('*libl')
如果VALUE参数没放在引号内,也是错误的,这是因为没有引号要自动转换或大写,这个错误经常发生在这个参数从显示设备做为输入的字符串传送给过程或程序时,显示设备是用小写来输入的。
2.4.4 代替保留值或数值参数的值的变量
某些CL命令的参数允许数字或预先定义的值。这时,也用字符变量来代表这些参数的值。
每个参数只能接收一定类型的值。参数值可以是整数、字符串、保留值、某类变量以及这些的组合。如果一个参数允许数字值也允许保留值,也能用变量做参数值。如果用保留值做变量,那么这个变量必须说明为类型为*CHAR。例如CHGOUTQ命令的参数JOBSEP的值,可以是数字0—9,也可是予先定义的保留值*SAME。那么可以写下面的CL过程来把JOBSEP定义做字符变量:
DCL &SEP *CHAR LEN(4)
DCL &FILNAM *CHAR LEN(10)
DCL &FILLIB *CHAR LEN(10)
DCLF.....
.
.
.
IF (&SEP *EQ IGNR) GOTO END
ELSE IF (&SEP *EQ NONE) CHGVAR &NRESP '0'
ELSE IF (&SEP *EQ NORM) CHGVAR &NRESP '1'
ELSE IF (&SEP *EQ SAME) CHGVAR &NRESP '*SAME'
CHGOUTQ OUTQ(&FILLIB/&FILNAM) JOBSEP(&NRESP)
GOTO
END: RETURN
ENDPGM
在此例中,工作站用户在显示上输入说明某些输出队列的作业分隔符的数字。变量&NRESP是一个字符变量,放数字和预先定义的值,那么在CHGOUTQ命令中的JOBSEP参数就会认识这些值,程序中显示文件的DDS用VALUES键字来限制只能用IGNR,NONE,NORM或SAME来响应。
如果参数允许数字类型的值(*INT2,*INT4,或*DEC)且用户也不想用保留值(例如*SAME),那么在这个参数中可以使用数字型变量。
实现这个功能的另外一个方法是在CL过程中使用提示。
2.4.5 修改变量的值
可用CHGVAR命令来修改CL变量的值,可以修改为的值是:
修改为一个常量:
CHGVAR VAR(&INVCMPLT) VALUE(0)
或
CHGVAR &INVCMPLT 0
&INVCMPLT变为零。
修改为另一个变量的值:
CHGVAR VAR(&A) VALUE(&B)
或
CHGVAR &A &B
&A 设为变量&B的值。
修改为表达式赋值后的值:
CHGVAR VAR(&A) VALUE(&A + 1)
或
CHGVAR &A (&A + 1)
修改为内部函数%SST的结果值:
CHGVAR VAR(&A) VALUE(%SST(&B 1 5))
&A为&B的前五个字符。
修改为内部函数%SWITCH的结果值:
CHGVAR VAR(&A) VALUE(%SWITCH(0XX111X0))
如果作业开关1和8是0,开关4、5、6是1,则&A为1,否则&A为0。
修改为内部函数%BIN的结果值:
CHGVAR VAR(&A) VALUE(%BIN((%B 1 4))
&B的前4个字符转换成等值的十进数,放在变量&A中。
CHGVAR命令也用于取出或修改本地数据区的内容。例如下列命令把数据区的前10个字节变为空格,且取出数据区的部分内容:
CHGVAR %SST(*LDA 1 10) ' '
CHGVAR &A %SST(*LDA 1 10)
对一个逻辑变量必须修改成逻辑值,对十进制变量,可以用十进制或字符,对字符变量,可以接收字符或十进制值。
在对字符变量规定十进制值时,注意下面几点:
字符变量要右对齐,必要的话用前置零填充。
字符变量要足够长以放下小数点或符号。
减号放在值的最左边。
例如,&A是字符变量,要修改为十进制变量&B,&A为6位长,&B为5位长2位小数,&B的值为123,则&A的结果值为123.00。
在对十进制变量规定字符值时,注意以下几点:
小数点是由在字符值中的小数点位置定的。如果字符值不包括小数点,则小数点放在值的最右位置。
字符值可在值的最左边有十、一号,符号和字符间不能有空格。如果字符值无符号,则认定为正值。
如果字符值在小数点后的字符多于十进变量能放下的值,字符要被截断,但如果超出的字符值是在小数点左边,则不发生截断但出错。例如:&C是十进变量要修改为字符变量D的值,&C是5位长2位小数,&D是10位长,现在值为123.1bbbb(这里b是空格),则&C的结果值是123.10。
2.4.6 命令参数末尾的空白
有些命令定义为VARY(*YES)值,它把值的长度传输成引号内的字符数,当CL变量规定用这种方法定义的参数值时,系统在确定要传送的变量长度前要去掉尾部的空白。如果此空白对参数来讲是有意义的,那么必须用特别的方法来保证传送的长度中包括它们,大多数命令的参数在定义和使用时都不会发生此类情况。类似的情况在OVRDBF中的POSTION参数中的键值元素也会发生,当发生这种情况时,要构造一个命令串来把参数值放在引号中,然后把串传给QCMDEXC或QCAPCMD来处理,就能得到预期的结果。
下面是一个用来运行OVRDBF命令的程序,尾部的空白能做为键值的一部分,同样的技术也可用于用VARY(*YES)定义参数的命令,末尾空白必须传送给参数。
PGM PARM(&KEYVAL &LEN)
/* 此程序介绍如何规定一个有结尾空格的键值来做为CL程序中的OVRDBF */
/* 命令中位置参数的一部分。 */
/* 这个键值的元素是用VARY(*YES)参数定义的,在ELEM命令定义语句中 */
/* 此参数的说明规定了,用这种方法定义的参数是作为CL变量规定的, */
/* 它的长度作为去掉结尾空格的变量传送。 */
/* 用单引号限制此键值长度的调用,QCMDEXC可作为这个动作的循环。 */
/* 参数-- */
DCL VAR(&KEYVAL) TYPE(*CHAR) LEN(32) /* 请求键的
的值,定义为32个字符 */
DCL VAR(&LEN) TYPE(*DEC) LEN(15 5) /* 用此键值的
长度1—32的任何值都可用 */
/* 替换命令的结果串传给QCMDEXC(写两个引号才能得到一个引号的结果) */
DCL VAR(&STRING) TYPE(*CHAR) LEN(100) +
VALUE('OVRDBF FILE(X3) POSITION(*KEY 1 FMT1 '' ')
/* 位置标记 123456789 123456789 123456789 123456789 */
DCL VAR(&END) TYPE(*DEC) LEN(15 5) /* 计算 &STRING
键的结尾的变量 */
CHGVAR VAR(%SST(&STRING 40 &LEN)) VALUE(&KEYVAL) /*把
键值送在命令串中,让紧接引号后的QCMDEXC使用*/
CHGVAR VAR(&END) VALUE(&LEN + 40) /* 定位在键值的最
后一个字符之后 */
CHGVAR VAR(%SST(&STRING &END 2)) VALUE('')') /* 把结
束引号和括号放在结尾参数上 */
CALL PGM(QCMDEXC) PARM(&STRING 100) /* 调用处理命
令过程 */
ENDPGM
注:如果用VARY(*YES)和RTNVAL(*YES)且传送CL变量,传送的是变量的长度而不是CL变量中数据的长度。
2.4.7 在CL过程中写注释
如果是在CL过程中写注释或对命令加注释,使用字符对 /* 和 */。注释放在这对符号中间。
注释的起始界限符/*,要三个字符,除非/*出现在命令串的头二个位置。此时,在命令前/*后不用跟空格。
可用下列方法之一写三个字符的注释起始界限(b为空格):
/*b
b/*
/**
这样,注释起始界限符可由四种方法输入:
从命令串的第一个位置开始
前加一空格
后跟一空格
后跟一个星号(/**)
在下面的过程中,写一个注释来提示用户可以响应的菜单选项:
PGM /* ORD040C Order dept general menu */
DCLF FILE(ORD040CD)
START: SNDRCVF RCDFMT(MENU)
IF (&RESP=1) THEN(CALL CUS210)
/*Customer inquiry */
ELSE +
IF (&RESP=2) THEN(CALL ITM210)
/**Item inquiry */
ELSE +
IF (&RESP=3) THEN(CALL CUS210)
/* Customer name search */
ELSE +
IF (&RESP=4) THEN(CALL ORD215)
/** Orders by cust */
ELSE +
IF (&RESP=5) THEN(CALL ORD220)
/* Existing order */
ELSE +
IF (&RESP=6) THEN(CALL ORD410C)
/** Order entry */
ELSE +
IF (&RESP=7) THEN(RETURN)
GOTO START
ENDPGM
2.5 CL过程中的控制处理
CL过程中的命令是按顺序处理的,处理完一个再处理后面的一个。可用修改逻辑流程的命令来改变这个处理的顺序,这些命令可以是条件的(IF)或无条件的(GOTO)
无条件转移意思是不管转移指令发生时存在什么样的条件,过程执行都转移到过程中的某个命令或一组命令中,这时要用GOTO命令。
条件转移是在一定的条件下,处理要转移到过程内非连续的段或命令中,可转移到过程中的任何语句。因为转移发生在某个条件为真时,所以叫做条件处理。这时经常用IF命令,如果条件不为真,可用ELSE命令规定替代的处理。DO命令生成一组一起处理的命令,即在某种条件下,执行一组命令。
2.5.1 使用GOTO命令及标号
GOTO命令处理无条件转移,无论什么时候遇到GOTO命令,都要直接执行过程中的另一部分(用一个标号指出),这种转移不依赖表达式的求值。转移到标号指定的语句后,从这个语句开始,继续顺序处理。
它不返回到GOTO命令处,除非由另外指令特别指定返回。可以往前或向后转移,不能用GOTO语句走到过程外的标号处。GOTO命令没有参数,它只包含要转移语句的标号:
GOTO CMDLBL(标号)
标号标识过程中的语句,指出GOTO命令执行的方向,要使用GOTO语句,要转移的去处必须有一个标号:
PGM
.
.
.
START: SNDRCVF RCDFMT(MENU)
IF (&RESP=1) THEN(CALL CUS210)
.
.
.
GOTO START
.
.
.
ENDPGM
此例中,标号是START,标号最多10个字符,结尾必须有冒号,命令名和标号之间有空格。
2.5.2 使用IF命令
IF命令用来设置一个条件。如果为真,要规定在过程中运行的语句或一组语句,与IF一起可以规定ELSE命令,来规定条件不为真时执行的语句。
命令包括一个表达式(它用来测试真、假)和一个THEN参数,它现定表达式为真时要采取的动作,IF命令的格式为:
IF COND(逻辑表达式) THEN(CL命令)
逻辑表达式可以是一个逻辑变量或常量,或多个操作之间的关系,然后计算表达式是真还是假,详细内容请看2.5.6。
如果逻辑表达式的条件为真,处理THEN参数中的命令,它可以是一个命令,也可以是一组命令,如果条件不为真,运行下一个顺序的命令。COND和THEN 都是命令的键字,它们可以省略。下面是正确的写法:
IF COND(&RESP=1) THEN(CALL CUS210)
IF (&A *EQ &B) THEN(GOTO LABEL)
IF (&A=&B) GOTO LABEL
在IF和键字或值(&A)之间要有空格,在键字之间不允许有空格,如果有,用左边的括号括住值。
下面是IF的例子,假定在编码前,&A的值为2,&4的值为4。
IF (&A=2) THEN(GOTO FINAL)
IF (&A=3) THEN(CHGVAR &C 5)
.
.
.
FINAL: IF (&C=5) CALL PROGA
ENDPGM
在这种情况下,处理第一个IF之后转 到FINAL处,跳过中间的语句,不返回第二个IF命令处,在FINAL中,由于要测试&C是否为5,此时为假,则不调用PROGA,而执行ENDPGM。它指出过程结束,控制返回给调用它的过程。
如果变量的初值不同,则用同样的编码,处理逻辑会不同,比如开始&A为3,&C为4,则第一个IF为假,则不执行GOTO,而顺序执行第二个IF。测试为真,则把&C改为5。继续执行后续语句,当做到最后一个IF,此时&C=5,为真,则调用PROGA。
几个相邻IF语句是独立运行的,例如:
PGM /* IFFY */
DCL &A..
DCL &B..
DCL &C..
DCL &D..
DCL &AREA *CHAR LEN(5) VALUE(YESNO)
DCL &RESP..
IF (&A=&B) THEN(GOTO END) /* IF #1 */
IF (&C=&D) THEN(CALL PGMA) /* IF #2 */
IF (&RESP=1) THEN(CHGVAR &C 2) /* IF #3 */
IF (%SUBSTRING(&AREA 1 3) *EQ YES) THEN(CALL PGMB) /* IF #4 */
CHGVAR &B &C
.
.
.
END: ENDPGM
在上例中,如果&A¹&B,运行下面语句,假如&C=&D,则调用PGMA,当PGMA返回时,考虑第三个IF,等等。这些顺序的IF语句之间逻辑和处理都是不同的,有ELSE语句和嵌套的IF,详细内容请看2.5.4。
一个嵌套的命令是完全包括在另一个命令的参数中,在下例中,CHGVAR和DO命令是嵌套的:
IF (&A *EQ &B) THEN(CHGVAR &A (&A+1))
IF (&B *EQ &C) THEN(DO)
.
.
.
ENDDO
2.5.3 使用DO命令和DO组
DO命令处理一组命令,这一组命令是用DO和ENDDO之间的命令组成的,这组命令的处理通常是以有关命令为条件的。它经常与IF、ELSE或MONMSG一起使用。例如,
IF (&A=&B)THEN(DO)
DO 组
ENDDO
ENDPGM
如果逻辑表达式(&A=&B)为真,则执行DO组,如果不为真,则跳过DO组,处理ENDDO后的命令。
在下例中,如果&A¹&B,则调用PGMB,不调用PGMA,否则执行DO组中的命令。
CALL PGMA
CHGVAR &A &B DO 组
SNDPGMMSG
ENDDO
CALL PGMB
CHGVAR &ACCTS &B
DO组也能嵌入到另外的DO组中,最多可嵌套10层,下例中有三层,注意每个DO组都由ENDDO结束。
PGM
IF (&A=&B)DO
IF (&A=5) DO
CALL PGMB
IF (&AREA=YES) DO
第三层 CHGVAR &P (&P+2)
ENDDO
CALL ACCTSPAY
ENDDO
ENDDO
CALL PGMC
ENDPGM
此例中,如果第一层的&A¹5,调用PGMC,如果&A=5,则执行第三个DO组中的语句,如果第二个DO组中的&AREA¹YES,则调用ACCTSPAY,这是国为处理转到DO组后的下一个命令。
CL编译程序不指出DO组的起始和结束,如果编译程序发现不成对的条件,它不是很容易指出实际的错误,怎样指出DO组的层次错误请看QUSRTOOL的DSPCLPDO中的例子。
2.5.4 使用ELSE命令
ELSE命令在IF命令的条件为假时规定所做的处理。
IF命令也能不用ELSE:
IF (&A=&B) THEN(CALLPRC PROCA)
CALLPRC PROCB
在这种情况下,仅在&A=&B时调用PROCA,而总是调用PROCB。
如果要在这个过程中用ELSE命令,处理逻辑要修改,在下例中,如果&A=&B,则调用PROCA,但不调用PROCB,如果&A¹&B,则调用PROCB。
IF (&A=&B) THEN(CALLPRC PROCA)
ELSE CMD(CALLPRC PROCB)
CHGVAR &C 8
如果IF表达式有一个不为真条件导致明显的转移(即绝对的非此即彼转移),则必须用ELSE命令。
实际上ELSE非常有用体现在与DO组一起使用上。在下例中,依据IF表达式的条件,可能不运行DO组,但总要运行其余的命令。
仅当条件为真时运行
ENDDO
SAVOBJ
CALL PGM(PAYROLL) 不管条件是否为真,无条件运行
ENDPGM
如果IF的表达式不为真,用ELSE命令可以规定仅执一个命令或一组命令,则整个逻辑都改变了:
仅在条件为真时做
ENDDO
仅在条件为假时做
ENDDO
SAVOBJ… 无条件做
CALL PGM(PAYROLL)
每个ELSE前必须有一个与之相关的IF命令,如果IF有嵌套,那么每个IF的ELSE 都要与之匹配。
IF ... THEN ...
IF ...THEN(DO)
IF ...THEN(DO)
.
.
.
ENDDO
ELSE DO
IF ...THEN(DO)
.
.
.
ENDDO
ELSE DO
.
.
.
ENDDO
ENDDO
ELSE IF ... THEN ...
IF ... THEN ...
IF ... THEN ...
要查过程中的ELSE 是否匹配,要从最里面的一组查起。
ELSE也能用来检查一系列互相冲突的选项。在下例中,在成功地检查完第一个IF后,执行嵌套的命令和RCLRSC命令:
IF COND(&OPTION=1) THEN(CALLPRC PRC(ADDREC))
ELSE CMD(IF COND(&OPTION=2) THEN(CALLPRC PRC(DSPFILE)))
ELSE CMD(IF COND(&OPTION=3) THEN(CALLPRC PRC(PRINTFILE)))
ELSE CMD(IF COND(&OPTION=4) THEN(CALLPRC PRC(DUMP)))
RCLRSC
RETURN
2.5.5 使用嵌套的IF命令
一个IF命令可以嵌入到另一个IF中,当在条件为真时,执行的是自身的另外一个IF命令(要用THEN参数)
IF (&A=&B) THEN(IF (&C=&D) THEN(GOTO END))
GOTO START
在执行某个或某组命令前必须满足几个条件时,这种嵌套是很有用的。在前面的例子中,如果第一个表达式为真,系统读第一个THEN参数,假如&C=&D,表达式为真,系统执行第二个THEN的命令,即GOTO END。二个表达式都为真,才能执行GOTO END。如果有一个不为真,则执行GOTO START。注意表达式和命令的括号。
在CL程序设计中可允许10层嵌套。
嵌套层越多,逻辑就变得越复杂。可以用自由格式使嵌套看起来更清楚:
PGM
DCL &A *DEC 1
DCL &B *CHAR 2
DCL &RESP *DEC 1
IF (&RESP=1) +
IF (&A=5) +
IF (&B=NO) THEN(DO)
.
.
.
ENDDO
CHGVAR &A VALUE(8)
CALL PGM(DAILY)
ENDPGM
最前边的IF做一个嵌套命令处理,其中如有一个为假,则处理转移到嵌套外的语句(CHGVAR及后续的命令),只有所有的为真,才执行DO组,这就比在一个命令中用*AND组合几个表达式要容易。
在某些情况下,有些转移必须是在条件为假时才发生,这时可以对每个嵌套的IF加一个ELSE语句:
PGM
DCL &A ...
DCL &B ...
DCL &RESP ...
IF (&RESP=1) +
IF (&A=5) +
IF (&B=NO) THEN(DO)
.
.
.
SNDPGMMSG ...
.
.
.
ENDDO
ELSE CALLPRC PROCA
ELSE CALLPRC PROCB
CHGVAR &A 8
CALLPRC PROC(DAILY)
ENDPGM
如果所有条件为真,则执行SNDPGMMSG,以及后边的CHGVAR,如果第一、第二个条件为真,第三个条件为假,则调用PROCA。当从PROCA返回时,执行CHGVAR。如果第二个条件为假,调用PROCB,接着执行CHGVAR,最后,如果&RESP¹1,立即执行CHGVAR命令。ELSE为每个检查提供不同的转移路径。下面三个例子与前例中嵌套的IF是等价的:
IF (&RESP=1) THEN(IF (&A=5) THEN(IF (&B=NO) THEN(DO)))
IF (&RESP=1) THEN +
(IF (&A=5) THEN +
(IF (&B=NO) THEN(DO)))
IF (&RESP=1) +
(IF (&A=5) +
(IF (&B=NO) THEN(DO)))
2.5.6 使用*AND、*OR、和*NOT操作
*AND和*OR是逻辑操作的保留值,用来表示逻辑表达式操作之间的关系。*AND可以用&表示,*OR可以用¦表示,后边必须有一空格。一个逻辑表达式的操作用关系表达式或逻辑变量或常量用逻辑操作组合在一起。*AND表示两个操作都为真时才产生真的结果,*OR表示操作有一个为真就产生真的结果。其它非逻辑操作符,也可用在表达式中指出操作数之间的逻辑关系或表达式操作要完成的动作,除逻辑操作符外,有三类操作符:
_ 算术 (+, -, *, /)
_ 字符 (*CAT, ||, *BCAT, |>, *TCAT, |<)
_ 关系 (*EQ, =, *GT, >, *LT, <, *GE, >=, *LE, <=, *NE, ?=, *NG,
?>, *NL, ?<)
下面是逻辑表达式的例子:
((&C *LT 1) *AND (&TIME *GT 1430))
(&C *LT 1 *AND &TIME *GT 1430)
((&C < 1) & (&TIME>1430))
((&C< 1) & (&TIME>1430))
逻辑表达式由三部分组成:两个操作数和一个操作符,它是由操作符的类型(*AND和*OR)来表示表达式是逻辑型的而不是由操作数类型确定。逻辑表达式中的操作数可以是逻辑变量或其它表达式。例如:
((&C *LT 1) *AND (&TIME *GT 1430))
整个逻辑关系放在括号中,操作数都是关系表达式,也分别由括号括起。从逻辑表达式中的第二个例子看出,操作数不非得放在括号中,但放在括号中就比较清晰,不用括号是因为*AND和*OR有不同的优先级。即先做*AND,后做*OR。对同一优先级的操作,要用括号控制操作顺序。
在命令中可以用关系表达式做条件:
IF (&A=&B) THEN(DO)
.
.
.
ENDDO
关系表达式中操作数可以是常量。
如果要规定多个条件,可以用逻辑表达式与关系表达式一起做操作数:
IF ((&A=&B) *AND (&C=&D)) THEN(DO)
.
.
.
ENDDO
2.5.5中的例子也可如下编码:
PGM
DCL &RESP *DEC 1
DCL &A *DEC 1
DCL &B *CHAR 2
IF ((&RESP=1) *AND (&A=5) *AND (&B=NO)) THEN(DO)
.
.
.
ENDDO
CHGVAR &A VALUE(8)
CALLPRC PROC(DAILY)
ENDPGM
这时在关系表达式中多次使用逻辑操作。
由于逻辑表达式中也可用另外的逻辑表达式做操作数。那么可以形成复杂的逻辑关系:
IF (((&A=&B) *OR (&A=&C)) *AND ((&C=1) *OR (&D='0'))) THEN(DO)
此时,&D是逻辑变量。
任何逻辑或关系表达式的结果都为‘0’或‘1’。如果整个表达式结果为1,则执行相关的命令,下面的命令解释了这种情况:
IF ((&A = &B) *AND (&C = &D)) THEN(DO)
((true'1') *AND (not true'0'))
(not true '0')
表达式最后结果为0,这样,不执行DO。
同样也可用逻辑变量来计算表达式:
PGM
DCL &A *LGL
DCL &B *LGL
IF (&A *OR &B) THEN(CALL PGM(PGMA))
.
.
.
ENDPGM
这时要计算条件表达式的值,看&A或&B是否有一个为1。如果是这样,整个表达式为真,则调用PGMA。
在逻辑变量或常量间用*OR时,使用下列的真值表:
If &A is: '0' '0' '1' '1'
and &B is: '0' '1' '0' '1'
the OR expression is: '0' '1' '1' '1'
简单地说,对多个OR 操作,如果所有值都为假,则结果为假,有一个为真则结果为真。
PGM
DCL &A *LGL VALUE('0')
DCL &B *LGL VALUE('1')
DCL &C *LGL VALUE('1')
IF (&A *OR &B *OR &C) THEN(CALL PGMA)
.
.
.
ENDPGM
此时值不全为假,则结果为真,调用PGMA。
在用*AND和逻辑变量计算逻辑表达式时,用下面的真值表:
如果 &A 是: '0' '0' '1' '1'
且 &B 是: '0' '1' '0' '1'
AND的结果是: '0' '0' '0' '1'
对多个*AND操作,有一个值为假则结果为假,全为真时结果为真。
PGM
DCL &A *LGL VALUE('0')
DCL &B *LGL VALUE('1')
DCL &C *LGL VALUE('1')
IF (&A *AND &B *AND &C) THEN(CALL PGMA)
.
.
.
ENDPGM
此时值不全为真,则结果为假,不调用PGMA。
在前面的这些例子中,在操作数代表一个逻辑值时,才能在表达式中使用逻辑操作数,非逻辑型变量用OR或AND是不对的,例如:
PGM
DCL &A *CHAR 3
DCL &B *CHAR 3
DCL &C *CHAR 3
不正确的是:
IF (&A *OR &B *OR &C = YES) THEN...
正确的是:
IF ((&A=YES) *OR (&B=YES) *OR (&C=YES)) THEN...
这时,在关系操作符之间是OR。
逻辑操作符*NOT用来取逻辑变量或常量的非。*NOT要在*AND或*OR前计算,*NOT后的操作要在任何操作数间逻辑关系前进行。
PGM
DCL &A *LGL '1'
DCL &B *LGL '0'
IF (&A *AND *NOT &B) THEN(CALL PGMA)
在此例中,所有值都为真,表达式才为真,即调用PGMA。
PGM
DCL &A *LGL
DCL &B *CHAR 3 VALUE('ABC')
DCL &C *CHAR 3 VALUE('XYZ')
CHGVAR &A VALUE(&B *EQ &C)
IF (&A) THEN(CALLPRC PROCA)
在这个例子中,值不为真,不调用PROCA。
2.5.7 使用%BINARY内部函数
二进制内部函数(%BIN)把规定的CL字符变量的内容解释为一个二进整数。从规定的位置开始继续2个或4个字符,这个函数的语法为:
%BIN(字符变量名 起始位 长度)
或%BINARY(字符变量名 起始位 长度)
起始位和长度是可选的,如果没规定,起始位为1,长度为用的字符变量长,字符变量的长必须是2或4。
如果规定了起始位,也要规定长度为2或4,起始位必须是大于或等于1的正数。如果起始位和长度的和大于变量的长度,则出错。(起始位也可用CL十进制变量)。
可与二进制函数一起使用IF和CHGVAR命令。可做算数或逻辑表达式;也能在参数为数字型的命令中使用它。
在IF命令中的COND参数用%BIN,或 CHGVAR的VALUE用%BIN时,字符变量的内容解释为二——十进制转换。
在CHGVAR中的VAR参数中使用%BIN时,VALUE中的十进值转换或二字节或四字节的二进整数,结果放在起始位置规定的字符变量中,小数位被截断。
用在CALLPRC命令的RTNVAL参数中的%BIN指出调用一个过程并希望返回一个二进整数。2字节字符变量能装下从-32768—32767的二进整数。4字节字符变量能装下从-2147483648—2147483647的二进整数。