日本語でRubyを書こう。
jp-rubyは、Rubyの文法を日本語キーワードで記述できるプリプロセッサです。.jrbファイルに日本語でコードを書き、jp-rubyコマンドで実行できます。
git clone https://github.com/jp-ruby/jp-ruby.git
cd jp-ruby
bundle installbundle exec ruby exe/jp-ruby examples/hello_world.jrbbundle exec ruby exe/jp-ruby -e '表示 "こんにちは、世界!"'bundle exec ruby exe/jp-ruby --dump examples/animal_classes.jrb# hello_world.jrb
表示 "こんにちは、世界!"# animal_classes.jrb
クラス 動物
定義 初期化(名前)
@名前 = 名前
終わり
定義 自己紹介
表示 "私は#{@名前}です。"
終わり
終わり
クラス 犬 < 動物
定義 吠える
表示 "#{@名前}: ワンワン!"
終わり
終わり
ポチ = 犬.新規("ポチ")
ポチ.自己紹介
ポチ.吠える出力:
私はポチです。
ポチ: ワンワン!
# fizzbuzz.jrb
定義 フィズバズ(数)
もし 数 % 15 == 0
"フィズバズ"
そうでなければ 数 % 3 == 0
"フィズ"
そうでなければ 数 % 5 == 0
"バズ"
でなければ
数.文字列変換
終わり
終わり
1.まで上(100) する |i|
表示 フィズバズ(i)
終わり# pattern_match.jrb
定義 ステータス確認(応答)
場合 応答
中の { ステータス: 200, 本文: }
表示 "成功: #{本文}"
中の { ステータス: 404 }
表示 "見つかりません"
終わり
終わり# block_it.jrb
数列 = [1, 2, 3, 4, 5]
二倍 = 数列.変換 { それ * 2 }
表示 二倍.検査| 日本語 | Ruby |
|---|---|
クラス |
class |
モジュール |
module |
定義 |
def |
終わり |
end |
初期化 |
initialize |
新規 |
new |
| 日本語 | Ruby |
|---|---|
もし |
if |
そうでなければ |
elsif |
でなければ |
else |
でない限り |
unless |
場合 |
case |
条件 |
when |
そして |
then |
| 日本語 | Ruby |
|---|---|
繰り返す |
while |
まで |
until |
繰り返し |
for |
中の |
in |
する |
do |
| 日本語 | Ruby |
|---|---|
真 |
true |
偽 |
false |
無 |
nil |
| 日本語 | Ruby |
|---|---|
戻す |
return |
次へ |
next |
中断 |
break |
譲る |
yield |
自分 |
self |
親 |
super |
| 日本語 | Ruby |
|---|---|
始まり |
begin |
救済 |
rescue |
確保 |
ensure |
発生 |
raise |
| 日本語 | Ruby |
|---|---|
取り込む |
include |
拡張 |
extend |
公開 |
public |
非公開 |
private |
保護 |
protected |
必要 |
require |
| 日本語 | Ruby |
|---|---|
かつ |
and |
または |
or |
ではない |
not |
| 日本語 | Ruby |
|---|---|
表示 |
puts |
出力 |
print |
検査 |
p |
| 日本語 | Ruby |
|---|---|
属性 |
attr_accessor |
読み属性 |
attr_reader |
書き属性 |
attr_writer |
| 日本語 | Ruby |
|---|---|
それ |
it |
ラクター |
Ractor |
データ |
Data |
キーワード変換に加えて、組み込みクラスのメソッドにも日本語エイリアスが利用できます。
| 日本語 | Ruby |
|---|---|
それぞれ |
each |
変換 |
map |
選択 |
select |
除外 |
reject |
畳み込み |
reduce |
並べ替え |
sort |
逆順 |
reverse |
一意 |
uniq |
含む? |
include? |
追加 |
push |
長さ |
length |
空? |
empty? |
結合 |
join |
最初 |
first |
最後 |
last |
| 日本語 | Ruby |
|---|---|
それぞれ |
each |
鍵一覧 |
keys |
値一覧 |
values |
含む? |
include? |
結合 |
merge |
削除 |
delete |
| 日本語 | Ruby |
|---|---|
長さ |
length |
分割 |
split |
含む? |
include? |
置換 |
gsub |
大文字 |
upcase |
小文字 |
downcase |
除去 |
strip |
空? |
empty? |
整数変換 |
to_i |
小数変換 |
to_f |
| 日本語 | Ruby |
|---|---|
回 |
times |
偶数? |
even? |
奇数? |
odd? |
文字列変換 |
to_s |
絶対値 |
abs |
| 日本語 | Ruby |
|---|---|
凍結 |
freeze |
凍結済み? |
frozen? |
複製 |
dup |
は? |
is_a? |
jp-ruby 実行パイプライン
┌─────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────┐
│ .jrb │───>│ Tokenizer │───>│ Transpiler │───>│ Runner │
│ ファイル │ │トークナイザー │ │トランスパイラー│ │ 実行 │
└─────────┘ └──────────────┘ └──────────────┘ └──────────┘
│ │ │
▼ ▼ ▼
トークン列に分解 日本語→Ruby変換 Runtime読み込み
(コード/文字列/ + クラス名に + eval実行
コメントを区別) Cプレフィックス付与
jp-ruby/
├── exe/
│ └── jp-ruby # CLI エントリーポイント(OptionParser)
├── lib/
│ ├── jp_ruby.rb # メインモジュール(全コンポーネントの読み込み)
│ └── jp_ruby/
│ ├── version.rb # バージョン定数
│ ├── errors.rb # TranspileError, TokenizeError
│ ├── keywords.rb # 日本語→Ruby キーワードマッピング(50+語)
│ ├── tokenizer.rb # StringScannerベースのステートマシン
│ ├── transpiler.rb # 2パストランスパイラー
│ ├── runtime.rb # 組み込みクラスへのメソッドエイリアス定義
│ └── runner.rb # ファイル読み込み→変換→実行の統括
├── spec/ # RSpec テスト(84テスト)
└── examples/ # サンプル .jrb ファイル
StringScannerベースのステートマシンで、ソースコードをトークン列に分解します。各トークンにはコンテキスト(コード / 文字列 / コメント等)が付与され、トランスパイラーが「どこを置換すべきか」を正確に判断できます。
ステートマシン状態遷移
┌──────┐ " ┌───────────────┐
│ CODE │─────>│ DOUBLE_STRING │
│ │<─────│ │
│ │ " └───────┬───────┘
│ │ │ #{
│ │ ▼
│ │ ┌───────────────┐
│ │ │ INTERPOLATION │ ← コードとして処理
│ │ │ (ブレース深度 │ }で元の状態に復帰
│ │ │ を追跡) │
│ │ └───────────────┘
│ │
│ │ ' ┌───────────────┐
│ │─────>│ SINGLE_STRING │
│ │<─────│ │
│ │ ' └───────────────┘
│ │
│ │ # ┌─────────┐
│ │─────>│ COMMENT │──> 行末で復帰
│ │ └─────────┘
│ │
│ │=begin┌───────────────┐
│ │─────>│ MULTI_COMMENT │
│ │<─────│ │
│ │=end └───────────────┘
│ │
│ │ / ┌───────┐
│ │─────>│ REGEX │
│ │<─────│ │
│ │ / └───────┘
│ │
│ │ <<~ ┌────────┐
│ │─────>│HEREDOC │
│ │<─────│ │
└──────┘ └────────┘
トークン種別:
| 種別 | 説明 | キーワード置換 |
|---|---|---|
:word |
識別子・キーワード | 対象 |
:string_part |
文字列リテラルの中身 | 対象外 |
:comment |
コメント | 対象外 |
:interp_begin / :interp_end |
#{ と } |
- |
:space |
空白・改行 | - |
:other |
演算子・記号・数値 | - |
ワードパターン: /[\p{L}_][\p{L}\p{N}_]*[?!]?/ で日本語文字(漢字・ひらがな・カタカナ)を含む識別子にマッチします。
2パスでトークン列を変換します。
Pass 1: クラス名収集 Pass 2: 置換
トークン列を走査 各 :word トークンに対して
"クラス" の次の :word を収集 1. クラス名? → Cプレフィックス付与
2. キーワード? → Ruby変換
例: クラス 犬 3. それ以外 → そのまま
→ ["犬"] を記録
例: 犬 → C犬
※日本語名のみ対象 クラス → class
吠える → 吠える (変換なし)
クラス名のCプレフィックス: Rubyでは定数(クラス名・モジュール名)は大文字ASCII [A-Z] で始まる必要があります。日本語クラス名は C プレフィックスを自動付与することで、ユーザーが意識せずに日本語クラス名を使えるようにしています。
入力: クラス 犬 < 動物 出力: class C犬 < C動物
犬.新規("ポチ") C犬.new("ポチ")
行構造の保持: キーワード置換は行の追加・削除を行わないため、変換後のRubyコードの行番号は元の .jrb ファイルと完全に一致します。エラー発生時のスタックトレースがそのまま .jrb ファイルの行番号を指します。
実行前に alias_method で組み込みクラスにメソッドエイリアスを追加します。キーワード変換では対応できないメソッド呼び出し(.each, .map 等)を日本語化するための仕組みです。
# 内部的に行われること
Array.class_eval do
alias_method :それぞれ, :each
alias_method :変換, :map
alias_method :選択, :select
# ...
end全コンポーネントを統括する実行エンジンです。
Runner#run の処理フロー:
1. File.read(filename, encoding: "UTF-8")
2. Transpiler.new(source).transpile
3. Runtime.load! (メソッドエイリアス登録)
4. eval(ruby_code, TOPLEVEL_BINDING, filename, 1)
~~~~~~~~ ~
元ファイル名と行番号を渡すことで
エラー時の表示が正確になる
| 課題 | 解決策 |
|---|---|
| 文字列内のキーワード誤置換 | ステートマシンで文字列/コメント内を区別 |
#{}内は置換が必要 |
INTERPOLATION状態でコードモードに復帰 |
| 日本語クラス名がRubyの定数規則に反する | 自動でCプレフィックスを付与 |
部分一致による誤変換(例: 終わりました に 終わり がマッチ) |
トークナイザーが単語単位でスキャンするため発生しない |
| エラー時の行番号ずれ | 行構造を変えない置換 + evalに元ファイル名を渡す |
| キーワードの長さによる誤マッチ | マッピングを文字列長の降順でソート |
- Ruby >= 3.2.0
bundle exec rspecMIT License