はじめに
この記事は、forth Advent Calendar 2024 の15日目の記事となります。
アドベントカレンダのリストを見ていて、今、forthに初めて来た者の記録です。
基本事項
forthってなに? というわけでChatGPT先生に聞いてみる。
実行したい
おお。これはお手軽に試せる。
: SQUARE ( n -- n^2 ) DUP * ;
5 SQUARE .
https://tio.run/#forth-gforth
で実行してみよう。
https://tio.run/##S8svKsnQTU8DUf//WykEB4Y6BrkqaCjkKejqKuTFGSloKriEBihoKVhzmcJk9bj@/w![スクリーンショット 2024-12-20 10.50.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1032713/c574263a-2e7f-b22e-7d30-f29fc7120774.png)
cA
ふむふむ。
スタックの動きって頭の中でどうすればわかりやすいのだろう。
慣れるとできるようになるのか。練習していけばスラスラできるのかな・・。
forth1の処理系を作ってもらう
無茶振り!
<!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>
ほんとうに動くのか?
ブラウザに読み込んで、「使用例」を試してみる。
おー、すごい。
終わりに
歴史あるforthは頭の体操・ボケ防止に効果がありそうと思いました。
色々試す過程で、WASMでできた、もっと本格的な処理系も見つけました。
以上、「ChatGPT先生にforthを教えてもらう」でした。
最後までご覧いただきましてありがとうございました。