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

Rustで自作言語のインタプリタ作って Webで動くようにした話

garebare
August 01, 2020

Rustで自作言語のインタプリタ作って Webで動くようにした話

NEWDEBUGで話した内容です

garebare

August 01, 2020
Tweet

More Decks by garebare

Other Decks in Programming

Transcript

  1. Rustで自作言語のインタプリタ作っ

    Webで動くようにした話
    NEW! DEBUG 2.0
    ~
    ちょっとしたトランスパイラを添えて
    ~
    @garebare521

    View full-size slide

  2. やっぱり知名度が無いので自己紹介
    ガレバレ
    Twitter@garebare521
    作ったもの
    自作言語
    自作ブラウザ
    NES
    エミュレータの背景描画部分
    PixelVtuber

    View full-size slide

  3. 近況
    インターンニモ行ケズ
    エアコンガ壊レ
    夏モ暑サニモ耐エ
    時間ヲ持テ余シ

    View full-size slide

  4. そうだったのでUnityでゲーム作ってます

    View full-size slide

  5. きっかけ
    自作ブラウザとかパーサーを作ったあたりからパースにハマり始めた
    そこから自作言語へ
    あとはノリ
    このスライドも間違ってる所あるかも

    View full-size slide

  6. 本題
    説明すること
    字句解析
    構文解析
    インタプリタ
    wasm

    View full-size slide

  7. 字句解析とは
    ソースコードをプログラム上の最小単位に分割すること
    let a = 1;

    View full-size slide

  8. 字句解析とは
    ソースコードをプログラム上の最小単位に分割すること
    “let” “a” “=” “1” ”;”

    View full-size slide

  9. 字句解析とは
    ソースコードをプログラム上の最小単位に分割すること
    “let” “a” “=” “1” ”;”
    -1 -2 -3 -4 -5

    View full-size slide

  10. 実際の動作
    let a = 1;
    l
    le
    let
    この時点でトークンを発行する
    文字列や英数字の判断は正規表現
    その他の設定していないものは文字
    コードを返す

    View full-size slide

  11. 構文解析
    ほぼ自己流
    ほぼ何も見てない
    以上の理由でまぁまぁめちゃくちゃな実装

    View full-size slide

  12. 自作言語を作る本を買うべきだった

    View full-size slide

  13. 構文解析とは
    ざっくりいうと
    字句解析で発行したものもとに抽象構文木を生成する
    こと

    View full-size slide

  14. 抽象構文木
    まず木構造の根幹を定義し、要
    素になる構造体の定義
    配列には一つの型しか入らない
    ので列挙型も定義
    #[derive(Debug, Clone)]
    pub enum Types {
    Number(NumberAST),
    Variable(VariableAST),
    }
    #[derive(Debug)]
    pub struct RootAST {
    pub node: Vec,
    }
    #[derive(Debug, Clone)]
    pub struct NumberAST {
    pub val: i64,
    pub node: Vec,
    }
    pub struct VariableAST{
    pub name: String,
    pub node: Vec,
    }

    View full-size slide

  15. 抽象構文木
    let a = 1;
    を字句解析したものから
    このような木を作ります
    解析した要素をどんどん
    RootAST
    に放り込む
    これをいろんな構文や文字列な
    どに対応させる
    RootAST { node:[VariableAST (name:”a”, node:[NumberAST {val:1, node[] })] }

    View full-size slide

  16. インタプリター
    構文解析以上に何も見ていない
    完成してもこれがインタプリタかどうか悩んだ
    めちゃくちゃ中身を説明しにくい

    View full-size slide

  17. インタプリターとは
    随時解釈しながら実行するプログラムのこと
    種類も結構ある
    今回作ったものは
    Rust
    で構文木をそのまま実行させるタイプ
    なので何にも変換せず、ターミナルに出力する際にも
    Rust

    println!
    を使っている

    View full-size slide

  18. インタプリタで実装したこと
    ミュータブル、イミュータブルな変数
    関数
    四則演算
    for

    if
    、構文
    配列
    標準入出力
    変数
    他のファイルからのインポート
    etc…

    View full-size slide

  19. 変数の保存
    変数だった場合は構文木をそのま
    ま配列に保存しちゃう
    その配列から使うもの取得するよう
    にする
    match {
    asts::Types::Variable(var) => {
    let var_contents = vec_variable.variable(var, vec_function);
    let mut var_ast = asts::VariableAST::new(&var.name);
    if !var.muttable {
    var_ast.muttable = false;
    }
    match var_contents {
    Some(content) => {
    var_ast.node.push(content);
    vec_variable.push(asts::Types::Variable(var_ast));
    }
    None => {}
    }
    }

    View full-size slide

  20. 変数の中身を取り出す
    総当りして中に入ってる構文木を返す
    変数のスコープを考えて変数が入っている
    配列をリバース
    関数も同じ感じの処理をしている
    総当りなので改善したい
    for vars in var_vec {
    for var in vars {
    let mut in_var = asts::VariableAST::new("");
    match var {
    asts::Types::Variable(in_vars) => {
    in_var = in_vars;
    is_mutable = in_var.muttable;
    }
    _ => {
    let err = error::Error::new(&var);
    err.exit("This is not a variable");
    }
    }
    . . .
    }
    }

    View full-size slide

  21. トランスパイラ
    C
    言語を全く書かないくせに
    C
    言語へのトランスパイラを作ろうとしたのでずさんもずさん
    四則演算と
    if

    for
    は実装
    今は
    C
    言語をちょっと理解したのでまた気が向いたら作る
    なので割愛

    View full-size slide

  22. 全体的な改善点
    構文解析が非効率な部分が多い
    インタプリタの四則演算が非効率
    変数が総当り
    既知のバグが多数
    対応しきれていない書き方がある

    View full-size slide

  23. wasmに変換する

    View full-size slide

  24. wasmとは
    wasm(web assembly)
    クライアントサイドで動作する低水準なプログラミング言語
    javascript
    より高速で他の言語から変換できる

    View full-size slide

  25. wasm-pack
    ビルドツールで色々うまくやってくれる
    curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
     でインストール
    Rustのプロジェクト内で wasm-pack build で色々生成してくれる
    これを使ってwasmを生成する

    View full-size slide

  26. package.json {
    "author": "You ",
    "name": "rust-webpack-template",
    "version": "0.1.0",
    "scripts": {
    "devbuild": "webpack",
    "devstart": "webpack-dev-server --open -d",
    "start": "node ./js/server.js",
    "test": "cargo test && wasm-pack test --headless"
    },
    "devDependencies": {
    "@wasm-tool/wasm-pack-plugin": "^1.1.0",
    "copy-webpack-plugin": "^5.0.3",
    "webpack": "^4.42.0",
    "webpack-cli": "^3.3.3",
    "webpack-dev-server": "^3.7.1",
    "rimraf": "^3.0.0"
    }
    }

    View full-size slide

  27. webpack.config.js
    const path = require("path");
    const CopyPlugin = require("copy-webpack-plugin");
    const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin");
    module.exports = {
    mode: "production",
    entry: {
    index: "./js/index.js"
    },
    output: {
    path: path.resolve(__dirname, "./dist"),
    filename: "[name].js"
    },
    devServer: {
    contentBase: path.resolve(__dirname, "./dist"),
    },
    plugins: [
    new CopyPlugin([
    path.resolve(__dirname, "static")
    ]),
    new WasmPackPlugin({
    crateDirectory: __dirname,
    }),
    ]

    View full-size slide

  28. lib.rs
    #[wasm_bindgen]
    を付けるとエ
    クスポートできる
    #[wasm_bindgen]
    pub fn run(string:&str) {
    output_result("Run......\n");
    let mut lexer = lexer::lexers::Lexer::new(string);
    let tokens = lexer.start();
    let mut pars = ast::parsing::Parsing::new(&tokens);
    let result = parse.parsing();
    interpreter::interpreters::run(result);
    output_result("\n......Done");
    }

    View full-size slide

  29. lib.rs
    インポートは 
    #[wasm_bindgen(module=”
    ファイル名
    ”)]
    #[wasm_bindgen(module = "/js/import.js")]
    extern "C" {
    pub fn output_result(input: &str);
    }

    View full-size slide

  30. index.js
    wasm

    pkg
    に出力される
    コールバックから使える
    const wasm = import("../pkg/index.js");
    document.getElementById("run").onclick = function () {
    document.getElementById("result").textContent = "";
    wasm.then(mod => {
    try {
    let code = document.getElementById("code").value;
    mod.run(code);
    } catch (error) {
    document.getElementById("result").textContent = "Error!"
    console.log(error);
    }
    });
    }

    View full-size slide

  31. これでビルドしてhtmlから読み込めば
    ブラウザでインタプリタが実行できる

    View full-size slide

  32. server.js
    MINE Type

    wasm
    を追加しない
    と実行することができない
    const express = require('express');
    const path = require('path');
    express.static.mime.define({'application/wasm': ['wasm']});
    var app = express();
    app.use('/', express.static(path.resolve(__dirname, "../dist")));
    app.listen(process.env.PORT, function () {
    console.log('Example app listening on port' + process.env.PORT);
    })

    View full-size slide

  33. けれど今回はFire Baseでホスティングしたのでその
    あたりは上手いことやってくれました

    View full-size slide

  34. で実際できたのが

    View full-size slide

  35. https://koto-e2f64.web.app/

    View full-size slide

  36. やったー

    View full-size slide

  37. まぁしかし元が不安定なのでこれも不安定

    View full-size slide

  38. 詳細はリポジトリに
    https://github.com/garebareDA/koto

    View full-size slide

  39. マスコットキャラクターもいる

    View full-size slide

  40. 愛でやがれ!

    View full-size slide

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

    View full-size slide