Skip to content

Commit 1da6c61

Browse files
committed
fix of operator clashing
1 parent b994911 commit 1da6c61

File tree

14 files changed

+2266
-52
lines changed

14 files changed

+2266
-52
lines changed

src/main/java/com/igormaznitsa/prologparser/tokenizer/OpAssoc.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ public static Optional<OpAssoc> findForName(final String str) {
7171
return Stream.of(values()).filter(x -> x.text.equals(str)).findFirst();
7272
}
7373

74+
public boolean isPostfix() {
75+
return this == XF || this == YF;
76+
}
77+
78+
public boolean isPrefix() {
79+
return this == FX || this == FY;
80+
}
81+
7482
public int getArity() {
7583
return this.arity;
7684
}

src/main/java/com/igormaznitsa/prologparser/tokenizer/PrologParser.java

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -550,34 +550,38 @@ private PrologTerm readBlock(final Koi7CharOpMap endOperators) {
550550
if (currentTreeItem.getType() == TermType.OPERATOR) {
551551
// it's not first operator
552552
if (currentTreeItem.getPrecedence() <= readAtomPrecedence) {
553-
// new has lower or equal precedence
554-
// make it as ascendant one
555-
final TreeItem foundItem = currentTreeItem.findFirstNodeWithSuchOrLowerPrecedence(readAtomPrecedence);
556-
if (foundItem.getPrecedence() < readAtomPrecedence) {
557-
// make as parent
558-
currentTreeItem = foundItem.makeAsOwnerWithLeftBranch(readAtomTreeItem);
559-
} else if (foundItem.getPrecedence() > readAtomPrecedence) {
560-
// make new as right sub-branch
561-
currentTreeItem = foundItem.makeAsRightBranch(readAtomTreeItem);
553+
if (readAtom.getTermType() == TermType.OPERATOR && ((Op) readAtom).getOpAssoc().isPrefix()) {
554+
// it is a prefix operator so that it can be there
555+
currentTreeItem = currentTreeItem.makeAsRightBranch(readAtomTreeItem);
562556
} else {
563-
// equal precedence
564-
switch (foundItem.getOperatorType()) {
565-
case XF:
566-
case YF:
567-
case FX:
568-
case XFX:
569-
case YFX:
570-
currentTreeItem = foundItem.makeAsOwnerWithLeftBranch(readAtomTreeItem);
571-
break;
572-
case FY:
573-
case XFY:
574-
currentTreeItem = foundItem.makeAsRightBranch(readAtomTreeItem);
575-
break;
576-
default:
577-
throw new CriticalUnexpectedError();
557+
// new has lower or equal precedence
558+
// make it as ascendant one
559+
final TreeItem foundItem = currentTreeItem.findFirstNodeWithSuchOrLowerPrecedence(readAtomPrecedence);
560+
if (foundItem.getPrecedence() < readAtomPrecedence) {
561+
// make as parent
562+
currentTreeItem = foundItem.makeAsOwnerWithLeftBranch(readAtomTreeItem);
563+
} else if (foundItem.getPrecedence() > readAtomPrecedence) {
564+
// make new as right sub-branch
565+
currentTreeItem = foundItem.makeAsRightBranch(readAtomTreeItem);
566+
} else {
567+
// equal precedence
568+
switch (foundItem.getOpAssoc()) {
569+
case XF:
570+
case YF:
571+
case FX:
572+
case XFX:
573+
case YFX:
574+
currentTreeItem = foundItem.makeAsOwnerWithLeftBranch(readAtomTreeItem);
575+
break;
576+
case FY:
577+
case XFY:
578+
currentTreeItem = foundItem.makeAsRightBranch(readAtomTreeItem);
579+
break;
580+
default:
581+
throw new CriticalUnexpectedError();
582+
}
578583
}
579584
}
580-
581585
} else if (currentTreeItem.getPrecedence() > readAtomPrecedence) {
582586
// new has greater precedence
583587
if (readAtomTreeItem.getType() != TermType.OPERATOR && currentTreeItem.getRightBranch() != null) {

src/main/java/com/igormaznitsa/prologparser/tokenizer/TreeItem.java

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -170,11 +170,33 @@ private void replaceForOwner(final TreeItem newItem) {
170170
}
171171
}
172172

173-
OpAssoc getOperatorType() {
173+
OpAssoc getOpAssoc() {
174174
return ((Op) ((TermWrapper) savedTerm).getWrappedTerm()).getOpAssoc();
175175
}
176176

177-
private boolean isPrecedenceOrderOk() {
177+
private boolean isOperandsOk() {
178+
if (this.savedTerm.getTermType() == TermType.OPERATOR) {
179+
final Op wrappedOperator = (Op) ((TermWrapper) this.savedTerm).getWrappedTerm();
180+
switch (wrappedOperator.getOpAssoc()) {
181+
case FX:
182+
case FY:
183+
return this.leftBranch == null && this.rightBranch != null;
184+
case YF:
185+
case XF:
186+
return this.leftBranch != null && this.rightBranch == null;
187+
case XFX:
188+
case XFY:
189+
case YFX:
190+
return this.leftBranch != null && this.rightBranch != null;
191+
default:
192+
throw new CriticalUnexpectedError();
193+
}
194+
} else {
195+
return this.leftBranch == null && this.rightBranch == null;
196+
}
197+
}
198+
199+
private boolean isPrecedenceOk() {
178200
if (this.savedTerm.getTermType() == TermType.OPERATOR) {
179201
final int thisPrecedence = this.getPrecedence();
180202
final Op wrappedOperator = (Op) ((TermWrapper) this.savedTerm).getWrappedTerm();
@@ -205,6 +227,10 @@ private boolean isBlock() {
205227
return this.savedTerm.getTermType() == TermType.STRUCT && ((PrologStruct) this.savedTerm).getFunctor() == Op.VIRTUAL_OPERATOR_BLOCK;
206228
}
207229

230+
private boolean isOperator() {
231+
return this.savedTerm.getTermType() == TermType.OPERATOR;
232+
}
233+
208234
@Override
209235
public String toString() {
210236
return savedTerm.toString();
@@ -253,29 +279,23 @@ PrologTerm convertToTermAndRelease() {
253279
return this.leftBranch.convertToTermAndRelease();
254280
}
255281

256-
if (!isPrecedenceOrderOk()) {
257-
if (this.rightBranch != null ^ this.leftBranch != null) {
258-
final Op operator = (Op) wrapper.getWrappedTerm();
259-
if (operator.getOpAssoc() == OpAssoc.XF || operator.getOpAssoc() == OpAssoc.YF || operator.getOpAssoc() == OpAssoc.FX || operator.getOpAssoc() == OpAssoc.FY) {
260-
final PrologTerm that = this.rightBranch != null
261-
? this.rightBranch.convertToTermAndRelease()
262-
: this.leftBranch.convertToTermAndRelease();
263-
if (that.getTermType() != TermType.STRUCT && that.getTermType() != TermType.VAR) {
264-
return new PrologStruct(that, new PrologTerm[] {
265-
new PrologAtom(operator.getTermText())
266-
}, operator.getLine(), operator.getPos());
267-
}
268-
}
269-
}
270-
271-
throw new PrologParserException("Operator precedence clash or missing operator: [" + wrapper.getTermText() + ']', wrapper.getLine(), wrapper.getPos());
282+
if (!isOperandsOk()) {
283+
throw new PrologParserException("No operands: [" + wrapper.getTermText() + ']', wrapper.getLine(), wrapper.getPos());
272284
}
273285

274-
final PrologTerm left = this.leftBranch == null ? null : this.leftBranch.convertToTermAndRelease();
275-
final PrologTerm right = this.rightBranch == null ? null : this.rightBranch.convertToTermAndRelease();
286+
final PrologTerm left;
287+
final PrologTerm right;
276288

277-
if (left == null && right == null) {
278-
throw new PrologParserException("Op without operands", wrapper.getLine(), wrapper.getPos());
289+
if (!isPrecedenceOk()) {
290+
if (this.rightBranch != null && this.rightBranch.isOperator() && this.rightBranch.getOpAssoc().isPrefix()) {
291+
left = this.leftBranch == null ? null : this.leftBranch.convertToTermAndRelease();
292+
right = new PrologStruct(Op.VIRTUAL_OPERATOR_BLOCK, new PrologTerm[] {this.rightBranch.convertToTermAndRelease()});
293+
} else {
294+
throw new PrologParserException("Operator precedence clash or missing operator: [" + wrapper.getTermText() + ']', wrapper.getLine(), wrapper.getPos());
295+
}
296+
} else {
297+
left = this.leftBranch == null ? null : this.leftBranch.convertToTermAndRelease();
298+
right = this.rightBranch == null ? null : this.rightBranch.convertToTermAndRelease();
279299
}
280300

281301
// this code replaces '-'(number) to '-number'

src/test/java/com/igormaznitsa/prologparser/IntegrationTest.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@
3737
import static com.igormaznitsa.prologparser.terms.OpContainer.make;
3838
import static com.igormaznitsa.prologparser.terms.PrologTerm.QuotingType.*;
3939
import static com.igormaznitsa.prologparser.terms.TermType.ATOM;
40-
import static com.igormaznitsa.prologparser.tokenizer.OpAssoc.XFX;
41-
import static com.igormaznitsa.prologparser.tokenizer.OpAssoc.XFY;
40+
import static com.igormaznitsa.prologparser.tokenizer.OpAssoc.*;
4241
import static java.util.stream.Collectors.joining;
4342
import static org.junit.jupiter.api.Assertions.*;
4443
import static org.mockito.Mockito.*;
@@ -95,7 +94,7 @@ public void testSwiCpl() {
9594
assertEquals("V in inf .. -2 \\/ 0 \\/ 2 .. 16 \\/ 18 .. sup", parseCpl("V in inf.. -2 \\/ 0 \\/ 2..16 \\/ 18..sup.").next().toString());
9695
assertEquals("X #> Y #==> X - N #>= Y #/\\ X - M - 1 #< Y , X #=< Y #==> Y - N #>= X #/\\ Y - M - 1 #< X", parseCpl("X#>Y#==>X-N#>=Y#/\\X-M-1#<Y,X #=< Y #==> Y - N #>= X #/\\ Y - M - 1 #< X.").next().toString());
9796
assertEquals("clpfd : run_propagator(even(X), MState) :- (integer(X) -> clpfd : kill(MState) , 0 is X mod 2 ; true)", parseCpl("clpfd:run_propagator(even(X),MState):-(integer(X)->clpfd:kill(MState),0is X mod 2;true).").next().toString());
98-
assertThrows(PrologParserException.class, () -> parseCpl("#\\X..#\\Y.").next());
97+
assertEquals("#\\ X .. (#\\ Y)", parseCpl("#\\X..#\\Y.").next().toString());
9998
}
10099

101100
@Test
@@ -572,6 +571,16 @@ private void assertReadTerms(final int expected, final String resource, final Op
572571

573572
@Test
574573
public void testParseSourceFiles() {
574+
assertReadTerms(36, "cover.p", Op.make(400, FY, "private"), Op.make(400, XFX, "is_atom"));
575+
assertReadTerms(70, "sincos.p");
576+
assertReadTerms(49, "moddiv.p");
577+
assertReadTerms(48, "eqless.p");
578+
assertReadTerms(34, "bitwise.p");
579+
assertReadTerms(64, "basic.p");
580+
assertReadTerms(74, "signal.p");
581+
assertReadTerms(21, "pred.p");
582+
assertReadTerms(85, "logical.p");
583+
assertReadTerms(67, "kernel.p");
575584
assertReadTerms(20, "ConcurrentList.pl");
576585
assertReadTerms(5, "points_test.pl", Op.make(800, XFX, "<-"), Op.make(850, XFY, "returns"));
577586
assertReadTerms(6, "points_test2.pl", Op.make(800, XFX, "<-"), Op.make(850, XFY, "returns"));
@@ -967,9 +976,11 @@ public void testOperatorAsFunctor() throws Exception {
967976

968977
@Test
969978
public void testPairOfOperatorsWithIncompatiblePrecedence() throws Exception {
970-
assertEquals("-(discontiguous)", parseEd("-discontiguous.").next().toString());
979+
assertEquals("- (discontiguous)", parseEd("-discontiguous.").next().toString());
980+
assertEquals("2 ** (-1)", parseEd("2**-1.").next().toString());
981+
assertEquals("0.2 is 5 ** (-1)", parseEd("0.2 is 5** -1.").next().toString());
971982
assertEquals("a : b :> c :> d", parseEd("a:b:>c:>d.", DefaultParserContext.of(ParserContext.FLAG_NONE, Op.make(500, XFY, ":>"))).next().toString());
972-
assertThrows(PrologParserException.class, () -> parseEd("X=(a,b,c; dynamic d).").next());
983+
assertEquals("X = (a , b , c ; (dynamic d))", parseEd("X=(a,b,c; dynamic d).").next().toString());
973984
assertThrows(PrologParserException.class, () -> parseEd("a :- b :- c.").next());
974985
assertThrows(PrologParserException.class, () -> parseEd("?-mother(pam,bob);").next());
975986
}

0 commit comments

Comments
 (0)