-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathquasi-peg.js.ts
143 lines (129 loc) · 4.66 KB
/
quasi-peg.js.ts
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// PEG quasi Grammar for PEG quasi Grammars
// Michael FIG <[email protected]>, 2019-01-05
//
// This grammar is adapted from:
// http://piumarta.com/software/peg/peg-0.1.18/src/peg.peg
//
// Modified for Jessica to support:
// Semantic actions provided in tagged template HOLEs
// '~' for negative lookahead (instead of '!')
// ';' terminator for definitions
// '**' and '++' for separators
// 'super.RULE' syntax for extended grammars
// '\xFF' instead of octal character literals
// which are adapted from:
// https://github.com/erights/quasiParserGenerator
/// <reference path="peg.d.ts"/>
const makePeg = <T = IPegTag<any>, U = IPegTag<IParserTag<any>>>(
pegTag: IBootPegTag<T>,
metaCompile: (defs: PegDef[]) => (..._: any[]) => U) => {
const {ACCEPT, HOLE, SKIP} = pegTag;
function simple(prefix: string, list: PegExpr[]) {
if (list.length === 0) { return ['empty']; }
if (list.length === 1) { return list[0]; }
return [prefix, ...list];
}
function flatArgs(args: PegExpr[]) {
return args.reduce<PegExpr[]>((prior, a) => {
prior.push(...flatSeq(a));
return prior;
}, []);
}
function flatSeq(term: PegExpr): PegExpr[] {
if (Array.isArray(term)) {
if (term.length === 0) {
return [];
}
const [kind, ...terms] = term;
if (kind === 'seq') {
return flatArgs(terms);
} else if (terms.length === 0 && Array.isArray(kind)) {
return flatSeq(kind);
} else {
return [[kind, ...flatArgs(terms)]];
}
}
return [term];
}
return pegTag`
# Hierarchical syntax
Grammar <- _Spacing Definition+ _EndOfFile
${metaCompile};
Definition <- Identifier LEFTARROW Expression SEMI &${ACCEPT}
${(i, _, e, _2) => ['def', i, e]};
Expression <- Sequence ** _SLASH
${list => simple('or', list)};
Sequence <- (Prefix*
${list => simple('seq', list)})
HOLE?
${(seq, optHole) => optHole.length === 0 ?
['val0', ...flatSeq(seq)] :
['act', optHole[0], ...flatSeq(seq)]};
Prefix <- AND HOLE
${(_, a) => ['pred', a]}
/ AND Suffix
${(_, s) => ['peek', s]}
/ NOT Suffix
${(_, s) => ['peekNot', s]}
/ Suffix;
Suffix <- Primary (STARSTAR
/ PLUSPLUS) Primary
${(patt, q, sep) => [q, patt, sep]}
/ Primary (QUESTION
/ STAR
/ PLUS)
${(patt, optQ) => [optQ[0], patt]}
/ Primary;
Primary <- Super
/ Identifier ~LEFTARROW
/ OPEN Expression CLOSE
${(_, e, _2) => e}
/ Literal
${(s) => ['lit', s]}
/ Class
${(c) => ['cls', c]}
/ DOT
${() => ['dot']}
/ BEGIN
${() => ['begin']}
/ END
${() => ['end']}
;
Super <- 'super.' Identifier
${(_, i) => ['super', i]};
# Lexical syntax
Identifier <- < IdentStart IdentCont* > _Spacing;
IdentStart <- [a-zA-Z_];
IdentCont <- IdentStart / [0-9];
Literal <- ['] < (~['] Char )* > ['] _Spacing
/ ["] < (~["] Char )* > ["] _Spacing;
Class <- '[' < (~']' Range)* > ']' _Spacing;
Range <- Char '-' Char / Char;
Char <- '\\' [abefnrtv'"\[\]\\\`\$]
/ '\\x' [0-9a-fA-F][0-9a-fA-F]
/ '\\' '-'
/ ~'\\' .;
LEFTARROW <- '<-' _Spacing;
_SLASH <- '/' _Spacing ${_ => SKIP};
SEMI <- ';' _Spacing;
AND <- '&' _Spacing;
NOT <- '~' _Spacing;
QUESTION <- '?' _Spacing;
STAR <- '*' _Spacing;
PLUS <- '+' _Spacing;
OPEN <- '(' _Spacing;
CLOSE <- ')' _Spacing;
DOT <- '.' _Spacing;
_Spacing <- (Space / Comment)* ${_ => SKIP};
Comment <- '#' (~EndOfLine .)* EndOfLine;
Space <- ' ' / '\t' / EndOfLine;
EndOfLine <- '\r\n' / '\n' / '\r';
_EndOfFile <- ~.;
HOLE <- &${HOLE} _Spacing;
BEGIN <- '<' _Spacing;
END <- '>' _Spacing;
PLUSPLUS <- '++' _Spacing;
STARSTAR <- '**' _Spacing;
`;
};
export default makePeg;