Implicit Multiplication xyz -> ((x*y)*z)

Thanks for your reply.

Yes. With this grammar, whatever rule I came up with to allow Pow on the rhs would fail some of the other tests, so I left the grammar as it is on purpose…

With precedence operators, it’s usually better to put every type of expression in one big expression rule rather than having subcategories like secondaryExpression , primaryExpression , juxtaposeExpression , etc. It makes the grammar easier to read, faster to parse, and avoids issues like this.

That’s how I structured my grammar in the full version (with one big expression). This was my attempt at avoiding some expressions to be valid, like -a-b being parsed as Mul( Negate(a), Negate(b) ) through the implicit multiplication. I separated like this to try to be more strict in some rules.

Below is another grammar where I bunched up things in a big expression and made the Juxtapose more general. In this one, stuff like abc^{2} works as expected, but -a-b-c fails as it is parsed as the implicit multiplication of (-a) * (-b) * (-c). This was my original problem. I got the abc^{2} problem only after trying to fix the -a-b-c one.

@top Program { expression* }

@precedence { 
  highest @left
  pow @right
  times @left 
  unary @left
  juxt @left
  juxtfunc @left
  implicitmul @left
  div @left
  plus @left
  minus @left
  lowest @left
}


expression {
  Variable | Constant | unaryExpression | binaryExpression | juxtaposeExpression
}

Pow { expression !pow "^{" expression "}"}
Add { expression !plus "+" expression}
Sub { expression !minus "-" expression}
Mul { expression !times ("*"|'\\cdot') expression}
Div { expression !div "/" expression | !div '\\frac{'expression'}{'expression'}'}

binaryExpression { 
   Add | Sub | Mul | Div | Pow
}

Negate { !unary "-" expression}
unaryExpression {
  Negate
}

juxtaposeExpression {
  Mul { (Variable | Constant) !implicitmul expression } | // Specific one I want to catch here
  FunctionCall { FunctionName !juxtfunc expression } | // Specific one I want to catch here
  // I'll have a bunch of other specific ones...
  Juxtapose { expression !juxt expression } // General juxtaposition that I deal with it later
}

@skip { '$' | space | '\\ ' }

@tokens {
  space { @whitespace }
  Variable { ('_')*$[A-Za-z]("_{"($[A-Za-z]|@digit+)*"}")*}
  FunctionName { ("\\sin" | "\\cos" | "\\tan")}
  Constant { 
    (
      (std.digit+ ("." std.digit*)?)
    ) |
    std.digit+ $[uU]? |
    "0x" (std.digit | $[a-fA-F])+ $[uU]?
  }
}