rhaiscript / rhai

Rhai - An embedded scripting language for Rust.
https://crates.io/crates/rhai
Apache License 2.0
3.73k stars 175 forks source link

Nesting switch and if #671

Closed ThiNei2l closed 1 year ago

ThiNei2l commented 1 year ago

Hi there,

I am not really sure what is happening here or if this is a bug, but I thought I would report it. Consider this pretty nonsensical program:

use rhai::Engine;

fn main() {
    let engine = Engine::new();

    engine.eval::<()>(r#"
        let s = if 1==1 {"foo"} else {"foo"} + "bar";
        print(s);
    "#).unwrap();

    engine.eval::<()>(r#"
        let s = switch 1 {
            1 => {
                if 1==1 {"foo"} else {"foo"} + "bar"
            },
            _ => "baz",
        };
        print(s);
    "#).unwrap();
}

The output is

foobar
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ErrorFunctionNotFound("+ (&str | ImmutableString | String)", 4:46)', src/main.rs:19:9

The first snippet behaves as expected: we concatenate the result of the if-expression and "bar". In the second snippet the same expression ceases to work. If I understand the message correctly rhai is now looking for a unary +?

Is this a bug or am I missing something?

schungx commented 1 year ago

It is parsing the expression as:

if 1==1 {
    "foo"
} else {
    "foo"
}

+"bar"

Obviously that is syntactically correct (and valid as an if parses first as a statement...)

However, parsing it as an expression will cause similar problems:

fn foo() {
    if hello() {
        do_work();
    } else {
        goof_off();
    }

    -1   // would need (-1) to parse correctly
}
schungx commented 1 year ago

This case is similar to https://github.com/rhaiscript/rhai/issues/628

I'm open to ideas on whether it is better to parse first as a statement or as an expression...

Rust simply won't parse it as an expression unless it is enclosed by parentheses:

// syntax error without ( ... ) 
(if 1==1 {"foo"} else {"foo"}) + "bar"
schungx commented 1 year ago

FYI, just found this in the Rust compiler source code, which highlights similar problems, but this time with a | which can be interpreted both as an OR operator or the first half of a closure.

https://github.com/rust-lang/rust/blob/70fe5f08fffd16dc20506f7d140e47b074f77964/compiler/rustc_ast/src/util/classify.rs#L7-L25

if true {...} else {...} 
|x| 5 

 /// shouldn't be parsed as:
(if true {...} else {...} | x) | 5 
schungx commented 1 year ago

Closing this for now. I'm going to leave it as is, but add new material to the Book to alert users regarding this.