LoginSignup
3
2

More than 3 years have passed since last update.

pythonでも中括弧{ }でブロックを作れるようにしてみた【php】

Last updated at Posted at 2019-10-01

0. 動機

「pythonのブロックはc言語やjavaみたいに中括弧({ })でやるんじゃなくて、インデントを使うんだよ」というのを そういうものだと受け入れたら、今までできたはずの言語でうっかり同じことをしてしまい、構文エラーしまくることになる事例を知った。
僕自身はpythonについて、ほとんどしらない。
唯一知ってるのは「pythonがインデントに意味を持たせたのは有害」ということだけ。
→pythonには「1行1ステートメント」という考え方があり、だからこそブロックの括弧やセミコロンは不要[1]というのは後から知った。

そこで、インデントの代わりに{ }を使ってブロックを記述したpythonコードを、正しいpythonコードに置き換えるプログラムを作ろうという気になった。
phpがインストールされているサーバからみんなが使えるように、phpで作る。

1.プログラムの構成

  1. テキストエリアから、インデントの代わりにブロックで書かれたpyファイルの中身を受け取る(元コンテンツと呼ぶことにする)
  2. 元ファイルのブロックはインデントに置き換え、元ファイルのインデントは捨てる
  3. 結果を表示

1-1.

次のようなblock2indent.htmlを作ってみた。

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>インデントが不要でブロックが使えるpython</title>
    </head>
    <body>
        ①インデントの代わりにブロックを用いたpythonコードを入力<br>
        <form method="post" action="block2indent.php">
            <textarea name="content" cols="50" rows="5"></textarea>
            <br>
            <br>
            ②改行コードを選択<br>
            <select name="returnCode">
                <option value="rn"> &#92;r&#92;n </option>
                <option value="n"> &#92;n </option>
                <option value="r"> &#92;r </option>
            </select>
            <br>
            <br><input type="submit">
        </form>
    </body>
</html>

余談だが、Eclipseで「PHP 開発ツール (PDT)」をインストールし、エディタに「<」とだけ打ち込むと、入力候補としてという選択肢が表示される。これを無視してバックスペースキーを押して「<」を消すと、cssやhtmlなど様々なコードのテンプレートが選べるようになる。これでhtml 4.01 strictのコードなども容易にかける。

block2indent.htmlをクロームで開くと、図1.1.1のように表示され、「送信」を押すと、「ファイルが見つかりません」のエラーへ遷移した。
1.png

図1.1.1 block2indent.html

1-2.

block2indent.htmlを名前変更しblock2indent.phpとした。
そして、次のように追記した。

<?php
$samplePy=
"
class Man:{
    def __inin__(self, name):{
        self.name = name;
        print(\"インスタンス生成および初期化に成功\");
    }

    def hello(self):{
        print(\"Hello \" + self.name + \"!\")
    }
}

m = Man(\"David\");
m.hello();
";

/*/
if(isset($_POST['content']))
    print block2indent(killIndent($_POST['content']));
/*/
if(true)
    print block2indent(killIndent($samplePy));
//*/
else printHtml();


function printHtml()
{
    print
'<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>インデントが不要でブロックが使えるpython</title>
    </head>
    <body>
        ①インデントの代わりにブロックを用いたpythonコードを入力<br>
        <form method="post" action="block2indent.php">
            <textarea name="content" cols="50" rows="5"></textarea>
            <br>
            <br>
            ②改行コードを選択<br>
            <select name="returnCode">
                <option value="rn"> &#92;r&#92;n </option>
                <option value="n"> &#92;n </option>
                <option value="r"> &#92;r </option>
            </select>
            <br>
            <br>
            ③<input type="submit">
        </form>
    </body>
</html>';
}

    function killIndent($indentAlive)
    {
        $indentDying = preg_replace("/\A( |\t)+/i", "", $indentAlive);
        return preg_replace("/(\n|\r|(\r\n))( |\t)+/i", "$1", $indentDying);
    }

    function block2indent($blockAlive)
    {
        //行頭以外でブロックが開始していたら、ブロック開始直前に改行挿入
        //同様に、ブロックの終わりも改行を強制
        $blockDying = putNewLineBeforeBlockOpen($blockAlive);
        $blockDying = putNewLineAfterBlockClose($blockDying);

        //各行ごとの「深さ」を調べ、その分だけスペース4つを挿入
        $blockDying = createIndent($blockDying);

        //ブロックをなくす
        return killBlock($blockDying);
    }

        function putNewLineBeforeBlockOpen($blockOpenWhileARow)
        {
            return preg_replace("/(.+){/i", "$1\r\n{", $blockOpenWhileARow);
        }

        function putNewLineAfterBlockClose($blockCloseWhileARow)
        {
            return preg_replace("/}(.+)/i", "}\r\n$1", $blockCloseWhileARow);
        }

        function createIndent($noIndent)
        {
            //改行コードを統一
            $returnCode
            =
            strcmp($_POST['returnCode'], "r")===0 ? "\r":
            strcmp($_POST['returnCode'], "n")===0 ? "\n":
            "\r\n";

            $noIndent = preg_replace("/\r([^\n])/i", $returnCode."$1", $noIndent);
            $noIndent = preg_replace("/([^\r])\n/i", "$1".$returnCode, $noIndent);

            //改行区切りで配列に入れる
            $lines = explode($returnCode, $noIndent);

            //各行に対して、深さを調べる
            $i = 0;
            $depth = 0;
            $lineIdx2depth = array();
            foreach($lines as $line)
            {
                if(strpos($line, "{")===0)
                    $depth++;

                $lineIdx2depth[$i] = $depth;

                if(strpos($line, "}")!==false)
                    $depth--;

                $i++;
            }

            //書き込む
            $i = 0;
            $ownIndent = "";
            foreach($lines as $line)
            {
                for($j=0; $j < $lineIdx2depth[$i]*4; $j++)
                    $ownIndent .= " ";
                $ownIndent .= $line;
                $ownIndent .= $returnCode;
                $i++;
            }

            return $ownIndent;

        }


        function killBlock($blockAlive)
        {
            $returnCode
            =
            strcmp($_POST['returnCode'], "r")===0 ? "\r":
            strcmp($_POST['returnCode'], "n")===0 ? "\n":
            "\r\n";

            return preg_replace("/".$returnCode."( |\t)*{( |\t)*/i", "", preg_replace("/".$returnCode."( |\t)*}( |\t)*".$returnCode."/i", "", $blockAlive));

        }

printHtml関数が実行されると、1-1でみたblock2indent.htmlの内容がphpにより出力される。
しかし今回はif(true)に対するelseとして記述されているため、printHtmlは呼び出されない。その代わり、$samplePyに対してkillIndent関数やblock2indent関数が適用されたものが出力される。

$samplePyの内容は、次の通り、{ }を用いて書かれた「謝ったpythonコード」である。

class Man:{
    def __inin__(self, name):{
        self.name = name;
        print("インスタンス生成および初期化に成功");
    }

    def hello(self):{
        print("Hello " + self.name + "!")
    }
}

m = Man("David");
m.hello();

これにkillIndent関数が適用されると、次のようになる。

class Man:{
def __inin__(self, name):{
self.name = name;
print("インスタンス生成および初期化に成功");
}

def hello(self):{
print("Hello " + self.name + "!")
}
}

m = Man("David");
m.hello();

さらにblock2indent関数が適用されると、次のように正しいpythonコードが出力される。


class Man:
    def __inin__(self, name):
        self.name = name;
        print("インスタンス生成および初期化に成功");    

    def hello(self):
        print("Hello " + self.name + "!")    

m = Man("David");
m.hello();

実際には次の手順に従って実行されている。

  1. {の直前と}の直後に改行がなければ挿入
  2. 各行ごとに{ }の深さ$depthの取得
  3. 各行ごとに4×$depth個だけ空白を先頭に追加
  4. {}を行ごと削除

1-3

プログラム自体は完成したので、後はサーバにアップロードして、外部からhttp通信でアクセスできるようにすることを考えよう。

1-2で書いたblock2indent.phpに対し、

/*/
if(isset($_POST['content']))
    print block2indent(killIndent($_POST['content']));
/*/
if(true)
    print block2indent(killIndent($samplePy));
//*/
else printHtml();

の部分の先頭に/を追加し、

//*/
if(isset($_POST['content']))
    print block2indent(killIndent($_POST['content']));
/*/
if(true)
    print block2indent(killIndent($samplePy));
//*/
else printHtml();

として、サーバへアップロードするだけでよい。
こうすることで、サーバ上のblock2indent.phpは次のように振舞うことになる。

  1. 自身へ何もpost送信されていない場合(=最初にアクセスされたとき)
    1. 1-1で書いたblock2indent.htmlと同じものを出力
    2. 送信ボタンが押されると、 自分自身へテキストエリア内の文字列をpost送信
  2. 自身へテキストエリア内の文字列がpost送信された場合
    1. 1-2のアルゴリズム通り、pythonコードへ変換を行う
    2. 結果を出力する

2.png
図1.3.1 「誤ったpythonコード」を入力する例

実際、図1.3.1のように入力し、送信ボタンを押すと、次のように正しいコードが出力された。(但し、改行コードの選択肢に<br>は含めていないので、「ソースを表示」から見る必要がある。)

class Woman:
    def __init__(self, name):
        self.name = name;
        print("インスタンス生成および初期化に成功");    

    def hello(self):
        print("Hello " + self.name + "!")    

m = Man("Alice");
m.hello();

入力例のインデントを多少変えても、出力は同じになる。

2. このプログラムを利用したい方

つまらないものですが、http://rights-for.men/block2indent.php からどうぞ。

3.参考

[1]https://python.keicode.com/lang/control-basic-rule.php

3
2
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
3
2