Forth言語インタプリタ
実装演習

スタックベース言語とインタプリタの仕組み

2025年11月26日 伊澤侑祐

本日の内容

  1. Forth とは
  2. 動的言語とインタプリタ
  3. スタックとForthでの使い方
  4. 参照実装
  5. 演習問題

1. Forth言語とは

歴史と背景

  • 1970年代: Charles H. Moore が開発
  • もともと天文台の望遠鏡制御のために設計
  • その後、様々な分野で利用:
    • 組み込み・制御システム
    • 宇宙開発
    • PostScript (印刷技術・PDFレンダリング)

Forthの特徴

1. スタックベースの計算

すべての演算はスタック(LIFO)を介して行われる

2. 逆ポーランド記法(RPN)

通常の記法 Forth
(3 + 4) * 2 3 4 + 2 *

3. 単純で拡張可能

基本的な「ワード」の組み合わせで新しいワードを定義

なぜForthを学ぶのか?

  1. インタプリタの基礎
    シンプルな構文で実装原理を学びやすい
  2. スタック機械の理解
    Java VM、Python VM、WebAssembly などで採用
  3. 言語設計の思想
    最小限の機能で最大限の表現力

2. 動的言語とインタプリタ

プログラムの実行方式

コンパイラ

ソースコード
コンパイラ
機械語(実行ファイル)
実行結果
例: C, C++, Rust, Go

インタプリタ

ソースコード
インタプリタ
実行結果
(読みながら即座に実行)
例: Python, Ruby, JavaScript, Forth

Forth インタプリタの基本構造

字句解析

"3 4 + 2 *"
字句解析
Lexical Analysis
"3"
"4"
"+"
"2"
"*"
Forthの場合: 字句解析は非常にシンプル
→ 空白で文字列を分割するだけ! code.split()

Forthインタプリタの基本構造

トークンに対してディスパッチループ (Dispatch Loop) を行う

READ
トークンを
1つ取得
EVAL
(評価)
↩︎ 繰り返し
↓ EVAL の際にトークンの種類で分岐 (Dispatch)
数値?
→ プッシュ
演算子?
→ 演算実行
ワード?
→ 定義実行

Pythonでの実装パターン


def forth_eval(stack, code):
    tokens = code.split()  # 字句解析
    
    for token in tokens:   # 評価ループ
        if 数値なら:
            スタックにプッシュ
        elif 演算子なら:
            スタックから値を取り出して演算
        elif 定義済みワードなら:
            そのワードの本体を実行
                    

動的言語の特徴

1. 実行時に型が決まる(動的型付け)


x = 10        # xは整数
x = "hello"   # 同じxが文字列に(エラーなし)
                        

2. 実行時にプログラムを変更できる


: SQUARE DUP * ;   ( 実行中に新しい命令を定義 )
5 SQUARE .         ( すぐに使える → 出力: 25 )
                        

3. 対話的な開発が可能(REPL)


> 3 4 +     ← 入力
7           ← 即座に結果
                        

3. スタックとForthでの使い方

(先週の復習)

スタックの基本操作

  • push: 値を追加
  • pop: 値を取り出す
  • peek: 値を見る(取り出さない)
push(1): [1]
push(2): [1, 2]
push(3): [1, 2, 3] ← top
pop(): [1, 2] → 3を取得
pop(): [1] → 2を取得

Forthでの計算: 3 4 +

入力 操作 スタック
3 3をプッシュ [3]
4 4をプッシュ [3, 4]
+ 2つポップして加算、結果をプッシュ [7]

Forthでの計算: 3 4 + 2 *

通常の記法: (3 + 4) × 2 = 14

入力 スタック
3 [3]
4 [3, 4]
+ [7]
2 [7, 2]
* [14]

スタック操作関数(先週の復習)


def stack_new():
    return []

def stack_push(stack, value):
    stack.append(value)

def stack_pop(stack):
    if len(stack) == 0:
        raise IndexError("Stack underflow")
    return stack.pop()

def stack_peek(stack):
    if len(stack) == 0:
        raise IndexError("Stack underflow")
    return stack[-1]
                    

4. 参照実装

数値と四則演算のみの最小限の実装

基本的なインタプリタ


def forth_eval(stack, code):
    """
    Forthコードを評価する (最小実装)
    """
    tokens = code.split()  # 字句解析
    
    for token in tokens:   # 評価ループ
        # 数値の場合:スタックにプッシュ
        try:
            num = int(token)
            stack_push(stack, num)
            continue
        except ValueError:
            pass

        # 演算子の場合
        if token == '+':
            b = stack_pop(stack)
            a = stack_pop(stack)
            stack_push(stack, a + b)
        elif token == '-':
            b = stack_pop(stack)
            a = stack_pop(stack)
            stack_push(stack, a - b)
        elif token == '*':
            b, a = stack_pop(stack), stack_pop(stack)
            stack_push(stack, a * b)
        elif token == '/':
            b, a = stack_pop(stack), stack_pop(stack)
            stack_push(stack, a // b)
                    

動作確認


>>> stack = stack_new()
>>> forth_eval(stack, "3 4 +")
>>> print(stack)
[7]

>>> stack = stack_new()
>>> forth_eval(stack, "3 4 + 2 *")
>>> print(stack)
[14]

>>> stack = stack_new()
>>> forth_eval(stack, "10 3 -")
>>> print(stack)
[7]
                    

5. 演習問題

課題1: スタック操作ワード

ワード 動作 スタック変化
. トップを出力して削除 (a → )
DUP トップを複製 (a → a a)
DROP トップを削除 (a → )
SWAP 上位2つを交換 (a b → b a)
OVER 2番目をコピー (a b → a b a)
ROT 3番目をトップへ (a b c → b c a)

課題2: ユーザ定義ワード

: NAME ... ; で新しいワードを定義


: SQUARE DUP * ;     ( 2乗を計算するワードを定義 )
5 SQUARE .           ( 25を出力 )

: DOUBLE DUP + ;
: QUADRUPLE DOUBLE DOUBLE ;
5 QUADRUPLE .        ( 20を出力 )
                    
ポイント: 辞書(dictionary)にワード名と本体を保存

発展課題

  • 発展1: 比較演算子(<, >, =
  • 発展2: 追加のスタック操作(2DUP, NIP, TUCK
  • 発展3: コメント機能(( ... )

まとめ

  • インタプリタ: 字句解析 ⇢ ディスパッチループ ⇢ 結果
  • 動的言語: 実行時に拡張可能
  • Forth:
    • プログラム: 逆ポーランド記法
    • 処理系: スタックをデータ処理に用いるインタプリタ
今週の目標: Forthインタプリタを自分で実装する
  • 残り時間: ノートブックを開いて演習課題に取り組む
  • 実装したノートブックを締切までに提出