locator-lang
Version:
Language bundles compiler for locator
184 lines (166 loc) • 5.54 kB
JavaScript
/*
* Copyright (c) 2013, Yahoo! Inc. All rights reserved.
* Copyrights licensed under the New BSD License.
* See the accompanying LICENSE.txt file for terms.
*/
/**
* Converts resource bundles provided in YRB format into JSON format.
*
* YRB format is documented at
* http://developer.yahoo.com/yui/3/intl/index.html#yrb
*
* This implementation comes from one of our legacy components (builder):
* https://github.com/yui/builder/tree/master/componentbuild/lib/yrb2json
*/
/*jslint onevar: true, undef: true, nomen: true, eqeqeq: true, bitwise: true, regexp: true, newcap: true, immed: true */
function isCommentLine(line) {
return (line.match(/^[ \t]*#/) !== null);
}
function isWhitespaceLine(line) {
return (line.match(/^[ \t]*$/) !== null);
}
function isWhitespaceChar(c) {
return c === " " || c === "\t";
}
/**
* Checks whether the given key is not empty and doesn't contain backslashes.
*/
function checkKey(key) {
if (key.length === 0) {
throw new Error("Empty key not allowed.");
}
if (key.indexOf("\\") >= 0) {
throw new Error("Backslash not allowed in key: " + key);
}
}
function trimWhitespace(s) {
var start = 0, end = s.length;
while (start < s.length && isWhitespaceChar(s.charAt(start))) {
start++;
}
while (end > start && isWhitespaceChar(s.charAt(end - 1))) {
end--;
}
return s.substring(start, end);
}
/**
* Unescapes all escape sequences that occur in s. Optionally trims whitespace
* from beginning and end of string, taking care not to trim whitespace
* that's part of an escape sequence.
*/
function unescapeValue(s, trim) {
var pos, start, end, result;
pos = s.indexOf("\\");
if (pos >= 0) {
result = "";
start = 0;
if (trim) {
while (isWhitespaceChar(s.charAt(start))) {
start++;
}
}
while (pos >= 0) {
result += s.substring(start, pos);
if (pos + 1 >= s.length) {
throw new Error("Illegal escape sequence: unaccompanied \\");
}
switch (s.charAt(pos + 1)) {
case "\\":
result += "\\";
break;
case "n":
result += "\n";
break;
case "t":
result += "\t";
break;
case " ":
result += " ";
break;
case "#":
result += "#";
break;
default:
throw new Error("Illegal escape sequence: \\" + s.charAt(pos + 1));
}
start = pos + 2;
pos = s.indexOf("\\", start);
}
end = s.length;
if (trim) {
while (end > start && isWhitespaceChar(s.charAt(end - 1))) {
end--;
}
}
result += s.substring(start, end);
} else {
result = trim ? trimWhitespace(s) : s;
}
return result;
}
/**
* Convert a string into JSON representation. Should use stringify once Rhino gets it.
*/
function jsonString(s) {
return "\"" + s.replace(/\\/g, "\\\\").replace(/\"/g, "\\\"").replace(/\n/g, "\\n").replace(/\t/g, "\\t") + "\"";
}
function jsonKeyValue(key, value) {
return jsonString(key) + ":" + jsonString(value);
}
/**
* Converts the contents of one YRB file into corresponding JSON content.
*/
module.exports = function (source) {
var lines, properties, hereDocId, i, line, equalsPos, key, remainder,
match, value;
if (source.length > 0 && source.charAt(0) === "\ufeff") {
// remove BOM
source = source.substring(1);
}
lines = source.replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n');
properties = [];
hereDocId = null; // null indicates we're not inside heredoc section
for (i = 0; i < lines.length; i++) {
line = lines[i];
if (isCommentLine(line)) {
continue;
}
if (hereDocId === null) {
if (isWhitespaceLine(line)) {
continue;
}
// extract the key
equalsPos = line.indexOf("=");
if (equalsPos < 0) {
throw new Error("Missing '=' in line " + line + ".");
}
key = trimWhitespace(line.substring(0, equalsPos));
checkKey(key);
// decide whether it's simple or heredoc form
remainder = line.substring(equalsPos + 1);
if ((match = remainder.match(/^[ \t]*<<</)) !== null) {
hereDocId = trimWhitespace(remainder.substring(match[0].length));
value = null;
} else {
value = unescapeValue(remainder, true);
properties.push(jsonKeyValue(key, value));
}
} else {
if (line === hereDocId || line === hereDocId + ";") {
value = value || "";
properties.push(jsonKeyValue(key, value));
hereDocId = null;
} else {
if (value === null) {
value = unescapeValue(line, false);
} else {
value += "\n" + unescapeValue(line, false);
}
}
}
}
if (hereDocId !== null) {
throw new Error("Incomplete heredoc with id " + hereDocId + ".");
}
return '{' + properties.join(",") + "}";
};