@retorquere/bibtex-parser 7.0.10 → 7.0.12

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/chunker.d.ts CHANGED
@@ -37,14 +37,24 @@ export interface ParserOptions {
37
37
  */
38
38
  max_entries?: number;
39
39
  }
40
+ type Entry = {
41
+ type: string;
42
+ key?: string;
43
+ fields: Record<string, string>;
44
+ };
40
45
  /**
41
- * 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
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
42
47
  * make the more sophisticated [[bibtex.parse]] reject the whole file
43
48
  *
44
49
  * @returns array of chunks, with markers for type and errors (if any) found.
45
50
  */
46
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
+ };
47
56
  export declare const promises: {
48
57
  parse(input: string, options?: ParserOptions): Promise<Chunk[]>;
49
58
  };
59
+ export {};
50
60
  //# sourceMappingURL=chunker.d.ts.map
package/chunker.js CHANGED
@@ -5,7 +5,7 @@
5
5
  // CommonJS port by Mikola Lysenko 2013
6
6
  //
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.promises = exports.parse = void 0;
8
+ exports.promises = exports.entries = exports.parse = void 0;
9
9
  class ParseError extends Error {
10
10
  constructor(message, parser) {
11
11
  message += ` @ ${parser.pos}`;
@@ -29,21 +29,24 @@ const letter = new RegExp('[' + [
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
31
  class BibtexParser {
32
- constructor() {
32
+ reset(input) {
33
+ this.input = input;
33
34
  this.pos = 0;
34
- this.entries = 0;
35
+ this.entries = [];
36
+ this.strings = {};
35
37
  this.parsing = null;
36
38
  this.chunks = [];
39
+ // this.linebreaks = undefined
37
40
  }
38
41
  parse(input, options = {}) {
42
+ this.reset(input);
39
43
  this.max_entries = options.max_entries || 0;
40
- this.input = input;
41
44
  this.bibtex();
42
45
  return this.chunks;
43
46
  }
44
47
  async parseAsync(input, options = {}) {
48
+ this.reset(input);
45
49
  this.max_entries = options.max_entries || 0;
46
- this.input = input;
47
50
  await this.bibtexAsync();
48
51
  return this.chunks;
49
52
  }
@@ -141,7 +144,8 @@ class BibtexParser {
141
144
  return this.value_quotes();
142
145
  }
143
146
  else {
144
- return this.key();
147
+ const bare = this.key();
148
+ return this.strings[bare] || bare;
145
149
  }
146
150
  }
147
151
  value() {
@@ -177,9 +181,16 @@ class BibtexParser {
177
181
  }
178
182
  this.match('=');
179
183
  const val = this.value();
180
- return [key, val];
184
+ if (this.parsing === 'string') {
185
+ this.strings[key.toLowerCase()] = val;
186
+ }
187
+ else {
188
+ this.entries[0].fields[key] = val;
189
+ }
181
190
  }
182
- key_value_list() {
191
+ entry(_d) {
192
+ this.entries[0].key = this.key(true);
193
+ this.match(',');
183
194
  this.key_equals_value();
184
195
  while (this.tryMatch(',')) {
185
196
  this.match(',');
@@ -190,14 +201,9 @@ class BibtexParser {
190
201
  this.key_equals_value();
191
202
  }
192
203
  }
193
- entry(_d) {
194
- this.parsing = this.key(true);
195
- this.match(',');
196
- this.key_value_list();
197
- }
198
204
  directive() {
199
205
  this.match('@');
200
- return `@${this.key()}`.toLowerCase();
206
+ return this.key().toLowerCase();
201
207
  }
202
208
  string() {
203
209
  this.key_equals_value();
@@ -225,7 +231,7 @@ class BibtexParser {
225
231
  }
226
232
  */
227
233
  hasMore() {
228
- if (this.max_entries && this.entries >= this.max_entries)
234
+ if (this.max_entries && this.entries.length >= this.max_entries)
229
235
  return false;
230
236
  return (this.pos < this.input.length);
231
237
  }
@@ -238,6 +244,15 @@ class BibtexParser {
238
244
  // eslint-disable-next-line @typescript-eslint/no-unsafe-return
239
245
  return this.hasMore() ? (new Promise(resolve => resolve(this.parseNext()))).then(() => this.bibtexAsync()) : Promise.resolve(null);
240
246
  }
247
+ matchGuard() {
248
+ for (const guard of ['{}', '()']) {
249
+ if (this.tryMatch(guard[0])) {
250
+ this.match(guard[0]);
251
+ return guard[1];
252
+ }
253
+ }
254
+ throw new ParseError(`Token mismatch, expected '{' or '(', found ${JSON.stringify(this.input.substr(this.pos, 20))}...`, this); // eslint-disable-line no-magic-numbers
255
+ }
241
256
  parseNext() {
242
257
  // this.progress()
243
258
  const chunk = {
@@ -253,39 +268,30 @@ class BibtexParser {
253
268
  return;
254
269
  let guard = '';
255
270
  try {
256
- const d = this.directive();
271
+ const d = this.parsing = this.directive();
257
272
  switch (d) {
258
- case '@string':
259
- this.match('{');
273
+ case 'string':
274
+ guard = this.matchGuard();
260
275
  this.string();
261
- this.match('}');
276
+ this.match(guard);
262
277
  chunk.stringDeclaration = true;
263
278
  break;
264
- case '@preamble':
265
- this.match('{');
279
+ case 'preamble':
280
+ guard = this.matchGuard();
266
281
  this.preamble();
267
- this.match('}');
282
+ this.match(guard);
268
283
  chunk.preamble = true;
269
284
  break;
270
- case '@comment':
285
+ case 'comment':
271
286
  this.comment();
272
287
  chunk.comment = true;
273
288
  break;
274
289
  default:
275
- if (this.tryMatch('{')) {
276
- guard = '{}';
277
- }
278
- else if (this.tryMatch('(')) {
279
- guard = '()';
280
- }
281
- else {
282
- throw new ParseError(`Token mismatch, expected '{' or '(', found ${JSON.stringify(this.input.substr(this.pos, 20))}...`, this); // eslint-disable-line no-magic-numbers
283
- }
284
- this.match(guard[0]);
290
+ guard = this.matchGuard();
291
+ this.entries.unshift({ type: d, fields: {} });
285
292
  this.entry(d);
286
- this.match(guard[1]);
293
+ this.match(guard);
287
294
  chunk.entry = true;
288
- this.entries++;
289
295
  break;
290
296
  }
291
297
  }
@@ -297,6 +303,22 @@ class BibtexParser {
297
303
  this.pos = chunk.offset.pos + 1;
298
304
  while (this.pos < this.input.length && this.input[this.pos] !== '@')
299
305
  this.pos++;
306
+ /*
307
+ if (!this.linebreaks) {
308
+ this.linebreaks = []
309
+ for (let pos = 0; pos < this.input.length; pos++) {
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
+ */
300
322
  }
301
323
  const text = this.input.substring(chunk.offset.pos, this.pos);
302
324
  const last = this.chunks.length - 1;
@@ -310,7 +332,7 @@ class BibtexParser {
310
332
  }
311
333
  }
312
334
  /**
313
- * 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
335
+ * 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
314
336
  * make the more sophisticated [[bibtex.parse]] reject the whole file
315
337
  *
316
338
  * @returns array of chunks, with markers for type and errors (if any) found.
@@ -319,6 +341,15 @@ function parse(input, options = {}) {
319
341
  return (new BibtexParser).parse(input, options);
320
342
  }
321
343
  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;
322
353
  exports.promises = {
323
354
  async parse(input, options = {}) {
324
355
  return await (new BibtexParser).parseAsync(input, options);