@tbela99/css-parser
Version:
CSS parser, minifier and validator for node and the browser
119 lines (116 loc) • 4.64 kB
JavaScript
import { EnumToken } from '../../ast/types.js';
import { tokensfuncDefMap, LOC } from '../../syntax/constants.js';
import { equalsIgnoreCase } from './text.js';
function matchGenericSyntax(stream) {
const stack = [];
let i = 0;
let success = true;
let expectAndOr = false;
let expectComma = false;
const errors = [];
const scopes = [new Set()];
for (; i < stream.length; i++) {
const token = stream[i];
if (token.typ === EnumToken.WhitespaceTokenType || token.typ === EnumToken.CommentTokenType) {
continue;
}
if (token.typ === EnumToken.StartParensTokenType || tokensfuncDefMap.has(token.typ)) {
stack.push(token);
scopes.push(new Set());
expectAndOr = false;
continue;
}
if (token.typ === EnumToken.EndParensTokenType) {
if (stack.length === 0 ||
(stack.at(-1)?.typ !== EnumToken.StartParensTokenType && !tokensfuncDefMap.has(stack.at(-1)?.typ))) {
errors.push({
action: "drop",
message: `unexpected token ${EnumToken[token.typ]} at ${token[LOC].src}:${token[LOC].sta.lin}:${token[LOC].sta.col}`,
node: token,
location: token[LOC],
});
success = false;
break;
}
stack.pop();
scopes.pop();
// if (scopes.length === 0) {
// return {
// success: false,
// errors: [
// {
// action: "drop",
// node: token,
// message: `Unexpected token ${EnumToken[token.typ]} at ${token[LOC]!.src}:${token[LOC]!.sta.lin}:${token[LOC]!.sta.col}`,
// location: token[LOC]!,
// },
// ],
// };
// }
continue;
}
if (token.typ === EnumToken.IdenTokenType && equalsIgnoreCase("and", token.val)) {
if (!expectAndOr || scopes.at(-1)?.has(EnumToken.OrTokenType)) {
errors.push({
action: "drop",
message: `unexpected token ${EnumToken[token.typ]} at ${token[LOC].src}:${token[LOC].sta.lin}:${token[LOC].sta.col}`,
node: token,
location: token[LOC],
});
success = false;
break;
}
Object.assign(token, { typ: EnumToken.AndTokenType });
scopes.at(-1).add(EnumToken.AndTokenType);
expectComma = false;
continue;
}
if (token.typ === EnumToken.IdenTokenType && equalsIgnoreCase("or", token.val)) {
if (!expectAndOr || scopes.at(-1)?.has(EnumToken.AndTokenType)) {
errors.push({
action: "drop",
message: `unexpected token ${EnumToken[token.typ]} at ${token[LOC].src}:${token[LOC].sta.lin}:${token[LOC].sta.col}`,
node: token,
location: token[LOC],
});
success = false;
break;
}
expectComma = false;
Object.assign(token, { typ: EnumToken.OrTokenType });
scopes.at(-1).add(EnumToken.OrTokenType);
continue;
}
if (token.typ === EnumToken.CommaTokenType) {
if (!expectComma) {
errors.push({
action: "drop",
message: `unexpected token ${EnumToken[token.typ]} at ${token[LOC].src}:${token[LOC].sta.lin}:${token[LOC].sta.col}`,
node: token,
location: token[LOC],
});
success = false;
break;
}
stack.push(token);
expectComma = false;
continue;
}
expectAndOr = true;
expectComma = true;
if (stack.length > 0 && stack.at(-1)?.typ === EnumToken.CommaTokenType) {
stack.pop();
}
}
if (stack.length > 0) {
errors.push({
action: "drop",
message: `unexpected token ${EnumToken[stack.at(-1)?.typ]} at ${stack.at(-1)?.[LOC]?.src}:${stack.at(-1)?.[LOC]?.sta.lin}:${stack.at(-1)?.[LOC]?.sta.col}`,
node: stack.at(-1),
location: stack.at(-1)?.[LOC],
});
success = false;
}
return { success, errors };
}
export { matchGenericSyntax };