Skip to content

Commit ebab1cf

Browse files
committed
Merge branch 'develop'
2 parents e3a710d + 8687d1b commit ebab1cf

13 files changed

+537
-152
lines changed

CHANGELOG.md

+10
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@
1111
1212
-->
1313

14+
## 0.4.0 (2022/07/24)
15+
16+
### Features
17+
- Add api: parser.parse
18+
- Add api: P.match
19+
- Add api: P.lazy
20+
21+
### Changes
22+
- Change api: parser.sep(separator, min) -> P.sep(item, separator, min)
23+
1424
## 0.3.0 (2022/07/18)
1525

1626
### Features

README.md

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
A Parsimmon-like, stateful parser-combinator library with TypeScript.
33
[Try it out!](https://npm.runkit.com/terrario)
44

5+
The terrario is a parser-combinator library inspired by PEG.js, Parsimmon, etc.
6+
57
[![NPM](https://nodei.co/npm/terrario.png?downloads=true&downloadRank=true&stars=true)](https://www.npmjs.com/package/terrario)
68

79
## Installation
@@ -10,8 +12,7 @@ npm i terrario
1012
```
1113

1214
## Documenation
13-
WIP
14-
[docs](https://github.com/marihachi/terrario/tree/develop/docs/index.md)
15+
[Docs](https://github.com/marihachi/terrario/tree/develop/docs/index.md)
1516

1617
## Basic Example
1718
```ts
@@ -22,7 +23,7 @@ const parser = P.str('hello world');
2223

2324
// parse the input string
2425
const input = 'hello world';
25-
const result = parser.handler(input, 0, {});
26+
const result = parser.parse(input, {});
2627

2728
// check errors
2829
if (!result.success) {

docs/api.md

+218-6
Original file line numberDiff line numberDiff line change
@@ -3,48 +3,260 @@
33
## P.str(value: string): Parser
44
Generates a new parser that consumes the input string using the specified string.
55

6+
```ts
7+
// [Equivalent PEG] "test"
8+
const parser = P.str('test');
9+
10+
const result = parser.parse('test');
11+
console.log(result);
12+
// => { success: true, value: 'test', index: 4 }
13+
```
14+
615
## P.regexp(pattern: Regexp): Parser
716
Generates a new parser that consumes the input string using the specified regular expression.
817

18+
```ts
19+
// [Equivalent PEG] [a-z]
20+
const parser = P.regexp(/[a-z]/);
21+
22+
const result = parser.parse('a');
23+
console.log(result);
24+
// => { success: true, value: 'a', index: 1 }
25+
```
26+
927
## P.seq(parsers: Parser[], select?: boolean): Parser
1028

29+
```ts
30+
// [Equivalent PEG] "a" "1"
31+
const parser = P.seq([
32+
P.str('a'),
33+
P.str('1'),
34+
]);
35+
36+
const result = parser.parse('a1');
37+
console.log(result);
38+
// => { success: true, value: [ 'a', '1' ], index: 2 }
39+
```
40+
41+
You can also select a result to be returned from all of them:
42+
```ts
43+
// [Equivalent PEG] value0:"a" value1:"1" { return value1; }
44+
const parser = P.seq([
45+
P.str('a'),
46+
P.str('1'),
47+
], 1);
48+
49+
const result = parser.parse('a1');
50+
console.log(result);
51+
// => { success: true, value: '1', index: 2 }
52+
```
53+
1154
## P.alt(parsers: Parser[]): Parser
1255

56+
```ts
57+
// [Equivalent PEG] "a" / "1"
58+
const parser = P.alt([
59+
P.str('a'),
60+
P.str('1'),
61+
]);
62+
63+
let result;
64+
65+
result = parser.parse('a');
66+
console.log(result);
67+
// => { success: true, value: 'a', index: 1 }
68+
69+
result = parser.parse('1');
70+
console.log(result);
71+
// => { success: true, value: '1', index: 1 }
72+
```
73+
74+
## P.sep(item: Parser, separator: Parser, min: number): Parser
75+
76+
```ts
77+
// [Equivalent PEG] head:"a" tail:("," @"a")* { return [head, ...tail]; }
78+
const parser = P.sep(P.str('a'), P.str(','), 1);
79+
80+
let result;
81+
82+
result = parser.parse('a');
83+
console.log(result);
84+
// => { success: true, value: [ 'a' ], index: 1 }
85+
86+
result = parser.parse('a,a');
87+
console.log(result);
88+
// => { success: true, value: [ 'a', 'a' ], index: 3 }
89+
```
90+
91+
## P.lazy(fn: () => Parser): Parser
92+
Generates a new parser that is lazy-evaluated.
93+
94+
## P.match(parser: Parser): Parser
95+
Generates a new parser to continue if the match is successful.
96+
The generated parser does not consume input.
97+
98+
```ts
99+
// TODO
100+
```
101+
13102
## P.notMatch(parser: Parser): Parser
14103
Generates a new parser to continue if the match fails.
15104
The generated parser does not consume input.
16105

106+
```ts
107+
// TODO
108+
```
17109

18110
# Parsers
19111

20-
## P.char
112+
## P.char: Parser
113+
Matches any character.
21114

22-
## P.cr
115+
```ts
116+
// [Equivalent PEG] .
117+
const parser = P.char;
23118

24-
## P.lf
119+
const result = parser.parse('a');
120+
console.log(result);
121+
// => { success: true, value: 'a', index: 1 }
122+
```
25123

26-
## P.newline
124+
## P.cr: Parser
125+
Matches `\r` (CR)
27126

127+
## P.lf: Parser
128+
Matches `\n` (LF)
129+
130+
## P.newline: Parser
131+
Matches `\r\n` or `\r` or `\n`
28132

29133
# Parser APIs
30134

135+
## parser.parse(input: string, state?: any): Result
136+
```ts
137+
const parser = P.str('a');
138+
139+
parser.parse('a');
140+
141+
// specify states
142+
parser.parse('a', { flag: true, count: 0 });
143+
```
144+
31145
## parser.map(fn: (value) => any): Parser
146+
```ts
147+
// [Equivalent PEG] value0:"a" value1:"b" value2:"c" { return [value0, value2]; }
148+
const parser = P.seq([
149+
P.str('a'),
150+
P.str('b'),
151+
P.str('c'),
152+
]).map(value => {
153+
return [value[0], value[2]];
154+
});
155+
156+
const result = parser.parse('abc');
157+
console.log(result);
158+
// => { success: true, value: [ 'a', 'c' ], index: 3 }
159+
```
32160

33161
## parser.text(): Parser
162+
```ts
163+
// [Equivalent PEG] "a" "b" "c" { return text(); }
164+
const parser = P.seq([
165+
P.str('a'),
166+
P.str('b'),
167+
P.str('c'),
168+
]).text();
169+
170+
const result = parser.parse('abc');
171+
console.log(result);
172+
// => { success: true, value: 'abc', index: 3 }
173+
```
34174

35175
## parser.many(min: number): Parser
36176

37-
## parser.sep(separator: Parser, min: number): Parser
177+
Matches 0 or more items:
178+
```ts
179+
// [Equivalent PEG] "abc"*
180+
const parser = P.str('abc').many(0);
181+
182+
let result;
183+
184+
result = parser.parse('');
185+
console.log(result);
186+
// => { success: true, value: [], index: 0 }
187+
188+
result = parser.parse('abc');
189+
console.log(result);
190+
// => { success: true, value: [ 'abc' ], index: 3 }
191+
```
192+
193+
Matches 1 or more items:
194+
```ts
195+
// [Equivalent PEG] "abc"+
196+
const parser = P.str('abc').many(1);
197+
198+
let result;
199+
200+
result = parser.parse('abc');
201+
console.log(result);
202+
// => { success: true, value: [ 'abc' ], index: 3 }
203+
204+
result = parser.parse('abcabc');
205+
console.log(result);
206+
// => { success: true, value: [ 'abc', 'abc' ], index: 6 }
207+
```
38208

39209
## parser.option(): Parser
40210
Generates a new parser that returns null even if the match fails.
41211

212+
```ts
213+
// [Equivalent PEG] "a" "b"?
214+
const parser = P.seq([
215+
P.str('a'),
216+
P.str('b').option(),
217+
]);
218+
219+
let result;
220+
221+
result = parser.parse('ab');
222+
console.log(result);
223+
// => { success: true, value: [ 'a', 'b' ], index: 2 }
224+
225+
result = parser.parse('a');
226+
console.log(result);
227+
// => { success: true, value: [ 'a', null ], index: 1 }
228+
```
42229

43230
# Other APIs
44231

45-
## P.createLanguage()
232+
## P.createLanguage(syntaxes: Record<string, (rules: Language) => Parser>): Language
46233
You can use createLanguage to create a set of syntax.
47234

235+
Each rule is lazy evaluated.
236+
237+
```ts
238+
const lang = P.createLanguage({
239+
root: rules => {
240+
return P.alt([
241+
rules.rule1,
242+
rules.rule2,
243+
]);
244+
},
245+
246+
rule1: rules => {
247+
return P.regexp('a');
248+
},
249+
250+
rule2: rules => {
251+
return P.regexp('b');
252+
},
253+
});
254+
255+
const result = lang.root.parse('a');
256+
console.log(result);
257+
// => { success: true, value: 'a', index: 1 }
258+
```
259+
48260
## P.success()
49261
for custom parser.
50262

examples/json/cli.js

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
const { performance } = require('perf_hooks');
2+
const readLine = require('readline');
3+
const { parse } = require('./built/index');
4+
5+
6+
class InputCanceledError extends Error {
7+
constructor(message) {
8+
super(message);
9+
}
10+
}
11+
12+
function inputLine(message) {
13+
return new Promise((resolve, reject) => {
14+
const rl = readLine.createInterface(process.stdin, process.stdout);
15+
rl.question(message, (ans) => {
16+
rl.close();
17+
resolve(ans);
18+
});
19+
rl.on('SIGINT', () => {
20+
console.log('');
21+
rl.close();
22+
reject(new InputCanceledError('SIGINT interrupted'));
23+
});
24+
});
25+
}
26+
27+
async function entryPoint() {
28+
while (true) {
29+
let input;
30+
try {
31+
input = await inputLine('> ');
32+
}
33+
catch (err) {
34+
if (err instanceof InputCanceledError) {
35+
return;
36+
}
37+
throw err;
38+
}
39+
40+
input = input
41+
.replace(/\\n/g, '\n')
42+
.replace(/\\t/g, '\t')
43+
.replace(/\\u00a0/g, '\u00a0');
44+
45+
try {
46+
const parseTimeStart = performance.now();
47+
const result = parse(input);
48+
const parseTimeEnd = performance.now();
49+
console.log(JSON.stringify(result, null, ' '));
50+
const parseTime = (parseTimeEnd - parseTimeStart).toFixed(3);
51+
console.log(`parsing time: ${parseTime}ms`);
52+
}
53+
catch (err) {
54+
console.error(err);
55+
}
56+
console.log();
57+
}
58+
}
59+
60+
entryPoint().catch(err => {
61+
console.log(err);
62+
process.exit(1);
63+
});

examples/json/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "json",
33
"version": "0.1.0",
44
"private": true,
5-
"main": "./built/index.js",
5+
"main": "./cli.js",
66
"scripts": {
77
"build": "tsc",
88
"start": "node ."
@@ -12,6 +12,6 @@
1212
"typescript": "4.7.4"
1313
},
1414
"dependencies": {
15-
"terrario": "0.3.0"
15+
"terrario": "0.4.0"
1616
}
1717
}

0 commit comments

Comments
 (0)