format: prettify entire project

This commit is contained in:
Rim
2025-04-02 06:50:39 -04:00
parent 86f0782a98
commit 7ccc0be712
1711 changed files with 755867 additions and 235931 deletions

View File

@ -3,288 +3,293 @@
const types = require('../../tokenizer/types.cjs');
const charCodeDefinitions = require('../../tokenizer/char-code-definitions.cjs');
const PLUSSIGN = 0x002B; // U+002B PLUS SIGN (+)
const HYPHENMINUS = 0x002D; // U+002D HYPHEN-MINUS (-)
const N = 0x006E; // U+006E LATIN SMALL LETTER N (n)
const PLUSSIGN = 0x002b; // U+002B PLUS SIGN (+)
const HYPHENMINUS = 0x002d; // U+002D HYPHEN-MINUS (-)
const N = 0x006e; // U+006E LATIN SMALL LETTER N (n)
const DISALLOW_SIGN = true;
const ALLOW_SIGN = false;
function checkInteger(offset, disallowSign) {
let pos = this.tokenStart + offset;
const code = this.charCodeAt(pos);
let pos = this.tokenStart + offset;
const code = this.charCodeAt(pos);
if (code === PLUSSIGN || code === HYPHENMINUS) {
if (disallowSign) {
this.error('Number sign is not allowed');
}
pos++;
if (code === PLUSSIGN || code === HYPHENMINUS) {
if (disallowSign) {
this.error('Number sign is not allowed');
}
pos++;
}
for (; pos < this.tokenEnd; pos++) {
if (!charCodeDefinitions.isDigit(this.charCodeAt(pos))) {
this.error('Integer is expected', pos);
}
for (; pos < this.tokenEnd; pos++) {
if (!charCodeDefinitions.isDigit(this.charCodeAt(pos))) {
this.error('Integer is expected', pos);
}
}
}
function checkTokenIsInteger(disallowSign) {
return checkInteger.call(this, 0, disallowSign);
return checkInteger.call(this, 0, disallowSign);
}
function expectCharCode(offset, code) {
if (!this.cmpChar(this.tokenStart + offset, code)) {
let msg = '';
if (!this.cmpChar(this.tokenStart + offset, code)) {
let msg = '';
switch (code) {
case N:
msg = 'N is expected';
break;
case HYPHENMINUS:
msg = 'HyphenMinus is expected';
break;
}
this.error(msg, this.tokenStart + offset);
switch (code) {
case N:
msg = 'N is expected';
break;
case HYPHENMINUS:
msg = 'HyphenMinus is expected';
break;
}
this.error(msg, this.tokenStart + offset);
}
}
// ... <signed-integer>
// ... ['+' | '-'] <signless-integer>
function consumeB() {
let offset = 0;
let sign = 0;
let type = this.tokenType;
let offset = 0;
let sign = 0;
let type = this.tokenType;
while (type === types.WhiteSpace || type === types.Comment) {
while (type === types.WhiteSpace || type === types.Comment) {
type = this.lookupType(++offset);
}
if (type !== types.Number) {
if (this.isDelim(PLUSSIGN, offset) || this.isDelim(HYPHENMINUS, offset)) {
sign = this.isDelim(PLUSSIGN, offset) ? PLUSSIGN : HYPHENMINUS;
do {
type = this.lookupType(++offset);
}
} while (type === types.WhiteSpace || type === types.Comment);
if (type !== types.Number) {
if (this.isDelim(PLUSSIGN, offset) ||
this.isDelim(HYPHENMINUS, offset)) {
sign = this.isDelim(PLUSSIGN, offset) ? PLUSSIGN : HYPHENMINUS;
do {
type = this.lookupType(++offset);
} while (type === types.WhiteSpace || type === types.Comment);
if (type !== types.Number) {
this.skip(offset);
checkTokenIsInteger.call(this, DISALLOW_SIGN);
}
} else {
return null;
}
}
if (offset > 0) {
if (type !== types.Number) {
this.skip(offset);
checkTokenIsInteger.call(this, DISALLOW_SIGN);
}
} else {
return null;
}
}
if (sign === 0) {
type = this.charCodeAt(this.tokenStart);
if (type !== PLUSSIGN && type !== HYPHENMINUS) {
this.error('Number sign is expected');
}
if (offset > 0) {
this.skip(offset);
}
if (sign === 0) {
type = this.charCodeAt(this.tokenStart);
if (type !== PLUSSIGN && type !== HYPHENMINUS) {
this.error('Number sign is expected');
}
}
checkTokenIsInteger.call(this, sign !== 0);
return sign === HYPHENMINUS ? '-' + this.consume(types.Number) : this.consume(types.Number);
checkTokenIsInteger.call(this, sign !== 0);
return sign === HYPHENMINUS ?
'-' + this.consume(types.Number)
: this.consume(types.Number);
}
// An+B microsyntax https://www.w3.org/TR/css-syntax-3/#anb
const name = 'AnPlusB';
const structure = {
a: [String, null],
b: [String, null]
a: [String, null],
b: [String, null],
};
function parse() {
/* eslint-disable brace-style*/
const start = this.tokenStart;
let a = null;
let b = null;
/* eslint-disable brace-style*/
const start = this.tokenStart;
let a = null;
let b = null;
// <integer>
if (this.tokenType === types.Number) {
checkTokenIsInteger.call(this, ALLOW_SIGN);
b = this.consume(types.Number);
// <integer>
if (this.tokenType === types.Number) {
checkTokenIsInteger.call(this, ALLOW_SIGN);
b = this.consume(types.Number);
}
// -n
// -n <signed-integer>
// -n ['+' | '-'] <signless-integer>
// -n- <signless-integer>
// <dashndashdigit-ident>
else if (
this.tokenType === types.Ident &&
this.cmpChar(this.tokenStart, HYPHENMINUS)
) {
a = '-1';
expectCharCode.call(this, 1, N);
switch (this.tokenEnd - this.tokenStart) {
// -n
// -n <signed-integer>
// -n ['+' | '-'] <signless-integer>
case 2:
this.next();
b = consumeB.call(this);
break;
// -n- <signless-integer>
case 3:
expectCharCode.call(this, 2, HYPHENMINUS);
this.next();
this.skipSC();
checkTokenIsInteger.call(this, DISALLOW_SIGN);
b = '-' + this.consume(types.Number);
break;
// <dashndashdigit-ident>
default:
expectCharCode.call(this, 2, HYPHENMINUS);
checkInteger.call(this, 3, DISALLOW_SIGN);
this.next();
b = this.substrToCursor(start + 2);
}
}
// '+'? n
// '+'? n <signed-integer>
// '+'? n ['+' | '-'] <signless-integer>
// '+'? n- <signless-integer>
// '+'? <ndashdigit-ident>
else if (
this.tokenType === types.Ident ||
(this.isDelim(PLUSSIGN) && this.lookupType(1) === types.Ident)
) {
let sign = 0;
a = '1';
// just ignore a plus
if (this.isDelim(PLUSSIGN)) {
sign = 1;
this.next();
}
// -n
// -n <signed-integer>
// -n ['+' | '-'] <signless-integer>
// -n- <signless-integer>
// <dashndashdigit-ident>
else if (this.tokenType === types.Ident && this.cmpChar(this.tokenStart, HYPHENMINUS)) {
a = '-1';
expectCharCode.call(this, 0, N);
expectCharCode.call(this, 1, N);
switch (this.tokenEnd - this.tokenStart) {
// '+'? n
// '+'? n <signed-integer>
// '+'? n ['+' | '-'] <signless-integer>
case 1:
this.next();
b = consumeB.call(this);
break;
switch (this.tokenEnd - this.tokenStart) {
// -n
// -n <signed-integer>
// -n ['+' | '-'] <signless-integer>
case 2:
this.next();
b = consumeB.call(this);
break;
// '+'? n- <signless-integer>
case 2:
expectCharCode.call(this, 1, HYPHENMINUS);
// -n- <signless-integer>
case 3:
expectCharCode.call(this, 2, HYPHENMINUS);
this.next();
this.skipSC();
this.next();
this.skipSC();
checkTokenIsInteger.call(this, DISALLOW_SIGN);
checkTokenIsInteger.call(this, DISALLOW_SIGN);
b = '-' + this.consume(types.Number);
break;
b = '-' + this.consume(types.Number);
break;
// '+'? <ndashdigit-ident>
default:
expectCharCode.call(this, 1, HYPHENMINUS);
checkInteger.call(this, 2, DISALLOW_SIGN);
this.next();
// <dashndashdigit-ident>
default:
expectCharCode.call(this, 2, HYPHENMINUS);
checkInteger.call(this, 3, DISALLOW_SIGN);
this.next();
b = this.substrToCursor(start + sign + 1);
}
}
b = this.substrToCursor(start + 2);
}
// <ndashdigit-dimension>
// <ndash-dimension> <signless-integer>
// <n-dimension>
// <n-dimension> <signed-integer>
// <n-dimension> ['+' | '-'] <signless-integer>
else if (this.tokenType === types.Dimension) {
const code = this.charCodeAt(this.tokenStart);
const sign = code === PLUSSIGN || code === HYPHENMINUS;
let i = this.tokenStart + sign;
for (; i < this.tokenEnd; i++) {
if (!charCodeDefinitions.isDigit(this.charCodeAt(i))) {
break;
}
}
// '+'? n
// '+'? n <signed-integer>
// '+'? n ['+' | '-'] <signless-integer>
// '+'? n- <signless-integer>
// '+'? <ndashdigit-ident>
else if (this.tokenType === types.Ident || (this.isDelim(PLUSSIGN) && this.lookupType(1) === types.Ident)) {
let sign = 0;
a = '1';
// just ignore a plus
if (this.isDelim(PLUSSIGN)) {
sign = 1;
this.next();
}
expectCharCode.call(this, 0, N);
switch (this.tokenEnd - this.tokenStart) {
// '+'? n
// '+'? n <signed-integer>
// '+'? n ['+' | '-'] <signless-integer>
case 1:
this.next();
b = consumeB.call(this);
break;
// '+'? n- <signless-integer>
case 2:
expectCharCode.call(this, 1, HYPHENMINUS);
this.next();
this.skipSC();
checkTokenIsInteger.call(this, DISALLOW_SIGN);
b = '-' + this.consume(types.Number);
break;
// '+'? <ndashdigit-ident>
default:
expectCharCode.call(this, 1, HYPHENMINUS);
checkInteger.call(this, 2, DISALLOW_SIGN);
this.next();
b = this.substrToCursor(start + sign + 1);
}
if (i === this.tokenStart + sign) {
this.error('Integer is expected', this.tokenStart + sign);
}
// <ndashdigit-dimension>
// <ndash-dimension> <signless-integer>
expectCharCode.call(this, i - this.tokenStart, N);
a = this.substring(start, i);
// <n-dimension>
// <n-dimension> <signed-integer>
// <n-dimension> ['+' | '-'] <signless-integer>
else if (this.tokenType === types.Dimension) {
const code = this.charCodeAt(this.tokenStart);
const sign = code === PLUSSIGN || code === HYPHENMINUS;
let i = this.tokenStart + sign;
for (; i < this.tokenEnd; i++) {
if (!charCodeDefinitions.isDigit(this.charCodeAt(i))) {
break;
}
}
if (i === this.tokenStart + sign) {
this.error('Integer is expected', this.tokenStart + sign);
}
expectCharCode.call(this, i - this.tokenStart, N);
a = this.substring(start, i);
// <n-dimension>
// <n-dimension> <signed-integer>
// <n-dimension> ['+' | '-'] <signless-integer>
if (i + 1 === this.tokenEnd) {
this.next();
b = consumeB.call(this);
} else {
expectCharCode.call(this, i - this.tokenStart + 1, HYPHENMINUS);
// <ndash-dimension> <signless-integer>
if (i + 2 === this.tokenEnd) {
this.next();
this.skipSC();
checkTokenIsInteger.call(this, DISALLOW_SIGN);
b = '-' + this.consume(types.Number);
}
// <ndashdigit-dimension>
else {
checkInteger.call(this, i - this.tokenStart + 2, DISALLOW_SIGN);
this.next();
b = this.substrToCursor(i + 1);
}
}
if (i + 1 === this.tokenEnd) {
this.next();
b = consumeB.call(this);
} else {
this.error();
}
expectCharCode.call(this, i - this.tokenStart + 1, HYPHENMINUS);
if (a !== null && a.charCodeAt(0) === PLUSSIGN) {
a = a.substr(1);
// <ndash-dimension> <signless-integer>
if (i + 2 === this.tokenEnd) {
this.next();
this.skipSC();
checkTokenIsInteger.call(this, DISALLOW_SIGN);
b = '-' + this.consume(types.Number);
}
// <ndashdigit-dimension>
else {
checkInteger.call(this, i - this.tokenStart + 2, DISALLOW_SIGN);
this.next();
b = this.substrToCursor(i + 1);
}
}
} else {
this.error();
}
if (b !== null && b.charCodeAt(0) === PLUSSIGN) {
b = b.substr(1);
}
if (a !== null && a.charCodeAt(0) === PLUSSIGN) {
a = a.substr(1);
}
return {
type: 'AnPlusB',
loc: this.getLocation(start, this.tokenStart),
a,
b
};
if (b !== null && b.charCodeAt(0) === PLUSSIGN) {
b = b.substr(1);
}
return {
type: 'AnPlusB',
loc: this.getLocation(start, this.tokenStart),
a,
b,
};
}
function generate(node) {
if (node.a) {
const a =
node.a === '+1' && 'n' ||
node.a === '1' && 'n' ||
node.a === '-1' && '-n' ||
node.a + 'n';
if (node.a) {
const a =
(node.a === '+1' && 'n') ||
(node.a === '1' && 'n') ||
(node.a === '-1' && '-n') ||
node.a + 'n';
if (node.b) {
const b = node.b[0] === '-' || node.b[0] === '+'
? node.b
: '+' + node.b;
this.tokenize(a + b);
} else {
this.tokenize(a);
}
if (node.b) {
const b = node.b[0] === '-' || node.b[0] === '+' ? node.b : '+' + node.b;
this.tokenize(a + b);
} else {
this.tokenize(node.b);
this.tokenize(a);
}
} else {
this.tokenize(node.b);
}
}
exports.generate = generate;

View File

@ -3,97 +3,106 @@
const types = require('../../tokenizer/types.cjs');
function consumeRaw(startToken) {
return this.Raw(startToken, this.consumeUntilLeftCurlyBracketOrSemicolon, true);
return this.Raw(
startToken,
this.consumeUntilLeftCurlyBracketOrSemicolon,
true
);
}
function isDeclarationBlockAtrule() {
for (let offset = 1, type; type = this.lookupType(offset); offset++) {
if (type === types.RightCurlyBracket) {
return true;
}
if (type === types.LeftCurlyBracket ||
type === types.AtKeyword) {
return false;
}
for (let offset = 1, type; (type = this.lookupType(offset)); offset++) {
if (type === types.RightCurlyBracket) {
return true;
}
return false;
}
if (type === types.LeftCurlyBracket || type === types.AtKeyword) {
return false;
}
}
return false;
}
const name = 'Atrule';
const walkContext = 'atrule';
const structure = {
name: String,
prelude: ['AtrulePrelude', 'Raw', null],
block: ['Block', null]
name: String,
prelude: ['AtrulePrelude', 'Raw', null],
block: ['Block', null],
};
function parse() {
const start = this.tokenStart;
let name;
let nameLowerCase;
let prelude = null;
let block = null;
const start = this.tokenStart;
let name;
let nameLowerCase;
let prelude = null;
let block = null;
this.eat(types.AtKeyword);
this.eat(types.AtKeyword);
name = this.substrToCursor(start + 1);
nameLowerCase = name.toLowerCase();
this.skipSC();
// parse prelude
if (
this.eof === false &&
this.tokenType !== types.LeftCurlyBracket &&
this.tokenType !== types.Semicolon
) {
if (this.parseAtrulePrelude) {
prelude = this.parseWithFallback(
this.AtrulePrelude.bind(this, name),
consumeRaw
);
} else {
prelude = consumeRaw.call(this, this.tokenIndex);
}
name = this.substrToCursor(start + 1);
nameLowerCase = name.toLowerCase();
this.skipSC();
}
// parse prelude
if (this.eof === false &&
this.tokenType !== types.LeftCurlyBracket &&
this.tokenType !== types.Semicolon) {
if (this.parseAtrulePrelude) {
prelude = this.parseWithFallback(this.AtrulePrelude.bind(this, name), consumeRaw);
} else {
prelude = consumeRaw.call(this, this.tokenIndex);
}
switch (this.tokenType) {
case types.Semicolon:
this.next();
break;
this.skipSC();
}
case types.LeftCurlyBracket:
if (
hasOwnProperty.call(this.atrule, nameLowerCase) &&
typeof this.atrule[nameLowerCase].block === 'function'
) {
block = this.atrule[nameLowerCase].block.call(this);
} else {
// TODO: should consume block content as Raw?
block = this.Block(isDeclarationBlockAtrule.call(this));
}
switch (this.tokenType) {
case types.Semicolon:
this.next();
break;
break;
}
case types.LeftCurlyBracket:
if (hasOwnProperty.call(this.atrule, nameLowerCase) &&
typeof this.atrule[nameLowerCase].block === 'function') {
block = this.atrule[nameLowerCase].block.call(this);
} else {
// TODO: should consume block content as Raw?
block = this.Block(isDeclarationBlockAtrule.call(this));
}
break;
}
return {
type: 'Atrule',
loc: this.getLocation(start, this.tokenStart),
name,
prelude,
block
};
return {
type: 'Atrule',
loc: this.getLocation(start, this.tokenStart),
name,
prelude,
block,
};
}
function generate(node) {
this.token(types.AtKeyword, '@' + node.name);
this.token(types.AtKeyword, '@' + node.name);
if (node.prelude !== null) {
this.node(node.prelude);
}
if (node.prelude !== null) {
this.node(node.prelude);
}
if (node.block) {
this.node(node.block);
} else {
this.token(types.Semicolon, ';');
}
if (node.block) {
this.node(node.block);
} else {
this.token(types.Semicolon, ';');
}
}
exports.generate = generate;

View File

@ -5,44 +5,48 @@ const types = require('../../tokenizer/types.cjs');
const name = 'AtrulePrelude';
const walkContext = 'atrulePrelude';
const structure = {
children: [[]]
children: [[]],
};
function parse(name) {
let children = null;
let children = null;
if (name !== null) {
name = name.toLowerCase();
}
if (name !== null) {
name = name.toLowerCase();
}
this.skipSC();
this.skipSC();
if (hasOwnProperty.call(this.atrule, name) &&
typeof this.atrule[name].prelude === 'function') {
// custom consumer
children = this.atrule[name].prelude.call(this);
} else {
// default consumer
children = this.readSequence(this.scope.AtrulePrelude);
}
if (
hasOwnProperty.call(this.atrule, name) &&
typeof this.atrule[name].prelude === 'function'
) {
// custom consumer
children = this.atrule[name].prelude.call(this);
} else {
// default consumer
children = this.readSequence(this.scope.AtrulePrelude);
}
this.skipSC();
this.skipSC();
if (this.eof !== true &&
this.tokenType !== types.LeftCurlyBracket &&
this.tokenType !== types.Semicolon) {
this.error('Semicolon or block is expected');
}
if (
this.eof !== true &&
this.tokenType !== types.LeftCurlyBracket &&
this.tokenType !== types.Semicolon
) {
this.error('Semicolon or block is expected');
}
return {
type: 'AtrulePrelude',
loc: this.getLocationFromList(children),
children
};
return {
type: 'AtrulePrelude',
loc: this.getLocationFromList(children),
children,
};
}
function generate(node) {
this.children(node);
this.children(node);
}
exports.generate = generate;

View File

@ -2,144 +2,144 @@
const types = require('../../tokenizer/types.cjs');
const DOLLARSIGN = 0x0024; // U+0024 DOLLAR SIGN ($)
const ASTERISK = 0x002A; // U+002A ASTERISK (*)
const EQUALSSIGN = 0x003D; // U+003D EQUALS SIGN (=)
const CIRCUMFLEXACCENT = 0x005E; // U+005E (^)
const VERTICALLINE = 0x007C; // U+007C VERTICAL LINE (|)
const TILDE = 0x007E; // U+007E TILDE (~)
const DOLLARSIGN = 0x0024; // U+0024 DOLLAR SIGN ($)
const ASTERISK = 0x002a; // U+002A ASTERISK (*)
const EQUALSSIGN = 0x003d; // U+003D EQUALS SIGN (=)
const CIRCUMFLEXACCENT = 0x005e; // U+005E (^)
const VERTICALLINE = 0x007c; // U+007C VERTICAL LINE (|)
const TILDE = 0x007e; // U+007E TILDE (~)
function getAttributeName() {
if (this.eof) {
this.error('Unexpected end of input');
}
if (this.eof) {
this.error('Unexpected end of input');
}
const start = this.tokenStart;
let expectIdent = false;
const start = this.tokenStart;
let expectIdent = false;
if (this.isDelim(ASTERISK)) {
expectIdent = true;
this.next();
} else if (!this.isDelim(VERTICALLINE)) {
this.eat(types.Ident);
}
if (this.isDelim(ASTERISK)) {
expectIdent = true;
this.next();
} else if (!this.isDelim(VERTICALLINE)) {
this.eat(types.Ident);
}
if (this.isDelim(VERTICALLINE)) {
if (this.charCodeAt(this.tokenStart + 1) !== EQUALSSIGN) {
this.next();
this.eat(types.Ident);
} else if (expectIdent) {
this.error('Identifier is expected', this.tokenEnd);
}
if (this.isDelim(VERTICALLINE)) {
if (this.charCodeAt(this.tokenStart + 1) !== EQUALSSIGN) {
this.next();
this.eat(types.Ident);
} else if (expectIdent) {
this.error('Vertical line is expected');
this.error('Identifier is expected', this.tokenEnd);
}
} else if (expectIdent) {
this.error('Vertical line is expected');
}
return {
type: 'Identifier',
loc: this.getLocation(start, this.tokenStart),
name: this.substrToCursor(start)
};
return {
type: 'Identifier',
loc: this.getLocation(start, this.tokenStart),
name: this.substrToCursor(start),
};
}
function getOperator() {
const start = this.tokenStart;
const code = this.charCodeAt(start);
const start = this.tokenStart;
const code = this.charCodeAt(start);
if (code !== EQUALSSIGN && // =
code !== TILDE && // ~=
code !== CIRCUMFLEXACCENT && // ^=
code !== DOLLARSIGN && // $=
code !== ASTERISK && // *=
code !== VERTICALLINE // |=
) {
this.error('Attribute selector (=, ~=, ^=, $=, *=, |=) is expected');
if (
code !== EQUALSSIGN && // =
code !== TILDE && // ~=
code !== CIRCUMFLEXACCENT && // ^=
code !== DOLLARSIGN && // $=
code !== ASTERISK && // *=
code !== VERTICALLINE // |=
) {
this.error('Attribute selector (=, ~=, ^=, $=, *=, |=) is expected');
}
this.next();
if (code !== EQUALSSIGN) {
if (!this.isDelim(EQUALSSIGN)) {
this.error('Equal sign is expected');
}
this.next();
}
if (code !== EQUALSSIGN) {
if (!this.isDelim(EQUALSSIGN)) {
this.error('Equal sign is expected');
}
this.next();
}
return this.substrToCursor(start);
return this.substrToCursor(start);
}
// '[' <wq-name> ']'
// '[' <wq-name> <attr-matcher> [ <string-token> | <ident-token> ] <attr-modifier>? ']'
const name = 'AttributeSelector';
const structure = {
name: 'Identifier',
matcher: [String, null],
value: ['String', 'Identifier', null],
flags: [String, null]
name: 'Identifier',
matcher: [String, null],
value: ['String', 'Identifier', null],
flags: [String, null],
};
function parse() {
const start = this.tokenStart;
let name;
let matcher = null;
let value = null;
let flags = null;
const start = this.tokenStart;
let name;
let matcher = null;
let value = null;
let flags = null;
this.eat(types.LeftSquareBracket);
this.skipSC();
this.eat(types.LeftSquareBracket);
this.skipSC();
name = getAttributeName.call(this);
this.skipSC();
name = getAttributeName.call(this);
this.skipSC();
if (this.tokenType !== types.RightSquareBracket) {
// avoid case `[name i]`
if (this.tokenType !== types.Ident) {
matcher = getOperator.call(this);
if (this.tokenType !== types.RightSquareBracket) {
// avoid case `[name i]`
if (this.tokenType !== types.Ident) {
matcher = getOperator.call(this);
this.skipSC();
this.skipSC();
value = this.tokenType === types.String
? this.String()
: this.Identifier();
value =
this.tokenType === types.String ? this.String() : this.Identifier();
this.skipSC();
}
// attribute flags
if (this.tokenType === types.Ident) {
flags = this.consume(types.Ident);
this.skipSC();
}
this.skipSC();
}
this.eat(types.RightSquareBracket);
// attribute flags
if (this.tokenType === types.Ident) {
flags = this.consume(types.Ident);
return {
type: 'AttributeSelector',
loc: this.getLocation(start, this.tokenStart),
name,
matcher,
value,
flags
};
this.skipSC();
}
}
this.eat(types.RightSquareBracket);
return {
type: 'AttributeSelector',
loc: this.getLocation(start, this.tokenStart),
name,
matcher,
value,
flags,
};
}
function generate(node) {
this.token(types.Delim, '[');
this.node(node.name);
this.token(types.Delim, '[');
this.node(node.name);
if (node.matcher !== null) {
this.tokenize(node.matcher);
this.node(node.value);
}
if (node.matcher !== null) {
this.tokenize(node.matcher);
this.node(node.value);
}
if (node.flags !== null) {
this.token(types.Ident, node.flags);
}
if (node.flags !== null) {
this.token(types.Ident, node.flags);
}
this.token(types.Delim, ']');
this.token(types.Delim, ']');
}
exports.generate = generate;

View File

@ -3,84 +3,79 @@
const types = require('../../tokenizer/types.cjs');
function consumeRaw(startToken) {
return this.Raw(startToken, null, true);
return this.Raw(startToken, null, true);
}
function consumeRule() {
return this.parseWithFallback(this.Rule, consumeRaw);
return this.parseWithFallback(this.Rule, consumeRaw);
}
function consumeRawDeclaration(startToken) {
return this.Raw(startToken, this.consumeUntilSemicolonIncluded, true);
return this.Raw(startToken, this.consumeUntilSemicolonIncluded, true);
}
function consumeDeclaration() {
if (this.tokenType === types.Semicolon) {
return consumeRawDeclaration.call(this, this.tokenIndex);
}
if (this.tokenType === types.Semicolon) {
return consumeRawDeclaration.call(this, this.tokenIndex);
}
const node = this.parseWithFallback(this.Declaration, consumeRawDeclaration);
const node = this.parseWithFallback(this.Declaration, consumeRawDeclaration);
if (this.tokenType === types.Semicolon) {
this.next();
}
if (this.tokenType === types.Semicolon) {
this.next();
}
return node;
return node;
}
const name = 'Block';
const walkContext = 'block';
const structure = {
children: [[
'Atrule',
'Rule',
'Declaration'
]]
children: [['Atrule', 'Rule', 'Declaration']],
};
function parse(isDeclaration) {
const consumer = isDeclaration ? consumeDeclaration : consumeRule;
const start = this.tokenStart;
let children = this.createList();
const consumer = isDeclaration ? consumeDeclaration : consumeRule;
const start = this.tokenStart;
let children = this.createList();
this.eat(types.LeftCurlyBracket);
this.eat(types.LeftCurlyBracket);
scan:
while (!this.eof) {
switch (this.tokenType) {
case types.RightCurlyBracket:
break scan;
scan: while (!this.eof) {
switch (this.tokenType) {
case types.RightCurlyBracket:
break scan;
case types.WhiteSpace:
case types.Comment:
this.next();
break;
case types.WhiteSpace:
case types.Comment:
this.next();
break;
case types.AtKeyword:
children.push(this.parseWithFallback(this.Atrule, consumeRaw));
break;
case types.AtKeyword:
children.push(this.parseWithFallback(this.Atrule, consumeRaw));
break;
default:
children.push(consumer.call(this));
}
default:
children.push(consumer.call(this));
}
}
if (!this.eof) {
this.eat(types.RightCurlyBracket);
}
if (!this.eof) {
this.eat(types.RightCurlyBracket);
}
return {
type: 'Block',
loc: this.getLocation(start, this.tokenStart),
children
};
return {
type: 'Block',
loc: this.getLocation(start, this.tokenStart),
children,
};
}
function generate(node) {
this.token(types.LeftCurlyBracket, '{');
this.children(node, prev => {
if (prev.type === 'Declaration') {
this.token(types.Semicolon, ';');
}
});
this.token(types.RightCurlyBracket, '}');
this.token(types.LeftCurlyBracket, '{');
this.children(node, (prev) => {
if (prev.type === 'Declaration') {
this.token(types.Semicolon, ';');
}
});
this.token(types.RightCurlyBracket, '}');
}
exports.generate = generate;

View File

@ -4,32 +4,32 @@ const types = require('../../tokenizer/types.cjs');
const name = 'Brackets';
const structure = {
children: [[]]
children: [[]],
};
function parse(readSequence, recognizer) {
const start = this.tokenStart;
let children = null;
const start = this.tokenStart;
let children = null;
this.eat(types.LeftSquareBracket);
this.eat(types.LeftSquareBracket);
children = readSequence.call(this, recognizer);
children = readSequence.call(this, recognizer);
if (!this.eof) {
this.eat(types.RightSquareBracket);
}
if (!this.eof) {
this.eat(types.RightSquareBracket);
}
return {
type: 'Brackets',
loc: this.getLocation(start, this.tokenStart),
children
};
return {
type: 'Brackets',
loc: this.getLocation(start, this.tokenStart),
children,
};
}
function generate(node) {
this.token(types.Delim, '[');
this.children(node);
this.token(types.Delim, ']');
this.token(types.Delim, '[');
this.children(node);
this.token(types.Delim, ']');
}
exports.generate = generate;

View File

@ -6,18 +6,18 @@ const name = 'CDC';
const structure = [];
function parse() {
const start = this.tokenStart;
const start = this.tokenStart;
this.eat(types.CDC); // -->
this.eat(types.CDC); // -->
return {
type: 'CDC',
loc: this.getLocation(start, this.tokenStart)
};
return {
type: 'CDC',
loc: this.getLocation(start, this.tokenStart),
};
}
function generate() {
this.token(types.CDC, '-->');
this.token(types.CDC, '-->');
}
exports.generate = generate;

View File

@ -6,18 +6,18 @@ const name = 'CDO';
const structure = [];
function parse() {
const start = this.tokenStart;
const start = this.tokenStart;
this.eat(types.CDO); // <!--
this.eat(types.CDO); // <!--
return {
type: 'CDO',
loc: this.getLocation(start, this.tokenStart)
};
return {
type: 'CDO',
loc: this.getLocation(start, this.tokenStart),
};
}
function generate() {
this.token(types.CDO, '<!--');
this.token(types.CDO, '<!--');
}
exports.generate = generate;

View File

@ -2,27 +2,27 @@
const types = require('../../tokenizer/types.cjs');
const FULLSTOP = 0x002E; // U+002E FULL STOP (.)
const FULLSTOP = 0x002e; // U+002E FULL STOP (.)
// '.' ident
const name = 'ClassSelector';
const structure = {
name: String
name: String,
};
function parse() {
this.eatDelim(FULLSTOP);
this.eatDelim(FULLSTOP);
return {
type: 'ClassSelector',
loc: this.getLocation(this.tokenStart - 1, this.tokenEnd),
name: this.consume(types.Ident)
};
return {
type: 'ClassSelector',
loc: this.getLocation(this.tokenStart - 1, this.tokenEnd),
name: this.consume(types.Ident),
};
}
function generate(node) {
this.token(types.Delim, '.');
this.token(types.Ident, node.name);
this.token(types.Delim, '.');
this.token(types.Ident, node.name);
}
exports.generate = generate;

View File

@ -2,57 +2,57 @@
const types = require('../../tokenizer/types.cjs');
const PLUSSIGN = 0x002B; // U+002B PLUS SIGN (+)
const SOLIDUS = 0x002F; // U+002F SOLIDUS (/)
const GREATERTHANSIGN = 0x003E; // U+003E GREATER-THAN SIGN (>)
const TILDE = 0x007E; // U+007E TILDE (~)
const PLUSSIGN = 0x002b; // U+002B PLUS SIGN (+)
const SOLIDUS = 0x002f; // U+002F SOLIDUS (/)
const GREATERTHANSIGN = 0x003e; // U+003E GREATER-THAN SIGN (>)
const TILDE = 0x007e; // U+007E TILDE (~)
const name = 'Combinator';
const structure = {
name: String
name: String,
};
// + | > | ~ | /deep/
function parse() {
const start = this.tokenStart;
let name;
const start = this.tokenStart;
let name;
switch (this.tokenType) {
case types.WhiteSpace:
name = ' ';
break;
switch (this.tokenType) {
case types.WhiteSpace:
name = ' ';
break;
case types.Delim:
switch (this.charCodeAt(this.tokenStart)) {
case GREATERTHANSIGN:
case PLUSSIGN:
case TILDE:
this.next();
break;
case types.Delim:
switch (this.charCodeAt(this.tokenStart)) {
case GREATERTHANSIGN:
case PLUSSIGN:
case TILDE:
this.next();
break;
case SOLIDUS:
this.next();
this.eatIdent('deep');
this.eatDelim(SOLIDUS);
break;
case SOLIDUS:
this.next();
this.eatIdent('deep');
this.eatDelim(SOLIDUS);
break;
default:
this.error('Combinator is expected');
}
default:
this.error('Combinator is expected');
}
name = this.substrToCursor(start);
break;
}
name = this.substrToCursor(start);
break;
}
return {
type: 'Combinator',
loc: this.getLocation(start, this.tokenStart),
name
};
return {
type: 'Combinator',
loc: this.getLocation(start, this.tokenStart),
name,
};
}
function generate(node) {
this.tokenize(node.name);
this.tokenize(node.name);
}
exports.generate = generate;

View File

@ -2,36 +2,37 @@
const types = require('../../tokenizer/types.cjs');
const ASTERISK = 0x002A; // U+002A ASTERISK (*)
const SOLIDUS = 0x002F; // U+002F SOLIDUS (/)
const ASTERISK = 0x002a; // U+002A ASTERISK (*)
const SOLIDUS = 0x002f; // U+002F SOLIDUS (/)
const name = 'Comment';
const structure = {
value: String
value: String,
};
function parse() {
const start = this.tokenStart;
let end = this.tokenEnd;
const start = this.tokenStart;
let end = this.tokenEnd;
this.eat(types.Comment);
this.eat(types.Comment);
if ((end - start + 2) >= 2 &&
this.charCodeAt(end - 2) === ASTERISK &&
this.charCodeAt(end - 1) === SOLIDUS) {
end -= 2;
}
if (
end - start + 2 >= 2 &&
this.charCodeAt(end - 2) === ASTERISK &&
this.charCodeAt(end - 1) === SOLIDUS
) {
end -= 2;
}
return {
type: 'Comment',
loc: this.getLocation(start, this.tokenStart),
value: this.substring(start + 2, end)
};
return {
type: 'Comment',
loc: this.getLocation(start, this.tokenStart),
value: this.substring(start + 2, end),
};
}
function generate(node) {
this.token(types.Comment, '/*' + node.value + '*/');
this.token(types.Comment, '/*' + node.value + '*/');
}
exports.generate = generate;

View File

@ -4,159 +4,176 @@ const names = require('../../utils/names.cjs');
const types = require('../../tokenizer/types.cjs');
const EXCLAMATIONMARK = 0x0021; // U+0021 EXCLAMATION MARK (!)
const NUMBERSIGN = 0x0023; // U+0023 NUMBER SIGN (#)
const DOLLARSIGN = 0x0024; // U+0024 DOLLAR SIGN ($)
const AMPERSAND = 0x0026; // U+0026 AMPERSAND (&)
const ASTERISK = 0x002A; // U+002A ASTERISK (*)
const PLUSSIGN = 0x002B; // U+002B PLUS SIGN (+)
const SOLIDUS = 0x002F; // U+002F SOLIDUS (/)
const NUMBERSIGN = 0x0023; // U+0023 NUMBER SIGN (#)
const DOLLARSIGN = 0x0024; // U+0024 DOLLAR SIGN ($)
const AMPERSAND = 0x0026; // U+0026 AMPERSAND (&)
const ASTERISK = 0x002a; // U+002A ASTERISK (*)
const PLUSSIGN = 0x002b; // U+002B PLUS SIGN (+)
const SOLIDUS = 0x002f; // U+002F SOLIDUS (/)
function consumeValueRaw(startToken) {
return this.Raw(startToken, this.consumeUntilExclamationMarkOrSemicolon, true);
return this.Raw(
startToken,
this.consumeUntilExclamationMarkOrSemicolon,
true
);
}
function consumeCustomPropertyRaw(startToken) {
return this.Raw(startToken, this.consumeUntilExclamationMarkOrSemicolon, false);
return this.Raw(
startToken,
this.consumeUntilExclamationMarkOrSemicolon,
false
);
}
function consumeValue() {
const startValueToken = this.tokenIndex;
const value = this.Value();
const startValueToken = this.tokenIndex;
const value = this.Value();
if (value.type !== 'Raw' &&
this.eof === false &&
this.tokenType !== types.Semicolon &&
this.isDelim(EXCLAMATIONMARK) === false &&
this.isBalanceEdge(startValueToken) === false) {
this.error();
}
if (
value.type !== 'Raw' &&
this.eof === false &&
this.tokenType !== types.Semicolon &&
this.isDelim(EXCLAMATIONMARK) === false &&
this.isBalanceEdge(startValueToken) === false
) {
this.error();
}
return value;
return value;
}
const name = 'Declaration';
const walkContext = 'declaration';
const structure = {
important: [Boolean, String],
property: String,
value: ['Value', 'Raw']
important: [Boolean, String],
property: String,
value: ['Value', 'Raw'],
};
function parse() {
const start = this.tokenStart;
const startToken = this.tokenIndex;
const property = readProperty.call(this);
const customProperty = names.isCustomProperty(property);
const parseValue = customProperty ? this.parseCustomProperty : this.parseValue;
const consumeRaw = customProperty ? consumeCustomPropertyRaw : consumeValueRaw;
let important = false;
let value;
const start = this.tokenStart;
const startToken = this.tokenIndex;
const property = readProperty.call(this);
const customProperty = names.isCustomProperty(property);
const parseValue =
customProperty ? this.parseCustomProperty : this.parseValue;
const consumeRaw =
customProperty ? consumeCustomPropertyRaw : consumeValueRaw;
let important = false;
let value;
this.skipSC();
this.eat(types.Colon);
const valueStart = this.tokenIndex;
if (!customProperty) {
this.skipSC();
this.eat(types.Colon);
}
const valueStart = this.tokenIndex;
if (parseValue) {
value = this.parseWithFallback(consumeValue, consumeRaw);
} else {
value = consumeRaw.call(this, this.tokenIndex);
}
if (!customProperty) {
this.skipSC();
if (customProperty && value.type === 'Value' && value.children.isEmpty) {
for (let offset = valueStart - this.tokenIndex; offset <= 0; offset++) {
if (this.lookupType(offset) === types.WhiteSpace) {
value.children.appendData({
type: 'WhiteSpace',
loc: null,
value: ' ',
});
break;
}
}
}
if (parseValue) {
value = this.parseWithFallback(consumeValue, consumeRaw);
} else {
value = consumeRaw.call(this, this.tokenIndex);
}
if (this.isDelim(EXCLAMATIONMARK)) {
important = getImportant.call(this);
this.skipSC();
}
if (customProperty && value.type === 'Value' && value.children.isEmpty) {
for (let offset = valueStart - this.tokenIndex; offset <= 0; offset++) {
if (this.lookupType(offset) === types.WhiteSpace) {
value.children.appendData({
type: 'WhiteSpace',
loc: null,
value: ' '
});
break;
}
}
}
// Do not include semicolon to range per spec
// https://drafts.csswg.org/css-syntax/#declaration-diagram
if (this.isDelim(EXCLAMATIONMARK)) {
important = getImportant.call(this);
this.skipSC();
}
if (
this.eof === false &&
this.tokenType !== types.Semicolon &&
this.isBalanceEdge(startToken) === false
) {
this.error();
}
// Do not include semicolon to range per spec
// https://drafts.csswg.org/css-syntax/#declaration-diagram
if (this.eof === false &&
this.tokenType !== types.Semicolon &&
this.isBalanceEdge(startToken) === false) {
this.error();
}
return {
type: 'Declaration',
loc: this.getLocation(start, this.tokenStart),
important,
property,
value
};
return {
type: 'Declaration',
loc: this.getLocation(start, this.tokenStart),
important,
property,
value,
};
}
function generate(node) {
this.token(types.Ident, node.property);
this.token(types.Colon, ':');
this.node(node.value);
this.token(types.Ident, node.property);
this.token(types.Colon, ':');
this.node(node.value);
if (node.important) {
this.token(types.Delim, '!');
this.token(types.Ident, node.important === true ? 'important' : node.important);
}
if (node.important) {
this.token(types.Delim, '!');
this.token(
types.Ident,
node.important === true ? 'important' : node.important
);
}
}
function readProperty() {
const start = this.tokenStart;
const start = this.tokenStart;
// hacks
if (this.tokenType === types.Delim) {
switch (this.charCodeAt(this.tokenStart)) {
case ASTERISK:
case DOLLARSIGN:
case PLUSSIGN:
case NUMBERSIGN:
case AMPERSAND:
this.next();
break;
// hacks
if (this.tokenType === types.Delim) {
switch (this.charCodeAt(this.tokenStart)) {
case ASTERISK:
case DOLLARSIGN:
case PLUSSIGN:
case NUMBERSIGN:
case AMPERSAND:
this.next();
break;
// TODO: not sure we should support this hack
case SOLIDUS:
this.next();
if (this.isDelim(SOLIDUS)) {
this.next();
}
break;
// TODO: not sure we should support this hack
case SOLIDUS:
this.next();
if (this.isDelim(SOLIDUS)) {
this.next();
}
break;
}
}
if (this.tokenType === types.Hash) {
this.eat(types.Hash);
} else {
this.eat(types.Ident);
}
if (this.tokenType === types.Hash) {
this.eat(types.Hash);
} else {
this.eat(types.Ident);
}
return this.substrToCursor(start);
return this.substrToCursor(start);
}
// ! ws* important
function getImportant() {
this.eat(types.Delim);
this.skipSC();
this.eat(types.Delim);
this.skipSC();
const important = this.consume(types.Ident);
const important = this.consume(types.Ident);
// store original value in case it differ from `important`
// for better original source restoring and hacks like `!ie` support
return important === 'important' ? true : important;
// store original value in case it differ from `important`
// for better original source restoring and hacks like `!ie` support
return important === 'important' ? true : important;
}
exports.generate = generate;

View File

@ -3,45 +3,43 @@
const types = require('../../tokenizer/types.cjs');
function consumeRaw(startToken) {
return this.Raw(startToken, this.consumeUntilSemicolonIncluded, true);
return this.Raw(startToken, this.consumeUntilSemicolonIncluded, true);
}
const name = 'DeclarationList';
const structure = {
children: [[
'Declaration'
]]
children: [['Declaration']],
};
function parse() {
const children = this.createList();
const children = this.createList();
while (!this.eof) {
switch (this.tokenType) {
case types.WhiteSpace:
case types.Comment:
case types.Semicolon:
this.next();
break;
while (!this.eof) {
switch (this.tokenType) {
case types.WhiteSpace:
case types.Comment:
case types.Semicolon:
this.next();
break;
default:
children.push(this.parseWithFallback(this.Declaration, consumeRaw));
}
default:
children.push(this.parseWithFallback(this.Declaration, consumeRaw));
}
}
return {
type: 'DeclarationList',
loc: this.getLocationFromList(children),
children
};
return {
type: 'DeclarationList',
loc: this.getLocationFromList(children),
children,
};
}
function generate(node) {
this.children(node, prev => {
if (prev.type === 'Declaration') {
this.token(types.Semicolon, ';');
}
});
this.children(node, (prev) => {
if (prev.type === 'Declaration') {
this.token(types.Semicolon, ';');
}
});
}
exports.generate = generate;

View File

@ -4,24 +4,24 @@ const types = require('../../tokenizer/types.cjs');
const name = 'Dimension';
const structure = {
value: String,
unit: String
value: String,
unit: String,
};
function parse() {
const start = this.tokenStart;
const value = this.consumeNumber(types.Dimension);
const start = this.tokenStart;
const value = this.consumeNumber(types.Dimension);
return {
type: 'Dimension',
loc: this.getLocation(start, this.tokenStart),
value,
unit: this.substring(start + value.length, this.tokenStart)
};
return {
type: 'Dimension',
loc: this.getLocation(start, this.tokenStart),
value,
unit: this.substring(start + value.length, this.tokenStart),
};
}
function generate(node) {
this.token(types.Dimension, node.value + node.unit);
this.token(types.Dimension, node.value + node.unit);
}
exports.generate = generate;

View File

@ -5,37 +5,38 @@ const types = require('../../tokenizer/types.cjs');
const name = 'Function';
const walkContext = 'function';
const structure = {
name: String,
children: [[]]
name: String,
children: [[]],
};
// <function-token> <sequence> )
function parse(readSequence, recognizer) {
const start = this.tokenStart;
const name = this.consumeFunctionName();
const nameLowerCase = name.toLowerCase();
let children;
const start = this.tokenStart;
const name = this.consumeFunctionName();
const nameLowerCase = name.toLowerCase();
let children;
children = recognizer.hasOwnProperty(nameLowerCase)
? recognizer[nameLowerCase].call(this, recognizer)
: readSequence.call(this, recognizer);
children =
recognizer.hasOwnProperty(nameLowerCase) ?
recognizer[nameLowerCase].call(this, recognizer)
: readSequence.call(this, recognizer);
if (!this.eof) {
this.eat(types.RightParenthesis);
}
if (!this.eof) {
this.eat(types.RightParenthesis);
}
return {
type: 'Function',
loc: this.getLocation(start, this.tokenStart),
name,
children
};
return {
type: 'Function',
loc: this.getLocation(start, this.tokenStart),
name,
children,
};
}
function generate(node) {
this.token(types.Function, node.name + '(');
this.children(node);
this.token(types.RightParenthesis, ')');
this.token(types.Function, node.name + '(');
this.children(node);
this.token(types.RightParenthesis, ')');
}
exports.generate = generate;

View File

@ -6,21 +6,21 @@ const types = require('../../tokenizer/types.cjs');
const xxx = 'XXX';
const name = 'Hash';
const structure = {
value: String
value: String,
};
function parse() {
const start = this.tokenStart;
const start = this.tokenStart;
this.eat(types.Hash);
this.eat(types.Hash);
return {
type: 'Hash',
loc: this.getLocation(start, this.tokenStart),
value: this.substrToCursor(start + 1)
};
return {
type: 'Hash',
loc: this.getLocation(start, this.tokenStart),
value: this.substrToCursor(start + 1),
};
}
function generate(node) {
this.token(types.Hash, '#' + node.value);
this.token(types.Hash, '#' + node.value);
}
exports.generate = generate;

View File

@ -4,27 +4,27 @@ const types = require('../../tokenizer/types.cjs');
const name = 'IdSelector';
const structure = {
name: String
name: String,
};
function parse() {
const start = this.tokenStart;
const start = this.tokenStart;
// TODO: check value is an ident
this.eat(types.Hash);
// TODO: check value is an ident
this.eat(types.Hash);
return {
type: 'IdSelector',
loc: this.getLocation(start, this.tokenStart),
name: this.substrToCursor(start + 1)
};
return {
type: 'IdSelector',
loc: this.getLocation(start, this.tokenStart),
name: this.substrToCursor(start + 1),
};
}
function generate(node) {
// Using Delim instead of Hash is a hack to avoid for a whitespace between ident and id-selector
// in safe mode (e.g. "a#id"), because IE11 doesn't allow a sequence <ident-token> <hash-token>
// without a whitespace in values (e.g. "1px solid#000")
this.token(types.Delim, '#' + node.name);
// Using Delim instead of Hash is a hack to avoid for a whitespace between ident and id-selector
// in safe mode (e.g. "a#id"), because IE11 doesn't allow a sequence <ident-token> <hash-token>
// without a whitespace in values (e.g. "1px solid#000")
this.token(types.Delim, '#' + node.name);
}
exports.generate = generate;

View File

@ -4,19 +4,19 @@ const types = require('../../tokenizer/types.cjs');
const name = 'Identifier';
const structure = {
name: String
name: String,
};
function parse() {
return {
type: 'Identifier',
loc: this.getLocation(this.tokenStart, this.tokenEnd),
name: this.consume(types.Ident)
};
return {
type: 'Identifier',
loc: this.getLocation(this.tokenStart, this.tokenEnd),
name: this.consume(types.Ident),
};
}
function generate(node) {
this.token(types.Ident, node.name);
this.token(types.Ident, node.name);
}
exports.generate = generate;

View File

@ -4,70 +4,70 @@ const types = require('../../tokenizer/types.cjs');
const name = 'MediaFeature';
const structure = {
name: String,
value: ['Identifier', 'Number', 'Dimension', 'Ratio', null]
name: String,
value: ['Identifier', 'Number', 'Dimension', 'Ratio', null],
};
function parse() {
const start = this.tokenStart;
let name;
let value = null;
const start = this.tokenStart;
let name;
let value = null;
this.eat(types.LeftParenthesis);
this.eat(types.LeftParenthesis);
this.skipSC();
name = this.consume(types.Ident);
this.skipSC();
if (this.tokenType !== types.RightParenthesis) {
this.eat(types.Colon);
this.skipSC();
name = this.consume(types.Ident);
this.skipSC();
if (this.tokenType !== types.RightParenthesis) {
this.eat(types.Colon);
this.skipSC();
switch (this.tokenType) {
case types.Number:
if (this.lookupNonWSType(1) === types.Delim) {
value = this.Ratio();
} else {
value = this.Number();
}
break;
case types.Dimension:
value = this.Dimension();
break;
case types.Ident:
value = this.Identifier();
break;
default:
this.error('Number, dimension, ratio or identifier is expected');
switch (this.tokenType) {
case types.Number:
if (this.lookupNonWSType(1) === types.Delim) {
value = this.Ratio();
} else {
value = this.Number();
}
this.skipSC();
break;
case types.Dimension:
value = this.Dimension();
break;
case types.Ident:
value = this.Identifier();
break;
default:
this.error('Number, dimension, ratio or identifier is expected');
}
this.eat(types.RightParenthesis);
this.skipSC();
}
return {
type: 'MediaFeature',
loc: this.getLocation(start, this.tokenStart),
name,
value
};
this.eat(types.RightParenthesis);
return {
type: 'MediaFeature',
loc: this.getLocation(start, this.tokenStart),
name,
value,
};
}
function generate(node) {
this.token(types.LeftParenthesis, '(');
this.token(types.Ident, node.name);
this.token(types.LeftParenthesis, '(');
this.token(types.Ident, node.name);
if (node.value !== null) {
this.token(types.Colon, ':');
this.node(node.value);
}
if (node.value !== null) {
this.token(types.Colon, ':');
this.node(node.value);
}
this.token(types.RightParenthesis, ')');
this.token(types.RightParenthesis, ')');
}
exports.generate = generate;

View File

@ -4,55 +4,50 @@ const types = require('../../tokenizer/types.cjs');
const name = 'MediaQuery';
const structure = {
children: [[
'Identifier',
'MediaFeature',
'WhiteSpace'
]]
children: [['Identifier', 'MediaFeature', 'WhiteSpace']],
};
function parse() {
const children = this.createList();
let child = null;
const children = this.createList();
let child = null;
this.skipSC();
this.skipSC();
scan:
while (!this.eof) {
switch (this.tokenType) {
case types.Comment:
case types.WhiteSpace:
this.next();
continue;
scan: while (!this.eof) {
switch (this.tokenType) {
case types.Comment:
case types.WhiteSpace:
this.next();
continue;
case types.Ident:
child = this.Identifier();
break;
case types.Ident:
child = this.Identifier();
break;
case types.LeftParenthesis:
child = this.MediaFeature();
break;
case types.LeftParenthesis:
child = this.MediaFeature();
break;
default:
break scan;
}
children.push(child);
default:
break scan;
}
if (child === null) {
this.error('Identifier or parenthesis is expected');
}
children.push(child);
}
return {
type: 'MediaQuery',
loc: this.getLocationFromList(children),
children
};
if (child === null) {
this.error('Identifier or parenthesis is expected');
}
return {
type: 'MediaQuery',
loc: this.getLocationFromList(children),
children,
};
}
function generate(node) {
this.children(node);
this.children(node);
}
exports.generate = generate;

View File

@ -4,35 +4,33 @@ const types = require('../../tokenizer/types.cjs');
const name = 'MediaQueryList';
const structure = {
children: [[
'MediaQuery'
]]
children: [['MediaQuery']],
};
function parse() {
const children = this.createList();
const children = this.createList();
this.skipSC();
this.skipSC();
while (!this.eof) {
children.push(this.MediaQuery());
while (!this.eof) {
children.push(this.MediaQuery());
if (this.tokenType !== types.Comma) {
break;
}
this.next();
if (this.tokenType !== types.Comma) {
break;
}
return {
type: 'MediaQueryList',
loc: this.getLocationFromList(children),
children
};
this.next();
}
return {
type: 'MediaQueryList',
loc: this.getLocationFromList(children),
children,
};
}
function generate(node) {
this.children(node, () => this.token(types.Comma, ','));
this.children(node, () => this.token(types.Comma, ','));
}
exports.generate = generate;

View File

@ -4,48 +4,48 @@ const types = require('../../tokenizer/types.cjs');
const name = 'Nth';
const structure = {
nth: ['AnPlusB', 'Identifier'],
selector: ['SelectorList', null]
nth: ['AnPlusB', 'Identifier'],
selector: ['SelectorList', null],
};
function parse() {
this.skipSC();
this.skipSC();
const start = this.tokenStart;
let end = start;
let selector = null;
let nth;
const start = this.tokenStart;
let end = start;
let selector = null;
let nth;
if (this.lookupValue(0, 'odd') || this.lookupValue(0, 'even')) {
nth = this.Identifier();
} else {
nth = this.AnPlusB();
}
if (this.lookupValue(0, 'odd') || this.lookupValue(0, 'even')) {
nth = this.Identifier();
} else {
nth = this.AnPlusB();
}
end = this.tokenStart;
this.skipSC();
if (this.lookupValue(0, 'of')) {
this.next();
selector = this.SelectorList();
end = this.tokenStart;
this.skipSC();
}
if (this.lookupValue(0, 'of')) {
this.next();
selector = this.SelectorList();
end = this.tokenStart;
}
return {
type: 'Nth',
loc: this.getLocation(start, end),
nth,
selector
};
return {
type: 'Nth',
loc: this.getLocation(start, end),
nth,
selector,
};
}
function generate(node) {
this.node(node.nth);
if (node.selector !== null) {
this.token(types.Ident, 'of');
this.node(node.selector);
}
this.node(node.nth);
if (node.selector !== null) {
this.token(types.Ident, 'of');
this.node(node.selector);
}
}
exports.generate = generate;

View File

@ -4,19 +4,19 @@ const types = require('../../tokenizer/types.cjs');
const name = 'Number';
const structure = {
value: String
value: String,
};
function parse() {
return {
type: 'Number',
loc: this.getLocation(this.tokenStart, this.tokenEnd),
value: this.consume(types.Number)
};
return {
type: 'Number',
loc: this.getLocation(this.tokenStart, this.tokenEnd),
value: this.consume(types.Number),
};
}
function generate(node) {
this.token(types.Number, node.value);
this.token(types.Number, node.value);
}
exports.generate = generate;

View File

@ -3,23 +3,23 @@
// '/' | '*' | ',' | ':' | '+' | '-'
const name = 'Operator';
const structure = {
value: String
value: String,
};
function parse() {
const start = this.tokenStart;
const start = this.tokenStart;
this.next();
this.next();
return {
type: 'Operator',
loc: this.getLocation(start, this.tokenStart),
value: this.substrToCursor(start)
};
return {
type: 'Operator',
loc: this.getLocation(start, this.tokenStart),
value: this.substrToCursor(start),
};
}
function generate(node) {
this.tokenize(node.value);
this.tokenize(node.value);
}
exports.generate = generate;

View File

@ -4,32 +4,32 @@ const types = require('../../tokenizer/types.cjs');
const name = 'Parentheses';
const structure = {
children: [[]]
children: [[]],
};
function parse(readSequence, recognizer) {
const start = this.tokenStart;
let children = null;
const start = this.tokenStart;
let children = null;
this.eat(types.LeftParenthesis);
this.eat(types.LeftParenthesis);
children = readSequence.call(this, recognizer);
children = readSequence.call(this, recognizer);
if (!this.eof) {
this.eat(types.RightParenthesis);
}
if (!this.eof) {
this.eat(types.RightParenthesis);
}
return {
type: 'Parentheses',
loc: this.getLocation(start, this.tokenStart),
children
};
return {
type: 'Parentheses',
loc: this.getLocation(start, this.tokenStart),
children,
};
}
function generate(node) {
this.token(types.LeftParenthesis, '(');
this.children(node);
this.token(types.RightParenthesis, ')');
this.token(types.LeftParenthesis, '(');
this.children(node);
this.token(types.RightParenthesis, ')');
}
exports.generate = generate;

View File

@ -4,19 +4,19 @@ const types = require('../../tokenizer/types.cjs');
const name = 'Percentage';
const structure = {
value: String
value: String,
};
function parse() {
return {
type: 'Percentage',
loc: this.getLocation(this.tokenStart, this.tokenEnd),
value: this.consumeNumber(types.Percentage)
};
return {
type: 'Percentage',
loc: this.getLocation(this.tokenStart, this.tokenEnd),
value: this.consumeNumber(types.Percentage),
};
}
function generate(node) {
this.token(types.Percentage, node.value + '%');
this.token(types.Percentage, node.value + '%');
}
exports.generate = generate;

View File

@ -5,57 +5,55 @@ const types = require('../../tokenizer/types.cjs');
const name = 'PseudoClassSelector';
const walkContext = 'function';
const structure = {
name: String,
children: [['Raw'], null]
name: String,
children: [['Raw'], null],
};
// : [ <ident> | <function-token> <any-value>? ) ]
function parse() {
const start = this.tokenStart;
let children = null;
let name;
let nameLowerCase;
const start = this.tokenStart;
let children = null;
let name;
let nameLowerCase;
this.eat(types.Colon);
this.eat(types.Colon);
if (this.tokenType === types.Function) {
name = this.consumeFunctionName();
nameLowerCase = name.toLowerCase();
if (this.tokenType === types.Function) {
name = this.consumeFunctionName();
nameLowerCase = name.toLowerCase();
if (hasOwnProperty.call(this.pseudo, nameLowerCase)) {
this.skipSC();
children = this.pseudo[nameLowerCase].call(this);
this.skipSC();
} else {
children = this.createList();
children.push(
this.Raw(this.tokenIndex, null, false)
);
}
this.eat(types.RightParenthesis);
if (hasOwnProperty.call(this.pseudo, nameLowerCase)) {
this.skipSC();
children = this.pseudo[nameLowerCase].call(this);
this.skipSC();
} else {
name = this.consume(types.Ident);
children = this.createList();
children.push(this.Raw(this.tokenIndex, null, false));
}
return {
type: 'PseudoClassSelector',
loc: this.getLocation(start, this.tokenStart),
name,
children
};
this.eat(types.RightParenthesis);
} else {
name = this.consume(types.Ident);
}
return {
type: 'PseudoClassSelector',
loc: this.getLocation(start, this.tokenStart),
name,
children,
};
}
function generate(node) {
this.token(types.Colon, ':');
this.token(types.Colon, ':');
if (node.children === null) {
this.token(types.Ident, node.name);
} else {
this.token(types.Function, node.name + '(');
this.children(node);
this.token(types.RightParenthesis, ')');
}
if (node.children === null) {
this.token(types.Ident, node.name);
} else {
this.token(types.Function, node.name + '(');
this.children(node);
this.token(types.RightParenthesis, ')');
}
}
exports.generate = generate;

View File

@ -5,59 +5,57 @@ const types = require('../../tokenizer/types.cjs');
const name = 'PseudoElementSelector';
const walkContext = 'function';
const structure = {
name: String,
children: [['Raw'], null]
name: String,
children: [['Raw'], null],
};
// :: [ <ident> | <function-token> <any-value>? ) ]
function parse() {
const start = this.tokenStart;
let children = null;
let name;
let nameLowerCase;
const start = this.tokenStart;
let children = null;
let name;
let nameLowerCase;
this.eat(types.Colon);
this.eat(types.Colon);
this.eat(types.Colon);
this.eat(types.Colon);
if (this.tokenType === types.Function) {
name = this.consumeFunctionName();
nameLowerCase = name.toLowerCase();
if (this.tokenType === types.Function) {
name = this.consumeFunctionName();
nameLowerCase = name.toLowerCase();
if (hasOwnProperty.call(this.pseudo, nameLowerCase)) {
this.skipSC();
children = this.pseudo[nameLowerCase].call(this);
this.skipSC();
} else {
children = this.createList();
children.push(
this.Raw(this.tokenIndex, null, false)
);
}
this.eat(types.RightParenthesis);
if (hasOwnProperty.call(this.pseudo, nameLowerCase)) {
this.skipSC();
children = this.pseudo[nameLowerCase].call(this);
this.skipSC();
} else {
name = this.consume(types.Ident);
children = this.createList();
children.push(this.Raw(this.tokenIndex, null, false));
}
return {
type: 'PseudoElementSelector',
loc: this.getLocation(start, this.tokenStart),
name,
children
};
this.eat(types.RightParenthesis);
} else {
name = this.consume(types.Ident);
}
return {
type: 'PseudoElementSelector',
loc: this.getLocation(start, this.tokenStart),
name,
children,
};
}
function generate(node) {
this.token(types.Colon, ':');
this.token(types.Colon, ':');
this.token(types.Colon, ':');
this.token(types.Colon, ':');
if (node.children === null) {
this.token(types.Ident, node.name);
} else {
this.token(types.Function, node.name + '(');
this.children(node);
this.token(types.RightParenthesis, ')');
}
if (node.children === null) {
this.token(types.Ident, node.name);
} else {
this.token(types.Function, node.name + '(');
this.children(node);
this.token(types.RightParenthesis, ')');
}
}
exports.generate = generate;

View File

@ -3,8 +3,8 @@
const types = require('../../tokenizer/types.cjs');
const charCodeDefinitions = require('../../tokenizer/char-code-definitions.cjs');
const SOLIDUS = 0x002F; // U+002F SOLIDUS (/)
const FULLSTOP = 0x002E; // U+002E FULL STOP (.)
const SOLIDUS = 0x002f; // U+002F SOLIDUS (/)
const FULLSTOP = 0x002e; // U+002E FULL STOP (.)
// Terms of <ratio> should be a positive numbers (not zero or negative)
// (see https://drafts.csswg.org/mediaqueries-3/#values)
@ -13,52 +13,55 @@ const FULLSTOP = 0x002E; // U+002E FULL STOP (.)
// to test a term is unsigned number without an exponent part.
// Additional checking may be applied on lexer validation.
function consumeNumber() {
this.skipSC();
this.skipSC();
const value = this.consume(types.Number);
const value = this.consume(types.Number);
for (let i = 0; i < value.length; i++) {
const code = value.charCodeAt(i);
if (!charCodeDefinitions.isDigit(code) && code !== FULLSTOP) {
this.error('Unsigned number is expected', this.tokenStart - value.length + i);
}
for (let i = 0; i < value.length; i++) {
const code = value.charCodeAt(i);
if (!charCodeDefinitions.isDigit(code) && code !== FULLSTOP) {
this.error(
'Unsigned number is expected',
this.tokenStart - value.length + i
);
}
}
if (Number(value) === 0) {
this.error('Zero number is not allowed', this.tokenStart - value.length);
}
if (Number(value) === 0) {
this.error('Zero number is not allowed', this.tokenStart - value.length);
}
return value;
return value;
}
const name = 'Ratio';
const structure = {
left: String,
right: String
left: String,
right: String,
};
// <positive-integer> S* '/' S* <positive-integer>
function parse() {
const start = this.tokenStart;
const left = consumeNumber.call(this);
let right;
const start = this.tokenStart;
const left = consumeNumber.call(this);
let right;
this.skipSC();
this.eatDelim(SOLIDUS);
right = consumeNumber.call(this);
this.skipSC();
this.eatDelim(SOLIDUS);
right = consumeNumber.call(this);
return {
type: 'Ratio',
loc: this.getLocation(start, this.tokenStart),
left,
right
};
return {
type: 'Ratio',
loc: this.getLocation(start, this.tokenStart),
left,
right,
};
}
function generate(node) {
this.token(types.Number, node.left);
this.token(types.Delim, '/');
this.token(types.Number, node.right);
this.token(types.Number, node.left);
this.token(types.Delim, '/');
this.token(types.Number, node.right);
}
exports.generate = generate;

View File

@ -3,43 +3,46 @@
const types = require('../../tokenizer/types.cjs');
function getOffsetExcludeWS() {
if (this.tokenIndex > 0) {
if (this.lookupType(-1) === types.WhiteSpace) {
return this.tokenIndex > 1
? this.getTokenStart(this.tokenIndex - 1)
: this.firstCharOffset;
}
if (this.tokenIndex > 0) {
if (this.lookupType(-1) === types.WhiteSpace) {
return this.tokenIndex > 1 ?
this.getTokenStart(this.tokenIndex - 1)
: this.firstCharOffset;
}
}
return this.tokenStart;
return this.tokenStart;
}
const name = 'Raw';
const structure = {
value: String
value: String,
};
function parse(startToken, consumeUntil, excludeWhiteSpace) {
const startOffset = this.getTokenStart(startToken);
let endOffset;
const startOffset = this.getTokenStart(startToken);
let endOffset;
this.skipUntilBalanced(startToken, consumeUntil || this.consumeUntilBalanceEnd);
this.skipUntilBalanced(
startToken,
consumeUntil || this.consumeUntilBalanceEnd
);
if (excludeWhiteSpace && this.tokenStart > startOffset) {
endOffset = getOffsetExcludeWS.call(this);
} else {
endOffset = this.tokenStart;
}
if (excludeWhiteSpace && this.tokenStart > startOffset) {
endOffset = getOffsetExcludeWS.call(this);
} else {
endOffset = this.tokenStart;
}
return {
type: 'Raw',
loc: this.getLocation(startOffset, endOffset),
value: this.substring(startOffset, endOffset)
};
return {
type: 'Raw',
loc: this.getLocation(startOffset, endOffset),
value: this.substring(startOffset, endOffset),
};
}
function generate(node) {
this.tokenize(node.value);
this.tokenize(node.value);
}
exports.generate = generate;

View File

@ -3,52 +3,54 @@
const types = require('../../tokenizer/types.cjs');
function consumeRaw(startToken) {
return this.Raw(startToken, this.consumeUntilLeftCurlyBracket, true);
return this.Raw(startToken, this.consumeUntilLeftCurlyBracket, true);
}
function consumePrelude() {
const prelude = this.SelectorList();
const prelude = this.SelectorList();
if (prelude.type !== 'Raw' &&
this.eof === false &&
this.tokenType !== types.LeftCurlyBracket) {
this.error();
}
if (
prelude.type !== 'Raw' &&
this.eof === false &&
this.tokenType !== types.LeftCurlyBracket
) {
this.error();
}
return prelude;
return prelude;
}
const name = 'Rule';
const walkContext = 'rule';
const structure = {
prelude: ['SelectorList', 'Raw'],
block: ['Block']
prelude: ['SelectorList', 'Raw'],
block: ['Block'],
};
function parse() {
const startToken = this.tokenIndex;
const startOffset = this.tokenStart;
let prelude;
let block;
const startToken = this.tokenIndex;
const startOffset = this.tokenStart;
let prelude;
let block;
if (this.parseRulePrelude) {
prelude = this.parseWithFallback(consumePrelude, consumeRaw);
} else {
prelude = consumeRaw.call(this, startToken);
}
if (this.parseRulePrelude) {
prelude = this.parseWithFallback(consumePrelude, consumeRaw);
} else {
prelude = consumeRaw.call(this, startToken);
}
block = this.Block(true);
block = this.Block(true);
return {
type: 'Rule',
loc: this.getLocation(startOffset, this.tokenStart),
prelude,
block
};
return {
type: 'Rule',
loc: this.getLocation(startOffset, this.tokenStart),
prelude,
block,
};
}
function generate(node) {
this.node(node.prelude);
this.node(node.block);
this.node(node.prelude);
this.node(node.block);
}
exports.generate = generate;

View File

@ -2,35 +2,37 @@
const name = 'Selector';
const structure = {
children: [[
'TypeSelector',
'IdSelector',
'ClassSelector',
'AttributeSelector',
'PseudoClassSelector',
'PseudoElementSelector',
'Combinator',
'WhiteSpace'
]]
children: [
[
'TypeSelector',
'IdSelector',
'ClassSelector',
'AttributeSelector',
'PseudoClassSelector',
'PseudoElementSelector',
'Combinator',
'WhiteSpace',
],
],
};
function parse() {
const children = this.readSequence(this.scope.Selector);
const children = this.readSequence(this.scope.Selector);
// nothing were consumed
if (this.getFirstListNode(children) === null) {
this.error('Selector is expected');
}
// nothing were consumed
if (this.getFirstListNode(children) === null) {
this.error('Selector is expected');
}
return {
type: 'Selector',
loc: this.getLocationFromList(children),
children
};
return {
type: 'Selector',
loc: this.getLocationFromList(children),
children,
};
}
function generate(node) {
this.children(node);
this.children(node);
}
exports.generate = generate;

View File

@ -5,35 +5,32 @@ const types = require('../../tokenizer/types.cjs');
const name = 'SelectorList';
const walkContext = 'selector';
const structure = {
children: [[
'Selector',
'Raw'
]]
children: [['Selector', 'Raw']],
};
function parse() {
const children = this.createList();
const children = this.createList();
while (!this.eof) {
children.push(this.Selector());
while (!this.eof) {
children.push(this.Selector());
if (this.tokenType === types.Comma) {
this.next();
continue;
}
break;
if (this.tokenType === types.Comma) {
this.next();
continue;
}
return {
type: 'SelectorList',
loc: this.getLocationFromList(children),
children
};
break;
}
return {
type: 'SelectorList',
loc: this.getLocationFromList(children),
children,
};
}
function generate(node) {
this.children(node, () => this.token(types.Comma, ','));
this.children(node, () => this.token(types.Comma, ','));
}
exports.generate = generate;

View File

@ -5,19 +5,19 @@ const types = require('../../tokenizer/types.cjs');
const name = 'String';
const structure = {
value: String
value: String,
};
function parse() {
return {
type: 'String',
loc: this.getLocation(this.tokenStart, this.tokenEnd),
value: string.decode(this.consume(types.String))
};
return {
type: 'String',
loc: this.getLocation(this.tokenStart, this.tokenEnd),
value: string.decode(this.consume(types.String)),
};
}
function generate(node) {
this.token(types.String, string.encode(node.value));
this.token(types.String, string.encode(node.value));
}
exports.generate = generate;

View File

@ -5,75 +5,68 @@ const types = require('../../tokenizer/types.cjs');
const EXCLAMATIONMARK = 0x0021; // U+0021 EXCLAMATION MARK (!)
function consumeRaw(startToken) {
return this.Raw(startToken, null, false);
return this.Raw(startToken, null, false);
}
const name = 'StyleSheet';
const walkContext = 'stylesheet';
const structure = {
children: [[
'Comment',
'CDO',
'CDC',
'Atrule',
'Rule',
'Raw'
]]
children: [['Comment', 'CDO', 'CDC', 'Atrule', 'Rule', 'Raw']],
};
function parse() {
const start = this.tokenStart;
const children = this.createList();
let child;
const start = this.tokenStart;
const children = this.createList();
let child;
while (!this.eof) {
switch (this.tokenType) {
case types.WhiteSpace:
this.next();
continue;
while (!this.eof) {
switch (this.tokenType) {
case types.WhiteSpace:
this.next();
continue;
case types.Comment:
// ignore comments except exclamation comments (i.e. /*! .. */) on top level
if (this.charCodeAt(this.tokenStart + 2) !== EXCLAMATIONMARK) {
this.next();
continue;
}
child = this.Comment();
break;
case types.CDO: // <!--
child = this.CDO();
break;
case types.CDC: // -->
child = this.CDC();
break;
// CSS Syntax Module Level 3
// §2.2 Error handling
// At the "top level" of a stylesheet, an <at-keyword-token> starts an at-rule.
case types.AtKeyword:
child = this.parseWithFallback(this.Atrule, consumeRaw);
break;
// Anything else starts a qualified rule ...
default:
child = this.parseWithFallback(this.Rule, consumeRaw);
case types.Comment:
// ignore comments except exclamation comments (i.e. /*! .. */) on top level
if (this.charCodeAt(this.tokenStart + 2) !== EXCLAMATIONMARK) {
this.next();
continue;
}
children.push(child);
child = this.Comment();
break;
case types.CDO: // <!--
child = this.CDO();
break;
case types.CDC: // -->
child = this.CDC();
break;
// CSS Syntax Module Level 3
// §2.2 Error handling
// At the "top level" of a stylesheet, an <at-keyword-token> starts an at-rule.
case types.AtKeyword:
child = this.parseWithFallback(this.Atrule, consumeRaw);
break;
// Anything else starts a qualified rule ...
default:
child = this.parseWithFallback(this.Rule, consumeRaw);
}
return {
type: 'StyleSheet',
loc: this.getLocation(start, this.tokenStart),
children
};
children.push(child);
}
return {
type: 'StyleSheet',
loc: this.getLocation(start, this.tokenStart),
children,
};
}
function generate(node) {
this.children(node);
this.children(node);
}
exports.generate = generate;

View File

@ -2,21 +2,20 @@
const types = require('../../tokenizer/types.cjs');
const ASTERISK = 0x002A; // U+002A ASTERISK (*)
const VERTICALLINE = 0x007C; // U+007C VERTICAL LINE (|)
const ASTERISK = 0x002a; // U+002A ASTERISK (*)
const VERTICALLINE = 0x007c; // U+007C VERTICAL LINE (|)
function eatIdentifierOrAsterisk() {
if (this.tokenType !== types.Ident &&
this.isDelim(ASTERISK) === false) {
this.error('Identifier or asterisk is expected');
}
if (this.tokenType !== types.Ident && this.isDelim(ASTERISK) === false) {
this.error('Identifier or asterisk is expected');
}
this.next();
this.next();
}
const name = 'TypeSelector';
const structure = {
name: String
name: String,
};
// ident
@ -28,29 +27,29 @@ const structure = {
// |ident
// |*
function parse() {
const start = this.tokenStart;
const start = this.tokenStart;
if (this.isDelim(VERTICALLINE)) {
this.next();
eatIdentifierOrAsterisk.call(this);
} else {
eatIdentifierOrAsterisk.call(this);
if (this.isDelim(VERTICALLINE)) {
this.next();
eatIdentifierOrAsterisk.call(this);
} else {
eatIdentifierOrAsterisk.call(this);
if (this.isDelim(VERTICALLINE)) {
this.next();
eatIdentifierOrAsterisk.call(this);
}
this.next();
eatIdentifierOrAsterisk.call(this);
}
}
return {
type: 'TypeSelector',
loc: this.getLocation(start, this.tokenStart),
name: this.substrToCursor(start)
};
return {
type: 'TypeSelector',
loc: this.getLocation(start, this.tokenStart),
name: this.substrToCursor(start),
};
}
function generate(node) {
this.tokenize(node.name);
this.tokenize(node.name);
}
exports.generate = generate;

View File

@ -3,54 +3,58 @@
const types = require('../../tokenizer/types.cjs');
const charCodeDefinitions = require('../../tokenizer/char-code-definitions.cjs');
const PLUSSIGN = 0x002B; // U+002B PLUS SIGN (+)
const HYPHENMINUS = 0x002D; // U+002D HYPHEN-MINUS (-)
const QUESTIONMARK = 0x003F; // U+003F QUESTION MARK (?)
const PLUSSIGN = 0x002b; // U+002B PLUS SIGN (+)
const HYPHENMINUS = 0x002d; // U+002D HYPHEN-MINUS (-)
const QUESTIONMARK = 0x003f; // U+003F QUESTION MARK (?)
function eatHexSequence(offset, allowDash) {
let len = 0;
let len = 0;
for (let pos = this.tokenStart + offset; pos < this.tokenEnd; pos++) {
const code = this.charCodeAt(pos);
for (let pos = this.tokenStart + offset; pos < this.tokenEnd; pos++) {
const code = this.charCodeAt(pos);
if (code === HYPHENMINUS && allowDash && len !== 0) {
eatHexSequence.call(this, offset + len + 1, false);
return -1;
}
if (code === HYPHENMINUS && allowDash && len !== 0) {
eatHexSequence.call(this, offset + len + 1, false);
return -1;
}
if (!charCodeDefinitions.isHexDigit(code)) {
this.error(
allowDash && len !== 0
? 'Hyphen minus' + (len < 6 ? ' or hex digit' : '') + ' is expected'
: (len < 6 ? 'Hex digit is expected' : 'Unexpected input'),
pos
);
}
if (!charCodeDefinitions.isHexDigit(code)) {
this.error(
allowDash && len !== 0 ?
'Hyphen minus' + (len < 6 ? ' or hex digit' : '') + ' is expected'
: len < 6 ? 'Hex digit is expected'
: 'Unexpected input',
pos
);
}
if (++len > 6) {
this.error('Too many hex digits', pos);
} }
if (++len > 6) {
this.error('Too many hex digits', pos);
}
}
this.next();
return len;
this.next();
return len;
}
function eatQuestionMarkSequence(max) {
let count = 0;
let count = 0;
while (this.isDelim(QUESTIONMARK)) {
if (++count > max) {
this.error('Too many question marks');
}
this.next();
while (this.isDelim(QUESTIONMARK)) {
if (++count > max) {
this.error('Too many question marks');
}
this.next();
}
}
function startsWith(code) {
if (this.charCodeAt(this.tokenStart) !== code) {
this.error((code === PLUSSIGN ? 'Plus sign' : 'Hyphen minus') + ' is expected');
}
if (this.charCodeAt(this.tokenStart) !== code) {
this.error(
(code === PLUSSIGN ? 'Plus sign' : 'Hyphen minus') + ' is expected'
);
}
}
// https://drafts.csswg.org/css-syntax/#urange
@ -73,83 +77,85 @@ function startsWith(code) {
// u <number-token> <number-token> |
// u '+' '?'+
function scanUnicodeRange() {
let hexLength = 0;
let hexLength = 0;
switch (this.tokenType) {
case types.Number:
// u <number-token> '?'*
// u <number-token> <dimension-token>
// u <number-token> <number-token>
hexLength = eatHexSequence.call(this, 1, true);
switch (this.tokenType) {
case types.Number:
// u <number-token> '?'*
// u <number-token> <dimension-token>
// u <number-token> <number-token>
hexLength = eatHexSequence.call(this, 1, true);
if (this.isDelim(QUESTIONMARK)) {
eatQuestionMarkSequence.call(this, 6 - hexLength);
break;
}
if (this.isDelim(QUESTIONMARK)) {
eatQuestionMarkSequence.call(this, 6 - hexLength);
break;
}
if (this.tokenType === types.Dimension ||
this.tokenType === types.Number) {
startsWith.call(this, HYPHENMINUS);
eatHexSequence.call(this, 1, false);
break;
}
if (
this.tokenType === types.Dimension ||
this.tokenType === types.Number
) {
startsWith.call(this, HYPHENMINUS);
eatHexSequence.call(this, 1, false);
break;
}
break;
break;
case types.Dimension:
// u <dimension-token> '?'*
hexLength = eatHexSequence.call(this, 1, true);
case types.Dimension:
// u <dimension-token> '?'*
hexLength = eatHexSequence.call(this, 1, true);
if (hexLength > 0) {
eatQuestionMarkSequence.call(this, 6 - hexLength);
}
if (hexLength > 0) {
eatQuestionMarkSequence.call(this, 6 - hexLength);
}
break;
break;
default:
// u '+' <ident-token> '?'*
// u '+' '?'+
this.eatDelim(PLUSSIGN);
default:
// u '+' <ident-token> '?'*
// u '+' '?'+
this.eatDelim(PLUSSIGN);
if (this.tokenType === types.Ident) {
hexLength = eatHexSequence.call(this, 0, true);
if (hexLength > 0) {
eatQuestionMarkSequence.call(this, 6 - hexLength);
}
break;
}
if (this.tokenType === types.Ident) {
hexLength = eatHexSequence.call(this, 0, true);
if (hexLength > 0) {
eatQuestionMarkSequence.call(this, 6 - hexLength);
}
break;
}
if (this.isDelim(QUESTIONMARK)) {
this.next();
eatQuestionMarkSequence.call(this, 5);
break;
}
if (this.isDelim(QUESTIONMARK)) {
this.next();
eatQuestionMarkSequence.call(this, 5);
break;
}
this.error('Hex digit or question mark is expected');
}
this.error('Hex digit or question mark is expected');
}
}
const name = 'UnicodeRange';
const structure = {
value: String
value: String,
};
function parse() {
const start = this.tokenStart;
const start = this.tokenStart;
// U or u
this.eatIdent('u');
scanUnicodeRange.call(this);
// U or u
this.eatIdent('u');
scanUnicodeRange.call(this);
return {
type: 'UnicodeRange',
loc: this.getLocation(start, this.tokenStart),
value: this.substrToCursor(start)
};
return {
type: 'UnicodeRange',
loc: this.getLocation(start, this.tokenStart),
value: this.substrToCursor(start),
};
}
function generate(node) {
this.tokenize(node.value);
this.tokenize(node.value);
}
exports.generate = generate;

View File

@ -6,46 +6,46 @@ const types = require('../../tokenizer/types.cjs');
const name = 'Url';
const structure = {
value: String
value: String,
};
// <url-token> | <function-token> <string> )
function parse() {
const start = this.tokenStart;
let value;
const start = this.tokenStart;
let value;
switch (this.tokenType) {
case types.Url:
value = url.decode(this.consume(types.Url));
break;
switch (this.tokenType) {
case types.Url:
value = url.decode(this.consume(types.Url));
break;
case types.Function:
if (!this.cmpStr(this.tokenStart, this.tokenEnd, 'url(')) {
this.error('Function name must be `url`');
}
case types.Function:
if (!this.cmpStr(this.tokenStart, this.tokenEnd, 'url(')) {
this.error('Function name must be `url`');
}
this.eat(types.Function);
this.skipSC();
value = string.decode(this.consume(types.String));
this.skipSC();
if (!this.eof) {
this.eat(types.RightParenthesis);
}
break;
this.eat(types.Function);
this.skipSC();
value = string.decode(this.consume(types.String));
this.skipSC();
if (!this.eof) {
this.eat(types.RightParenthesis);
}
break;
default:
this.error('Url or Function is expected');
}
default:
this.error('Url or Function is expected');
}
return {
type: 'Url',
loc: this.getLocation(start, this.tokenStart),
value
};
return {
type: 'Url',
loc: this.getLocation(start, this.tokenStart),
value,
};
}
function generate(node) {
this.token(types.Url, url.encode(node.value));
this.token(types.Url, url.encode(node.value));
}
exports.generate = generate;

View File

@ -2,22 +2,22 @@
const name = 'Value';
const structure = {
children: [[]]
children: [[]],
};
function parse() {
const start = this.tokenStart;
const children = this.readSequence(this.scope.Value);
const start = this.tokenStart;
const children = this.readSequence(this.scope.Value);
return {
type: 'Value',
loc: this.getLocation(start, this.tokenStart),
children
};
return {
type: 'Value',
loc: this.getLocation(start, this.tokenStart),
children,
};
}
function generate(node) {
this.children(node);
this.children(node);
}
exports.generate = generate;

View File

@ -3,29 +3,29 @@
const types = require('../../tokenizer/types.cjs');
const SPACE = Object.freeze({
type: 'WhiteSpace',
loc: null,
value: ' '
type: 'WhiteSpace',
loc: null,
value: ' ',
});
const name = 'WhiteSpace';
const structure = {
value: String
value: String,
};
function parse() {
this.eat(types.WhiteSpace);
return SPACE;
this.eat(types.WhiteSpace);
return SPACE;
// return {
// type: 'WhiteSpace',
// loc: this.getLocation(this.tokenStart, this.tokenEnd),
// value: this.consume(WHITESPACE)
// };
// return {
// type: 'WhiteSpace',
// loc: this.getLocation(this.tokenStart, this.tokenEnd),
// value: this.consume(WHITESPACE)
// };
}
function generate(node) {
this.token(types.WhiteSpace, node.value);
this.token(types.WhiteSpace, node.value);
}
exports.generate = generate;

View File

@ -41,8 +41,6 @@ const Url = require('./Url.cjs');
const Value = require('./Value.cjs');
const WhiteSpace = require('./WhiteSpace.cjs');
exports.AnPlusB = AnPlusB.generate;
exports.Atrule = Atrule.generate;
exports.AtrulePrelude = AtrulePrelude.generate;

View File

@ -16,8 +16,6 @@ const SelectorList = require('./SelectorList.cjs');
const String = require('./String.cjs');
const TypeSelector = require('./TypeSelector.cjs');
exports.AnPlusB = AnPlusB.parse;
exports.AttributeSelector = AttributeSelector.parse;
exports.ClassSelector = ClassSelector.parse;

View File

@ -41,8 +41,6 @@ const Url = require('./Url.cjs');
const Value = require('./Value.cjs');
const WhiteSpace = require('./WhiteSpace.cjs');
exports.AnPlusB = AnPlusB.parse;
exports.Atrule = Atrule.parse;
exports.AtrulePrelude = AtrulePrelude.parse;

View File

@ -41,8 +41,6 @@ const Url = require('./Url.cjs');
const Value = require('./Value.cjs');
const WhiteSpace = require('./WhiteSpace.cjs');
exports.AnPlusB = AnPlusB;
exports.Atrule = Atrule;
exports.AtrulePrelude = AtrulePrelude;