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

Support () in expressions #25

Closed
kornelski opened this issue Jun 18, 2018 · 10 comments
Closed

Support () in expressions #25

kornelski opened this issue Jun 18, 2018 · 10 comments

Comments

@kornelski
Copy link
Contributor

kornelski commented Jun 18, 2018

As per #2 ructe does not reliably find whole Rust expressions. A workaround for that could be supporting () as a special case. Because Rust guarantees all parenthesis are balanced in valid expressions, ructe won't need to parse full expressions, just tokenize (to skip string literals) and balance parens.

@if let Some(i) = Some(1) {
    @i+1 // prints 1+1
    @(i+1) // error, but should be supported
}

For basic arithmetic I'm working around this using .saturating_add(1), but it's a bit silly.

In some cases I also need more complex expressions, e.g. to convert Option<String> to Option<&str> an ugly .as_ref().map(|s| s.as_str()) is needed, but this expression is too complex for Ructe.

@kaj
Copy link
Owner

kaj commented Jun 22, 2018

Yes, allowing parenthesis seems like a good and reasonably simple idea. Do you want to implement it, or should I?

@kornelski
Copy link
Contributor Author

I'm currently too busy, so please implement it if you can.

@kaj
Copy link
Owner

kaj commented Jun 23, 2018

Actually as long as @expr is a valid expression, so is @(expr). The task here is that more expressions should be allowed in certain places. So this issue is actually kind of the same as #24 , but for arithmetic expressions rather than logic.

@kornelski
Copy link
Contributor Author

kornelski commented Jun 23, 2018

I wasn't sure if incomplete parsing was a bug or a feature :)

I'm assuming full parsing of arbitrary Rust expressions is relatively hard, and special-casing ( + whatever + ) makes the task much easier.

Parsing text as long as it matches Rust expression syntax seems problematic:

  • There's poor error handling. If I want foo() * bar() but make a typo, let's say foo() * .bar() I'll unexpectedly get foo() parsed as a valid expression and some unwanted text after it.

  • If I want to print some text after a Rust expression, I have to be careful to make that text not look like code. If I wanted @x + 2 to display as 2 + 2 I'd probably need some hack like @x <!-- --> + 2.

So rules for the expressions could be:

  • In text and attributes, expression can't contain spaces, unless spaces are inside of ()/{}/"". so @x.foo(1 + 2).bar("baz quz") is a single expression, and @(x + 1) is an arithmetic expression, but @x + 1 is x and " + 1".

  • In @if everything until { is an expression (not counting {s nested in ()/"" etc.)

This way all expression parsing could be simplified to a tokenizer and checks for balanced ()/{}.

@kaj
Copy link
Owner

kaj commented Jun 23, 2018

Yes, avoiding surprises in what is considered a single expression or not gets complex faster than I thought it would.

My original intent was that for a simple @something expression, only very "simple" expressions should be allowed, while allowing more complex things only if necessary. So I thought @foo+2 should parse as the expression foo followed by the string +2, while a method call like @method(foo+2) must include everything until the parenthesis are balanced in the expression. But then, it could be intended the expression method followed by the string (foo+2) ... And here my ideas gets a bit confusing ...

In the rules you suggest, is it only whitespace that is special, or operators as well? Would you consider @x+1 the same as @(x + 1) or as the expression x followed by the string +1? My hunch is that the later would be less surprising and make it easier to write templates.

@kornelski
Copy link
Contributor Author

kornelski commented Jun 23, 2018

Yes, breaking expression on operators could be OK. Or rather, instead of defining what ends the expression, it could be defined that at the "top level" (outside parens) only specific constructs are allowed (such as @ident, @method() and @path::to::method(), @expr.property), and everything else requires @(…).

kaj added a commit that referenced this issue Jun 23, 2018
Basically, just handle nesting parenthesis, comments, and strings, and
let the rust compiler sort everything else out in the generated code.

Attempts to handle #25 .
kaj added a commit that referenced this issue Jun 23, 2018
Basically, just handle nesting parenthesis, comments, and strings, and
let the rust compiler sort everything else out in the generated code.

Attempts to handle #25 .
@kaj
Copy link
Owner

kaj commented Jun 24, 2018

Ok, done, I think. The only thing I'm not certain about is that currently an arbitrary number of calls, indexes are allowed. Eg, if foo is a method returning a vector of functions, this is ok: @foo(args)[index](other, args). Maybe it should be limited to one set or arguments or index?

@kornelski
Copy link
Contributor Author

A chain of methods is certainly fine (foo().bar().baz()). Method + array may be useful get_foos()[0], so perhaps the rule in general is OK.

@kaj
Copy link
Owner

kaj commented Jun 25, 2018

Yes, I think it will be hard to create a more limited syntax that is not too limited, so I think this is ok for now. So I think this issue is ready to be closed (the code is already merged).

@kornelski
Copy link
Contributor Author

It works great, thanks!

kaj added a commit that referenced this issue Jul 5, 2018
Changes since v0.3.16 includes:

* Template syntax:
  - Allow local ranges (i.e. `2..7`) in loop expressions.
  - Allow underscore rust names.  There is use for unused variables in
    templates, so allow names starting with underscore.
  - Issue #24 / PR #28: Allow logic operators in `@if ...` expressions.
  - Issue #25 / PR #27: Allow much more in parentehsis expressions.

* Improved examples:
  - A new design for the framework examples web page, using svg graphics.
  - Improve code and inline documentation of iron and nickel examples.
  - Add a similar example with the Gotham framework.

* Recognize `.svg` static files.
* Allocate much fewer strings when parsing expressions in templates.
* PR #26: use `write_all` rather than the `write!` macro in generated
  code, contributed by @kornelski
* Fix `application/octet-stream` MIME type.  Contributed by @kornelski.
* Use write_str/write_all when generating output.  Contributed by
  @kornelski.
@Aunmag Aunmag mentioned this issue Dec 4, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants