TON(五)——main函数的基本初始操作

系列文章目录


开胃小菜——cons和pair

在 Fift 语言中,元组(Tuples)和列表(Lisp-style lists)是两种不同的数据结构,但只是语义上的不同,实际上两者底层是互通的。

元组是不可变的有序集合,可以包含任意数量和类型的值。在 Fift 中,元组的创建使用 tuple 操作,而访问元组中的元素则使用 untuple 操作(在你的描述中没有提到,但是它是处理元组的基本操作之一)。例如,创建一个包含两个元素的元组(也就是一个对):

2 3 pair

这将创建一个元组 (2, 3)

列表是 Fift 中用来表示序列的一种数据结构,它是由一系列元素组成的,每个元素都可以是任意类型。列表的末尾使用一个特殊的值 null 来表示结束,这是 Lisp 语言中的传统。在 Fift 中,列表的操作包括 cons(构造一个新的列表,将一个元素添加到另一个列表的前面),uncons(分解列表为头和尾),car(获取列表的第一个元素),cdr(获取列表的剩余部分),以及 list(创建一个新列表)。在 Fift 中,consuncons 被定义为 pairunpair 的别名,这意味着它们在功能上是等价的。但是,它们在语义上用于不同的数据结构:pairunpair 用于元组,而 consuncons 用于列表。

尽管 paircons 在功能上是等价的,但它们在语义上用于不同的数据结构。在 Fift 中,这种区分是通过上下文来实现的。当你使用 pair 时,你通常在处理元组;而当你使用 cons 时,你通常在处理列表。这种约定帮助程序员理解代码的意图,并且避免了操作的冲突。

示例代码

"main" 0 pair   @proclist @  cons  @proclist !

这段代码首先使用 pair 创建了一个包含字符串 "main" 和数字 0 的元组,然后使用 cons 将这个元组添加到一个名为 @proclist 的列表中。这里没有冲突,因为 paircons 在这里用于不同的上下文:pair 用于创建元组,而 cons 用于将元组添加到列表中。
但对于我们熟悉底层代码的人,我们知道这样的代码中pair和cons是可以随意互换的。

"main" 0  pair .s  @proclist @ .s pair .s @proclist !
"main" 0 cons  .s   @proclist @ .s pair ,s @proclist !
"main" 0 cons  .s   @proclist @ .s cons ,s @proclist !

以上几种情况都是等价的输出

[ "main" 0 ] 
[ "main" 0 ] (null) 
[ [ "main" 0 ] (null) ] 

一、main函数的信息

"main" 0 @proclistadd
  dictnew dup @procdict !
  @procinfo !  16 0 @procinfo!
} : PROGRAM{

这是main函数的基本信息,我们可以看出对于他来说@procdict @procinfo 都是空字典
在 @proclist 中放入了,(“main” 0)的元组,这便是一切程序的初始阶段 。接下来详细讲解16 0 @procinfo!

二、@procinfo!

{ <b rot 16 i, swap @procinfo @ @procdictkeylen b>idict!
  not abort"cannot add key to procedure info dictionary"
  @procinfo !
} : @procinfo!
  1. <b :这部分代码首先使用 <b 开始一个构建器build,在 Fift 语言中,<b 和 b> 是两个用于操作构建器(Builder)的原语(primitives)。构建器是一种可以动态构建数据结构的工具,它允许你逐步添加元素,最后生成一个新的单元(Cell)。
    <b:这个操作会创建一个新的空构建器,并将其推入栈中。构建器是一种可以容纳一系列数据的临时存储区域,你可以向其中添加数据,但还不能形成一个完整的单元。这个操作通常用于开始构建一个新的数据结构。b>:这个操作接受一个构建器作为输入,并将其转换成一个新的单元(Cell),这个单元包含了构建器中所有的数据。这个操作通常用于完成构建过程,将构建器中的所有数据固化到一个新的单元中。

  2. rot 操作,这通常用于旋转栈顶的三个元素的位置。

1 2 3 rot -> 2 3 1

故 16 0 <b rot
0 <b 16

  1. 16 i, 这里也是fift最麻烦的地方 储存类型的以及字节大小的相关定义,如

    1. i (b x y – b′)
    • 这个操作用于将一个有符号的 y 位整数 x 以大端序二进制形式追加到构建器 b 中。
    • x 的范围是从 -2^(y-1)2^(y-1)-1,其中 0 ≤ y ≤ 257
    • 如果构建器 b 中没有足够的空间来存储这个整数(即如果 b 已经包含超过 1023 - y 个数据位),或者整数 x 不能用 y 位表示,则会抛出一个异常。
    • b′ 是修改后的构建器,包含了追加的整数。
    1. u, (b x y – b′)
    • 这个操作用于将一个无符号的 y 位整数 x 以大端序二进制形式追加到构建器 b 中。
    • x 的范围是从 02^y-1,其中 0 ≤ y ≤ 256
    • 如果操作不可能执行(例如,由于空间不足或整数大小不合适),则会抛出一个异常。
    • b′ 是修改后的构建器,包含了追加的整数。
    1. ref, (b c – b′)
    • 这个操作用于将单元 c 的引用追加到构建器 b 中。
    • 如果 b 已经包含了四个引用,则会抛出一个异常,因为构建器通常限制了可以存储的引用数量。
    • b′ 是修改后的构建器,包含了追加的引用。
    1. s, (b s – b′)
    • 这个操作用于将切片 s 中的数据位和引用追加到构建器 b 中。
    • s 是一个包含数据位和引用的切片(Slice)。
    • b′ 是修改后的构建器,包含了追加的数据和引用。
    1. sr, (b s – b′)
    • 这个操作用于从切片 s 构建一个新的单元,并将这个新单元的引用追加到构建器 b 中。
    • 这相当于先使用 <b 创建一个新的空构建器,然后使用 s 中的数据和引用填充这个构建器,接着使用 b> 将填充好的构建器转换成一个新的单元,最后将这个新单元的引用追加到原始构建器 b 中。
    • b′ 是修改后的构建器,包含了新单元的引用。
    1. $, (b S – b′)
    • 这个操作用于将字符串 S 追加到构建器 b 中。
    • 字符串 S 被解释为一个长度为 8n 的二进制字符串,其中 nS 的 UTF-8 表示中的字节数。
    • 这意味着每个 UTF-8 字节都被转换为一个 8 位的二进制表示,并追加到构建器中。
    • b′ 是修改后的构建器,包含了追加的字符串。

所以,我们的程序就是要装入一个16位的有符号整数x,2^ 15 < x <2^ 15

  1. not abort"cannot add key to procedure info dictionary":如果 idict! 操作失败(即返回了 0),则执行 abort 操作,这是一个错误处理操作,用于在执行失败时终止程序,并输出错误信息 “cannot add key to procedure info dictionary”。

  2. 接下来我们来详细描述一下字典的封闭元语
    在 Fift 语言中,字典(dictionary)是一种重要的数据结构,用于存储键值对。以下是您提到的几个与字典操作相关的原语(primitives)的详细解释:

    1. dictnew ( – D)
    • 这个操作会创建一个新的空字典,并将其作为 Null 值推入栈中。这个空字典随后可以被用来添加键值对。
    1. idict! (v x D n – D′ −1 or D 0)
    • 这个操作会将一个由切片表示的新值 v 和一个有符号的大端序 n 位整数键 x 添加到字典 D 中。
    • 如果操作成功,返回新的字典 D′-1 作为成功的标志。
    • 如果操作失败(例如,如果键已存在或字典空间不足),则返回未改变的字典 D0
    • n 位键的长度,其中 0 ≤ n ≤ 1023
    1. idict!+ (v x D n – D′ −1 or D 0)
    • 这个操作与 idict! 类似,但它在尝试添加键值对时,如果键已经存在于字典中,则会失败,返回未改变的字典 D0
    1. b>idict!, b>idict!+
    • 这些操作是 idict!idict!+ 的变体,它们接受构建器(Builder)中的新值 v 而不是切片。
    • 构建器是一种临时存储区域,可以动态构建数据结构,如字典。这些操作允许你将构建器中的数据直接添加到字典中。这些操作提供了一种灵活的方式来操作字典,允许程序员在 Fift 程序中存储和管理键值对。字典在 Fift 中通常用于存储配置数据、状态信息或其他需要快速查找的数据结构。
      在实际使用中,这些操作可能会涉及到对字典的序列化和反序列化,以及对字典中键值对的遍历和查询。例如,你可以使用 idict! 来添加一个新的键值对到字典中,或者使用 idict!+ 来确保不会覆盖已有的键。如果需要将构建器中的数据添加到字典中,可以使用 b>idict!b>idict!+ 操作。
  3. @procinfo !:如果 idict! 操作成功,将更新后的 @procinfo 变量的值存储回 @procinfo

x y  <b rot 16 i, swap @procinfo @ @procdictkeylen b>idict!

我们可以看到此时此刻是一个装有16数据x的build作为value,@procdictkeylen长度的y作为key,他们一起被放入dict 字典中。

  1. 最后的处理
not abort"cannot add key to procedure info dictionary"  @procinfo !

这段代码的目的是尝试将一个新的键值对添加到 @procinfo 字典中。如果添加失败(可能是因为键已存在),则程序将终止并输出错误信息。如果添加成功,则更新 @procinfo 变量的值。

猜你喜欢

转载自blog.csdn.net/zhuqiyua/article/details/142879250