Upgrade to Pro — share decks privately, control downloads, hide ads and more …

オレオレ言語 soramame の紹介

オレオレ言語 soramame の紹介

Daiki Matsunaga

August 16, 2015
Tweet

More Decks by Daiki Matsunaga

Other Decks in Programming

Transcript

  1. オレオレ言語 soramame の紹介
    松永 大輝

    View Slide

  2. 自己紹介
    ● 松永 大輝 (@matsu1834)
    ● 京都工芸繊維大学 1年生
    ● 滋賀から来ました
    ● カーネルVM 初参加
    1000km

    View Slide

  3. 特徴
    ● 仮想機械で実行
    ● 手続き型言語
    ● 再代入可能
    ● 静的型付き(型推論)
    ● 基本型(int, double, bool, string)
    ● リスト、タプル、構造体
    ● ユーザ定義演算子
    ● クロージャ
    ● 継続
    ● 並行実行・並列実行
    ● チャンネル通信

    View Slide

  4. プログラム例(1)
    //クロージャのテスト
    fun make_adder()=> fun()=>int{
    var n=0
    return fun(){
    n=n+1; return n;
    }
    }
    fun main(){
    var a=make_adder()
    var b=make_adder()
    print_int(a())
    print_int(b())
    print_int(a())
    print_int(b())
    }
    出力:
    1122

    View Slide

  5. プログラム例(2)
    //並行実行・チャンネル通信のテスト
    fun main(){
    var c=new(channel(int))
    async fun(){
    sleep(1000)
    print(“hello\n”) ; c ! 1;
    }()
    async fun(){
    sleep(2000)
    print(“world!\n”) ; c ! 2;
    }()
    c?;
    c?;
    }
    出力:
    (1秒待つ)
    hello
    (もう1秒待つ)
    world!

    View Slide

  6. プログラム例(3)
    /* リサージュ曲線 */
    fun main(){
    glut_openwindow("Lissajous")
    glut_setdisplayfunc(fun(){
    glut_clear()
    glut_begin_point()
    var a=4.0,b=3.0,t=0.0
    while(t<1000.0){
    glut_color3i(255,0,0)
    glut_vertex2i(d2i(sin(a*t)*50.0)+100,
    d2i(cos(b*t)*50.0)+100)
    t=t+1.0
    }
    glut_end()
    glut_flush()
    })
    glut_mainloop()
    }

    View Slide

  7. パーサ
    ● ファイル単位でパース
    ● 自作LRパーサ
    (BNFを配列の形でソースコード内に記述)
    ● 式のパースを、ユーザ定義演算子のために
    あとから行う

    View Slide

  8. コンパイラ
    ● 構文木を生成し、型検査を行い、コード生成
    ● 定数畳み込み
    ● 関数のアドレス・double値などは
    定数領域(コンスタントプール)へ登録
    ● 関数定義・グローバル変数には型を書かせる
    ● ローカル変数の型を省略した場合、初期値から型推論

    View Slide

  9. コードジェネレータ
    ● 末尾呼び出し・asyncのコード生成
    – 呼び出しのバイトコード(invoke)の後ろに
    即値で印をつけておく
    – 実行時にそれを見て、末尾呼び出しだと分かれば
    不要なものを記憶しない。asyncなら、新たにス
    レッドを生成。

    View Slide

  10. 仮想機械
    ● スタックマシン
    ● mallocでヒープ領域にスタックフレームなどを確保
    ● スタックフレームは連結リストになっている
    – 呼び出し元のスタックフレームを指す DynamicLink と、自分を
    生成した関数のスタックフレームを指す StaticLink を持つ
    – 継続は、生成時にスタックフレームをコピーする方法で実現
    ● C++のスマートポインタ(shared_ptr)で参照カウンタ方式の
    ガベージコレクション(循環参照は回収されない)

    View Slide

  11.  ローカル変数領域|x=100
     オペランドスタック
     プログラム・カウンタ
     関数へのポインタ
    関数:
    main
    x=999
    関数:f
    仮想機械
    (ヒープ領域)
    大域環境
    var x:int = 100
    fun main(){
    var x=999 //ここではx=999
    f()
    }
    fun f(){
    print_int(x) //ここではx=100
    }
    StaticLink
    DynamicLink

    View Slide

  12. 並行実行・並列実行
    ● C++のスレッドライブラリ(std::thread)
    を使用
    ● そのためマルチコア対応
    並列実行
    ● 構文:
    async func(arg1, arg2, …, argn)

    ……



    invoke
    0
    1
    生成されるバイトコード

    View Slide

  13. チャンネル通信
    ● std::threadのcondition_variable(条件変数)を利用し、
    スレッドの中断・再開を実装
    ● チャンネルはFIFOバッファ(動的に確保、サイズは無限)を持つ
    ● 受信時に受け取る値がなければそのスレッドはブロックされ

    ● 構文:
    chan ! value //チャンネルへ送信
    chan? //チャンネルから受信

    View Slide

  14. チャンネル通信
    var chan = new( channel( int ) ) //チャンネルオブジェクト生成
    chan ! 3    //チャンネルへ値 3 を送信
    var a = chan?    //チャンネルから受信
    makechannel チャンネルオブジェクトをスタックにプッシュ
    storelocal00 ローカル変数0にスタックトップの値をセット
    pushi3 整数 3 をプッシュ
    loadlocal00 ローカル変数0の値をプッシュ
    channel_send スタックトップのチャンネルに値送信
    loadlocal00 ローカル変数0の値をプッシュ
    channel_receive チャンネルから受信し、プッシュ
    storelocal01 ローカル変数1にスタックトップの値をセット














    View Slide

  15. デモ
    ● スリープソート
    ● 15パズル
    ● マンデルブロ集合

    View Slide

  16. 御清聴ありがとうございました

    View Slide

  17. 機械語一覧(スタック)
    名前 引数 説明 スタックの変化
    ipush value 整数値プッシュ [ ] → [ int ]
    bpush value 論理値プッシュ [ ] → [ bool ]
    pushnull nullプッシュ [ ] → [ ref(null) ]
    pushim1 定数(-1)プッシュ [ ] → [ int ]
    pushi0 定数(0)プッシュ [ ] → [ int ]
    pushi1 定数(1)プッシュ [ ] → [ int ]
    pushi2 定数(2)プッシュ [ ] → [ int ]
    pushi3 定数(3)プッシュ [ ] → [ int ]
    pushi4 定数(4)プッシュ [ ] → [ int ]
    pushi5 定数(5)プッシュ [ ] → [ int ]
    ldc number
    コンスタントプールの指定番号
    のアイテムをプッシュ
    [ ] → [ value ]

    View Slide

  18. 機械語一覧(算術演算・論理演算)
    名前 引数 説明 スタックの変化
    iadd 整数足し算 [ int , int ] → [ int ]
    dadd 実数足し算 [ double , double ] → [ double ]
    isub 整数引き算 [ int , int ] → [ int ]
    dsub 実数引き算 [ double , double ] → [ double ]
    imul 整数掛け算 [ int , int ] → [ int ]
    dmul 実数掛け算 [ double , double ] → [ double ]
    idiv 整数割り算 [ int , int ] → [ int ]
    ddiv 実数割り算 [ double , double ] → [ double ]
    band 論理積 [ bool , bool ] → [ bool ]
    bor 論理和 [ bool , bool ] → [ bool ]
    imod 余り [ int , int ] → [ int ]
    ineg 整数・符号反転 [ int ] → [ int ]
    bnot 否定 [ bool ] → [ bool ]
    dneg 実数・符号反転 [ double ] → [ double ]
    ilshift 左シフト [ int , int ] → [ int ]

    View Slide

  19. 機械語一覧(呼び出し・変数)
    名前 引数 説明 スタックの変化
    invoke
    is_tail, 
    is_async
    呼び出し
    [ closure/continuation ,
    arg1 , arg2, … , argn ]
    → [ ]
    loadlocal
    flame,
    index
    指定位置のローカル変数の値を
    プッシュ
    [ ] → [ value ]
    loadbyindex
    リストの指定されたインデックス
    の値をプッシュ
    [ int , list ] → [ value ]
    loadfield name
    スタックトップの構造体の
    フィールドnameの値をプッシュ
    [ structure ] → [ value ]
    loadlocal00
    現在のスタックフレームの変数領
    域の0番目の値をプッシュ
    [ ] → [ value ]
    ・・・
    loadlocal05
    現在のスタックフレームの変数領
    域の5番目の値をプッシュ
    [ ] → [ value ]

    View Slide

  20. 機械語一覧(復帰・変数)
    名前 引数 説明 スタックの変化
    ret リターン [ ] → [ ]
    ret_withvalue
    スタックトップの値を
    戻り先のオペランドスタックにプッシュし、
    リターン
    [ value ] →
    [ 戻り先: value ]
    storelocal
    flame,
    index
    指定された変数領域の値を
    スタックトップの値にする
    [ value ] → [ ]
    storefield name
    スタックトップの構造体の
    フィールドnameの値を変更
    [ structure , value ]
    → [ ]
    storebyindex
    リストの、
    指定されたインデックスの値を変更
    [ int , list , value ]
    → [ ]
    storelocal00
    現在のフレームの0番目の変数領域の値を
    スタックトップの値にする
    [ value ] → [ ]
    ・・・
    storelocal05
    現在のフレームの5番目の変数領域の値を
    スタックトップの値にする
    [ value ] → [ ]

    View Slide

  21. 機械語一覧(クロージャ生成・ジャンプ)
    名前 引数 説明
    スタックの
    変化
    makeclosure number
    コンスタントプール:numberにある関数情報と
    現在のスタックフレームから
    クロージャオブジェクトを生成、プッシュ
    [ ] →
    [ closure ]
    skip distance distanceをプログラム・カウンタへ加算 [ ] → [ ]
    iffalse_skip distance
    スタックトップがfalseの時distanceを
    プログラム・カウンタへ加算
    [ bool ] → [ ]
    back distance distanceだけをプログラム・カウンタから減算 [ ] → [ ]

    View Slide

  22. 機械語一覧(比較演算)
    名前 引数 説明 スタックの変化
    icmpeq == (int) [ int , int ] → [ bool ]
    icmpne != (int) [ int , int ] → [ bool ]
    icmplt < (int) [ int , int ] → [ bool ]
    icmple <= (int) [ int , int ] → [ bool ]
    icmpgt > (int) [ int , int ] → [ bool ]
    icmpge >= (int) [ int , int ] → [ bool ]
    dcmpeq == (double) [ double , double ] → [ bool ]
    dcmpne != (double) [ double , double ] → [ bool ]
    dcmplt < (double) [ double , double ] → [ bool ]
    dcmple <= (double) [ double , double ] → [ bool ]
    dcmpgt > (double) [ double , double ] → [ bool ]
    dcmpge >= (double) [ double , double ] → [ bool ]
    bcmpeq == (bool) [ bool , bool ] → [ bool ]
    bcmpne != (bool) [ bool , bool ] → [ bool ]

    View Slide

  23. 機械語一覧(リスト・構造体・継続)
    名前 引数 説明 スタックの変化
    makelist len
    スタックからlen個だけ
    値を取ってきて
    リストオブジェクトを生成、
    プッシュ
    [ value1 , value2 ,
    … , valuen ]
    → [ list ]
    makedata name, len
    メンバの数がlenの構造体を
    生成
    [ name1 , value1 , … ,
    namen , valuen ]
    → [ structure ]
    makecontinuation 継続をスタックへプッシュ [ ] → [ continuation ]
    resume_continuation
    スタックトップの継続を
    呼び出す
    [ continuation ] → [ ]

    View Slide

  24. 機械語一覧(チャンネル通信・スタック)
    名前 引数 説明 スタックの変化
    makechannel
    チャンネルオブジェクトを生成し
    スタックへプッシュ
    [ ] → [ channel ]
    channel_send
    スタックトップのチャンネルへ
    送信
    [ channel , value ] → [ ]
    channel_receive
    スタックトップのチャンネルから
    受信
    [ channel ] → [ value ]
    dup スタックトップの値を複製
    [ value ]
    → [ value , value ]
    clean オペランドスタックを空にする [ …… ] → [ ]

    View Slide

  25. BNF(1)
    ::= ";" | "\n" |
    ::=
    ::=
    |
    | "var"
    ::=
    | "fun" "(" ")"
    | "[" "]"
    | "(" ")"
    | "continuation" "(" ")"
    | "channel" "(" ")"
    ::=
    | { "," }

    View Slide

  26. BNF(2)
    ::=
    "fun" "(" ")" [ ] "{" "}"
    | "fun" "," "," "(" ")"
    [ ] "{" "}"
    ::=
    | { "," }
    ::= ":"
    ::=
    | ","
    ::= ":"
    | [ ":" ]

    View Slide

  27. BNF(3)
    ::= { | | }+
    ::=
    ::=
    ::=
    ::=
    ::=
    ::= ( | ) "(" ")"
    ::= | |
    | | |
    | | |
    | | |
    | |

    View Slide

  28. BNF(4)
    ::=
    ::= "(" ")"
    ::= "fun" "(" ")"
    [ ] "{" "}"
    ::= "callcc" "(" [ "," ]
    “)" "{" "}"
    ::=
    | { "," }
    ::= ( | ) ","

    ::= "[" "]"
    ::= "(" ")"

    View Slide

  29. BNF(5)
    ::= "data" "{" "}"
    ::=
    | ":"
    ::= "{" "}"
    ::=
    |
    | ","

    ::= ( | ) "["
    "]"
    ::= ( | ) "."

    ::= "new" "(" ")"

    View Slide

  30. BNF(6)
    ::=
    ::=
    |
    | "var"
    ::=
    |
    |
    |
    |
    ::= "while" "(" ")" "{" "}"
    ::= "async"
    ::= "if" "(" ")" "{" "}"
    [ "else" "{" "}" ]
    ::= "return" [ ]

    View Slide

  31. 字句構造(1)
    ● コメント
    – // 行末までコメント
    – /* … */ 複数行コメント 入れ子にできる
    ● 識別子
    – 英数字・アンダースコアの並び(先頭は数字不可)
    – 大文字小文字は区別される
    ● キーワード
    var fun new channel if while else
    return true false data group
    continuation callcc async

    View Slide

  32. 字句構造(2)
    ● リテラル
    – 整数リテラル
    ● 10進表記のみ
    – 浮動小数点数リテラル
    – 文字リテラル
    ● シングルクオーテーションで1文字を囲んだもの
    ● 整数(ASCIIコード値)として扱われる
    – 文字列リテラル
    ● ダブルクオーテーションで文字列を囲んだもの
    ● 以下のエスケープシーケンスを使用可
    – 改行(\n)、円記号(\\)、水平タブ(\t)、垂直タブ(\v)、バックスペース(\b)、シングルクオーテーション(\')、
    ダブルクオーテーション(\")、ベル文字(\a)
    – 真偽値リテラル
    ● true / false
    ● 演算子
    ● 以下の文字の1〜6文字の並び
    – %$\#=~|^+-*/<>&!?@

    View Slide

  33. 式(1)
    ● 整数リテラル
    – ex: 758
    – バイトコード:
    ipush , (数値)
    -1〜5の場合は専用の命令有り
    ● 浮動小数点数リテラル
    – ex: 1.4142
    – バイトコード:
    ldc, (コンスタントプール番号...コンパイル時に決定)
    ● 文字リテラル
    – ex: 'A'
    ● 真偽値リテラル
    – ex: true
    – バイトコード:
    bpush, (0/1)

    View Slide

  34. 式(2)
    ● 文字列リテラル
    – ex: “hello,world”
    – バイトコード:
    ldc, (コンスタントプール番号...コンパイル時に決定)
    ● 識別子
    – ex: x _data Aichi
    – バイトコード:
    loadlocal, x, y
    ( x はスタックフレームを遡る回数、y は変数領域の番号、
    x, y はコンパイル時に決定)
    ● 呼び出し式
    – callee( arg1 , arg2 , … , argn )
    – バイトコード:
    , , …, , invoke, x, 0
    末尾呼び出しならばxは1、違えば0

    View Slide

  35. 式(3)
    ● クロージャリテラル
    – fun(arg1,arg2,...argn)=>type { ... }
    – バイトコード:
    makeclosure, (コンスタントプールの番号...コンパイル時に決まる)
    ● call/cc式
    – callcc(varname, type){ … }
    – typeは式自身の型
    – typeは省略可(void型となる)
    – バイトコード:
    makecontinuation, makeclosure, (コンスタントプールの番号), invoke, 0, 0
    ● リストリテラル
    – ex: [ 1, 2, 3, 4, 5 ]
    – バイトコード:
    , …, , , (リストの長さ:n), makelist
    ● タプルリテラル
    – ex: (“nagoya”, false, 758)
    – バイトコード: リストと同じ

    View Slide

  36. 式(4)
    ● 構造体リテラル
    – ex: Point{x=10, y=50}
    – バイトコード:
    , (fieldnのコンスタントプール番号), …, , (field1のコンスタン
    トプール番号), makedata, (構造体名のコンスタントプール番号), (メンバ数: n)
    ● new式
    – ex: new( channel(int) )
    – 現在はチャンネル型しか指定できない...
    – バイトコード:
    makechannel
    ● リスト/タプル添字式
    – ex: a[3]
    – バイトコード:
    , , loadbyindex

    View Slide

  37. 式(5)
    ● 構造体メンバ参照式
    – ex: b.size
    – バイトコード:
    , loadfield, (フィールド文字列のコンスタントプール番号)
    ● 単項演算式
    – ex: b! -6
    – バイトコード:
    専用の命令があればそれが使用される。
    ユーザ定義演算子であれば、1引数関数の呼び出しとなる。
    ● 二項演算子
    – ex: 1+1 3*6
    – バイトコード:
    専用の命令があればそれが使用される。
    ユーザ定義演算子であれば、2引数関数の呼び出しとなる。
    ● 丸括弧で囲まれた式
    – ( 123 + 456 )

    View Slide

  38. 文(1)
    ● プログラムは文の集合
    ● 文の終端
    – 改行またはセミコロン
    – ' } ' の手前の文は、セミコロンが省略できる
    ● 変数宣言文
    – var a:int, b=true
    – カンマで区切って複数の変数を一度に宣言できる
    – コロンの後に型を記述
    ● 省略する場合は、続いて初期化が必要
    ● 初期化する式で型推論が行われる
    ● トップレベルでの変数宣言の場合は、型の記述が必須
    ● 再帰が行われる無名関数を代入する場合も、型の記述が必須
    ● 構造体宣言文
    – data Point{
    x: int
    y: int
    }
    – トップレベルにのみ記述できる

    View Slide

  39. 文(2)
    ● 関数定義文
    – トップレベルにのみ記述できる
    – fun name(arg1,arg2,…,argn)=>type{ … }
    – 戻り値の型を省略した場合、void型となる
    ● 演算子定義文
    – トップレベルにのみ記述できる
    – fun op optype , assoc , pred (...)=>type{ … }
    – opは演算子
    – optypeはunary又はbinary
    – assocはleft又はright
    – predは優先順位
    – 1・2引数関数の定義である
    (実際に、関数と同じ領域に登録され、
    ユーザ定義演算子を使用すると関数呼び出しのコードが吐かれる)

    View Slide

  40. 文(3)
    ● 式文
    ● return文
    – return [式];
    – バイトコード:
    <式>, ret_withvalue (戻り値有りの場合) | ret (戻り値無しの場合)
    ● async文
    – async [関数呼び出し式];
    – バイトコード:
    , , …, , invoke, 0, 1
    ● if文
    – if(cond){ … then_clause … }else{ … else_clause ... }
    – else節は省略可
    – バイトコード:
    , iffalse_skip, (then_clauseの長さ +2), , skip, (else_clauseの長さ),
    ● while文
    – while(cond){ … clause ... }
    – バイトコード:
    , iffalse_skip, (clauseの長さ +2), , back, (clauseの長さ +3)

    View Slide

  41. 型(1)
    ● 整数型: int
    – C++のint相当
    ● 浮動小数点数型: double
    – C++のdouble相当
    ● 論理型: bool
    – C++のbool相当
    ● 文字列型: string
    – 内部でC++のstringを利用
    ● リスト型: [ t ]
    – 内部でC++のlistを利用
    – 範囲外アクセスを行うとランタイムエラーになる

    View Slide

  42. 型(2)
    ● タプル型: ( t1, t2, …, tn)
    – 内部でC++のlistを利用
    – リストと同じように要素の参照、書き換えが可能
    – アクセス時の添字は定数である必要がある(型を決定するため)
    ● 構造体型
    – 内部でC++のmapを利用
    ● 関数型: fun( args )=>t
    ● 継続型: continuation( t )
    ● チャンネル型: channel( t )

    View Slide

  43. 組み込み関数(1)
    ● 数学
    – int rand()
    – int abs()
    – double sin(double)
    – double cos(double)
    – double tan(double)
    – double asin(double)
    – double acos(double)
    – double atan(double)
    – double sqrt(double)
    – int pow(int,int)
    ● 型変換
    – double i2d(int) //int→doubleの型変換
    – int d2i(double) //double→intの型変換

    View Slide

  44. 組み込み関数(2)
    ● 出力
    – void print(string)
    – void print_int(int)
    – void print_double(double)
    – void print_bool(bool)
    ● 並列
    – int hardware_concurrency() //論理コア数を取得
    – void sleep(int) //指定時間(ミリ秒単位)待つ

    View Slide

  45. 組み込み関数(3)
    ● グラフィックス(GLUT)
    – void glut_mainloop()
    – void glut_clear()
    – void glut_flush()
    – void glut_begin_point()
    – void glut_begin_line()
    – void glut_begin_strip()
    – void glut_begin_lineloop()
    – void glut_begin_triangle()
    – void glut_begin_quad()
    – void glut_begin_trianglefan()
    – void glut_begin_polygon()
    – void glut_end()
    – void glut_postredisp()
    – void glut_setkeyboardfunc(fun(int,int,int)=>void)
    – void glut_setmousefunc(fun(int,int,int,int)=>void)
    – void glut_openwindow(string) //タイトルを指定
    – void glut_vertex2i(int,int)
    – void glut_color3i(int,int,int) //RGBをそれぞれ0~255の範囲で指定
    – void glut_char(int)

    View Slide

  46. 優先順位 演算子 結合性
    70
    -(単項: 符号反転)!(単項: 論理否定 )
    ?(単項: チャンネル受信)
    @?(単項: リスト長)@>(単項: cdr)@<(単項: car)
    右結合
    40 *(2項)/(2項)%(2項)
    左結合
    20
    +(2項)-(2項)
    @+(2項: リスト連結)
    10 <<(2項)>>(2項)
    8 >(2項)>=(2項)<(2項)<=(2項)!=(2項)==(2項)
    5 &&(2項)||(2項)
    2
    !(2項: チャンネル送信)
    =(2項: 代入)
    右結合
    演算子

    View Slide