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

ユーザが構文を自由に変更できるプログラミング言語

 ユーザが構文を自由に変更できるプログラミング言語

第5期サイボウズ・ラボユース成果報告会
言語処理系ゼミ

03a3fc5381358c8b29b0a89f49b1e317?s=128

Akama Hitoshi

March 30, 2016
Tweet

Transcript

  1. ユーザが構文を自由に変更できる プログラミング言語 第5期サイボウズラボ・ユース成果報告会 言語処理系ゼミ 2016/03/30 東京工業大学 理学部 情報科学科 赤間 仁志

  2. 目的 • 構文を自由に変更できるプログラミング言語を作れないか? • ユーザが新しい構文を導入するメリット: ◦ 簡潔な見た目 ◦ 少ない記述量で望んだ処理を行う •

    書いてる途中にどんどん構文が変わっていく言語 ◦ 面白そう ◦ 個人的な興味 2
  3. 構文を変更できるとうれしい例 in C • 固定回数のループ文 • forループ特有の条件部分を書かなくてよい for (int i

    = 0; i < 10; i++) { printf(“Hello, %d\n”, i); } times(i, 10) { printf(“Hello, %d\n”, i); } Before After 3 #define times(i, n) \ for (int i; i < (n); i++)
  4. 構文を変更できる言語の要件 1. 処理系を変更することで構文を変更するのはダメ ◦ ユーザの手によって構文が変更できること 2. 構文を追加することも,削除することもできること ◦ 不要になった構文もユーザの手で削除できる 3.

    構文を定義する構文も作成できること ◦ 構文の定義方法もユーザが作成可能 4 画像素材:http://www.irasutoya.com/
  5. プログラミング言語Garbanzo(仮) ver. 2 • 動的型つけ • インタプリタ方式 ◦ いくつかの命令を搭載 •

    組み込みの構文は最小限 ◦ ほぼ構文木をそのまま ◦ どんどん構文を拡張可能 5 {“@”: “print”, {“@”: “append”, “left”: “Hello”, “right”: ”World” } } 画像素材:http://www.irasutoya.com/
  6. 一般的なインタプリタ 構文解析器 評価器 AST ソース コード 結果 “(3 + 4)

    * 6” * 3 4 6 + 42 6
  7. Garbanzoでの構文解析 • 実行にともない,構文解析ルールを書き換える ◦ 文を1つずつ読み込んで実行 • 構文解析器そのものがGarbanzoのプログラム ◦ Garbanzoの評価器の上で動作 •

    構文解析ルールはファーストクラスの値 ◦ プログラム内で操作できる 7 プログラム 構文を変更する 宣言・命令 新しい構文で 記述したプログラム 7
  8. 実行の流れ 構文解析器 評価器 AST ソース コード 結果 構文解析 実行 ルールの

    追加・削除 8
  9. 構文の定義 • 組込の構文は,構文木をそのまま書くので,なんでもできる • けど長ったらしい 9 {"@": "set", "object": {"@":

    "get", "object": {"@": "get", "object": {"@": "get", "object": {"@": "get", "object": {"@": "getenv" }, "key": "/" }, "key": "parser" }, "key": "sentence" }, "key": "children" }, "key": "newline", "value": {"@": "quote", "value": {"@": "scope", "body": {"0": {"@": "terminal", "string": "\n" } } } } }
  10. 自動生成 • 構文の定義をバージョン1のコードで自動生成 ◦ 共通のランタイム • バージョン1(自作言語)でバージョン2(自作言語)の開発 ◦ デバッガなし ◦

    リファレンスなし ◦ 質問する相手なし • 生成された構文をGarbanzoの初期構文とする ◦ 標準ライブラリ的な構文 10
  11. 自動生成 • 構文の定義をバージョン1のコードで自動生成 ◦ 共通のランタイム • バージョン1(自作言語)でバージョン2(自作言語)の開発 ◦ デバッガなし ◦

    リファレンスなし ◦ 質問する相手なし • 生成された構文をGarbanzoの初期構文とする ◦ 標準ライブラリ的な構文 11 ヤバい
  12. 初期構文を用いる例 fib = fun(n) if n < 2 n; else

    ../fib(n - 1) + ../fib(n - 2); end end; /print(fib(7)); 12 ここで利用している構文: • 数値リテラル • 代入 • 匿名関数 • if〜else文 • 加算・減算 • 関数呼び出し • etc.
  13. 構文の拡張例(カッコ) paren = block %/tokenize(/terminal("(")); inner = %/parser/expression; %/tokenize(/terminal(")")); inner;

    end; /parser/expression/children/paren = paren; 13 ここで利用している構文: • block ~ end • 代入 • 関数呼び出し • etc.
  14. 構文を拡張する構文 • 構文の拡張はまだ煩雑 • 構文を拡張する構文を作成 • Schemeのマクロのような,パターンマッチ式での構文 • 構文ルールの優先度(prec)も指定 let:prec

    pattern1 pattern2 … patternN := template; • 初期構文を用いて約60行で実装 14
  15. 構文を拡張する構文の使用例 • 優先順位をまとめるためのカッコ let:0 “(“ inner:1000 “)” := inner; 例:

    (4 + 5) • 添え字つきのアクセス let:15 store:14 “[“ key:1000 “]” := /get(store, key); 例: hoge[“key”] 15
  16. 課題 • 構文解析ルールの曖昧さへの対処 ◦ ルールが合成可能な構文解析の方式が必要 • Rubyでプロトタイプを実装したため,極めて低速 ◦ のちのち他の言語で処理系を書き直す •

    構文解析エラーのわかりやすさの向上 16 画像素材:http://www.irasutoya.com/
  17. ラボユースでの活動 • プログラミング言語処理系の開発 ◦ プロトタイプ作成による試行錯誤 • 情報処理学会 第57回プログラミング・シンポジウムに参加 ◦ 論文を執筆

    ◦ 勉強になる 17
  18. まとめ • 構文を拡張可能なプログラミング言語Garbanzo(仮)を紹介 • 構文解析ルールをユーザが動的に追加することで構文を変更 • 構文を定義する構文も作成可能 ◦ 構文の拡張をくり返すことができる •

    プログラミング・シンポジウムで発表 ◦ 開発以外でもラボユースの活動を行う ※適当につけた名前なので,カッコいい名前を募集中です GitHub: https://github.com/akamah/garbanzo 18
  19. 補足のスライド 19

  20. データ型 • 文字列,数値,真偽値 • 関数 • データストア ◦ 順序をもったハッシュテーブル ◦

    構文木の構成などに利用 20
  21. 細かな実行の流れ 1. 特別な構文解析ルール sentence に従って入力から読み取る 2. その結果を評価器に入力(eval)する ◦ この時,読み込んだプログラムが構文解析ルールを変更することがある 3.

    入力が空になるまで1から2をくり返す 21
  22. 構文解析の実際 • 構文解析ルール = Garbanzoのプログラム片 • evalされると,構文解析を実行する • 構文解析専用の命令を評価器に搭載 ◦

    terminal: 指定された文字列を読み取る ◦ choice: 与えられた構文解析ルールを順に試す ◦ etc. 22
  23. 構文解析器の変更 • いくつかの構文解析ルール(= Garbanzoのプログラム)が既に存在 • これら既に存在したルールの構文木を直接変更 23

  24. Ver. 1による自動生成の例 integer = '/parser/integer = '{ digit = [@:

    "quote", value: [@: "oneof", string: "0123456789"]] a = [@: "sub", left: [@: "tocode", string: %digit], right: [@: "tocode", string: "0"]] rest = [@: "many", parser: digit] ten = [@: "sub", left: [@: "tocode", string: "K"], right: [@: "tocode", string: "A"]] "generate"; /foreach([store: rest, func: ^{ n = [@: "sub", left: [@: "tocode", string: value], right: [@: "tocode", string: "0"]] ../a = ../a * ../ten + n }]); a; } 24