Crazy: postfix dereferencing operator


#1

I’ve always wanted one in a language I’d have some reason to use, ever since learning Pascal as a kid. I think they’re less ambiguous, and thus easier to work with. With C you can use [0], which is kind of a poor-man’s postfix dereferencing operator… But in Rust, it seems you have to resort to parentheses to deal with the ambiguity? Consider this program, demonstrating the ambiguity:

use std::ops::Deref;

struct Foo(Bar);
struct Bar(Baz);
struct Baz;

impl Deref for Foo {
    type Target = Bar;
    fn deref<'a>(&'a self) -> &'a Bar {
        println!("Deref Foo");
        &self.0
    }
}

impl Deref for Bar {
    type Target = Baz;
    fn deref<'a>(&'a self) -> &'a Baz {
        println!("deref Bar");
        &self.0
    }
}

fn main() {
    let x = Foo(Bar(Baz));
    let _ = *x;
    let _ = *x.0;
    let _ = (*x).0;
}

(playground)

On the other hand, the ambiguity would be removed with a postfix dereferencing operator:

fn main() {
  let x = Foo(Bar(Baz));
  let _ = x*;
  let _ = x.0*;
  let _ = x*.0;
}

(aside: I know this would allow a syntactic ambiguity on something like x.0 * 3… it’s the same ambiguity that currently exists on 3 * x.0, just with the terms reversed.)

I know this won’t happen at this point, but it would have been nice…


#2

I may be mistaken, but I think the ambiguity issue with the prefix operator is less severe than with postfix?

Postfix:

foo*(1+2)

Now is this multiplying foo by (1+2), or dereferencing foo and then function-calling the result with 1+2?

Prefix:

(1+2)*foo

does not have the possibility of being a function call.

I think a postfix dereferencing operator (if we continue to use * for it, which we also use for infix multiplication) would have to require whitespace-sensitive syntax, but I think those are both good ideas and plan to use them if I ever design a language myself.


#3

Actually, neither of those are ambiguous. x.0 * 3 still only has one valid parse. It’s just that x.0 * 3 requires more lookahead than 3 * x.0. The current 3 * x.0 case only requires one token of lookahead for the operator itself. While your x.0 * 3 requires infinite lookahead, since it has to keep looking ahead until it finds a token that starts a primary expression or ends statement. To demonstrate, x.0***; is 3 derefs, x.0***3 is 2 derefs and a multiplication.


#4

It doesn’t look like that requires infinite lookahead? x.0***3 looks like it requires only one token of look-ahead. At the first *, look ahead one, see another *, then the first token must be a unary deref while the second token is undetermined… By the time the parser reaches the 3, the first two *'s have already been resolved to unary deref expressions, right?