Template:McMaster/JS/bibtexParse

/* start bibtexParse 0.0.22 */

//Original work by Henrik Muehe (c) 2010 // //CommonJS port by Mikola Lysenko 2013 // //Port to Browser lib by ORCID / RCPETERS // //Issues: //no comment handling within strings //no string concatenation //no variable values yet //Grammar implemented here: //bibtex -> (string | preamble | comment | entry)*; //string -> '@STRING' '{' key_equals_value '}'; //preamble -> '@PREAMBLE' '{' value '}'; //comment -> '@COMMENT' '{' value '}'; //entry -> '@' key '{' key ',' key_value_list '}'; //key_value_list -> key_equals_value (',' key_equals_value)*; //key_equals_value -> key '=' value; //value -> value_quotes | value_braces | key; //value_quotes -> '"' .*? '"'; // not quite //value_braces -> '{' .*? '"'; // not quite (function(exports) {

   function BibtexParser() {
       
       this.months = ["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"];
       this.notKey = [',','{','}',' ','='];
       this.pos = 0;
       this.input = "";
       this.entries = new Array();
       this.currentEntry = "";
       this.setInput = function(t) {
           this.input = t;
       };
       this.getEntries = function() {
           return this.entries;
       };
       this.isWhitespace = function(s) {
           return (s == ' ' || s == '\r' || s == '\t' || s == '\n');
       };
       this.match = function(s, canCommentOut) {
           if (canCommentOut == undefined || canCommentOut == null)
               canCommentOut = true;
           this.skipWhitespace(canCommentOut);
           if (this.input.substring(this.pos, this.pos + s.length) == s) {
               this.pos += s.length;
           } else {
               throw "Token mismatch, expected " + s + ", found "
                       + this.input.substring(this.pos);
           };
           this.skipWhitespace(canCommentOut);
       };
       this.tryMatch = function(s, canCommentOut) {
           if (canCommentOut == undefined || canCommentOut == null)
               canComment = true;
           this.skipWhitespace(canCommentOut);
           if (this.input.substring(this.pos, this.pos + s.length) == s) {
               return true;
           } else {
               return false;
           };
           this.skipWhitespace(canCommentOut);
       };
       /* when search for a match all text can be ignored, not just white space */
       this.matchAt = function() {
           while (this.input.length > this.pos && this.input[this.pos] != '@') {
               this.pos++;
           };
           if (this.input[this.pos] == '@') {
               return true;
           };
           return false;
       };
       this.skipWhitespace = function(canCommentOut) {
           while (this.isWhitespace(this.input[this.pos])) {
               this.pos++;
           };
           if (this.input[this.pos] == "%" && canCommentOut == true) {
               while (this.input[this.pos] != "\n") {
                   this.pos++;
               };
               this.skipWhitespace(canCommentOut);
           };
       };
       this.value_braces = function() {
           var bracecount = 0;
           this.match("{", false);
           var start = this.pos;
           var escaped = false;
           while (true) {
               if (!escaped) {
                   if (this.input[this.pos] == '}') {
                       if (bracecount > 0) {
                           bracecount--;
                       } else {
                           var end = this.pos;
                           this.match("}", false);
                           return this.input.substring(start, end);
                       };
                   } else if (this.input[this.pos] == '{') {
                       bracecount++;
                   } else if (this.pos >= this.input.length - 1) {
                       throw "Unterminated value";
                   };
               };
               if (this.input[this.pos] == '\\' && escaped == false)
                   escaped == true;
               else
                   escaped == false;
               this.pos++;
           };
       };
       this.value_comment = function() {
           var str = ;
           var brcktCnt = 0;
           while (!(this.tryMatch("}", false) && brcktCnt == 0)) {
               str = str + this.input[this.pos];
               if (this.input[this.pos] == '{')
                   brcktCnt++;
               if (this.input[this.pos] == '}')
                   brcktCnt--;
               if (this.pos >= this.input.length - 1) {
                   throw "Unterminated value:" + this.input.substring(start);
               };
               this.pos++;
           };
           return str;
       };
       this.value_quotes = function() {
           this.match('"', false);
           var start = this.pos;
           var escaped = false;
           while (true) {
               if (!escaped) {
                   if (this.input[this.pos] == '"') {
                       var end = this.pos;
                       this.match('"', false);
                       return this.input.substring(start, end);
                   } else if (this.pos >= this.input.length - 1) {
                       throw "Unterminated value:" + this.input.substring(start);
                   };
               }
               if (this.input[this.pos] == '\\' && escaped == false)
                   escaped == true;
               else
                   escaped == false;
               this.pos++;
           };
       };
       this.single_value = function() {
           var start = this.pos;
           if (this.tryMatch("{")) {
               return this.value_braces();
           } else if (this.tryMatch('"')) {
               return this.value_quotes();
           } else {
               var k = this.key();
               if (k.match("^[0-9]+$"))
                   return k;
               else if (this.months.indexOf(k.toLowerCase()) >= 0)
                   return k.toLowerCase();
               else
                   throw "Value expected:" + this.input.substring(start) + ' for key: ' + k;
           
           };
       };
       this.value = function() {
           var values = [];
           values.push(this.single_value());
           while (this.tryMatch("#")) {
               this.match("#");
               values.push(this.single_value());
           };
           return values.join("");
       };
       this.key = function() {
           var start = this.pos;
           while (true) {
               if (this.pos >= this.input.length) {
                   throw "Runaway key";
               };
                               // а-яА-Я is Cyrillic
               //console.log(this.input[this.pos]);
               if (this.notKey.indexOf(this.input[this.pos]) >= 0) {
                   return this.input.substring(start, this.pos);
               } else {
                   this.pos++;
                   
               };
           };
       };
       this.key_equals_value = function() {
           var key = this.key();
           if (this.tryMatch("=")) {
               this.match("=");
               var val = this.value();
               return [ key, val ];
           } else {
               throw "... = value expected, equals sign missing:"
                       + this.input.substring(this.pos);
           };
       };
       this.key_value_list = function() {
           var kv = this.key_equals_value();
           this.currentEntry['entryTags'] = {};
           this.currentEntry['entryTags'][kv[0]] = kv[1];
           while (this.tryMatch(",")) {
               this.match(",");
               // fixes problems with commas at the end of a list
               if (this.tryMatch("}")) {
                   break;
               }
               ;
               kv = this.key_equals_value();
               this.currentEntry['entryTags'][kv[0]] = kv[1];
           };
       };
       this.entry_body = function(d) {
           this.currentEntry = {};
           this.currentEntry['citationKey'] = this.key();
           this.currentEntry['entryType'] = d.substring(1);
           this.match(",");
           this.key_value_list();
           this.entries.push(this.currentEntry);
       };
       this.directive = function() {
           this.match("@");
           return "@" + this.key();
       };
       this.preamble = function() {
           this.currentEntry = {};
           this.currentEntry['entryType'] = 'PREAMBLE';
           this.currentEntry['entry'] = this.value_comment();
           this.entries.push(this.currentEntry);
       };
       this.comment = function() {
           this.currentEntry = {};
           this.currentEntry['entryType'] = 'COMMENT';
           this.currentEntry['entry'] = this.value_comment();
           this.entries.push(this.currentEntry);
       };
       this.entry = function(d) {
           this.entry_body(d);
       };
       this.bibtex = function() {
           while (this.matchAt()) {
               var d = this.directive();
               this.match("{");
               if (d == "@STRING") {
                   this.string();
               } else if (d == "@PREAMBLE") {
                   this.preamble();
               } else if (d == "@COMMENT") {
                   this.comment();
               } else {
                   this.entry(d);
               }
               this.match("}");
           };
       };
   };
   
   exports.toJSON = function(bibtex) {
       var b = new BibtexParser();
       b.setInput(bibtex);
       b.bibtex();
       return b.entries;
   };
   /* added during hackathon don't hate on me */
   exports.toBibtex = function(json) {
       out = ;
       for ( var i in json) {
           out += "@" + json[i].entryType;
           out += '{';
           if (json[i].citationKey)
               out += json[i].citationKey + ', ';
           if (json[i].entry)
               out += json[i].entry ;
           if (json[i].entryTags) {
               var tags = ;
               for (jdx in json[i].entryTags) {
                   if (tags.length != 0)
                       tags += ', ';
                   tags += jdx + '= {' + json[i].entryTags[jdx] + '}';
               }
               out += tags;
           }
           out += '}\n\n';
       }
       return out;
       
   };

})(typeof exports === 'undefined' ? this['bibtexParse'] = {} : exports);

/* end bibtexParse */