sakai-memoru
12/12/2019 - 11:11 AM

Sakura Macro Library::CSON.jse 簡易CSON Converter (ある特定のフォーマットのCSONに対応したコンバータ)

CSONというCoffee-Scriptの形式で、JSONを記述した形式がある。

cson-parserのライブラリが、WSH上で動作しなかったので、ある特定のフォーマットを対象にしたCSON Converterを、共通マクロライブラリとして作った。

  • CSON.converttoJSON(str) // json strに変換

cson-parser
https://github.com/groupon/cson-parser


// Set your module name collectly.
var this_file_name = 'CSON.jse';  // Notice! If you rename this module file name.
var exports = typeof exports !== 'undefined' ? exports : {};

/////////////////////////////////////////////////////////////////  
// CSON.jse  
// CSON Format Parse simply ※複雑なものはパースは未考慮
//   19/12/12 memoru  
//   
//   static method  
//     - CSON.converttoJSON(str)
//  

(function(){

var CLS_ = {};
CLS_.module_name = this_file_name;

// local method 
// 
// From StringUtil
var repeat = function(str, len){
  var ret = '';
  if(len == 0) return ret;
  for (var i=0; i < len; ++i){
    ret += str;
  }
  return ret;
};

var trim = function(str){
  return str.replace(/^\s+|\s+$/g, "");
};

var rtrim = function(str){
  return str.replace(/\s+$/, "");
};

var endsWith = function(str, suffix_, start_num){
  var len_str = str.length;
  var len_suf = suffix_.length;
  var pos = len_str;
  if (arguments.length > 2){
    if(typeof(start_num) !== 'undefined'){
      pos = start_num ? parseInt(start_num) : 0;
      if(isNaN(pos)){
        pos = 0;
      }
    }
  }
  var pos_end = Math.min(Math.max(pos,0), len_str);
  var pos_start = pos_end - len_suf;
  if(pos_start < 0){
    return false;
  }
  return (str.lastIndexOf(suffix_, pos_start) == pos_start);
};

var put_bracket = function(str, bra, bra_closed){
  if(typeof(bra) === 'undefined')  bra = '"';
  if(typeof(bra_closed) === 'undefined') bra_closed = bra;
  return bra + str + bra_closed;
}

var unifyLineCode = function(str,line_code){
  if(typeof line_code === 'undefined') line_code = "\r\n";
  var replacement = "\\v"; // dummy string
  var buf = str.replace(/\r\n|\n/gm,replacement)
  var ret = buf.replace(/\\v/g, line_code);
  return ret
}

var right = function(str, len){
  return (str.slice(len * -1));
};

var left = function(str, len){
  return (str.slice(0, len));
}

var chopHead = function(str, num){
  if(typeof(num) === 'undefined') num = 1;
  var len = str.length;
  return str.substr(num, len);
};

var chopTail = function(str, num){
  if(typeof(num) === 'undefined') num = 1;
  var len = str.length;
  return str.substr(0,len - num);
};

var chopDouble = function(str, num){
  if(typeof(num) === 'undefined') num = 1;
  var ret = str;
  ret = chopHead(ret, num);
  ret = chopTail(ret, num);
  return ret;
};

var contains = function(str, search_str, start_num){
  var pos = 0;
  if(typeof(start_num) !== 'undefined'){
    pos = start_num ? parseInt(start_num) : 0;
    if(isNaN(pos)){
      pos = 0;
    }
  }
  if(start_num + search_str.lenght > str.lenght) return false;
  return (str.indexOf(search_str, pos) != -1);
}

// local method
var get_minIndent = function(lines, line_code){
  /** get indent, such as a space or tab from all lines, 
   *  this code don't be considered confused pattern tab and space  
   */
  if(typeof line_code === 'undefined') line_code = "\r\n";
  lines = unifyLineCode(lines, line_code);
  var ary = lines.split(line_code);
  var indent_len = 0;
  var ary_buf = [];
  //
  var len = ary.length;
  for (var i = 0; i < len; i++){
    var regx = new RegExp("^\\s*","m");
    regx.exec(ary[i]);
    var lastMatch_ = RegExp.lastMatch;
    if(i == 0) indent = lastMatch_;
    indent = indent.length < lastMatch_.length ? indent: lastMatch_;
  }
  return indent
}

var chop_indent = function(lines, indent, tab_size){ 
  /** chop indent string from lines to extract table parts */
  // if confused tabs, not available
  if(typeof indent === 'undefined') indent = '';
  if(typeof tab_size === 'undefined') tab_size = 2;
  var regx = new RegExp('^' + indent, 'gm');
  var ret_lines = lines.replace(regx, '');
  return ret_lines
}

var startsWithByRegx = function(str, prefix_regx){
  var headStr = left(str, 1);
  return (prefix_regx.test(headStr));
}

var endsWithByRegx = function(str, suffix_regx){
  var tailStr = right(str, 1);
  return (suffix_regx.test(tailStr));
}

var normalize_quate = function(str){
  // quateが対でない場合はJSON.parseでエラーとなる
  var ret = str;
  if(startsWithByRegx(str, /["']/)){  // '"
    if(endsWithByRegx(str, /["']/)){  // '"
      ret = chopDouble(str);
    }
  }
  //Editor.TraceOut('L154 : CSON.normalize ' + ret);
  if (/[\[\]\{\}]/.test(ret)) {
    null;
  } else {
    if ( !( /([^0-9\.]+)/.test(ret) )) {
      null;
    } else {
      if (/(true)|(false)/.test(ret)) {
        null;
      } else {
        //Editor.TraceOut('L164 : CSON.normalize ' + ret);
        ret = put_bracket(ret, '"', '"');
      }
    }
  }
  return ret
}

var get_elmentAndIndentLevel = function(ary_line, tab_size){
  if(typeof tab_size === 'undefined') tab_size = 2;
  var ary_level = [];
  var ary_elm = [];
  var len = ary_line.length;
  var regx = new RegExp("\\s*","");
  for (var i = 0; i < len; i++){
    regx.exec(ary_line[i]);
    var lastMatch_ = RegExp.lastMatch;
    var rightContext_ = RegExp.rightContext;
    ary_level.push(lastMatch_.length / tab_size + 1);
    ary_elm.push(rtrim(rightContext_));
  }
  var ary_elmFormed = [];
  for (var i = 0; i < len; i++){
    var buff_ary = ary_elm[i].split(':'); // stringの中に:は許さない
    var len_buff = buff_ary.length;
    var buf = '';
    for (var j = 0; j < len_buff; j++){
      buf = buff_ary[j];
      buf = normalize_quate(trim(buf));
      buff_ary[j] = buf;
    }
    ary_elm[i] = buff_ary.join(':')
  }
  return [ ary_elm, ary_level ]
}

var normalize_ary = function(ary){
  var ret_ary = [];
  var regx = new RegExp('\s*#+.*', 'm')
  var regx_whitespace = new RegExp('^\s*$', 'm')
  //
  var len = ary.length;
  for (var i = 0; i < len; i++){
    if(regx.test(ary[i])){
      null;
    } else {
      if(regx_whitespace.test(ary[i])){
        null;
      } else {
        ret_ary.push(ary[i]);
      }
    }
  }
  return ret_ary
}

var parse_str = function(str){
  var indent = get_minIndent(str);
  var str_chopped = chop_indent(str, indent);
  var ary = str_chopped.split('\r\n');
  var ary_normalized = normalize_ary(ary)
  var ary_parsed = get_elmentAndIndentLevel(ary_normalized);
  return ary_parsed
}

var form_data = function(elm, level, level_pre){
  var ret = '';
  if(level_pre === level) {
    null;
  } else if(level_pre != level) {
    ret = ret + '} ';
  }
  if(endsWith(elm,':')){
    ret = trim(elm) + '  ' ;
  } else if(endsWith(elm,'[')){
    ret = trim(elm) + ' ' ;
  } else if(endsWith(elm,']')){
    ret = trim(elm) + ', ' ;
  } else if(endsWith(elm,'{')){
    ret = trim(elm) + ' ' ;
  } else if(endsWith(elm,'}')){
    ret = trim(elm) + ', ' ;
  } else {
    ret = trim(elm)  + ', ';
  }
  return ret
}

// static method
// 
CLS_.converttoJSON = function(str){
  var ret = '';
  var ary_parsed = parse_str(str);
  var elms = ary_parsed[0];
  var levels = ary_parsed[1];
  
  var buff = '';
  var lvl_pre = levels[0];
  var len = elms.length;
  var formed = '';
  for (var i = 0; i < len; i++){
    formed = form_data(elms[i], levels[i], lvl_pre);
    if(endsWith(elms[i],']')){
      buff = StringUtil.chopTail(buff,2);
    }
    if(endsWith(elms[i],'}')){
      buff = StringUtil.chopTail(buff,2);
    }
    buff = buff + formed;
    if(endsWith(elms[i],'[')){
      if(contains(elms[i+1], ':')){
        flag_object = true; // FIXME
      }
    }
    if(endsWith(elms[i],':')){
      if(contains(elms[i+1], ':')){
        flag_object = true; // FIXME
      }
    }
    lvl_pre = levels[i];
  }
  if(StringUtil.startsWith(buff,'[')){
    ret = chopTail(buff,2);
  } else if(StringUtil.startsWith(buff,'{')){
    ret = chopTail(buff,2);
  } else {
    ret = put_bracket(chopTail(buff,2),'{','}');
  }
  return ret
}

CLS_.doDebug = function(){
  WScript.Echo('------------ debug start');
  WScript.Echo('module name = ' + CSON.module_name);
  var str = '  sample : 123 \r\n  date : "19/12/11"';
  var ary_parsed = parse_str(str);
  WScript.Echo('ary_elm = ' + ary_parsed[0]);
  WScript.Echo('ary_level = ' + ary_parsed[1]);
  var elms = ary_parsed[0];
  var levels = ary_parsed[1];
  var buff = '';
  var lvl_pre = levels[0];
  var len = elms.length;
  var formed = '';
  for (var i = 0; i < len; i++){
    formed = form_data(elms[i], levels[i], lvl_pre);
    buff = buff + formed;
    lvl_pre = levels[i];
  }
  WScript.Echo('buff = ' + put_bracket(chopTail(buff,2),'{','}'));
}

exports.CSON = CLS_;

}());

// --------------------------------------------------- entry point
function is_module(this_module_name){
  // reference : https://senooken.jp/blog/2016/08/20/
  //             https://gist.github.com/lamsh/f16ee7032051868844fc081926bf0854
  var isWSH = (typeof(WScript) !== "undefined" && WScript.ScriptName === this_module_name);
  var isEditor = (typeof(Editor) !== "undefined" && Editor.ExpandParameter('$f') === this_module_name);
  var isBrowser = (typeof(alert) !== "undefined");
  return (isWSH || isEditor || isBrowser)
}

if(typeof(Editor) !== 'undefined'){
  if (is_module(this_file_name)){
    Editor.TraceOut('------------ debug from Editor start');
    var CSON = exports.CSON;
    var str = '  sample : 12.3 \r\n  date : "19/12/11"';
    var json_str = CSON.converttoJSON(str);
    Editor.TraceOut('json_str = ' + json_str);
  }
} else {
  if(typeof(WScript) !== 'undefined'){
    null;
    var CSON = exports.CSON;
    CSON.doDebug();
  } else {
    console.log('Test on IE11')
  }
}


id: 'bk102'
author: 'Crockford, Douglas'
title: 'JavaScript The Good Parts'
genre: "Computer"
price: 29.99
publish_date: 2008-05-01
alive: true

{
  id: 'bk102'
  author: 'Crockford, Douglas'
  title: 'JavaScript The Good Parts'
  genre: "Computer"
  price: 29.99
  publish_date: 2008-05-01
  alive: true
}

books: 
  {
    id: 'bk102'
    author: 'Crockford, Douglas'
    title: 'JavaScript The Good Parts'
    genre: "Computer"
    price: 29.99
    publish_date: 2008-05-01
    alive: true
  }

books:{
  id: 'bk102'
  author: 'Crockford, Douglas'
  title: 'JavaScript The Good Parts'
  genre: "Computer"
  price: 29.99
  publish_date: 2008-05-01
  description: "Unearthing the Excellence in JavaScript"
  alive: true
}

books: [
  {
    id: 'bk102'
    author: 'Crockford, Douglas'
    title: 'JavaScript The Good Parts'
    genre: "Computer"
    price: 29.99
    publish_date: 2008-05-01
    description: "Unearthing the Excellence in JavaScript"
    alive: true
  }
  {
    id: 'bk103'
    author: 'Crockford, Douglas'
    title: 'JavaScript The Good Parts'
    genre: "Computer"
    price: 29.99
    publish_date: 2008-05-01
    description: "Unearthing the Excellence in JavaScript"
    alive: true
  }
]


books: 
  [
    {
      id: 'bk102'
      author: 'Crockford, Douglas'
      title: 'JavaScript The Good Parts'
      genre: "Computer"
      price: 29.99
      publish_date: 2008-05-01
      description: "Unearthing the Excellence in JavaScript"
      alive: true
    }
    {
      id: 'bk102'
      author: 'Crockford, Douglas'
      title: 'JavaScript The Good Parts'
      genre: "Computer"
      price: 29.99
      publish_date: 2008-05-01
      description: "Unearthing the Excellence in JavaScript"
      alive: true
    }
  ]

books: 
[
  {
    id: 'bk102'
    author: 'Crockford, Douglas'
    title: 'JavaScript The Good Parts'
    genre: "Computer"
    price: 29.99
    publish_date: 2008-05-01
    description: "Unearthing the Excellence in JavaScript"
    alive: true
  }
  {
    id: 'bk102'
    author: 'Crockford, Douglas'
    title: 'JavaScript The Good Parts'
    genre: "Computer"
    price: 29.99
    publish_date: 2008-05-01
    description: "Unearthing the Excellence in JavaScript"
    alive: true
  }
]

[
  {
    id: 'bk102'
    author: 'Crockford, Douglas'
    title: 'JavaScript The Good Parts'
    genre: "Computer"
    price: 29.99
    publish_date: 2008-05-01
    description: "Unearthing the Excellence in JavaScript"
    alive: true
  }
  {
    id: 'bk102'
    author: 'Crockford, Douglas'
    title: 'JavaScript The Good Parts'
    genre: "Computer"
    price: 29.99
    publish_date: 2008-05-01
    description: "Unearthing the Excellence in JavaScript"
    alive: true
  }
]