-
Notifications
You must be signed in to change notification settings - Fork 62
Expand file tree
/
Copy pathparse.ts
More file actions
98 lines (95 loc) · 3.23 KB
/
parse.ts
File metadata and controls
98 lines (95 loc) · 3.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import { lossyNormalize } from '../format/format.js';
import type { ParseResult } from '../lib.js';
import { P } from './parsimmon.js';
/* eslint-disable sort-keys, @typescript-eslint/naming-convention, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access */
const cashAssemblyParser = P.createLanguage({
script: (r) =>
P.seqMap(
P.optWhitespace,
r.expression.sepBy(P.optWhitespace),
P.optWhitespace,
(_, expressions) => expressions,
).node('Script'),
expression: (r) =>
P.alt(
r.comment,
r.push,
r.evaluation,
r.utf8,
r.binary,
r.hex,
r.bigint,
r.identifier,
),
comment: (r) =>
P.alt(r.singleLineComment, r.multiLineComment).node('Comment'),
singleLineComment: () =>
P.seqMap(
P.string('//').desc("the start of a single-line comment ('//')"),
P.regexp(/[^\n]*/u),
(__, comment) => comment.trim(),
),
multiLineComment: () =>
P.seqMap(
P.string('/*').desc("the start of a multi-line comment ('/*')"),
P.regexp(/[\s\S]*?\*\//u).desc(
"the end of this multi-line comment ('*/')",
),
(__, comment) => comment.slice(0, -'*/'.length).trim(),
),
push: (r) =>
P.seqMap(
P.string('<').desc("the start of a push statement ('<')"),
r.script,
P.string('>').desc("the end of this push statement ('>')"),
(_, push) => push,
).node('Push'),
evaluation: (r) =>
P.seqMap(
P.string('$').desc("the start of an evaluation ('$')"),
P.string('(').desc("the opening parenthesis of this evaluation ('(')"),
r.script,
P.string(')').desc("the closing parenthesis of this evaluation (')')"),
(_, __, evaluation) => evaluation,
).node('Evaluation'),
identifier: () =>
P.regexp(/[a-zA-Z_][.a-zA-Z0-9_-]*/u)
.desc('a valid identifier')
.node('Identifier'),
utf8: () =>
P.alt(
P.seqMap(
P.string('"').desc('a double quote (")'),
P.regexp(/[^"]*/u),
P.string('"').desc('a closing double quote (")'),
(__, literal) => literal,
),
P.seqMap(
P.string("'").desc("a single quote (')"),
P.regexp(/[^']*/u),
P.string("'").desc("a closing single quote (')"),
(__, literal) => literal,
),
).node('UTF8Literal'),
hex: () =>
P.seqMap(
P.string('0x').desc("a hex literal ('0x...')"),
P.regexp(/[0-9a-f]_*(?:_*[0-9a-f]_*[0-9a-f]_*)*[0-9a-f]/iu).desc(
'a valid hexadecimal string',
),
(__, literal) => literal,
).node('HexLiteral'),
binary: () =>
P.seqMap(
P.string('0b').desc("a binary literal ('0b...')"),
P.regexp(/[01]+(?:[01_]*[01]+)*/iu).desc('a string of binary digits'),
(__, literal) => literal,
).node('BinaryLiteral'),
bigint: () =>
P.regexp(/-?[0-9]+(?:[0-9_]*[0-9]+)*/u)
.desc('an integer literal')
.node('BigIntLiteral'),
});
/* eslint-enable sort-keys, @typescript-eslint/naming-convention, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access */
export const parseScript = (script: string) =>
cashAssemblyParser.script.parse(lossyNormalize(script)) as ParseResult;