tarsec 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,224 +0,0 @@
1
- import { trace } from "./trace";
2
- import { Parser, ParserSuccess } from "./types";
3
- import { escape, merge, mergeCaptures } from "./utils";
4
-
5
- export function many<T>(parser: Parser<T>): Parser<T[]> {
6
- return trace("many", (input: string) => {
7
- let match: T[] = [];
8
- let rest = input;
9
- while (true) {
10
- let result = parser(rest);
11
- if (!result.success) {
12
- return { success: true, match, rest };
13
- }
14
- match.push(result.match);
15
- rest = result.rest;
16
- }
17
- });
18
- }
19
-
20
- export function many1<T>(parser: Parser<T>): Parser<T[]> {
21
- return trace(`many1`, (input: string) => {
22
- let result = many(parser)(input);
23
- // this logic doesn't work with optional and not
24
- if (result.rest !== input) {
25
- return result;
26
- }
27
- return {
28
- success: false,
29
- rest: input,
30
- message: "expected at least one match",
31
- };
32
- });
33
- }
34
-
35
- export function manyWithJoin(parser: Parser<string>): Parser<string> {
36
- return transform<string[], string>(many(parser), (x) => x.join(""));
37
- }
38
-
39
- export function many1WithJoin(parser: Parser<string>): Parser<string> {
40
- return transform<string[], string>(many1(parser), (x) => x.join(""));
41
- }
42
-
43
- export function or<T>(parsers: Parser<T>[], name: string = ""): Parser<T> {
44
- return trace(`or(${name})`, (input: string) => {
45
- for (let parser of parsers) {
46
- let result = parser(input);
47
- if (result.success) {
48
- return result;
49
- }
50
- }
51
- return {
52
- success: false,
53
- rest: input,
54
- message: "all parsers failed",
55
- };
56
- });
57
- }
58
-
59
- export function optional<T>(parser: Parser<T>): Parser<T | null> {
60
- return trace<T | null>("optional", (input: string) => {
61
- let result = parser(input);
62
- if (result.success) {
63
- return result;
64
- }
65
- return { success: true, match: null, rest: input };
66
- });
67
- }
68
-
69
- export function not(parser: Parser<any>): Parser<null> {
70
- return trace("not", (input: string) => {
71
- let result = parser(input);
72
- if (result.success) {
73
- return {
74
- success: false,
75
- rest: input,
76
- message: "unexpected match",
77
- };
78
- }
79
- return { success: true, match: null, rest: input };
80
- });
81
- }
82
-
83
- export function between<O, C, P>(
84
- open: Parser<O>,
85
- close: Parser<C>,
86
- parser: Parser<P>
87
- ): Parser<P> {
88
- return (input: string) => {
89
- const result1 = open(input);
90
- if (!result1.success) {
91
- return result1;
92
- }
93
- const parserResult = parser(result1.rest);
94
- if (!parserResult.success) {
95
- return parserResult;
96
- }
97
- const result2 = close(parserResult.rest);
98
- if (!result2.success) {
99
- return result2;
100
- }
101
- return { success: true, match: parserResult.match, rest: result2.rest };
102
- };
103
- }
104
-
105
- export function sepBy<S, P>(
106
- separator: Parser<S>,
107
- parser: Parser<P>
108
- ): Parser<P[]> {
109
- return (input: string) => {
110
- let match: P[] = [];
111
- let rest = input;
112
- while (true) {
113
- const result = parser(rest);
114
- if (!result.success) {
115
- return { success: true, match, rest };
116
- }
117
- match.push(result.match);
118
- rest = result.rest;
119
-
120
- const sepResult = separator(rest);
121
- if (!sepResult.success) {
122
- return { success: true, match, rest };
123
- }
124
- rest = sepResult.rest;
125
- }
126
- };
127
- }
128
-
129
- export function seq<M, C extends string>(
130
- parsers: Parser<M>[],
131
- name: string = ""
132
- ): Parser<M[], C> {
133
- return trace(`seq(${name})`, (input: string) => {
134
- let match: M[] = [];
135
- let rest = input;
136
- // @ts-ignore
137
- let captures: Record<U, any> = {};
138
- for (let parser of parsers) {
139
- let result = parser(rest);
140
- if (!result.success) {
141
- return result;
142
- }
143
- match.push(result.match);
144
- rest = result.rest;
145
- if (result.captures) {
146
- captures = mergeCaptures(captures, result.captures);
147
- }
148
- }
149
- return { success: true, match, rest, captures };
150
- });
151
- }
152
-
153
- export function capture<M, C extends string>(
154
- parser: Parser<M>,
155
- name: string
156
- ): Parser<M, C> {
157
- return trace(`captures(${escape(name)})`, (input: string) => {
158
- let result = parser(input);
159
- if (result.success) {
160
- const captures: Record<string, any> = {
161
- [name]: result.match,
162
- };
163
- return {
164
- ...result,
165
- captures: mergeCaptures(result.captures || {}, captures),
166
- };
167
- }
168
- return result;
169
- });
170
- }
171
-
172
- export function captureCaptures<M, C extends string>(
173
- parser: Parser<M>,
174
- name: string
175
- ): Parser<M, C> {
176
- return trace(`captures(${escape(name)})`, (input: string) => {
177
- let result = parser(input);
178
- if (result.success) {
179
- const captures: Record<string, any> = {
180
- [name]: result.captures,
181
- };
182
- return {
183
- ...result,
184
- captures: mergeCaptures(result.captures || {}, captures),
185
- };
186
- }
187
- return result;
188
- });
189
- }
190
-
191
- export function shapeCaptures<M, C extends string>(
192
- parser: Parser<M>,
193
- func: (captures: Record<string, any>) => Record<string, any>,
194
- name: string
195
- ): Parser<M, C> {
196
- return trace(`captures(${escape(name)})`, (input: string) => {
197
- let result = parser(input);
198
- if (result.success) {
199
- const captures: Record<string, any> = result.captures || {};
200
-
201
- return {
202
- ...result,
203
- captures: func(captures),
204
- };
205
- }
206
- return result;
207
- });
208
- }
209
-
210
- export function transform<T, X>(
211
- parser: Parser<T>,
212
- transformerFunc: (x: T) => X
213
- ): Parser<X> {
214
- return trace(`transform(${transformerFunc})`, (input: string) => {
215
- let result = parser(input);
216
- if (result.success) {
217
- return {
218
- ...result,
219
- match: transformerFunc(result.match),
220
- };
221
- }
222
- return result;
223
- });
224
- }
@@ -1,300 +0,0 @@
1
- import { describe, expect, it, test } from "vitest";
2
- import {
3
- alphanum,
4
- digit,
5
- letter,
6
- noneOf,
7
- num,
8
- oneOf,
9
- space,
10
- spaces,
11
- str,
12
- word,
13
- char,
14
- } from "./parsers";
15
- import { success, failure } from "../vitest.globals.js";
16
- import {
17
- capture,
18
- many,
19
- many1,
20
- many1WithJoin,
21
- not,
22
- optional,
23
- or,
24
- seq,
25
- } from "./combinators";
26
- test.skip("hello", () => {
27
- describe("Parser Tests", () => {
28
- describe("optional parser", () => {
29
- const parser = optional(char("a"));
30
-
31
- it("should parse the character if it exists", () => {
32
- const result = parser("a");
33
- expect(result).toEqual(success({ rest: "", match: "a" }));
34
- });
35
-
36
- it("should return an empty string if the character is missing", () => {
37
- const result = parser("b");
38
- expect(result).toEqual(success({ rest: "b", match: "" }));
39
- });
40
-
41
- it("should not consume any input if it fails", () => {
42
- // @ts-ignore
43
- const parser2 = optional(seq(many1WithJoin(letter), char("!")));
44
- const result1 = parser2("hello!");
45
- expect(result1).toEqual(
46
- success({ rest: "", match: "hello!", captures: {} })
47
- );
48
-
49
- const result2 = parser2("hello");
50
- expect(result2).toEqual(success({ rest: "hello", match: "" }));
51
- });
52
- });
53
-
54
- describe("not parser", () => {
55
- const parser = not(char("a"));
56
-
57
- it("should fail if the character is present", () => {
58
- const result = parser("a");
59
- expect(result).toEqual(
60
- failure({ rest: "a", message: "unexpected match" })
61
- );
62
- });
63
-
64
- it("should return an empty string if the character is missing", () => {
65
- const result = parser("b");
66
- expect(result).toEqual(success({ rest: "b", match: "" }));
67
- });
68
- });
69
-
70
- describe("space parser", () => {
71
- it("should parse a space character", () => {
72
- const result = space(" ");
73
- expect(result).toEqual(success({ rest: "", match: " " }));
74
- });
75
-
76
- it("should fail if the character is not a space", () => {
77
- const result = space("a");
78
- expect(result).toEqual(
79
- failure({ rest: "a", message: "expected , got a" })
80
- );
81
- });
82
- });
83
-
84
- describe("spaces parser", () => {
85
- it("should parse multiple space characters", () => {
86
- const result = spaces(" ");
87
- expect(result).toEqual(success({ rest: "", match: " " }));
88
- });
89
-
90
- it("should fail if no space characters found", () => {
91
- const result = spaces("abc");
92
- expect(result).toEqual(
93
- failure({ rest: "abc", message: "expected at least one match" })
94
- );
95
- });
96
- });
97
-
98
- describe("digit parser", () => {
99
- it("should parse a single digit", () => {
100
- const result = digit("1");
101
- expect(result).toEqual(success({ rest: "", match: "1" }));
102
- });
103
-
104
- it("should fail if the character is not a digit", () => {
105
- const result = digit("a");
106
- expect(result).toEqual(
107
- failure({ rest: "a", message: "expected one of 0123456789" })
108
- );
109
- });
110
- });
111
-
112
- describe("letter parser", () => {
113
- it("should parse a single letter", () => {
114
- const result = letter("a");
115
- expect(result).toEqual(success({ rest: "", match: "a" }));
116
- });
117
-
118
- it("should fail if the character is not a letter", () => {
119
- const result = letter("1");
120
- expect(result).toEqual(
121
- failure({
122
- rest: "1",
123
- message: "expected one of abcdefghijklmnopqrstuvwxyz",
124
- })
125
- );
126
- });
127
- });
128
-
129
- describe("alphanum parser", () => {
130
- it("should parse a single alphanumeric character", () => {
131
- const result = alphanum("1");
132
- expect(result).toEqual(success({ rest: "", match: "1" }));
133
- });
134
-
135
- it("should fail if the character is not alphanumeric", () => {
136
- const result = alphanum("_");
137
- expect(result).toEqual(
138
- failure({
139
- rest: "_",
140
- message: "expected one of abcdefghijklmnopqrstuvwxyz0123456789",
141
- })
142
- );
143
- });
144
- });
145
-
146
- describe("word parser", () => {
147
- const parser = word;
148
-
149
- it("should parse a single word", () => {
150
- const result = parser("hello");
151
- expect(result).toEqual(success({ rest: "", match: "hello" }));
152
- });
153
-
154
- it("should fail if no word characters found", () => {
155
- const result = parser("123");
156
- expect(result).toEqual(
157
- failure({ rest: "123", message: "expected at least one match" })
158
- );
159
- });
160
- });
161
-
162
- describe("number parser", () => {
163
- const parser = num;
164
-
165
- it("should parse a single number", () => {
166
- const result = parser("123");
167
- expect(result).toEqual(success({ rest: "", match: "123" }));
168
- });
169
-
170
- it("should fail if no number characters found", () => {
171
- const result = parser("abc");
172
- expect(result).toEqual(
173
- failure({ rest: "abc", message: "expected at least one match" })
174
- );
175
- });
176
- });
177
- });
178
-
179
- describe("seq parser", () => {
180
- const parser = seq([char("a"), char("b")]);
181
-
182
- it("should parse both characters in sequence", () => {
183
- const result = parser("ab");
184
- expect(result).toEqual(success({ rest: "", match: "ab", captures: {} }));
185
- });
186
-
187
- it("should fail if any of the parsers fail", () => {
188
- const result = parser("ac");
189
- expect(result).toEqual(
190
- failure({ rest: "c", message: "expected b, got c" })
191
- );
192
- });
193
- });
194
-
195
- describe("seq parser - hello world", () => {
196
- it("multiple char parsers", () => {
197
- const parser = seq([
198
- char("h"),
199
- char("e"),
200
- char("l"),
201
- char("l"),
202
- char("o"),
203
- ]);
204
- const result = parser("hello world");
205
- expect(result).toEqual(
206
- success({ match: "hello", rest: " world", captures: {} })
207
- );
208
- });
209
-
210
- it("multiple str parsers", () => {
211
- const parser = seq([str("hello"), space, str("world")]);
212
- const result = parser("hello world");
213
- expect(result).toEqual(
214
- success({ match: "hello world", rest: "", captures: {} })
215
- );
216
- });
217
-
218
- it("multiple str parsers + capture", () => {
219
- const parser = seq([str("hello"), space, capture(str("world"), "name")]);
220
- const result = parser("hello world");
221
- expect(result).toEqual(
222
- success({ match: "hello world", rest: "", captures: { name: "world" } })
223
- );
224
- });
225
- });
226
-
227
- /* test("quote parser - single quote", () => {
228
- const input = "'";
229
- const result = quote(input);
230
- expect(result).toEqual(success({ rest: "", match: "'", captures: {} }));
231
- });
232
-
233
- test("quote parser - double quote", () => {
234
- const input = '"';
235
- const result = quote(input);
236
- expect(result).toEqual(success({ rest: "", match: '"', captures: {} }));
237
- });
238
-
239
- test("quote parser - invalid quote", () => {
240
- const input = "`";
241
- const result = quote(input);
242
- expect(result).toEqual(
243
- failure({ rest: "`", message: "unexpected end of input" })
244
- );
245
- });
246
-
247
- // Test for anyChar parser
248
- test("anyChar parser - non-empty input", () => {
249
- const input = "abc";
250
- const result = anyChar(input);
251
- expect(result).toEqual(success({ rest: "bc", match: "a", captures: {} }));
252
- });
253
-
254
- test("anyChar parser - empty input", () => {
255
- const input = "";
256
- const result = anyChar(input);
257
- expect(result).toEqual(
258
- failure({ rest: "", message: "unexpected end of input" })
259
- );
260
- });
261
-
262
- // Test for between parser
263
- test("between parser - valid input", () => {
264
- const open = quote;
265
- const close = quote;
266
- const parser = anyChar;
267
- const input = "'abc'";
268
- const result = between(open, close, parser)(input);
269
- expect(result).toEqual(success({ rest: "", match: "a", captures: {} }));
270
- });
271
-
272
- test("between parser - invalid input", () => {
273
- const open = quote;
274
- const close = quote;
275
- const parser = anyChar;
276
- const input = "\"abc'";
277
- const result = between(open, close, parser)(input);
278
- expect(result).toEqual(
279
- failure({ rest: "abc'", message: "unexpected end of input" })
280
- );
281
- });
282
-
283
- // Test for sepBy parser
284
- test("sepBy parser - valid input", () => {
285
- const separator = anyChar;
286
- const parser = anyChar;
287
- const input = "a,b,c";
288
- const result = sepBy(separator, parser)(input);
289
- expect(result).toEqual(success({ rest: "", match: "abc", captures: {} }));
290
- });
291
-
292
- test("sepBy parser - invalid input", () => {
293
- const separator = quote;
294
- const parser = anyChar;
295
- const input = '"a"bc';
296
- const result = sepBy(separator, parser)(input);
297
- expect(result).toEqual(success({ rest: "bc", match: "a", captures: {} }));
298
- });
299
- */
300
- });
package/lib/parsers.ts DELETED
@@ -1,108 +0,0 @@
1
- import { many1, many1WithJoin, seq, transform } from "./combinators";
2
- import { trace } from "./trace";
3
- import { Parser, ParserResult } from "./types";
4
- import { escape } from "./utils";
5
- export function char(c: string): Parser<string> {
6
- return trace(`char(${escape(c)})`, (input: string) => {
7
- if (input.length === 0) {
8
- return {
9
- success: false,
10
- rest: input,
11
- message: "unexpected end of input",
12
- };
13
- }
14
- if (input[0] === c) {
15
- return { success: true, match: c, rest: input.slice(1) };
16
- }
17
- return {
18
- success: false,
19
- rest: input,
20
- message: `expected ${c}, got ${input[0]}`,
21
- };
22
- });
23
- }
24
-
25
- export function str(s: string): Parser<string> {
26
- return trace(`str(${escape(s)})`, (input: string) => {
27
- if (input.substring(0, s.length) === s) {
28
- return { success: true, match: s, rest: input.slice(s.length) };
29
- }
30
- return {
31
- success: false,
32
- rest: input,
33
- message: `expected ${s}, got ${input.substring(0, s.length)}`,
34
- };
35
- });
36
- }
37
-
38
- export function oneOf(chars: string): Parser<string> {
39
- return trace(`oneOf(${escape(chars)})`, (input: string) => {
40
- if (input.length === 0) {
41
- return {
42
- success: false,
43
- rest: input,
44
- message: "unexpected end of input",
45
- };
46
- }
47
- const c = input[0];
48
- if (chars.includes(c)) {
49
- return char(c)(input);
50
- }
51
- return {
52
- success: false,
53
- rest: input,
54
- message: `expected one of ${escape(chars)}, got ${c}`,
55
- };
56
- });
57
- }
58
-
59
- export function noneOf(chars: string): Parser<string> {
60
- return trace(`noneOf(${escape(chars)})`, (input: string) => {
61
- if (input.length === 0) {
62
- return {
63
- success: false,
64
- rest: input,
65
- message: "unexpected end of input",
66
- };
67
- }
68
- if (chars.includes(input[0])) {
69
- return {
70
- success: false,
71
- rest: input,
72
- message: `expected none of ${chars}`,
73
- };
74
- }
75
- return char(input[0])(input);
76
- });
77
- }
78
-
79
- export function anyChar(input: string): Parser<string> {
80
- return trace("anyChar", (input: string) => {
81
- if (input.length === 0) {
82
- return {
83
- success: false,
84
- rest: input,
85
- message: "unexpected end of input",
86
- };
87
- }
88
- return { success: true, match: input[0], rest: input.slice(1) };
89
- });
90
- }
91
-
92
- export const space: Parser<string> = oneOf(" \t\n\r");
93
- export const spaces: Parser<string> = many1WithJoin(space);
94
- export const digit: Parser<string> = oneOf("0123456789");
95
- export const letter: Parser<string> = oneOf("abcdefghijklmnopqrstuvwxyz");
96
- export const alphanum: Parser<string> = oneOf(
97
- "abcdefghijklmnopqrstuvwxyz0123456789"
98
- );
99
- export const word: Parser<string> = many1WithJoin(letter);
100
- export const num: Parser<string> = many1WithJoin(digit);
101
- export const quote: Parser<string> = oneOf(`'"`);
102
- export const tab: Parser<string> = char("\t");
103
- export const newline: Parser<string> = char("\n");
104
-
105
- export const quotedString = transform(
106
- seq<any, string>([quote, word, quote], "quotedString"),
107
- (x) => x.join("")
108
- );
package/lib/trace.ts DELETED
@@ -1,40 +0,0 @@
1
- import { ParserResult, Parser } from "./types";
2
- import { escape } from "./utils";
3
- const STEP = 2;
4
-
5
- export function resultToString<T>(
6
- name: string,
7
- result: ParserResult<T, string>
8
- ): string {
9
- if (result.success) {
10
- return `✅ ${name} -- match: ${escape(result.match)}, rest: ${escape(
11
- result.rest
12
- )}`;
13
- }
14
- return `❌ ${name} -- message: ${escape(result.message)}, rest: ${escape(
15
- result.rest
16
- )}`;
17
- }
18
-
19
- let level = 0;
20
-
21
- export function trace<T>(name: string, parser: Parser<T>): Parser<T> {
22
- return (input: string) => {
23
- if (process.env.DEBUG) {
24
- console.log(" ".repeat(level) + `🔍 ${name} -- input: ${escape(input)}`);
25
- }
26
- level += STEP;
27
- const result = parser(input);
28
- level -= STEP;
29
- if (process.env.DEBUG) {
30
- console.log(" ".repeat(level) + resultToString(name, result));
31
- if (result.success && result.captures) {
32
- console.log(
33
- " ".repeat(level) +
34
- `⭐ ${name} -- captures: ${JSON.stringify(result.captures)}`
35
- );
36
- }
37
- }
38
- return result;
39
- };
40
- }
package/lib/types.ts DELETED
@@ -1,24 +0,0 @@
1
- export type Object = Record<string, string>;
2
- export type ParserSuccess<M, C extends string> = {
3
- success: true;
4
- match: M;
5
- captures?: Record<C, any>;
6
- rest: string;
7
- };
8
-
9
- export type ParserFailure = {
10
- success: false;
11
- rest: string;
12
- message: string;
13
- };
14
-
15
- export type ParserOptions = {
16
- capture: string;
17
- };
18
-
19
- export type ParserResult<M, C extends string> =
20
- | ParserSuccess<M, C>
21
- | ParserFailure;
22
- export type Parser<M, C extends string = string> = (
23
- input: string
24
- ) => ParserResult<M, C>;