-
Notifications
You must be signed in to change notification settings - Fork 9
/
parser.js
138 lines (127 loc) · 4.03 KB
/
parser.js
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
"use strict";
var punctuation = require("./punctuation");
// builds a string parser from a streaming character parser
exports.makeParser = makeParser;
function makeParser(production, errorHandler) {
var errorHandler = errorHandler || function (error, text) {
throw new Error(error + " while parsing " + JSON.stringify(text));
};
return function (text /*, ...args*/) {
// the parser is a monadic state machine.
// each state is represented by a function that accepts
// a character. parse functions accept a callback (for forwarding the
// result) and return a state.
text = text.trim();
var result;
var state = production.apply(null, [function (_result) {
result = _result;
return expectEof(function (error) {
return errorHandler(error, text);
});
}].concat(Array.prototype.slice.call(arguments, 1)));
// drive the state machine
Array.prototype.forEach.call(text, function (letter, i) {
state = state(letter);
});
// break break break
while (!result) {
state = state(""); // EOF
}
return result;
};
}
function expectEof(errback) {
return function (character) {
if (character !== "") {
errback("Unexpected " + JSON.stringify(character));
}
return function noop() {
return noop;
};
};
}
exports.makeExpect = makeExpect;
function makeExpect(expected) {
return function (callback) {
return function (character) {
if (character === expected) {
return callback(character);
} else {
return callback()(character);
}
};
};
}
exports.makeParseSome = makeParseSome;
function makeParseSome(parseOne) {
var parseSome = function (callback) {
return parseOne(function (one) {
if (one != null) {
return parseRemaining(callback, [one]);
} else {
return callback([]);
}
});
};
var parseRemaining = makeParseAny(parseOne);
return parseSome;
}
exports.makeParseAny = makeParseAny;
function makeParseAny(parseOne) {
return function parseRemaining(callback, any) {
any = any || [];
return parseOne(function (one) {
if (one != null) {
return parseRemaining(callback, any.concat([one]));
} else {
return callback(any);
}
});
};
}
exports.makeDelimitedParser = makeDelimitedParser;
function makeDelimitedParser(parsePrevious, parseDelimiter) {
return function parseSelf(callback, options, terms) {
terms = terms || [];
return parsePrevious(function (term) {
if (!term.length) {
return callback(terms);
} else {
terms = terms.concat([term]);
return parseDelimiter(function (delimiter) {
if (delimiter) {
return parseSelf(callback, options, terms);
} else {
return callback(terms);
}
});
}
}, options);
}
}
// used by parsers to determine whether the cursor is on a word break
exports.isBreak = isBreak;
function isBreak(character) {
return character === " " || character === "\n" || character === "";
}
exports.isFinal = isFinal;
function isFinal(character) {
return isBreak(character) || punctuation[character];
}
// used by multiple modes
exports.countPrimes = countPrimes;
function countPrimes(callback, primes, rewind) {
primes = primes || 0;
rewind = rewind || function (state) {
return state;
};
return function (character) {
if (character === "`") {
return countPrimes(callback, primes + 1, function (state) {
return rewind(state)("`");
});
} else {
return callback(primes, rewind)(character);
}
};
}