Skip to content

Commit 303dba3

Browse files
committed
docs: refresh README
1 parent 792794c commit 303dba3

2 files changed

Lines changed: 117 additions & 42 deletions

File tree

README.md

Lines changed: 116 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,75 @@
1-
<center><img src="https://github.com/ClaudiuCeia/ts-duckling/blob/main/logo.png" width="160"/></center>
1+
<p align="center">
2+
<img
3+
src="https://raw.githubusercontent.com/ClaudiuCeia/ts-duckling/main/logo.png"
4+
width="200"
5+
alt="ts-duckling logo"
6+
/>
7+
</p>
28

39
# ts-duckling
410

5-
A Typescript library for Deno that parses text into structured data. Inspired by
6-
[duckling](https://github.com/facebook/duckling) but using a more naive approach
7-
with parser combinators.
11+
[![CI](https://github.com/ClaudiuCeia/ts-duckling/actions/workflows/ci.yml/badge.svg)](https://github.com/ClaudiuCeia/ts-duckling/actions/workflows/ci.yml)
12+
[![JSR](https://jsr.io/badges/@claudiu-ceia/ts-duckling)](https://jsr.io/@claudiu-ceia/ts-duckling)
13+
[![License](https://img.shields.io/github/license/ClaudiuCeia/ts-duckling)](./LICENSE)
14+
[![Playground](https://img.shields.io/badge/playground-GitHub%20Pages-3b82f6)](https://claudiuceia.github.io/ts-duckling/)
815

9-
What this means in practice is that while the library is easy to extend and is
10-
relatively light, it will perform badly on larger data sets and it will be much
11-
more error prone since the rules for entities are hard coded.
16+
A tiny, deterministic entity extractor for TypeScript/Deno, inspired by
17+
[duckling](https://github.com/facebook/duckling). This version uses parser
18+
combinator grammars (no ML).
1219

1320
Online playground: https://claudiuceia.github.io/ts-duckling/
1421

22+
## Install (Deno / JSR)
23+
24+
```ts
25+
import { Duckling } from "jsr:@claudiu-ceia/ts-duckling@^0.0.13";
26+
```
27+
28+
## Quickstart
29+
30+
```ts
31+
import {
32+
Duckling,
33+
Email,
34+
Time,
35+
URL,
36+
} from "jsr:@claudiu-ceia/ts-duckling@^0.0.13";
37+
38+
const text =
39+
"Email me at foo@example.com and visit https://example.com tomorrow.";
40+
const res = Duckling([Email.parser, URL.parser, Time.parser]).extract({
41+
text,
42+
index: 0,
43+
});
44+
45+
if (res.success) {
46+
console.log(res.value);
47+
} else {
48+
console.error(res.expected, res.location);
49+
}
50+
```
51+
52+
## Supported Entities
53+
54+
- Time (relative, day-of-week, common dates, ISO `...Z` timestamps)
55+
- Range (time ranges, year ranges, temperature ranges)
56+
- Temperature
57+
- Quantity
58+
- Location (countries, dataset-backed)
59+
- URL
60+
- Email
61+
- Institution
62+
- Language (dataset-backed)
63+
- Phone (E.164-ish)
64+
- IP address (IPv4 + deterministic IPv6 full form)
65+
- SSN (US)
66+
- Credit card (Luhn)
67+
- UUID
68+
- API keys (best-effort detection for common provider prefixes)
69+
1570
## When would I use this?
1671

17-
If you have a Deno Typescript codebase, and you want to extract entities from
72+
If you have a Deno TypeScript codebase, and you want to extract entities from
1873
relatively small data samples (ie: blog posts, comments, messages, etc.), this
1974
will probably work for you. Even more so if the format of the data is relatively
2075
stable.
@@ -25,46 +80,66 @@ entities:
2580

2681
```ts
2782
// ts-duckling falsely assumes that 6/2022 is a date
28-
const res = Duckling([Time.parser]).extract("6/2022 is 0.00296735905");
29-
/**
30-
[
31-
{
32-
end: 7,
33-
kind: "time",
34-
start: 0,
35-
text: "6/2022 ",
36-
value: {
37-
grain: "day",
38-
when: "2022-01-05T22:00:00.000Z",
39-
},
40-
},
41-
]
42-
*/
83+
const res = Duckling([Time.parser]).extract({
84+
text: "6/2022 is 0.00296735905",
85+
index: 0,
86+
});
87+
88+
if (res.success) {
89+
console.log(res.value);
90+
// [
91+
// {
92+
// kind: "time",
93+
// start: 0,
94+
// end: 6,
95+
// text: "6/2022",
96+
// value: { when: "...", grain: "day", era: "CE" },
97+
// },
98+
// ]
99+
}
43100
```
44101

45-
## Adding new entity types
102+
## Adding New Entity Types
103+
104+
Define a parser that returns an `Entity`, then pass its `.parser` to `Duckling`.
46105

47106
```ts
48-
type FizzBuzzLanguage = EntityLanguage<
49-
{
50-
fizz: Parser<string>;
51-
buzz: Parser<string>;
52-
fizzbuzz: Parser<string>;
107+
import {
108+
createLanguageThis,
109+
map,
110+
type Parser,
111+
regex,
112+
} from "jsr:@claudiu-ceia/combine@^0.2.8";
113+
import {
114+
type AnyEntity,
115+
Duckling,
116+
ent,
117+
type Entity,
118+
} from "jsr:@claudiu-ceia/ts-duckling@^0.0.13";
119+
120+
type HashtagEntity = Entity<"hashtag", { tag: string }>;
121+
122+
const Hashtag = createLanguageThis({
123+
Full() {
124+
return map(
125+
regex(/#[A-Za-z0-9_]{2,64}/, "hashtag"),
126+
(m, b, a) => ent({ tag: m.slice(1) }, "hashtag", b, a),
127+
);
128+
},
129+
parser() {
130+
return this.Full;
53131
},
54-
string
55-
>;
56-
57-
const Fizzbuzz = createLanguage<FizzBuzzLanguage>({
58-
fizz: () => fuzzyCase("fizz"),
59-
buzz: () => fuzzyCase("buzz"),
60-
fizzbuzz: (s) => mapJoin(seq(s.fizz, s.buzz)),
61-
parser: (s) => either(s.fizz, s.buzz, s.fizzbuzz),
62132
});
63133

64-
Duckling([Fizzbuzz.parser]).extract(`
65-
FizzBuzz is a programming problem where you print fizz for multiples
66-
of 3, buzz for multiples of 5, and fizzbuzz for multiples of both 3 and 5.
67-
`);
134+
// `Duckling` is typed to the built-in entity union (`AnyEntity`).
135+
// To mix in custom entities, widen the type (or cast) and handle it downstream.
136+
const res2 = Duckling([Hashtag.parser as unknown as Parser<AnyEntity>]).extract(
137+
{
138+
text: "hello #duckling",
139+
index: 0,
140+
},
141+
);
142+
if (res2.success) console.log(res2.value);
68143
```
69144

70145
# License

deno.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@claudiu-ceia/ts-duckling",
3-
"version": "0.0.12",
3+
"version": "0.0.13",
44
"exports": {
55
".": "./mod.ts"
66
},

0 commit comments

Comments
 (0)