【问题标题】:How to properly implement POSTPONE in a Forth system?如何在 Forth 系统中正确实现 POSTPONE?
【发布时间】:2015-07-26 12:25:54
【问题描述】:

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 推送到编译器堆栈而不是编译文字推送)。

...但是两个测试都通过了 gForth。

那么发生了什么?

【问题讨论】:

  • POSTPONE \ THEN ... 推迟评论。我确定这不是你的意思。您能否修复该列表?

标签: forth


【解决方案1】:

让我在这里回答,因为问题发生了很大变化。不过,我仍然不确定我是否理解这个问题:)

在您的示例中,您定义

: GT4 POSTPONE GT1 ; IMMEDIATE

这里发生的情况如下:

  1. : 被执行,读取 GT4 并创建新单词
  2. 执行POSTPONE的编译语义,也就是编译GT1的编译语义——正如你在GForth中看到的那样。
  3. 执行;,结束定义
  4. 执行IMMEDIATE,将最后定义的单词标记为立即数。

POSTPONE 仅在编译 GT4 时被调用,并且它不会出现在编译后的代码中。所以以后在GT5的定义中使用这个直接词时,就不需要POSTPONE的解释语义了。

顺便说一句,根据the standardPOSTPONE只有编译语义,解释语义是未定义的。

另请参阅GForth manual 中的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

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

【讨论】:

  • 是的,我都知道!我理解的是POSTPONE 在这种情况下IFFLOORED 情况下如何工作。 GT4 最终成为一个词,当被调用时,编译GT1 的调用。但是IFFLOORED 最终成为一个词,当被调用时,执行 对 \ 的调用。 POSTPONE 怎么能两者兼得?
  • POSTPONE 总是编译单词的编译语义。对于GT1,它的编译语义是编译对GT1 的调用。而在 \ 的情况下,作为 IMMEDIATE 单词,它的编译语义是将行的其余部分视为注释。所以,本质上,POSTPONE 推迟了下一个单词的执行——如果它是立即执行的,那么它会编译一个对它的简单调用;如果它是一个简单的调用,那么它会编译代码来编译它。
  • 哦,看看是什么字!这是完全有道理的(一旦我完成了实现,它甚至可以工作)。谢谢!
【解决方案2】:

你引用的 sn-p 做了以下事情:

  1. 评估 -3/2(在编译时),并检查它是否为 -2。
  2. 如果是,则存储一个0(假),否则存储一个-1(真)inIFFLOORED,所以当它被求值时,它会将这个值放入堆栈。 (这是LITERAL的效果。)
  3. 在评估IFFLOORED 时,将值压入堆栈后,会出现IF - THEN 表达式。当值为 true 时,意味着我们不是处于地板环境中,因此我们想要注释掉该行的其余部分,这就是 \ 所做的。

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

【讨论】:

  • 很抱歉,您完全错过了我的问题的重点 --- 我了解 IFFLOORED 的工作原理;这是POSTPONE 我很困惑。我已经更新了这个问题,希望能更清楚我想知道的内容。
【解决方案3】:

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

单词的编译语义可以是特殊的也可以是普通的(参见 Forth-2012 的 3.4.3.3 Compilation semantics 部分)。要正常工作,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

在我的帖子How POSTPONE should work中查看更多详细信息。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-31
    • 1970-01-01
    • 1970-01-01
    • 2014-10-15
    相关资源
    最近更新 更多