jsonnet icon indicating copy to clipboard operation
jsonnet copied to clipboard

Add std.parseFloat

Open elkotito opened this issue 5 years ago • 13 comments

I would like to suggest a feature to parse strings into float numbers. I haven't found std.parseFloat() function in https://jsonnet.org/ref/stdlib.html. Since std.extVar returns a string it's impossible to use float numbers as external variables.

In my case, it would be useful since I used jsonnet to describe experiments in AllenNLP framework as sometimes I pass hyperparameters like learning rate as an environmental variable. Although I could obviously pass them using a top-level function, some platforms like Polyaxon requires to pass them as environmental variables.

elkotito avatar Jan 20 '20 12:01 elkotito

Since std.extVar returns a string it's impossible to use float numbers as external variables.

Not necessarily. You can do something like:

21x.jsonnet:

std.extVar('foo') * 21

Command to run (see that --ext-code is used rather than --ext-var):

jsonnet --ext-code foo=2 21x.jsonnet

sbarzowski avatar Jan 20 '20 12:01 sbarzowski

That said, parseFloat would be a useful addition to stdlib.

sbarzowski avatar Jan 20 '20 12:01 sbarzowski

Workaround: use std.parseJson, which can also parse a float as a special case:

$ ./jsonnet -e 'std.parseJson("3.14")'
3.1400000000000001

sbarzowski avatar Jan 20 '20 12:01 sbarzowski

Thank you for the workaround!

elkotito avatar Jan 20 '20 13:01 elkotito

Workaround: use std.parseJson, which can also parse a float as a special case:

That's what we've done. It's not easy to discover that workaround the first time, but it does work well enough so far.

seh avatar Jan 20 '20 13:01 seh

I need to be able to parse a float that isn't an external variable. Unfortunately, std.parseJson has a little problem:

% jsonnet -e 'std.parseJson("x3.1")'
Something went wrong during jsonnet_evaluate_snippet, please report this: [json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - invalid literal; last read: 'x'

glenntrewitt avatar Jul 02 '21 22:07 glenntrewitt

The error is not ideal and should be fixed (it shouldn't just crash) and it should be fixed. That said, "x3.1" is not a valid float, so I don't see how std.parseFloat would help you here.

sbarzowski avatar Jul 03 '21 08:07 sbarzowski

std.parseJson("1.0") is parsed as 1 (int) instead of 1.0 (float). Is there a workaround for this?

eladsegal avatar Jul 03 '21 14:07 eladsegal

I was just fooling around to see what would happen. I'd have expected a failure, of course. What I actually want to do is to validate a string that contains a bunch of numbers. I think that failing on such an assertion is OK for many purposes, but if you want to check that a string encodes a number as a option, you're out of luck.

What I'd really like to see is support for regexp matching.

glenntrewitt avatar Jul 03 '21 18:07 glenntrewitt

@eladsegal

std.parseJson("1.0") is parsed as 1 (int) instead of 1.0 (float).

There's no separate int type in Jsonnet. A floating point number 1.0 is printed out as 1. (That's the same behavior as in JS).

sbarzowski avatar Jul 14 '21 14:07 sbarzowski

@sbarzowski Oh, I see. Python's json library does make the distinction, so I hoped the same from jsonnet's python bindings. Thanks!

eladsegal avatar Jul 14 '21 15:07 eladsegal

@glenntrewitt

Weel parseFloat wouldn't help you. It would still error out.

Regexp support may come in the future. In the meantime you can validate "manually" which shouldn't be too hard in this case.

You can try a library I wrote: https://github.com/sbarzowski/jsonnet-parser-combinators. It can actually do more than regexp, but it's very slow.

sbarzowski avatar Jul 14 '21 18:07 sbarzowski

Just playing around with this before reading about the std.parseJson workaround and came up with this naive implementation:

{
  parseFloat(str)::
    local decimalPoint = std.find('.', std.reverse(std.stringChars(str)));
    assert std.length(decimalPoint) <= 1 : 'Not a float, str has too many decimal points';
    local strWithoutDecimalPoint = std.strReplace(str, '.', '');
    std.parseInt(strWithoutDecimalPoint) / std.pow(10, decimalPoint[0]),
}

Duologic avatar Feb 12 '24 10:02 Duologic