UNPKG

clarity-pattern-parser

Version:

Parsing Library for Typescript and Javascript.

338 lines (273 loc) 11.4 kB
import { Cursor } from "./Cursor"; import { Sequence } from "./Sequence"; import { Literal } from "./Literal"; import { Node } from "../ast/Node"; import { Optional } from "./Optional"; import { Pattern } from "./Pattern"; describe("Sequence", () => { test("No Patterns", () => { expect(() => { new Sequence("empty", []); }).toThrow(); }); test("One Pattern Match Successful", () => { const sequence = new Sequence("sequence", [new Literal("a", "A")]); const cursor = new Cursor("A"); const result = sequence.parse(cursor); const expected = new Node("sequence", "sequence", 0, 0, [ new Node("literal", "a", 0, 0, [], "A") ]); expect(result?.isEqual(expected)).toBeTruthy(); expect(cursor.furthestError).toBe(null); expect(cursor.index).toBe(0); }); test("One Pattern Match Fails", () => { const sequence = new Sequence("sequence", [new Literal("a", "A")]); const cursor = new Cursor("V"); const result = sequence.parse(cursor); expect(result).toEqual(null); expect(cursor.error?.startIndex).toBe(0); expect(cursor.error?.lastIndex).toBe(0); expect(cursor.index).toBe(0); }); test("Two Pattern Match Successful", () => { const sequence = new Sequence("sequence", [ new Literal("a", "A"), new Literal("b", "B") ]); const cursor = new Cursor("AB"); const result = sequence.parse(cursor); const expected = new Node("sequence", "sequence", 0, 1, [ new Node("literal", "a", 0, 0, [], "A"), new Node("literal", "b", 1, 1, [], "B") ]); expect(result?.isEqual(expected)).toBeTruthy(); expect(cursor.error).toBe(null) expect(cursor.index).toBe(1); }); test("Two Pattern Match Fails", () => { const sequence = new Sequence("sequence", [ new Literal("a", "A"), new Literal("b", "B") ]); const cursor = new Cursor("AC"); const result = sequence.parse(cursor); expect(result).toEqual(null); expect(cursor.error?.startIndex).toBe(1); expect(cursor.error?.lastIndex).toBe(1); expect(cursor.index).toBe(0); }); test("One Pattern Match Fails (Optional)", () => { const sequence = new Optional("optional-sequence", new Sequence("sequence", [new Literal("a", "A")])); const cursor = new Cursor("V"); const result = sequence.parse(cursor); expect(result).toEqual(null); expect(cursor.error).toBe(null); expect(cursor.index).toBe(0); }); test("Trailing Optional Child Patterns", () => { const sequence = new Sequence("sequence", [ new Literal("a", "A"), new Optional("b", new Literal("b", "B")) ]); const cursor = new Cursor("AD"); const result = sequence.parse(cursor); const expected = new Node("sequence", "sequence", 0, 0, [ new Node("literal", "a", 0, 0, [], "A") ]); expect(result?.isEqual(expected)).toBeTruthy(); expect(cursor.error).toBe(null); expect(cursor.index).toBe(0); }); test("Trailing Optional Child Patterns With No More Text", () => { const sequence = new Sequence("sequence", [ new Literal("a", "A"), new Optional("b", new Literal("b", "B")) ]); const cursor = new Cursor("A"); const result = sequence.parse(cursor); const expected = new Node("sequence", "sequence", 0, 0, [ new Node("literal", "a", 0, 0, [], "A") ]); expect(result?.isEqual(expected)).toBeTruthy(); expect(cursor.error).toBe(null) expect(cursor.index).toBe(0); }); test("Incomplete Parse (Optional)", () => { const sequence = new Optional("optional-sequence", new Sequence("sequence", [ new Literal("a", "A"), new Literal("b", "B") ])); const cursor = new Cursor("A"); const result = sequence.parse(cursor); expect(result).toEqual(null); expect(cursor.error).toBe(null); expect(cursor.index).toBe(0); }); test("Optional Child Pattern With More Patterns", () => { const sequence = new Optional("optional-sequence", new Sequence("sequence", [ new Optional("a", new Literal("a", "A")), new Literal("b", "B"), new Literal("c", "C") ])); const cursor = new Cursor("BC"); const result = sequence.parse(cursor); const expected = new Node("sequence", "sequence", 0, 1, [ new Node("literal", "b", 0, 0, [], "B"), new Node("literal", "c", 1, 1, [], "C"), ]); expect(result?.isEqual(expected)).toBeTruthy(); expect(cursor.error).toBe(null) expect(cursor.index).toBe(1); }); test("Nothing Matched", () => { const sequence = new Optional("optional-sequence", new Sequence("sequence", [ new Literal("a", "A"), ])); const cursor = new Cursor("BC"); const result = sequence.parse(cursor); expect(result).toEqual(null); expect(cursor.error).toBe(null); expect(cursor.index).toBe(0); }); test("No Matches On Optional Child Patterns", () => { const sequence = new Optional("optional-sequence", new Sequence("sequence", [ new Optional("a", new Literal("a", "A")), new Optional("b", new Literal("b", "B")), ])); const cursor = new Cursor("XYZ"); const result = sequence.parse(cursor); expect(result).toEqual(null); expect(cursor.error).toBe(null); expect(cursor.index).toBe(0); }); test("Properties", () => { const a = new Literal("a", "A"); const sequence = new Sequence("sequence", [ a, ]); expect(sequence.type).toBe("sequence"); expect(sequence.name).toBe("sequence"); expect(sequence.parent).toBe(null); expect(sequence.children[0].type).toBe("literal"); expect(sequence.children[0].name).toBe("a"); }); test("Exec", () => { const sequence = new Sequence("sequence", [new Literal("a", "A")]); const { ast: result, cursor } = sequence.exec("A"); const expected = new Node("sequence", "sequence", 0, 0, [ new Node("literal", "a", 0, 0, undefined, "A") ]); expect(result?.isEqual(expected)).toBeTruthy(); expect(cursor).not.toBeNull(); }); test("Test With Match", () => { const sequence = new Sequence("sequence", [new Literal("a", "A")]); const hasMatch = sequence.test("A"); expect(hasMatch).toBeTruthy(); }); test("Test With No Match", () => { const sequence = new Sequence("sequence", [new Literal("a", "A")]); const hasMatch = sequence.test("B"); expect(hasMatch).toBeFalsy(); }); test("Set Parent", () => { const a = new Optional("a", new Literal("a", "A")); const sequence = new Sequence("sequence", [ a, ]); const parent = new Sequence("parent", [sequence]); expect(parent.type).toBe("sequence"); expect(parent.children[0].type).toBe("sequence"); }); test("Get Tokens", () => { const sequence = new Sequence("sequence", [ new Optional("a", new Literal("a", "A")), new Literal("b", "B"), ]); const tokens = sequence.getTokens(); const expected = ["A", "B"]; expect(tokens).toEqual(expected); }); test("Get Tokens After", () => { const sequence = new Sequence("sequence", [ new Literal("a", "A"), new Optional("b", new Literal("b", "B")), new Literal("c", "C"), ]); const tokens = sequence.getTokensAfter(sequence.children[0]); const expected = ["B", "C"]; expect(tokens).toEqual(expected); }); test("Get Tokens After With Invalid Pattern", () => { const sequence = new Sequence("sequence", [ new Literal("a", "A"), new Optional("b", new Literal("b", "B")), new Literal("c", "C"), ]); const tokens = sequence.getTokensAfter(new Literal("not-child", "not-child")); expect(tokens).toEqual([]); }); test("Get Tokens After With Last Child", () => { const sequence = new Sequence("sequence", [ new Literal("a", "A"), ]); const parent = new Sequence("parent", [sequence, new Literal("b", "B")]); const tokens = parent.children[0].getTokensAfter(parent.children[0].children[0]) expect(tokens).toEqual(["B"]); }); test("Get Tokens After With Last Optional Child", () => { const sequence = new Sequence("sequence", [ new Literal("a", "A"), new Optional("b", new Literal("b", "B")), ]); const parent = new Sequence("parent", [sequence, new Literal("c", "C")]); const tokens = parent.children[0].getTokensAfter(parent.children[0].children[0]) expect(tokens).toEqual(["B", "C"]); }); test("Get Next Tokens", () => { const sequence = new Sequence("sequence", [new Literal("a", "A")]); const parent = new Sequence("parent", [sequence, new Literal("b", "B")]); const sequenceClone = parent.find(p => p.name === "sequence"); const tokens = sequenceClone?.getNextTokens() || []; expect(tokens[0]).toBe("B"); }); test("Get Next Tokens With Null Parent", () => { const sequence = new Sequence("sequence", [new Literal("a", "A")]); const tokens = sequence.getNextTokens(); expect(tokens.length).toBe(0); }); test("Get Patterns", () => { const sequence = new Sequence("sequence", [ new Optional("a", new Literal("a", "A")), new Literal("b", "B"), ]); const tokens = sequence.getPatterns(); const a = sequence.find(p => p.name === "a"); const b = sequence.find(p => p.name === "b"); const expected = [a, b] expect(tokens).toEqual(expected); }); test("Get Next Patterns", () => { const sequence = new Sequence("sequence", [new Literal("a", "A")]); const parent = new Sequence("parent", [sequence, new Literal("b", "B")]); const sequenceClone = parent.find(p => p.name === "sequence"); const nextPatterns = sequenceClone?.getNextPatterns() || []; const b = parent.find(p => p.name === "b") as Pattern; expect(nextPatterns[0].isEqual(b)).toBeTruthy(); }); test("Get Next Patterns With Null Parent", () => { const sequence = new Sequence("sequence", [new Literal("a", "A")]); const nextPatterns = sequence.getNextPatterns() expect(nextPatterns.length).toBe(0); }); test("All Patterns are Optional", () => { const sequence = new Sequence("sequence", [ new Optional("optional-a", new Literal("a", "A")), new Optional("optional-b", new Literal("b", "B")) ]); const result = sequence.exec(""); expect(result.ast).toBe(null); expect(result.cursor.hasError).toBeFalsy(); }); });