构建你自己的Forth解释器
Build Your Own Forth Interpreter

原始链接: https://codingchallenges.fyi/challenges/challenge-forth/

## Forth 类似解释器挑战总结 这个编程挑战要求你构建一个 Forth 编程语言子集的解释器。Forth 是一种基于栈的语言,具有独特的逆波兰表示法 (RPN) 语法,历史上曾被用于游戏开发和嵌入式系统等领域。 挑战分为几个步骤,从基本的读取-求值-打印循环 (REPL) 开始,逐步实现整数运算、栈操作(复制、删除、交换)、输出命令(.、emit、cr),以及关键的定义新“词”(子程序)的能力。 后续步骤引入条件语句(if/then/else)和循环结构(do 循环)。 你可以选择你喜欢的编程语言和开发环境——命令行、基于 Web 或 GUI。最终目标是能够运行类似 Forth 的代码,包括生成斐波那契数列和实现 FizzBuzz 等示例。提供了“Starting FORTH”和“Easy Forth”等资源来帮助学习。鼓励分享你的解决方案并为社区做出贡献!

## Forth 解释器与 NES 开发 - Hacker News 摘要 一个 Hacker News 讨论围绕着构建 Forth 解释器展开,起因是一位用户正在尝试制作自制 NES 游戏。面对汇编语言的复杂性,该用户探索了 Forth 作为一种更高级的替代方案,最初使用了 IceForth 和 Codex。Codex 提供了令人惊讶的良好性能(80% 的汇编速度,代码量更少),但用户现在正在构建自定义 Forth 解释器,作为学习经验并根据自身需求进行定制。 该帖子强调了 Forth 独特的能够在不同层面上运行的能力——从类似于 Lisp 的高级脚本,到与汇编语言的直接交互。 几位评论者分享了他们自己的 Forth 实现,包括一个针对 6809 处理器的实现,以及另一个针对 MSP430 微控制器的实现。 讨论还涉及构建解释器的教育价值,建议使其自举,并辩论了实现 Forth 与 Lisp 的相对难度。 一些评论者挑战了 Forth 对初学者来说很困难的看法,提倡它的简单性,尤其是在虚拟 CPU/架构开发等领域。
相关文章

原文

This challenge is to build your own Forth-like interpreter. Forth is a stack-oriented programming language that was designed by Charles H. "Chuck" Moore and first used by other programmers in 1970. It gained an official standard in 1994. Although it’s not an acronym, for much of it like Forth was referred to as FORTH. Why Forth and not Fourth? Here’s why:

“The first time I combined the ideas I had been developing into a single entity, I was working on an IBM 1130, a “third-generation” computer. The result seemed so powerful that I considered it a “fourth-generation computer language.” I would have called it FOURTH, except that the 1130 permitted only five-character identifiers. So FOURTH became FORTH, a nicer play on words anyway.”

— Charles H. Moore, creator of Forth

Forth has been used to write bestselling computer games, firmware, spaceflight software and various embedded systems. For us it’s a fun language to implement an interpreter for and it gives you a chance to learn about Reverse Polish Notation (RPN), stack oriented-programming and writing interpreters.

This challenge is to build your own Forth-like interpreter. That is an interpreter for a subset of the Forth language, sufficient for you to write and run code to generate the Fibonacci sequence and the coding interview favourite, FizzBuzz.

You can build a Forth-like interpreter in any programming language or stack. You could build it as a CLI tool or as an in-browser interpreter with a web based IDE. So this coding challenge will suit frontend, backend, fullstack, data engineer, system, or whatever type of software engineer you consider yourself to be.

Me, I built it as a CLI tool just like the other compilers and interpreters I’ve built.

Step Zero

As always, in a throwback to the days when I wrote C, Coding Challenges is zero indexed. In this step your goal is to:

  1. Decide if you’re building:
    1. A web-based solution and if so, is it frontend only or fullstack?
    2. Decide if you’re building a desktop or mobile application complete with a GUI.
    3. Decide if you’re building a CLI tool, like me. 🙂
  2. Decide what programming language you’re going to build your solution in.
  3. Learn some Forth!

When it comes to learning Forth, there are two good places to start:

  • Starting FORTH, the classic FORTH tutorial by Leo Brodie.
  • Easy Forth an online ebook with integrated interpreter letting you try out the code right there in the book.

N.B.: If you want to go deeper there is a more in depth set of resources on the Coding Challenges blog post: learning Forth.

Step 1

In this step your goal is to build a simple REPL (Read-Eval-Print Loop) and allow the user to terminate the REPL by entering the Forth word: bye. In Forth, subroutines are referred to as ‘words’.

The typical Forth prompt in the REPL is: ok>

So when you’ve completed this step, using your interpreter will look something like this:

Not very exciting is it, so let’s add enough Forth for us to do simple calculations.

Step 2

In this step your goal is to handle the user entering integers and some basic mathematical operations. To enable that you need to handle entering an integer. The integer should then be pushed onto the interpreter’s data stack - remember Forth is stack based.

In Forth, anything between brackets is a comment, so words are often described using comments, for example:

WordCommentDescription
+( n1 n2 -- sum )Pops the top two elements (n1 and n2) on the stack, pushes the sum on to the top of the stack
-( n1 n2 -- diff )Pops the top two elements on the stack, subtracts n2 from n1 stores the result on the top of the stack
*( n1 n2 -- multiplied )Pops the top two elements on the stack, pushes the product on to the top of the stack
/( n1 n2 -- divided )Pops the top two elements on the stack, pushes the result of n2 / n1 on to the top of the stack
mod( n1 n2 -- modulus )Pops the top two elements on the stack, pushes the remainder of n2 / n1 on to the top of the stack

You should add support for all these operations. To test them you’ll need to also implement a feature of most Forth REPLs, showing the stack from bottom to top before the prompt. For example:

% ./ccforth
ok> 1 2 2 + +
5 ok>

Here the integers 1,2, and 2 are pushed onto the stack, then the operations + and + are run. The result of each addition is pushed onto the stack, resulting in the stack containing 5. This could also have been done in stages:

% ./goforth
ok> 1
1 ok> 2
1 2 ok> 2
1 2 2 ok> +
1 4 ok> +
5 ok>

Leaving the result, 5 on the top of the stack.

Step 3

In this step your goal is to add support for several Forth words used to manipulate the stack: dup, drop, rot, over and swap:

WordCommentDescription
swap( n1 n2 -- n2 n1 )Swaps the top two elements on the stack
dup( n -- n n )Duplicates the top element on the stack
over( n1 n2 -- n1 n2 n1 )Duplicates the second from top element and pushes it on to the top of the stack
rot( n1 n2 n3 -- n2 n3 n1 )Rotates the top three elements on the stack
drop( n1 -- )Pops the top element off the stack

Be sure to test them.

Step 4

In this step your goal is to implement support for four new words:

WordCommentDescription
.( n1 -- )Prints and pops the top of the stack
emit( n1 -- )Prints the top of the stack as n ASCII character and pops the top of the stack
cr( -- )Prints a newline
."( -- )Prints the string from after the space to the ending quote, i.e. ." hello" prints "hello"

Using them looks like this:

ok> 5 . cr
5
ok> 78 72 79 74 emit emit emit emit cr
JOHN
ok> ." Hello, Coding Challenges" cr
Hello, Coding Challenges
ok>

Step 5

In this step your goal is to implement support for defining new words. In Forth words are defined between : and ; and then called using the word, for example to define a new word say-hi, and then call it:

ok> : say-hi ." Hi, John" cr ;
ok> say-hi
Hi, John
ok>

You should also handle calling a word that doesn’t exist. For that simply print the word and a question mark:

Finally add support for comments. Comments are any text between brackets and are not interpreted:

ok> ( this is a comment )
ok> : hi ( says hi ) ." Hi!" cr ;
ok> hi
Hi!
ok>

Step 6

In this step your goal is to add support for conditionals and loops. The conditionals are:

WordCommentDescription
<( n1 n2 -- -1/0 )Pops the top two elements on the stack, checks if n1 is less than n2, pushes -1 if it is otherwise 0
>( n1 n2 -- -1/0 )Pops the top two elements on the stack, checks if n1 is greater than n2, pushes -1 if it is otherwise 0
=( n1 n2 -- -1/0 )Pops the top two elements on the stack, checks if n1 is equal to n2, pushes -1 if it is otherwise 0
<>( n1 n2 -- -1/0 )Pops the top two elements on the stack, checks if n1 is not equal to n2, pushes -1 if it is otherwise 0
and( n1 n2 -- -1/0 )Pops the top two elements on the stack and if both are true pushes -1, otherwise 0
or( n1 n2 -- -1/0 )Pops the top two elements on the stack and if either is true pushes -1, otherwise 0
invert( n1 -- -1/0 )Pops the top element on the stack and pushes the boolean negation

Once you have them you should be able to implement if then blocks:

: buzz 5 mod 0 = if ." Buzz" then ; 5 buzz

And if else then blocks:

5 3 = if ." Equal" else ." Not Equal" then

Then move on to adding support for do loop:

ok> : loop5 5 0 do ." Test" cr loop ; loop5
Test
Test
Test
Test
Test
ok>

Don’t forget to refer to the Forth book for full details of the syntax if you need clarification.

Step 7

In this step your goal is to support running Forth-like code from a script file, with the name provided as an argument to the interpreter. Here are two scripts you should be able to run with the language features built so far:

: fib over over + ;  ( generate the next number in the Fibonacci sequence )
: fibn 10 1 do fib dup . loop ;
0 dup . 1 dup . fibn ( generate the next 10 numbers )
cr

To generate the Fibonacci sequence and FizzBuzz:

: fizz  3 mod 0 = dup if ." Fizz" then ;
: buzz 5 mod 0 = dup if ." Buzz" then ;
: fizz-buzz dup fizz swap buzz or invert ;
: do-fizz-buzz 25 1 do cr i fizz-buzz if i . then loop ;
do-fizz-buzz

Once that works, have some fun writing your own Forth code to run in your interpreter!

Going Further

If you’d like to take this further, work through the resources on learning Forth and read the Forth standard, then add the new features that would be of interest to you. You could also explore compiling the code, either directly or leveraging the LLVM backend.

Help Others by Sharing Your Solutions!

If you think your solution is an example other developers can learn from please share it, put it on GitHub, GitLab or elsewhere. Then let me know - ping me a message on the Discord Server, via Twitter or LinkedIn or just post about it there and tag me. Alternately please add a link to it in the Coding Challenges Shared Solutions Github repo.

Get The Challenges By Email

If you would like to receive the coding challenges by email, you can subscribe to the weekly newsletter on SubStack here:

联系我们 contact @ memedata.com