Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/loops/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在C语言编写的FORTH-like语言解释器中实现循环_C_Loops_Interpreter_Lookahead_Forth - Fatal编程技术网

如何在C语言编写的FORTH-like语言解释器中实现循环

如何在C语言编写的FORTH-like语言解释器中实现循环,c,loops,interpreter,lookahead,forth,C,Loops,Interpreter,Lookahead,Forth,我正在用C编写一个简单的基于堆栈的语言,我想知道如何实现某种循环结构和/或前瞻符号。因为这个页面的代码有点长(超过200行),所以我把它放进去了 编辑:主程序位于文件stack.c中 编辑:代码只需要输入单词,有点像FORTH。它使用scanf并从左到右工作。然后它使用一系列的ifs和strcmps来决定该做什么。确实如此。第四种方法是在数据堆栈旁边添加一个单独的循环堆栈。然后定义使用此循环堆栈的操作。例如: 5 0 DO I . LOOP 将打印 0 1 2 3 4 其工作方式是: DO

我正在用C编写一个简单的基于堆栈的语言,我想知道如何实现某种循环结构和/或前瞻符号。因为这个页面的代码有点长(超过200行),所以我把它放进去了

编辑:主程序位于文件
stack.c


编辑:代码只需要输入
单词
,有点像FORTH。它使用
scanf
并从左到右工作。然后它使用一系列的
if
s和
strcmp
s来决定该做什么。确实如此。

第四种方法是在数据堆栈旁边添加一个单独的循环堆栈。然后定义使用此循环堆栈的操作。例如:

5 0 DO I . LOOP
将打印

0 1 2 3 4
其工作方式是:

  • DO
    将索引(0)和控件(5)移到循环堆栈上
  • I
    将循环堆栈的顶部复制到数据堆栈
  • 循环
    增加索引(循环堆栈顶部)。如果索引小于控件(循环堆栈顶部下方的一个),则它将从
    DO
    返回到
    loop
    。如果索引大于等于,则从循环堆栈中弹出索引和控件,控件恢复正常

    • 您的语言根本不像Forth,所以不要复制Forth的循环结构(仅限于编译,这对您的语言来说是毫无意义的区别)

      查看您的代码,添加
      直到
      作为条件重新启动评估词。也就是说,将它作为普通单词添加到
      范围
      跳转
      旁边,使其弹出堆栈,如果堆栈顶部为true,则将stack.c的
      cmd
      设置回开头

      0
      dup . 1 + dup 5 > until
      .
      

      ,将在三次求值中生成输出
      0 1 2 3 4 5 6
      ,并多次重新求值第二行。这假设您在各个求值之间保留状态,并且求值是面向行的。您可以挖掘更多类似的想法。

      向基于堆栈的语言添加控件结构的明显方法是添加“控件堆栈”。我将描述Postscript机制,因为这是我所知道的,但是Forth有一些类似的行为(当然有细微的差异)

      最简单的控制回路是
      重复
      回路。从技术上讲,无限
      循环
      更简单,但使用它需要一个显式的
      退出
      命令,并解释这将使事情复杂化

      repeat的语法为:

      int proc  **repeat**  -
      
      因此,当
      repeat
      命令开始时,它希望在操作数堆栈的顶部找到一个过程(这是要执行的循环体),并在该过程的正下方找到一个整数(执行该过程的次数)

      由于还有一个执行堆栈(我认为Forth称之为“return”堆栈),因此命令可以通过将其他命令推到执行堆栈上来“执行”其他命令,从而安排在当前命令完成后立即执行其他命令,但在恢复当前命令的调用方之前执行。这是一个很长的句子,但关键的想法就在那里

      作为一个具体的例子,假设解释器是从输入流执行的。堆栈看起来像这样:

      operand: -empty-
      execute: <stdin>
      
      operand: -empty-
      execute: <stdin> repeat {(Hello World!\n) print} 1 **{(Hello World!\n) print}**
      
      struct object {
          int type;
          union {
              int i;
              void (*command)(context *);
          } u;
      };
      
      struct dict {
          int sz;
          struct rec {
              char *key;
              object val;
          }  data[]; //think resizable!
      };
      
      带引号的(*)过程体被推送到堆栈上

      operand: 2
      execute: <stdin>
      
      operand: 2 {(Hello World!\n) print}
      execute: <stdin>
      
      执行重复过程(一次)会使堆栈如下所示:

      operand: -empty-
      execute: <stdin>
      
      operand: -empty-
      execute: <stdin> repeat {(Hello World!\n) print} 1 **{(Hello World!\n) print}**
      
      struct object {
          int type;
          union {
              int i;
              void (*command)(context *);
          } u;
      };
      
      struct dict {
          int sz;
          struct rec {
              char *key;
              object val;
          }  data[]; //think resizable!
      };
      
      这样,每个命令都有自己的功能。好处是:功能小。你应该尽量使你的功能足够小,你可以看到整个东西在屏幕上的同时。一次扫描整个过程可以让你的右脑做一些检测问题区域的工作

      然后需要另一种类型来保存命令序列。数组或列表应该可以工作。如果可以定义序列,则可以更轻松地重复序列

      这里的好处是你离图灵完成只有一步之遥。序列、迭代、决策(或选择):这就是编写任何可计算函数所需的全部


      *。正如评论员所发现的,后记实际上并没有我在描述中使用的引用概念。这里我借用了Lisp术语的概念。Postscript有一个文字/可执行标志,可以设置
      cvx
      cvlit
      或测试
      xcheck
      。将执行执行堆栈上的可执行数组。所以对于“引用的”过程体,它实际上是一个文本过程体(即数组)。因此,
      repeat
      还必须在下一次迭代之前执行对
      cvx
      的调用。因此,对于
      repeat
      的postscript实现,更正确的伪代码是:

      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-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
      操作符与循环紧密相连,它将清除exec堆栈直至循环操作符的“帧”。对于双功能样式,这是一个
      null
      对象或类似对象。对于单函数样式,这是循环操作符本身。

      这里有一篇博客文章,在我的小TransForth项目中实现了DO/LOOP、BEGIN/UNTIL、WHILE/REPEAT等:

      然而,我后来改变了主意,完全依赖递归,没有这样的循环词


      希望有帮助,玩得开心

      一句忠告:如果代码太大而无法在这里发布,那么我们可能无法仔细研究以帮助您。将问题分解成更小的问题,并将其发布到这里。考虑到我现有的代码,我可以如何表达更小的问题吗?@paxdiablo:它实际上只是一个简单的线程解释器。代码很大,但并不复杂——问题本身实际上已经足够小了。然后,
      eval
      @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