第十章 Caché 函数大全 $COMPILE 函数
编译源代码,生成可执行的目标代码。
大纲
$COMPILE(source,language,errors,object)
$COMPILE(source,language,errors,object,,,rname)
参数
- source 指定包含要编译的源代码的下标数组的局部或全局变量。
- language 指定源代码编程语言的整数标志。0=ObjectScript。
- errors 接收编译期间发生的任何错误的无下标局部变量。此变量是一个列表结构,每个报告的错误对应一个元素。每个错误本身都是一个列表结构,指定错误位置和类型(见下文)。
- object 第一个语法-一个无下标的局部或全局变量,用于生成用于保存编译后的目标代码的数组。第二个语法-此参数是可选的。如果指定,对象将清除其先前值,但不会设置。通常,第二种语法省略宾语并指定占位符逗号。
- rname 第二个语法-指定用于在
^rOBJ
全局变量数据库中存储编译的目标代码的例程名称的字符串。
描述
$COMPILE
编译源代码并生成OBJ(对象)代码(例程的可执行形式)。$COMPILE
报告编译错误,可用于检查源代码是否存在编译错误,而无需实际生成目标代码。$COMPILE
接受INT代码,而不是MAC代码作为输入。因此,在编译之前,源代码中的任何宏都必须由预处理器(如ObjectScript宏预处理器)解析。
注意:Caché提供了几个强大的源代码编译工具,Studio就是其中之一。通常,源代码编译是使用这些工具执行的,而不是这个$COMPILE
函数。
$COMPILE
有两种语法形式:
- 第一个
$COMPILE
语法形式返回对象数组中的对象代码。它首先终止对象变量。编译之后,对象数组被设置为编译后的目标代码的大小。
对象数组包含的目标代码格式与^rOBJ
全局变量格式相同。可以使用命令MERGE ^rOBJ(rname)=object(1)
将^rOBJ
中的目标代码替换为新的目标代码。但是,设置多个节点时,MERGE
命令不是原子的,因此,如果另一个进程同时加载同一例程,则此操作可能会导致不可预测的结果。
如果省略object参数,则将编译源代码并检查是否有错误,但不会创建任何目标代码。
- 第二种
$COMPILE
语法形式将目标代码直接返回到名为rname
的例程中。可以通过返回^rOBJ(rname)
来查看此OBJ代码。$COMPILE
操作在内部锁定^rOBJ(rname)
,从而防止任何其他进程在完全存储新的目标代码之前加载例程目标代码。
如果省略rname
参数,则将编译源代码并检查是否有错误,但不会创建目标代码。
通常,以这种语法形式省略对象参数(第4个参数)。如果指定对象参数,则将杀死对象变量,但未设置。其他省略的参数(以占位符逗号表示)仅供内部使用,不应指定。
$COMPILE
返回如下的整数代码:0 =未检测到错误,并且创建了目标代码。1 =检测到错误并创建了目标代码。-1 =检测到错误,并且没有创建目标代码。当省略保存目标代码的参数(第一语法:object;第二语法rname)时,将返回相同的返回代码。
当ObjectScript编译器检测到错误时,它将在该点创建目标代码,该目标代码在执行该行时将引发错误。当Basic编译器检测到错误时,它们不会产生任何目标代码。
编译类方法
Caché提供了用于编译一个或多个类的类方法。这些方法提供了许多限定符和标志,以更精确地指定编译选项:
-
$SYSTEM.OBJ.Compile()
编译指定的类和该类中的所有例程。
DHC-APP>d $SYSTEM.OBJ.Compile("PHA.TEST.Function")
编译于07/31/2020 14:42:01开始,包含限定符''
编译类 PHA.TEST.Function
正在编译例程 PHA.TEST.Function.1
0.011s中已成功完成编译.
-
$SYSTEM.OBJ.CompileList()
编译指定类和这些类中所有例程的列表。 -
$SYSTEM.OBJ.CompilePackage()
编译指定包(模式)中的所有类/例程。
DHC-APP>w $SYSTEM.OBJ.CompilePackage("PHA.TEST")
编译于07/31/2020 14:41:25开始,包含限定符''
,正在编辑5类,正在使用5工作者作业
编译类 PHA.TEST.Arithmetic
编译类 PHA.TEST.Function
编译类 PHA.TEST.Command
编译类 PHA.TEST.ObjectScript
编译类 PHA.TEST.ObjectScriptTwo
正在编译例程 PHA.TEST.ObjectScriptTwo.1
正在编译例程 PHA.TEST.Command.1
正在编译例程 PHA.TEST.ObjectScript.1
正在编译例程 PHA.TEST.Arithmetic.1
正在编译例程 PHA.TEST.Function.1
0.296s中已成功完成编译.
1
-
$SYSTEM.OBJ.CompileAll()
编译当前名称空间中的所有类/例程。 -
$SYSTEM.OBJ.CompileAllNamespaces()
编译所有名称空间中的所有类/例程。
参数
source
包含要编译的源代码的数组。源的格式可以是ObjectScript的int
例程、CachéBasic的BAS例程或CachéMVBasic的MVI例程。数组元素source(0)
必须包含源代码的行数,每个source(N)
包含源代码的第n行。源行必须从1到n连续编号,不能遗漏行。可执行的ObjectScript代码必须缩进。例如:
/// d ##class(PHA.TEST.Function).compiles()
ClassMethod compiles()
{
SET mysrc(0)=6
SET mysrc(1)=" SET x=1"
SET mysrc(2)="Main" // a label
SET mysrc(3)=" WRITE ""x is:"",x,!"
SET mysrc(4)=" SET x=x+1"
SET mysrc(5)=" IF x=4 {WRITE ""x is:"",x,"" all done"" QUIT}"
SET mysrc(6)=" GOTO Main"
SET rtn=$COMPILE(mysrc,0,errs,,,,"myobj")
IF rtn=0 {
WRITE "成功生成OBJ代码",!
} ELSE {
WRITE "未生成OBJ代码返回代码: ",rtn,! QUIT
}
WRITE "运行代码",!!
DO ^myobj
}
DHC-APP>d ##class(PHA.TEST.Function).compiles()
成功生成OBJ代码
运行代码
x is:1
x is:2
x is:3
x is:4 all done
Source参数可以是无下标的局部变量名,也可以是可能有下标的全局变量名称。
如果未定义source(0)
,则无论%SYSTEM.Process.Unfined()
方法设置如何,系统都会生成
错误。
如果source(0)
值大于源代码行数,或者缺少连续的源代码行,则系统会生成
错误,后跟缺少的源代码行的名称。可以通过设置%SYSTEM.Process.Unfined()
方法来更改此行为。以下示例中显示了这些类型的错误:
/// d ##class(PHA.TEST.Function).compiles1()
ClassMethod compiles1()
{
SET src(0)=4,src(1)="TestA ",src(2)=" WRITE 123",src(3)=" WRITE 456,!"
SET stat=$COMPILE(src,0,errs,TestA) /* generates *src(4) */
}
DHC-APP>d ##class(PHA.TEST.Function).compiles1()
SET stat=$COMPILE(src,0,errs,TestA) /* generates *src(4) */ }
^
zcompiles1+2^PHA.TEST.Function.1 *src(4)
DHC-APP 2d1>
/// d ##class(PHA.TEST.Function).compiles2()
ClassMethod compiles2()
{
SET src(0)=4,src(1)="TestA ",src(3)=" WRITE 123",src(4)=" WRITE 456,!"
SET stat=$COMPILE(src,0,errs,TestA) /* generates *src(2) */
}
DHC-APP>d ##class(PHA.TEST.Function).compiles2()
SET stat=$COMPILE(src,0,errs,TestA) /* generates *src(2) */ }
^
zcompiles2+2^PHA.TEST.Function.1 *src(2)
/// d ##class(PHA.TEST.Function).compiles3()
ClassMethod compiles3()
{
SET src(0)=3,src(1)="TestA ",src(3)=" WRITE 123",src(4)=" WRITE 456,!"
SET stat=$COMPILE(src,0,errs,TestA) /* generates *src(2) */
}
DHC-APP>d ##class(PHA.TEST.Function).compiles3()
SET stat=$COMPILE(src,0,errs,TestA) /* generates *src(2) */ }
^
zcompiles3+2^PHA.TEST.Function.1 *src(2)
language
指定要编译的源的类型的语言模式。常见值包括:
- 0 = ObjectScript
- 9 = Caché Basic
- 11 = Caché MVBasic
其他值指定旧版ObjectScript模式,应仅在咨询系统间支持人员后才能使用。
errors
设置为编译器检测到的任何错误的无下标局部变量。任何现有值都将被取消。如果未检测到错误,则将变量设置为空字符串(“”
)。如果检测到错误,则将ERROR
变量设置为$LIST
结构,每个错误对应一个元素。每个错误本身都是$LISTBUILD(line,offset,errnum,text)
格式 $LIST
结构,其中:
- line =检测到错误的行号
- offset =错误源行中的偏移量
- errnum =错误类型的错误号
- text =描述错误的文字
Basic编译器将另外两个元素添加到列表中,文字描述错误的位置以及源代码行本身。
object
接收编译器目标代码输出的数组。对象参数可以是未下标的局部变量名称,也可以是可能下标的全局名称。上面描述了对象数组的内容。
rname
例程名称,用于指定目标代码在^rOBJ 下的全局变量中应保存的位置。 $COMPILE
在保存新的目标代码之前会杀 ^rOBJ(rname)
的所有现有内容。在以下示例中, rname=”myobj”
:
要查看OBJ
代码:
DHC-APP>WRITE ^rOBJ("myobj")
=!�m??#_
J?? �???? ?????? H?? ?? �A?�???? ??�??????�??p all done�x is:�Mainxó??�???@@@@@@@@@@@@@@@@@@@@@@@@@@@@HHHHf??ù??
DHC-APP> ZWRITE ^rOBJ("myobj")
^rOBJ("myobj")=$c(12)_"=!"_$c(22)_"m??#_"_$c(1,0,10,8,0)_" J"_$c(0,149)_" "_$c(17)_"??"_$c(138)_" ????"_$c(137)_" H"_$c(152)_" "_$c(129)_" "_$c(4)_"A??"_$c(8,17)_"??"_$c(138)_" ??"_$c(18)_"??????"_$c(18,0)_"??"_$c(0,0,0)_"p"_$c(9)_" all done"_$c(5)_"x is:"_$c(4)_"Main"_$c(1)_"x"_$c(2,0)_"ó??é??"_$c(0,0,23,0,6,0,2,0,2,0,8,0,13,0,27,0,30,0,1,0)_"????"_$c(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)_"@"_$c(0)_"@"_$c(0)_"@"_$c(0)_"@"_$c(0)_"@"_$c(0)_"@"_$c(0)_"@"_$c(0)_"@"_$c(0)_"@"_$c(0)_"@"_$c(0)_"@"_$c(0)_"@"_$c(0)_"@"_$c(0)_"@"_$c(0)_"@"_$c(0)_"@"_$c(0)_"@"_$c(0)_"@"_$c(0)_"@"_$c(0)_"@"_$c(0)_"@"_$c(0)_"@"_$c(0)_"@"_$c(0)_"@"_$c(0)_"@"_$c(0)_"@"_$c(0)_"@"_$c(0)_"@"_$c(0)_"H"_$c(0)_"H"_$c(0)_"H"_$c(0)_"H"_$c(0)_"f??ù??"_$c(2,0,0,0)
要执行OBJ代码:
DHC-APP>DO ^myobj
x is:1
x is:2
x is:3
x is:4 all done
列出创建时间戳记和OBJ代码的长度:
DHC-APP>ZWRITE ^rINDEX("myobj")
^rINDEX("myobj","OBJ")=$lb("2020-07-31 14:47:09",196)
请注意,^rINDEX()
仅列出OBJ代码行,因为$COMPILE
创建的代码没有相应的存储的MAC或INT代码版本。
中断编译
可以发出Ctrl-C或调用^RESJOB
实用程序来中断正在进行的编译。所有语言模式均支持这些编译中断。
示例
下面的示例使用第一种$COMPILE
格式编译四行ObjectScript程序:
/// d ##class(PHA.TEST.Function).compiles4()
ClassMethod compiles4()
{
SourceCode
SET src(0)=4
SET src(1)="TestA "
SET src(2)=" WRITE ""Hello "" "
SET src(3)=" WRITE ""World"",!"
SET src(4)=" QUIT"
CompileSource
SET stat=$COMPILE(src,0,errs,TestA)
IF stat=0 {
WRITE "编译成功"
} ELSE {
WRITE "状态",stat,!
WRITE "编译错误长度=",$LISTLENGTH(errs)
}
}
DHC-APP>d ##class(PHA.TEST.Function).compiles4()
编译成功
下面的示例使用第二种$COMPILE
格式编译相同的四行ObjectScript程序:
/// d ##class(PHA.TEST.Function).compiles5()
ClassMethod compiles5()
{
SourceCode
SET src(0)=4
SET src(1)="TestB "
SET src(2)=" WRITE ""Hello "" "
SET src(3)=" WRITE ""World"",!"
SET src(4)=" QUIT"
CompileSource
SET stat=$COMPILE(src,0,errs,,,,"TestB")
IF stat=0 {
WRITE "编译成功",!
DO ^TestB
} ELSE {
WRITE "状态=",stat,!
WRITE "编译错误长度==",$LISTLENGTH(errs)
}
}
DHC-APP>d ##class(PHA.TEST.Function).compiles5()
编译成功
Hello World
下面的示例对七行ObjectScript程序执行编译错误检查。请注意,此$COMPILE
仅测试错误。它没有提供变量来从成功的编译中接收目标代码。在这个例子中,每一行源代码都包含一个错误; $COMPILE
仅返回第1、3、5、6和7行中的编译时错误,而不返回诸如零除错误(第2行)或未定义的变量错误(第4行)之类的运行时错误:
/// d ##class(PHA.TEST.Function).compiles6()
ClassMethod compiles6()
{
SourceCode
SET src(0)=7
SET src(1)="?TestC "
SET src(2)=" SET a=2/0"
SET src(3)=" SET b=3+#2"
SET src(4)=" SET c=xxx"
SET src(5)=" SET? d=5"
SET src(6)=" SET 123=""abc"""
SET src(7)=" SETT f=7"
CompileSource
SET stat=$COMPILE(src,0,errs)
zw errs
IF stat {
WRITE $LISTLENGTH(errs)," 编译错误 ",!
FOR i=1:1:$LISTLENGTH(errs) {
WRITE !,i,": "
SET errn=$LIST(errs,i)
FOR j=1:1:$LISTLENGTH(errn) {
WRITE $LIST(errn,j)," "
}
}
} ELSE {
WRITE "编译成功",!
WRITE "但没有生成目标代码"
}
}
DHC-APP>d ##class(PHA.TEST.Function).compiles6()
errs=$lb($lb(1,1,1002,"Invalid character in tag"),$lb(3,10,1054,"Invalid expression"),$lb(5,5,1003,"Expected space"),$lb(6,6,1027,"Error in SET command"),$lb(7,5,1026,"Invalid command"))
5 编译错误
1: 1 1 1002 Invalid character in tag
2: 3 10 1054 Invalid expression
3: 5 5 1003 Expected space
4: 6 6 1027 Error in SET command
5: 7 5 1026 Invalid command