richardhundt / shine

A Shiny Lua Dialect
Other
231 stars 18 forks source link

fn{...} as a possible shorthand for fn({...}) #67

Closed leonidborisenko closed 9 years ago

leonidborisenko commented 9 years ago

Resulting AST in macro should be constructed with calls to ctx.op({...}):

macro dummy(ctx, expr)
  return ctx.op({"!assign", ctx.op({"!index", "self", ctx.op"members"}), "!nil"})
end

because this would not work as expected:

macro dummy(ctx, expr)
  -- I believe it's the same as ctx.op({ "!assign", ctx.op({...},"!nil") })
  return ctx.op{"!assign", ctx.op{"!index", "self", ctx.op"members"}, "!nil"}
end

Third argument to first ctx.op call is parsed as second argument to second ctx.op call.

Would significance of space between called symbol and argument list delimiters (fn and {}) in call expression be confusing?

Now difference between Lua and Shine in handling fn{} call is confusing.

richardhundt commented 9 years ago

Shine allows for optional parenthesis following a function call. The following are equivalent:

print "Hello", "World!"
print("Hello", "World!")

However, because argument lists are parsed with a high priority, these two are equivalent:

print string::format "Hello %s", "World!"
print(string::format("Hello %s", "World!"))

It would be ambiguous if the "World!" argument would be a parameter to string::format in some cases, but to print in other cases. Do we treat table literals differently? String literals? Should only identifiers be parsed at higher precedence?

So yep, it is different to how Lua handles it. In Lua the parenthesis are only optional if you pass a table or string literal. In Shine, they're always optional, but you need to handle the fact that bare argument lists are parsed greedily.

leonidborisenko commented 9 years ago

I'd say, yes, tables and string literals should be treated differently, because they are starting with paired delimiters.

There is a difference between parsing of print(1, 2) (no space between print and () and parsing of print (1, 2) (with space between print and () -- both in Shine in Lua. But { and " delimiters aren't working like ( in Shine in call expressions.

In this sense `(space or space-like character) could also being named "fake paired delimiter", soprint{1, 2}({right afterprint) would take{as a delimiter andprint 1, 2(space right afterprint`) would take space and newline as "delimiters".

Or maybe rule could be rephrased from "argument list is greedy" to "argument list is greedy when no paired delimiter is met right after callied symbol"?

I can live with current behavior, but I was really surprised with different ctx.op{} (no space between ctx.op and {) behavior in second demonstrated example. Code worked, but "!nil" was missed from macro result and I thought it was [logical] error in Shine translator, not in my code (because in Lua it'd worked as I expected). Link between "greedy argument lists" and Shine behavior in this case wasn't clear for me and even right before posting I wasn't 100% sure I got the culprit right (thanks for clearing BTW).

So it'd be useful either to change Shine behavior for consistency with Lua (I still vote for this), or document gotcha with { and " in call expression documentation with examples in Lua and Shine.

richardhundt commented 9 years ago

I could make it behave like Lua, however what about arrays?

If foo.bar{42} means foo.bar({42}), what does foo.bar[42] mean? So tables and strings are special, but not arrays and pattern literals? I find that more surprising in a language.

I'll have to think about the significant space idea, although I don't see how it solves our problem per se.

leonidborisenko commented 9 years ago

Ah, really, presence of [] (arrays) and // (patterns) ruins my reasoning (obviously, Lua doesn't have this problem, because array and pattern literals are Shine-specific). It's frustrating, but now I can't find sane counter-argument in defense of unifying fn{} behavior with Lua. So, I think, case closed (but still difference with Lua should be documented somehow)..

Significant space (if you meant "fake paired delimiter" paragraph) wasn't an idea. It's just an attemt in finding a metaphor for describing current behavior in documentation purposes (if/when fn{} would be like fn({})). Absence of "real delimiters" (i.e. putting space(s) between called symbol and arguments) is like having space as delimiter. And so behavior of fn{1, 2} and fn 1, 2 could be described uniformly: { is a delimiter and `(space) is a delimiter (note that space after,` doesn't fall under this metaphor, because it's not the space after called symbol).

Ah, or maybe you've just cited "space significance" from my first post? In that context "significance" meant difference between fn1{arg, fn {}, arg} and fn1{arg, fn{}, arg} (now there is no difference and if there would be difference, space beween fn and { could be described as "significant"). So just a metaphor also.

richardhundt commented 9 years ago

Just a note that in Lua a space between print and { is not significant, so print{} and print { } has exactly the same meaning, so if if Shine were to treat { as a delimiter if it followed immediately after print, but otherwise not, then it would still deviate from Lua.

All in all, I do agree with you that this is a gotcha. It's hard to find a balance between expressiveness and consistency in a language. There are a couple of other warts in the language which also either need a solution or need to be documented.