Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cxx-ruby

cxx-ruby は C++23 で書かれた実験的な Ruby インタプリタです。

このプロジェクトは MRI Ruby の互換実装ではありません。Ruby 風の構文、オブジェクト、ブロック、クラス、例外、標準的な組み込みメソッドの一部を実装した、小さなツリーウォーク型インタプリタです。教育用途、実装実験、言語処理系の拡張題材として使うことを想定しています。

実用的な Ruby アプリケーション、RubyGems、Rails、Bundler、標準ライブラリ互換などを動かすための処理系ではありません。

現在できること

  • C++23 と CMake でビルドできます。
  • Ruby ソースファイルを実行できます。
  • -e で短い Ruby 断片を実行できます。
  • 引数なしで起動すると簡易 REPL として動作します。
  • 字句解析器、再帰下降パーサ、AST、動的な値表現、ツリーウォーク評価器を持ちます。
  • nil、真偽値、整数、浮動小数点数、シンボル、文字列、配列、Hash、Range、Proc、Class、ユーザー定義オブジェクトを扱えます。
  • 変数、代入、算術、比較、制御構文、メソッド定義、ブロック、yield、クラス定義、継承、例外処理の基本を実装しています。
  • StringArrayHashRangeEnumerable 風のメソッドを一部実装しています。
  • examples/tour.rb で主要機能をまとめて確認できます。

必要なもの

  • CMake 3.20 以上
  • C++23 に対応した C++ コンパイラ

外部ライブラリへの依存はありません。

ビルド

cmake -S . -B build
cmake --build build

実行ファイルは次に作成されます。

./build/cxx-ruby

実行方法

Ruby ファイルを実行します。

./build/cxx-ruby examples/tour.rb

短いコードを実行します。

./build/cxx-ruby -e 'puts [1, 2, 3].map { |n| n * 2 }.inspect'

引数なしで起動すると簡易 REPL になります。

./build/cxx-ruby

REPL は IRB 互換ではありません。1 行ずつ簡単に動作確認するためのものです。

サンプル

class Animal
  attr_accessor :name

  def initialize(name)
    @name = name
  end

  def speak
    "..."
  end

  def to_s
    "#{self.class.name}(#{@name}): #{speak}"
  end
end

class Dog < Animal
  def speak
    "Woof"
  end
end

puts Dog.new("Rex")

実装済みの言語機能

パーサと評価器は、現在おおよそ次の構文を扱えます。

  • 整数、浮動小数点数、真偽値、nil、シンボル、文字列、配列、Hash、Range リテラル
  • ダブルクォート文字列の #{...} 補間
  • ローカル変数、インスタンス変数、グローバル変数、クラス変数、定数
  • 通常代入、演算代入、多重代入、添字代入
  • 算術演算、比較演算、ビット演算の一部
  • &&||andornot!
  • ifelsifelseunless
  • 後置 if / unless / while / until
  • 三項演算子
  • whileuntilfor
  • case / when
  • returnbreaknext
  • { ... }do ... end のブロック
  • yield
  • proclambda-> の簡易サポート
  • 明示的レシーバー呼び出し、暗黙の self 呼び出し
  • 括弧あり・括弧なしのメソッド呼び出しの一部
  • 簡易 splat、ブロック引数、ブロック渡し
  • safe navigation 構文 &.
  • def、endless method definition
  • def self.foo 形式の簡易 singleton method
  • classmodule、継承指定
  • super
  • begin / rescue / ensure
  • メソッド本体末尾の rescue
  • 基本的な定数参照とクラス作成

実装済みの主な組み込み機能

組み込みライブラリは C++ で手書きされています。MRI Ruby の標準ライブラリではなく、この処理系専用の最小実装です。

Kernel 風のメソッド例:

  • puts
  • print
  • p
  • pp
  • raise
  • fail
  • rand
  • srand
  • sleep
  • exit
  • exit!
  • abort
  • block_given?
  • loop
  • format
  • sprintf
  • printf
  • Integer
  • Float
  • String
  • Array
  • gets
  • require
  • require_relative
  • attr_reader
  • attr_writer
  • attr_accessor

Enumerable 風のメソッド例:

  • each
  • each_with_index
  • each_with_object
  • map
  • collect
  • flat_map
  • collect_concat
  • select
  • filter
  • reject
  • find
  • detect
  • find_index
  • find_all
  • count
  • reduce
  • inject
  • sum
  • min
  • max
  • min_by
  • max_by
  • sort
  • sort_by
  • group_by
  • partition
  • any?
  • all?
  • none?
  • tally
  • to_a
  • entries
  • first
  • include?
  • member?
  • each_slice
  • each_cons
  • zip
  • take
  • drop
  • take_while
  • drop_while
  • to_h
  • reverse_each

そのほか、数値、文字列、配列、Hash、Range、Symbol、Proc、Class、nil、真偽値向けに多数のメソッドを実装しています。正確な一覧は src/builtins.cppsrc/builtins_methods.cppsrc/builtins_dispatch.cpp を参照してください。

内部構造

主なファイルは次の通りです。

  • src/lexer.cpp / src/lexer.hpp: Ruby 風ソースコードをトークン列に変換します。
  • src/parser.cpp / src/parser.hpp: トークン列から AST を構築します。
  • src/ast.hpp: AST ノード定義です。
  • src/value.hpp / src/value.cpp: 実行時の値、クラス、オブジェクト、配列、Hash、Proc などを定義します。
  • src/interpreter.cpp / src/interpreter.hpp: AST を評価するツリーウォーク実行器です。
  • src/builtins.cpp: Kernel 風メソッド、例外、表示、変換などを実装します。
  • src/builtins_methods.cpp: Enumerable、Numeric、String などのメソッドを実装します。
  • src/builtins_dispatch.cpp: Array、Hash、Range、Proc、Class、Symbol などの dispatch を実装します。
  • src/main.cpp: CLI、-e、REPL の入口です。

実行時の値は std::variant で表現されています。文字列、配列、Hash、Range、Proc、Class、ユーザー定義オブジェクトなど、可変または同一性を持つ値は std::shared_ptr で保持します。

returnbreaknext、Ruby 例外は、評価器内部では C++ 例外として実装されています。

MRI Ruby との主な違いと制限事項

この章がこの README で最も重要な部分です。cxx-ruby は Ruby 風の処理系ですが、MRI Ruby とは多くの点で違います。

全体方針

  • MRI Ruby 互換実装ではありません。
  • Ruby 仕様への準拠を目標にしていません。
  • ある MRI バージョンとの完全互換を目指していません。
  • RUBY_VERSION は合成された値であり、MRI の実バージョン互換を示しません。
  • RubyGems は動きません。
  • Bundler は動きません。
  • Rails は動きません。
  • Rake は動きません。
  • IRB は含まれていません。
  • Ruby 標準ライブラリは含まれていません。
  • 実際の Ruby プロジェクトをそのまま実行する用途には向いていません。
  • examples/tour.rb が通ることは、一般的な Ruby 互換性を意味しません。

構文解析の違い

  • パーサは独自の再帰下降パーサです。
  • MRI の parse.y、Prism、Ripper とは別物です。
  • Ruby の文法全体は実装していません。
  • 多くの合法な Ruby コードは parse error になります。
  • MRI と同じエラーメッセージは出ません。
  • MRI と同じ warning は出ません。
  • magic comment は解釈しません。
  • # frozen_string_literal: true は無視されます。
  • encoding comment は無視されます。
  • __FILE____LINE____ENCODING__ は実装していません。
  • 正規表現リテラルは実装していません。
  • %w%i%q%Q%r%x%s などの percent literal は実装していません。
  • heredoc は実装していません。
  • バッククォートによるコマンド実行は実装していません。
  • 文字列エスケープは MRI ほど完全ではありません。
  • シングルクォート文字列とダブルクォート文字列の差異は MRI ほど正確ではありません。
  • Unicode 識別子は想定していません。
  • Ruby の全ての数値リテラル形式は実装していません。
  • 2 進数、8 進数、16 進数、underscore、Rational、Complex などは不完全です。
  • pattern matching の case ... in は実装していません。
  • pin operator は実装していません。
  • endless range、beginless range は実装していません。
  • BEGIN / END ブロックは実装していません。
  • alias は言語機能として実装していません。
  • undef は実装していません。
  • defined? は parser header に名残がありますが、実用的な言語機能としては未実装です。
  • retry は実装していません。
  • redo は実装していません。
  • unless、後置構文、コマンド呼び出し構文は一部だけ対応しています。
  • MRI の細かい演算子優先順位や曖昧構文の解釈とは異なる場合があります。
  • キーワード引数構文は簡易的に Hash 化するだけで、MRI 互換ではありません。

オブジェクトモデルの違い

  • MRI の内部オブジェクトモデルとは全く違います。
  • VALUE、RBasic、RClass など MRI の内部構造は使っていません。
  • singleton class / eigenclass は一般的には実装していません。
  • オブジェクト単位の singleton method は基本的にありません。
  • def self.foo など一部のクラス側メソッドだけ簡易的に扱います。
  • method_missing は実装していません。
  • respond_to_missing? は実装していません。
  • sendpublic_sendmethodinstance_method などのリフレクションは未実装または不完全です。
  • ObjectSpace はありません。
  • object_id の MRI 互換性はありません。
  • オブジェクトの identity、allocation、finalizer は MRI と違います。
  • GC はありません。
  • C++ の shared_ptr で寿命を管理します。
  • 循環参照は回収されない可能性があります。
  • freeze は凍結状態を強制しません。
  • frozen? は MRI と同じ意味を持ちません。
  • taint、trust、fstring、hidden class、shape など MRI 固有の概念はありません。
  • BasicObjectObjectClassModule はありますが、メソッド数も意味も非常に限定的です。

クラス、モジュール、継承の違い

  • クラス定義と単純な継承はできます。
  • モジュールは class-like なオブジェクトとして作られますが、MRI の Module 互換ではありません。
  • include はプレースホルダーに近く、実際の mixin として機能しません。
  • extend も MRI のような singleton class への mixin ではありません。
  • prepend は実装していません。
  • refinements は実装していません。
  • using は実装していません。
  • 可視性制御は実質的にありません。
  • privatepublicprotected は MRI のように dispatch を制限しません。
  • 定数探索は簡易実装です。
  • lexical constant lookup は MRI と一致しません。
  • namespace と :: は一部だけ対応しています。
  • autoload はありません。
  • class variable の探索・警告・継承挙動は MRI と違います。
  • super の引数省略時 forwarding は MRI と一致しません。

メソッド呼び出しと引数の違い

  • 引数束縛は簡易実装です。
  • 必須引数、optional 引数、splat、block 引数は一部だけ対応しています。
  • keyword 引数は MRI 互換ではありません。
  • positional 引数と keyword 引数の分離は再現していません。
  • Ruby 3 系の keyword argument semantics は実装していません。
  • arity error は MRI ほど厳密ではありません。
  • 引数不足でも nil が入る場合があります。
  • ブロック引数の destructuring は簡易的です。
  • ブロックローカル変数は実装していません。
  • Proclambda の非局所 return / break の違いは完全ではありません。
  • Proc#arity は簡易計算です。
  • Proc#curry は実質的にプレースホルダーです。
  • Enumerator を返すべき場面で Enumerator を返さないことが多いです。
  • &:symbol は一部動作しますが、MRI の完全な Symbol#to_proc ではありません。

ブロックと制御フローの違い

  • yield は単純なブロック呼び出しで動きます。
  • block_given? は現在の評価フレームに基づく簡易判定です。
  • breaknextreturn は C++ 例外で実装されています。
  • ネストした proc、lambda、method、ensure をまたぐ複雑な非局所制御フローは MRI と一致しない場合があります。
  • Fiber はありません。
  • Continuation はありません。
  • Enumerator::Yielder はありません。
  • lazy enumerator はありません。

例外処理の違い

  • 例外クラス階層は小さな手書き実装です。
  • ExceptionStandardErrorRuntimeErrorArgumentError など一部だけあります。
  • backtrace は記録しません。
  • Exception#backtrace は実用的に使えません。
  • Exception#cause は実装していません。
  • full_message は実装していません。
  • rescue の型照合は簡易的です。
  • rescue 節の else は MRI と同じようには評価されません。
  • ensure は基本的なケースに対応していますが、全ての制御フローで MRI と一致する保証はありません。
  • raise の引数バリエーションは一部だけ対応しています。
  • system exception、signal、interrupt は扱いません。

標準ライブラリの違い

  • Ruby 標準ライブラリは付属しません。
  • require はファイルを読みません。
  • require_relative もファイルを読みません。
  • 現状の require / require_relativefalse を返すスタブです。
  • $LOAD_PATH は実装していません。
  • $LOADED_FEATURES は実装していません。
  • RubyGems の activation はありません。
  • native extension は読み込めません。
  • File はありません。
  • Dir はありません。
  • 実用的な IO クラス階層はありません。
  • gets は標準入力から 1 行読む程度の実装です。
  • TimeDateJSONCSVYAMLSetPathnameBigDecimal などはありません。
  • SocketOpenSSLNet::HTTP などはありません。
  • RipperPrismRubyVM はありません。
  • ThreadFiberMonitorMutex はありません。
  • ARGV は空配列として用意されていますが、CLI 引数とは連動していません。

数値の違い

  • Integerstd::int64_t です。
  • MRI のような任意精度整数ではありません。
  • オーバーフロー時の挙動は MRI と違います。
  • Float は C++ の double です。
  • NaN、Infinity、丸め、表示は MRI と一致しない場合があります。
  • Rational はありません。
  • Complex はありません。
  • Numeric 階層は名前だけ近く、メソッド群は限定的です。
  • Integer#timesuptodowntostep などは基本ケース向けです。
  • 例外や境界条件は MRI と一致しない場合があります。

文字列とエンコーディングの違い

  • String は基本的に byte string として扱います。
  • Encoding はありません。
  • UTF-8 文字単位の処理は保証しません。
  • Unicode case folding はありません。
  • upcase / downcase は ASCII 的な処理です。
  • length はバイト長に近い挙動です。
  • 正規表現がないため、sub / gsub は単純な文字列置換です。
  • split は MRI の regexp、limit、空白分割の細かい仕様を再現しません。
  • String#% / sprintf は一部のフォーマットだけです。
  • 破壊的メソッドと非破壊的メソッドの戻り値は MRI と異なる場合があります。
  • frozen string literal はありません。

Array の違い

  • 多くの基本メソッドはありますが、完全ではありません。
  • Enumerator を返すべきケースで Enumerator を返さないことがあります。
  • Array#pack は空文字列を返すだけのプレースホルダーです。
  • Array#sample / shuffle は簡易乱数です。
  • 比較、再帰構造、ハッシュ化、破壊的操作の細かい挙動は MRI と一致しません。
  • Array#fill などの引数バリエーションは限定的です。
  • Array#sort は実装の比較関数に依存し、MRI の全挙動とは異なります。

Hash の違い

  • Hash は順序を保持します。
  • 内部的には vector の線形探索です。
  • 大きな Hash では遅いです。
  • default value / default proc は実質的にありません。
  • Hash#defaultnil を返すだけです。
  • rehash はありません。
  • key mutation まわりの挙動は MRI と違います。
  • Hash#fetch など一部はありますが、例外やメッセージは MRI と一致しません。
  • block arity による key/value の渡し方は簡易的です。

Range の違い

  • 主に整数 Range の反復を想定しています。
  • 文字列 Range、独自オブジェクト Range は MRI ほど動きません。
  • beginless range はありません。
  • endless range はありません。
  • cover?include?=== は単純な比較です。
  • step は基本ケースだけです。
  • Range と Enumerator の連携はありません。

Enumerable の違い

  • 実装は配列化して処理するものが多いです。
  • 遅延評価はありません。
  • Enumerator オブジェクトはありません。
  • each にブロックがない場合の MRI 互換挙動はありません。
  • size hint はありません。
  • chaining は MRI の Enumerator ほどできません。
  • Hash の key/value 展開は MRI と完全一致しません。
  • Enumerable モジュールとして include される仕組みではなく、型ごとの組み込み dispatch です。

I/O、プロセス、OS 連携の違い

  • ファイル I/O はありません。
  • ディレクトリ操作はありません。
  • ソケットはありません。
  • system はありません。
  • backtick はありません。
  • execforkspawn はありません。
  • pipe はありません。
  • process status はありません。
  • 環境変数 API はありません。
  • signal handling はありません。
  • sleep は実際に待たず、すぐ返るスタブです。
  • exitabort はホストプロセスを直接終了します。

並行処理の違い

  • Thread はありません。
  • Mutex はありません。
  • Queue はありません。
  • ConditionVariable はありません。
  • Fiber はありません。
  • Scheduler はありません。
  • Ractor はありません。
  • GVL のような概念はありません。

パフォーマンス上の制限

  • ツリーウォーク実行です。
  • bytecode compiler はありません。
  • JIT はありません。
  • inline cache はありません。
  • method cache はありません。
  • Hash は線形探索です。
  • 多くのメソッドは配列や文字列を eager にコピーします。
  • 再帰的な Ruby コードは C++ の call stack を消費します。
  • 最適化より読みやすさと実験しやすさを優先しています。

セキュリティ上の制限

  • サンドボックスではありません。
  • ユーザーコードは実装済みの exit などを呼べます。
  • 無限ループを止める仕組みはありません。
  • メモリ使用量制限はありません。
  • CPU 時間制限はありません。
  • 深い再帰でプロセスが落ちる可能性があります。
  • 信頼できないコードを安全に実行する用途には使えません。

REPL の違い

  • IRB ではありません。
  • 複数行入力はほぼ考慮していません。
  • 履歴はありません。
  • 補完はありません。
  • pretty inspect の補助はありません。
  • 例外からの復帰や構文エラー表示は最小限です。

テストと互換性確認の制限

  • RubySpec は通していません。
  • MRI のテストスイートは通していません。
  • 自動テストターゲットはまだありません。
  • 現在の主な検証は examples/tour.rb です。
  • 「この README にある機能」は現時点の実装に基づく説明であり、MRI 互換保証ではありません。

開発メモ

よく使う確認コマンドです。

cmake --build build
./build/cxx-ruby examples/tour.rb
./build/cxx-ruby -e 'puts 1 + 2 * 3'

機能追加時の主な編集場所です。

  • トークンを増やす場合: src/lexer.cppsrc/lexer.hpp
  • AST ノードを増やす場合: src/ast.hpp
  • 構文を増やす場合: src/parser.cppsrc/parser.hpp
  • 評価規則を増やす場合: src/interpreter.cppsrc/interpreter.hpp
  • 組み込みメソッドを増やす場合: src/builtins.cppsrc/builtins_methods.cppsrc/builtins_dispatch.cpp

現在の動作確認

この README 作成時点では、次のコマンドで確認しています。

cmake --build build
./build/cxx-ruby examples/tour.rb
./build/cxx-ruby -e 'puts [1,2,3].map { |x| x * 2 }.inspect'

examples/tour.rb は、算術、文字列、配列、Hash、制御構文、メソッド、ブロック、クラス、継承、attr_accessor<=> を使った比較、例外処理、メソッドレベル rescue、case を確認します。

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages