{"action":"closed","issue":{"url":"https://api.github.com/repos/sass/libsass/issues/1728","labels_url":"https://api.github.com/repos/sass/libsass/issues/1728/labels{/name}","comments_url":"https://api.github.com/repos/sass/libsass/issues/1728/comments","events_url":"https://api.github.com/repos/sass/libsass/issues/1728/events","html_url":"https://github.com/sass/libsass/issues/1728","id":116718389,"number":1728,"title":"Binary operators adjacent to interpolations produce incorrect output and/or errors","user":{"login":"andrew-skybound","id":1355174,"avatar_url":"https://avatars.githubusercontent.com/u/1355174?v=3","gravatar_id":"","url":"https://api.github.com/users/andrew-skybound","html_url":"https://github.com/andrew-skybound","followers_url":"https://api.github.com/users/andrew-skybound/followers","following_url":"https://api.github.com/users/andrew-skybound/following{/other_user}","gists_url":"https://api.github.com/users/andrew-skybound/gists{/gist_id}","starred_url":"https://api.github.com/users/andrew-skybound/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/andrew-skybound/subscriptions","organizations_url":"https://api.github.com/users/andrew-skybound/orgs","repos_url":"https://api.github.com/users/andrew-skybound/repos","events_url":"https://api.github.com/users/andrew-skybound/events{/privacy}","received_events_url":"https://api.github.com/users/andrew-skybound/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/sass/libsass/labels/Bug%20-%20Confirmed","name":"Bug - Confirmed","color":"e11d21"},{"url":"https://api.github.com/repos/sass/libsass/labels/Dev%20-%20Test%20Written","name":"Dev - Test Written","color":"bfe5bf"}],"state":"closed","locked":false,"assignee":null,"milestone":{"url":"https://api.github.com/repos/sass/libsass/milestones/5","html_url":"https://github.com/sass/libsass/milestones/3.4","labels_url":"https://api.github.com/repos/sass/libsass/milestones/5/labels","id":1013792,"number":5,"title":"3.4","description":"- operator precedences\r\n- binary expressions\r\n- correct sourcemaps\r\n","creator":{"login":"mgreter","id":282293,"avatar_url":"https://avatars.githubusercontent.com/u/282293?v=3","gravatar_id":"","url":"https://api.github.com/users/mgreter","html_url":"https://github.com/mgreter","followers_url":"https://api.github.com/users/mgreter/followers","following_url":"https://api.github.com/users/mgreter/following{/other_user}","gists_url":"https://api.github.com/users/mgreter/gists{/gist_id}","starred_url":"https://api.github.com/users/mgreter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mgreter/subscriptions","organizations_url":"https://api.github.com/users/mgreter/orgs","repos_url":"https://api.github.com/users/mgreter/repos","events_url":"https://api.github.com/users/mgreter/events{/privacy}","received_events_url":"https://api.github.com/users/mgreter/received_events","type":"User","site_admin":false},"open_issues":29,"closed_issues":1,"state":"open","created_at":"2015-03-09T22:28:53Z","updated_at":"2016-01-17T03:29:02Z","due_on":"2016-05-31T22:00:00Z","closed_at":null},"comments":12,"created_at":"2015-11-13T07:37:59Z","updated_at":"2016-01-17T03:29:02Z","closed_at":"2016-01-17T03:29:02Z","body":"This issue is somewhat complicated so I'm first going to explain how I have observed Ruby SASS to work with respect to operators that appear adjacent to interpolation expressions, followed by the tests and results.\r\n\r\n#### Background\r\nIn SASS, the behavior of binary operators changes when they are adjacent to an interoplation expression. They produce a kind of delimited string-concatenation operation, where the delimiter is the operator itself, and they cause the entire surrounding sequence of terms to become part of a single, unquoted string.\r\n\r\nThis behavior is specific to interpolation expressions; unquoted strings produced by `unquote()` do not operate in this way:\r\n\t\r\n\tunquote(\"a\")+b -> ab\r\n\t#{a}+b         -> a+b\r\n\ta*#{b}         -> a*b\r\n\t\r\nThis behavior is consistent for all numeric, comparison, and logical binary operators.  But here is the interesting part: when **another** binary expression is concatenated to an interpolation expression in this way, the entire expression is evaluated **according to operator precedence even though concatenation does not produce a numeric result**.  Consider these examples:\r\n\t\r\n\t#{2}+3px*4 -> 2+12px    // right (numeric multiplication) expression is evaluated first, then concatenated\r\n\t#{2}*3px+4 -> 2*3px4    // left (concatenation) expression is evaluated first, then concatenated\r\n\t#{2}+3px+4 -> 2+7px     // when both operators have the same precedence, concatenation happens last\r\n\t#{2}*3px*4 -> 2*12px    // same -- concatenation happens last\r\n\t3px*4*#{2} -> 12px*2    // same -- concatenation happens last\r\n\t\r\nTake special notice of example 3, 4, and 5: the order in which these terms are being evaluated is different than it would be if the same operators were used for a numeric expression.  Sequential numeric expressions with operators of the same precedence are usually evaluated left-to-right, but here, the concatenation *always happens last* even when it is on the left.\r\n\r\nFinally, whitespace around the operator is preserved in the output as either zero or one space:\r\n\t\r\n\t#{2} +3px    -> 2 +3px\r\n\t#{2}+  3px   -> 2+ 3px\r\n\t#{2}   + 3px -> 2 + 3px\r\n\t\r\n#### Tests\r\nHere is a complete test suite that should cover everything.  I have categorized the tests:\r\n\r\n(a) tests the spacing around numeric operators when the interpolation expression is on the left\r\n(b) tests the spacing around numeric operators when the interpolation expression is on the right\r\n(c) tests numeric operator precedence when the interpolation expression is on the left\r\n(d) tests numeric operator precedence when the interpolation expression is on the right\r\n(e) tests comparison operator precedence\r\n\r\nLogical operators (and/or) have been omitted for brevity but exhibit the same behavior.\r\n\r\n```SCSS\r\n.test {\r\n  a01: (#{a}+5.0% + 2);\r\n  a02: (#{a}+ 5.0% + 2);\r\n  a03: (#{a}  +5.0% + 2);\r\n  a04: (#{a} +  5.0% + 2);\r\n  b01: (5 + 2.0%+#{a});\r\n  b02: (5 + 2.0%+ #{a});\r\n  b03: (5 + 2.0%  +#{a});\r\n  b04: (5 + 2.0% +  #{a});\r\n  c01: (#{a} +5.0% + 2);\r\n  c02: (#{a} -5.0% + 2);\r\n  c03: (#{a} /5.0% + 2);\r\n  c04: (#{a} *5.0% + 2);\r\n  c05: (#{a} +5.0% - 2);\r\n  c06: (#{a} -5.0% - 2);\r\n  c07: (#{a} /5.0% - 2);\r\n  c08: (#{a} *5.0% - 2);\r\n  c09: (#{a} +5.0% / 2);\r\n  c10: (#{a} -5.0% / 2);\r\n  c11: (#{a} /5.0% / 2);\r\n  c12: (#{a} *5.0% / 2);\r\n  c13: (#{a} +5.0% * 2);\r\n  c14: (#{a} -5.0% * 2);\r\n  c15: (#{a} /5.0% * 2);\r\n  c16: (#{a} *5.0% * 2);\r\n  d01: (5 + 2.0% +#{a});\r\n  d02: (5 + 2.0% -#{a});\r\n  d03: (5 + 2.0% /#{a});\r\n  d04: (5 + 2.0% *#{a});\r\n  d05: (5 - 2.0% +#{a});\r\n  d06: (5 - 2.0% -#{a});\r\n  d07: (5 - 2.0% /#{a});\r\n  d08: (5 - 2.0% *#{a});\r\n  d09: (5% / 2.0 +#{a});\r\n  d10: (5% / 2.0 -#{a});\r\n  d11: (5% / 2.0 /#{a});\r\n  d12: (5% / 2.0 *#{a});\r\n  d13: (5 * 2.0% +#{a});\r\n  d14: (5 * 2.0% -#{a});\r\n  d15: (5 * 2.0% /#{a});\r\n  d16: (5 * 2.0% *#{a});\r\n  e01: (#{a} ==5.0% == 2);\r\n  e02: (#{a} >5.0% > 2);\r\n  e03: (#{a} <5.0% < 2);\r\n  e04: (#{a} >=5.0% >= 2);\r\n  e05: (#{a} <=5.0% <= 2);\r\n  e06: (#{a} !=5.0% != 2);\r\n}\r\n```\r\n\r\nRuby SASS Output: (3.4.19)\r\n```CSS\r\n.test {\r\n  a01: a+7%;\r\n  a02: a+ 7%;\r\n  a03: a +7%;\r\n  a04: a + 7%;\r\n  b01: 7%+a;\r\n  b02: 7%+ a;\r\n  b03: 7% +a;\r\n  b04: 7% + a;\r\n  c01: a +7%;\r\n  c02: a -3%;\r\n  c03: a /5%2;\r\n  c04: a *5%2;\r\n  c05: a +3%;\r\n  c06: a -7%;\r\n  c07: a /5%-2;\r\n  c08: a *5%-2;\r\n  c09: a +2.5%;\r\n  c10: a -2.5%;\r\n  c11: a /2.5%;\r\n  c12: a *2.5%;\r\n  c13: a +10%;\r\n  c14: a -10%;\r\n  c15: a /10%;\r\n  c16: a *10%;\r\n  d01: 7% +a;\r\n  d02: 7% -a;\r\n  d03: 52% /a;\r\n  d04: 52% *a;\r\n  d05: 3% +a;\r\n  d06: 3% -a;\r\n  d07: 5-2% /a;\r\n  d08: 5-2% *a;\r\n  d09: 2.5% +a;\r\n  d10: 2.5% -a;\r\n  d11: 2.5% /a;\r\n  d12: 2.5% *a;\r\n  d13: 10% +a;\r\n  d14: 10% -a;\r\n  d15: 10% /a;\r\n  d16: 10% *a;\r\n  e01: a ==false;\r\n  e02: a >true;\r\n  e03: a <false;\r\n  e04: a >=true;\r\n  e05: a <=false;\r\n  e06: a !=true;\r\n}\r\n```\r\n\r\nNow, because of the way Libsass incorrectly interprets these operators, many of these tests cause errors to occur.  Here is the same test as above with the declarations that Libsass cannot yet handle commented out:\r\n\r\n```SCSS\r\n.test {\r\n  a01: (#{a}+5.0% + 2);\r\n  a02: (#{a}+ 5.0% + 2);\r\n  a03: (#{a}  +5.0% + 2);\r\n  a04: (#{a} +  5.0% + 2);\r\n  b01: (5 + 2.0%+#{a});\r\n  b02: (5 + 2.0%+ #{a});\r\n  b03: (5 + 2.0%  +#{a});\r\n  b04: (5 + 2.0% +  #{a});\r\n  c01: (#{a} +5.0% + 2);\r\n  c02: (#{a} -5.0% + 2);\r\n  c03: (#{a} /5.0% + 2);\r\n  //c04: (#{a} *5.0% + 2);\r\n  c05: (#{a} +5.0% - 2);\r\n  c06: (#{a} -5.0% - 2);\r\n  c07: (#{a} /5.0% - 2);\r\n  //c08: (#{a} *5.0% - 2);\r\n  c09: (#{a} +5.0% / 2);\r\n  c10: (#{a} -5.0% / 2);\r\n  c11: (#{a} /5.0% / 2);\r\n  //c12: (#{a} *5.0% / 2);\r\n  c13: (#{a} +5.0% * 2);\r\n  c14: (#{a} -5.0% * 2);\r\n  //c15: (#{a} /5.0% * 2);\r\n  //c16: (#{a} *5.0% * 2);\r\n  d01: (5 + 2.0% +#{a});\r\n  d02: (5 + 2.0% -#{a});\r\n  d03: (5 + 2.0% /#{a});\r\n  //d04: (5 + 2.0% *#{a});\r\n  d05: (5 - 2.0% +#{a});\r\n  d06: (5 - 2.0% -#{a});\r\n  d07: (5 - 2.0% /#{a});\r\n  //d08: (5 - 2.0% *#{a});\r\n  d09: (5% / 2.0 +#{a});\r\n  d10: (5% / 2.0 -#{a});\r\n  d11: (5% / 2.0 /#{a});\r\n  //d12: (5% / 2.0 *#{a});\r\n  d13: (5 * 2.0% +#{a});\r\n  d14: (5 * 2.0% -#{a});\r\n  d15: (5 * 2.0% /#{a});\r\n  //d16: (5 * 2.0% *#{a});\r\n  //e01: (#{a} ==5.0% == 2);\r\n  //e02: (#{a} >5.0% > 2);\r\n  //e03: (#{a} <5.0% < 2);\r\n  //e04: (#{a} >=5.0% >= 2);\r\n  //e05: (#{a} <=5.0% <= 2);\r\n  //e06: (#{a} !=5.0% != 2);\r\n}\r\n```\r\n\r\nIt's worth noting that the \"e\" tests fail beacuse of #1727.\r\n\r\nHere is the output Libsass produces.  Only c02, c10, and c14 are correct:\r\n\r\nLibsass Output (3.2.5):\r\n```CSS\r\n.test {\r\n  a01: a+5.0%2;\r\n  a02: a5%2;\r\n  a03: a5%2;\r\n  a04: a5%2;\r\n  b01: 7%a;\r\n  b02: 7%a;\r\n  b03: 7%a;\r\n  b04: 7%a;\r\n  c01: a5%2;\r\n  c02: a -3%;\r\n  c03: a/5%2;\r\n  c05: a5%-2;\r\n  c06: a -7%;\r\n  c07: a/5%-2;\r\n  c09: a2.5%;\r\n  c10: a -2.5%;\r\n  c11: a/5%/2;\r\n  c13: a10%;\r\n  c14: a -10%;\r\n  d01: 7%a;\r\n  d02: 7%-a;\r\n  d03: 52%/a;\r\n  d05: 3%a;\r\n  d06: 3%-a;\r\n  d07: 5-2%/a;\r\n  d09: 2.5%a;\r\n  d10: 2.5%-a;\r\n  d11: 2.5%/a;\r\n  d13: 10%a;\r\n  d14: 10%-a;\r\n  d15: 10%/a;\r\n}\r\n```\r\n\r\n<bountysource-plugin>\r\n---\r\nWant to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/28213234-binary-operators-adjacent-to-interpolations-produce-incorrect-output-and-or-errors?utm_campaign=plugin&utm_content=tracker%2F283068&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F283068&utm_medium=issues&utm_source=github).\r\n</bountysource-plugin>"}}