Skip to content

Instantly share code, notes, and snippets.

@bathos
Last active March 23, 2018 20:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bathos/9b632d6ccfd02211420d37d4f93a21e5 to your computer and use it in GitHub Desktop.
Save bathos/9b632d6ccfd02211420d37d4f93a21e5 to your computer and use it in GitHub Desktop.
unwāpurify.js
const { Transform } = require('stream');
// Although superficially there are "rules" we could implement programmatically,
// this is a grammar; its only real rules from a computer’s perspective are the
// fact that certain inputs map to certain terminal symbols (or don’t). In other
// words, the grammar itself is just data.
const TABLE = new Map([
[ ' ', '' ],
[ '\n', '\n' ],
[ '\r', '\n' ],
[ 'a', 'あ' ],
[ 'b', new Map([
[ 'a', 'ば' ],
[ 'e', 'べ' ],
[ 'i', 'び' ],
[ 'o', 'ぼ' ],
[ 'u', 'ぶ' ],
[ 'y', new Map([
[ 'a', 'びゃ' ],
[ 'o', 'びょ' ],
[ 'u', 'びゅ' ]
]) ]
]) ],
[ 'c', new Map([
[ 'c', new Map([
[ 'h', new Map([
[ 'a', 'っちゃ' ],
[ 'i', 'っち' ],
[ 'o', 'っちょ' ],
[ 'u', 'っちゅ' ]
]) ]
]) ],
[ 'h', new Map([
[ 'a', 'ちゃ' ],
[ 'i', 'ち' ],
[ 'o', 'ちょ' ],
[ 'u', 'ちゅ' ]
]) ]
]) ],
[ 'd', new Map([
[ 'a', 'だ' ],
[ 'e', 'で' ],
[ 'i', 'ぢ' ],
[ 'o', 'ど' ],
[ 'u', 'づ' ],
[ 'y', new Map([
[ 'a', 'ぢゃ' ],
[ 'o', 'ぢょ' ],
[ 'u', 'ぢゅ' ]
]) ]
]) ],
[ 'e', 'え' ],
[ 'f', new Map([
[ 'u', 'ふ' ]
]) ],
[ 'g', new Map([
[ 'a', 'が' ],
[ 'e', 'げ' ],
[ 'i', 'ぎ' ],
[ 'o', 'ご' ],
[ 'u', 'ぐ' ],
[ 'y', new Map([
[ 'a', 'ぎゃ' ],
[ 'o', 'ぎょ' ],
[ 'u', 'ぎゅ' ]
]) ]
]) ],
[ 'h', new Map([
[ 'a', 'は' ],
[ 'e', 'へ' ],
[ 'i', 'ひ' ],
[ 'o', 'ほ' ],
[ 'y', new Map([
[ 'a', 'ひゃ' ],
[ 'o', 'ひょ' ],
[ 'u', 'ひゅ' ]
]) ]
]) ],
[ 'i', 'い' ],
[ 'j', new Map([
[ 'a', 'じゃ' ],
[ 'i', 'じ' ],
[ 'o', 'じょ' ],
[ 'u', 'じゅ' ]
]) ],
[ 'k', new Map([
[ 'a', 'か' ],
[ 'e', 'け' ],
[ 'i', 'き' ],
[ 'k', new Map([
[ 'a', 'っか' ],
[ 'e', 'っけ' ],
[ 'i', 'っき' ],
[ 'o', 'っこ' ],
[ 'u', 'っく' ],
[ 'y', new Map([
[ 'a', 'っきゃ' ],
[ 'o', 'っきょ' ],
[ 'u', 'っきゅ' ]
]) ]
]) ],
[ 'o', 'こ' ],
[ 'u', 'く' ],
[ 'y', new Map([
[ 'a', 'きゃ' ],
[ 'o', 'きょ' ],
[ 'u', 'きゅ' ]
]) ]
]) ],
[ 'm', new Map([
[ 'a', 'ま' ],
[ 'e', 'め' ],
[ 'i', 'み' ],
[ 'o', 'も' ],
[ 'u', 'む' ],
[ 'y', new Map([
[ 'a', 'みゃ' ],
[ 'o', 'みょ' ],
[ 'u', 'みゅ' ]
]) ]
]) ],
[ 'n', new Map([
[ ' ', 'ん' ],
[ '\n', 'ん\n' ],
[ '\r', 'ん\n' ],
[ 'a', 'な' ],
[ 'e', 'ね' ],
[ 'i', 'に' ],
[ 'o', 'の' ],
[ 'u', 'ぬ' ],
[ 'y', new Map([
[ 'a', 'にゃ' ],
[ 'o', 'にょ' ],
[ 'u', 'にゅ' ]
]) ]
]) ],
[ 'o', 'お' ],
[ 'p', new Map([
[ 'a', 'ぱ' ],
[ 'e', 'ぺ' ],
[ 'i', 'ぴ' ],
[ 'o', 'ぽ' ],
[ 'p', new Map([
[ 'a', 'っぱ' ],
[ 'e', 'っぺ' ],
[ 'i', 'っぴ' ],
[ 'o', 'っぽ' ],
[ 'u', 'っぷ' ],
[ 'y', new Map([
[ 'a', 'っぴゃ' ],
[ 'o', 'っぴょ' ],
[ 'u', 'っぴゅ' ]
]) ]
]) ],
[ 'u', 'ぷ' ],
[ 'y', new Map([
[ 'a', 'ぴゃ' ],
[ 'o', 'ぴょ' ],
[ 'u', 'ぴゅ' ]
]) ]
]) ],
[ 'r', new Map([
[ 'a', 'ら' ],
[ 'e', 'れ' ],
[ 'i', 'り' ],
[ 'o', 'ろ' ],
[ 'u', 'る' ],
[ 'y', new Map([
[ 'a', 'りゃ' ],
[ 'o', 'りょ' ],
[ 'u', 'りゅ' ]
]) ]
]) ],
[ 's', new Map([
[ 'a', 'さ' ],
[ 'e', 'せ' ],
[ 'h', new Map([
[ 'a', 'しゃ' ],
[ 'i', 'し' ],
[ 'o', 'しょ' ],
[ 'u', 'しゅ' ]
]) ],
[ 'o', 'そ' ],
[ 's', new Map([
[ 'a', 'っさ' ],
[ 'e', 'っせ' ],
[ 'h', new Map([
[ 'a', 'っしゃ' ],
[ 'i', 'っし' ],
[ 'o', 'っしょ' ],
[ 'u', 'っしゅ' ]
]) ],
[ 'o', 'っそ' ],
[ 'u', 'っす' ]
]) ],
[ 'u', 'す' ]
]) ],
[ 't', new Map([
[ 'a', 'た' ],
[ 'e', 'て' ],
[ 'o', 'と' ],
[ 's', new Map([
[ 'u', 'つ' ]
]) ],
[ 't', new Map([
[ 'a', 'った' ],
[ 'e', 'って' ],
[ 'o', 'っと' ]
]) ]
]) ],
[ 'u', 'う' ],
[ 'w', new Map([
[ 'a', 'わ' ],
[ 'o', 'を' ]
]) ],
[ 'y', new Map([
[ 'a', 'や' ],
[ 'o', 'よ' ],
[ 'u', 'ゆ' ]
]) ],
[ 'z', new Map([
[ 'a', 'ざ' ],
[ 'e', 'ぜ' ],
[ 'o', 'ぞ' ],
[ 'u', 'ず' ]
]) ]
]);
const OBSOLETE_TABLE = new Map(TABLE);
OBSOLETE_TABLE.set('w', new Map(OBSOLETE_TABLE.get('w')));
OBSOLETE_TABLE.get('w').set('e', 'ゑ');
OBSOLETE_TABLE.get('w').set('i', 'ゐ');
function * unwāpurify(baseTable, table=baseTable, input) {
while (table.has(input = (yield))) {
const res = table.get(input);
if (typeof res === 'string') table = baseTable, yield res;
else table = res;
}
throw new Error(`Unexpected input ${ input }`);
}
if (!module.parent) {
process.stdin.setRawMode(true);
const table = process.env.INCLUDE_OBSOLETE ? OBSOLETE_TABLE : TABLE;
const iter = unwāpurify(table);
iter.next();
const transform = new Transform({
flush(done) {
const { value } = iter.next(' ');
if (value) this.push(value);
},
transform(chunk, _, done) {
try {
for (const char of String(chunk)) {
if (char === '\x03') return process.exit();
if (char === '\x7F') {
throw new Error(`Backspace? Backspace???`);
}
if (char < ' ' && char !== '\n' && char !== '\r') continue;
const { value } = iter.next(char);
if (value !== undefined) iter.next(), this.push(value);
}
done();
} catch (err) {
this.push('\n');
done(err);
}
}
});
process.stdin.pipe(transform).pipe(process.stdout);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment