-
Notifications
You must be signed in to change notification settings - Fork 1
/
parser.js
66 lines (59 loc) · 2.03 KB
/
parser.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
const { L_PAREN, R_PAREN, COMMA, COLON } = require("./tokens");
module.exports = function parser(tokens) {
let index = 0;
const parseCommaSeparatedList = () => {
const mediaQueryList = [];
do {
mediaQueryList.push(parseMediaQuery());
} while (maybe(COMMA));
return { any: mediaQueryList };
};
const parseMediaQuery = () => {
let isNot = false;
switch (maybeIdent().toLowerCase()) {
case "not":
isNot = true;
break;
case "":
break;
default:
E_UNEXPECTED("IDENT");
}
const featureList = [];
do {
const feature = parseFeature();
if (isNot) feature.not = true;
featureList.push(feature);
} while (maybeIdent().toLowerCase() === "and");
return { all: featureList };
};
const parseFeature = () => {
const expression = {};
read(L_PAREN);
expression.feature = readIdent();
if (maybe(COLON)) {
expression.value = readIdent();
} else {
expression.value = true;
}
read(R_PAREN);
return expression;
};
// `read*` methods:
// - will advance cursor if token is found
// - will throw an exception if token is not found
//
// `maybe*` methods:
// - will advance cursor if token is found
// - will return falsy if token is not found
/* eslint-disable prettier/prettier */
const E_EXPECTED = name => { throw new Error('Expected ' + name); };
const E_UNEXPECTED = name => { throw new Error('Unexpected ' + name); };
const read = token => { const t = maybe(token); if (!t) { E_EXPECTED(token.name); } return t; };
const readIdent = () => { const i = maybeIdent(); if (!i) { E_EXPECTED('IDENT'); } return i; };
const maybe = token => { const t = tokens[index]; if (t === token) { index++; return t; } };
const maybeIdent = () => { const i = tokens[index]; if (isString(i)) { index++; return i; } return ''; };
const isString = s => Object.prototype.toString.call(s) === '[object String]';
/* eslint-enable prettier/prettier */
return parseCommaSeparatedList();
};