-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added some number formatting capability #1
Changes from 12 commits
3df01f9
d944419
14b3af4
16e8d1a
871d473
c296e94
524eaa0
41c131c
d1d351c
067f47e
be9c843
3119990
17af19b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,13 @@ | ||
# vim: ts=2:sw=2:expandtab | ||
### | ||
Source code and build tools for this file are available at: | ||
https://github.com/deleted/string-format | ||
|
||
This project attempts to implement python-style string formatting, as documented here: | ||
http://docs.python.org/2/library/string.html#format-string-syntax | ||
|
||
The format spec part is not complete, but it can handle field padding, float precision, and such | ||
### | ||
format = String::format = (args...) -> | ||
|
||
if args.length is 0 | ||
|
@@ -8,8 +18,8 @@ format = String::format = (args...) -> | |
message = 'cannot switch from {} to {} numbering'.format() | ||
|
||
@replace \ | ||
/([{}])\1|[{](.*?)(?:!(.+?))?[}]/g, | ||
(match, literal, key, transformer) -> | ||
/([{}])\1|[{](.*?)(?:!([^:]+?)?)?(?::(.+?))?[}]/g, | ||
(match, literal, key, transformer, formatSpec) -> | ||
return literal if literal | ||
|
||
if key.length | ||
|
@@ -21,7 +31,10 @@ format = String::format = (args...) -> | |
throw new Error message 'explicit', 'implicit' if explicit | ||
value = args[idx++] ? '' | ||
|
||
value = value.toString() | ||
if formatSpec | ||
value = applyFormat value, formatSpec | ||
else | ||
value = "#{value}" | ||
if fn = format.transformers[transformer] then fn.call(value) ? '' | ||
else value | ||
|
||
|
@@ -37,6 +50,68 @@ resolve = (object, key) -> | |
value = object[key] | ||
if typeof value is 'function' then value.call object else value | ||
|
||
format.transformers = {} | ||
# An implementation of http://docs.python.org/2/library/string.html#format-specification-mini-language | ||
applyFormat = (value, formatSpec) -> | ||
pattern = /// | ||
([^{}](?=[<>=^]))?([<>]^)? # fill & align | ||
([-+\x20])? # sign | ||
(\#)? # integer base specifier | ||
(0)? # zero-padding | ||
(\d+)? # width | ||
(,)? # use a comma thousands-seperator | ||
(?:\.(\d+))? # precision | ||
([bcdeEfFgGnosxX%])? # type | ||
/// | ||
[fill, align, sign, hash, zeropad, width, comma, precision, type] = formatSpec.match(pattern)[1..] | ||
if zeropad | ||
fill = '0' | ||
align = '=' | ||
align or= '>' | ||
|
||
switch type | ||
when 'b', 'c', 'd', 'o', 'x', 'X', 'n' # integer | ||
isNumeric = yes | ||
value = '' + parseInt(value, 10) | ||
when 'e','E','f','F','g','G','n','%' # float | ||
isNumeric = true | ||
value = parseFloat(value) | ||
if precision | ||
value = value.toFixed(parseInt(precision)) | ||
else | ||
value = ''+value | ||
when 's' #string | ||
isNumeric = false | ||
value = "#{value}" | ||
|
||
if isNumeric and sign | ||
if sign in ["+"," "] | ||
if value[0] != '-' | ||
value = sign + value | ||
|
||
if isNumeric and value.charAt(0) in "+-" | ||
memoSign = value.charAt 0 | ||
value = value.substr 1 | ||
|
||
if fill | ||
value = ''+value | ||
while value.split('.')[0].length < parseInt(width) | ||
switch align | ||
when '=' | ||
if value[0] in "+- " | ||
value = value[0] + fill + value[1..] | ||
else | ||
value = fill + value | ||
when '<' | ||
value = value + fill | ||
when '>' | ||
value = fill + value | ||
when '^' | ||
throw new Error("Not implemented") | ||
|
||
value = if memoSign then "#{memoSign}#{value}" else value | ||
|
||
return value | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if memoSign then "#{memoSign}#{value}" else value |
||
|
||
format.transformers or= {} | ||
|
||
format.version = '0.2.1' |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -101,3 +101,9 @@ describe 'String::format', -> | |
'{{{{0}}}}'.format(null).should.equal '{{0}}' | ||
'}}{{'.format(null).should.equal '}{' | ||
'}}x{{'.format(null).should.equal '}x{' | ||
|
||
it "correctly formats floats", -> | ||
'{:0}'.format(1.2345).should.equal '1.2345' | ||
'{:03.2f}'.format(1.2345).should.equal '001.23' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Python (2.7.2) behaves differently:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are correct. It helps to actually test against the python behavior. Fixed and added some additional tests. |
||
'{:03.2f}'.format(-1.2345).should.equal '-001.23' | ||
'{:+02.3f}'.format(1.2345).should.equal '+01.234' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.