LoginSignup
7
3

More than 3 years have passed since last update.

30分で実装シリーズ自作言語もどき編

Last updated at Posted at 2020-11-10

30分で実装シリーズ自作言語もどき編

この記事の目標
30分で自作言語もどきを実装する
うぷ主の環境

  • macOS Big Sur 11.0.1
  • Node 12.16.3

手順
1. 自作言語の構文を考える
2. トランスパイル先の言語を決める
3. ファイルを読みこむ
4. トランスパイラを作る
5. トランスパイラをビルドする

トランスパイラとか難しい言葉がありますが80行ほどで実装できます
早速作っていきましょう

1. 自作言語の構文を考える

今回はjavascriptをベースにします
構文は
log "Hello World";
logでコンソールに出力
命令の間にスペースと文末に;は必須です
今回はとりあえずHello Worldだけにします
コンセプトは
pythonのように簡単な構文で
javascriptを書くなので
名前はjpyとします

2.トランスパイル先の言語を決める

今回はjavascriptです
トランスパイラもjavascriptで書きます

3.ファイルを読みこむ

Nodeには標準でファイルの入出力の機能が提供されているのでそれを利用します
スマートに書きたいのでES6で書いたのをES5にbabelでトランスパイルします
まずはモジュールを読み込みます

index.js
import fs from 'fs';

まずはファイルの有無を判定する関数を作ります
あったらtrueなかったらfalseを返します

index.js
const check = (file) => {
    let hasfaile = false;
    try {
        fs.statSync(file);
        hasfaile = true;
    } catch (err) {
        hasfaile = false;
    }
    return hasfaile;
}

次はファイルを読み込みます
先ほどの関数を使ってfileがあったら読み込みます
読み込んだ結果を文字列で返します

index.js
let inpfile = "";
const read = (file) => {
    if (check(file)) {;
        inpfile = fs.readFileSync(file, 'utf8');
    }
    return inpfile;
}

4.トランスパイラを作る

今回の難所です

まず対応表を作ります

index.js
let jpy_command = ["log "];
let js_command = [['console.log("', 1, '");\n']];

次に変数を用意します

index.js
let out = "";//出力結果

次にファイルを読み込みます
改行を消して
;ごとに区切ります

index.js
let inp = (read('src/index.jpy').replace(/\n/g, '')).split(/;/);

次は"を区切ります

index.js
for (const i in inp) {
      inp[i] = inp[i].split(/"/)
}

これでトークン化的なことをした二次元配列が作成できました
最後にトランスパイル部分を作れば完成です
処理の流れは
1. 読み込み結果を一つずつ取り出す
2. jpyの命令を含んでいたら
3. javascriptの命令に変換して変数に追記
です

index.js
for (const i in inp) {
    for (const u in jpy_command) {
        if (~inp[i][0].indexOf(jpy_command[u])) {
            for (const f in js_command[u]) {
                if (typeof js_command[u][f] == 'number') {
                    out += inp[i][1]
                } else {
                    out += js_command[u][f]
                }
            }
        }
    }
}

最後にfileを出力します
buidl_jpyを言うフォルダにbuild後のfileを出力をします
build_jpyがあったら出力
build_jpyがなかったらディレクトリを作成後出力と言う処理にします

index.js
if (check('build_jpy')) {
    fs.writeFile('build_jpy/build.js', out, function(err) {
        if (err) { throw err; }
        console.log('build成功');
    });
}
else{
    fs.mkdir('build_jpy', (err) => {
        if (err) { throw err; }
        fs.writeFile('build_jpy/build.js', out, function(err) {
            if (err) { throw err; }
            console.log('build成功');
        });
    });
}

5. トランスパイラをビルドする

ここまでくればあとは楽勝です
babelのインストールだけです
npm init -y
npm install @babel/core @babel/cli @babel/preset-env
touch babel.config.js
今回はES5に変換できればいいのでbabelの設定は最小限です

babel.config.js
const presets = [
  [
    "@babel/env",
  ],
];

module.exports = { presets };

babelを実行してES5に変換しましょう

./node_modules/.bin/babel src --out-dir dist

最後に実行しましょう
node dist/index.js
完成

まとめ

いつかプログラミング言語を作ってみたいと思っていましたが
そんな技術は残念ながら持ってないプログラマーによる自作言語もどきの作り方でした

7
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3