diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/library.js | 292 | ||||
-rw-r--r-- | src/parseTools.js | 2 |
2 files changed, 255 insertions, 39 deletions
diff --git a/src/library.js b/src/library.js index 083bf29c..54256c44 100644 --- a/src/library.js +++ b/src/library.js @@ -63,10 +63,10 @@ var Library = { }, sscanf: '_scanString', - _formatString__deps: ['$STDIO'], + _formatString__deps: ['$STDIO', 'isdigit'], _formatString: function() { function isFloatArg(type) { - return String.fromCharCode(type) in Runtime.set('f', 'e', 'g'); + return String.fromCharCode(type).toLowerCase() in Runtime.set('f', 'e', 'g'); } var cStyle = false; var textIndex = arguments[0]; @@ -101,66 +101,250 @@ var Library = { if (curr === 0) break; next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; if (curr == '%'.charCodeAt(0)) { - // Handle very very simply formatting, namely only %.X[f|d|u|etc.] - var precision = -1; + // Handle flags. + var flagAlwaysSigned = false; + var flagLeftAlign = false; + var flagAlternative = false; + var flagZeroPad = false; + flagsLoop: while (1) { + switch (next) { + case '+'.charCodeAt(0): + flagAlwaysSigned = true; + break; + case '-'.charCodeAt(0): + flagLeftAlign = true; + break; + case '#'.charCodeAt(0): + flagAlternative = true; + break; + case '0'.charCodeAt(0): + if (flagZeroPad) { + break flagsLoop; + } else { + flagZeroPad = true; + break; + } + default: + break flagsLoop; + } + textIndex++; + next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; + } + + // Handle width. + var width = 0; + if (next == '*'.charCodeAt(0)) { + width = getNextArg('i'); + textIndex++; + next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; + } else { + while (_isdigit(next)) { + width = width * 10 + (next - '0'.charCodeAt(0)); + textIndex++; + next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; + } + } + + // Handle precision. + var precisionSet = false; if (next == '.'.charCodeAt(0)) { + var precision = 0; + precisionSet = true; textIndex++; - precision = 0; - while(1) { - var precisionChr = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; - if (!(precisionChr >= '0'.charCodeAt(0) && precisionChr <= '9'.charCodeAt(0))) break; - precision *= 10; - precision += precisionChr - '0'.charCodeAt(0); + next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; + if (next == '*'.charCodeAt(0)) { + precision = getNextArg('i'); textIndex++; + } else { + while(1) { + var precisionChr = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; + if (!_isdigit(precisionChr)) break; + precision = precision * 10 + (precisionChr - '0'.charCodeAt(0)); + textIndex++; + } } next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; + } else { + var precision = 6; // Standard default. } - if (next == 'l'.charCodeAt(0) || next == 'L'.charCodeAt(0)) { + + // Handle (ignore) integer sizes. + if (next == 'l'.charCodeAt(0) && {{{ makeGetValue(0, 'textIndex+2', 'i8') }}} == 'l'.charCodeAt(0) || + next == 'h'.charCodeAt(0) && {{{ makeGetValue(0, 'textIndex+2', 'i8') }}} == 'h'.charCodeAt(0)) { + textIndex += 2; + next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; + } else if (next == 'l'.charCodeAt(0) || next == 'L'.charCodeAt(0) || + next == 'h'.charCodeAt(0) || next == 'z'.charCodeAt(0) || + next == 'j'.charCodeAt(0) || next == 't'.charCodeAt(0)) { textIndex++; next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; } - if (isFloatArg(next)) { - next = 'f'.charCodeAt(0); // no support for 'e' - } - if (['d', 'i', 'u', 'p', 'f'].indexOf(String.fromCharCode(next)) != -1) { - var currArg; + + // Handle type specifier. + if (['d', 'i', 'u', 'o', 'x', 'X', 'p'].indexOf(String.fromCharCode(next)) != -1) { + // Integer. + var currArg = +getNextArg(next); // +: boolean=>int + var currAbsArg = Math.abs(currArg); var argText; - currArg = getNextArg(next); - argText = String(+currArg); // +: boolean=>int - if (next == 'u'.charCodeAt(0)) { - argText = String(unSign(currArg, 32)); + var prefix = ''; + if (next == 'd'.charCodeAt(0) || next == 'i'.charCodeAt(0)) { + argText = currAbsArg.toString(10); + } else if (next == 'u'.charCodeAt(0)) { + argText = unSign(currArg, 32).toString(10); + currArg = Math.abs(currArg); + } else if (next == 'o'.charCodeAt(0)) { + argText = (flagAlternative ? '0' : '') + currAbsArg.toString(8); + } else if (next == 'x'.charCodeAt(0)) { + prefix = flagAlternative ? '0x' : ''; + argText = currAbsArg.toString(16); + } else if (next == 'X'.charCodeAt(0)) { + prefix = flagAlternative ? '0X' : ''; + argText = currAbsArg.toString(16).toUpperCase(); } else if (next == 'p'.charCodeAt(0)) { - argText = '0x' + currArg.toString(16); - } else { - argText = String(+currArg); // +: boolean=>int + prefix = '0x'; + argText = currAbsArg.toString(16); } - if (precision >= 0) { - if (isFloatArg(next)) { - argText = (Math.round(currArg*Math.pow(10,precision))/Math.pow(10,precision)).toString(); - var dotIndex = argText.indexOf('.'); - if (dotIndex == -1 && next == 'f'.charCodeAt(0)) { - dotIndex = argText.length; - argText += '.'; - } - argText += '00000000000'; // padding - argText = argText.substr(0, dotIndex+1+precision); + if (precisionSet) { + while (argText.length < precision) { + argText = '0' + argText; + } + } + + // Add sign. + if (currArg < 0) { + prefix = '-' + prefix; + } else if (flagAlwaysSigned) { + prefix = '+' + prefix; + } + + // Add padding. + while (prefix.length + argText.length < width) { + if (flagLeftAlign) { + argText += ' '; } else { - while (argText.length < precision) { + if (flagZeroPad) { argText = '0' + argText; + } else { + prefix = ' ' + prefix; + } + } + } + + argText = prefix + argText; + argText.split('').forEach(function(chr) { + ret.push(chr.charCodeAt(0)); + }); + textIndex += 2; + } else if (['f', 'F', 'e', 'E', 'g', 'G'].indexOf(String.fromCharCode(next)) != -1) { + // Float. + var currArg = +getNextArg(next); // +: boolean=>int + var argText; + + if (isNaN(currArg)) { + argText = 'nan'; + } else if (!isFinite(currArg)) { + argText = (currArg < 0 ? '-' : '') + 'inf'; + } else { + var isGeneral = false; + var effectivePrecision = Math.min(precision, 20); + + // Convert g/G to f/F or e/E, as per: + // http://pubs.opengroup.org/onlinepubs/9699919799/functions/printf.html + if (next == 'g'.charCodeAt(0) || next == 'G'.charCodeAt(0)) { + isGeneral = true; + precision = precision || 1; + var exponent = parseInt(currArg.toExponential(effectivePrecision).split('e')[1], 10); + if (precision > exponent && exponent >= -4) { + next = ((next == 'g'.charCodeAt(0)) ? 'f' : 'F').charCodeAt(0); + precision -= exponent + 1; + } else { + next = ((next == 'g'.charCodeAt(0)) ? 'e' : 'E').charCodeAt(0); + precision--; + } + effectivePrecision = Math.min(precision, 20); + } + + if (next == 'e'.charCodeAt(0) || next == 'E'.charCodeAt(0)) { + argText = currArg.toExponential(effectivePrecision); + // Make sure the exponent has at least 2 digits. + if (/[eE][-+]\d$/.test(argText)) { + argText = argText.slice(0, -1) + '0' + argText.slice(-1); + } + } else if (next == 'f'.charCodeAt(0) || next == 'F'.charCodeAt(0)) { + argText = currArg.toFixed(effectivePrecision); + } + + var parts = argText.split('e'); + if (isGeneral && !flagAlternative) { + // Discard trailing zeros and periods. + while (parts[0].length > 1 && (parts[0].slice(-1) == '0' || parts[0].slice(-1) == '.')) { + parts[0] = parts[0].slice(0, -1); + } + } else { + // Make sure we have a period in alternative mode. + if (flagAlternative && argText.indexOf('.') == -1) parts[0] += '.'; + // Zero pad until required precision. + while (precision > effectivePrecision++) parts[0] += '0'; + } + argText = parts[0] + (parts.length > 1 ? 'e' + parts[1] : ''); + + // Capitalize 'E' if needed. + if (next == 'E'.charCodeAt(0)) argText = argText.toUpperCase(); + + // Add sign. + if (flagAlwaysSigned && currArg >= 0) { + argText = '+' + argText; + } + + // Add padding. + while (argText.length < width) { + if (flagLeftAlign) { + argText += ' '; + } else { + if (flagZeroPad && (argText[0] == '-' || argText[0] == '+')) { + argText = argText[0] + '0' + argText.slice(1); + } else { + argText = (flagZeroPad ? '0' : ' ') + argText; + } } } } + if (next < 'a'.charCodeAt(0)) argText = argText.toUpperCase(); argText.split('').forEach(function(chr) { ret.push(chr.charCodeAt(0)); }); textIndex += 2; } else if (next == 's'.charCodeAt(0)) { - ret = ret.concat(String_copy(getNextArg(next))); + // String. + var copiedString = String_copy(getNextArg(next)); + if (precisionSet && copiedString.length > precision) { + copiedString = copiedString.slice(0, precision); + } + if (!flagLeftAlign) { + while (copiedString.length < width--) { + ret.push(' '.charCodeAt(0)); + } + } + ret = ret.concat(copiedString); + if (flagLeftAlign) { + while (copiedString.length < width--) { + ret.push(' '.charCodeAt(0)); + } + } textIndex += 2; } else if (next == 'c'.charCodeAt(0)) { - ret = ret.concat(getNextArg(next)); + if (flagLeftAlign) ret = ret.concat(getNextArg(next)); + while (--width > 0) { + ret.push(' '.charCodeAt(0)); + } + if (!flagLeftAlign) ret = ret.concat(getNextArg(next)); + textIndex += 2; + } else if (next == 'n'.charCodeAt(0)) { + // TODO: Implement. Requires arguments to be passed in C-style. + // {{{ makeSetValue('argIndex', '0', 'ret.length', 'i32') }}} textIndex += 2; } else { + // TODO: Add support for a/A specifiers (hex float). ret.push(next); textIndex += 2; // not sure what to do with this %, so print it } @@ -643,19 +827,24 @@ var Library = { strtod__deps: ['isspace', 'isdigit'], strtod: function(str, endptr) { - // FIXME handles only whitespace + |[0-9]+(.[0.9]+)?|, no e+ + // Skip space. while (_isspace(str)) str++; + var chr; var ret = 0; + + // Get whole part. while(1) { chr = {{{ makeGetValue('str', 0, 'i8') }}}; if (!_isdigit(chr)) break; ret = ret*10 + chr - '0'.charCodeAt(0); str++; } + + // Get fractional part. if ({{{ makeGetValue('str', 0, 'i8') }}} == '.'.charCodeAt(0)) { str++; - var mul=1/10; + var mul = 1/10; while(1) { chr = {{{ makeGetValue('str', 0, 'i8') }}}; if (!_isdigit(chr)) break; @@ -664,9 +853,36 @@ var Library = { str++; } } + + // Get exponent part. + chr = {{{ makeGetValue('str', 0, 'i8') }}}; + if (chr == 'e'.charCodeAt(0) || chr == 'E'.charCodeAt(0)) { + str++; + var exponent = 0; + var expNegative = false; + chr = {{{ makeGetValue('str', 0, 'i8') }}}; + if (chr == '-'.charCodeAt(0)) { + expNegative = true; + str++; + } else if (chr == '+'.charCodeAt(0)) { + str++; + } + chr = {{{ makeGetValue('str', 0, 'i8') }}}; + while(1) { + if (!_isdigit(chr)) break; + exponent = exponent*10 + chr - '0'.charCodeAt(0); + str++; + chr = {{{ makeGetValue('str', 0, 'i8') }}}; + } + if (expNegative) exponent = -exponent; + ret *= Math.pow(10, exponent); + } + + // Set end pointer. if (endptr) { {{{ makeSetValue('endptr', 0, 'str', '*') }}} } + return ret; }, diff --git a/src/parseTools.js b/src/parseTools.js index 530bf54b..3855c633 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -477,7 +477,7 @@ function IEEEUnHex(stringy) { a = a & 0xfffff; if (e === 0x7ff) { if (a == 0 && b == 0) { - return 'Infinity'; + return neg ? '-Infinity' : 'Infinity'; } else { return 'NaN'; } |