clarity-pattern-parser
Version:
Parsing Library for Typescript and Javascript.
338 lines (273 loc) • 11.4 kB
text/typescript
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();
});
});