编辑:主程序在
stack.c
文件中。编辑:该代码只是使用
scanf
从左到右处理输入的word
,类似于FORTH。然后使用一系列if
和strcmp
来决定要做什么。就是这样。stack.c
文件中。scanf
从左到右处理输入的word
,类似于FORTH。然后使用一系列if
和strcmp
来决定要做什么。就是这样。5 0 DO I . LOOP
会打印出来
0 1 2 3 4
这是它的工作方式:loop { double index; double control; size_t buffer_size; char buffer[]; } loopStack;
。然后,eval
需要使用缓冲区来保存从 DO
到看到 LOOP
后的所有内容,以便根据 index
和 control
来重放它。 - Jeremy W. Shermanstatic
表示将变量存储在静态存储区。 loop
是结构体标识符;它使得可以声明其他类型的变量,如 struct loop loop2;
。 - Jeremy W. ShermanI
,有很好的惯用理由(并且还有嵌套循环的兄弟J
和K
),但再次强调:您不会实现任何类似于Forth的东西,因此您称呼循环索引的名称并不重要。 - Julian Fondren为栈式语言添加控制结构的明显方法是添加一个“控制堆栈”。我将描述Postscript机制,因为那是我所知道的,但Forth具有类似的行为(当然还有微妙的差异)。
最简单的受控循环是repeat
循环。严格来说,无限loop
更简单,但要使用它需要显式的exit
命令,而解释这一点会使事情变得复杂。
repeat的语法如下:
int proc **repeat** -
repeat
命令开始时,它期望在操作数栈的顶部找到一个过程(这是要执行的循环体),并在过程正下方找到一个整数(要执行该过程的次数)。operand: -empty-
execute: <stdin>
2 {(Hello World!\n) print} repeat
。operand: 2
execute: <stdin>
引用(*)的过程体被推入堆栈。
operand: 2 {(Hello World!\n) print}
execute: <stdin>
< p > repeat
命令将被执行。
operand: 2 {(Hello World!\n) print}
execute: <stdin> repeat
Repeat: expects operands: int proc
if int<0 Error
if int==0 return //do nothing
push `repeat` itself on exec stack
push quoted proc on exec stack
push int-1 on exec stack
push "executable" proc on exec stack
operand: -empty-
execute: <stdin> repeat {(Hello World!\n) print} 1 **{(Hello World!\n) print}**
operand: 1
execute: <stdin> repeat {(Hello World!\n) print}
operand: 1 {(Hello World!\n) print}
execute: <stdin> repeat
我们又回到了起点!准备进行下一次迭代(或终止,如果整数已经降为零)。
希望这可以帮助你。
编辑:
实际查看您的代码后,我有一个不同的建议,或许可以作为上述内容的跳板。我认为您应该暂时忘记代码,专注于数据结构。您已经为变量准备了一个漂亮的表格,但所有命令都是散布在代码中的嵌入文字!
如果将表格设置为变量记录类型,则可以对两者使用相同的查找机制(甚至升级到哈希表或三叉搜索树(我目前最喜欢的))。我想到的是类似于以下内容:
struct object {
int type;
union {
int i;
void (*command)(context *);
} u;
};
struct dict {
int sz;
struct rec {
char *key;
object val;
} data[]; //think resizable!
};
Repeat: expects operands: int proc
if int<0 Error
if int==0 return //do nothing
push `repeat` itself on exec stack
push 'cvx' on the exec stack
push cvlit(proc) on exec stack
push int-1 on exec stack
push "executable" proc on exec stack
这执行必要的标志操作,将该过程作为数据传递到执行堆栈上。
我见过另一种实现此控制结构的方式是使用两个函数:相同的入口点repeat
和一个内部续延操作符,理论上不需要名称。我想ghostscript将其称为类似于@repeat-continue@的东西。使用单独的继续函数,您就不必在堆栈上仔细处理各种顺序,并且不需要转换文字标志。相反,您可以在执行堆栈上的递归调用下方存储一些持久化数据;但是您也必须清理它。
因此,备选的repeat
是:
int proc **repeat** -
if int<0 Error
if int==0 return //do nothing
push null on exec stack <--- this marks our "frame"
push int-1 on exec stack
push proc on exec stack
push '@repeat-continue' on exec stack
push executable proc on exec stack
@repeat-continue
peek proc from exec stack
peek int from exec stack
if int==0 clear-to-null and return
push '@repeat-continue' on exec stack
push executable proc on exec stack
最后,exit
运算符与循环密切相关,它清除执行堆栈直到循环运算符的“帧”。对于2函数样式,这是一个null
对象或类似对象。对于1函数样式,这是循环运算符本身。
until
添加为有条件的重新评估单词。也就是说,将其作为普通单词添加到您的 range
和 jump
中,将其弹出堆栈,并且如果堆栈顶部为 true,则设置 stack.c 的 cmd
回到开头。0
dup . 1 + dup 5 > until
.
在您的语言中,将产生输出0 1 2 3 4 5 6
,跨越三个评估,并多次重新评估第二行。这假定您在评估之间保留状态,并且评估是基于行的。您可以从LSE64获取更多类似这样的想法。
这是一篇关于我的小型TransForth项目中实现DO/LOOP、BEGIN/UNTIL、WHILE/REPEAT等的博客文章:http://blogs.msdn.com/b/ashleyf/archive/2011/02/06/loopty-do-i-loop.aspx
然而,我已经改变了主意,完全依赖递归而没有使用循环语句。
希望这有所帮助,玩得开心!