From 7be6352531a94c7ac4f9a70ce94b597a27bfc19a Mon Sep 17 00:00:00 2001 From: Brian Donovan Date: Thu, 21 Oct 2010 09:54:28 -0700 Subject: [PATCH 1/3] Add format parameter index support to sprintf. This is primarily useful in cases when the format string can vary, but the argument order will not, as is often the case with format strings in Localized.strings files. From http://perldoc.perl.org/functions/sprintf.html: An explicit format parameter index, such as 2$. By default sprintf will format the next unused argument in the list, but this allows you to take the arguments out of order. See also this Cocoa example: http://stackoverflow.com/questions/1063843/is-there-a-way-to-specify-argument-position-index-in-nsstring-stringwithformat --- Objective-J/sprintf.js | 27 ++++++++++++--------- Tests/Objective-J/sprintfTest.j | 43 +++++++++++++++++++++++---------- 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/Objective-J/sprintf.js b/Objective-J/sprintf.js index d5df1b2a5..e728ed64e 100644 --- a/Objective-J/sprintf.js +++ b/Objective-J/sprintf.js @@ -22,8 +22,8 @@ // sprintf: -var formatRegex = new RegExp("([^%]+|%[\\+\\-\\ \\#0]*[0-9\\*]*(.[0-9\\*]+)?[hlL]?[cbBdieEfgGosuxXpn%@])", "g"); -var tagRegex = new RegExp("(%)([\\+\\-\\ \\#0]*)([0-9\\*]*)((.[0-9\\*]+)?)([hlL]?)([cbBdieEfgGosuxXpn%@])"); +var formatRegex = new RegExp("([^%]+|%(?:\\d+\\$)?[\\+\\-\\ \\#0]*[0-9\\*]*(.[0-9\\*]+)?[hlL]?[cbBdieEfgGosuxXpn%@])", "g"); +var tagRegex = new RegExp("(%)(?:(\\d+)\\$)?([\\+\\-\\ \\#0]*)([0-9\\*]*)((?:.[0-9\\*]+)?)([hlL]?)([cbBdieEfgGosuxXpn%@])"); exports.sprintf = function(format) { @@ -55,21 +55,27 @@ exports.sprintf = function(format) } var percentSign = subtokens[1], - flags = subtokens[2], - widthString = subtokens[3], - precisionString = subtokens[4], + argIndex = subtokens[2] + flags = subtokens[3], + widthString = subtokens[4], + precisionString = subtokens[5], length = subtokens[6], specifier = subtokens[7]; + if (argIndex === undefined) + argIndex = arg++; + else + argIndex = Number(argIndex); + var width = null; if (widthString == "*") - width = arguments[arg++]; + width = arguments[argIndex]; else if (widthString != "") width = Number(widthString); var precision = null; if (precisionString == ".*") - precision = arguments[arg++]; + precision = arguments[argIndex]; else if (precisionString != "") precision = Number(precisionString.substring(1)); @@ -80,7 +86,7 @@ exports.sprintf = function(format) if (RegExp("[bBdiufeExXo]").test(specifier)) { - var num = Number(arguments[arg++]); + var num = Number(arguments[argIndex]); var sign = ""; if (num < 0) @@ -154,12 +160,11 @@ exports.sprintf = function(format) if (specifier == "%") subresult = "%"; else if (specifier == "c") - subresult = String(arguments[arg++]).charAt(0); + subresult = String(arguments[argIndex]).charAt(0); else if (specifier == "s" || specifier == "@") - subresult = String(arguments[arg++]); + subresult = String(arguments[argIndex]); else if (specifier == "p" || specifier == "n") { - arg++; subresult = ""; } diff --git a/Tests/Objective-J/sprintfTest.j b/Tests/Objective-J/sprintfTest.j index b51bb7cd0..785c7707c 100644 --- a/Tests/Objective-J/sprintfTest.j +++ b/Tests/Objective-J/sprintfTest.j @@ -1,20 +1,37 @@ - -// TODO: add many many more of these... -var sprintfTestCases = [ - [["[%@]", "hello world"], "[hello world]"], - [["[%d]", 123], "[123]"], - [["[%f]", 123.1234], "[123.1234]"], - [["[%d]", 123.1234], "[123]"] -]; +var sprintf = ObjectiveJ.sprintf; @implementation sprintfTest : OJTestCase -- (void)test_sprintf +// TODO: add many many more of these... + +- (void)testObjectWithPrefixAndSuffix { - for (var i = 0; i < sprintfTestCases.length; i++) - { - [self assert:ObjectiveJ.sprintf.apply(null, sprintfTestCases[i][0]) equals:sprintfTestCases[i][1]]; - } + [self assert:@"[hello world]" equals:sprintf(@"[%@]", @"hello world")]; +} + +- (void)testDecimalWithPrefixAndSuffix +{ + [self assert:@"[123]" equals:sprintf(@"[%d]", 123)]; +} + +- (void)testFloatWithPrefixAndSuffix +{ + [self assert:@"[123.1234]" equals:sprintf(@"[%f]", 123.1234)]; +} + +- (void)testZeroPaddingWithWidthAndPercentEscaping +{ + [self assert:@"099%" equals:sprintf(@"%03d%%", 99)]; +} + +- (void)testOutOfOrderExplicitFormatParameterIndexes +{ + [self assert:@"2 > 1" equals:sprintf(@"%2$d > %1$d", 1, 2)]; +} + +- (void)testMixingImplicitAndExplicitFormatParameterIndexes +{ + [self assert:@"a < b && b > a" equals:sprintf(@"%@ < %2$@ && %@ > %1$@", @"a", @"b")]; } @end From 8427697a4b7a2118dc68d330cdc654ffabf8f178 Mon Sep 17 00:00:00 2001 From: Brian Donovan Date: Fri, 22 Oct 2010 14:15:22 -0700 Subject: [PATCH 2/3] Fix accidental globals. --- Objective-J/sprintf.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objective-J/sprintf.js b/Objective-J/sprintf.js index e728ed64e..82bf3c34c 100644 --- a/Objective-J/sprintf.js +++ b/Objective-J/sprintf.js @@ -55,7 +55,7 @@ exports.sprintf = function(format) } var percentSign = subtokens[1], - argIndex = subtokens[2] + argIndex = subtokens[2], flags = subtokens[3], widthString = subtokens[4], precisionString = subtokens[5], From 8ca2a1bf5b31adb924c778f289c5fcb0e006cf6a Mon Sep 17 00:00:00 2001 From: Brian Donovan Date: Fri, 22 Oct 2010 14:41:31 -0700 Subject: [PATCH 3/3] IE uses an empty string as the value of an empty capture group. --- Objective-J/sprintf.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objective-J/sprintf.js b/Objective-J/sprintf.js index 82bf3c34c..9ad4723a5 100644 --- a/Objective-J/sprintf.js +++ b/Objective-J/sprintf.js @@ -62,7 +62,7 @@ exports.sprintf = function(format) length = subtokens[6], specifier = subtokens[7]; - if (argIndex === undefined) + if (argIndex === undefined || argIndex === null || argIndex === "") argIndex = arg++; else argIndex = Number(argIndex);