fixpoint algorithm: parameterize the Map to use
This commit is contained in:
parent
ea878975be
commit
47786ae792
3 changed files with 58 additions and 10 deletions
|
|
@ -4,10 +4,12 @@
|
||||||
// FRANCOIS POTTIER,<a href="https://gallium.inria.fr/~fpottier/publis/fpottier-fix.pdf"> Lazy Least Fixed Points in ML</a>
|
// FRANCOIS POTTIER,<a href="https://gallium.inria.fr/~fpottier/publis/fpottier-fix.pdf"> Lazy Least Fixed Points in ML</a>
|
||||||
// <a href="https://gitlab.inria.fr/fpottier/fix">OCAML Code</a>
|
// <a href="https://gitlab.inria.fr/fpottier/fix">OCAML Code</a>
|
||||||
|
|
||||||
export const fixer = (eqsFunction, lattice) => {
|
// eqsFunction: V -> (V -> P) -> P
|
||||||
const mFixed = new Map();
|
// elemCmp: (V -> V -> Ordering)
|
||||||
const mTransient = new Map();
|
export const fixer = (eqsFunction, dictType, lattice) => {
|
||||||
const mParents = new Map();
|
const mFixed = dictType();
|
||||||
|
const mTransient = dictType();
|
||||||
|
const mParents = dictType();
|
||||||
|
|
||||||
const mWorkset = []; // Note: we use a Stack (LIFO), but FIFO queue or any other collection would also work.
|
const mWorkset = []; // Note: we use a Stack (LIFO), but FIFO queue or any other collection would also work.
|
||||||
|
|
||||||
|
|
@ -16,7 +18,6 @@ export const fixer = (eqsFunction, lattice) => {
|
||||||
const ensureTransient = node => {
|
const ensureTransient = node => {
|
||||||
if (mTransient.has(node)) return;
|
if (mTransient.has(node)) return;
|
||||||
mTransient.set(node, lattice.bottom());
|
mTransient.set(node, lattice.bottom());
|
||||||
mParents.set(node, []);
|
|
||||||
mWorkset.push(node);
|
mWorkset.push(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -47,7 +48,7 @@ export const fixer = (eqsFunction, lattice) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!lattice.equality(mTransient.get(current), newProperty)) {
|
if (!lattice.equality(mTransient.get(current), newProperty)) {
|
||||||
mTransient.put(current, newProperty);
|
mTransient.set(current, newProperty);
|
||||||
mWorkset.push(...(mParents.get(current) || []));
|
mWorkset.push(...(mParents.get(current) || []));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,3 +29,46 @@ export class RBTreeWrapper {
|
||||||
return this.tree.keys.map(key => [key, this.tree.get(key)]);
|
return this.tree.keys.map(key => [key, this.tree.get(key)]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Can be used as drop-in replacement for Map
|
||||||
|
// Why create a mutable adapter for a purely-functional data structure?
|
||||||
|
// Because the builtin Map does not allow custom comparison functions and this one does.
|
||||||
|
export class MutableRBTree {
|
||||||
|
constructor(tree, printf = defaultPrintf) {
|
||||||
|
this.tree = tree;
|
||||||
|
this[inspect.custom] = printf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static new(cmp) {
|
||||||
|
return new MutableRBTree(createRBTree(cmp));
|
||||||
|
}
|
||||||
|
|
||||||
|
set(key, value) {
|
||||||
|
this.tree = this.tree.remove(key).insert(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(key) {
|
||||||
|
this.tree = this.tree.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
get(key) {
|
||||||
|
return this.tree.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
has(key) {
|
||||||
|
return this.tree.get(key) !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.tree = createRBTree(this.tree._compare);
|
||||||
|
}
|
||||||
|
|
||||||
|
*[Symbol.iterator]() {
|
||||||
|
const iter = this.tree.begin;
|
||||||
|
while (iter !== undefined && iter.valid) {
|
||||||
|
yield [iter.key, iter.value];
|
||||||
|
iter.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
|
import { compareStrings } from "../lib/compare/primitives.js";
|
||||||
import { fixer } from "../lib/util/fixpoint.js";
|
import { fixer } from "../lib/util/fixpoint.js";
|
||||||
|
import { MutableRBTree } from "../lib/util/rbtree_wrapper.js";
|
||||||
|
|
||||||
const booleanLattice = {
|
const booleanLattice = {
|
||||||
isMaximal: (value) => value,
|
isMaximal: (value) => value,
|
||||||
|
|
@ -7,7 +9,7 @@ const booleanLattice = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const nullable = grammar => nonterminal => request => {
|
const nullable = grammar => nonterminal => request => {
|
||||||
console.log(nonterminal);
|
console.log(nonterminal);
|
||||||
const f = nt => {
|
const f = nt => {
|
||||||
return {
|
return {
|
||||||
Epsilon: () => true,
|
Epsilon: () => true,
|
||||||
|
|
@ -15,7 +17,7 @@ const nullable = grammar => nonterminal => request => {
|
||||||
NT: () => request(nt.nt),
|
NT: () => request(nt.nt),
|
||||||
Seq: () => f(nt.l) && f(nt.r),
|
Seq: () => f(nt.l) && f(nt.r),
|
||||||
Alt: () => f(nt.l) || f(nt.r),
|
Alt: () => f(nt.l) || f(nt.r),
|
||||||
}[nt.kind]();
|
}[nt.kind]();
|
||||||
}
|
}
|
||||||
return f(grammar[nonterminal]);
|
return f(grammar[nonterminal]);
|
||||||
}
|
}
|
||||||
|
|
@ -65,8 +67,10 @@ const exampleGrammar = {
|
||||||
// B is nullable because B → A C, and both A and C are nullable.
|
// B is nullable because B → A C, and both A and C are nullable.
|
||||||
// S is nullable because S → A B C, and A, B, and C are all nullable.
|
// S is nullable because S → A B C, and A, B, and C are all nullable.
|
||||||
|
|
||||||
|
const isNullable = fixer(
|
||||||
const isNullable = fixer(nullable(exampleGrammar), booleanLattice);
|
nullable(exampleGrammar), // eqs
|
||||||
|
() => MutableRBTree.new((a,b) => compareStrings(a)(b)), // dict to use
|
||||||
|
booleanLattice);
|
||||||
|
|
||||||
console.log(isNullable("S"));
|
console.log(isNullable("S"));
|
||||||
console.log(isNullable("A"));
|
console.log(isNullable("A"));
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue