0. 動機
「pythonのブロックはc言語やjavaみたいに中括弧({
}
)でやるんじゃなくて、インデントを使うんだよ」というのを そういうものだと受け入れたら、今までできたはずの言語でうっかり同じことをしてしまい、構文エラーしまくることになる事例を知った。
僕自身はpythonについて、ほとんどしらない。
唯一知ってるのは「pythonがインデントに意味を持たせたのは有害」ということだけ。
→pythonには「1行1ステートメント」という考え方があり、だからこそブロックの括弧やセミコロンは不要[1]というのは後から知った。
~~そこで、~~インデントの代わりに{
}
を使ってブロックを記述したpythonコードを、正しいpythonコードに置き換えるプログラムを作ろうという気になった。
phpがインストールされているサーバからみんなが使えるように、phpで作る。
1.プログラムの構成
- テキストエリアから、インデントの代わりにブロックで書かれたpyファイルの中身を受け取る(元コンテンツと呼ぶことにする)
- 元ファイルのブロックはインデントに置き換え、元ファイルのインデントは捨てる
- 結果を表示
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"> \r\n </option>
<option value="n"> \n </option>
<option value="r"> \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.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"> \r\n </option>
<option value="n"> \n </option>
<option value="r"> \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();
実際には次の手順に従って実行されている。
-
{
の直前と}
の直後に改行がなければ挿入 - 各行ごとに
{
}
の深さ$depth
の取得 - 各行ごとに4×
$depth
個だけ空白を先頭に追加 -
{
や}
を行ごと削除
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は次のように振舞うことになる。
- 自身へ何もpost送信されていない場合(=最初にアクセスされたとき)
- 1-1で書いたblock2indent.htmlと同じものを出力
- 送信ボタンが押されると、 自分自身へテキストエリア内の文字列をpost送信
- 自身へテキストエリア内の文字列がpost送信された場合
- 1-2のアルゴリズム通り、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 からどうぞ。