mirror of
https://github.com/thangisme/notes.git
synced 2024-12-22 20:06:44 -05:00
243 lines
7.4 KiB
JavaScript
243 lines
7.4 KiB
JavaScript
var openParentheses = '('.charCodeAt(0);
|
|
var closeParentheses = ')'.charCodeAt(0);
|
|
var singleQuote = '\''.charCodeAt(0);
|
|
var doubleQuote = '"'.charCodeAt(0);
|
|
var backslash = '\\'.charCodeAt(0);
|
|
var slash = '/'.charCodeAt(0);
|
|
var comma = ','.charCodeAt(0);
|
|
var colon = ':'.charCodeAt(0);
|
|
var star = '*'.charCodeAt(0);
|
|
|
|
module.exports = function (input) {
|
|
var tokens = [];
|
|
var value = input;
|
|
|
|
var next, quote, prev, token, escape, escapePos, whitespacePos;
|
|
var pos = 0;
|
|
var code = value.charCodeAt(pos);
|
|
var max = value.length;
|
|
var stack = [{ nodes: tokens }];
|
|
var balanced = 0;
|
|
var parent;
|
|
|
|
var name = '';
|
|
var before = '';
|
|
var after = '';
|
|
|
|
while (pos < max) {
|
|
// Whitespaces
|
|
if (code <= 32) {
|
|
next = pos;
|
|
do {
|
|
next += 1;
|
|
code = value.charCodeAt(next);
|
|
} while (code <= 32);
|
|
token = value.slice(pos, next);
|
|
|
|
prev = tokens[tokens.length - 1];
|
|
if (code === closeParentheses && balanced) {
|
|
after = token;
|
|
} else if (prev && prev.type === 'div') {
|
|
prev.after = token;
|
|
} else if (code === comma || code === colon || code === slash && value.charCodeAt(next + 1) !== star) {
|
|
before = token;
|
|
} else {
|
|
tokens.push({
|
|
type: 'space',
|
|
sourceIndex: pos,
|
|
value: token
|
|
});
|
|
}
|
|
|
|
pos = next;
|
|
|
|
// Quotes
|
|
} else if (code === singleQuote || code === doubleQuote) {
|
|
next = pos;
|
|
quote = code === singleQuote ? '\'' : '"';
|
|
token = {
|
|
type: 'string',
|
|
sourceIndex: pos,
|
|
quote: quote
|
|
};
|
|
do {
|
|
escape = false;
|
|
next = value.indexOf(quote, next + 1);
|
|
if (~next) {
|
|
escapePos = next;
|
|
while (value.charCodeAt(escapePos - 1) === backslash) {
|
|
escapePos -= 1;
|
|
escape = !escape;
|
|
}
|
|
} else {
|
|
value += quote;
|
|
next = value.length - 1;
|
|
token.unclosed = true;
|
|
}
|
|
} while (escape);
|
|
token.value = value.slice(pos + 1, next);
|
|
|
|
tokens.push(token);
|
|
pos = next + 1;
|
|
code = value.charCodeAt(pos);
|
|
|
|
// Comments
|
|
} else if (code === slash && value.charCodeAt(pos + 1) === star) {
|
|
token = {
|
|
type: 'comment',
|
|
sourceIndex: pos
|
|
};
|
|
|
|
next = value.indexOf('*/', pos);
|
|
if (next === -1) {
|
|
token.unclosed = true;
|
|
next = value.length;
|
|
}
|
|
|
|
token.value = value.slice(pos + 2, next);
|
|
tokens.push(token);
|
|
|
|
pos = next + 2;
|
|
code = value.charCodeAt(pos);
|
|
|
|
// Dividers
|
|
} else if (code === slash || code === comma || code === colon) {
|
|
token = value[pos];
|
|
|
|
tokens.push({
|
|
type: 'div',
|
|
sourceIndex: pos - before.length,
|
|
value: token,
|
|
before: before,
|
|
after: ''
|
|
});
|
|
before = '';
|
|
|
|
pos += 1;
|
|
code = value.charCodeAt(pos);
|
|
|
|
// Open parentheses
|
|
} else if (openParentheses === code) {
|
|
// Whitespaces after open parentheses
|
|
next = pos;
|
|
do {
|
|
next += 1;
|
|
code = value.charCodeAt(next);
|
|
} while (code <= 32);
|
|
token = {
|
|
type: 'function',
|
|
sourceIndex: pos - name.length,
|
|
value: name,
|
|
before: value.slice(pos + 1, next)
|
|
};
|
|
pos = next;
|
|
|
|
if (name === 'url' && code !== singleQuote && code !== doubleQuote) {
|
|
next -= 1;
|
|
do {
|
|
escape = false;
|
|
next = value.indexOf(')', next + 1);
|
|
if (~next) {
|
|
escapePos = next;
|
|
while (value.charCodeAt(escapePos - 1) === backslash) {
|
|
escapePos -= 1;
|
|
escape = !escape;
|
|
}
|
|
} else {
|
|
value += ')';
|
|
next = value.length - 1;
|
|
token.unclosed = true;
|
|
}
|
|
} while (escape);
|
|
// Whitespaces before closed
|
|
whitespacePos = next;
|
|
do {
|
|
whitespacePos -= 1;
|
|
code = value.charCodeAt(whitespacePos);
|
|
} while (code <= 32);
|
|
if (pos !== whitespacePos + 1) {
|
|
token.nodes = [{
|
|
type: 'word',
|
|
sourceIndex: pos,
|
|
value: value.slice(pos, whitespacePos + 1)
|
|
}];
|
|
} else {
|
|
token.nodes = [];
|
|
}
|
|
if (token.unclosed && whitespacePos + 1 !== next) {
|
|
token.after = '';
|
|
token.nodes.push({
|
|
type: 'space',
|
|
sourceIndex: whitespacePos + 1,
|
|
value: value.slice(whitespacePos + 1, next)
|
|
});
|
|
} else {
|
|
token.after = value.slice(whitespacePos + 1, next);
|
|
}
|
|
pos = next + 1;
|
|
code = value.charCodeAt(pos);
|
|
tokens.push(token);
|
|
} else {
|
|
balanced += 1;
|
|
token.after = '';
|
|
tokens.push(token);
|
|
stack.push(token);
|
|
tokens = token.nodes = [];
|
|
parent = token;
|
|
}
|
|
name = '';
|
|
|
|
// Close parentheses
|
|
} else if (closeParentheses === code && balanced) {
|
|
pos += 1;
|
|
code = value.charCodeAt(pos);
|
|
|
|
parent.after = after;
|
|
after = '';
|
|
balanced -= 1;
|
|
stack.pop();
|
|
parent = stack[balanced];
|
|
tokens = parent.nodes;
|
|
|
|
// Words
|
|
} else {
|
|
next = pos;
|
|
do {
|
|
if (code === backslash) {
|
|
next += 1;
|
|
}
|
|
next += 1;
|
|
code = value.charCodeAt(next);
|
|
} while (next < max && !(
|
|
code <= 32 ||
|
|
code === singleQuote ||
|
|
code === doubleQuote ||
|
|
code === comma ||
|
|
code === colon ||
|
|
code === slash ||
|
|
code === openParentheses ||
|
|
code === closeParentheses && balanced
|
|
));
|
|
token = value.slice(pos, next);
|
|
|
|
if (openParentheses === code) {
|
|
name = token;
|
|
} else {
|
|
tokens.push({
|
|
type: 'word',
|
|
sourceIndex: pos,
|
|
value: token
|
|
});
|
|
}
|
|
|
|
pos = next;
|
|
}
|
|
}
|
|
|
|
for (pos = stack.length - 1; pos; pos -= 1) {
|
|
stack[pos].unclosed = true;
|
|
}
|
|
|
|
return stack[0].nodes;
|
|
};
|