Giter Site home page Giter Site logo

10-min-fizzbuzz's Introduction

10 min fizzbuzz

Fizzbuzzが動くRuby to JavaScriptコンパイラを10分で作ります。

Files

  • fizzbuzz.rb
    • 今回コンパイルする対象のコード
  • compiler.rb
    • コンパイラのひな型
  • goal.rb
    • 今回作るコンパイラの完成形

必要なもの

  • Ruby 2.7+
    • Pattern Matching

あると便利なもの

前提知識

  • RubyVM::AbstractSyntaxTree.parse(ruby_code)で、RubyのコードからAST(抽象構文木)を手に入れることができます。
    • 抽象構文木はソースコードが構造化されたものです。そのためプログラムからソースコードを扱うのが容易になります。
    • Ruby 2.6からの機能です
  • case inでパターンマッチを行います。
    • case x
      in [1, 2, 3]
        # x == [1, 2, 3]
      in [1, y, 3]
        # x == [1, なにか, 3]
        p y # => x[1]
      in [1, 2, [3, y]]
        # x == [1, 2, [3, なにか]]
        p y # => x[2][1]
      end
    • case whenと似ていますが、任意の値にマッチさせて、マッチさせた値を変数に格納することができます。
    • Ruby 2.7からの機能です。

実装手順

compile_exprcompile_stmtを実装していきます。Rubyでは全ての構文要素が値を持ちますが、JavaScriptではifなどの文は値を持ちません。 そのため、値を持つ構文要素をcompile_expr、値を持たないものをcompile_stmtでコンパイルするようにします。

ruby compiler.rb fizzbuzz.rbを実行すると、fizzbuzz.rbに書かれているコードをコンパイルした結果のJavaScriptが得られます。 また、ruby compiler.rb fizzbuzz.rb | nodeとすると、コンパイルした結果をNodeで実行できます。

1. 20をコンパイルする

単なる数値リテラルをコンパイルできるようにします。20が20にコンパイルされれば良いですね。 これにはLITSCOPEの2つのtypeの対応が必要です。

2. puts 20 をコンパイルする

次にputs 20をコンパイルしてみましょう。これはconsole.log(20)にコンパイルされれば良いですね。 メソッドの引数はARRAYに包まれているので、それを剥がす必要があります。

3. ローカル変数への代入をコンパイルする

max = 20をコンパイルします。

4. ローカル変数の参照をコンパイルする

max = 20; puts maxをコンパイルします。 このへんでつまったらBLOCKノードのコンパイルが必要かもしれません。

5. 比較演算子 <= をコンパイルする

比較演算子が動くようにしてみましょう。

puts 1 <= 20 とかが動くと良いですね。

6. whileをコンパイルする

whileをコンパイルします。 while i <= max; puts i; endとかが動くと良いでしょう。無限にi(つまり1)を出力し続けます。

7. + をコンパイルする

+演算子をコンパイルして、i = i + 1を動くようにしましょう。 <=のコンパイルをすこし工夫すれば良いですね。 ここまで実装すれば、1から20を列挙するコードが動きます。あと少しですね。

8. 文字列リテラルをコンパイルする

文字列リテラルをコンパイルします。puts "fizzbuzz"が動くと良いでしょう。 文字列の出力にはString#dumpメソッドが便利です。厳密にはRubyとJavaScriptで文字列リテラルは違うものですが、Fizzbuzzを書くぐらいなら気になりません。

9. ifをコンパイルする

最後にifをコンパイルします。 ここまで実装すると、1から20までのFizzbuzzを出力できます!

ちなみに、RubyVM::AbstractSyntaxTreeではif-elsif-endは次のものと同じ構造のASTになります。(parser gemでも同様です。)

if cond1
  body1
elsif cond2
  body2
else
  body3
end

# ↑は↓とおなじ
if cond1
  body1
else
  if cond2
    body2
  else
    body3
  end
end

つまり、今回はelsifのことは考えずにif-else-endをコンパイルできるようにすれば良いだけです。

今回やらないこと

  • ローカル変数の定義と代入の使い分け
    • 全ての変数はvarletconstもなしで定義します。
    • 真面目にやろうとしたら10分じゃ終わらないので…
  • 関数呼び出しの際の関数探索
    • 今回はconsole.logしか呼び出さないので
  • そのたたくさん

10-min-fizzbuzz's People

Contributors

pocke avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.