Skip to content
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

Integer and Float Comparisons After Using Times Filter #267

Closed
shalaniw opened this issue Mar 15, 2023 · 15 comments
Closed

Integer and Float Comparisons After Using Times Filter #267

shalaniw opened this issue Mar 15, 2023 · 15 comments

Comments

@shalaniw
Copy link

I'm observing some odd behavior when using the times filter with a float value. In both of the cases below, the comparingValue variable should be 98.0. In both tests I check if 99 is greater than that value. For some reason, the case with the times filter fails with false and the case with the divide_by filter passes with true.

    // Test Fails
    @Test
    public void testUseVariableAndTimesFilter() throws RecognitionException {
        assertPatternResultEquals(TemplateParser.DEFAULT, "true", 
            "{% assign comparingValue = 98 | times: 1.0 %}{{ 99 > comparingValue }}");
    }

    // Test Passes   
    @Test
    public void testUseVariableAndDividedByFilter() throws RecognitionException {
        assertPatternResultEquals(TemplateParser.DEFAULT, "true",
            "{% assign comparingValue = 98 | divided_by: 1.0 %}{{ 99 > comparingValue }}");
    }

I believe this is a bug and that both cases should pass with true.

@msangel
Copy link
Collaborator

msangel commented Mar 16, 2023

Hello!
Thanks for the report. Bug confirmed.
I already have a fix for this, but the problem seems to be wider, so more investigation needed.

tech details: problem was in this part of code:

        return (a instanceof Number) && (b instanceof Number) &&
                super.asNumber(a).doubleValue() > super.asNumber(b).doubleValue();

in a case when variables are string (and in this case they are since variable evaluation perform to string)

@msangel
Copy link
Collaborator

msangel commented Mar 16, 2023

Another interesting question is about compatibility of this samples with original liquid.
In Jekyll I got "98" and in Liquid I got

/usr/lib/ruby/gems/3.1.0/gems/liquid-5.4.0/lib/liquid/parser.rb:18:in `consume': Liquid syntax error: Expected end_of_string but found comparison in "{{ '98' > comparingValue }}" (Liquid::SyntaxError)

So in solving this issue the preference for ruby's implementation will be given.

@kohlschuetter
Copy link
Contributor

@msangel Shouldn't we follow Jekyll's implementation here? We might control this using Flavor.

@msangel
Copy link
Collaborator

msangel commented Mar 16, 2023

Yep, we should.

msangel added a commit that referenced this issue Mar 16, 2023
@msangel
Copy link
Collaborator

msangel commented Mar 16, 2023

Have to debug ruby.... Looks like Liquid version of variable is not designed for printing anything but atom. it consume the atom and expects the end, even if there more tokens (that's where the error came from)
image

test code in shopify/liquid/test/integration/expression_test.rb file:

  def test_comparing_expression
    assert_template_result("true", "{% assign comparingValue = 98.0 %}{{ '98' > comparingValue }}")
  end

@msangel
Copy link
Collaborator

msangel commented Mar 16, 2023

Will debug Jekyll ones later....

@msangel
Copy link
Collaborator

msangel commented Mar 16, 2023

More of Liquid (just for self-documenting first)
This is not something developers will expect from "evaluate expression" functionality so I am pretty sure this behavior for Liquid is simply broken BUT we must follow it
image

@msangel
Copy link
Collaborator

msangel commented Mar 16, 2023

Jekyll have similar behavior but it simply return first atom(without any further expectations)
image

@msangel
Copy link
Collaborator

msangel commented Mar 17, 2023

In the end they are the same, Jekyll uses :warn mode strictness, while liquid :strict
Both are configurable in both places.

@msangel
Copy link
Collaborator

msangel commented Mar 17, 2023

Jekyll default configuration is :warn:
image
Liquid default is lax:
image

Traces are:
lax name eval: https://github.com/Shopify/liquid/blob/master/lib/liquid/variable.rb#L50
strict name eval: https://github.com/Shopify/liquid/blob/master/lib/liquid/variable.rb#L68
--->
where only first token is taken (in our case number): https://github.com/Shopify/liquid/blob/master/lib/liquid/parser.rb#L51-L61

@msangel
Copy link
Collaborator

msangel commented Mar 17, 2023

Currently, this library has only strict and non-strict modes. And this strictness is applied only to context behavior, as partial parsing is not supported. Template source is tokenized no matter what or general fail.

@msangel
Copy link
Collaborator

msangel commented Mar 17, 2023

So we have two options:

  • Have ruby compatibility with their weirdness - not execute expressions in out tag but simply print the first atom
  • Have proper and predictable behavior that meets any expectations but goes against the original templator - execute expressions in out tag and print evaluated result

What would you pick?
@bkiers

@bkiers
Copy link
Owner

bkiers commented Mar 17, 2023

During my active development, I've always thrived for compatibility with Ruby's Liquid library. But given this is rather odd, I'd go for the predictable route. But given I am not actively working on this, if you'd rather be compatible with Ruby, I'd support that too @msangel.

@msangel
Copy link
Collaborator

msangel commented Mar 19, 2023

Ok. My decision is: by default this will behave as expected(as of now). BUT there will be a flag that will turn on either the first token either error on something more than one token. If someone will need full compatibility, the one will get it.
Technical notes: here on parser level, lexer is not affected:

output
 : outStart expr filter* OutEnd
 ;

@msangel
Copy link
Collaborator

msangel commented Aug 2, 2023

Closed fully in af65618 accoarding to exact ruby behavior

@msangel msangel closed this as completed Aug 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants