@retorquere/bibtex-parser 7.0.16 → 8.0.1
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.
- package/grammar.js +107 -70
- package/index.d.ts +114 -221
- package/index.js +839 -1318
- package/package.json +30 -21
- package/sentence-case.d.ts +32 -1
- package/sentence-case.js +192 -79
- package/unabbrev.json +743 -749
- package/verbatim.d.ts +75 -0
- package/{chunker.js → verbatim.js} +203 -129
- package/chunker.d.ts +0 -60
package/verbatim.d.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export type Entry = {
|
|
2
|
+
input: string;
|
|
3
|
+
type: string;
|
|
4
|
+
key: string;
|
|
5
|
+
fields: Record<string, string>;
|
|
6
|
+
};
|
|
7
|
+
export interface ParseError {
|
|
8
|
+
error: string;
|
|
9
|
+
input: string;
|
|
10
|
+
location?: {
|
|
11
|
+
line: number;
|
|
12
|
+
column: number;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export interface ParserOptions {
|
|
16
|
+
/**
|
|
17
|
+
* stop parsing after `max_entries` entries have been found. Useful for quick detection if a text file is in fact a bibtex file
|
|
18
|
+
*/
|
|
19
|
+
max_entries?: number;
|
|
20
|
+
}
|
|
21
|
+
declare class Parser {
|
|
22
|
+
parsing: string;
|
|
23
|
+
entries: Entry[];
|
|
24
|
+
strings: Record<string, string>;
|
|
25
|
+
comments: string[];
|
|
26
|
+
errors: ParseError[];
|
|
27
|
+
preambles: string[];
|
|
28
|
+
private default_strings;
|
|
29
|
+
/**
|
|
30
|
+
* error found, if any.
|
|
31
|
+
*/
|
|
32
|
+
error?: string;
|
|
33
|
+
private pos;
|
|
34
|
+
private linebreaks;
|
|
35
|
+
private input;
|
|
36
|
+
private max_entries;
|
|
37
|
+
private keywords;
|
|
38
|
+
constructor(input: string, options?: ParserOptions);
|
|
39
|
+
location(pos: number): {
|
|
40
|
+
line: number;
|
|
41
|
+
column: number;
|
|
42
|
+
};
|
|
43
|
+
parse(): void;
|
|
44
|
+
parseAsync(): Promise<void>;
|
|
45
|
+
private isWhitespace;
|
|
46
|
+
private match;
|
|
47
|
+
private tryMatch;
|
|
48
|
+
private skipWhitespace;
|
|
49
|
+
private value_braces;
|
|
50
|
+
private value_quotes;
|
|
51
|
+
private single_value;
|
|
52
|
+
private value;
|
|
53
|
+
private key;
|
|
54
|
+
private key_equals_value;
|
|
55
|
+
private entry;
|
|
56
|
+
private directive;
|
|
57
|
+
private string;
|
|
58
|
+
private preamble;
|
|
59
|
+
private comment;
|
|
60
|
+
private hasMore;
|
|
61
|
+
private bibtex;
|
|
62
|
+
private bibtexAsync;
|
|
63
|
+
private matchGuard;
|
|
64
|
+
private parseNext;
|
|
65
|
+
}
|
|
66
|
+
export type Bibliography = Parser;
|
|
67
|
+
/**
|
|
68
|
+
* Parses a bibtex source in verbatim mode. Good for detection of bibtex and for later LaTeX-reparsing.
|
|
69
|
+
*/
|
|
70
|
+
export declare function parse(input: string, options?: ParserOptions): Bibliography;
|
|
71
|
+
export declare const promises: {
|
|
72
|
+
parse(input: string, options?: ParserOptions): Promise<Bibliography>;
|
|
73
|
+
};
|
|
74
|
+
export {};
|
|
75
|
+
//# sourceMappingURL=verbatim.d.ts.map
|
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
// CommonJS port by Mikola Lysenko 2013
|
|
6
6
|
//
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.promises = exports.
|
|
9
|
-
class
|
|
8
|
+
exports.promises = exports.parse = void 0;
|
|
9
|
+
class ParsingError extends Error {
|
|
10
10
|
constructor(message, parser) {
|
|
11
11
|
message += ` @ ${parser.pos}`;
|
|
12
12
|
if (parser.parsing)
|
|
13
13
|
message += ` in ${JSON.stringify(parser.parsing)}`;
|
|
14
14
|
super(message);
|
|
15
|
-
this.name = '
|
|
15
|
+
this.name = 'ParsingError';
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
// eslint-disable-next-line prefer-template
|
|
@@ -28,61 +28,140 @@ const letter = new RegExp('[' + [
|
|
|
28
28
|
// Letter, Other
|
|
29
29
|
/\u00AA\u00BA\u01BB\u01C0-\u01C3\u0294\u05D0-\u05EA\u05F0-\u05F2\u0620-\u063F\u0641-\u064A\u066E-\u066F\u0671-\u06D3\u06D5\u06EE-\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u0800-\u0815\u0840-\u0858\u08A0-\u08B4\u0904-\u0939\u093D\u0950\u0958-\u0961\u0972-\u0980\u0985-\u098C\u098F-\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC-\u09DD\u09DF-\u09E1\u09F0-\u09F1\u0A05-\u0A0A\u0A0F-\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32-\u0A33\u0A35-\u0A36\u0A38-\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2-\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0-\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F-\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32-\u0B33\u0B35-\u0B39\u0B3D\u0B5C-\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99-\u0B9A\u0B9C\u0B9E-\u0B9F\u0BA3-\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60-\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0-\u0CE1\u0CF1-\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32-\u0E33\u0E40-\u0E45\u0E81-\u0E82\u0E84\u0E87-\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA-\u0EAB\u0EAD-\u0EB0\u0EB2-\u0EB3\u0EBD\u0EC0-\u0EC4\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065-\u1066\u106E-\u1070\u1075-\u1081\u108E\u10D0-\u10FA\u10FD-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17DC\u1820-\u1842\u1844-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE-\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C77\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5-\u1CF6\u2135-\u2138\u2D30-\u2D67\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3006\u303C\u3041-\u3096\u309F\u30A1-\u30FA\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA014\uA016-\uA48C\uA4D0-\uA4F7\uA500-\uA60B\uA610-\uA61F\uA62A-\uA62B\uA66E\uA6A0-\uA6E5\uA78F\uA7F7\uA7FB-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9E0-\uA9E4\uA9E7-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA6F\uAA71-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5-\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADC\uAAE0-\uAAEA\uAAF2\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40-\uFB41\uFB43-\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF66-\uFF6F\uFF71-\uFF9D\uFFA0-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC/.source,
|
|
30
30
|
].join('') + ']');
|
|
31
|
-
class
|
|
32
|
-
|
|
33
|
-
this.input = input;
|
|
34
|
-
this.pos = 0;
|
|
31
|
+
class Parser {
|
|
32
|
+
constructor(input, options = {}) {
|
|
35
33
|
this.entries = [];
|
|
36
34
|
this.strings = {};
|
|
35
|
+
this.comments = [];
|
|
36
|
+
this.errors = [];
|
|
37
|
+
this.preambles = [];
|
|
38
|
+
this.default_strings = {
|
|
39
|
+
JAN: '01',
|
|
40
|
+
JANUARY: '01',
|
|
41
|
+
FEB: '02',
|
|
42
|
+
FEBRUARY: '02',
|
|
43
|
+
MAR: '03',
|
|
44
|
+
MARCH: '03',
|
|
45
|
+
APR: '04',
|
|
46
|
+
APRIL: '04',
|
|
47
|
+
MAY: '05',
|
|
48
|
+
JUN: '06',
|
|
49
|
+
JUNE: '06',
|
|
50
|
+
JUL: '07',
|
|
51
|
+
JULY: '07',
|
|
52
|
+
AUG: '08',
|
|
53
|
+
AUGUST: '08',
|
|
54
|
+
SEP: '09',
|
|
55
|
+
SEPTEMBER: '09',
|
|
56
|
+
OCT: '10',
|
|
57
|
+
OCTOBER: '10',
|
|
58
|
+
NOV: '11',
|
|
59
|
+
NOVEMBER: '11',
|
|
60
|
+
DEC: '12',
|
|
61
|
+
DECEMBER: '12',
|
|
62
|
+
ACMCS: 'ACM Computing Surveys',
|
|
63
|
+
ACTA: 'Acta Informatica',
|
|
64
|
+
CACM: 'Communications of the ACM',
|
|
65
|
+
IBMJRD: 'IBM Journal of Research and Development',
|
|
66
|
+
IBMSJ: 'IBM Systems Journal',
|
|
67
|
+
IEEESE: 'IEEE Transactions on Software Engineering',
|
|
68
|
+
IEEETC: 'IEEE Transactions on Computers',
|
|
69
|
+
IEEETCAD: 'IEEE Transactions on Computer-Aided Design of Integrated Circuits',
|
|
70
|
+
IPL: 'Information Processing Letters',
|
|
71
|
+
JACM: 'Journal of the ACM',
|
|
72
|
+
JCSS: 'Journal of Computer and System Sciences',
|
|
73
|
+
SCP: 'Science of Computer Programming',
|
|
74
|
+
SICOMP: 'SIAM Journal on Computing',
|
|
75
|
+
TOCS: 'ACM Transactions on Computer Systems',
|
|
76
|
+
TODS: 'ACM Transactions on Database Systems',
|
|
77
|
+
TOG: 'ACM Transactions on Graphics',
|
|
78
|
+
TOMS: 'ACM Transactions on Mathematical Software',
|
|
79
|
+
TOOIS: 'ACM Transactions on Office Information Systems',
|
|
80
|
+
TOPLAS: 'ACM Transactions on Programming Languages and Systems',
|
|
81
|
+
TCS: 'Theoretical Computer Science',
|
|
82
|
+
};
|
|
83
|
+
this.pos = 0;
|
|
84
|
+
this.linebreaks = [];
|
|
85
|
+
this.max_entries = options.max_entries || 0;
|
|
86
|
+
this.input = input;
|
|
37
87
|
this.parsing = null;
|
|
38
|
-
this.
|
|
39
|
-
|
|
88
|
+
this.keywords = 0;
|
|
89
|
+
let pos = input.indexOf('\n');
|
|
90
|
+
while (pos !== -1) {
|
|
91
|
+
this.linebreaks.push(pos);
|
|
92
|
+
pos = input.indexOf('\n', pos + 1);
|
|
93
|
+
}
|
|
40
94
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
95
|
+
location(pos) {
|
|
96
|
+
let line = 1;
|
|
97
|
+
let column = 1;
|
|
98
|
+
for (const linebreak of this.linebreaks) {
|
|
99
|
+
if (pos < linebreak)
|
|
100
|
+
break;
|
|
101
|
+
line++;
|
|
102
|
+
column = pos - linebreak;
|
|
103
|
+
}
|
|
104
|
+
return { line, column };
|
|
105
|
+
}
|
|
106
|
+
parse() {
|
|
44
107
|
this.bibtex();
|
|
45
|
-
|
|
108
|
+
this.entries.reverse();
|
|
46
109
|
}
|
|
47
|
-
async parseAsync(
|
|
48
|
-
this.reset(input);
|
|
49
|
-
this.max_entries = options.max_entries || 0;
|
|
110
|
+
async parseAsync() {
|
|
50
111
|
await this.bibtexAsync();
|
|
51
|
-
|
|
112
|
+
this.entries.reverse();
|
|
52
113
|
}
|
|
53
114
|
isWhitespace(s, horizontalOnly = false) {
|
|
54
115
|
return (s === ' ' || s === '\t' || (!horizontalOnly && (s === '\r' || s === '\n')));
|
|
55
116
|
}
|
|
56
|
-
match(s) {
|
|
117
|
+
match(s, sws = true) {
|
|
57
118
|
this.skipWhitespace();
|
|
58
119
|
if (this.input.substr(this.pos, s.length) !== s) {
|
|
59
|
-
throw new
|
|
120
|
+
throw new ParsingError(`Token mismatch, expected ${JSON.stringify(s)}, found ${JSON.stringify(this.input.substr(this.pos, 20))}...`, this); // eslint-disable-line no-magic-numbers
|
|
60
121
|
}
|
|
61
122
|
this.pos += s.length;
|
|
62
|
-
|
|
123
|
+
if (sws)
|
|
124
|
+
this.skipWhitespace();
|
|
63
125
|
}
|
|
64
|
-
tryMatch(s) {
|
|
126
|
+
tryMatch(s, consume = false) {
|
|
65
127
|
this.skipWhitespace();
|
|
66
|
-
|
|
128
|
+
const match = (this.input.substr(this.pos, s.length) === s);
|
|
129
|
+
if (match && consume)
|
|
130
|
+
this.pos += s.length;
|
|
131
|
+
return match;
|
|
67
132
|
// this.skipWhitespace()
|
|
68
133
|
}
|
|
69
134
|
skipWhitespace() {
|
|
70
|
-
while (this.pos < this.input.length && this.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
135
|
+
while (this.pos < this.input.length && this.input[this.pos].match(/[ \t\r\n%]/)) {
|
|
136
|
+
// test for '%' is shady
|
|
137
|
+
if (this.input[this.pos] === '%') {
|
|
138
|
+
while (this.pos < this.input.length && this.input[this.pos] !== '\n')
|
|
139
|
+
this.pos++;
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
77
142
|
this.pos++;
|
|
143
|
+
}
|
|
78
144
|
}
|
|
79
145
|
}
|
|
146
|
+
/*
|
|
147
|
+
private fix_path() {
|
|
148
|
+
// workaround for https://github.com/siefkenj/unified-latex/issues/94
|
|
149
|
+
const path = '\\path|'
|
|
150
|
+
const verb = path.replace('path', 'verb')
|
|
151
|
+
if (this.input.substring(this.pos).startsWith(path)) {
|
|
152
|
+
console.log('replacing path')
|
|
153
|
+
// eslint-disable-next-line prefer-template, no-magic-numbers
|
|
154
|
+
this.input = this.input.substring(0, this.pos) + verb + this.input.substring(this.pos + path.length)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
*/
|
|
80
158
|
value_braces() {
|
|
81
159
|
let bracecount = 0;
|
|
82
|
-
this.match('{');
|
|
160
|
+
this.match('{', false);
|
|
83
161
|
const start = this.pos;
|
|
84
162
|
let math = false;
|
|
85
163
|
while (true) { // eslint-disable-line no-constant-condition
|
|
164
|
+
// this.fix_path()
|
|
86
165
|
switch (this.input[this.pos]) {
|
|
87
166
|
case '\\':
|
|
88
167
|
this.pos += 1;
|
|
@@ -93,7 +172,7 @@ class BibtexParser {
|
|
|
93
172
|
case '}':
|
|
94
173
|
if (bracecount === 0) {
|
|
95
174
|
if (math)
|
|
96
|
-
throw new
|
|
175
|
+
throw new ParsingError('Unclosed math section', this);
|
|
97
176
|
this.pos++;
|
|
98
177
|
return this.input.substring(start, this.pos - 1);
|
|
99
178
|
}
|
|
@@ -105,15 +184,16 @@ class BibtexParser {
|
|
|
105
184
|
}
|
|
106
185
|
this.pos++;
|
|
107
186
|
if (this.pos >= this.input.length) {
|
|
108
|
-
throw new
|
|
187
|
+
throw new ParsingError(`Unterminated brace-value ${JSON.stringify(this.input.substr(start, 20))}`, this); // eslint-disable-line no-magic-numbers
|
|
109
188
|
}
|
|
110
189
|
}
|
|
111
190
|
}
|
|
112
191
|
value_quotes() {
|
|
113
|
-
this.match('"');
|
|
192
|
+
this.match('"', false);
|
|
114
193
|
const start = this.pos;
|
|
115
194
|
let bracecount = 0;
|
|
116
195
|
while (true) { // eslint-disable-line no-constant-condition
|
|
196
|
+
// this.fix_path()
|
|
117
197
|
switch (this.input[this.pos]) {
|
|
118
198
|
case '\\':
|
|
119
199
|
this.pos += 1;
|
|
@@ -132,7 +212,7 @@ class BibtexParser {
|
|
|
132
212
|
}
|
|
133
213
|
this.pos++;
|
|
134
214
|
if (this.pos >= this.input.length) {
|
|
135
|
-
throw new
|
|
215
|
+
throw new ParsingError(`Unterminated quote-value ${JSON.stringify(this.input.substr(start, 20))}`, this); // eslint-disable-line no-magic-numbers
|
|
136
216
|
}
|
|
137
217
|
}
|
|
138
218
|
}
|
|
@@ -145,7 +225,18 @@ class BibtexParser {
|
|
|
145
225
|
}
|
|
146
226
|
else {
|
|
147
227
|
const bare = this.key();
|
|
148
|
-
|
|
228
|
+
if (bare.match(/^\d+$/))
|
|
229
|
+
return bare;
|
|
230
|
+
const u_bare = bare.toUpperCase();
|
|
231
|
+
const resolved = this.strings[u_bare] || this.default_strings[u_bare];
|
|
232
|
+
if (typeof resolved === 'undefined') {
|
|
233
|
+
this.errors.push({
|
|
234
|
+
error: `Unresolved @string reference ${JSON.stringify(bare)}`,
|
|
235
|
+
location: this.location(this.pos),
|
|
236
|
+
input: bare,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
return resolved || `{{${bare}}}`;
|
|
149
240
|
}
|
|
150
241
|
}
|
|
151
242
|
value() {
|
|
@@ -157,16 +248,13 @@ class BibtexParser {
|
|
|
157
248
|
}
|
|
158
249
|
return values.join('');
|
|
159
250
|
}
|
|
160
|
-
key(
|
|
251
|
+
key(allow = '') {
|
|
161
252
|
const start = this.pos;
|
|
162
253
|
while (true) { // eslint-disable-line no-constant-condition
|
|
163
254
|
if (this.pos === this.input.length) {
|
|
164
|
-
throw new
|
|
255
|
+
throw new ParsingError('Runaway key', this);
|
|
165
256
|
}
|
|
166
|
-
if (this.input[this.pos].match(/['a-zA-Z0-9&;_:\\./-]/)) {
|
|
167
|
-
this.pos++;
|
|
168
|
-
}
|
|
169
|
-
else if (allowUnicode && this.input[this.pos].match(letter)) {
|
|
257
|
+
if (this.input[this.pos].match(/[+'a-zA-Z0-9&;_:\\./-]/) || this.input[this.pos].match(letter) || allow.includes(this.input[this.pos])) {
|
|
170
258
|
this.pos++;
|
|
171
259
|
}
|
|
172
260
|
else {
|
|
@@ -174,30 +262,56 @@ class BibtexParser {
|
|
|
174
262
|
}
|
|
175
263
|
}
|
|
176
264
|
}
|
|
177
|
-
key_equals_value() {
|
|
178
|
-
|
|
265
|
+
key_equals_value(key) {
|
|
266
|
+
key = key || this.key();
|
|
267
|
+
if (!key)
|
|
268
|
+
return; // no key found, stray comma
|
|
179
269
|
if (!this.tryMatch('=')) {
|
|
180
|
-
throw new
|
|
270
|
+
throw new ParsingError(`${key} = value expected, equals sign missing: ${JSON.stringify(this.input.substr(this.pos, 20))}...`, this); // eslint-disable-line no-magic-numbers
|
|
181
271
|
}
|
|
182
272
|
this.match('=');
|
|
183
273
|
const val = this.value();
|
|
184
274
|
if (this.parsing === 'string') {
|
|
185
|
-
this.strings[key.
|
|
275
|
+
this.strings[key.toUpperCase()] = val;
|
|
186
276
|
}
|
|
187
277
|
else {
|
|
278
|
+
key = key.toLowerCase();
|
|
279
|
+
if (key.toLowerCase() === 'keywords' && this.entries[0].fields.keywords) { // #873... the things people do with bibtex
|
|
280
|
+
this.keywords++;
|
|
281
|
+
key = `keywords[${this.keywords}]`;
|
|
282
|
+
}
|
|
188
283
|
this.entries[0].fields[key] = val;
|
|
189
284
|
}
|
|
190
285
|
}
|
|
191
|
-
entry(
|
|
192
|
-
|
|
193
|
-
|
|
286
|
+
entry(d, guard) {
|
|
287
|
+
if (this.tryMatch(guard))
|
|
288
|
+
return; // empty entry
|
|
289
|
+
let key;
|
|
290
|
+
// mendeley end endnote can output entries without key... sure...
|
|
291
|
+
if (this.tryMatch(',')) {
|
|
292
|
+
// mendeley absurdity which outputs keyless entries, but at least have the decency to put a comma there
|
|
293
|
+
key = '';
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
// so maybe a real key?
|
|
297
|
+
key = this.key('*"');
|
|
298
|
+
}
|
|
299
|
+
if (this.tryMatch('=')) {
|
|
300
|
+
// nope; endnote just skips the comma; we can infer we're in this moronic situation because we've been dumped into field-parsing
|
|
301
|
+
this.key_equals_value(key);
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
// so we did find the real key earlier
|
|
305
|
+
this.entries[0].key = key;
|
|
306
|
+
}
|
|
307
|
+
this.tryMatch(',', true);
|
|
308
|
+
if (this.tryMatch(guard))
|
|
309
|
+
return; // no fields
|
|
194
310
|
this.key_equals_value();
|
|
195
|
-
while (this.tryMatch(',')) {
|
|
196
|
-
this.match(',');
|
|
311
|
+
while (this.tryMatch(',', true)) {
|
|
197
312
|
// fixes problems with commas at the end of a list
|
|
198
|
-
if (this.tryMatch(
|
|
199
|
-
|
|
200
|
-
}
|
|
313
|
+
if (this.tryMatch(guard))
|
|
314
|
+
return;
|
|
201
315
|
this.key_equals_value();
|
|
202
316
|
}
|
|
203
317
|
}
|
|
@@ -209,27 +323,21 @@ class BibtexParser {
|
|
|
209
323
|
this.key_equals_value();
|
|
210
324
|
}
|
|
211
325
|
preamble() {
|
|
212
|
-
this.value();
|
|
326
|
+
this.preambles.push(this.value());
|
|
213
327
|
}
|
|
214
328
|
comment() {
|
|
215
329
|
while (this.isWhitespace(this.input[this.pos], true))
|
|
216
330
|
this.pos++;
|
|
217
331
|
if (this.input[this.pos] === '{') {
|
|
218
|
-
this.value_braces();
|
|
219
|
-
|
|
332
|
+
this.comments.push(this.value_braces());
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
const start = this.pos;
|
|
336
|
+
while (this.input[this.pos] !== '\n' && this.pos < this.input.length)
|
|
337
|
+
this.pos++;
|
|
338
|
+
this.comments.push(this.input.substring(start, this.pos));
|
|
220
339
|
}
|
|
221
|
-
while (this.input[this.pos] !== '\n' && this.pos < this.input.length)
|
|
222
|
-
this.pos++;
|
|
223
|
-
}
|
|
224
|
-
/*
|
|
225
|
-
private progress() {
|
|
226
|
-
const progress = Math.round((this.pos / this.input.length * 100) / 5) * 5 // eslint-disable-line no-magic-numbers
|
|
227
|
-
if (this._progress !== progress) {
|
|
228
|
-
this._progress = progress
|
|
229
|
-
process.stdout.write(` (${this._progress}%) `)
|
|
230
|
-
}
|
|
231
340
|
}
|
|
232
|
-
*/
|
|
233
341
|
hasMore() {
|
|
234
342
|
if (this.max_entries && this.entries.length >= this.max_entries)
|
|
235
343
|
return false;
|
|
@@ -251,22 +359,23 @@ class BibtexParser {
|
|
|
251
359
|
return guard[1];
|
|
252
360
|
}
|
|
253
361
|
}
|
|
254
|
-
throw new
|
|
362
|
+
throw new ParsingError(`Token mismatch, expected '{' or '(', found ${JSON.stringify(this.input.substr(this.pos, 20))}...`, this); // eslint-disable-line no-magic-numbers
|
|
255
363
|
}
|
|
256
364
|
parseNext() {
|
|
257
|
-
// this.progress()
|
|
258
|
-
const chunk = {
|
|
259
|
-
offset: {
|
|
260
|
-
pos: this.pos,
|
|
261
|
-
line: this.input.substring(0, this.pos).split('\n').length - 1,
|
|
262
|
-
},
|
|
263
|
-
error: null,
|
|
264
|
-
text: null,
|
|
265
|
-
};
|
|
266
365
|
this.skipWhitespace();
|
|
366
|
+
while (this.pos < this.input.length && this.input[this.pos] !== '@') {
|
|
367
|
+
if (this.input[this.pos] === '%') {
|
|
368
|
+
while (this.pos < this.input.length && this.input[this.pos] !== '\n')
|
|
369
|
+
this.pos++;
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
this.pos++;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
267
375
|
if (this.pos >= this.input.length)
|
|
268
376
|
return;
|
|
269
377
|
let guard = '';
|
|
378
|
+
const start = this.pos;
|
|
270
379
|
try {
|
|
271
380
|
const d = this.parsing = this.directive();
|
|
272
381
|
switch (d) {
|
|
@@ -274,85 +383,50 @@ class BibtexParser {
|
|
|
274
383
|
guard = this.matchGuard();
|
|
275
384
|
this.string();
|
|
276
385
|
this.match(guard);
|
|
277
|
-
chunk.stringDeclaration = true;
|
|
278
386
|
break;
|
|
279
387
|
case 'preamble':
|
|
280
|
-
guard = this.matchGuard();
|
|
281
388
|
this.preamble();
|
|
282
|
-
this.match(guard);
|
|
283
|
-
chunk.preamble = true;
|
|
284
389
|
break;
|
|
285
390
|
case 'comment':
|
|
286
391
|
this.comment();
|
|
287
|
-
chunk.comment = true;
|
|
288
392
|
break;
|
|
289
393
|
default:
|
|
290
394
|
guard = this.matchGuard();
|
|
291
|
-
this.entries.unshift({ type: d, fields: {} });
|
|
292
|
-
this.entry(d);
|
|
395
|
+
this.entries.unshift({ input: '', type: d, key: '', fields: {} });
|
|
396
|
+
this.entry(d, guard);
|
|
293
397
|
this.match(guard);
|
|
294
|
-
|
|
398
|
+
this.entries[0].input = this.input.substring(start, this.pos).trim();
|
|
295
399
|
break;
|
|
296
400
|
}
|
|
297
401
|
}
|
|
298
402
|
catch (err) {
|
|
299
|
-
if (err.name !== '
|
|
403
|
+
if (err.name !== 'ParsingError')
|
|
300
404
|
throw err;
|
|
301
|
-
chunk.error = err.message;
|
|
302
405
|
// skip ahead to the next @ and try again
|
|
303
|
-
this.pos =
|
|
406
|
+
this.pos = start + 1;
|
|
304
407
|
while (this.pos < this.input.length && this.input[this.pos] !== '@')
|
|
305
408
|
this.pos++;
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
if (this.input[pos] === '\n') this.linebreaks.push({ pos, line: this.linebreaks.length + 1 })
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
const line = this.linebreaks.find(l => l.pos >= chunk.offset.pos)
|
|
314
|
-
if (line) {
|
|
315
|
-
chunk.offset.line = line.line
|
|
316
|
-
chunk.offset.pos = chunk.offset.pos - line.pos
|
|
317
|
-
}
|
|
318
|
-
else {
|
|
319
|
-
chunk.offset.line = 1
|
|
320
|
-
}
|
|
321
|
-
*/
|
|
322
|
-
}
|
|
323
|
-
const text = this.input.substring(chunk.offset.pos, this.pos);
|
|
324
|
-
const last = this.chunks.length - 1;
|
|
325
|
-
if (chunk.error && this.chunks.length && this.chunks[last].error) {
|
|
326
|
-
this.chunks[last].text += text;
|
|
327
|
-
}
|
|
328
|
-
else {
|
|
329
|
-
chunk.text = text;
|
|
330
|
-
this.chunks.push(chunk);
|
|
409
|
+
this.errors.push({
|
|
410
|
+
error: err.message,
|
|
411
|
+
input: this.input.substring(start, this.pos),
|
|
412
|
+
});
|
|
331
413
|
}
|
|
332
414
|
}
|
|
333
415
|
}
|
|
334
416
|
/**
|
|
335
|
-
*
|
|
336
|
-
* make the more sophisticated [[bibtex.parse]] reject the whole file
|
|
337
|
-
*
|
|
338
|
-
* @returns array of chunks, with markers for type and errors (if any) found.
|
|
417
|
+
* Parses a bibtex source in verbatim mode. Good for detection of bibtex and for later LaTeX-reparsing.
|
|
339
418
|
*/
|
|
340
419
|
function parse(input, options = {}) {
|
|
341
|
-
|
|
420
|
+
const parser = new Parser(input, options);
|
|
421
|
+
parser.parse();
|
|
422
|
+
return parser;
|
|
342
423
|
}
|
|
343
424
|
exports.parse = parse;
|
|
344
|
-
function entries(input, options = {}) {
|
|
345
|
-
const parser = new BibtexParser;
|
|
346
|
-
parser.parse(input, options);
|
|
347
|
-
return {
|
|
348
|
-
entries: parser.entries.reverse(),
|
|
349
|
-
errors: parser.chunks.filter(chunk => chunk.error),
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
exports.entries = entries;
|
|
353
425
|
exports.promises = {
|
|
354
426
|
async parse(input, options = {}) {
|
|
355
|
-
|
|
427
|
+
const parser = new Parser(input, options);
|
|
428
|
+
await parser.parseAsync();
|
|
429
|
+
return parser;
|
|
356
430
|
},
|
|
357
431
|
};
|
|
358
|
-
//# sourceMappingURL=
|
|
432
|
+
//# sourceMappingURL=verbatim.js.map
|
package/chunker.d.ts
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
export interface Chunk {
|
|
2
|
-
/**
|
|
3
|
-
* The text content of the chunk
|
|
4
|
-
*/
|
|
5
|
-
text: string;
|
|
6
|
-
/**
|
|
7
|
-
* Start location of the chunk in the bib file
|
|
8
|
-
*/
|
|
9
|
-
offset: {
|
|
10
|
-
pos: number;
|
|
11
|
-
line: number;
|
|
12
|
-
};
|
|
13
|
-
/**
|
|
14
|
-
* error found, if any.
|
|
15
|
-
*/
|
|
16
|
-
error?: string;
|
|
17
|
-
/**
|
|
18
|
-
* set to `true` if the chunk is a `@preamble` block
|
|
19
|
-
*/
|
|
20
|
-
preamble?: boolean;
|
|
21
|
-
/**
|
|
22
|
-
* set to `true` if the chunk is a bibtex entry
|
|
23
|
-
*/
|
|
24
|
-
entry?: boolean;
|
|
25
|
-
/**
|
|
26
|
-
* set to `true` if the chunk is an `@string` declaration
|
|
27
|
-
*/
|
|
28
|
-
stringDeclaration?: boolean;
|
|
29
|
-
/**
|
|
30
|
-
* set to `true` if the chunk is an `@comment`
|
|
31
|
-
*/
|
|
32
|
-
comment?: boolean;
|
|
33
|
-
}
|
|
34
|
-
export interface ParserOptions {
|
|
35
|
-
/**
|
|
36
|
-
* stop parsing after `max_entries` entries have been found. Useful for quick detection if a text file is in fact a bibtex file
|
|
37
|
-
*/
|
|
38
|
-
max_entries?: number;
|
|
39
|
-
}
|
|
40
|
-
type Entry = {
|
|
41
|
-
type: string;
|
|
42
|
-
key?: string;
|
|
43
|
-
fields: Record<string, string>;
|
|
44
|
-
};
|
|
45
|
-
/**
|
|
46
|
-
* Reads the bibtex input and splits it into separate chunks of `string`s, `@comment`s, and bibtex entries. Useful for detecting if a file is bibtex file and for filtering out basic errors that would
|
|
47
|
-
* make the more sophisticated [[bibtex.parse]] reject the whole file
|
|
48
|
-
*
|
|
49
|
-
* @returns array of chunks, with markers for type and errors (if any) found.
|
|
50
|
-
*/
|
|
51
|
-
export declare function parse(input: string, options?: ParserOptions): Chunk[];
|
|
52
|
-
export declare function entries(input: string, options?: ParserOptions): {
|
|
53
|
-
entries: Entry[];
|
|
54
|
-
errors: Chunk[];
|
|
55
|
-
};
|
|
56
|
-
export declare const promises: {
|
|
57
|
-
parse(input: string, options?: ParserOptions): Promise<Chunk[]>;
|
|
58
|
-
};
|
|
59
|
-
export {};
|
|
60
|
-
//# sourceMappingURL=chunker.d.ts.map
|