Slide 1

Slide 1 text

Rustで自作言語のインタプリタ作っ て Webで動くようにした話 NEW! DEBUG 2.0 ~ ちょっとしたトランスパイラを添えて ~ @garebare521

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

抽象構文木 まず木構造の根幹を定義し、要 素になる構造体の定義 配列には一つの型しか入らない ので列挙型も定義 #[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, }

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

インタプリタで実装したこと ミュータブル、イミュータブルな変数 関数 四則演算 for 、 if 、構文 配列 標準入出力 変数 他のファイルからのインポート etc…

Slide 19

Slide 19 text

変数の保存 変数だった場合は構文木をそのま ま配列に保存しちゃう その配列から使うもの取得するよう にする 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 => {} } }

Slide 20

Slide 20 text

変数の中身を取り出す 総当りして中に入ってる構文木を返す 変数のスコープを考えて変数が入っている 配列をリバース 関数も同じ感じの処理をしている 総当りなので改善したい 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"); } } . . . } }

Slide 21

Slide 21 text

トランスパイラ C 言語を全く書かないくせに C 言語へのトランスパイラを作ろうとしたのでずさんもずさん 四則演算と if や for は実装 今は C 言語をちょっと理解したのでまた気が向いたら作る なので割愛

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

wasmに変換する

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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" } }

Slide 27

Slide 27 text

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, }), ]

Slide 28

Slide 28 text

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"); }

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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); } }); }

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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); })

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

で実際できたのが

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

やったー

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

愛でやがれ!

Slide 41

Slide 41 text

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