seanyoung / lrpeg

Left Recursive PEG for rust
MIT License
67 stars 3 forks source link

How to specify operator right associativity #6

Closed oovm closed 3 years ago

oovm commented 3 years ago

For example, exponentiation(^) is right associative

seanyoung commented 3 years ago

I've just pushed an example of this:

https://github.com/seanyoung/lrpeg/commit/ae913624de386f0fb475b7d461ea196ac5190f51

Hope this helps

oovm commented 3 years ago

Oh, I saw it, left recursive combined left, right recursive combined right

oovm commented 3 years ago

I have some ideas about branch_tag and associativity.

expr <- lhs=expr ("+"/"-") rhs=expr #ADD
    / lhs=expr ("*"/"/") rhs=expr   #MUL
    / lhs=expr "^" rhs=expr         #POW
    / "(" expr ")"                  #P
    / num                           #N

We can solve it as:

expr <- expr0;               #ADD
expr0 <- expr0 ("+"/"-") expr1
    / expr1;                 #MUL
expr1 <- expr1 ("*"/"/") expr2
    / expr2;                 #POW
expr2 <- expr3 "^" expr2
    / expr3;                 #?
expr3 <- "(" expr ")"        #P
    / num;                   #N
oovm commented 3 years ago

Looks like it can replace prec climber

ANTLR can also express precedence and associativity in a more concise way

https://github.com/antlr/grammars-v4/blob/7c2f67b5d042e091ee82344ddfc778aa0dad0f35/javascript/javascript/JavaScriptParser.g4#L333-L349

oovm commented 3 years ago

I still think this way of writing in ANTLR is more appropriate

I wrote a transformation to put

expr <- "(" expr ")"
    / expr ("+"/"-") expr
    / expr ("*"/"/") expr
    / expr "^" expr
    / prefix ~ expr
    / expr ~ postfix
    / num
    ;

num <- re#0|[0-9][1-9]+#;
prefix <- "++"/"--"/"+"/"-";
postfix <- "++";

into

expr <- expr0 EOI
    ;
expr0 <- "(" expr0 ")"
    / expr1
    ;
expr1 <- expr1 ("+"/"-") expr2
    / expr2
    ;
expr2 <- expr2 ("*"/"/") expr3
    / expr3
    ;
expr3 <- expr4 "^" expr3
    / expr4
    ;
expr4 <- expr4 postfix
    / expr5
    ;
expr5 <- prefix expr5
    / expr6
    ;
expr6 <- var;

var <- re#[a-z]#;
prefix <- "++"/"--"/"+"/"-";
postfix <- "++";

In my experiment, it can correctly handle the precedence and associativity.

I now need a way to simplify the intermediate nodes, which requires additional node information.

Now there are two solutions, one is to add the tag Rule::PROMOTE, and the other is tag_node("PROMOTE")

    Rule::PROMOTE => {
        let len = node.children.len();
        match len {
            0 => {  },
            1 => {
                let mut children = node.children;
                flatten_rec(children.remove(0), buffer)
            },
            _ => buffer.push(flatten(node))
        }
    }

I haven't decided how to do it yet, I'm going to wait until branch_tag is available and deal with it together

seanyoung commented 3 years ago

I see your point about antlr being more concise. Antlr is not peg though, and lrpeg is.

Having said that I don't see a reason for being dogmatic about pegs.