読者です 読者をやめる 読者になる 読者になる

素人がプログラミングを勉強していたブログ

プログラミング、セキュリティ、英語、Webなどのブログ since 2008

連絡先: すかいぷ:javascripter_  か javascripter あっと tsukkun.net skypeのほうがいいです

JSON判定(構文解析の練習)

そういえばこういうのまともに書いたことないなと思って練習がてらJSONのシンタックスが正しいか判定する関数を作った。
だいたいhttp://www.ietf.org/rfc/rfc4627.txt?number=4627: JavaScript Object Notation (JSON)に沿って書いた。
minus記号を判定するだけのごく小さい判定機を作って、正しければ一文字進めて…といったようにしてnumber関数を作って、最終的にJSON全体の判定機を作る。
これを少し改造して途中でスタックに値を積むようにすれば構文解析器として使えるんだと思う…けど、なんかよくわからなくなったので一応これで完成として、あとで考える。

var parseJSON;
(function () {
  parseJSON = function (text) {
    if (value(text, 0) <= 0) {
      throw new SyntaxError("parseJSON");
    }
    return eval("(" + text + ")");
  };

  var and = function () {
    var seq = arguments;
    return function (value, index) {
      var i;
      for (i = 0; i < seq.length; i++) {
        index = seq[i](value, index);
        if (index < 0) {
          return -1;
        }
      }
      return index;
    };
  };

  var or = function () {
    var seq = arguments;
    return function (value, index) {
      var i;
      var result;
      for (i = 0; i < seq.length; i++) {
        result = seq[i](value, index);
        if (result >= 0) {
          return result;
        }
      }
      return -1;
    };
  };

  var optional = function () {
    var proc = arguments[0];
    return function (value, index) {
      var result = proc(value, index);
      if (result >= 0) {
        return result;
      }
      return index;
    };
  };

  var some = function () {
    var proc = arguments[0];
    return function (value, index) {
      while (proc(value, index) >= 0) {
        index = proc(value, index);
      }
      return index;
    };
  };

  var equals = function (a) {
    return function (b, index) {
      if (a === b.charAt(index)) {
        return index + 1;
      } else {
        return -1;
      }
    };
  };

  var matches = function (a) {
    return function (b, index) {
      if (a.test(b.charAt(index))) {
        return index + 1;
      } else {
        return -1;
      }
    };
  };

  var JSONText = function (value, index) {
    JSONText = or(object, array);
    return JSONText(value, index);
  };

  var beginArray = function (value, index) {
    beginArray = and(
      ws,
      equals("["),
      ws);
    return beginArray(value, index);
  };

  var beginObject = function (value, index) {
    beginObject = and(
      ws,
      equals("{"),
      ws);
    return beginObject(value, index);
  };

  var endArray = function (value, index) {
    endArray = and(
      ws,
      equals("]"),
      ws);
    return endArray(value, index);
  };

  var endObject = function (value, index) {
    endObject = and(
      ws,
      equals("}"),
      ws);
    return endObject(value, index);
  };

  var nameSeparator = function (value, index) {
    nameSeparator = and(
      ws,
      equals(":"),
      ws);
    return nameSeparator(value, index);
  };

  var valueSeparator = function (value, index) {
    valueSeparator = and(
      ws,
      equals(","),
      ws);
    return valueSeparator(value, index);
  };

  var ws = function (value, index) {
    ws = some(matches(/^[ \t\n\r]$/));
    return ws(value, index);
  };

  var value = function (value_, index) {
    value = or(
      false_,
      null_,
      true_,
      object,
      array,
      number,
      string);
    return value(value_, index);
  };

  var false_ = function (value, index) {
    false_ = and(
      equals("f"),
      equals("a"),
      equals("l"),
      equals("s"),
      equals("e"));
    return false_(value, index);
  };

  var null_ = function (value, index) {
    null_ = and(
      equals("n"),
      equals("u"),
      equals("l"),
      equals("l"));
    return null_(value, index);
  };

  var true_ = function (value, index) {
    true_ = and(
      equals("t"),
      equals("r"),
      equals("u"),
      equals("e"));
    return true_(value, index);
  };

  var object = function (value, index) {
    object = and(
      beginObject,
      optional(
        and(
          member,
          some(
            and(valueSeparator, member)))),
      endObject);
    return object(value, index);
  };

  var member = function (value_, index) {
    member = and(string, nameSeparator, value);
    return member(value_, index);
  };

  var array = function (value_, index) {
    array = and(
      beginArray,
      optional(
        and(
          value,
          some(
            and(
              valueSeparator,
              value)))),
      endArray);
    return array(value_, index);
  };

  var number = function (value, index) {
    number = and(
      optional(minus),
      int,
      optional(frac),
      optional(exp));
    return number(value, index);
  };

  var decimalPoint = function (value, index) {
    decimalPoint = equals(".");
    return decimalPoint(value, index);
  };

  var digitFrom1To9 = function (value, index) {
    digitFrom1To9 = matches(/^[1-9]$/);
    return digitFrom1To9(value, index);
  };

  var e = function (value, index) {
    e = matches(/^[eE]$/);
    return e(value, index);
  };

  var exp = function (value, index) {
    exp = and(
      e,
      optional(
        or(minus, plus)),
        some(digit));
    return exp(value, index);
  };

  var digit = function (value, index) {
    digit = matches(/^\d$/);
    return digit(value, index);
  };

  var frac = function (value, index) {
    frac = and(
      decimalPoint,
      some(digit));
    return frac(value, index);
  };

  var int = function (value, index) {
    int = or(
      zero,
      and(
        digitFrom1To9,
        some(digit)));
    return int(value, index);
  };

  var minus = function (value, index) {
    minus = equals("-");
    return minus(value, index);
  };

  var plus = function (value, index) {
    plus = equals("+");
    return plus(value, index);
  };

  var zero = function (value, index) {
    zero = equals("0");
    return zero(value, index);
  };

  var string = function (value, index) {
    string = and(
      quotationMark,
      some(char),
      quotationMark);
    return string(value, index);
  };

  var char = function (value, index) {
    char = or(
      unescaped,
      and(
        escape,
        or(
          equals("\""),
          equals("\\"),
          equals("/"),
          equals("b"),
          equals("f"),
          equals("n"),
          equals("r"),
          equals("t"),
          and(
            equals("u"),
            matches(/^[0-9a-fA-F]$/),
            matches(/^[0-9a-fA-F]$/),
            matches(/^[0-9a-fA-F]$/),
            matches(/^[0-9a-fA-F]$/)))));
    return char(value, index);
  };

  var escape = function (value, index) {
    escape = equals("\\");
    return escape(value, index);
  };

  var quotationMark = function (value, index) {
    quotationMark = equals("\"");
    return quotationMark(value, index);
  };

  var unescaped = function (value, index) {
    unescaped = or(
      matches(/^[^\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]$/))
    return unescaped(value, index);
  };
})();