forked from expo/troggle
163 lines
5.0 KiB
JavaScript
163 lines
5.0 KiB
JavaScript
var SparqlParser = Editor.Parser = (function() {
|
|
function wordRegexp(words) {
|
|
return new RegExp("^(?:" + words.join("|") + ")$", "i");
|
|
}
|
|
var ops = wordRegexp(["str", "lang", "langmatches", "datatype", "bound", "sameterm", "isiri", "isuri",
|
|
"isblank", "isliteral", "union", "a"]);
|
|
var keywords = wordRegexp(["base", "prefix", "select", "distinct", "reduced", "construct", "describe",
|
|
"ask", "from", "named", "where", "order", "limit", "offset", "filter", "optional",
|
|
"graph", "by", "asc", "desc", ]);
|
|
var operatorChars = /[*+\-<>=&|]/;
|
|
|
|
var tokenizeSparql = (function() {
|
|
function normal(source, setState) {
|
|
var ch = source.next();
|
|
if (ch == "$" || ch == "?") {
|
|
source.nextWhileMatches(/[\w\d]/);
|
|
return "sp-var";
|
|
}
|
|
else if (ch == "<" && !source.matches(/[\s\u00a0=]/)) {
|
|
source.nextWhileMatches(/[^\s\u00a0>]/);
|
|
if (source.equals(">")) source.next();
|
|
return "sp-uri";
|
|
}
|
|
else if (ch == "\"" || ch == "'") {
|
|
setState(inLiteral(ch));
|
|
return null;
|
|
}
|
|
else if (/[{}\(\),\.;\[\]]/.test(ch)) {
|
|
return "sp-punc";
|
|
}
|
|
else if (ch == "#") {
|
|
while (!source.endOfLine()) source.next();
|
|
return "sp-comment";
|
|
}
|
|
else if (operatorChars.test(ch)) {
|
|
source.nextWhileMatches(operatorChars);
|
|
return "sp-operator";
|
|
}
|
|
else if (ch == ":") {
|
|
source.nextWhileMatches(/[\w\d\._\-]/);
|
|
return "sp-prefixed";
|
|
}
|
|
else {
|
|
source.nextWhileMatches(/[_\w\d]/);
|
|
if (source.equals(":")) {
|
|
source.next();
|
|
source.nextWhileMatches(/[\w\d_\-]/);
|
|
return "sp-prefixed";
|
|
}
|
|
var word = source.get(), type;
|
|
if (ops.test(word))
|
|
type = "sp-operator";
|
|
else if (keywords.test(word))
|
|
type = "sp-keyword";
|
|
else
|
|
type = "sp-word";
|
|
return {style: type, content: word};
|
|
}
|
|
}
|
|
|
|
function inLiteral(quote) {
|
|
return function(source, setState) {
|
|
var escaped = false;
|
|
while (!source.endOfLine()) {
|
|
var ch = source.next();
|
|
if (ch == quote && !escaped) {
|
|
setState(normal);
|
|
break;
|
|
}
|
|
escaped = !escaped && ch == "\\";
|
|
}
|
|
return "sp-literal";
|
|
};
|
|
}
|
|
|
|
return function(source, startState) {
|
|
return tokenizer(source, startState || normal);
|
|
};
|
|
})();
|
|
|
|
function indentSparql(context) {
|
|
return function(nextChars) {
|
|
var firstChar = nextChars && nextChars.charAt(0);
|
|
if (/[\]\}]/.test(firstChar))
|
|
while (context && context.type == "pattern") context = context.prev;
|
|
|
|
var closing = context && firstChar == matching[context.type];
|
|
if (!context)
|
|
return 0;
|
|
else if (context.type == "pattern")
|
|
return context.col;
|
|
else if (context.align)
|
|
return context.col - (closing ? context.width : 0);
|
|
else
|
|
return context.indent + (closing ? 0 : indentUnit);
|
|
}
|
|
}
|
|
|
|
function parseSparql(source) {
|
|
var tokens = tokenizeSparql(source);
|
|
var context = null, indent = 0, col = 0;
|
|
function pushContext(type, width) {
|
|
context = {prev: context, indent: indent, col: col, type: type, width: width};
|
|
}
|
|
function popContext() {
|
|
context = context.prev;
|
|
}
|
|
|
|
var iter = {
|
|
next: function() {
|
|
var token = tokens.next(), type = token.style, content = token.content, width = token.value.length;
|
|
|
|
if (content == "\n") {
|
|
token.indentation = indentSparql(context);
|
|
indent = col = 0;
|
|
if (context && context.align == null) context.align = false;
|
|
}
|
|
else if (type == "whitespace" && col == 0) {
|
|
indent = width;
|
|
}
|
|
else if (type != "sp-comment" && context && context.align == null) {
|
|
context.align = true;
|
|
}
|
|
|
|
if (content != "\n") col += width;
|
|
|
|
if (/[\[\{\(]/.test(content)) {
|
|
pushContext(content, width);
|
|
}
|
|
else if (/[\]\}\)]/.test(content)) {
|
|
while (context && context.type == "pattern")
|
|
popContext();
|
|
if (context && content == matching[context.type])
|
|
popContext();
|
|
}
|
|
else if (content == "." && context && context.type == "pattern") {
|
|
popContext();
|
|
}
|
|
else if ((type == "sp-word" || type == "sp-prefixed" || type == "sp-uri" || type == "sp-var" || type == "sp-literal") &&
|
|
context && /[\{\[]/.test(context.type)) {
|
|
pushContext("pattern", width);
|
|
}
|
|
|
|
return token;
|
|
},
|
|
|
|
copy: function() {
|
|
var _context = context, _indent = indent, _col = col, _tokenState = tokens.state;
|
|
return function(source) {
|
|
tokens = tokenizeSparql(source, _tokenState);
|
|
context = _context;
|
|
indent = _indent;
|
|
col = _col;
|
|
return iter;
|
|
};
|
|
}
|
|
};
|
|
return iter;
|
|
}
|
|
|
|
return {make: parseSparql, electricChars: "}]"};
|
|
})();
|