Skip to content

Commit

Permalink
Fix a bug when comparing non lazy against a lazy
Browse files Browse the repository at this point in the history
  • Loading branch information
barsdeveloper committed Nov 19, 2023
1 parent 7c53982 commit b4826f7
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 37 deletions.
4 changes: 0 additions & 4 deletions src/Regexer.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,6 @@ export default class Regexer {
static equals(lhs, rhs, strict = false) {
const a = lhs.getParser()
const b = rhs.getParser()
if (b instanceof a.constructor && !(a instanceof b.constructor)) {
// typeof b extends typeof a, invert to take advantage of polymorphism
return b.equals(Reply.makeContext(rhs), a, strict)
}
return a.equals(Reply.makeContext(lhs), b, strict)
}

Expand Down
30 changes: 20 additions & 10 deletions src/parser/Parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,25 +85,35 @@ export default class Parser {

/**
* @param {Context} context
* @param {Parser<any>} other
* @param {Parser<any>} rhs
* @param {Boolean} strict
*/
equals(context, other, strict) {
let self = /** @type {Parser<any>} */(this)
if (self === other) {
equals(context, rhs, strict) {
let lhs = /** @type {Parser<any>} */(this)
if (lhs === rhs) {
return true
}
if (!strict) {
self = this.actualParser()
other = other.actualParser()
lhs = this.actualParser()
rhs = rhs.actualParser()
}
if (
rhs instanceof lhs.constructor && !(lhs instanceof rhs.constructor)
// @ts-expect-error
|| rhs.resolve && !lhs.resolve
) {
// Take advantage of polymorphism or compare a lazy against a non lazy (not the other way around)
const temp = lhs
lhs = rhs
rhs = temp
}
let memoized = context.visited.get(self, other)
let memoized = context.visited.get(lhs, rhs)
if (memoized !== undefined) {
return memoized
} else if (memoized === undefined) {
context.visited.set(self, other, true)
memoized = self.doEquals(context, other, strict)
context.visited.set(self, other, memoized)
context.visited.set(lhs, rhs, true)
memoized = lhs.doEquals(context, rhs, strict)
context.visited.set(lhs, rhs, memoized)
}
return memoized
}
Expand Down
67 changes: 44 additions & 23 deletions tests/equality.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ test("Test 12", async ({ page }) => {
expect(R.equals(lhs, rhs, true)).toBeTruthy()
expect(R.equals(rhs, lhs, true)).toBeTruthy()

// It's similar to rhs but with some omissions that still make it functionally equal
const rhs2 = R.nonGrp(R.seq(
R.str("Italy"),
R.regexp(/Switzerland/).chain(fp),
Expand All @@ -360,19 +361,21 @@ test("Test 12", async ({ page }) => {
expect(R.equals(rhs2, lhs)).toBeTruthy()
expect(R.equals(lhs, rhs2, true)).toBeFalsy()
expect(R.equals(rhs2, lhs, true)).toBeFalsy()
expect(R.equals(lhs, rhs2, true)).toBeFalsy()
expect(R.equals(rhs2, lhs, true)).toBeFalsy()
})

test("Test 13", async ({ page }) => {
class Grammar {
/** @type {Regexer<Parser<any>>} */
static a = R.seq(R.str("a"), R.str("a"), R.lazy(() => this.a))
static root = this.a
}
expect(R.equals(
Grammar.a,
R.seq(R.str("a"), R.str("a"), R.seq(R.str("a"), R.str("a"), R.seq(R.str("a"), R.str("a"), Grammar.a))),
true
)).toBeTruthy()
const other =
R.seq(R.str("a"), R.str("a"), R.seq(R.str("a"), R.str("a"), R.seq(R.str("a"), R.str("a"), Grammar.a)))
expect(R.equals(Grammar.a, other)).toBeTruthy()
expect(R.equals(other, Grammar.a)).toBeTruthy()
expect(R.equals(Grammar.a, other, true)).toBeTruthy()
expect(R.equals(other, Grammar.a, true)).toBeTruthy()
})

test("Test 14", async ({ page }) => {
Expand All @@ -384,11 +387,11 @@ test("Test 14", async ({ page }) => {
static d = R.seq(Grammar.c, R.str("d"))
static root = this.d
}
expect(R.equals(
Grammar.root,
R.seq(R.alt(R.regexp(/a/).map(f3), Grammar.b, R.seq(Grammar.c, R.str("d"))), R.str("d")),
true
)).toBeTruthy()
const other = R.seq(R.alt(R.regexp(/a/).map(f3), Grammar.b, R.seq(Grammar.c, R.str("d"))), R.str("d"))
expect(R.equals(Grammar.root, other)).toBeTruthy()
expect(R.equals(other, Grammar.root)).toBeTruthy()
expect(R.equals(Grammar.root, other, true)).toBeTruthy()
expect(R.equals(other, Grammar.root, true)).toBeTruthy()
})

test("Test 15", async ({ page }) => {
Expand All @@ -397,10 +400,10 @@ test("Test 15", async ({ page }) => {
static a = R.seq(R.str("a"), R.str("a"), R.lazy(() => this.a))
static b = R.seq(R.str("a"), R.str("a"), R.lazy(() => this.b))
}
expect(R.equals(
Grammar.a,
Grammar.b,
)).toBeTruthy()
expect(R.equals(Grammar.a, Grammar.b)).toBeTruthy()
expect(R.equals(Grammar.b, Grammar.a)).toBeTruthy()
expect(R.equals(Grammar.a, Grammar.b, true)).toBeTruthy()
expect(R.equals(Grammar.b, Grammar.a, true)).toBeTruthy()
})

test("Test 16", async ({ page }) => {
Expand All @@ -409,10 +412,10 @@ test("Test 16", async ({ page }) => {
static a = R.seq(R.str("a"), R.str("a"), R.lazy(() => this.a))
static b = R.seq(R.str("a"), R.lazy(() => this.b))
}
expect(R.equals(
Grammar.a,
Grammar.b,
)).toBeFalsy()
expect(R.equals(Grammar.a, Grammar.b)).toBeFalsy()
expect(R.equals(Grammar.b, Grammar.a)).toBeFalsy()
expect(R.equals(Grammar.a, Grammar.b, true)).toBeFalsy()
expect(R.equals(Grammar.b, Grammar.a, true)).toBeFalsy()
})

test("Test 17", async ({ page }) => {
Expand All @@ -421,8 +424,26 @@ test("Test 17", async ({ page }) => {
static a = R.seq(R.str("a"), R.lazy(() => this.a))
static b = R.seq(R.str("a"), R.seq(R.str("a"), R.seq(R.str("a"), R.lazy(() => this.b))))
}
expect(R.equals(
Grammar.a,
Grammar.b,
)).toBeTruthy()
expect(R.equals(Grammar.a, Grammar.b)).toBeTruthy()
expect(R.equals(Grammar.b, Grammar.a)).toBeTruthy()
expect(R.equals(Grammar.a, Grammar.b, true)).toBeTruthy()
expect(R.equals(Grammar.b, Grammar.a, true)).toBeTruthy()
})

test("Test 18", async ({ page }) => {
class Grammar {
/** @type {Regexer<Parser<any>>} */
static a1 = R.seq(R.grp(R.str("a").many()), R.lazy(() => this.a1), R.str("x"))
/** @type {Regexer<Parser<any>>} */
static a2 = R.seq(R.grp(R.str("a").many()), R.lazy(() => this.a2), R.str("y"))
static b = R.seq(R.grp(R.str("a").many()), R.lazy(() => R.seq(R.grp(R.str("a").many()), R.lazy(() => this.b), R.str("y"))), R.str("y"))
}
expect(R.equals(Grammar.a2, Grammar.b)).toBeTruthy()
expect(R.equals(Grammar.b, Grammar.a2)).toBeTruthy()
expect(R.equals(Grammar.a2, Grammar.b, true)).toBeTruthy()
expect(R.equals(Grammar.b, Grammar.a2, true)).toBeTruthy()
expect(R.equals(Grammar.a1, Grammar.b)).toBeFalsy()
expect(R.equals(Grammar.b, Grammar.a1)).toBeFalsy()
expect(R.equals(Grammar.a1, Grammar.b, true)).toBeFalsy()
expect(R.equals(Grammar.b, Grammar.a1, true)).toBeFalsy()
})

0 comments on commit b4826f7

Please sign in to comment.