[JavaScript] クオートで囲まれた部分を抜き出す不完全なJavaScriptを書いた

クオートされた部分 + スラッシュで書いた正規表現の部分 を抜き出す不完全なJavaScriptを書いた。かなりテキトーでしょぼいプログラムだけれど… RegExp を勉強した成果ということで。。

function get_quote_location(str)
{
  /* RegExpの用意 */
  var re = [];

  // 囲いの始点
  re.start = /['"\/]/g;

  // 囲いの終点
  re.end_sq = /[^\\\r\n]'/g;
  re.end_dq = /[^\\\r\n]"/g;
  re.end_sl = /[^\\\r\n]\//g;

  // スラッシュの囲いは判定が必要
  // lastMatch が "/" だった場合、正規表現 or 除算演算子 の判定が必要
  // スキルの問題でカンペキな判別はいまのところできない
  // そしてバグの多い強引な正規表現
  re.check = /[^/]*?(?:\r\n|typeof|delete|void|[\r\n.()[\]{}<>,:;+\-*%!?^~&|=])[\t ]*?\/(?!\*)[^\r\n]*?[^\r\n\\]\/[igmy]{0,4}[\t ]*?(?:\r\n|instanceof|[\r\n.()[\]{}\/<>,:;+\-*%!?^~&|=$])/g;

  /* とりあえず囲いの部分を特定する */
  // 位置を記録する配列。位置は文字化けしてるかも…"いち"
  var record = { start : [], end : [] };

  // 引数を RegExp にセット
  RegExp.input = str;

  // 検索
  for(
    var start, end=0, letter, check;
    re.start.exec();
    re.start.lastIndex = end
  )
  {
    start = re.start.lastIndex;

    // 開始位置を記録
    // lastIndex - 1 の位置が開始の位置
    record.start.push( start - 1 );

    // lastMatch マッチした文字が入ってる
    letter = RegExp.lastMatch;
    if(letter === "'")
    {
      // 閉じ位置を探す
      // 後読みできたらラクなのに…
      re.end_sq.lastIndex = start - 1;
      re.end_sq.exec();
      end = re.end_sq.lastIndex;
      // lastIndex - 1 閉じ位置を記録
      record.end.push( end - 1 );
      // これをしないと無限ループ
      if( end === 0 ) end = start;
    }
    else if(letter === '"')
    {
      re.end_dq.lastIndex = start - 1;
      re.end_dq.exec();
      end = re.end_dq.lastIndex;
      record.end.push( end - 1 );
      if( end === 0 ) end = start;
    }
    else if(letter === '/')
    {
      // スラッシュが 除算か正規表現か判定する必要がある
      // 知識・能力不足により構文解析できないため
      // 現在の判定プログラムは判定もどき
      check = re.check();
      if( check !== null) check = check.index;
      if( check === end )
      {
        // 正規表現の場合
        re.end_sl.lastIndex = start - 1;
        re.end_sl.exec();
        end = re.end_sl.lastIndex;
        record.end.push( end - 1 );
        if( end === 0 ) end = start;
      }
      else
      {
        // 除算演算子の場合
        end = start;
        record.end.push( -1 );
      }
    }
    else
    {
      alert("正規表現が異常");
      return false; // 失敗
    }
    re.check.lastIndex = end ;
  } // /for-loop

  // record.start と record.end の配列数は同じのはず…
  // 違う場合は失敗 : false を返す
  if(
    record.start.length !== record.end.length ||
    ! record.start instanceof Array ||
    ! record.end instanceof Array
  )return false;

  // record.end[i] が record.start[i] 以下。または
  // record.end[i] がマイナスならその列は削除
  var result = [[],[]];
  for(var i = 0; i < record.start.length; i++)
  {
    if(record.end[i] >= 0 && record.start[i] < record.end[i])
    {
      result[0].push( record.start[i] );
      result[1].push( record.end[i] );
    }
  }
  return result;
}

// 出力用
function d(o){
  var s = '';
  for(var k in o){
    s += k + ' | (' + typeof o[k] + ')' + o[k] + "\n";
  }
  document.write(s);
}

var str = "var test = /\"reg\\/exp'/gim.exec(\"\\\"\").lastMatch.match('\\''); alert( 1000 / 100 );\r\n/* comment1 */\r\n//comment2\r\n\"TEST\".replace( /TEST/, 'test');alert( 1000 / 100 / 10);";
var loc = get_quote_location(str);
for(var i = 0, picked = []; i < loc[0].length; i++){
  picked.push(
    str.substring(loc[0][i], loc[1][i] + 1)
  );
}

d( loc );
document.write("\n" + str + "\n\n");
d( picked );

結果

0 | (object)11,32,54,111,127,135
1 | (object)22,35,57,116,132,140

var test = /"reg\/exp'/gim.exec("\"").lastMatch.match('\''); alert( 1000 / 100 );
/* comment1 */
//comment2
"TEST".replace( /TEST/, 'test');alert( 1000 / 100 / 10);

0 | (string)/"reg\/exp'/
1 | (string)"\""
2 | (string)'\''
3 | (string)"TEST"
4 | (string)/TEST/
5 | (string)'test'

と、こんな感じに抜き出せる。 でもほとんど構文解析していないので問題が多数…

スラッシュが正規表現か除算演算子かキチンと判別できない

書き始めたときは除算演算子や構文解析のことは思い浮かべもしなかった。 で、とりあえずできたのを動かしたら見事に除算演算子に引っかかった…強引に何とかしようとするもJavaScriptの正規表現まわりの記述が柔軟すぎてどう考えても思いつかなかった。

/*
 - 以下は全て正常に動く
 */
alert( typeof/ /.exec(1/1)instanceof String ); /* false */
alert( 1/ /\/* */i/!/!/ /1 ); /* NaN */
alert( /[/]/ ('//') ); /* / */

結局、キチンと構文解析しないと判別できないと悟る… その知識もスキルも無いためテキトウで不完全なもので今は我慢 (´;ω;`)ブワッ

またコメントの仕様もぜんぜん理解していなくて var string = "//"; や var regexp = /\/* */ はエラーになると思ってたし、 var string = "/comment/"; は空文字になるとか思ってたおバカさんな私。 コメントは最初に消すなりしてから処理をしたらいいと思っていたのでさらに微妙なプログラムとなってしまった。 コメント内の文字列を抜き出してしまうことがある…

syntaxhighlighter の中身を見てみるも理解不能で撃沈。レベルが足りません。よく見たら syntaxhighlighter もちゃんとパースできてないけども。。 そういうことで以下のような文字列だとうまくいかない。

var str, arr=[];

str = "// コメントの中に'クオートがある'と抜き出してしまう";
arr.push( get_quote_location(str) );

str = "/regexp/('スラッシュのあとに括弧があると正規表現が抜き出せない')";
arr.push( get_quote_location(str) );

str = "/[/]/;正規表現の中にバックスラッシュが付かないスラッシュがある";
arr.push( get_quote_location(str) );

str = "/* ' */(\"そんな感じで'役に立たない\"";
arr.push( get_quote_location(str) );

d( arr );

結果

0 | (object)10,18
1 | (object)9,36
2 | (object)2,4
3 | (object)3,15
Share
関連記事