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

feature request: elsif #72

Closed
rsaccon opened this issue May 1, 2011 · 9 comments · Fixed by #892
Closed

feature request: elsif #72

rsaccon opened this issue May 1, 2011 · 9 comments · Fixed by #892
Labels
Milestone

Comments

@rsaccon
Copy link

rsaccon commented May 1, 2011

of course it can be done with the existing helpers (if, else, unless), but often elsif is just the natural choice people are used too.

@wycats
Copy link
Collaborator

wycats commented Dec 27, 2011

I'm not sure how this would be implemented. The else functionality is implemented by passing an inverse option to helpers. I can't think of a way to implement elsif without adding a lot of complexity to the options hash and usage of it.

Thoughts?

@wagenet
Copy link
Collaborator

wagenet commented Feb 9, 2012

Seeing as there are no suggestions for how to implement this, I'm closing it. If anyone has ideas or a PR for this, we'll gladly reconsider.

@wagenet wagenet closed this as completed Feb 9, 2012
@kpdecker
Copy link
Collaborator

kpdecker commented Sep 4, 2014

Reopening as I think the recent inverse and program changes from @mmun might allow for something like multiple chained inverses if we can get the Jison productions to agree.

Some concerns to think about:

  1. Is short circuiting an issue with parameter evaluation, particularly subexpressions as arguments?

  2. Do we want to enforce the if behavior or is it sensible to allow for any helper? One straw man would be

      {{#if foo}}
      {{#else unless bar}}
      {{#else for bar}}
      {{#else if baz}}
      {{/if}}
    

    With only the opening and closing helper names being checked for matches.

@kpdecker kpdecker reopened this Sep 4, 2014
@kpdecker
Copy link
Collaborator

kpdecker commented Sep 4, 2014

In terms of the actual execution, inverse would effectively become a chain, so there would be no helper changes needed and they would not need to be aware of anything more than "there is something after me in the negative case" which they would continue to define as they please.

@kpdecker kpdecker added this to the 2.1 milestone Sep 4, 2014
@wycats
Copy link
Collaborator

wycats commented Sep 4, 2014

@kpdecker This is nice! I'm surprised by this, but in a good way.

I'd love to look at an implementation when we have one 😄

@mmun
Copy link
Contributor

mmun commented Sep 4, 2014

I am intrigued as well and curious how it could be generalized for other use cases besides if.

@yiminghe
Copy link

yiminghe commented Sep 5, 2014

a suggestion: you can do elseif parsing in compiler and group them, then test them and run the specified branch in if block.

here is an implementation of xtemplate:
compiler: https://github.com/kissyteam/xtemplate/blob/052de55251cd620ae59f717e7ec8b30924f3b0fa/lib/xtemplate/compiler.js#L280

if: https://github.com/kissyteam/xtemplate/blob/052de55251cd620ae59f717e7ec8b30924f3b0fa/lib/xtemplate/runtime/commands.js#L89

example:

{{#if(x>y)}}
1
{{elseif(x>z)}}
2
{{/if}}

compiled to

var id2 = scope.resolve(["x"]);
    var exp4 = id2;
    var id3 = scope.resolve(["y"]);
    exp4 = (id2) > (id3);
    params1.push(exp4);
    option0.params = params1;
    option0.fn = function (scope, buffer) {
        buffer.append('\n1\n');
        return buffer;
    };
    var elseIfs5 = []
    var elseIf6 = {}
    elseIf6.test = function (scope) {
        var id7 = scope.resolve(["x"]);
        var exp9 = id7;
        var id8 = scope.resolve(["z"]);
        exp9 = (id7) > (id8);
        return (exp9);
    };
    elseIf6.fn = function (scope, buffer) {
        buffer.append('\n2\n');
        return buffer;
    };
    elseIfs5.push(elseIf6);
    option0.elseIfs = elseIfs5;
    pos.line = 1;
    pos.col = 6;
    buffer = ifCommand.call(tpl, scope, option0, buffer);
    return buffer;

@kpdecker
Copy link
Collaborator

The basic approach here creates ambiguous parse trees.

{{#foo}}
{{^bar}}
{{/foo}}

and

{{#foo}}
  {{^bar}}
  {{/bar}}
{{/foo}}

Can't be expressed in a context free manner as long as the same syntax construct is used to both start an inversed block and the chained inverse operation.

In order to support this feature, we will need to define a syntax that is only used for the chained operations. One thought is using {{else as the token that will signify chaining. This means that we will have to desupport the {{else foo}}{{/foo}} syntax but this particular syntax feels very weird as a top-level declaration and there is a fix by replacing {{else with {{^ so I think we can get away with such a change.
Current we support {{else foo}}{{/foo}} as a root

kpdecker added a commit that referenced this issue Oct 28, 2014
Allows users to chain multiple helpers together using their inverse callbacks. I.e.

```
{{#if foo}}
{{else if bar}}
{{else}}
{{/if}}
```

The control flow here effectively causes the helpers to be nested. The above is actually syntactic sugar for this:

```
{{#if foo}}
{{else}}
  {{#if bar}}
  {{else}}
  {{/if}}
{{/if}}
```

Any helper may be used in this manner, the only requirement is they support normal calls and inverse calls.

Introduces a breaking change in that `{{else foo}}` may no longer be used as a root level operator. Instead `{{^foo}}` must be used.

Fixes #72.
@kpdecker
Copy link
Collaborator

Closing in favor of #892

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants