Shift/reduce conflict in simple grammar

I’m looking to write a simple grammar that allows the following examples:


'attribute' EQUALS "property"

'attribute' EQUALS "property" AND 'attribute' EQUALS "property"

('attribute' EQUALS "property" OR 'attribute' EQUALS "property")

('attribute' EQUALS "property") AND ('attribute' EQUALS "property")

'attribute' EQUALS "property" OR ('attribute' EQUALS "property" AND 'attribute' EQUALS "property")

This is the grammar I wrote so far…


@top Expression {
  optionalParenthesis<
    MultiCondition
  >
}

@skip { space }

MultiCondition {
  Condition
  | (MultiCondition (Connector MultiCondition)*)
}

Condition {
  optionalParenthesis<Attribute Operator Value>
}

optionalParenthesis<content> { content | "(" content ")" }

@tokens {
  Attribute { "'" @asciiLetter+ "'" }
  Operator { 'EQUALS' }
  Value { '"' (@asciiLetter | @whitespace)+ '"' }

  Connector {
    'AND' | 'OR'
  }

  space { @whitespace }
}

… however, this runs into a shift/reduce conflict between:


shift/reduce conflict between
  (Connector MultiCondition)+ -> · Connector MultiCondition
and
  MultiCondition -> MultiCondition
With input:
  MultiCondition · Connector …
Shared origin: optionalParenthesis<MultiCondition> -> · MultiCondition
  via MultiCondition -> MultiCondition · (Connector MultiCondition)+
    (Connector MultiCondition)+ -> · Connector MultiCondition

shift/reduce conflict between
  optionalParenthesis<Attribute Operator Value> -> "(" Attribute Operator Value · ")"
and
  optionalParenthesis<Attribute Operator Value> -> Attribute Operator Value
With input:
  "(" Attribute Operator Value · ")" …
Shared origin: Condition -> · optionalParenthesis<Attribute Operator Value>

I’ve tried to set the right precende, but nothing seemed to have removed this error. Any ideas of what I might be missing? Thank you!

Update: I was able to write a grammar that matches all the examples I had…

@precedence {
  single @left,
  multiple @left
}

@top Expression {
  MultiCondition
}

@skip { space }

MultiCondition {
  !single Condition
  | optionalParenthesis<!multiple MultiCondition Connector MultiCondition>
}

Condition {
  optionalParenthesis<Attribute Operator Value>
}

optionalParenthesis<content> { content | "(" content ")" }

@tokens {
  Attribute { "'" char+ "'" }
  Operator { 'EQUALS' }
  Value { '"' char+ '"' }

  Connector { 'AND' | 'OR' }

  char { @asciiLetter | @whitespace }
  space { @whitespace }
}

… there is only one small thing missing. It does not accept multiple redundant parethesis, like so:

(('attribute' EQUALS "property"))

Any idea how to accomplish that? Thank you again