欢迎来到 Factor!Factor 是一门拼接式语言:不用像 square(add(2, 3)) 那样调用函数,而是从左到右写出一串单词,比如 2 3 + square,它们通过一个共享的数据栈互相传递值。想象码头上一摞货箱,最新的放在最顶上。
! 开始一行注释,Factor 会忽略从 ! 到行尾的所有内容。下面的例子用 ! 来标注代码的作用。
写下一个字面量,就会把它压到栈顶。代码从左到右读取。
1 2 3 ! stack (bottom → top): 1 2 3
每个单词都从栈顶读取自己的输入,再把输出写回那里。. 弹出栈顶的值并打印它;.s 会打印整个栈,不消耗任何东西,方便查看一段代码留下了什么。
每个单词都用形如 ( inputs -- outputs ) 的栈效应来记录。这是单词的契约:单词从栈顶弹出输入,并在原处留下输出。里面的名字是写给人看的说明,栈本身是按位置排列的,没有名字。栈顶对应的是最右边的那个名字。
: 开始一个单词定义,接下来是栈效应,然后是主体,最后用 ; 结束。Factor 的编译器会检查主体是否真的留下栈效应所声明的内容。
: restow ( a b -- b a ) swap ;
这些 kernel 单词重新排列栈顶,不做任何算术运算,只是把货箱搬来搬去:
dup ( x -- x x ) ! copy the top crate
drop ( x -- ) ! remove the top crate
swap ( x y -- y x ) ! flip the top two
over ( x y -- x y x ) ! copy the second crate onto the top
nip ( x y -- y ) ! remove the second crate
rot ( x y z -- y z x ) ! roll the third crate up to the top
当一个货箱要同时供给两个单词时用 dup,想扔掉一个货箱时用 drop,栈顶两个货箱顺序不对时用 swap,要处理下面的货箱时用 over 或 nip,要把埋在下面的货箱翻到最顶上时用 rot。
更广阔的家族(-rot、dupd、swapd、2dup 等)遵循同样的思路,稍后你会遇到这些单词。
排成一行的单词按从左到右的顺序运行:每个单词都作用于前一个单词留下的栈。下面看看 swap nip 如何一步一步地作用于栈 1 2(底 → 顶):
1 2 ! 1 2 the starting crates
swap ! 2 1 swap flipped the top two
nip ! 1 nip dropped the 2nd-from-top crate (the 2)
再看 dup rot 作用于同样的初始货箱,同样一次一个单词:
1 2 ! 1 2 the starting crates
dup ! 1 2 2 dup copied the top crate
rot ! 2 2 1 rot rolled the 3rd-from-top crate up to the top
单词采用 lowercase-kebab-case:由连字符连接的小写字母。
你是码头上负责重新堆装货箱的甲板水手。每个任务都是一个单独的 词,它的定义体只用重排词,不做任何算术。
定义 swap-crates 词。它从栈上取走两个货箱,再以相反的顺序把它们留在栈上。
1 2 swap-crates .s
! 2
! 1
定义 clear-spill 词。它从栈上取走三个货箱,把下面两个留下,丢弃最上面那个。
1 2 3 clear-spill .s
! 1
! 2
定义 peek-under 词。它从栈上取走两个货箱,把它们留在栈上,并在最上面放一份下面那个货箱的副本。
1 2 peek-under .s
! 1
! 2
! 1
定义 tidy-deck 词。它从栈上取走三个货箱(顺序为 x y z),然后留下 z z y:丢弃最下面的货箱,再让最上面货箱的两份副本位于中间那个货箱的下方。
1 2 3 tidy-deck .s
! 3
! 3
! 2