import { compareStrings } from "../lib/compare/primitives.js"; import { fixer } from "../lib/util/fixpoint.js"; import { MutableRBTree } from "../lib/util/rbtree_wrapper.js"; const booleanLattice = { isMaximal: (value) => value, equality: (l, r) => l === r, bottom: () => false, }; const nullable = grammar => nonterminal => request => { console.log(nonterminal); const f = nt => { return { Epsilon: () => true, T: () => false, NT: () => request(nt.nt), Seq: () => f(nt.l) && f(nt.r), Alt: () => f(nt.l) || f(nt.r), }[nt.kind](); } return f(grammar[nonterminal]); } // S → A B C // A → a A | ε // B → b B | A C // C → c C | ε const exampleGrammar = { S: {kind: "Seq", l: {kind: "NT", nt: "A"}, r: {kind: "Seq", l: {kind: "NT", nt: "B"}, r: {kind: "NT", nt: "C"}, }, }, A: {kind: "Alt", l: {kind: "Seq", l: {kind: "T", token: "a"}, r: {kind: "NT", nt: "A"} }, r: {kind: "Epsilon"}, }, B: {kind: "Alt", l: {kind: "Seq", l: {kind: "T", token: "b"}, r: {kind: "NT", nt: "B"} }, r: {kind: "Seq", l: {kind: "NT", nt: "A"}, r: {kind: "NT", nt: "C"} }, }, C: {kind: "Alt", l: {kind: "Seq", l: {kind: "T", token: "c"}, r: {kind: "NT", nt: "C"} }, r: {kind: "Epsilon"}, }, }; // A is nullable because it has a production A → ε. // C is nullable because it has a production C → ε. // 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. const isNullable = fixer( nullable(exampleGrammar), // eqs () => MutableRBTree.new((a,b) => compareStrings(a)(b)), // dict to use booleanLattice); console.log(isNullable("S")); console.log(isNullable("A")); console.log(isNullable("B")); console.log(isNullable("C"));