Skip to content

Instantly share code, notes, and snippets.

@s-hiiragi
Created November 30, 2012 18:31
Show Gist options
  • Save s-hiiragi/4177602 to your computer and use it in GitHub Desktop.
Save s-hiiragi/4177602 to your computer and use it in GitHub Desktop.
千反田える
/**
* @name 千反田える.js
* @author s_hiiragi
* @created_date 2012/11/07 02:13
*/
/* Usage:
*
* node 千反田える.js {options}
*
* options:
* -d | --daiji
* 漢数字ではなく大字を使う
*
* num1, num2, ...
* num1, num2, ...について計算
* 例) node 千反田える.js 1000
* node 千反田える.js 1, 2, 3
*
* begin ".." end, ...
* [begin .. end], ...について計算
* 例) node 千反田える.js 1 .. 1000
*
* [-b begin=1] [-e end=Infinity]
* [begin .. end]について計算
* 例) node 千反田える.js -b 1 -e 1000
* node 千反田える.js --begin 1 --end 1000
*/
function 漢数字(n) {
this.n = n;
}
漢数字.prototype = {
NUM: ['〇', '一', 'ニ', '三', '四', '五', '六', '七', '八', '九'],
KETA: ['〇', '一', '十', '百', '千', '万', '億'],
toString: function() {
if (this._str) return this._str;
this._str = this._toString(this.n);
return this._str;
},
_toString: function(n) {
if (n == 0) return this.NUM[0];
if (n == 1) return this.NUM[1];
var s = String(n), len = s.length;
var res = [];
for (var i = 0; i < len; ++i) {
var keta = len - i;
var num = s[i];
if (num == 0) continue;
if (num >= 2) res.push(this.NUM[num]);
if (keta >= 2) {
if (keta >= 5 && num == 1) res.push(this.NUM[1]);
res.push(this.KETA[keta]);
}
}
return res.join('');
}
};
function 大字(n) {
this.n = n;
}
大字.prototype = new 漢数字(0);
大字.prototype.NUM = ['零', '壱', '弐', '参', '肆', '伍', '陸', '漆', '捌', '玖'
];
大字.prototype.KETA = ['零', '壱', '拾', '佰', '仟', '萬', '億'];
/* 兆以上 (漢数字/大字共通, int64で必要)
* 兆, 京, 垓, (じょ), 穣, 溝, 澗, 正, 載, 極,
* 恒河沙, 阿僧祇, 那由他, 不可思議, 無量大数
*/
/**
* 拡張RegExpクラス
*/
function MyRegExp(re) {
var flags = (re.global ? 'g' : '') +
(re.ignoreCase ? 'i' : '') +
(re.multiline ? 'm' : '');
re.compile(re.source, flags);
this._re = re;
this.lastIndex = 0;
}
MyRegExp.METACHAR_REPLACER = /[\[\](){}^$|\\+*?.]/g;
MyRegExp.escape = function(text) {
return text.replace(this.METACHAR_REPLACER, '\\$&');
};
MyRegExp.prototype = {
exec: function(text, index) {
var beginIndex;
if (this._re.global) {
beginIndex = typeof index != 'undefined'
? index : this.lastIndex;
this._re.lastIndex = beginIndex;
}
var matches = this._re.exec(text);
if (!matches) return null;
if (this._re.global) {
var lastIndex = this._re.lastIndex;
this.lastIndex = lastIndex;
matches.beginIndex = beginIndex;
matches.matchIndex = matches.index;
matches.nextIndex = lastIndex;
}
return matches;
}
};
/**
* スキャナクラス
*/
function Scanner(name, re) {
this.name = name;
if (typeof re == 'string') {
re = new RegExp('^(' + MyRegExp.escape(re) + ')', 'g');
}
this._re = new MyRegExp(re);
}
Scanner.prototype = {
deserializer: function(callback) {
this._deserializer = callback;
return this;
},
scan: function(text) {
var matches = this._re.exec(text, 0);
if (!matches) return null;
var token = new Token(this, matches[1]);
if (this._deserializer) {
token.value = this._deserializer(token.str);
}
return {
token: token,
// next: text.substring(matches.nextIndex)
nextIndex: matches.nextIndex
};
}
};
/**
* トークンクラス
*/
function Token(scanner, str) {
this['is' + scanner.name] = true;
this.str = str;
}
Token.prototype = {};
/**
* Rangeクラス
*/
function Range(begin, end) {
this.begin = begin != null ? begin : 1;
this.end = end != null ? end : Infinity;
}
Range.prototype = {
toString: function() {
return '[' + begin + ' .. ' + end + ']';
}
};
/**
* rangeOpt
*/
var rangeOpt = {
_Scanners: [
new Scanner('Integer', /^(\d+)/g)
.deserializer(function(t) { return Number(t); }),
new Scanner('RangeMark', '..'),
new Scanner('Comma', ',')
],
_input: null,
_pos: 0,
get _curInput() {
if (this._pos == this._cur_pos) {
return this._cur_input;
}
this._cur_pos = this._pos;
this._cur_input = this._input.substring(this._pos);
return this._cur_input;
},
get _isEOF() {
return (this._pos >= this._input.length);
},
parse: function(args) {
var tokens = this._tokenize(args.join(' '));
//console.log('tokens = ', tokens);
if (tokens.length == 0) return null;
var numOrRanges = this._parse(tokens);
//console.log('numOrRanges', numOrRanges);
return numOrRanges;
},
_tokenize: function(command) {
//console.log('command', '"' + command + '"');
this._input = command;
this._pos = 0;
this._cur_pos = -1;
var tokens = [];
while (true) {
this._skip_wsp();
if (this._isEOF) break;
tokens.push(this._scan());
}
return tokens;
},
_skip_wsp: function() {
var re = new MyRegExp(/^\s+/g);
var matches = re.exec(this._curInput);
if (!matches) return;
this._pos += matches.nextIndex;
},
_scan: function() {
//console.log('_scan: "' + this._curInput + '"');
var token;
var that = this;
this._Scanners.some(function(scanner) {
var res = scanner.scan(that._curInput);
if (!res) return false;
token = res.token;
that._pos += res.nextIndex;
return true;
});
if (!token) {
throw new Error('unknown char [' + that._curInput + ']');
}
return token;
},
_parse: function(tokens) {
var numOrRanges = [];
while (tokens.length >= 1)
{
var nor = this._getNumOrRange(tokens);
if (!nor) break;
numOrRanges.push(nor);
if (tokens[0] && tokens[0].isComma)
tokens.shift();
}
if (numOrRanges.length == 0)
throw new Error('no token');
return numOrRanges;
},
_getNumOrRange: function(tokens) {
var t = tokens[0];
if (t.isInteger) {
var t2 = tokens[1];
// Number [EOF]
if (!t2) return tokens.shift().value;
if (t2.isRangeMark) {
var t3 = tokens[2];
// Number .. [EOF]
if (!t3) {
var begin = tokens.shift().value;
tokens.shift();
return new Range(begin, null);
}
// Number .. Number
if (t3.isInteger) {
var begin = tokens.shift().value;
tokens.shift();
var end = tokens.shift().value;
return new Range(begin, end);
}
// Number .. {next}
else {
var begin = tokens.shift().value;
tokens.shift();
return new Range(begin, null);
}
}
// Number {next}
else return tokens.shift().value;
}
else if (t.isRangeMark) {
var t2 = tokens[1];
// .. [EOF]
if (!t2) throw new Error('no token');
// .. Number
if (t2.isInteger) {
tokens.shift();
var end = tokens.shift().value;
return new Range(null, end);
}
// .. ???
else throw new Error('unknown token', t2);
}
else throw new Error('unknown token', t);
}
};
var assert = require('assert');
// rangeOpt test
//
// 正常系
assert.equal(rangeOpt.parse([]), null);
assert.deepEqual(rangeOpt.parse(['1']) , [1]);
assert.deepEqual(rangeOpt.parse(['1', '2', '3']) , [1, 2, 3]);
assert.deepEqual(rangeOpt.parse(['1', ',', '2', ',', '3']) , [1, 2, 3]);
assert.deepEqual(rangeOpt.parse(['1,2,3']) , [1, 2, 3]);
assert.deepEqual(rangeOpt.parse(['1', '..']) , [new Range(1)]);
assert.deepEqual(rangeOpt.parse(['1..']) , [new Range(1)]);
assert.deepEqual(rangeOpt.parse(['1', '..', ',', '2']) , [new Range(1), 2]);
assert.deepEqual(rangeOpt.parse(['1..,2']) , [new Range(1), 2]);
assert.deepEqual(rangeOpt.parse(['1', '..', '2']) , [new Range(1, 2)]);
assert.deepEqual(rangeOpt.parse(['1..2']) , [new Range(1, 2)]);
assert.deepEqual(rangeOpt.parse(['1', '..', '2', '3']) , [new Range(1, 2), 3]);
assert.deepEqual(rangeOpt.parse(['1', '..', '2', ',', '3']) , [new Range(1, 2), 3]);
assert.deepEqual(rangeOpt.parse(['1..2,3']) , [new Range(1, 2), 3]);
assert.deepEqual(rangeOpt.parse(['..', '1']) , [new Range(null, 1)]);
assert.deepEqual(rangeOpt.parse(['..1']) , [new Range(null, 1)]);
assert.deepEqual(rangeOpt.parse(['..', '1', '2']) , [new Range(null, 1), 2]);
assert.deepEqual(rangeOpt.parse(['..', '1', ',' , '2']) , [new Range(null, 1), 2]);
assert.deepEqual(rangeOpt.parse(['..1,2']) , [new Range(null, 1), 2]);
//
// 異常系
assert.throws(function() {
rangeOpt.parse(['hoge']);
}, Error);
//
// /rangeOpt test
var opts = require('opts');
opts.parse([
{
'short': 'd',
'long': 'daiji',
'description': 'use 大字',
'value': false,
'require': false
},
{
'short': 'b',
'long': 'begin',
'description': 'begin number (default is 0)',
'value': true,
'required': false
}, {
'short': 'e',
'long': 'end',
'description': 'end number (default is Infinity)',
'value': true,
'required': false
}
]);
var alp = [
'えー', 'びー', 'しー', 'でぃー', 'いー', 'えふ', 'じー',
'えいち', 'あい', 'じぇい', 'けー', 'える', 'えむ', 'えぬ',
'おー', 'ぴー', 'きゅー', 'あーる', 'えす', 'てぃー', 'ゆー',
'ぶい', 'だぶりゅー', 'えっくす', 'わい', 'ぜっと'
];
var Num2kanji = opts.get('daiji') ? 大字 : 漢数字;
function print_chitanda(begin, end) {
for (var i = begin; i <= end; ++i) {
var s = new Num2kanji(i) + '反田' + alp[(i-1) % alp.length];
console.log(s);
}
}
var numOrRanges = rangeOpt.parse(opts.args());
if (numOrRanges) {
numOrRanges.forEach(function(nor) {
if (typeof nor == 'number') {
print_chitanda(nor, nor);
} else {
print_chitanda(nor.begin, nor.end);
}
});
} else {
var begin = Number(opts.get('begin'));
if (isNaN(begin)) begin = 1;
var end = opts.get('end');
if (isNaN(end)) end = Infinity;
print_chitanda(begin, end);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment