Skip to content

Instantly share code, notes, and snippets.

@ne-sachirou
Last active December 1, 2015 14:51
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 ne-sachirou/0d7da119dd4a8a323842 to your computer and use it in GitHub Desktop.
Save ne-sachirou/0d7da119dd4a8a323842 to your computer and use it in GitHub Desktop.
node_modules
printf
printf.c
*.min.js
#!/usr/bin/env babel-node
'use strict';
import * as babel from 'babel-core';
import fs from 'fs';
import primisify from './promisify';
const COMPONENTS = {
sprintf : true,
strftime: false,
debug : false,
};
Promise.all([
promisify(fs.readFile)('sprintf.js', {encoding: 'utf8'}),
promisify(fs.readFile)('package.json', {encoding: 'utf8'}),
]).
then((values) => {
var code = values[0];
var config = JSON.parse(values[1]).babel;
for (let compoment in COMPONENTS) if (COMPONENTS.hasOwnProperty(compoment)) {
if (!COMPONENTS[compoment]) {
code = code.replace(new RegExp(`^// {{{ !${compoment}(?:.|\n)*^// }}} !${compoment}`, 'mg'), '');
}
}
code = babel.transform(code, config).code;
return promisify(fs.writeFile)('sprintf.min.js', code);
}).
catch((err) => console.error(err));
{
"name": "sprintf",
"version": "1.0.0",
"description": "",
"main": "sprintf.js",
"scripts": {
"start": "babel-node .",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "Public Domain",
"babel": {
"compact": true,
"plugins": [
"syntax-async-functions",
"syntax-class-properties",
"syntax-object-rest-spread",
"transform-class-properties",
"transform-es2015-arrow-functions",
"transform-es2015-classes",
"transform-es2015-for-of",
"transform-es2015-modules-commonjs",
"transform-es2015-parameters",
"transform-es2015-spread",
"transform-es2015-sticky-regex",
"transform-es2015-template-literals",
"transform-object-rest-spread",
"transform-regenerator"
]
},
"devDependencies": {
"babel-core": "^6.2.1",
"babel-plugin-syntax-async-functions": "^6.1.18",
"babel-plugin-syntax-class-properties": "^6.1.18",
"babel-plugin-syntax-object-rest-spread": "^6.1.18",
"babel-plugin-transform-class-properties": "^6.2.2",
"babel-plugin-transform-es2015-arrow-functions": "^6.1.18",
"babel-plugin-transform-es2015-classes": "^6.2.2",
"babel-plugin-transform-es2015-for-of": "^6.1.18",
"babel-plugin-transform-es2015-modules-commonjs": "^6.2.0",
"babel-plugin-transform-es2015-parameters": "^6.1.18",
"babel-plugin-transform-es2015-spread": "^6.1.18",
"babel-plugin-transform-es2015-sticky-regex": "^6.1.18",
"babel-plugin-transform-es2015-template-literals": "^6.1.18",
"babel-plugin-transform-object-rest-spread": "^6.1.18",
"babel-plugin-transform-regenerator": "^6.2.0",
"mustache": "^2.2.0",
"node-uuid": "^1.4.7",
"permute": "^1.0.0"
}
}
#include <stdio.h>
int main(int argc, char *argv[]) {
printf({{{args}}});
return 0;
}
'use strict';
function promisify(func) {
return function (...args) {
var me = this;
return new Promise((resolve, reject) => {
function done(err, ...results) {
if (err) {
console.error(err);
return reject(err);
}
if (0 === results.length) {
resolve.call(me);
} else if (1 === results.length) {
resolve.call(me, results[0]);
} else {
resolve.call(me, results);
}
}
args.push(done);
func.apply(me, args);
});
};
}
module.exports = promisify;
// vim:set fdm=marker:
// https://gist.github.com/ne-sachirou/0d7da119dd4a8a323842
(function (global) {
'use strict';
class Evaluator {
patterns = [];
constructor() {
this._init();
}
evaluate(format, values) {
this._init();
var result = '';
const formatLength = format.length;
while (this._indexOfFormat < formatLength) {
for (let e of this.patterns) {
let pattern = e[0];
let callback = e[1];
pattern.lastIndex = this._indexOfFormat;
let match = pattern.exec(format);
if (match && match.index === this._indexOfFormat) {
result += callback.call(this, match, values);
this._indexOfFormat += match[0].length;
}
}
}
return result;
}
_init() {
this._indexOfFormat = 0;
}
}
// {{{ !sprintf
class SprintfEvaluator extends Evaluator {
patterns = [
[
/[^%]+/g,
(match) => match[0],
],
[
/%([-#0 +'l]+)?(\d+)?(\.\d*)?(hh|h|l|ll|L|j|z|t)?([diouxXeEfFgGaAcspnm%])/g,
function (match, values) {
var flags = match[1] || '';
var fieldWidth = match[2] ? parseInt(match[2], 10) : 0;
var precision = match[3] ? parseInt(match[3].slice(1), 10) : null;
var lengthModifier = match[4];
var conversionSpecifier = match[5];
var isFlagSwitch = flags.includes('#');
var isFlag0 = flags.includes('0');
var isFlagLeft = flags.includes('-');
var isFlagSpace = flags.includes(' ');
var isFlagSign = flags.includes('+');
var isFlagMoney = flags.includes("'");
var isFlagLocal = flags.includes('l');
switch (conversionSpecifier) {
case 'd': case 'i':
{
if (isFlagLeft && isFlag0) {
isFlag0 = false;
isFlagSpace = true;
}
if (null === precision) {
precision = 1;
}
let value = values[this._indexOfValues];
++this._indexOfValues;
if ('string' === typeof value || value instanceof String) {
let base = 10;
if (value.startsWith('0b')) {
base = 2;
} else if (value.startsWith('0x')) {
base = 16;
} else if ('0' === value[0]) {
base = 8;
}
value = parseInt(value, base);
} else {
value = ~~Number(value);
}
let resultLeftSpace = '';
let resultSign = '';
let resultLeft0 = '';
let resultBody = Math.abs(value).toString();
let resultRight = '';
if (value < 0 || isFlagSign) {
resultSign = value < 0 ? '-' : '+';
}
if (isFlagSpace && !resultSign) {
resultLeftSpace = ' ';
}
let mainLength = resultLeftSpace.length + resultSign.length + resultBody.length;
if (fieldWidth > mainLength) {
if (isFlagLeft) {
resultRight = (isFlag0 ? '0' : ' ').repeat(fieldWidth - mainLength);
} else {
if (isFlag0) {
resultLeft0 = '0'.repeat(fieldWidth - mainLength);
} else {
resultLeftSpace += ' '.repeat(fieldWidth - mainLength);
}
}
}
return resultLeftSpace + resultSign + resultLeft0 + resultBody + resultRight;
}
case 'o':
break;
case 'u':
break;
case 'x':
break;
case 'X':
break;
case 'e':
break;
case 'E':
break;
case 'f':
break;
case 'F':
break;
case 'g':
break;
case 'G':
break;
case 'a':
break;
case 'A':
break;
case 'c':
break;
case 's':
{
let resultLeft = '';
let resultBody = values[this._indexOfValues].toString();
let resultRight = '';
++this._indexOfValues;
if (fieldWidth > resultBody.length) {
if (isFlagLeft) {
resultRight = ' '.repeat(fieldWidth - resultBody.length);
} else {
resultLeft = ' '.repeat(fieldWidth - resultBody.length);
}
}
return resultLeft + resultBody + resultRight;
}
case 'p':
break;
case 'n':
break;
case 'm':
break;
case '%':
return '%';
default:
throw new Error('NotImplemented');
}
}
],
];
constructor() {
super();
}
_init() {
super._init();
this._indexOfValues = 0;
}
}
function sprintf(format, ...values) {
return sprintf.evaluator.evaluate(format, values);
}
sprintf.evaluator = new SprintfEvaluator();
global.sprintf = sprintf;
// }}} !sprintf
// {{{ !strftime
class StrftimeEvaluator extends Evaluator {
patterns = [
[
/[^%]+/g,
(match) => match[0]
],
[
/%([aAbBcCdDeEFGghHIJklmMnOpPrRsStTuUVwWxXyYzZ+%])/g,
function (match, date) {
date = date[0];
switch (match[1]) {
default:
throw new Error('NotImplemented');
}
}
],
];
constructor() {
super();
}
}
function strftime(format, date) {
return strftime.evaluator.evaluate(format, [date]);
}
strftime.evaluator = new StrftimeEvaluator();
global.strftime = strftime;
// }}} !strftime
}(/*(module && module.exports) || */(this || 0).self || global));
// {{{ !debug
(function () {
'use strict';
var assert = require('assert');
var cp = require('child_process');
var fs = require('fs');
var Mustache = require('mustache');
var permute = require('permute');
var promisify = require('./promisify');
var util = require('util');
var uuid = require('node-uuid');
const FLAG_SETS = (function () {
function push() {
for (let i = 2, iz = flags.length - 1; i <= iz; ++i) {
let set = flags.slice(0, i).sort().join('');
if (!sets.includes(set)) {
sets.push(set);
}
}
}
var flags = ['-', '#', '0', ' ', '+', "'", 'l'];
var sets = [''].concat(flags);
sets.push(flags.join(''));
flags = flags.sort();
push();
while (permute(flags)) {
push();
}
return sets.sort((l, r) => l.length - r.length);
}());
function test(format, ...values) {
function calcExpected() {
var expected;
var tmpId = uuid.v4();
return promisify(fs.readFile)('printf.c.mustache', {encoding: 'utf8'}).
then((c) => {
c = Mustache.render(
c,
{
args: [format, ...values].map((v) => {
if ('number' === typeof v || v instanceof Number || /^(?:\d+)|(?:0[bx][\da-zA-Z]+)$/.test(v)) {
return '' + v;
}
return `"${v}"`
}).join(','),
}
)
return promisify(fs.writeFile)(`printf-${tmpId}.c`, c);
}).
then(() => promisify(cp.exec)(`clang -o printf-${tmpId} printf-${tmpId}.c`)).
then(() => promisify(cp.exec)(`./printf-${tmpId}`)).
then((v) => expected = v[0].toString()).
then(() => promisify(fs.unlink)(`printf-${tmpId}`)).
then(() => promisify(fs.unlink)(`printf-${tmpId}.c`)).
then(() => expected).
catch((err) => console.error(err));
}
var promise = new Promise(async (resolve, reject) => {
if (!test.startAt) {
test.startAt = Date.now();
}
var actual = sprintf(format, ...values);
var expected = await calcExpected();
try {
assert.strictEqual(actual, expected);
} catch (ex) {
console.error(`Actual: \t${util.inspect(ex.actual)}\nExpected:\t${util.inspect(ex.expected)}\n\tArguments:\t${util.inspect(Array.from(arguments))}\n`);
test.endAt = Date.now();
test.result.push(false);
return resolve();
}
test.endAt = Date.now();
test.result.push(true);
resolve();
});
test.promises.push(promise);
return promise;
}
test.startAt = null;
test.endAt = null;
test.promises = [];
test.result = [];
test.printResult = function () {
Promise.all(test.promises).then(() => {
console.log(`${(test.endAt - test.startAt) / 1000} sec`);
console.log(`${test.result.filter((r) => r).length}/${test.result.length} passed`);
console.log(test.result.map((r) => r ? '.' : 'F').join(''));
});
};
test('string');
test('%d', '02322');
test('%d', '0x4d2');
FLAG_SETS.
filter((set) => {
if (set.includes('#') || set.includes('l')) {
return false;
}
if (set.includes(' ') && set.includes('+')) {
return false;
}
if (set.includes('0') && set.includes('-')) {
return false;
}
return true;
}).
forEach((set) => {
test(`%${set}d` , 1234);
test(`%${set}d` , -1234);
test(`%${set}8d`, 1234);
test(`%${set}8d`, -1234);
});
test('a %s b', 'and');
test('%s and %s', 'a', 'b');
test('%4s', 'yu');
test('%-4s', 'yu');
test('%%');
test.printResult();
}());
// }}} !debug
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment