butterfly dream

FX自動取引のための時系列分析・信号処理・戦略ブログ

科学技術計算用プログラミング言語:Sci-Lispの自作(8日目):文法の厳選

chaploud-blog.hatenablog.com

Sci-Lisp自作の8日目です。昨日の記事 で文法の厳選をすることをまずやるということで、今日は文法を厳選して用例を作成しました。

GitHubのREADME.mdの下部に、Language Overviewとして用例を掲載しました。

github.com

サンプルとして、クラスとマクロのコードのスクリーンショットを載せます。

クラス

classの用例

マクロ

macroの用例

終わりに

C++で実装することを前提としているので、C++で扱いづらい処理はまずは省きました。型の記述を必須にしたことによって手軽さと簡潔さが少し損なわれてしまいましたが、C++に変換しやすい所を目指しているので一旦は目をつぶります。

科学技術計算用プログラミング言語:Sci-Lispの自作(7日目):時間との闘い

chaploud-blog.hatenablog.com

Sci-Lisp自作の7日目です。言語自作は趣味で行っているため、本業と違って締め切りがありません。しかし、プロトタイプを作るのに5年もかかっていたら、多くのものを無駄にしてしまいそうです。ここは、はっきりと半年でβ版ローンチすると言い切っておいた方がよいと思います。

さて、そうなってくると本業の傍らで作業していくにはいかに時間を効率的に使うかが問題になってきます。日によって体調・モチベーションが異なるのはどうしようもありません。ゴールにより早く近づくにはどうすればよいか?おそらく何かしらの啓発本には書かれていることと思いますが、以下の2点に尽きます。

  • ゴールを明確にする
  • ゴールから逆算する

ボトムアップで勉強しながら作っていこうという試みなので、ゴールを明確にするというのは意外と難しいです。考えてみましょう。

Sci-Lisp開発の(一旦の)ゴール

まずは多少粗削りでもいいのでこれを実現します。

ゴールから逆算する

  • C++コードのコンパイル
    • C++のコードを吐き出す
      • Sci-Lispプログラムのパース
        • 文法の整理
          • 最低限の文法に絞る

まずは、最低限の文法に絞り、スモールスタートすることにします。

Sci-Lisp開発の+α

長期的には、以下を実現したいと頭の片隅に置いておきます。ただし、最初からこれらに取り組まないように。

  • 高速化
  • VSCode拡張機能を充実させる
  • REPLの機能を充実させる
  • 標準ライブラリを充実させていく

おわりに

なかなか時間が取れないですが、12月中は基盤固めとしてもブログは毎日更新していきます。

科学技術計算用プログラミング言語:Sci-Lispの自作(6日目):型(1)

chaploud-blog.hatenablog.com

Sci-Lisp自作の6日目です。型について考えます。

手軽さで人気のPythonJavaScriptなどのスクリプト言語も、型アノテーションを導入したりTypeScriptが現れたりと、型のありがたみが再評価されているところです。特にデータベースやAPI経由でのデータがどのような情報を持っているのかを事前に定義しておけば、開発の際に大きな手掛かりとなります。また、コンパイルして実行する際に実行時型検査のコードが不要となり、より高速な実行が見込めます。

変数の型の指定

あくまで草案に過ぎないのですが、Sci-Lispでは以下のように型指定します。

Sci-Lispの型指定

上記の例では、リテラルによって型推論が可能なため、型指定の :hoge はつけなくても構いません。 (※シンボルの場合どう解析するのか、考えないといけませんが...)

リスト型については、どのような型も受け入れるLinked Listを想定しています。(C++list<std::any>)。

コレクション型(vector, map, set)については、型をv[i32] などのようにジェネリクスで指定します。vectorについては特別に確保領域を追加で指定できます。(v[i32][3]

関数型も指定できます。引数の型と ->によって返り値を指定します。デフォルト引数などを考え出すと、どういうシンタックスにしたらいいのか悩みます。

関数型

終わりに

Sci-Lispスクリプト言語的な手軽さをもちつつも、静的に型を持たせたいと考えています。このためにはRustのような強力な型推論機構をコンパイラに備える必要があります。また、型システムについて整合性を担保しなければなりません。かなりハードルが高いです。また、書きながら思いましたが、コレクション型などのヒープ領域確保系は参照型が必要になってきますね...。とにかく、作りながらブラッシュアップしていきたいと思います。

科学技術計算用プログラミング言語:Sci-Lispの自作(5日目):組み込み関数スクリーンショット集

chaploud-blog.hatenablog.com

Sci-Lisp自作の5日目です。実装に先立ちどのような組み込み関数を用意しようかなと考えています。このあたりは、まずコアだけを実装してから標準ライブラリとしてモジュール的に追加していくのが筋だとは思うのですが、自分の作る言語の書き味を確かめ、モチベーションを上げるためにいくつか例を用意しました。新言語なのでシンタックスハイライトがうまくいかないため、同時に自作している拡張機能による色付けがなされている画像をはりつけていきます。

コメントとリテラル

組み込み関数その1

スペシャルフォームたち

組み込み関数その2

組み込み関数その3

終わりに

言語の書き味を確かめることによってモチベーションを上げる回でした。

科学技術計算用プログラミング言語:Sci-Lispの自作(4日目):キーワード

chaploud-blog.hatenablog.com

Sci-Lisp自作の4日目です。3日目に紹介した、「インタプリタの作り方」という本を先にすべてやり終えてから、という気持ちを抑えつつ今日の記事です。(なぜなら、大概こういったものはやり終える前に熱量が冷めてしまうから)

book.impress.co.jp

Sci-Lispのキーワード

Sci-Lispの仕様決めのため、草案としていろんなサンプルコードを書き出しました。その中で、言語の基本機能に関わるキーワード(予約語)が見えてきましたので紹介します。

Sci-Lispのキーワード

せっかくなので、同時並行で開発しているSci-LispVSCode拡張機能によるシンタックスハイライトが適用されている画像を貼りました。まだ何も実装が決まっていないのですが、キーワードの大まかな機能を説明します。変数じゃなくてVarだろ、とか定義/値/代入/束縛の意味が揺れているだろ、と突っ込みを入れたくなると思いますが、そのあたりは、申し訳ありません、ぼんやり使っています。厳密性はあとから勉強しつつ整えていきたいと思います。

let

レキシカルスコープを持つ変数への値の束縛に使います。Clojureと違い、変数への再代入は許す予定です。

const

主にグローバルで定数を定義するのに用います(defに:constをつけるなどして不要かも?)

def

グローバルで変数への値の束縛に使います。関数やクラスも束縛できます。

defn

グローバルで関数定義に使います。def + fnの組み合わせのショートハンドです。

fn

無名関数を作ります。

if

条件分岐に使います。

when

真の場合のみ評価する場合に使います。

for

forループに使います。

while

whileループに使います。

break

ループのbreakに用います。

continue

ループのcontinueに用います。

struct

構造体の定義に用います。

enum

列挙体の定義に用います。

class

クラスの定義に用います。

macro

マクロの定義に用います。

throw

例外を投げるのに使います。

try

例外を受け取りたい範囲を囲みます。

catch

例外を受け取り、例外処理を行います。

ns

名前空間の定義に用います。

set!

束縛された変数に破壊的変更(要するに再代入)を行うときに用います。

cond

リッチな条件分岐に用います。

return

早期リターンしたい場合に用います。

yield

ジェネレータ、遅延評価の際に用います。

import

モジュールやライブラリの読み込みに用います。

this

クラス内でインスタンスを指すために用います。

終わりに

単なるS式の皮をかぶったPythonじゃないか!と突っ込まれそうですが、その通りです。最初の動機としては、「Python+Numpy+matplotlib+pandas+scipyとC++の高速性がものすごく手短に扱える時系列・信号処理・統計計算用DSLを作る」だったので。S式である必要性は今のところ差別化以外の理由が見つかりませんが、検討・実装のイテレーションを繰り返していくうちに必ずや一部の人には「使いたい」と思わせられる美しさと利便性を備えた言語に仕上げていきたいと思います。Done is better than perfect.

科学技術計算用プログラミング言語:Sci-Lispの自作(3日目):言語開発のロードマップ

chaploud-blog.hatenablog.com

github.com

Sci-Lisp自作の3日目です。もともとはネット上の情報を拾い集めて言語(とそのインタプリタコンパイラ)を作成をしようとしていたのですが、ちょうとAmazonブラックフライデーに以下の本が推薦されて出てきたので思わず買ってしまいました。

インタプリタの作り方 -言語設計/開発の基本と2つの方式による実装

CRAFTING INTERPRETERS

ちなみにこちら、英文ですがWeb上で無料公開もされています。レビューを見るに大変な良書のようです。

craftinginterpreters.com

言語開発のロードマップ

さて、この本を頭から読んでいくだけでもかなり時間を要するので、Sci-Lisp開発とは同時並行で読み進めたいと思っていますが、2章にプログラミング言語開発(インタプリタ実装)についての概観がまとまっていましたので、紹介します。

言語開発のロードマップ

  1. 字句解析(Scanning)
  2. 構文解析(Parsing)
  3. 静的分析・意味分析(Analysis)
  4. 中間表現(Intermediate Representations)
  5. 最適化(Optimizing)
  6. コード生成(Code Generation)
  7. 仮想マシン(Virtual Machine)
  8. ランタイム(Runtime)

次のテキストに処理を施していくことを考えます。

var agerage = (min + max) / 2;

1. 字句解析(Scanning)

スキャナによって、コメントや空白を読み捨て、意味のあるトークンだけの純粋なシーケンスを残す。

var average = ( min + max ) / 2 ;

2. 構文解析(Parsing)

パーサによって、トークンの並びを受け取って、文法の入れ子構造を反映した木構造を構築する。これは「構文解析木」(parse tree)とも「抽象構文木」(abstract syntax tree: AST)とも呼ばれる。構文エラーがあれば、ここで報告される。

3. 静的分析・意味分析(Analysis)

変数や関数、文が何を参照しているかを、スコープに応じて明らかにする。型チェックをする言語であれば、ここで行う。

4. 中間表現(Intermediate Representation:IR)

ソース言語ごとにIRを生成するフロントエンドを1つずつ作り、ターゲットのアーキテクチャx86, ARM,...)ごとにバックエンドを1つずつ書けば、少ない労力でサポートすることができる。

5. 最適化(Optimizing)

ユーザーのプログラムが何を意味するのかを理解したら、それと同じセマンティクスを保ちながら、もっと実行効率の良い別のプログラムと自由に交換することができる。

pennyArea = 3.14159 * (0.75 / 2) * (0.75 / 2);
⇒ pennyArea = 0.4417860938;

6. コード生成(Code Generation)

マシンが実行できる形式に変換(アセンブリ機械語VM用のバイトコード等)する処理。

7. 仮想マシン(Virtual Machine)

架空のチップをエミュレートし、命令実行をランタイムでシミュレートする。ネイティブコードに変換した場合より遅くなるが、単純さと移植性が得られる。

8. ランタイム(Runtime)

ガベージコレクタやオブジェクトの種類を調べる等、実行時に行う作業。完全にコンパイルされる言語では、結果として生成される実行ファイルにランタイムの実装コードが直接挿入される。Go言語ならGoランタイムのコピーが直接埋め込まれ、VM内で実行される言語(Java, Python, JavaScript)ならランタイムはVMに含まれる。

終わりに

このインタプリタの作り方という書籍は、Sci-Lispが初めてβ版として公開されるまでには一度は読み通しておく必要があると感じました。長く果てしない道のりにも感じますが、今回紹介したロードマップを浮かべて、今自分がどの位置にいるのかを把握しておけば幾ばくか安心感を得られます。

科学技術計算用プログラミング言語:Sci-Lispの自作(2日目):VSCode拡張機能開発・正規表現との格闘

Sci-Lisp開発の2日目記録です。

GitHubにも上げているとおり、まずは言語のexampleを考えました。

github.com

いずれ、字句解析・構文解析をすることになると思われるので、まずはVSCodeでSci-Lispシンタックスハイライトをできるようにしておきたいと思いました。 VSCode拡張機能開発については、公式ドキュメントや他のブログでも多数掲載されているのでそちらをごらんください。

ハマったポイントとしては、例えば複素数(1.0 + 2.3jなど)を完璧に識別するためにはかなり複雑な正規表現を書かなければならないということでした。私はSci-Lispにおける複素数は以下の正規表現で識別するとしました。(ほかにもっと良い書き方があるかもしれません)

{
  "name": "constant.numeric.complex.lisp",
  "match": "(([-+]?(?:\\d+\\.\\d*|\\.\\d+|\\d+)(?:[eE][-+]?\\d+)?[-+])?(?<![-+])[-+]?(?:\\d+\\.\\d*|(?<!\\d+)\\.\\d+|\\d+)(?:[eE][-+]?\\d+)?[jJ])"
},

ためしにシンタックスハイライトさせてみた例が以下の画像です。不正な複素数ははじき、様々なパターンの複素数は橙色に色付けされています。

この拡張機能については、まだテンプレートからほとんど変えていませんが、GitHubに上げています。

github.com