ChatGPT先生にforthを教えてもらう

# はじめに
この記事は、[forth Advent Calendar 2024](https://qiita.com/advent-calendar/2024/forth) の15日目の記事となります。

アドベントカレンダのリストを見ていて、今、forthに初めて来た者の記録です。

# 基本事項
forthってなに？　というわけでChatGPT先生に聞いてみる。

![スクリーンショット 2024-12-20 10.37.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/377a08e2-501a-ea41-9e84-071d935901dc.png)
![スクリーンショット 2024-12-20 10.37.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/e1d3995f-5802-c351-8969-5e924470b1de.png)

# 実行したい
![スクリーンショット 2024-12-20 10.39.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/ffdc7c25-f5d0-a7a1-7b84-7000cd978f4a.png)

![スクリーンショット 2024-12-20 10.40.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/f5b44028-adcf-f274-3d38-0f2cc7723602.png)

おお。これはお手軽に試せる。

https://tio.run/#forth-gforth

```forth:1
: SQUARE ( n -- n^2 ) DUP * ;
5 SQUARE .
```

![スクリーンショット 2024-12-20 10.43.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/e7d22fc6-57ae-8284-6bdf-d6f134c659f8.png)

https://tio.run/#forth-gforth
で実行してみよう。

https://tio.run/##S8svKsnQTU8DUf//WykEB4Y6BrkqaCjkKejqKuTFGSloKriEBihoKVhzmcJk9bj@/w

![スクリーンショット 2024-12-20 10.44.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/d07736b5-1ecd-f331-4ff9-0dd0918802cd.png)


![スクリーンショット 2024-12-20 10.45.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/3250819b-7090-7c3c-cb1b-e4db6468bcdc.png)

ふむふむ。

https://tio.run/##S8svKsnQTU8DUf//GyoYKWgr6HH9/w8A

![スクリーンショット 2024-12-20 10.47.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/5c87f908-a580-260d-7ae0-1a92ac04248b.png)

スタックの動きって頭の中でどうすればわかりやすいのだろう。

![スクリーンショット 2024-12-20 10.50.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/e099db69-4ff3-bf29-a9f0-adaaaadca28d.png)

![スクリーンショット 2024-12-20 10.51.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/c358db5f-8ee9-38d3-5c8f-0ea55947e237.png)

![スクリーンショット 2024-12-20 10.52.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/ecbc7572-5729-6b94-3b0a-2ab4dfa2355d.png)
![スクリーンショット 2024-12-20 10.52.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/065953b4-71e8-c136-b797-a00484bd631f.png)
![スクリーンショット 2024-12-20 10.52.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/f46c998a-5a3a-bbe8-cdce-1598e4880e24.png)
![スクリーンショット 2024-12-20 10.52.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/140f733a-6cec-5781-ff9f-a429f742434c.png)
![スクリーンショット 2024-12-20 10.52.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/4a2019cc-bbee-980a-6c96-59ef99c3d92e.png)
![スクリーンショット 2024-12-20 10.52.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/da392922-b54f-cb74-aaf0-be0115c96fa4.png)
![スクリーンショット 2024-12-20 10.53.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/42046e73-0eaa-b4d8-0afd-b0a95c03ba89.png)
![スクリーンショット 2024-12-20 10.53.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/c1b6ede6-4b3f-3bde-1ed1-d7aa836c6faf.png)
![スクリーンショット 2024-12-20 10.53.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/d9fe3862-88cd-9c31-2265-83e27f78dc14.png)
![スクリーンショット 2024-12-20 10.53.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/767a2ad8-0a78-5a92-e6e4-5c7f6bb77ed5.png)
![スクリーンショット 2024-12-20 10.53.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/cf5b8316-20ef-fd20-3f4d-c819734d8d64.png)
![スクリーンショット 2024-12-20 10.53.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/e5aa9391-8810-45f2-a54e-c89689f9f878.png)
![スクリーンショット 2024-12-20 10.54.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/a0262c1a-5cc5-8625-7957-7810eeccf02b.png)
![スクリーンショット 2024-12-20 10.54.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/54b9062e-67a2-49dc-b510-50231f645f4b.png)

慣れるとできるようになるのか。練習していけばスラスラできるのかな・・。

# forth1の処理系を作ってもらう

![スクリーンショット 2024-12-20 11.03.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/9ddfd601-436e-4d43-dd46-69bac9b88c91.png)

無茶振り！

![スクリーンショット 2024-12-20 11.04.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/a1adaeb4-8a77-fa01-5d0f-7d7460a0be5b.png)

```html:forth1.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>ブラウザ上のForthインタープリタ</title>
    <style>
        body {
            font-family: monospace;
            background-color: #2e2e2e;
            color: #f1f1f1;
            padding: 20px;
        }
        #output {
            width: 100%;
            height: 300px;
            background-color: #1e1e1e;
            color: #f1f1f1;
            padding: 10px;
            overflow-y: scroll;
            white-space: pre-wrap;
            border: 1px solid #555;
            margin-bottom: 10px;
        }
        #input {
            width: 100%;
            padding: 10px;
            background-color: #1e1e1e;
            color: #f1f1f1;
            border: 1px solid #555;
        }
    </style>
</head>
<body>
    <h1>ブラウザ上のForthインタープリタ</h1>
    <div id="output"></div>
    <input type="text" id="input" placeholder="Forthコードを入力してEnterを押してください">
    
    <script>
        class ForthInterpreter {
            constructor(outputCallback) {
                this.stack = [];
                this.words = {
                    '.': () => {
                        if (this.stack.length < 1) {
                            throw new Error("Stack underflow for '.'");
                        }
                        const value = this.stack.pop();
                        outputCallback(String(value));
                    },
                    'dup': () => {
                        if (this.stack.length < 1) {
                            throw new Error("Stack underflow for 'dup'");
                        }
                        const value = this.stack[this.stack.length - 1];
                        this.stack.push(value);
                    },
                    'drop': () => {
                        if (this.stack.length < 1) {
                            throw new Error("Stack underflow for 'drop'");
                        }
                        this.stack.pop();
                    },
                    'swap': () => {
                        if (this.stack.length < 2) {
                            throw new Error("Stack underflow for 'swap'");
                        }
                        const a = this.stack.pop();
                        const b = this.stack.pop();
                        this.stack.push(a);
                        this.stack.push(b);
                    },
                    '+': () => {
                        if (this.stack.length < 2) {
                            throw new Error("Stack underflow for '+'");
                        }
                        const a = this.stack.pop();
                        const b = this.stack.pop();
                        this.stack.push(b + a);
                    },
                    '-': () => {
                        if (this.stack.length < 2) {
                            throw new Error("Stack underflow for '-'");
                        }
                        const a = this.stack.pop();
                        const b = this.stack.pop();
                        this.stack.push(b - a);
                    },
                    '*': () => {
                        if (this.stack.length < 2) {
                            throw new Error("Stack underflow for '*'");
                        }
                        const a = this.stack.pop();
                        const b = this.stack.pop();
                        this.stack.push(b * a);
                    },
                    '/': () => {
                        if (this.stack.length < 2) {
                            throw new Error("Stack underflow for '/'");
                        }
                        const a = this.stack.pop();
                        const b = this.stack.pop();
                        if (a === 0) {
                            throw new Error("Division by zero");
                        }
                        this.stack.push(Math.floor(b / a));
                    }
                };
                this.definitions = {}; // ユーザー定義の単語
            }

            defineWord(name, definition) {
                this.words[name] = definition;
            }

            execute(tokens) {
                for (let token of tokens) {
                    if (/^-?\d+$/.test(token)) { // 整数リテラル
                        this.stack.push(parseInt(token, 10));
                    } else {
                        const word = this.words[token.toLowerCase()];
                        if (word) {
                            word();
                        } else if (this.definitions[token.toLowerCase()]) {
                            this.execute(this.definitions[token.toLowerCase()]);
                        } else {
                            throw new Error(`未知の単語: ${token}`);
                        }
                    }
                }
            }

            interpret(input) {
                const lines = input.split('\n');
                for (let line of lines) {
                    line = line.trim();
                    if (line === '' || line.startsWith('\\')) continue; // 空行やコメントを無視
                    const tokens = line.split(/\s+/);
                    // 簡単な定義処理
                    if (tokens[0].toLowerCase() === ':') {
                        const endIndex = tokens.indexOf(';');
                        if (endIndex === -1) {
                            throw new Error("';' が見つかりません");
                        }
                        const name = tokens[1].toLowerCase();
                        const definition = tokens.slice(2, endIndex).map(t => t.toLowerCase());
                        this.definitions[name] = definition;
                    } else {
                        this.execute(tokens);
                    }
                }
            }
        }

        const outputDiv = document.getElementById('output');
        const inputBox = document.getElementById('input');

        const interpreter = new ForthInterpreter((output) => {
            outputDiv.textContent += output + '\n';
            outputDiv.scrollTop = outputDiv.scrollHeight;
        });

        inputBox.addEventListener('keydown', (e) => {
            if (e.key === 'Enter') {
                const code = inputBox.value;
                inputBox.value = '';
                outputDiv.textContent += `> ${code}\n`;
                try {
                    interpreter.interpret(code);
                } catch (err) {
                    outputDiv.textContent += `Error: ${err.message}\n`;
                }
                outputDiv.scrollTop = outputDiv.scrollHeight;
            }
        });

        // 初期メッセージ
        outputDiv.textContent = "Forth インタープリタへようこそ！\n";
        outputDiv.textContent += "単語を入力してEnterを押してください。\n";
    </script>
</body>
</html>

```
![スクリーンショット 2024-12-20 11.05.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/5369d556-41a5-6fc9-c188-22cf3e6742a0.png)
![スクリーンショット 2024-12-20 11.06.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/8bfbf6f9-58c7-f2aa-8c32-e23fa8fce8ab.png)
![スクリーンショット 2024-12-20 11.06.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/eef2a769-2279-5190-ec7c-18847a1c2774.png)
![スクリーンショット 2024-12-20 11.06.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/247171d4-9f1d-383e-5366-2769661135dc.png)
![スクリーンショット 2024-12-20 11.06.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/daeb6cb2-9d13-99cc-b7ee-7482ee7404ee.png)
![スクリーンショット 2024-12-20 11.06.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/4f0e3569-35ad-0905-f6bf-c8c32855656b.png)

ほんとうに動くのか？
ブラウザに読み込んで、「使用例」を試してみる。

![スクリーンショット 2024-12-20 11.08.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/9f324d20-7b50-a743-b1c3-f77c740d98a8.png)

おー、すごい。


# 終わりに

歴史あるforthは頭の体操・ボケ防止に効果がありそうと思いました。

色々試す過程で、WASMでできた、もっと本格的な処理系も見つけました。

https://github.com/remko/waforth


以上、「ChatGPT先生にforthを教えてもらう」でした。

最後までご覧いただきましてありがとうございました。