@erml/parser
Version:
Parser implementation of ERML
199 lines (174 loc) • 5.9 kB
text/typescript
import {
ParsingPipeline,
assertToken,
processNumber,
processStringLiteral,
processIdentifier,
processBody,
walkPipeline,
testables,
} from "../../engine/parser/pipeline"
import { isDuplicateIdentifier } from "../../engine/parser/identifiers"
const { bracesMatchAt } = testables
describe("tests for assertToken", () => {
it("should throw a syntax error", () => {
expect(() =>
assertToken({ value: "foo", position: -1, line: -1 }, ["bar"])
).toThrow(SyntaxError)
})
it("should invoke callback and pass in the correct matched index", () => {
const token = { value: "bar", position: -1, line: -1 }
const expectedValues = ["foo", token.value]
const callback = jest.fn()
assertToken(token, expectedValues, callback)
expect(callback).toHaveBeenCalledWith(expectedValues.indexOf(token.value))
})
})
describe("tests for processNumber", () => {
const token = { value: "5", position: -1, line: -1 }
const numericValue = Number(token.value)
it("should throw a type error", () => {
expect(() =>
processNumber(
{ value: "foo", position: -1, line: -1 },
[0, 10],
() => undefined
)
).toThrow(TypeError)
})
it("should throw a range error. value below the range", () => {
expect(() =>
processNumber(
token,
[numericValue + 1, numericValue + 2],
() => undefined
)
).toThrow(RangeError)
})
it("should throw a range error. value above the range", () => {
expect(() =>
processNumber(
token,
[numericValue - 2, numericValue - 1],
() => undefined
)
).toThrow(RangeError)
})
it("should invoke callback and pass in the correct numerical value", () => {
const callback = jest.fn()
processNumber(token, [numericValue - 1, numericValue + 1], callback)
expect(callback).toHaveBeenCalledWith(numericValue)
})
})
describe("tests for processStringLiteral", () => {
it("should throw a type error", () => {
expect(() =>
processStringLiteral(
{ value: "foo", position: -1, line: -1 },
() => undefined
)
).toThrow(TypeError)
})
it("should invoke callback and pass in the correct string value", () => {
const callback = jest.fn()
processStringLiteral(
{ value: `"\\\\\\\\a\\\\\\a b\\\\b \\" "`, position: -1, line: -1 },
callback
)
expect(callback).toHaveBeenCalledWith(`\\\\a\\a b\\b " `)
})
})
describe("tests for processIdentifier", () => {
it("should throw a syntax error. not a valid identifier", () => {
expect(() =>
processIdentifier(
{ value: "1foo", position: -1, line: -1 },
false,
() => undefined
)
).toThrow(SyntaxError)
})
it("should throw a reference error", () => {
expect(() =>
processIdentifier(
{ value: "foo", position: -1, line: -1 },
true,
() => undefined
)
).toThrow(ReferenceError)
})
it("should throw a syntax error. identifier already defined", () => {
isDuplicateIdentifier("foo")
expect(() =>
processIdentifier(
{ value: "foo", position: -1, line: -1 },
false,
() => undefined
)
).toThrow(SyntaxError)
})
it("should invoke callback", () => {
const callback = jest.fn()
processIdentifier({ value: "bar", position: -1, line: -1 }, false, callback)
expect(callback).toHaveBeenCalled()
})
})
describe("tests for processBody", () => {
it('should throw a syntax error. value is not "{"', () => {
const tokens = [{ value: "<", position: -1, line: -1 }]
expect(() => processBody(tokens, 0, () => undefined)).toThrow(SyntaxError)
})
it("should throw a syntax error. grouping braces don't match", () => {
const tokens = [{ value: "{", position: -1, line: -1 }]
expect(() => processBody(tokens, 0, () => undefined)).toThrow(SyntaxError)
})
it("should throw a syntax error. body is empty", () => {
const tokens = [
{ value: "{", position: -1, line: -1 },
{ value: "}", position: -1, line: -1 },
]
expect(() => processBody(tokens, 0, () => undefined)).toThrow(SyntaxError)
})
it("should invoke callback and pass in the correct the bodyStart and bodyEnd indexes", () => {
const tokens = [
{ value: "{", position: -1, line: -1 },
{ value: "foo", position: -1, line: -1 },
{ value: "}", position: -1, line: -1 },
]
const callback = jest.fn()
processBody(tokens, 0, callback)
expect(callback).toHaveBeenCalledWith(1, tokens.length - 2)
})
})
describe("tests for walkPipeline", () => {
const tokens = [
{ value: "foo", position: -1, line: -1 },
{ value: "bar", position: -1, line: -1 },
]
const parsingPipeline: ParsingPipeline = [
() => undefined,
() => undefined,
() => undefined,
]
it("should throw a syntax error. unexpected reach of end", () => {
expect(() => walkPipeline(parsingPipeline, tokens, 0)).toThrow(SyntaxError)
})
it("should walk through the parsing pipeline, and return the next token index correctly ", () => {
parsingPipeline.pop()
expect(walkPipeline(parsingPipeline, tokens, 0)).toBe(tokens.length)
})
})
describe("tests for bracesMatchAt", () => {
const tokens = [
{ value: "{", position: -1, line: -1 },
{ value: "{", position: -1, line: -1 },
{ value: "}", position: -1, line: -1 },
]
it("should return null for non-matching grouping braces", () => {
expect(bracesMatchAt(tokens, 0)).toBe(null)
})
it("Should return the correct index in which braces matched at", () => {
tokens.shift()
expect(bracesMatchAt(tokens, 0)).toBe(tokens.length - 1)
})
})