Skip to content

Commit

Permalink
Recursive equals fix
Browse files Browse the repository at this point in the history
  • Loading branch information
barsdeveloper committed Nov 18, 2023
1 parent b6c3168 commit 25b3838
Show file tree
Hide file tree
Showing 28 changed files with 459 additions and 260 deletions.
372 changes: 242 additions & 130 deletions dist/regexer.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/regexer.min.js

Large diffs are not rendered by default.

10 changes: 4 additions & 6 deletions src/Regexer.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ export default class Regexer {
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(a, strict)
return b.equals(Reply.makeContext(rhs), a, strict)
}
return a.equals(b, strict)
return a.equals(Reply.makeContext(lhs), b, strict)
}

getParser() {
Expand All @@ -114,7 +114,7 @@ export default class Regexer {
* @returns {Result<ParserValue<T>>}
*/
run(input) {
const result = this.#parser.parse(Reply.makeContext(input), 0)
const result = this.#parser.parse(Reply.makeContext(this, input), 0)
return result.status && result.position === input.length ? result : Reply.makeFailure(result.position)
}

Expand Down Expand Up @@ -187,12 +187,11 @@ export default class Regexer {
}

/**
* @template {Regexer<Parser<any>>} P
* @template {Regexer<any>} P
* @param {() => P} parser
* @returns {Regexer<LazyParser<UnwrapParser<P>>>}
*/
static lazy(parser) {
// @ts-expect-error
return new Regexer(new LazyParser(parser))
}

Expand Down Expand Up @@ -231,7 +230,6 @@ export default class Regexer {
sepBy(separator, allowTrailing = false) {
const results = Regexer.seq(
this,
// @ts-expect-error
Regexer.seq(separator, this).map(Regexer.#secondElementGetter).many()
)
.map(Regexer.#arrayFlatter)
Expand Down
9 changes: 6 additions & 3 deletions src/Reply.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
* }} Result
*/

import PairMap from "./utility/PairMap.js"

export default class Reply {

/**
Expand Down Expand Up @@ -34,11 +36,12 @@ export default class Reply {
})
}

/** @param {String} input */
static makeContext(input) {
/** @param {Regexer<Parser<any>>} regexer */
static makeContext(regexer, input = "") {
return /** @type {Context} */({
regexer: regexer,
input: input,
visited: new Set(),
visited: new PairMap()
})
}
}
2 changes: 2 additions & 0 deletions src/grammars/RegExpGrammar.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,10 @@ export const R = class extends Regexer {
* @template {Regexer<Parser<any>>} T
* @param {T} parser
* @param {String | Symbol} id
* @returns {Regexer<CapturingGroupParser<UnwrapParser<T>>>}
*/
static grp(parser, id = "") {
// @ts-expect-error
return new Regexer(new CapturingGroupParser(parser.getParser(), id))
}

Expand Down
11 changes: 3 additions & 8 deletions src/parser/AlternativeParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,21 +46,16 @@ export default class AlternativeParser extends Parser {
}

/**
* @param {Context} context
* @param {Parser<any>} other
* @param {Boolean} strict
*/
equals(other, strict) {
if (!strict) {
other = other.actualParser()
}
if (this === other) {
return true
}
doEquals(context, other, strict) {
if (!(other instanceof AlternativeParser) || this.#parsers.length != other.#parsers.length) {
return false
}
for (let i = 0; i < this.#parsers.length; ++i) {
if (!this.#parsers[i].equals(other.#parsers[i], strict)) {
if (!this.#parsers[i].equals(context, other.#parsers[i], strict)) {
return false
}
}
Expand Down
6 changes: 2 additions & 4 deletions src/parser/AnchorParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,11 @@ export default class AnchorParser extends Parser {
}

/**
* @param {Context} context
* @param {Parser<any>} other
* @param {Boolean} strict
*/
equals(other, strict) {
if (!strict) {
other = other.actualParser()
}
doEquals(context, other, strict) {
return other instanceof AnchorParser && this.#type === other.#type
}

Expand Down
8 changes: 3 additions & 5 deletions src/parser/AtomicGroupParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,12 @@ export default class AtomicGroupParser extends Parser {
}

/**
* @param {Context} context
* @param {Parser<any>} other
* @param {Boolean} strict
*/
equals(other, strict) {
if (!strict) {
other = other.actualParser()
}
return other instanceof AtomicGroupParser && this.#parser.equals(other.#parser, strict)
doEquals(context, other, strict) {
return other instanceof AtomicGroupParser && this.#parser.equals(context, other.#parser, strict)
}

toString(indent = 0) {
Expand Down
8 changes: 3 additions & 5 deletions src/parser/CapturingGroupParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,14 @@ export default class CapturingGroupParser extends Parser {
}

/**
* @param {Context} context
* @param {Parser<any>} other
* @param {Boolean} strict
*/
equals(other, strict) {
if (!strict) {
other = other.actualParser()
}
doEquals(context, other, strict) {
return other instanceof CapturingGroupParser
&& this.#id == other.#id
&& this.#parser.equals(other.#parser, strict)
&& this.#parser.equals(context, other.#parser, strict)
}

toString(indent = 0) {
Expand Down
8 changes: 3 additions & 5 deletions src/parser/ChainedParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,14 @@ export default class ChainedParser extends Parser {
}

/**
* @param {Context} context
* @param {Parser<any>} other
* @param {Boolean} strict
*/
equals(other, strict) {
if (!strict) {
other = other.actualParser()
}
doEquals(context, other, strict) {
return other instanceof ChainedParser
&& this.#fn === other.#fn
&& this.#parser.equals(other.parser, strict)
&& this.#parser.equals(context, other.parser, strict)
}

toString(indent = 0) {
Expand Down
8 changes: 3 additions & 5 deletions src/parser/ClassParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,16 @@ export default class ClassParser extends AlternativeParser {
}

/**
* @param {Context} context
* @param {Parser<any>} other
* @param {Boolean} strict
*/
equals(other, strict) {
if (!strict) {
other = other.actualParser()
}
doEquals(context, other, strict) {
return (
!strict && !this.#negative && !(other instanceof ClassParser && other.#negative)
|| other instanceof ClassParser && this.negative === other.#negative
)
&& super.equals(other, strict)
&& super.doEquals(context, other, strict)
}

toString(indent = 0) {
Expand Down
6 changes: 2 additions & 4 deletions src/parser/ClassShorthandParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,11 @@ export default class ClassShorthandParser extends RegExpParser {
}

/**
* @param {Context} context
* @param {Parser<any>} other
* @param {Boolean} strict
*/
equals(other, strict) {
if (!strict) {
other = other.actualParser()
}
doEquals(context, other, strict) {
return other instanceof ClassShorthandParser && this.#char == other.#char
}

Expand Down
8 changes: 3 additions & 5 deletions src/parser/EscapedCharParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,13 @@ export default class EscapedCharParser extends StringParser {
}

/**
* @param {Context} context
* @param {Parser<any>} other
* @param {Boolean} strict
*/
equals(other, strict) {
if (!strict) {
other = other.actualParser()
}
doEquals(context, other, strict) {
return (!strict || other instanceof EscapedCharParser && this.#type === other.#type)
&& super.equals(other, strict)
&& super.doEquals(context, other, strict)
}

toString(indent = 0) {
Expand Down
6 changes: 2 additions & 4 deletions src/parser/FailureParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,11 @@ export default class FailureParser extends Parser {
}

/**
* @param {Context} context
* @param {Parser<any>} other
* @param {Boolean} strict
*/
equals(other, strict) {
if (!strict) {
other = other.actualParser()
}
doEquals(context, other, strict) {
return other instanceof FailureParser
}

Expand Down
17 changes: 8 additions & 9 deletions src/parser/LazyParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,19 @@ export default class LazyParser extends Parser {
}

/**
* @param {Context} context
* @param {Parser<any>} other
* @param {Boolean} strict
*/
equals(other, strict) {
if (other instanceof LazyParser && this.#parser === other.#parser) {
return true
}
this.resolve()
if (!strict) {
other = other.actualParser()
} else if (other instanceof LazyParser) {
doEquals(context, other, strict) {
if (other instanceof LazyParser) {
if (this.#parser === other.#parser) {
return true
}
other = other.resolve()
}
return this.#resolvedPraser === other || this.#resolvedPraser.equals(other, strict)
this.resolve()
return this.#resolvedPraser.equals(context, other, strict)
}

toString(indent = 0) {
Expand Down
8 changes: 3 additions & 5 deletions src/parser/LookaroundParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,15 @@ export default class LookaroundParser extends Parser {
}

/**
* @param {Context} context
* @param {Parser<any>} other
* @param {Boolean} strict
*/
equals(other, strict) {
if (!strict) {
other = other.actualParser()
}
doEquals(context, other, strict) {
return this === other
|| other instanceof LookaroundParser
&& this.#type === other.#type
&& this.#parser.equals(other.#parser, strict)
&& this.#parser.equals(context, other.#parser, strict)
}

toString(indent = 0) {
Expand Down
17 changes: 5 additions & 12 deletions src/parser/MapParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,21 +55,14 @@ export default class MapParser extends Parser {
}

/**
* @param {Context} context
* @param {Parser<any>} other
* @param {Boolean} strict
*/
equals(other, strict) {
let self = /** @type {Parser<any>} */(this)
if (!strict) {
other = other.actualParser()
}
return this === other || (
strict
? other instanceof MapParser
&& this.#mapper === other.#mapper
&& this.#parser.equals(other.#parser, strict)
: this.actualParser().equals(other, strict)
)
doEquals(context, other, strict) {
return other instanceof MapParser
&& this.#mapper === other.#mapper
&& this.#parser.equals(context, other.#parser, strict)
}

toString(indent = 0) {
Expand Down
10 changes: 3 additions & 7 deletions src/parser/NonCapturingGroupParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,12 @@ export default class NonCapturingGroupParser extends Parser {
}

/**
* @param {Context} context
* @param {Parser<any>} other
* @param {Boolean} strict
*/
equals(other, strict) {
if (!strict) {
other = other.actualParser()
}
return strict
? other instanceof NonCapturingGroupParser && this.#parser.equals(other.#parser, strict)
: this.actualParser().equals(other, strict)
doEquals(context, other, strict) {
return other instanceof NonCapturingGroupParser && this.#parser.equals(context, other.#parser, strict)
}

toString(indent = 0) {
Expand Down
32 changes: 29 additions & 3 deletions src/parser/Parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default class Parser {
* @param {Parser<any>} parser
*/
dominates(parser) {
return this.equals(parser, false)
//return this.equals(context, parser, false)
}

/** @returns {Parser<T>[]} */
Expand Down Expand Up @@ -84,11 +84,37 @@ export default class Parser {
}

/**
* @param {Context} context
* @param {Parser<any>} other
* @param {Boolean} strict
*/
equals(context, other, strict) {
let self = /** @type {Parser<any>} */(this)
if (self === other) {
return true
}
if (!strict) {
self = this.actualParser()
other = other.actualParser()
}
let memoized = context.visited.get(self, other)
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)
}
return memoized
}

/**
* @param {Context} context
* @param {Parser<any>} other
* @param {Boolean} strict
*/
equals(other, strict) {
return strict ? this.actualParser() === other.actualParser() : this === other
doEquals(context, other, strict) {
return false
}

toString(indent = 0) {
Expand Down
Loading

0 comments on commit 25b3838

Please sign in to comment.