Cyan

Cyan, Yet Another New language - takuto_hの日記
http://www.geocities.jp/takt0_h/cyan/index.html
ほうほう。


実行の流れとしては、Parser#ParseでコードをパースしてCyanオブジェクト作成→Evaluatorオブジェクトを引数にCyBase#Compileを呼ぶとEvaluatorに中間言語コンパイル結果が格納される→Evaluator#Runで実行。という処理をLoaderクラス内でやってる。

エンジン部分はこのへん。命令文字列-引数のペアが格納されたリストから命令を拾ってきてリフレクションで同名メソッドを呼んでるわけか、なるほど。

//Evaluator#Run()より抜粋
while (instructions.Count > 0)
{
	insn = instructions[0];
	instructions.RemoveAt(0);

	GetType().GetMethod(insn.Key).Invoke(this, insn.Value);
}
return dataStack.Pop();

で、実際どんな命令が実行されるのか見てみる。

Evaluator#Run、PrintStateメソッド呼び出しのコメントをはずす。#if falseされてるメソッド本体も有効化しておく(最初気づかずに自分で書いてしまった)

変数の参照
cyan> Object
SetInfo (stdin):2,
Get Object,
 => #<Object>

まあ普通。SetInfoはソース内での位置を記録するための命令かな。

リスト
cyan> [12,3]
SetInfo (stdin):2
Push 1
Push 2
Push 3
Push []
Cons
Cons
Cons
 => [1, 2, 3]

おー。リスプだ。[]はnilの文字列表現

変数の代入
cyan> a=10
SetInfo (stdin):2
Push a
Send (=),&(10)
Push #<cont 00f90be8>
Push a
Push 10
Push []
Cons
MakeArguments
Send (=),[]
Push []
Cons
MakeArguments
Call []
 => 10

代入は内部的にはメソッドの呼び出し。
MakeArgumentsでスタックトップを引数にするための処理してからSendで呼ぶ。
メッセージ送るとき、継続も一緒に渡してるようだ。

quasiquote
cyan> `hoge
SetInfo (stdin):2
Push hoge
 => hoge
cyan> `?x
SetInfo (stdin):2
Get x
 => 10
cyan> `[1,2,x]
SetInfo (stdin):2
Push 1
Push 2
Push x
Push []
Cons
Cons
Cons
 => [1, 2, x]

cyan> `[1,2,?x]
SetInfo (stdin):2
Push 1
Push 2
Get x
Push []
Cons
Cons
Cons
 => [1, 2, 10]

コンパイルの段階で展開されてる。
CyBase#Compile/CyBase#QuoteCompile,CyQuasiQuote,CyUnquoteあたり。

実行環境、継続

Evaluatorの状態

private List<KeyValuePair<string, object[]>> instructions;
private Stack<CyBase> dataStack;
private Env environment;
private SourceInfo sInfo;

CyContinuationでもこれらを保持してるみたいですよと。
詳しい説明があった→http://www.geocities.jp/takt0_h/cyan/doc/ref/about-continuation.html

call/ccしてみる

cyan> cont=callcc(^(cont){cont})
  SetInfo (stdin):2
  Push cont
  Send (=) &(callcc(^(cont){ cont }))
  Push #<cont 029bf0b7>
  Push cont
  SetInfo (stdin):2
  Get callcc
  Call &(^(cont){ cont })
  Push #<cont 01695035>
  Push ^(func){ func(return) }
  Push $(cont)
  Push { cont }
  SetEnv
  MakeFunction
  Push []
  Cons
  MakeArguments
  Call []
  Push func
  Send (:=) &('^(cont){ cont })
  Push #<cont 0383196a>
  Push func
  Push ^(cont){ cont }
  Push []
  Cons
  MakeArguments
  Send (:=) []
  Push []
  Cons
  MakeArguments
  Call []
  Pop
  Push &(^(cont){ cont })
  Pop
  Push #<cont 01695035>
  SetInfo ./init/primitive.cy:10
  Get func
  Call &(return)
  Push #<cont 01695035>
  Push ^(cont){ cont }
  Get return
  Push []
  Cons
  MakeArguments
  Call []
  Push cont
  Send (:=) &('#<cont 01695035>)
  Push #<cont 020b77b0>
  Push cont
  Push #<cont 01695035>
  Push []
  Cons
  MakeArguments
  Send (:=) []
  Push []
  Cons
  MakeArguments
  Call []
  Pop
  Push &(#<cont 01695035>)
  Pop
  Push #<cont 01695035>
  SetInfo (stdin):1
  Get cont
  Push []
  Cons
  MakeArguments
  Call []
  Push []
  Cons
  MakeArguments
  Send (=) []
  Push []
  Cons
  MakeArguments
  Call []
 => #<cont 01695035>

うおう!

呼んでみると、

cyan> cont("hage")
  SetInfo (stdin):2
  SetInfo (stdin):2
  Get cont
  Call &("hage")
  Push #<cont 02f88675>
  Push #<cont 02a8480e>
  Push "hage"
  Push []
  Cons
  MakeArguments
  Call []
  Push []
  Cons
  MakeArguments
  Send (=) []
  Push []
  Cons
  MakeArguments
  Call []
 => "hage"
cyan> cont
  SetInfo (stdin):2
  Get cont
 => "hage"

なるほど