# アーキテクチャ設計書

## 概要

本ドキュメントは、CoffeeScript コンパイラ（バージョン 2.7.0）のアーキテクチャ設計を記載したものです。CoffeeScript は JavaScript へコンパイルされる小さなプログラミング言語であり、本プロジェクトはそのコンパイラ本体のソースコードです。コンパイラは Node.js 環境およびブラウザ環境の両方で動作し、コマンドラインツール（`coffee` / `cake`）、REPL、require フックなど複数のエントリーポイントを提供します。

## システム全体構成

### アーキテクチャ概要図

[アーキテクチャ構成図.md](./アーキテクチャ構成図.md) を参照してください。

### システム境界と外部連携

| 外部システム | 連携方式 | 用途 |
| --- | --- | --- |
| Node.js | CommonJS require | 実行環境、ファイルシステムアクセス、VM実行 |
| Jison | ライブラリ呼び出し | パーサー生成（grammar.coffee からパーサーコードを生成） |
| Babel (@babel/core) | オプショナル連携 | ES6+ へのトランスパイル（--transpile オプション） |
| ブラウザ環境 | script タグ / XHR | ブラウザ内でのCoffeeScript実行 |
| Webpack | モジュールバンドル | ブラウザ用コンパイラのビルド |

## レイヤー構成

### アーキテクチャスタイル

パイプライン・フィルターアーキテクチャ（コンパイラパイプライン）

CoffeeScript コンパイラは、典型的なコンパイラアーキテクチャに従い、ソースコードをトークン列、構文木（AST）、そして最終的に JavaScript コードへと段階的に変換するパイプライン構造を採用しています。各フェーズは独立したモジュールとして実装され、明確な責務分離が行われています。

### レイヤー定義

| レイヤー | 責務 | 主なコンポーネント |
| --- | --- | --- |
| Entry Point | コマンドライン解析、環境設定、コンパイル実行の制御 | command.coffee, index.coffee, browser.coffee, cake.coffee |
| Compiler Core | コンパイルパイプラインのオーケストレーション | coffeescript.coffee |
| Lexical Analysis | ソースコードのトークン化 | lexer.coffee, rewriter.coffee |
| Syntactic Analysis | トークン列の構文解析、AST生成 | grammar.coffee, parser.js（生成物） |
| Code Generation | ASTからJavaScriptコードの生成 | nodes.coffee, scope.litcoffee |
| Support | ユーティリティ、ソースマップ、ヘルパー | helpers.coffee, sourcemap.litcoffee, optparse.coffee |
| Runtime | REPL、require拡張、スタックトレース | repl.coffee, register.coffee |

### レイヤー間の依存関係ルール

```
Entry Point
    |
    v
Compiler Core <---> Support
    |
    v
+---+---+---+
|   |   |   |
v   v   v   v
Lexical -> Syntactic -> Code Generation
Analysis   Analysis

Runtime
    |
    v
Compiler Core
```

**依存の方向性:**
- Entry Point は Compiler Core に依存
- Compiler Core は Lexical Analysis、Syntactic Analysis、Code Generation を順に呼び出し
- 全レイヤーは Support（helpers）に依存可能
- Runtime は Compiler Core に依存
- 下位レイヤーから上位レイヤーへの依存は禁止

## モジュール構成

### ドメイン分割

| ドメイン | 責務 | 関連モジュール |
| --- | --- | --- |
| 字句解析 | ソースコードをトークン列に変換 | lexer.coffee, rewriter.coffee |
| 構文解析 | トークン列をASTに変換 | grammar.coffee, parser.js |
| コード生成 | ASTをJavaScriptに変換 | nodes.coffee, scope.litcoffee |
| ソースマップ | ソースコード位置情報の管理 | sourcemap.litcoffee |
| CLI | コマンドラインインターフェース | command.coffee, optparse.coffee, cake.coffee |
| REPL | 対話型実行環境 | repl.coffee |
| ブラウザ統合 | ブラウザ環境での実行サポート | browser.coffee |
| Node.js統合 | require拡張、ファイル読み込み | register.coffee, index.coffee |

### パッケージ構造

```
src/
├── coffeescript.coffee    # コンパイラコア（メインエントリーポイント）
├── index.coffee           # Node.js向けAPI（run, eval, register等）
├── browser.coffee         # ブラウザ向けAPI
├── command.coffee         # CLIコマンド処理
├── cake.coffee            # cakeビルドツール
├── lexer.coffee           # 字句解析器
├── rewriter.coffee        # トークン書き換え
├── grammar.coffee         # 文法定義（Jison DSL）
├── nodes.coffee           # ASTノード定義とコード生成
├── scope.litcoffee        # スコープ管理
├── sourcemap.litcoffee    # ソースマップ生成
├── helpers.coffee         # 共通ユーティリティ
├── optparse.coffee        # オプション解析
├── repl.coffee            # 対話型シェル
└── register.coffee        # require拡張
lib/
├── coffeescript/          # コンパイル済みJSファイル
├── coffeescript-browser-compiler-legacy/  # レガシーブラウザ用
└── coffeescript-browser-compiler-modern/  # モダンブラウザ用（ESモジュール）
bin/
├── coffee                 # coffeeコマンド
└── cake                   # cakeコマンド
test/                      # テストスイート
```

### コンポーネント依存関係

**コンパイルパイプラインの流れ:**

1. `coffeescript.coffee` がコンパイルを統括
2. `lexer.coffee` がソースコードをトークン化
3. `rewriter.coffee` がトークン列を正規化（暗黙の括弧、インデント等を補完）
4. `parser.js`（`grammar.coffee` から生成）がトークン列をAST（`nodes.coffee` で定義）に変換
5. `nodes.coffee` の各ノードクラスがJavaScriptコードを生成
6. `scope.litcoffee` がスコープ情報を管理
7. `sourcemap.litcoffee` がソースマップを生成

## ミドルウェア構成

### データベース

該当なし（コンパイラのためデータベースは使用しない）

| 種類 | ミドルウェア | バージョン | 用途 |
| --- | --- | --- | --- |
| - | - | - | - |

### キャッシュ

| ミドルウェア | バージョン | 用途 | TTL |
| --- | --- | --- | --- |
| インメモリキャッシュ | - | ソースマップのキャッシュ（`SourceMap.sourceMaps`） | プロセス終了まで |

### メッセージキュー

該当なし

| ミドルウェア | バージョン | 用途 |
| --- | --- | --- |
| - | - | - |

### 検索エンジン

該当なし

| ミドルウェア | バージョン | 用途 |
| --- | --- | --- |
| - | - | - |

### その他ミドルウェア

| ミドルウェア | バージョン | 用途 |
| --- | --- | --- |
| Jison | 0.4.18 | パーサー生成（開発時） |
| @babel/core | 7.17.9 | トランスパイル（オプション、開発時依存） |
| Webpack | 5.72.0 | ブラウザ用バンドル（開発時） |

## データフロー

### リクエスト処理の流れ

**CoffeeScript ソースコードのコンパイル:**

1. **入力受付**: `command.coffee` または API (`coffeescript.compile`) がソースコードを受け取る
2. **前処理**: BOM除去、改行正規化、Literate CoffeeScript の場合はMarkdown除去
3. **字句解析**: `Lexer.tokenize()` がソースをトークン列に変換
4. **トークン書き換え**: `Rewriter.rewrite()` が暗黙の括弧、インデント等を補完
5. **構文解析**: `parser.parse()` がトークン列をAST（Root ノード）に変換
6. **コード生成**: `nodes.compileToFragments()` がASTをCodeFragmentの配列に変換
7. **結合**: CodeFragmentを結合してJavaScript文字列を生成
8. **ソースマップ**: オプション指定時、`SourceMap.generate()` でソースマップを生成
9. **トランスパイル**: `--transpile` オプション指定時、Babelでトランスパイル
10. **出力**: JavaScript文字列（とソースマップ）を返却

### 非同期処理の流れ

**ファイル監視モード (`coffee --watch`):**

1. `command.coffee` の `watch()` 関数が `fs.watch()` でファイル監視を開始
2. ファイル変更検知時、25ms のデバウンス後にコンパイル実行
3. コンパイル結果をファイルに書き出し
4. エラー発生時はコンソールに出力し、監視を継続

**REPL の非同期実行:**

1. `repl.coffee` が Node.js REPL を拡張
2. 入力をCoffeeScriptとしてコンパイル
3. `async/await` を含む場合、結果をPromiseとして処理
4. 結果を `__` 変数に格納して表示

### データ永続化の流れ

**コンパイル結果の保存:**

1. `command.coffee` の `writeJs()` がコンパイル結果を受け取る
2. 出力ディレクトリが存在しない場合は再帰的に作成
3. `.js` ファイルとオプションで `.js.map` ファイルを書き出し

**ソースマップキャッシュ:**

1. コンパイル時に `SourceMap.registerCompiled()` でキャッシュに登録
2. スタックトレース変換時に `SourceMap.getSourceMap()` で取得
3. キャッシュはプロセス終了まで保持

## 横断的関心事

### 認証・認可

該当なし（コンパイラのため認証・認可機能は不要）

| 方式 | 実装箇所 | 対象 |
| --- | --- | --- |
| - | - | - |

### ロギング・監査

| 種類 | 実装方式 | 保存先 |
| --- | --- | --- |
| コンパイル進捗 | console.log (--watch モード時) | 標準出力 |
| エラーメッセージ | console.error, process.stderr | 標準エラー出力 |
| デバッグ出力 | 環境変数 `DEBUG_TOKEN_STREAM` | 標準出力 |

### エラーハンドリング

CoffeeScript コンパイラは、構文エラーを詳細な位置情報付きで報告します。

| エラー種別 | ハンドリング方式 | レスポンス |
| --- | --- | --- |
| 構文エラー (SyntaxError) | `helpers.throwSyntaxError()` | ファイル名:行:列: error: メッセージ + ソースコード表示 + キャレット表示 |
| 字句解析エラー | `Lexer` 内で `@error()` | 構文エラーとして処理 |
| パースエラー | `parser.yy.parseError()` | 構文エラーとして処理 |
| ファイル不在 | try-catch で捕捉 | "File not found: {path}" |
| オプションエラー | OptionParser 内で throw | "unrecognized option {flag}" |

**エラー表示の例:**
```
example.coffee:3:5: error: unexpected IDENTIFIER
    x = @
        ^
```

TTY環境では、エラー箇所を赤色でハイライト表示します。

### トランザクション管理

該当なし（データベーストランザクションは使用しない）

| 範囲 | 管理方式 | 分離レベル |
| --- | --- | --- |
| - | - | - |

## 設計原則・コーディング規約

### 適用している設計原則

| 原則 | 適用箇所 | 実装例 |
| --- | --- | --- |
| 単一責任の原則 | 各モジュール | Lexer（字句解析のみ）、Parser（構文解析のみ）、Nodes（コード生成のみ） |
| 開放閉鎖の原則 | ASTノード | Base クラスを継承した各ノードクラス |
| コンポジットパターン | ASTノード | Block, Value などのコンテナノード |
| ビジターパターン | AST走査 | `traverseChildren()`, `eachChild()` メソッド |
| テンプレートメソッドパターン | コード生成 | Base.compile() -> compileNode() |
| ファクトリパターン | パーサー | grammar.coffee の o() 関数 |

### コーディング規約

1. **言語**: コンパイラ本体はCoffeeScriptで記述（セルフホスティング）
2. **ドキュメント**: Literate CoffeeScript（`.litcoffee`）形式で一部のモジュールを記述
3. **コメント**: 各関数・クラスにドキュメントコメントを記述
4. **テスト**: test/ ディレクトリに機能ごとのテストファイルを配置
5. **エクスポート**: CommonJS形式（`module.exports`, `exports.xxx`）
6. **命名規則**:
   - クラス名: パスカルケース（Lexer, Rewriter, Base）
   - 関数名: キャメルケース（tokenize, compileNode）
   - 定数: 大文字スネークケース（SWITCHES, INVERSES）

## 備考

### セルフホスティング

CoffeeScript コンパイラはCoffeeScript自身で記述されています（セルフホスティング）。コンパイル済みの JavaScript は `lib/coffeescript/` ディレクトリに格納され、この JavaScript を使って新しいソースをコンパイルします。

### ビルドプロセス

1. `Cakefile` がビルドタスクを定義
2. `cake build` でソースをコンパイル
3. `cake build:parser` で Jison を使ってパーサーを生成
4. `cake build:browser` で Webpack を使ってブラウザ用バンドルを生成

### 対応環境

- **Node.js**: 6.x 以上
- **ブラウザ**: ES5対応ブラウザ（レガシー版）、ESモジュール対応ブラウザ（モダン版）
