如何在Forth系统中正确地实现deferred



John Heyes的ANS Forth测试套件包含以下定义:

: IFFLOORED [ -3 2 / -2 = INVERT ] LITERAL IF POSTPONE  THEN ;

然后根据我们是使用底除法还是对称除法来有条件地定义各种词:

IFFLOORED : T/MOD  >R S>D R> FM/MOD ;

因此,根据表达式的结果,IFFLOORED要么像noop,要么像。很好。这很容易在我的线程解释器上实现:

: POSTPONE ' , ; IMMEDIATE

…现在IFFLOORED工作;定义等价于: IFFLOORED -1 IF ['] EXECUTE THEN ;

不幸的是,测试套件的下面是以下代码:

: GT1 123 ;
: GT4 POSTPONE GT1 ; IMMEDIATE
: GT5 GT4 ;
 assertion here that the stack is empty

相同的实现在这里不起作用。如果POSTPONE编译了对它的词的引用,那么GT4就变成了: GT4 123 ;的等价物…但GT4直接。因此,当定义GT5时,123被压入编译器的堆栈,而GT5成为noop。但这是不对的;测试套件期望调用GT5将123留在堆栈上。因此,为了使其工作,POSTPONE必须生成以下代码:

: POSTPONE  ' LITERAL  ['] , LITERAL ;

事实上,如果我玩gForth,我看到POSTPONE实际上是这样工作的:

: GT1 123 ;
: GT4 POSTPONE GT1 ; IMMEDIATE
SEE GT4
<long number> compile, ;

但是这两个定义是不兼容的。如果我使用第二个定义,第一个测试就会失败(因为现在IFFLOORED试图编译而不是执行它)。如果我使用第一个定义,第二个测试就会失败(因为GT4将压入编译器堆栈,而不是编译文字压入)。

…但是两个测试都通过了。

这是怎么回事?

让我在这里回答,因为问题改变了很多。我仍然不确定我理解这个问题,虽然:)

在您的示例中,您定义了

: GT4 POSTPONE GT1 ; IMMEDIATE

这里发生的事情如下:

    执行
  1. :,读取GT4并创建新单词
  2. POSTPONE的编译语义被执行,这是为了编译GT1的编译语义——正如你在GForth中看到的。
  3. ;被执行,结束定义
  4. IMMEDIATE被执行,将最后一个定义的字标记为immediate。

POSTPONE只在编译 GT4时调用,在编译后的代码中不出现。因此,当以后在GT5的定义中使用这个直接词时,不需要POSTPONE的解释语义。

顺便说一下,根据标准,POSTPONE只有编译语义,解释语义没有定义。

参见GForth手册中的POSTPONE教程。

EDIT解释和编译语义的示例:

: TEST1 ." interpretation" ;  => ok
: TEST2 ." compilation" ; IMMEDIATE  => ok
: TEST3 TEST1 TEST2 ;  => compilation ok
TEST3  => interpretation ok
: TEST4 POSTPONE TEST1 ; IMMEDIATE  => ok
: TEST5 TEST4 ;  => ok
TEST5  => interpretation ok
: TEST6 POSTPONE TEST2 ; IMMEDIATE  => ok
TEST6  => compilation ok

如果您还有问题,可以参考这些测试

您引用的代码片段做了以下事情:

  1. 计算-3/2(编译时),并检查是否为-2。
  2. 如果是,在 IFFLOORED中存储一个0 (false),否则存储一个-1 (true) ,所以当它被求值时,它会把这个值放在堆栈上。(这是LITERAL的效果)
  3. 当计算IFFLOORED时,在堆栈上压入值后,会出现IF - THEN表达式。当该值为true时,这意味着我们是而不是在底层环境中,所以我们想注释掉其余的行,这就是所做的。

棘手的部分来了——IMMEDIATE,也就是说,你不能在冒号定义中使用它,因为它会注释掉该行的其余部分。你必须显式地告诉编译器你想编译这个函数,而不是执行它,这就是POSTPONE所做的。

postpone word在编译状态的行为是确定其解析参数的编译语义,并将这些语义附加到当前定义中。

一个词的编译语义可以是特殊的,也可以是普通的(参见第4 -2012节3.4.3.3编译语义)。为了正常工作,postpone 应该区分这些情况,并根据不同的模式生成代码。

你的实现的一个问题是它们对于普通编译语义或特殊编译语义都是正确的。

符合标准的实现如下:

: state-on  ( -- )  1 state ! ;
: state-off ( -- )  0 state ! ;
: execute-compiling ( i*x xt --j*x )
  state @ if  execute  exit  then
  state-on  execute  state-off
;
: postpone ( "name" -- )
  bl word find dup 0= -13 and throw 1 = ( xt flag-special )
  swap lit, if ['] execute-compiling else ['] compile, then compile,
; immediate

在我的文章中看到更多的细节如何推迟应该工作

最新更新