Red预处理器是Red的一种方言,可以转换Red源码在常规的Red语言特定层之上。 转换是通过Red源码中的内联预处理器关键字(称为`directives`)实现的。这些指令将会处理:
-
当Red源码被编译的时候
-
当Red源码通过`do`函数调用文件参数执行的时候
-
当`expand-directives`函数在block值上被调用的时候
预处理器在加载阶段(LOADing phase)之后被调用,所以它处理Red值,而不是文本形式的源码。
指令分类:
-
条件指令:根据表达式执行的结果包含代码。
-
控制指令:控制预处理器的行为。
-
宏:使用函数转换代码,启用更复杂的转换。
指令被表示为一个特定的`issue!值(以
#`开头)。
当指令被处理的时候,它会被替换为它返回的结果值(有些指令没有返回值,所以它们仅仅只是被移除)。这就是源码转换是如何实现的。
注意:Red/System有它自己的 预处理器,它和Red的预处理相似,但是更底层并且应用到文本形式的源码。
在条件表达式或宏中,为了访问编译源码时提供的设置,有一个隐式的`config`对象被提供。这些设置经常用于在条件表达式中包含代码。详细的设置列表可以在 这里 查看。
例子:
#if config/OS = 'Windows [#include %windows.red]
注意: 当在运行时(从Red的解释器中)使用预处理器时,`config`对象也有效,并且可以反射在编译Red可执行文件到可运行代码时的选项。
为了避免暴露words到全局上下文,以造成不必要的副作用,在指令中的所有表达式都限定在特定的上下文中。上下文延伸到在条件表达式、宏和`#do`指令的每个set-word。
提示:
-
可以用以下表达式打印输出隐藏上下文:
#do [probe self]
-
当在运行时使用的时候,隐藏上下文也可以用以下形式直接访问:
probe preprocessor/exec
Red预处理器支持定义宏函数(简称为*macros*)来实现更复杂的转换。宏允许高效的元编程,即执行花费在编译时而不是运行时。Red在运行时已经有很强的元编程能力,但是,为了使得Red源码在编译器和解释器运行等效,宏也可以在运行时被解析。
注意:没有读取时间宏。考虑到用解析方言对文本形式的源进行预处理是多么简单,这种支持现在是多余的。
预处理器支持两种类型的宏:
Red [] #macro make-KB: func [n][n * 1024] print make-KB 64
将会输出:
Red [] print 65536
在一个宏中调用另一个宏:
Red [] #macro make-KB: func [n][n * 1024] #macro make-MB: func [n][make-KB make-KB n]
print make-MB 1
将会输出:
Red [] print 1048576
=== 模式匹配宏 与匹配单词和获取参数相反,这种类型的宏按照Parse方言的规则或关键字来匹配宏。和命名宏相同,返回值被用作匹配模式的替换值。 虽然,也有这种类型的宏的低级版本,采用`[manual]属性触发`。在这种情况下,没有隐式的行为,而是由用户完全控制。它不会自动替换匹配的值,而是取决于宏函数应用所需的转换并设置处理的恢复点。 模式匹配宏的典型形式为:
#macro <rule> func [<attribute> start end /local word1 word2...][...code...]
`<rule>`部分可以是: * lit-word!值:用来匹配指定的word。 * word!值:Parse关键字,和数据类型名字相同或者用`skip`匹配*所有*的值。 * block!值:Parse方言规则。 `start`和`end`参数是引用划定匹配模式的源代码。返回值需要是对恢复位置的引用。 `<attribute>`可以是`[manual]`,使得触发宏的低级手动模式。 *例子:*
Red []
#macro integer! func [s e][s/1 + 1] print 1 + 2
将会输出:
Red [] print 2 + 3
使用*manual*模式,相同的宏可以被写作:
Red []
#macro integer! func [[manual] s e][s/1: s/1 + 1 next s] print 1 + 2
使用block规则创建一个变元函数:
Red [] #macro ['max some [integer!]] func [s e][ first maximum-of copy/part next s e ] print max 4 2 3 8 1
将会输出:
Red [] print 8
== 指令 === #if *语法*
#if <expr> [<body>]
<expr> : expression whose last value will be used as a condition. <body> : code to be included if <expr> is true.
*描述* 如果条件表达式为真,则包含一段代码。如果包含`<body>`块,它也将被传递给预处理器。 *例子*
Red []
#if config/OS = 'Windows [print "OS is Windows"]
如果在Windows运行的话将会输出以下结果:
Red []
print "OS is Windows"
否则的话,仅仅输出:
Red []
也可以利用`#do`指令定义你自己的word,使得可以用在条件表达式后:
Red []
#do [debug?: yes]
#if debug? [print "running in debug mode"]
将会输出:
Red []
print "running in debug mode"
=== #either *语法*
#either <expr> [<true>][<false>]
<expr> : expression whose last value will be used as a condition. <true> : code to be included if <expr> is true. <false> : code to be included if <expr> is false.
*描述* 根据条件表达式选择要包含的代码块。包括块也将传递给预处理器。 *例子*
Red []
print #either config/OS = 'Windows ["Windows"]["Unix"]
如果在Windows运行的话将会输出以下结果:
Red []
print "Windows"
否则的话,将会输出:
Red []
print "Unix"
=== #switch *语法*
#switch <expr> [<value1> [<case1>] <value2> [<case2>] …] #switch <expr> [<value1> [<case1>] <value2> [<case2>] … #default [<default>]]
<valueN> : value to match. <caseN> : code to be included if last tested value matched. <default> : code to be included if no other value matched.
*描述* 选择一个代码块,根据它的值选择是否包含多个代码块中的一个。所包含的块也将传递给预处理器。 *例子*
Red []
print #switch config/OS [ Windows ["Windows"] Linux ["Unix"] MacOSX ["macOS"] ]
如果在Windows运行的话将会输出以下结果:
Red []
print "Windows"
=== #case *语法*
#case [<expr1> [<case1>] <expr2> [<case2>] …]
<exprN> : conditional expression. <caseN> : code to be included if last conditional expression was true.
*描述* 选择一个代码块,根据它的值选择是否包含多个代码块中的一个。所包含的块也将传递给预处理器。 *例子*
Red []
#do [level: 2]
print #case [ level = 1 ["Easy"] level >= 2 ["Medium"] level >= 4 ["Hard"] ]
将会输出:
Red []
print "Medium"
=== #include *语法*
#include <file>
<file> : Red file to be included (file!).
*描述* 在编译时进行评估时,读取并将参数文件内容包含在当前位置。 该文件可以包含与当前脚本绝对或相对的路径。 当红色解释器运行时,该指令只是被替换为`do`,并且不会包含文件。 === #do *语法*
#do [<body>] #do keep [<body>]
<body> : any Red code.
*描述* 执行隐藏上下文中的body块。 如果使用`keep`,则将指令和参数替换为“body”的结果。 *例子*
Red []
#do [a: 1]
print ["2 + 3 =" #do keep [2 + 3]]
#if a < 0 [print "negative"]
将会输出:
Red []
print ["2 + 3 =" 5]
=== #macro *语法*
#macro <name> func <spec> <body> #macro <pattern> func <spec> <body>
<name> : name of the macro function (set-word!). <pattern> : matching rule for triggering the macro (block!, word!, lit-word!). <spec> : specification block for the macro function. <body> : body block of the macro function.
*描述* 创建一个宏函数。 对于命名宏,指定的block可以根据需要声明任意数量的参数。body需要返回一个用于替换宏调用及其参数的值。 返回空块将仅删除宏调用及其参数。 对于模式匹配宏,spec块必须只声明**两个**参数,匹配模式的起始引用和结束引用。按照惯例,参数名称为:`func [start end]`或`func [s e]`作为短格式。 默认情况下,主体需要返回一个用于替换匹配模式的值。 返回一个空块将只是删除匹配的模式。 *手动*模式也可用于模式匹配宏。 可以通过在函数* spec *块中放置一个`[manual]`属性来设置:`func [[manual] start end]`。这种手动模式需要宏返回恢复位置(而不是替换值)。如果需要*重新处理*一个替换的模式,那么`start`是要返回的值。 如果需要*跳过匹配的模式,那么`end`是要返回的值。还可以返回其他位置,这取决于宏实现的转换,以及部分或全部重新处理替换值的愿望。 模式匹配宏接受: * block: 使用Parse方言指定要匹配的模式。 * word: 指定一个有效的Parse方言word(如数据类型名称,或“skip”匹配所有值)。 * lit-word: 指定一个特定的文字来匹配。 *例子*
Red [] #macro pow2: func [n][to integer! n ** 2] print pow2 10 print pow2 3 + pow2 4 = pow2 5
将会输出:
Red [] print 100 print 9 + 16 = 25
模式匹配宏例子:
Red [] #macro [number! '+ number! '= number!] func [s e][ do copy/part s e ]
print 9 + 16 = 25
将会输出:
Red [] print true
手动模式的模式匹配宏:
Red [] #macro ['sqrt number!] func [[manual] s e][ if negative? s/2 [ print [ "* SQRT Error: no negative number allowed" lf "* At:" copy/part s e ] halt ] e ;-- returns position passed the matched pattern ]
print sqrt 9 print sqrt -4
将会输出:
-
SQRT Error: no negative number allowed
-
At: sqrt -4 (halted)
=== #local *语法*
#local [<body>]
<body> : arbitrary Red code containing local macros definitions.
*描述* 为宏创建本地上下文。 在该上下文中定义的所有宏将在退出时被丢弃。 因此,本地宏也需要在本地应用。 这个指令可以递归使用(`#local`是`<body>`中的一个有效的指令)。 *例子*
Red [] print 1.0 #local [ #macro float! func [s e][to integer! s/1] print [1.23 2.54 123.789] ] print 2.0
将会输出:
Red [] print 1.0 print [1 3 124] print 2.0
=== #reset *语法*
#reset
*描述* 重置隐藏的上下文,将其从所有以前定义的单词中清空,并删除所有以前定义的宏。 === #process *语法*
#process [on | off]
*描述* 启用或禁用预处理器(默认情况下启用)。 这是一个避免处理Red文件的部分的转义机制,其中使用了指令,而不是用于预处理器(例如,如果在具有不同含义的方言中使用)。 实现约束:在禁用它之前再次启用预处理器时,`#process off`指令需要在代码中嵌套相同(或更高)的级别。 *例子*
Red []
print "Conditional directives:" #process off foreach d [#if #either #switch #case][probe d] #process on
将会输出:
Red []
print "Conditional directives:" foreach d [#if #either #switch #case][probe d]
=== #trace *语法*
#trace [on | off]
*描述* 启用或禁用屏幕上评估的表达式和宏的调试输出。 在Red源中可以使用该指令的方式没有特别的限制。 == 运行时API anchor:runtime-api[] Red预处理器也可以在运行时工作,以便能够使用解释器中的预处理器指令来评估源代码。 在`file``值上使用`do`时会自动调用它。 请注意,以下形式可用于在不调用预处理程序的情况下执行文件:`do load%file`。 === expand-directives *语法*
expand-directives [<body>] expand-directives/clean [<body>]
<body> : arbitrary Red code containing preprocessor directives.
*描述* 对块值调用预处理器。 参数块将被修改并用作返回值。 如果使用`/ clean`细化,则预处理器状态被重置,所以先前定义的所有宏被擦除。 *例子*
expand-directives [print #either config/OS = 'Windows ["Windows"]["Unix"]]
Windows平台上会输出: