uniform-team / Uniform-Validation-Language

A logic language to simplify and improve online form validation.
Apache License 2.0
1 stars 0 forks source link

If Statement #44

Open dgp1130 opened 7 years ago

dgp1130 commented 7 years ago

I think we overlooked the necessity of an if-statement. It's generally more of an imperative tool, but I think it can still be applicable here. This would work similarly to the SQL case statement.

<ifBlock> -> <if> <elseIf> <else>
<if> -> if <expression> then <expression>
<elseIf> -> else if <expression> then <expression> <elseIf> | Ø
<else> -> else <expression> end

The else clause would be required, because the if statement must return a value, in any case which occurs. An example of this would be counting the number of members of a family.

@familySize: 1 // Count self
    + @numChildren // Count children
    + if married then 1 else 0 end // Count spouse if present
;
dgp1130 commented 7 years ago

Done. I've updated the Language Grammar wiki page to reflect the new changes.

I'm not completely sure of the best way to indent if-statements #OCD. The best I could come up with was:

valid: if a then b
    else if c then d
    else if e then f
    else g
end;

Though that can become hard to read if a single if-then becomes too long. This makes it a little difficult to distinguish a condition from its result however. Only other thing I could really think of:

valid: if a then
    reallyLongExpressionOfLotsOfStuff
else if c then
    reallyLongExpressionOfLotsOfStuff
else if e then
    reallyLongExpressionOfLotsOfStuff
else
    reallyLongExpressionOfLotsOfStuff
end;

That works better for long lines, but looks pretty bad for short ones. Of course all if-bodies will only be one expression, so the extra line and indent would be rarely needed. Both of these also use a semicolon on its own line, which I really like for terminating blocks like this without braces but that's certainly not a common notation. I guess it's ultimately up to the programmer, but we'd probably want to have an official style of some kind to encourage best practices.

Regarding a more important topic: there is one possible issue regarding typing. There is the case where the developer writes an if-statement which can return different types. Such as:

if @useBoolean then true else if @useInteger then 1 else "useString" end

This could result in some strange behavior, since different user inputs would dynamically change the type of the if-statement's result. Such a case would generally be either a conceptual error on the part of the programmer, or it would be someone using the dynamic typing with a overloaded operator (technically correct, but very stupid).

if @useInteger then 1 else "hello" end + if @useInteger then 2 else "world" end
// Returns either 3 or "helloworld"

I think the best thing here would be to detect if-statements that return different types and throw a TypeError at compile-time (this is also what the SQL case statement does). Unfortunately, we currently have dynamic typing, so we don't know the type until run-time, making this impossible to detect. On the plus side, #48 will allow us to change that behavior since typing will be explicitly defined in the Uniform code at compile-time. We will need to move our type-checking code from run-time to compile-time and define each operation as returning a certain type.

dgp1130 commented 7 years ago

Just realized, there is one case that could cause issues.

if a then
    b
else
    if c then
        d
    else
        e
    end
end

This makes sense, and should work. There are plenty of reasons to write code like this if the logic works like that conceptually. However this won't parse because we ignore whitespace and there is actually an extra end token. It would really be parsed like:

if a then
    b
else if c then
    d
else
    e
end // Only one end tag

Logically, the two statements are equivalent, but grammatically the first one is invalid (would throw ParsingError, unexpected token "end"). I'm not sure what to do about this. We definitely shouldn't factor whitespace into parsing and I don't think it's a good idea to allow either number of ends.

I checked against SQL to see how it addresses this, but it dodges the issue altogether.

case a
    when b then c
    when d then e
    else f
end

It uses when as an else if so there is no ambiguity between an else followed by an if and an else if. I guess that explains why they didn't use the far more intuitive if-then syntax we went with. I definitely don't like their syntax, but I can't think of a better way of phrasing it. We could make elseif one word, that would allow the parser to distinguish between the two cases. Unfortunately it would be very easy to add a space when it shouldn't be there and vice versa, which would throw errors at the end line and be difficult to understand for users. We could use otherwise in place of else I suppose, though that would be a little weird and is kind of a long keyword. Not sure what the best course of action is here.

https://docs.oracle.com/cd/B19306_01/server.102/b14200/expressions004.htm

dgp1130 commented 7 years ago

After discussing the issue with Sawyer, we decided to change else if to elif. This creates a clear distinction between an elif and an else followed by an if. It is still easy for a user to accidentally use else if instead of elif, so I added a warning whenever it detects an else immediately followed by an if with no newline between them. The user will still get the "ParsingError: Unexpected token 'end'" error, but it will be accompanied by a warning explicitly calling out their mistake. I definitely think this is the best solution.