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. 特徴 • 仮想機械で実行 • 手続き型言語 • 再代入可能 • 静的型付き(型推論) •

    基本型(int, double, bool, string) • リスト、タプル、構造体 • ユーザ定義演算子 • クロージャ • 継続 • 並行実行・並列実行 • チャンネル通信
  2. プログラム例(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
  3. プログラム例(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!
  4. プログラム例(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() }
  5. 仮想機械 • スタックマシン • mallocでヒープ領域にスタックフレームなどを確保 • スタックフレームは連結リストになっている – 呼び出し元のスタックフレームを指す DynamicLink

    と、自分を 生成した関数のスタックフレームを指す StaticLink を持つ – 継続は、生成時にスタックフレームをコピーする方法で実現 • C++のスマートポインタ(shared_ptr)で参照カウンタ方式の ガベージコレクション(循環参照は回収されない)
  6.  ローカル変数領域|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
  7. 並行実行・並列実行 • C++のスレッドライブラリ(std::thread) を使用 • そのためマルチコア対応 並列実行 • 構文: async

    func(arg1, arg2, …, argn) <argn> …… <arg2> <arg1> <func> invoke 0 1 生成されるバイトコード
  8. チャンネル通信 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にスタックトップの値をセット ソ ー ス 生 成 さ れ る バ イ ト コ ー ド
  9. 機械語一覧(スタック) 名前 引数 説明 スタックの変化 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 ]
  10. 機械語一覧(算術演算・論理演算) 名前 引数 説明 スタックの変化 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 ]
  11. 機械語一覧(呼び出し・変数) 名前 引数 説明 スタックの変化 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 ]
  12. 機械語一覧(復帰・変数) 名前 引数 説明 スタックの変化 ret リターン [ ] →

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

    クロージャオブジェクトを生成、プッシュ [ ] → [ closure ] skip distance distanceをプログラム・カウンタへ加算 [ ] → [ ] iffalse_skip distance スタックトップがfalseの時distanceを プログラム・カウンタへ加算 [ bool ] → [ ] back distance distanceだけをプログラム・カウンタから減算 [ ] → [ ]
  14. 機械語一覧(比較演算) 名前 引数 説明 スタックの変化 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 ]
  15. 機械語一覧(リスト・構造体・継続) 名前 引数 説明 スタックの変化 makelist len スタックからlen個だけ 値を取ってきて リストオブジェクトを生成、

    プッシュ [ value1 , value2 , … , valuen ] → [ list ] makedata name, len メンバの数がlenの構造体を 生成 [ name1 , value1 , … , namen , valuen ] → [ structure ] makecontinuation 継続をスタックへプッシュ [ ] → [ continuation ] resume_continuation スタックトップの継続を 呼び出す [ continuation ] → [ ]
  16. 機械語一覧(チャンネル通信・スタック) 名前 引数 説明 スタックの変化 makechannel チャンネルオブジェクトを生成し スタックへプッシュ [ ]

    → [ channel ] channel_send スタックトップのチャンネルへ 送信 [ channel , value ] → [ ] channel_receive スタックトップのチャンネルから 受信 [ channel ] → [ value ] dup スタックトップの値を複製 [ value ] → [ value , value ] clean オペランドスタックを空にする [ …… ] → [ ]
  17. BNF(1) <eol> ::= ";" | "\n" | <EMPTY> <S> ::=

    <program> <program> ::= <EMPTY> | <program> <functiondef> | <program> "var" <variabledef_list> <eol> <type> ::= <IDENTIFIER> | "fun" "(" <type_list> ")" <operator_n> <type> | "[" <type> "]" | "(" <type_list> ")" | "continuation" "(" <type> ")" | "channel" "(" <type> ")" <type_list> ::= <EMPTY> | <type> { "," <type> }
  18. BNF(2) <functiondef> ::= "fun" <IDENT> "(" <param_list> ")" [ <op_n>

    <type> ] "{" <block> "}" | "fun" <op_n> <IDENT> "," <IDENT> "," <INTVAL> "(" <parameter_list> ")" [ <operator_n> <type> ] "{" <block> "}" <parameter_list> ::= <EMPTY> | <parameter_list> { "," <parameter> } <parameter> ::= <IDENTIFIER> ":" <type> <variabledef_list> ::= <variabledef> | <variabledef_list> "," <variabledef> <variabledef> ::= <IDENTIFIER> ":" <type> | <IDENTIFIER> [ ":" <type> ] <operator_n> <expression>
  19. BNF(3) <expression> ::= { <primary> | <operator_n> | <parenexpr> }+

    <intvalexpr> ::= <INTVAL> <doublevalexpr> ::= <DOUBLEVAL> <boolvalexpr> ::= <BOOLVAL> <stringvalexpr> ::= <STRINGVAL> <operator_n> ::= <OPERATOR> <funcallexpr> ::= ( <primary> | <parenexpr> ) "(" <arg_list> ")" <primary> ::= <intvalexpr> | <doublevalexpr> | <boolvalexpr> | <stringvalexpr> | <funcallexpr> | <closureexpr> | <callccexpr> | <variableexpr> | <listvalexpr> | <tuplevalexpr> | <dataexpr> | <newobjexpr> | <listrefexpr> | <datamemberrefexpr>
  20. BNF(4) <variableexpr> ::= <IDENTIFIER> <parenexpr> ::= "(" <expression> ")" <closureexpr>

    ::= "fun" "(" <parameter_list> ")" [ <operator_n> <type> ] "{" <block> "}" <callccexpr> ::= "callcc" "(" <IDENTIFIER> [ "," <type> ] “)" "{" <block> "}" <arg_list> ::= <EMPTY> | <expression> { "," <expression> } <tuple_list> ::= ( <expression> | <tuple_list> ) "," <expression> <listvalexpr> ::= "[" <arg_list> "]" <tuplevalexpr> ::= "(" <tuple_list> ")"
  21. BNF(5) <datadef> ::= "data" <IDENTIFIER> "{" <datamember_list> "}" <datamember_list> ::=

    <EMPTY> | <datamember_list> <IDENTIFIER> ":" <type> <eol> <dataexpr> ::= <IDENTIFIER> "{" <initassign_list> "}" <initassign_list> ::= <EMPTY> | <IDENTIFIER> <operator_n> <expression> | <initassign_list> "," <IDENTIFIER> <operator_n> <expression> <listrefexpr> ::= ( <primary> | <parenexpr> ) "[" <expression> "]" <datamemberrefexpr> ::= ( <primary> | <parenexpr> ) "." <IDENTIFIER> <newobjexpr> ::= "new" "(" <type> ")"
  22. BNF(6) <block> ::= <statement_list> <statement_list> ::= <EMPTY> | <statement_list> <statement>

    | <statement_list> "var" <variabledef_list> <eol> <statement> ::= <expression> <eol> | <returnstatement> <eol> | <asyncstatement> <eol> | <ifstatement> | <whilestatement> <whilestatement> ::= "while" "(" <expression> ")" "{" <block> "}" <asyncstatement> ::= "async" <expression> <ifstatement> ::= "if" "(" <expression> ")" "{" <block> "}" [ "else" "{" <block> "}" ] <returnstatement> ::= "return" [ <expression> ]
  23. 字句構造(1) • コメント – // 行末までコメント – /* … */

    複数行コメント 入れ子にできる • 識別子 – 英数字・アンダースコアの並び(先頭は数字不可) – 大文字小文字は区別される • キーワード var fun new channel if while else return true false data group continuation callcc async
  24. 字句構造(2) • リテラル – 整数リテラル • 10進表記のみ – 浮動小数点数リテラル –

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

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

    • 識別子 – ex: x _data Aichi – バイトコード: loadlocal, x, y ( x はスタックフレームを遡る回数、y は変数領域の番号、 x, y はコンパイル時に決定) • 呼び出し式 – callee( arg1 , arg2 , … , argn ) – バイトコード: <arg1>, <arg2>, …, <argn>, invoke, x, 0 末尾呼び出しならばxは1、違えば0
  27. 式(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 ] – バイトコード: <itemn>, …, <item2>, <item1>, (リストの長さ:n), makelist • タプルリテラル – ex: (“nagoya”, false, 758) – バイトコード: リストと同じ
  28. 式(4) • 構造体リテラル – ex: Point{x=10, y=50} – バイトコード: <exprn>,

    (fieldnのコンスタントプール番号), …, <expr1>, (field1のコンスタン トプール番号), makedata, (構造体名のコンスタントプール番号), (メンバ数: n) • new式 – ex: new( channel(int) ) – 現在はチャンネル型しか指定できない... – バイトコード: makechannel • リスト/タプル添字式 – ex: a[3] – バイトコード: <list>, <index>, loadbyindex
  29. 式(5) • 構造体メンバ参照式 – ex: b.size – バイトコード: <structure>, loadfield,

    (フィールド文字列のコンスタントプール番号) • 単項演算式 – ex: b! -6 – バイトコード: 専用の命令があればそれが使用される。 ユーザ定義演算子であれば、1引数関数の呼び出しとなる。 • 二項演算子 – ex: 1+1 3*6 – バイトコード: 専用の命令があればそれが使用される。 ユーザ定義演算子であれば、2引数関数の呼び出しとなる。 • 丸括弧で囲まれた式 – ( 123 + 456 )
  30. 文(1) • プログラムは文の集合 • 文の終端 – 改行またはセミコロン – ' }

    ' の手前の文は、セミコロンが省略できる • 変数宣言文 – var a:int, b=true – カンマで区切って複数の変数を一度に宣言できる – コロンの後に型を記述 • 省略する場合は、続いて初期化が必要 • 初期化する式で型推論が行われる • トップレベルでの変数宣言の場合は、型の記述が必須 • 再帰が行われる無名関数を代入する場合も、型の記述が必須 • 構造体宣言文 – data Point{ x: int y: int } – トップレベルにのみ記述できる
  31. 文(2) • 関数定義文 – トップレベルにのみ記述できる – fun name(arg1,arg2,…,argn)=>type{ … }

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

    <式>, ret_withvalue (戻り値有りの場合) | ret (戻り値無しの場合) • async文 – async [関数呼び出し式]; – バイトコード: <arg1>, <arg2>, …, <argn>, invoke, 0, 1 • if文 – if(cond){ … then_clause … }else{ … else_clause ... } – else節は省略可 – バイトコード: <cond>, iffalse_skip, (then_clauseの長さ +2), <then_clause>, skip, (else_clauseの長さ), <else_clause> • while文 – while(cond){ … clause ... } – バイトコード: <cond>, iffalse_skip, (clauseの長さ +2), <clause>, back, (clauseの長さ +3)
  33. 型(1) • 整数型: int – C++のint相当 • 浮動小数点数型: double –

    C++のdouble相当 • 論理型: bool – C++のbool相当 • 文字列型: string – 内部でC++のstringを利用 • リスト型: [ t ] – 内部でC++のlistを利用 – 範囲外アクセスを行うとランタイムエラーになる
  34. 型(2) • タプル型: ( t1, t2, …, tn) – 内部でC++のlistを利用

    – リストと同じように要素の参照、書き換えが可能 – アクセス時の添字は定数である必要がある(型を決定するため) • 構造体型 – 内部でC++のmapを利用 • 関数型: fun( args )=>t • 継続型: continuation( t ) • チャンネル型: channel( t )
  35. 組み込み関数(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の型変換
  36. 組み込み関数(2) • 出力 – void print(string) – void print_int(int) –

    void print_double(double) – void print_bool(bool) • 並列 – int hardware_concurrency() //論理コア数を取得 – void sleep(int) //指定時間(ミリ秒単位)待つ
  37. 組み込み関数(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)
  38. 優先順位 演算子 結合性 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項: 代入) 右結合 演算子