You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

167 lines
3.5 KiB

"use strict";
var Parser = require("fastparse");
function commentMatch(match, content) {
this.value.nodes.push({
type: "comment",
content: content
});
}
function spacingMatch(match) {
var item = this.value.nodes[this.value.nodes.length - 1];
item.after = (item.after || "") + match;
}
function initialSpacingMatch(match) {
this.value.before = match;
}
function endSpacingMatch(match) {
this.value.after = match;
}
function unescapeString(content) {
return content.replace(/\\(?:([a-fA-F0-9]{1,6})|(.))/g, function(all, unicode, otherCharacter) {
if (otherCharacter) {
return otherCharacter;
}
var C = parseInt(unicode, 16);
if(C < 0x10000) {
return String.fromCharCode(C);
} else {
return String.fromCharCode(Math.floor((C - 0x10000) / 0x400) + 0xD800) +
String.fromCharCode((C - 0x10000) % 0x400 + 0xDC00);
}
});
}
function stringMatch(match, content) {
var value = unescapeString(content);
this.value.nodes.push({
type: "string",
value: value,
stringType: match[0]
});
}
function commaMatch(match, spacing) {
var newValue = {
type: "value",
nodes: []
};
if(spacing) {
newValue.before = spacing;
}
this.root.nodes.push(newValue);
this.value = newValue;
}
function itemMatch(match) {
this.value.nodes.push({
type: "item",
name: match
});
}
function nestedItemMatch(match, name, spacing) {
this.stack.push(this.root);
this.root = {
type: "nested-item",
name: name,
nodes: [
{ type: "value", nodes: [] }
]
};
if(spacing) {
this.root.nodes[0].before = spacing;
}
this.value.nodes.push(this.root);
this.value = this.root.nodes[0];
}
function nestedItemEndMatch(match, spacing, remaining) {
if(this.stack.length === 0) {
if(spacing) {
var item = this.value.nodes[this.value.nodes.length - 1];
item.after = (item.after || "") + spacing;
}
this.value.nodes.push({
type: "invalid",
value: remaining
});
} else {
if(spacing) {
this.value.after = spacing;
}
this.root = this.stack.pop();
this.value = this.root.nodes[this.root.nodes.length - 1];
}
}
function urlMatch(match, innerSpacingBefore, content, innerSpacingAfter) {
var item = {
type: "url"
};
if(innerSpacingBefore) {
item.innerSpacingBefore = innerSpacingBefore;
}
if(innerSpacingAfter) {
item.innerSpacingAfter = innerSpacingAfter;
}
switch(content[0]) {
case "\"":
item.stringType = "\"";
item.url = unescapeString(content.substr(1, content.length - 2));
break;
case "'":
item.stringType = "'";
item.url = unescapeString(content.substr(1, content.length - 2));
break;
default:
item.url = unescapeString(content);
break;
}
this.value.nodes.push(item);
}
var parser = new Parser({
decl: {
"^\\s+": initialSpacingMatch,
"/\\*([\\s\\S]*?)\\*/": commentMatch,
"\"((?:[^\\\\\"]|\\\\.)*)\"": stringMatch,
"'((?:[^\\\\']|\\\\.)*)'": stringMatch,
"url\\((\\s*)(\"(?:[^\\\\\"]|\\\\.)*\")(\\s*)\\)": urlMatch,
"url\\((\\s*)('(?:[^\\\\']|\\\\.)*')(\\s*)\\)": urlMatch,
"url\\((\\s*)((?:[^\\\\)'\"]|\\\\.)*)(\\s*)\\)": urlMatch,
"([\\w\-]+)\\((\\s*)": nestedItemMatch,
"(\\s*)(\\))": nestedItemEndMatch,
",(\\s*)": commaMatch,
"\\s+$": endSpacingMatch,
"\\s+": spacingMatch,
"[^\\s,\)]+": itemMatch
}
});
function parseValues(str) {
var valueNode = {
type: "value",
nodes: []
};
var rootNode = {
type: "values",
nodes: [
valueNode
]
};
parser.parse("decl", str, {
stack: [],
root: rootNode,
value: valueNode
});
return rootNode;
}
module.exports = parseValues;