utils/sprintf.js

  1. /*
  2. * This file is part of AUX.
  3. *
  4. * AUX is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU General Public
  6. * License as published by the Free Software Foundation; either
  7. * version 3 of the License, or (at your option) any later version.
  8. *
  9. * AUX is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General
  15. * Public License along with this program; if not, write to the
  16. * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  17. * Boston, MA 02110-1301 USA
  18. */
  19. /**
  20. * Module containing sprintf like formatting functions.
  21. *
  22. * @module utils/sprintf
  23. */
  24. /**
  25. * Generates formatting functions from sprintf-style format strings.
  26. * This is generally faster when the same format string is used many times.
  27. *
  28. * @returns {function} A formatting function.
  29. * @param {string} fmt - The format string.
  30. * @function FORMAT
  31. * @example
  32. * var f = FORMAT("%.2f Hz");
  33. * @see sprintf
  34. */
  35. function round(num, precision) {
  36. const base = Math.pow(10, precision);
  37. return (Math.round(num * base) / base).toFixed(precision);
  38. }
  39. const fun =
  40. 'function round(num, precision) { \n\
  41. var base = 10 ** precision; \n\
  42. return (Math.round(num * base) / base).toFixed(precision);\n\
  43. };';
  44. export function FORMAT(fmt) {
  45. const args = [];
  46. let s = fun + ' return ';
  47. let res;
  48. let last = 0;
  49. let argnum = 0;
  50. let precision;
  51. const regexp = /%(\.\d+)?([bcdefgosO%])/g;
  52. let argname;
  53. while ((res = regexp.exec(fmt))) {
  54. if (argnum) s += '+';
  55. s += JSON.stringify(fmt.substring(last, regexp.lastIndex - res[0].length));
  56. s += '+';
  57. argname = 'a' + argnum;
  58. if (args.indexOf(argname) === -1) args.push(argname);
  59. if (argnum + 1 < arguments.length) {
  60. argname =
  61. '(' + sprintf(arguments[argnum + 1].replace('%', '%s'), argname) + ')';
  62. }
  63. switch (res[2].charCodeAt(0)) {
  64. case 100: // d
  65. s += '(' + argname + ' | 0)';
  66. break;
  67. case 102: // f
  68. if (res[1]) {
  69. // length qualifier
  70. precision = parseInt(res[1].substring(1));
  71. s += 'round(+' + argname + ', ' + precision + ')';
  72. } else {
  73. s += '(+' + argname + ')';
  74. }
  75. break;
  76. case 115: // s
  77. s += argname;
  78. break;
  79. case 37:
  80. s += '"%"';
  81. argnum--;
  82. break;
  83. case 79:
  84. case 111:
  85. s += 'JSON.stringify(' + argname + ')';
  86. break;
  87. default:
  88. throw new Error('unknown format:' + res[0]);
  89. }
  90. argnum++;
  91. last = regexp.lastIndex;
  92. }
  93. if (argnum) s += '+';
  94. s += JSON.stringify(fmt.substring(last));
  95. /* jshint -W054 */
  96. return new Function(args, s);
  97. }
  98. /**
  99. * Formats the arguments according to a given format string.
  100. * @returns {function} A formatting function.
  101. * @param {string} fmt - The format string.
  102. * @param {...*} args - The format arguments.
  103. * @function sprintf
  104. * @example
  105. * sprintf("%d Hz", 440);
  106. * @see FORMAT
  107. */
  108. export function sprintf(fmt) {
  109. let i, last_fmt;
  110. let c,
  111. arg_num = 1;
  112. const ret = [];
  113. let precision, s;
  114. let has_precision = false;
  115. for (
  116. last_fmt = 0;
  117. -1 !== (i = fmt.indexOf('%', last_fmt));
  118. last_fmt = i + 1
  119. ) {
  120. if (last_fmt < i) {
  121. ret.push(fmt.substring(last_fmt, i));
  122. }
  123. i++;
  124. if ((has_precision = fmt.charCodeAt(i) === 46) /* '.' */) {
  125. i++;
  126. precision = parseInt(fmt.substring(i));
  127. while ((c = fmt.charCodeAt(i)) >= 48 && c <= 57) i++;
  128. }
  129. c = fmt.charCodeAt(i);
  130. if (c === 37) {
  131. ret.push('%');
  132. continue;
  133. }
  134. s = arguments[arg_num++];
  135. switch (fmt.charCodeAt(i)) {
  136. case 102 /* f */:
  137. s = +s;
  138. if (has_precision) {
  139. s = round(s, precision);
  140. }
  141. break;
  142. case 100 /* d */:
  143. s = s | 0;
  144. break;
  145. case 115 /* s */:
  146. break;
  147. case 79: /* O */
  148. case 111 /* o */:
  149. s = JSON.stringify(s);
  150. break;
  151. default:
  152. throw new Error('Unsupported format.');
  153. }
  154. ret.push(s);
  155. last_fmt = i + 1;
  156. }
  157. if (last_fmt < fmt.length) {
  158. ret.push(fmt.substring(last_fmt, fmt.length));
  159. }
  160. return ret.join('');
  161. }