@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/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.entries = exports.parse = void 0;
9
- class ParseError extends Error {
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 = 'ParseError';
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 BibtexParser {
32
- reset(input) {
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.chunks = [];
39
- // this.linebreaks = undefined
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
- parse(input, options = {}) {
42
- this.reset(input);
43
- this.max_entries = options.max_entries || 0;
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
- return this.chunks;
108
+ this.entries.reverse();
46
109
  }
47
- async parseAsync(input, options = {}) {
48
- this.reset(input);
49
- this.max_entries = options.max_entries || 0;
110
+ async parseAsync() {
50
111
  await this.bibtexAsync();
51
- return this.chunks;
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 ParseError(`Token mismatch, expected ${JSON.stringify(s)}, found ${JSON.stringify(this.input.substr(this.pos, 20))}...`, this); // eslint-disable-line no-magic-numbers
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
- this.skipWhitespace();
123
+ if (sws)
124
+ this.skipWhitespace();
63
125
  }
64
- tryMatch(s) {
126
+ tryMatch(s, consume = false) {
65
127
  this.skipWhitespace();
66
- return (this.input.substr(this.pos, s.length) === s);
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.isWhitespace(this.input[this.pos]))
71
- this.pos++;
72
- // shady
73
- if (this.input[this.pos] === '%') {
74
- while (this.pos < this.input.length && this.input[this.pos] !== '\n')
75
- this.pos++;
76
- while (this.pos < this.input.length && this.isWhitespace(this.input[this.pos]))
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 ParseError('Unclosed math section', this);
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 ParseError(`Unterminated brace-value ${JSON.stringify(this.input.substr(start, 20))}`, this); // eslint-disable-line no-magic-numbers
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 ParseError(`Unterminated quote-value ${JSON.stringify(this.input.substr(start, 20))}`, this); // eslint-disable-line no-magic-numbers
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
- return this.strings[bare] || bare;
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(allowUnicode = false) {
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 ParseError('Runaway key', this);
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
- const key = this.key();
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 ParseError(`... = value expected, equals sign missing: ${JSON.stringify(this.input.substr(this.pos, 20))}...`, this); // eslint-disable-line no-magic-numbers
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.toLowerCase()] = val;
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(_d) {
192
- this.entries[0].key = this.key(true);
193
- this.match(',');
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
- break;
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
- return;
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 ParseError(`Token mismatch, expected '{' or '(', found ${JSON.stringify(this.input.substr(this.pos, 20))}...`, this); // eslint-disable-line no-magic-numbers
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
- chunk.entry = true;
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 !== 'ParseError')
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 = chunk.offset.pos + 1;
406
+ this.pos = start + 1;
304
407
  while (this.pos < this.input.length && this.input[this.pos] !== '@')
305
408
  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
- */
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
- * 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
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
- return (new BibtexParser).parse(input, options);
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
- return await (new BibtexParser).parseAsync(input, options);
427
+ const parser = new Parser(input, options);
428
+ await parser.parseAsync();
429
+ return parser;
356
430
  },
357
431
  };
358
- //# sourceMappingURL=chunker.js.map
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