commit 8c18398ae4c7e4c9297e2b54b02693d815568cc3 Author: JOLIMAITRE Matthieu Date: Wed Aug 7 10:17:45 2024 +0200 init diff --git a/lib/extractor.ts b/lib/extractor.ts new file mode 100644 index 0000000..6408cef --- /dev/null +++ b/lib/extractor.ts @@ -0,0 +1,21 @@ + +export class Extractor { + private extract: (text: string) => O | null; + + public constructor(extract: (text: string) => O | null) { + this.extract = extract; + } + + public get(text: string) { + return this.extract(text); + } + + public or(other: Extractor) { + const this_extract = this.extract; + return new Extractor(text => { + const r1 = this_extract(text); + if (r1 !== null) return r1; + else return other.extract(text); + }); + } +} diff --git a/lib/lib.ts b/lib/lib.ts new file mode 100644 index 0000000..0a75dbe --- /dev/null +++ b/lib/lib.ts @@ -0,0 +1,28 @@ +import { Rule } from "./rule.ts"; +import { PairElts, StructOfArr } from "./types.ts"; +import { Extractor } from "./extractor.ts" + +export function extr(...args: A): Extractor, string>> { + const seps = [] as string[]; + const vars = [] as string[]; + + let pair = true; + for (const arg of args) { + if (pair) seps.push(arg); + else vars.push(arg); + pair = !pair; + } + + const vars_ = vars as PairElts; + const rule = new Rule(seps, vars_); + return new Extractor(text => rule.get(text)) +} + +Deno.test("types", async () => { + const { assertEquals } = await import("https://deno.land/std@0.224.0/assert/assert_equals.ts"); + const vars = extr() + .or(extr("bonjour, ", 'animal', ", poilu")) + .or(extr("bonjour, ", 'arbre', ", feuillu")) + .get("bonjour, le chĂȘne, feuillu"); + assertEquals({ vars }, { vars: { "arbre": "le chĂȘne" } }) +}) diff --git a/lib/rule.ts b/lib/rule.ts new file mode 100644 index 0000000..b18da3c --- /dev/null +++ b/lib/rule.ts @@ -0,0 +1,34 @@ +import { StructOfArr } from "./types.ts"; +import { zip } from "./utils.ts"; + +export class Rule { + private separators: ({ kind: "word", word: string } | { kind: "end" })[]; + private vars: V; + + public constructor(separators: string[], vars: V) { + this.separators = separators.map(word => ({ kind: "word", word })); + this.vars = vars; + this.separators.push({ kind: "end" }); + } + + public get(text: string): StructOfArr | null { + const first_sep = this.separators[0] ?? ""; + if (first_sep.kind === "end") { + if (text.length > 0) return null; + else return {} as StructOfArr; + } + if (!text.includes(first_sep.word)) return null; + let [val, rest] = text.split(first_sep.word, 2); + const result = {} as Record; + for (const [key, sep] of zip(this.vars, this.separators.slice(1))) { + if (sep.kind === "end") { + result[key] = rest; + break; + } + if (!rest.includes(sep.word)) return null; + [val, rest] = rest.split(sep.word, 2); + result[key] = val; + } + return result as StructOfArr; + } +} diff --git a/lib/types.ts b/lib/types.ts new file mode 100644 index 0000000..73a304f --- /dev/null +++ b/lib/types.ts @@ -0,0 +1,13 @@ + +// deno-lint-ignore no-explicit-any +export type PairElts = + A extends [] ? [] : + A extends [infer _F] ? [] : + A extends [infer _F, infer S, ...infer R] ? [S, ...PairElts] : + never; + +export type StructOfArr = + // deno-lint-ignore ban-types + A extends [] ? {} : + A extends [infer K extends string, ...infer R extends string[]] ? { [key in K]: V } & StructOfArr : + never; diff --git a/lib/utils.ts b/lib/utils.ts new file mode 100644 index 0000000..b59e86a --- /dev/null +++ b/lib/utils.ts @@ -0,0 +1,14 @@ +export function* zip(a: Iterable, b: Iterable): Generator<[A, B], void, void> { + const iter_a = a[Symbol.iterator](), iter_b = b[Symbol.iterator](); + while (true) { + const next_a = next(iter_a), next_b = next(iter_b); + if (next_a === null || next_b === null) return; + yield [next_a, next_b]; + } +} + +export function next(iterator: Iterator) { + const result = iterator.next().value; + if (result === undefined) return null; + else return result as T; +} diff --git a/mod.ts b/mod.ts new file mode 100644 index 0000000..eff9d90 --- /dev/null +++ b/mod.ts @@ -0,0 +1 @@ +import { extr } from "./lib/lib.ts"