共用型がまさかの完成

共用型を対応するためにRoku側のコードを書いて
CILへの対応は一旦置いといてコンパイルしてみたところ、なんと期待通りに動いてしまった!
共用型はとりあえず1個目の型に変換しとくってロジックがあるだけなのでCILでは正しいわけがない
せやからCILでは実行時型エラーになるかと思ってたら、CILって型チェックせーへんのかい!?

f:id:zenu:20171106020320p:plain

関数fの中身はswitch文やから、呼べたらそらまぁ動くとして
f(x: Circle)って関数が一個だけ作られて呼び出し側がTriangleやRectangleでも
へーきな顔して呼びよる
なんでも来い状態やないか

真面目に対応するモチベ下がるわ、ホンマ~

switch対応 その2

型によるcase式に対応できた
参照型変数は「ldloc→isinst→ldnull→cgt.un」でいいんだけど値型変数だとうまくいかない
結局、値型変数は「ldloc→box→isinst→ldnull→cgt.un」にした
boxが出てくるのはどーよって気がしなくもないけど、まぁいっか

f:id:zenu:20171010212115p:plain

しかし、caseとその後ろの式の区切りに「:」を使ったのは失敗だったかなぁ
ほかんところはPascal式の「var n: Int」なのにcase部分だけコロンが使えないせいでC式?の「Int n」っちゅー不思議文法になってしまった

caseと式の区切りの記号は何がいいだろう
イコールはすっげー気持ち悪いしな、Haskellのガードとか
矢印かなー、こんな感じの

switch x
	n: Int    => print("Int")
	s: String => print("String " + s)
	h: Hoge   => print(h.i)

switch対応

switch文に対応してみた
まだswitchは式にできてないので戻り値は返せないけども

とりあえず[x, xs]のxs部分にcdr相当を渡したいんだけど
めっちゃめんどくさいので単純に配列をGetRangeしてコピーすることにした
GetRangeはご丁寧にもMSDNにO(n)っすよって書いてあるけど、もうほんとめんどくさいしいいや

f:id:zenu:20170924230018p:plain

コンパイルした結果がこれ
わらっちゃうくらいザツい(^-^;

.method public static void  switch.show(class [mscorlib]System.Collections.Generic.List`1<int32> A_0) cil managed
{
  // コード サイズ       96 (0x60)
  .maxstack  7
  .locals init (int32 V_0,
           bool V_1,
           int32 V_2,
           int32 V_3,
           class [mscorlib]System.Collections.Generic.List`1<int32> V_4)
  IL_0000:  ldarg.0
  IL_0001:  call       instance int32 class [mscorlib]System.Collections.Generic.List`1<int32>::get_Count()
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  ldc.i4.0
  IL_0009:  ceq
  IL_000b:  stloc.1
  IL_000c:  ldloc.1
  IL_000d:  brtrue     IL_0017

  IL_0012:  br         IL_001c

  IL_0017:  br         IL_005f

  IL_001c:  ldloc.0
  IL_001d:  ldc.i4.1
  IL_001e:  clt
  IL_0020:  ldc.i4.0
  IL_0021:  ceq
  IL_0023:  stloc.1
  IL_0024:  ldloc.1
  IL_0025:  brtrue     IL_002f

  IL_002a:  br         IL_005f

  IL_002f:  ldarg.0
  IL_0030:  ldc.i4.0
  IL_0031:  call       instance !0 class [mscorlib]System.Collections.Generic.List`1<int32>::get_Item(int32)
  IL_0036:  stloc.2
  IL_0037:  ldloc.0
  IL_0038:  ldc.i4.1
  IL_0039:  sub
  IL_003a:  stloc.3
  IL_003b:  ldarg.0
  IL_003c:  ldc.i4.1
  IL_003d:  ldloc.3
  IL_003e:  call       instance class [mscorlib]System.Collections.Generic.List`1<!0> class [mscorlib]System.Collections.Generic.List`1<int32>::GetRange(int32,
                                                                                                                                                         int32)
  IL_0043:  stloc      V_4
  IL_0047:  nop
  IL_0048:  nop
  IL_0049:  ldloc.2
  IL_004a:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_004f:  ldloc      V_4
  IL_0053:  nop
  IL_0054:  nop
  IL_0055:  call       void switch.show(class [mscorlib]System.Collections.Generic.List`1<int32>)
  IL_005a:  br         IL_005f

  IL_005f:  ret
} // end of global method switch.show

.NETのYieldが何をしているか

.NETのYieldがどんなふうにILを出力してるんか調べてみた
VBだけ調べたけどC#も多分一緒だろう
割とcall/ccするのめんどいな

1. Yieldの付いた関数毎にIEnumeratorなクラスを作る、とりあえずStateMachineと呼ぶ
2. Yieldの付いた関数の引数、ローカル変数を全部メンバ変数に持つ
3. 戻り値と状態もメンバ変数に持つ
4. Yieldの出現箇所にユニークな連番を振る
5. Yieldしている個所の前で次のことをする
・Yieldの値を戻り値のメンバ変数に入れる
・4.で付けたユニークな番号を状態のメンバ変数に入れる
・Return Trueしとく
・Returnの後にユニークな番号に対応したラベルを張る
6. 関数を抜けるときに次のことをする
・最終行でっせというラベルを張る
・状態のメンバ変数に最終行でっせという値を入れる
・Return Falseする

' 呼び出し側
For Each x In Cdr(xs)
    ' なんか処理
Next

Public Shared Iterator Function Cdr(xs As List(Of Integer))
	For i = 1 To xs.Count - 1
		Yield xs(i)
	Next
End Function' 呼び出し側
For Each x In New StateMachine With {._xs = xs}
    ' なんか処理
Next

' Yieldの関数展開結果
Class StateMachine
	Implements IEnumerator

	Private _i As Integer
	Private _xs As List(Of Integer)
	
	Private _value As Integer
	Private _state_machine As Integer = 0
	
	Public ReadOnly Property Current() As Boolean
		Get
			Return _value
		End Get
	End Property
	
	Public Function MoveNext() As Boolean
		
		If _state_machine = 1 Then Goto __STATE1
		If _state_machine = 2 Then Goto __STATE2
		
		_i = 1
		Do While _i <= _xs.Count - 1
			
			_value = _xs(_i)
			_state_machine = 1
			Return True
			__STATE1:
			
			_i += 1
		Loop
		
		__STATE2:
		_state_machine = 2
		Return False
	End Function
End Class

簡単にcdrするILの関数書きたいだけなんだけど難しいなー・・・

関数の戻り値型から空配列の型解決

なんのこっちゃか分かりにくいけど
戻り値の型から空配列の型解決ができるようになった
f:id:zenu:20170913204222p:plain

型解決をするために配列を正規化している

return([])
↓
$0 = []
return($0)

これをやると問題になるのがこんなやつ

xs = [Int]()
↓
$0 = [Int]
xs = $0()

Intってのが型解決してみるまで変数なのか型なのか分からないので$0が配列型か配列か分からない
ってゆーか、変数に型の代入ができなかった


Nodeにインスタンスか型なのかのフラグを付けて対応したけど、これができひんのは分かっている
どうしようもないので後で考える

var xs = []
xs.Add(Int)
var i = xs[0]()

関数内型定義

関数内で型が定義できるようになった
インナー関数が作れたついでにやってみたらすぐできた
メソッド的なものは作るつもりはないので大体そろった感じ
f:id:zenu:20170903023907p:plain
次はなにつくろっかなー
例外関係かな