◆ REPL

この記事の内容のために作りました

REPL ってすごく便利ですよね
このコードで結果がどうなるのかちょっと確認したいとか 返り値のデータ構造を実行しながら見ていきたいとか
REPL があるかないかでその言語の理解のしやすさが一気に変わります

今回は JScript でエクセル操作してみようかな というところで エクセルのオブジェクトとかを これで動くのかとか適当に REPL で試しながら使えると楽そうなので作りました

デフォルトの JScript は IE7 くらいのエンジンなのですごく古くて使う気になりません
なので Windows10 限定になりますが Edge のエンジンを使って REPL を作ります

REPL

let acc = ""
while(true){
WScript.StdOut.Write("> ")
const input = WScript.StdIn.ReadLine()
if(input === "exit"){
break
}
if(input.endsWith("\\")){
acc += input.slice(0, -1) + "\n"
continue
}
const source = acc + input
acc = ""
try{
const [result = "undefined" ] = [eval(source)]
WScript.Echo(result)
}catch(err){
WScript.Echo(err.message)
WScript.Echo(err.stack)
}
}
C:\Users\user\Desktop>cscript //E:{1b7cd997-e5ff-4932-a7a6-2a9e636da385} repl.js
Microsoft (R) Windows Script Host Version 5.812
Copyright (C) Microsoft Corporation. All rights reserved.

> if(true){\
> 1\
> }
1
> var a = 10
undefined
> a
10
> exit

Gist

REPL(2)

const g = function*(___){
while(true)
try {
___ = eval(yield ___)
} catch(error) {
___ = {error}
}
}()
g.next()

!function(){
const stdin = WScript.StdIn
const stdout = WScript.StdOut

let str = ""
let neos = true
while (neos){
stdout.Write("> ")
if (neos = !stdin.AtEndOfStream) {
str += stdin.ReadLine()
if (str.endsWith("\\n")) {
str = str.replace(/\\n$/, "\n")
continue
}

const result = g.next(str).value
if (result && result.error) {
stdout.WriteLine(result.error.message)
} else {
try {
stdout.WriteLine(result)
} catch (err) {
stdout.WriteLine("Cannot print")
}
}
str = ""
} else {
stdout.WriteLine("Bye.")
}
}
}()
C:\Users\user\Desktop>cscript jsconsole.js //E:{1b7cd997-e5ff-4932-a7a6-2a9e636da385}
Microsoft (R) Windows Script Host Version 5.812
Copyright (C) Microsoft Corporation. All rights reserved.

> var a = 1

> a
1
> const b = 2

> b
'b' is undefined
> const b = 10;a + b
11
> let c = 100\n
> let d = 200\n
> c*d
20000
> ^Z
Bye.

Gist

2つあった

2つあります

というのも 前に作ってたのを忘れてて 新しく作ったんですよね
2つ目が新しい方です

この時調べてた方法だと stdin.AtEndOfStream を使ってた例があったので使ってみました
boolean 値を返すだけのように書かれていますが 参照するとここでキーボード入力を待機になります
stdin.ReadLine() で待機すると思っていたのでちゃんと動かずちょっとハマりました

プロンプト 「>」 を表示するなら ReadLine() の直前でなく AtEndOfStream の直前にする必要があります


また 1 つ目は単純にやってるので 短いですが eval されるスコープで参照出来るものが多いです
2 つ目では generator を使って yield と next() でデータの受け渡しをして eval するスコープで参照可能なものは最小限にしてます

どちらも while ループの中で try catch をしている都合上 ブロックスコープの const や let は毎回クリアされます
使うなら 1 回の実行で定義と使用をしてしまう必要アリです

1 つめだと 「\」 2 つめだと 「\n」 で実行せずに複数行入力になります
「\」 だと単に複数行書けるだけで改行はしないように見えますが セミコロン無しで書くために内部的には改行になります

そこが 「\」 と直感に反するかなということで 新しい方は 「\n」 にしました
ちょっと書く時大変ですが わかりやすさをとりました

基本的にはこれくらい
シンプルな REPL です



実行例のところにあるように Edge エンジンを使うために Edge の CLSID を指定しないといけません
これを忘れると IE 並の機能しか使えません

まぁこれが原因でせっかく作った REPL が…………になったのですが そこは元記事のほうをみてください