Perhaps a better way to implement this would be to use Ruby's string interpolation, which I'm pretty sure allows arbitrary expressions and unlimited levels of nesting. For example, this ERB document:
<html>
<%= form_for @stuff do |f| %>
<%= f.text_field :things %>
<% end %>
</html>
So, ERB goes back to being a very simple transformation with no special cases and you can use any partial syntax in <%= %> blocks that would make sense.
EDIT: Ok, I'm pretty much giving up on this as I can't think of a good enough solution. The two best options I came up with are:
1. Feed the embedded code to Ripper as it comes in and use that to keep track of the lexical context. Ripper is pretty fast so it might not be a significant performance hit, but it only works in Ruby 1.9 and I don't know if a comparable parser for 1.8 exists.
2. Use a setter method like _buf= to append to the output buffer, and don't wrap the embedded expressions in parens. Because the = operator has such low precendence, the generated code will almost always behave as intended, but there are a few lower precendence operators that will break like "and", "or", and postfix "if", "unless", "while" and "until".
Indeed, I'm hacking on it now and it seems like there is no way to avoid detecting complete vs incomplete expressions. I guess the question then becomes, can the expressions be classified in a way that is both fast and correct? Let's see if I can find a way.
The author seems to be unfamiliar with the "expression" and "statement" conventions used by imperative languages. An expression is something which has a value, whereas a statement describes an action that doesn't (necessarily) have a result. <%= %> expects an expression ie something that has a value. <% %> expects a statement (or series of statements), which don't necessarily have a value.
Since the author admittedly made up the term "complete expression" whose definition mirrors what I would expect for just "expression". He also used the word "subexpression" to describe parts of something that would otherwise be called a "statement".
Though I think his vocabulary is a little confusing, after re-reading I think I get what he's actually complaining about. It is really awkward for a <%= %> tag to not actually be a self-contained, parse-able expression. On the other hand, you'd really rather have things like form_for return the text to print, not just print it (thus allowing the use of the <%= %> instead of <% %>). It's just one of those annoying situations where attempting to be pure and elegant results in an ugly kludge in the real world.
Why do you get that idea? I didn't get that impression at all. I don't think the author doesn't know the difference, I think he just used different terms to get his point across.
8 comments
[ 2.8 ms ] story [ 30.6 ms ] threadEDIT: Ok, I'm pretty much giving up on this as I can't think of a good enough solution. The two best options I came up with are:
1. Feed the embedded code to Ripper as it comes in and use that to keep track of the lexical context. Ripper is pretty fast so it might not be a significant performance hit, but it only works in Ruby 1.9 and I don't know if a comparable parser for 1.8 exists.
2. Use a setter method like _buf= to append to the output buffer, and don't wrap the embedded expressions in parens. Because the = operator has such low precendence, the generated code will almost always behave as intended, but there are a few lower precendence operators that will break like "and", "or", and postfix "if", "unless", "while" and "until".
Though I think his vocabulary is a little confusing, after re-reading I think I get what he's actually complaining about. It is really awkward for a <%= %> tag to not actually be a self-contained, parse-able expression. On the other hand, you'd really rather have things like form_for return the text to print, not just print it (thus allowing the use of the <%= %> instead of <% %>). It's just one of those annoying situations where attempting to be pure and elegant results in an ugly kludge in the real world.
I, thankfully, prefer to use Haml anyways :)