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

Expression statements can't begin with {, even in quasi #287

Closed
dan-simon opened this issue Apr 26, 2018 · 4 comments
Closed

Expression statements can't begin with {, even in quasi #287

dan-simon opened this issue Apr 26, 2018 · 4 comments

Comments

@dan-simon
Copy link
Contributor

While trying to debug

macro swap(a, b) { return quasi { my temp = {{{a}}}; {{{a}}} = {{{b}}}; {{{b}}} = temp; None } }; my w = 5; my z = 10; swap(w, z); say([w, z])

(the swap example from #194) I found that the error message seems to be due to the statement:expr token's definition being:

token statement:expr {
    <![{]>       # }], you're welcome vim
    <EXPR>
}

This causes issues in expressions like

macro foo (a, b) {return quasi { {{{a}}} + {{{b}}} } };

Where the statement expression {{{a}}} + {{{b}}} starts with { and so is not accepted by statement:expr.

This issue also occurs with objects:

{a: 2}

fails to parse.

I don't feel like I understand perl 6, the parser well enough to know how to fix this issue. I assume that <![{]> is there since we want to accept blocks (which start with {) as blocks rather than expressions, but I'm not sure.

@masak
Copy link
Owner

masak commented May 8, 2018

Thanks! 👍

Discussed this one with @sergot tonight. I think I have a proper fix for it, namely this:

@@ -64,7 +64,7 @@ grammar _007::Parser::Syntax {
         ['=' <EXPR>]?
     }
     token statement:expr {
-        <![{]>       # }], you're welcome vim
+        <!before <!before '{{{'> '{'>   # } }}}, you're welcome vim
         <EXPR>
     }
     token statement:block { <pblock> }

It passes all the tests on master.

But I'd like to (a) explain/explore why that line is there in the first place, and (b) write a regression test involving unquotes first in an expression.

masak pushed a commit that referenced this issue May 9, 2018
Closes #287.

As described in that issue, a statement starting with an unquote '{{{'
wouldn't parse properly. This was a bug, ultimately caused by the
fact that a statement starting with a '{' was deemed to *not* be an
expression statement.

The reason for this latter rule is that '{' starting a statement
introduces a *statement block*, like so:

    {
        my thing = "statement block";
        say("Hello, I'm a " ~ thing);
    }

The same disambiguation technique exists in Perl 5, Perl 6, and
JavaScript: '{' starts an object literal, except at the start of a
statement where it starts a statement block.

(Confounsing factor: I used to be able to demonstrate this as
parsing errors in Chrome Devtools, Firefox, and Node -- but all these
have lately improved their CLI parsing to introduce exceptions so
that it magically understands your intent and prefers object
literals. This also breaks some of the examples from the WAT talk.)

Fortunately, LTM offers us an out here: a '{{{' "token" is not a
type of '{' token, and so we should only disallow the former, not
the latter. It's quite unambiguously the case that '{' can not
occur at the start of an expression statement, but '{{{' *can*.
@masak
Copy link
Owner

masak commented May 9, 2018

Fix uploaded as #289@dan-simon and @sergot, feel free to have a look when you have time.

@dan-simon
Copy link
Contributor Author

This fix looks good to me.

We still have the analogous problem for object literals, but I don't think that can be fixed (for example, the empty object literal looks the same as the empty block). This being a problem is very rare, but it comes up in macros like

macro moo () {
    return quasi {{{
        {a: 2}
    }
}

In this case one can just surround the object in parentheses. But if we ever want, for example, object destructuring assignment, that won't work.

@masak
Copy link
Owner

masak commented May 10, 2018

Agreed about the problem for object literals. They way that problem is solved outside of quasis is with this disambiguation rule:

  • If it's at the start of a statement, it's a block.
  • Otherwise, it's an object literal.

As rules go, I'm pretty comfortable with this one, given that we want to avoid backtracking and given the unfortunate circumstance that object literals and blocks share a starter.

The one interesting complication is that quasis try to be "minimal" in the sense that they evaluate to the syntactically "simplest" form: a Q::Expr, a Q::Statement, or a Q::StatementList, whichever one's the first possible.

But, first, that shouldn't affect the parse at all. Having the parse be influenced by such a minimality criterion — such as, for example, choosing to parse something as an empty object literal instead of an empty block because that would make it able to return a simpler form — would not be a good idea.

Secondly, I don't know if that minimality rule is even necessary anymore, given the most recent happenings around Q::Expr::BlockAdapter. Essentially statements and statementlists can evaluate to values now, so there's less of a need to find the simplest form.

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

2 participants