Forth 编译程序

时间:2022-12-09 17:32:59

body, table{font-family: 微软雅黑; font-size: 13.5pt}
table{border-collapse: collapse; border: solid gray; border-width: 2px 0 2px 0;}
th{border: 1px solid gray; padding: 4px; background-color: #DDD;}
td{border: 1px solid gray; padding: 4px;}
tr:nth-child(2n){background-color: #f8f8f8;}

      解释状态==执行状态         编译地址==执行地址

      FORTH 系统内部有一个系统变量 STATE,当 STATE==0,系统处于"执行"状态,当 STATE <> 0,系统处于"编译"状态。系统从输入缓冲区中取出下一个字符串放到词缓冲区,然后去词典中查找该词;如果该词在词典中,就把该词的定义的编译地址(cfa,也叫执行地址)送到参数栈顶。然后,如果系统处于执行状态,则外部解释程序(即文本解释程序)就调用内部解释程序(地址解释程序)执行该定义;如果系统处于编译状态,则编译程序就把留在参数栈顶的 cfa 移到词典的顶部,把该定义的 cfa 编入到正在被编译的定义的参数域中,但是,如果cfa所代表的定义是一个立即词,不管系统处于什么状态,都执行它。
Forth 编译程序
      若输入流中分离出来的字符串在词典中没有定义,系统就把它转化成一个数字,如果成功,文本解释程序就把该数字放置到参数栈上;若系统处于编译状态,则编译程序就把该数字编入词典成为一个数字文字常数(literal)。
      

定义 [ 和 ] :
      FORTH 提供 [ 和 ] 这两个定义以显示改变系统的状态,[ 使系统从编译状态变为执行状态;] 使系统从执行状态变为编译状态。这两个定义均只能在冒号定义中使用。若  [  ] 成对出现在冒号定义中,则包含的部分被立即执行而不被编译。
Forth 编译程序

文字常数:
      FORTH 中,有一些进行编译工作的词,称为编译类词。它们的效果是给词典中编译进这样或那样的内容。可以把参数堆栈上的数字“原封不动”的搬进词典形成“文字常数(literal)”;所以在词典中不少情况下是编译地址和文字常量并存的局面。五个编译词:LIT、LITERAL、(DLITERAL)、COMPILE、[COMPILE]。
:   LIT    ( - n )       编译数字的运行时间代码
        R@  @         取出编译在词身内的数字
        R>  2+  >R  ;        调整IP
运行时 LIT 做两项工作:
1、它必须把跟着它的数字放到参数堆栈顶;
2、它必须移动解释指针 IP 越过这个数字而指向下一个编译地址。
:  FIVE+   5  +  ;
FIVE+ ^LIT 5 ^+ ^UNNEST
1000 1002 1004 1006
开始执行 LIT 之前,IP 偏移指向 5 。即(IP)=1002。执行 LIT,由于 LIT 是冒号定义,所以先执行的是 LIT 的代码指针域 cfa 内的 NEST,执行过程:
1、(IP) -> 返回栈顶保存,于是返回栈首项的内容为1002
2、(W)+2 -> W  它是 LIT 的 pfa
3、(W) -> IP  
4、执行 NEST 内最后的 NEXT ,于是就开始执行 LIT 的词身
^R@  把返回栈顶的内容1002复制到参数栈顶  ^@ 取出1002单元的内容5   ^R> 把返回栈顶的1002送到参数栈顶  ^2+  参数栈首项变为1004,指着下一个要执行的词 +  ^>R 把1004送回返回栈顶保存  ^UNNEST 把返回栈顶的1004 -> IP,接着执行1004处的 +。
:  COMPILE   ( - )      只可以用在冒号定义内部。当该冒号定义执行时,COMPILE 就把跟在它后面的定义编译进词典中。
         R>
         DUP  2+  >R
         @  ,  ;
与 LIT 定义相比,COMPILE 仅仅在定义的最后多了一个逗号,由它把留在堆栈顶上的跟在 COMPILE 之后的词的 cfa 编入词典
Forth 编译程序
:  LITERAL   ( n -  )       把在参数栈上的单字长整数编译成为一个文字常数。
       COMPILE  LIT       首先编译运行时间代码 LIT
       ,                             接着编译数字本身
       ;  IMMEDIATE      使其成为立即词
eg:
:  test   [  5  ]  LITERAL  +  ;
//数字 5 必须处于词 [ 和 ] 中间。
:  DLITERAL   ( d -  )     把在参数栈上的双字长整数编译成为一个双字长的文字常量
        SWAP      颠倒双字长整数的次序
        [COMPILE]  LITERAL   把文字常数的编译推迟到 DLITERAL 执行时进行。因为 LITERAL 是立即词,故只有用[COMPILE]才能把它编入 DLITERAL 中。
        [COMPILE]  LITERAL   强行编译双字长文字常数的高16位。
        ;  IMMEDIATE
:  [COMPILE]   ( - )      强行编译跟着的立即型定义
          '           寻求跟着的词的执行地址
          ,           编译它
          ;  IMMEDIATE       必须立即执行

编译循环:
      FORTH 中编译过程与解释过程(即执行过程)的区别在于:在编译过程中,不是执行从输入流中分离出来的词,而是把词的编译地址添加到词典的顶部,在那儿系统正在构造一个新的冒号定义的参数域;如果从输入流中分离出来的是数字,那么编译过程不是把数字留在参数堆栈上,而是把它编译成为定义中的数字文字常数。以后当执行该定义时,同一数字便可被重新取出并放置到参数栈顶。冒号定义的编译程序就是定义 ] ,它和解释程序 INTERPERT 有类似操作。

冒号定义的开始和结束:
:    :    ( - )                    定义一个高级定义,新定义完成之前被"隐藏"起来,冒号的执行代码给程序增添一层嵌套
         !CSP            把当前参数堆栈指针存入变量CSP以在定义结束时查错之用,正常编译不应影响参数堆栈的深度
         CURRENT  @  CONTEXT  !                   使 context 词汇与 current 词汇一致
         CREATE                使用跟在冒号后面的名字在词典中建立一个首部
         HIDE                    使该首部躲过词典搜索
         ]                          进入冒号定义的编译程序,开始建造在参数域中的执行地址表
         ;USES                把跟着的代码程序(即下面的NEST)的地址插入到新定义的代码指针域,使新定义成为一个冒号定义。(类似DOES>,编译运行过程已经规定好的代码到代码域)
         NEST  ,                编译地址解释程序NEST的地址,使其可被放进新定义的cfa
:     ( - )           结束一个冒号定义,它编译执行代码UNNEST退出一层件嵌套,改变STATE的值以解释编译
         ?CSP             当前的参数堆栈指针与CSP的值相符吗?若不相符则停止
         COMPILE  UNNEST             给新定义的末尾加上UNNEST,使程序的执行返回到调用者
         REVEAL         与HIDE想反操作
         [COMPILE]  [         在此外编译,[  以结束新定义的编译 
         ;  IMMEDIATE       分号自身必须在编译状态下执行,所以必须是一个立即词。

数字文字常量:
      指的是词典中存放运行时间代码(LIT)的cfa的单元及跟着它的数字单元;执行是LIT把数字送到参数堆栈。LITERAL、DLITERAL、ASCII、[']……
:  ASCII      ( - char )         把输入流中下一个字符的ASCII代码编译为一个数字文字常量
        BL  WORD        取出下一个字符
        1+  C@          得到它的ASCII代码
        STATE  @         获取当前系统状态(编译?解释)
        IF  [COMPILE]  LITERAL          编译状态就把其编为文字常数
        THEN          否则留在参数堆栈上
        ;  IMMEDIATE
Forth 编译程序
:  [']   ( - )
       '  
       [COMPILE]  LITERAL
       ;  IMMEDIATE
[']  被定义成为一个立即型的定义,因此对于 ['] 定义内的 ' 来说,所谓的输入流中的下一个词就是冒号定义内跟在 ['] 后面的那个词。

数字输出转换:

HLD  (- add)  在数字输出转换过程中,保存文本最后字符地址的变量。
:  HOLD   ( char -  )      把ASCII字符char插入输出字符串中
        -1  HLD  +!      HLD为指向输出文本缓冲区的字符指针,在输出文本缓冲区中形成输出数字字符串。数字字符串是从最低有效位开始逆向建立,为把一个字符插入该字符串。HLD 的内容要减一
         HLD  @       取到指定地址
         C!  ;        把字符串插入HLD所指定的地址字节
:  <#   ( - )        初始化数字转换过程,要求栈中是个无符号双字长数
       PAD                返回输出文本的缓冲区地址
       HLD  !  ;      HLD 指向 PAD ,以便字符串能在 PAD 缓冲区中建立
:  #>   ( d - addr len )         结束输出数字转换,并把字符串的地址和长度送入堆栈,它们正是 TYPE 命令所需要的参数
       2DROP       不再需要堆栈中的双字长数
       HLD  @            取出字符串首址
       PAD           取出字符串的末尾地址
       OVER  -  ;         得到字符串的长度
:  SIGN  ( n -  )          如果栈顶项 n 是负数,则在输出数字字符串中插入一个负号
       0<  IF       判断栈顶是不是负数
       ASCII  -  HOLD         插入负号
       THEN  ;   
:  #   ( d1 - d2  )           转换一位数字,并把它加到输出数字字符串中。转换中,d1除以基,商d2被保留在堆栈中,余数被转换成ASCII码,送入输出缓冲区
       BASE  @  MU/MOD         余数和双字长整数商保留在堆栈上
       ROT         把余数移到栈顶
       9  OVER  <        如果余数大于9
       IF  7  +  THEN       加7以形成A
       ASCII  0  +  HOLD  ;     把余数转换成 ASCII 码,并插入到输出缓冲区
:  #S  ( d - 0 0 )         转换一个双字长整数,直至结果为零
       BEGIN
           #       转换一位数字
           2DUP  OR        判断商是否为 0 ?
       0=  UNTIL  ;         如果商为零,退出循环;否则,继续转换。
eg:无符号双字长整数 12345.  ,要求打印成:123.45  ; 命令:<#  #  #  ASCII  .  HOLD  #s  #>  TYPE
Forth 编译程序
小数点的前三位整数用 #S 实现转换。整个转换过程是在 PAD 缓冲区内进行的。转换完成堆栈顶部存放的是ASCII字符串的首地址和长度。
Forth 编译程序

Forth 编译程序的更多相关文章

  1. delphi7 编译程序时报win32&period;indcu&period;a病毒的解决方法

    Delphi7用了很久一直都没问题,同一个工程文件昨天编译时mod32还不会报毒,今天重新编译时,生成的exe突然nod32报毒. 提示: “Project1.exe Win32/Induc.A 病毒 ...

  2. Flex编译程序出现 Could not find compiled resource bundle &&num;39&semi;SharedResources&&num;39&semi; for locale &&num;39&semi;en&lowbar;US&&num;39&semi;&period;

    Flex编译程序出现 Could not find compiled resource bundle 'SharedResources' for locale 'en_US'. 而且静态类居然为nul ...

  3. 在Linux下如何使用GCC编译程序、简单生成 静态库及动态库

      最近在编写的一个Apache  kafka 的C/C++客户端,,在看他写的 example中,他的编译是用librdkafka++.a和librdkafka.a    静态库编译的,,,而我们这 ...

  4. ubuntu下 GCC编译程序出现 undefined reference to &grave;std&colon;&colon;ios&lowbar;base&colon;&colon;Init&colon;&colon;Init&lpar;&rpar;&&num;39&semi;问题

    网上的解释是:“ you need to add -lstdc++, or use 'g++' rather than 'gcc' as your driver program.”,也就是说如果想要使 ...

  5. 减小Gcc编译程序的体积

    众所周知,Gcc编译的原始程序一般很大,其实有几种方法能大大减小目标代码的体积,一般有以下几种方法. 基本知识来源:http://www.mingw.org/wiki/Large_executable ...

  6. MFC编译程序,缺少MFC动态链接库的解决

    MFC编译程序,缺少MFC动态链接库的解决 问题:VS2010 c++编写的程序在别人的机子运行不了,缺少mfc100u.dll xxx100d.dll等的解决方法 解决方法: 1.将这些dll打包, ...

  7. RHEL 7特性说明(七):编译程序及工具

    转载自:RedHat https://access.redhat.com/documentation/zh-CN/Red_Hat_Enterprise_Linux/7/html/7.0_Release ...

  8. windows下用vs2008和boost结合编译程序

      原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://co63oc.blog.51cto.com/904636/504469 win ...

  9. Delphi使用大图标编译程序

    在Windows Vista. Windows7以上Windows系统中可以支持大图标显示了,但是Delphi编译出来的程序却只能显示32x32的图标,这使Delphi编译的程序看起来很不专业.下面就 ...

  10. VS2012 编译程序时报无法载入PDB文件错误解决方式

    VS2012 编译程序时报无法载入PDB文件错误解决方式 "ConsoleApplication1.exe"(Win32): 已载入"C:\Users\hp\Docume ...

随机推荐

  1. Akka&period;NET v1&period;0 已发布,支持Mono

    Akka.NET 是Java/Scala 流行框架Akka的一个 .NET 开源移植.可用于构建高并发,分布式和容错事件驱动的应用在 .NET 和 Mono 平台之上.Akka.NET 经过一年多的努 ...

  2. js 小数相加

    <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat=&qu ...

  3. Ubuntu12&period;04 安装openjdk-8-jdk

    参考文章:http://ubuntuhandbook.org/index.php/2015/01/install-openjdk-8-ubuntu-14-04-12-04-lts/ OpenJDK J ...

  4. c&num;中sqlhelper类的编写&lpar;二&rpar;

    上一篇文章讲了简易版的SqlHelper类的编写,我们在这里就上一篇文章末尾提出的问题写出解决方案. sql语句注入攻击已经是众所周知的了.我们如何在C#中保护自己的数据库不被这样的方式攻击呢? 不用 ...

  5. 生日小助手V4&period;0——迁移到Python3

    生日小助手V4.0——迁移到Python3 生日小助手V4.0只支持Linux系统,依赖命令行软件lunar Ubuntu系统安装方法:1.安装lunarsudo apt-get install lu ...

  6. jvm - 内存结构以其解析

    可以将jvm粗略分为以下部分: Heap Memory:存储java对象. Non-Heap Memory:存储加载的class文件,以及其他meta-data信息. Other:存储java代码,j ...

  7. HDOJ 1391 Number Steps&lpar;打表DP&rpar;

    Problem Description Starting from point (0,0) on a plane, we have written all non-negative integers ...

  8. C语言-最后一次作业

    1.当初你是如何做出选择计算机专业的决定的? 经过一个学期,你的看法改变了么,为什么? 你觉得计算机是你喜欢的领域吗,它是你擅长的领域吗? 为什么? 我当初选择计算机专业是因为我是真的很向往计算机这方 ...

  9. netty中的传输

    终于在课设的闲时间把netty实战的四五章给解决了 这里来记录一下第四章里面所讲的IO 首先说到IO,我想,必须要先了解阻塞,非阻塞,同步和异步这四个词 看到一个讲的很易懂的例子:https://ww ...

  10. Python学习之旅(十三)

    Python基础知识(12):函数(Ⅲ) 高阶函数 1.map map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterat ...