How to use IdentToken method in Playwright Internal

Best JavaScript code snippet using playwright-internal

parse-css.js

Source:parse-css.js Github

copy

Full Screen

1/**2 * @license3 * Copyright 2015 The AMP HTML Authors. All Rights Reserved.4 *5 * Licensed under the Apache License, Version 2.0 (the "License");6 * you may not use this file except in compliance with the License.7 * You may obtain a copy of the License at8 *9 * http://www.apache.org/licenses/LICENSE-2.010 *11 * Unless required by applicable law or agreed to in writing, software12 * distributed under the License is distributed on an "AS-IS" BASIS,13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.14 * See the License for the specific language governing permissions and15 * limitations under the license.16 *17 * Credits:18 * This original version of this file was derived from19 * https://github.com/tabatkins/parse-css by Tab Atkins,20 * licensed under the CC0 license21 * (http://creativecommons.org/publicdomain/zero/1.0/).22 */23goog.provide('parse_css.AtRule');24goog.provide('parse_css.BlockType');25goog.provide('parse_css.Declaration');26goog.provide('parse_css.ParsedCssUrl');27goog.provide('parse_css.QualifiedRule');28goog.provide('parse_css.Rule');29goog.provide('parse_css.RuleVisitor');30goog.provide('parse_css.Stylesheet');31goog.provide('parse_css.TokenStream');32goog.provide('parse_css.extractAFunction');33goog.provide('parse_css.extractASimpleBlock');34goog.provide('parse_css.extractUrls');35goog.provide('parse_css.parseAStylesheet');36goog.provide('parse_css.parseInlineStyle');37goog.provide('parse_css.parseMediaQueries');38goog.provide('parse_css.stripVendorPrefix');39goog.require('amp.validator.LIGHT');40goog.require('amp.validator.ValidationError.Code');41goog.require('goog.asserts');42goog.require('goog.string');43goog.require('parse_css.EOFToken');44goog.require('parse_css.ErrorToken');45goog.require('parse_css.TRIVIAL_EOF_TOKEN');46goog.require('parse_css.TRIVIAL_ERROR_TOKEN');47goog.require('parse_css.Token');48goog.require('parse_css.TokenType');49/**50 * @param {!Array<?>} arr51 * @return {!Array<!Object>}52 */53function arrayToJSON(arr) {54 const json = [];55 for (let i = 0; i < arr.length; i++) {56 json.push(arr[i].toJSON());57 }58 return json;59}60/**61 * A TokenStream is essentially an array of Token objects62 * with a reference to a current position. Consume/Reconsume methods63 * move the current position. tokenAt, current, and next inspect tokens64 * at specific points.65 */66parse_css.TokenStream = class {67 /**68 * @param {!Array<!parse_css.Token>} tokens69 */70 constructor(tokens) {71 goog.asserts.assert(72 tokens.length > 0,73 'Internal Error: empty TokenStream - must have EOF token');74 goog.asserts.assert(75 tokens[tokens.length - 1].tokenType === parse_css.TokenType.EOF_TOKEN,76 'Internal Error: TokenStream must end with EOF');77 /** @type {!Array<!parse_css.Token>} */78 this.tokens = tokens;79 /** @type {number} */80 this.pos = -1;81 }82 /**83 * Returns the token at an absolute position in the token stream.84 *85 * @param {number} num86 * @return {!parse_css.Token}87 */88 tokenAt(num) {89 // The last token is guaranteed to be the EOF token (with correct90 // line / col!) so any request past the length of the array91 // fetches that.92 return (num < this.tokens.length) ? this.tokens[num] :93 this.tokens[this.tokens.length - 1];94 }95 /**96 * Returns the token at the current position in the token stream.97 * @return {!parse_css.Token}98 */99 current() {100 return this.tokenAt(this.pos);101 }102 /**103 * Returns the token at the next position in the token stream.104 * @return {!parse_css.Token}105 */106 next() {107 return this.tokenAt(this.pos + 1);108 }109 /**110 * Advances the stream by one.111 */112 consume() {113 this.pos++;114 }115 /** Rewinds to the previous position in the input. */116 reconsume() {117 this.pos--;118 }119};120/**121 * Strips vendor prefixes from identifiers, e.g. property names or names122 * of at rules. E.g., "-moz-keyframes" -> "keyframes".123 * @param {string} prefixedString124 * @return {string}125 */126parse_css.stripVendorPrefix = function(prefixedString) {127 // Checking for '-' is an optimization.128 if (prefixedString !== '' && prefixedString[0] === '-') {129 if (goog.string./*OK*/ startsWith(prefixedString, '-o-'))130 {return prefixedString.substr('-o-'.length);}131 if (goog.string./*OK*/ startsWith(prefixedString, '-moz-'))132 {return prefixedString.substr('-moz-'.length);}133 if (goog.string./*OK*/ startsWith(prefixedString, '-ms-'))134 {return prefixedString.substr('-ms-'.length);}135 if (goog.string./*OK*/ startsWith(prefixedString, '-webkit-'))136 {return prefixedString.substr('-webkit-'.length);}137 }138 return prefixedString;139};140/**141 * Returns a Stylesheet object with nested parse_css.Rules.142 *143 * The top level Rules in a Stylesheet are always a series of144 * QualifiedRule's or AtRule's.145 *146 * @param {!Array<!parse_css.Token>} tokenList147 * @param {!Object<string,parse_css.BlockType>} atRuleSpec block type rules for148 * all CSS AT rules this canonicalizer should handle.149 * @param {parse_css.BlockType} defaultSpec default block type for types not150 * found in atRuleSpec.151 * @param {!Array<!parse_css.ErrorToken>} errors output array for the errors.152 * @return {!parse_css.Stylesheet}153 */154parse_css.parseAStylesheet = function(155 tokenList, atRuleSpec, defaultSpec, errors) {156 const canonicalizer = new Canonicalizer(atRuleSpec, defaultSpec);157 const stylesheet = new parse_css.Stylesheet();158 stylesheet.rules =159 canonicalizer.parseAListOfRules(tokenList, /* topLevel */ true, errors);160 tokenList[0].copyPosTo(stylesheet);161 const eof = /** @type {!parse_css.EOFToken} */162 (tokenList[tokenList.length - 1]);163 stylesheet.eof = eof;164 return stylesheet;165};166/**167 * Returns a array of Declaration objects.168 *169 * @param {!Array<!parse_css.Token>} tokenList170 * @param {!Array<!parse_css.ErrorToken>} errors output array for the errors.171 * @return {!Array<!parse_css.Declaration>}172 */173parse_css.parseInlineStyle = function(tokenList, errors) {174 const canonicalizer =175 new Canonicalizer({}, parse_css.BlockType.PARSE_AS_DECLARATIONS);176 return canonicalizer.parseAListOfDeclarations(tokenList, errors);177};178/**179 * Abstract super class for the parser rules.180 */181parse_css.Rule = class extends parse_css.Token {182 constructor() {183 super();184 /** @type {parse_css.TokenType} */185 this.tokenType = parse_css.TokenType.UNKNOWN;186 }187 /** @param {!parse_css.RuleVisitor} visitor */188 accept(visitor) {}189 /**190 * @param {number=} opt_indent191 * @return {string}192 */193 toString(opt_indent) {194 return JSON.stringify(this.toJSON(), null, opt_indent);195 }196};197parse_css.Stylesheet = class extends parse_css.Rule {198 constructor() {199 super();200 /** @type {!Array<!parse_css.Rule>} */201 this.rules = [];202 /** @type {?parse_css.EOFToken} */203 this.eof = null;204 /** @type {parse_css.TokenType} */205 this.tokenType = parse_css.TokenType.STYLESHEET;206 }207 /** @inheritDoc */208 accept(visitor) {209 visitor.visitStylesheet(this);210 for (const rule of this.rules) {211 rule.accept(visitor);212 }213 visitor.leaveStylesheet(this);214 }215};216if (!amp.validator.LIGHT) {217 /** @inheritDoc */218 parse_css.Stylesheet.prototype.toJSON = function() {219 const json = parse_css.Rule.prototype.toJSON.call(this);220 json['rules'] = arrayToJSON(this.rules);221 json['eof'] = this.eof.toJSON();222 return json;223 };224}225parse_css.AtRule = class extends parse_css.Rule {226 /**227 * @param {string} name228 */229 constructor(name) {230 super();231 /** @type {string} */232 this.name = name;233 /** @type {!Array<!parse_css.Token>} */234 this.prelude = [];235 /** @type {!Array<!parse_css.Rule>} */236 this.rules = [];237 /** @type {!Array<!parse_css.Declaration>} */238 this.declarations = [];239 /** @type {parse_css.TokenType} */240 this.tokenType = parse_css.TokenType.AT_RULE;241 }242 /** @inheritDoc */243 accept(visitor) {244 visitor.visitAtRule(this);245 for (const rule of this.rules) {246 rule.accept(visitor);247 }248 for (const declaration of this.declarations) {249 declaration.accept(visitor);250 }251 visitor.leaveAtRule(this);252 }253};254if (!amp.validator.LIGHT) {255 /** @inheritDoc */256 parse_css.AtRule.prototype.toJSON = function() {257 const json = parse_css.Rule.prototype.toJSON.call(this);258 json['name'] = this.name;259 json['prelude'] = arrayToJSON(this.prelude);260 json['rules'] = arrayToJSON(this.rules);261 json['declarations'] = arrayToJSON(this.declarations);262 return json;263 };264}265parse_css.QualifiedRule = class extends parse_css.Rule {266 constructor() {267 super();268 /** @type {!Array<!parse_css.Token>} */269 this.prelude = [];270 /** @type {!Array<!parse_css.Declaration>} */271 this.declarations = [];272 /** @type {parse_css.TokenType} */273 this.tokenType = parse_css.TokenType.QUALIFIED_RULE;274 }275 /** @inheritDoc */276 accept(visitor) {277 visitor.visitQualifiedRule(this);278 for (const declaration of this.declarations) {279 declaration.accept(visitor);280 }281 visitor.leaveQualifiedRule(this);282 }283};284if (!amp.validator.LIGHT) {285 /** @inheritDoc */286 parse_css.QualifiedRule.prototype.toJSON = function() {287 const json = parse_css.Rule.prototype.toJSON.call(this);288 json['prelude'] = arrayToJSON(this.prelude);289 json['declarations'] = arrayToJSON(this.declarations);290 return json;291 };292 /** @return {string} The concatenation of the qualified rule name. */293 parse_css.QualifiedRule.prototype.ruleName = function() {294 let ruleName = '';295 for (let i = 0; i < this.prelude.length; ++i) {296 const prelude =297 /** @type {!parse_css.IdentToken} */ (this.prelude[i]);298 if (prelude.value) {ruleName += prelude.value;}299 }300 return ruleName;301 };302}303parse_css.Declaration = class extends parse_css.Rule {304 /**305 * @param {string} name306 */307 constructor(name) {308 super();309 /** @type {string} */310 this.name = name;311 /** @type {!Array<!parse_css.Token>} */312 this.value = [];313 /** @type {boolean} */314 this.important = false;315 /** @type {parse_css.TokenType} */316 this.tokenType = parse_css.TokenType.DECLARATION;317 }318 /**319 * For a declaration, if the first non-whitespace token is an identifier,320 * returns its string value. Otherwise, returns the empty string.321 * @return {string}322 */323 firstIdent() {324 if (this.value.length === 0) {325 return '';326 }327 if (this.value[0].tokenType === parse_css.TokenType.IDENT) {328 return /** @type {!parse_css.StringValuedToken} */ (this.value[0]).value;329 }330 if (this.value.length >= 2 &&331 (this.value[0].tokenType === parse_css.TokenType.WHITESPACE) &&332 this.value[1].tokenType === parse_css.TokenType.IDENT) {333 return /** @type {!parse_css.StringValuedToken} */ (this.value[1]).value;334 }335 return '';336 }337 /** @inheritDoc */338 accept(visitor) {339 visitor.visitDeclaration(this);340 visitor.leaveDeclaration(this);341 }342};343if (!amp.validator.LIGHT) {344 /** @inheritDoc */345 parse_css.Declaration.prototype.toJSON = function() {346 const json = parse_css.Rule.prototype.toJSON.call(this);347 json['name'] = this.name;348 json['important'] = this.important;349 json['value'] = arrayToJSON(this.value);350 return json;351 };352}353/**354 * A visitor for Rule subclasses (StyleSheet, AtRule, QualifiedRule,355 * Declaration). Pass this to the Rule::Accept method.356 * Visitation order is to call the Visit* method on the current node,357 * then visit the children, then call the Leave* method on the current node.358 */359parse_css.RuleVisitor = class {360 constructor() {}361 /** @param {!parse_css.Stylesheet} stylesheet */362 visitStylesheet(stylesheet) {}363 /** @param {!parse_css.Stylesheet} stylesheet */364 leaveStylesheet(stylesheet) {}365 /** @param {!parse_css.AtRule} atRule */366 visitAtRule(atRule) {}367 /** @param {!parse_css.AtRule} atRule */368 leaveAtRule(atRule) {}369 /** @param {!parse_css.QualifiedRule} qualifiedRule */370 visitQualifiedRule(qualifiedRule) {}371 /** @param {!parse_css.QualifiedRule} qualifiedRule */372 leaveQualifiedRule(qualifiedRule) {}373 /** @param {!parse_css.Declaration} declaration */374 visitDeclaration(declaration) {}375 /** @param {!parse_css.Declaration} declaration */376 leaveDeclaration(declaration) {}377};378/**379 * Enum describing how to parse the rules inside a CSS AT Rule.380 * @enum {string}381 */382parse_css.BlockType = {383 // Parse this simple block as a list of rules384 // (Either Qualified Rules or AT Rules)385 'PARSE_AS_RULES': 'PARSE_AS_RULES',386 // Parse this simple block as a list of declarations387 'PARSE_AS_DECLARATIONS': 'PARSE_AS_DECLARATIONS',388 // Ignore this simple block, do not parse. This is generally used389 // in conjunction with a later step emitting an error for this rule.390 'PARSE_AS_IGNORE': 'PARSE_AS_IGNORE',391};392/**393 * A canonicalizer is created with a specific spec for canonicalizing CSS AT394 * rules. It otherwise has no state.395 * @private396 */397class Canonicalizer {398 /**399 * @param {!Object<string,parse_css.BlockType>} atRuleSpec block400 * type rules for all CSS AT rules this canonicalizer should handle.401 * @param {parse_css.BlockType} defaultSpec default block type for402 * types not found in atRuleSpec.403 */404 constructor(atRuleSpec, defaultSpec) {405 /**406 * @type {!Object<string,parse_css.BlockType>}407 * @private408 */409 this.atRuleSpec_ = atRuleSpec;410 /**411 * @type {parse_css.BlockType}412 * @private413 */414 this.defaultAtRuleSpec_ = defaultSpec;415 }416 /**417 * Returns a type telling us how to canonicalize a given AT rule's block.418 * @param {!parse_css.AtRule} atRule419 * @return {!parse_css.BlockType}420 */421 blockTypeFor(atRule) {422 const maybeBlockType =423 this.atRuleSpec_[parse_css.stripVendorPrefix(atRule.name)];424 if (maybeBlockType !== undefined) {425 return maybeBlockType;426 } else {427 return this.defaultAtRuleSpec_;428 }429 }430 /**431 * Parses and returns a list of rules, such as at the top level of a stylesheet.432 * Return list has only QualifiedRule's and AtRule's as top level elements.433 * @param {!Array<!parse_css.Token>} tokenList434 * @param {boolean} topLevel435 * @param {!Array<!parse_css.ErrorToken>} errors output array for the errors.436 * @return {!Array<!parse_css.Rule>}437 */438 parseAListOfRules(tokenList, topLevel, errors) {439 const tokenStream = new parse_css.TokenStream(tokenList);440 const rules = [];441 while (true) {442 tokenStream.consume();443 const current = tokenStream.current().tokenType;444 if (current === parse_css.TokenType.WHITESPACE) {445 continue;446 } else if (current === parse_css.TokenType.EOF_TOKEN) {447 return rules;448 } else if (449 current === parse_css.TokenType.CDO ||450 current === parse_css.TokenType.CDC) {451 if (topLevel) {452 continue;453 }454 this.parseAQualifiedRule(tokenStream, rules, errors);455 } else if (current === parse_css.TokenType.AT_KEYWORD) {456 rules.push(this.parseAnAtRule(tokenStream, errors));457 } else {458 this.parseAQualifiedRule(tokenStream, rules, errors);459 }460 }461 }462 /**463 * Parses an At Rule.464 *465 * @param {!parse_css.TokenStream} tokenStream466 * @param {!Array<!parse_css.ErrorToken>} errors output array for the errors.467 * @return {!parse_css.AtRule}468 */469 parseAnAtRule(tokenStream, errors) {470 goog.asserts.assert(471 tokenStream.current().tokenType === parse_css.TokenType.AT_KEYWORD,472 'Internal Error: parseAnAtRule precondition not met');473 const startToken =474 /** @type {!parse_css.AtKeywordToken} */ (tokenStream.current());475 const rule = new parse_css.AtRule(startToken.value);476 if (!amp.validator.LIGHT) {477 startToken.copyPosTo(rule);478 }479 while (true) {480 tokenStream.consume();481 const current = tokenStream.current().tokenType;482 if (current === parse_css.TokenType.SEMICOLON ||483 current === parse_css.TokenType.EOF_TOKEN) {484 rule.prelude.push(tokenStream.current());485 return rule;486 }487 if (current === parse_css.TokenType.OPEN_CURLY) {488 rule.prelude.push(489 tokenStream.current().copyPosTo(new parse_css.EOFToken()));490 /** @type {!Array<!parse_css.Token>} */491 const contents = parse_css.extractASimpleBlock(tokenStream);492 switch (this.blockTypeFor(rule)) {493 case parse_css.BlockType.PARSE_AS_RULES: {494 rule.rules =495 this.parseAListOfRules(contents, /* topLevel */ false, errors);496 break;497 }498 case parse_css.BlockType.PARSE_AS_DECLARATIONS: {499 rule.declarations = this.parseAListOfDeclarations(contents, errors);500 break;501 }502 case parse_css.BlockType.PARSE_AS_IGNORE: {503 break;504 }505 default: {506 goog.asserts.fail(507 'Unrecognized blockType ' + this.blockTypeFor(rule));508 break;509 }510 }511 return rule;512 }513 consumeAComponentValue(tokenStream, rule.prelude);514 }515 }516 /**517 * Parses one Qualified rule or ErrorToken appended to either rules or errors518 * respectively. Rule will include a prelude with the CSS selector (if any)519 * and a list of declarations.520 *521 * @param {!parse_css.TokenStream} tokenStream522 * @param {!Array<!parse_css.Rule>} rules output array for new rule523 * @param {!Array<!parse_css.ErrorToken>} errors output array for new error.524 */525 parseAQualifiedRule(tokenStream, rules, errors) {526 goog.asserts.assert(527 tokenStream.current().tokenType !== parse_css.TokenType.EOF_TOKEN &&528 tokenStream.current().tokenType !== parse_css.TokenType.AT_KEYWORD,529 'Internal Error: parseAQualifiedRule precondition not met');530 if (amp.validator.LIGHT && errors.length > 0) {531 return;532 }533 const rule = tokenStream.current().copyPosTo(new parse_css.QualifiedRule());534 tokenStream.reconsume();535 while (true) {536 tokenStream.consume();537 const current = tokenStream.current().tokenType;538 if (current === parse_css.TokenType.EOF_TOKEN) {539 if (amp.validator.LIGHT) {540 errors.push(parse_css.TRIVIAL_ERROR_TOKEN);541 } else {542 errors.push(rule.copyPosTo(new parse_css.ErrorToken(543 amp.validator.ValidationError.Code544 .CSS_SYNTAX_EOF_IN_PRELUDE_OF_QUALIFIED_RULE,545 ['style'])));546 }547 return;548 }549 if (current === parse_css.TokenType.OPEN_CURLY) {550 rule.prelude.push(551 tokenStream.current().copyPosTo(new parse_css.EOFToken()));552 // This consumes declarations (ie: "color: red;" ) inside553 // a qualified rule as that rule's value.554 rule.declarations = this.parseAListOfDeclarations(555 parse_css.extractASimpleBlock(tokenStream), errors);556 rules.push(rule);557 return;558 }559 // This consumes a CSS selector as the rules prelude.560 consumeAComponentValue(tokenStream, rule.prelude);561 }562 }563 /**564 * @param {!Array<!parse_css.Token>} tokenList565 * @param {!Array<!parse_css.ErrorToken>} errors output array for the errors.566 * @return {!Array<!parse_css.Declaration>}567 */568 parseAListOfDeclarations(tokenList, errors) {569 if (amp.validator.LIGHT && errors.length > 0) {570 return [];571 }572 /** @type {!Array<!parse_css.Declaration>} */573 const decls = [];574 const tokenStream = new parse_css.TokenStream(tokenList);575 while (true) {576 tokenStream.consume();577 const current = tokenStream.current().tokenType;578 if (current === parse_css.TokenType.WHITESPACE ||579 current === parse_css.TokenType.SEMICOLON) {580 continue;581 } else if (current === parse_css.TokenType.EOF_TOKEN) {582 return decls;583 } else if (current === parse_css.TokenType.AT_KEYWORD) {584 // The CSS3 Parsing spec allows for AT rules inside lists of585 // declarations, but our grammar does not so we deviate a tiny bit here.586 // We consume an AT rule, but drop it and instead push an error token.587 if (amp.validator.LIGHT) {588 errors.push(parse_css.TRIVIAL_ERROR_TOKEN);589 return [];590 }591 const atRule = this.parseAnAtRule(tokenStream, errors);592 errors.push(atRule.copyPosTo(new parse_css.ErrorToken(593 amp.validator.ValidationError.Code.CSS_SYNTAX_INVALID_AT_RULE,594 ['style', atRule.name])));595 } else if (current === parse_css.TokenType.IDENT) {596 this.parseADeclaration(tokenStream, decls, errors);597 } else {598 if (amp.validator.LIGHT) {599 errors.push(parse_css.TRIVIAL_ERROR_TOKEN);600 return [];601 }602 errors.push(tokenStream.current().copyPosTo(new parse_css.ErrorToken(603 amp.validator.ValidationError.Code.CSS_SYNTAX_INVALID_DECLARATION,604 ['style'])));605 tokenStream.reconsume();606 while (607 !(tokenStream.next().tokenType === parse_css.TokenType.SEMICOLON ||608 tokenStream.next().tokenType === parse_css.TokenType.EOF_TOKEN)) {609 tokenStream.consume();610 const dummyTokenList = [];611 consumeAComponentValue(tokenStream, dummyTokenList);612 }613 }614 }615 }616 /**617 * Adds one element to either declarations or errors.618 * @param {!parse_css.TokenStream} tokenStream619 * @param {!Array<!parse_css.Declaration>} declarations output array for620 * declarations621 * @param {!Array<!parse_css.ErrorToken>} errors output array for the errors.622 */623 parseADeclaration(tokenStream, declarations, errors) {624 goog.asserts.assert(625 tokenStream.current().tokenType === parse_css.TokenType.IDENT,626 'Internal Error: parseADeclaration precondition not met');627 if (amp.validator.LIGHT && errors.length > 0) {628 return;629 }630 const startToken =631 /** @type {!parse_css.IdentToken} */ (tokenStream.current());632 const decl =633 startToken.copyPosTo(new parse_css.Declaration(startToken.value));634 while (tokenStream.next().tokenType === parse_css.TokenType.WHITESPACE) {635 tokenStream.consume();636 }637 tokenStream.consume();638 if (!(tokenStream.current().tokenType === parse_css.TokenType.COLON)) {639 if (amp.validator.LIGHT) {640 errors.push(parse_css.TRIVIAL_ERROR_TOKEN);641 return;642 }643 errors.push(startToken.copyPosTo(new parse_css.ErrorToken(644 amp.validator.ValidationError.Code.CSS_SYNTAX_INCOMPLETE_DECLARATION,645 ['style'])));646 tokenStream.reconsume();647 while (648 !(tokenStream.next().tokenType === parse_css.TokenType.SEMICOLON ||649 tokenStream.next().tokenType === parse_css.TokenType.EOF_TOKEN)) {650 tokenStream.consume();651 }652 return;653 }654 while (655 !(tokenStream.next().tokenType === parse_css.TokenType.SEMICOLON ||656 tokenStream.next().tokenType === parse_css.TokenType.EOF_TOKEN)) {657 tokenStream.consume();658 consumeAComponentValue(tokenStream, decl.value);659 }660 decl.value.push(tokenStream.next().copyPosTo(new parse_css.EOFToken()));661 let foundImportant = false;662 for (let i = decl.value.length - 1; i >= 0; i--) {663 if (decl.value[i].tokenType === parse_css.TokenType.WHITESPACE) {664 continue;665 } else if (666 decl.value[i].tokenType === parse_css.TokenType.IDENT &&667 /** @type {parse_css.IdentToken} */668 (decl.value[i]).ASCIIMatch('important')) {669 foundImportant = true;670 } else if (671 foundImportant &&672 decl.value[i].tokenType === parse_css.TokenType.DELIM &&673 /** @type {parse_css.DelimToken} */ (decl.value[i]).value === '!') {674 decl.value.splice(i, decl.value.length);675 decl.important = true;676 break;677 } else {678 break;679 }680 }681 declarations.push(decl);682 }683}684/**685 * Consumes one or more tokens from a tokenStream, appending them to a686 * tokenList.687 * @param {!parse_css.TokenStream} tokenStream688 * @param {!Array<!parse_css.Token>} tokenList output array for tokens.689 */690function consumeAComponentValue(tokenStream, tokenList) {691 const current = tokenStream.current().tokenType;692 if (current === parse_css.TokenType.OPEN_CURLY ||693 current === parse_css.TokenType.OPEN_SQUARE ||694 current === parse_css.TokenType.OPEN_PAREN) {695 consumeASimpleBlock(tokenStream, tokenList);696 } else if (current === parse_css.TokenType.FUNCTION_TOKEN) {697 consumeAFunction(tokenStream, tokenList);698 } else {699 tokenList.push(tokenStream.current());700 }701}702/**703 * Appends a simple block's contents to a tokenList, consuming from704 * the stream all those tokens that it adds to the tokenList,705 * including the start/end grouping token.706 * @param {!parse_css.TokenStream} tokenStream707 * @param {!Array<!parse_css.Token>} tokenList output array for tokens.708 */709function consumeASimpleBlock(tokenStream, tokenList) {710 const current = tokenStream.current().tokenType;711 goog.asserts.assert(712 (current === parse_css.TokenType.OPEN_CURLY ||713 current === parse_css.TokenType.OPEN_SQUARE ||714 current === parse_css.TokenType.OPEN_PAREN),715 'Internal Error: consumeASimpleBlock precondition not met');716 const startToken =717 /** @type {!parse_css.GroupingToken} */ (tokenStream.current());718 const {mirror} = startToken;719 tokenList.push(startToken);720 while (true) {721 tokenStream.consume();722 const current = tokenStream.current().tokenType;723 if (current === parse_css.TokenType.EOF_TOKEN) {724 tokenList.push(tokenStream.current());725 return;726 } else if (727 (current === parse_css.TokenType.CLOSE_CURLY ||728 current === parse_css.TokenType.CLOSE_SQUARE ||729 current === parse_css.TokenType.CLOSE_PAREN) &&730 /** @type {parse_css.GroupingToken} */ (tokenStream.current()).value ===731 mirror) {732 tokenList.push(tokenStream.current());733 return;734 } else {735 consumeAComponentValue(tokenStream, tokenList);736 }737 }738}739/**740 * Returns a simple block's contents in tokenStream, excluding the741 * start/end grouping token, and appended with an EOFToken.742 * @param {!parse_css.TokenStream} tokenStream743 * @return {!Array<!parse_css.Token>}744 */745parse_css.extractASimpleBlock = function(tokenStream) {746 /** @type {!Array<!parse_css.Token>} */747 const consumedTokens = [];748 consumeASimpleBlock(tokenStream, consumedTokens);749 // A simple block always has a start token (e.g. '{') and750 // either a closing token or EOF token.751 goog.asserts.assert(consumedTokens.length >= 2);752 // Exclude the start token. Convert end token to EOF.753 const end = consumedTokens.length - 1;754 consumedTokens[end] = amp.validator.LIGHT ?755 parse_css.TRIVIAL_EOF_TOKEN :756 consumedTokens[end].copyPosTo(new parse_css.EOFToken());757 return consumedTokens.slice(1);758};759/**760 * Appends a function's contents to a tokenList, consuming from the761 * stream all those tokens that it adds to the tokenList, including762 * the function token and end grouping token.763 * @param {!parse_css.TokenStream} tokenStream764 * @param {!Array<!parse_css.Token>} tokenList output array for tokens.765 */766function consumeAFunction(tokenStream, tokenList) {767 goog.asserts.assert(768 tokenStream.current().tokenType === parse_css.TokenType.FUNCTION_TOKEN,769 'Internal Error: consumeAFunction precondition not met');770 tokenList.push(tokenStream.current());771 while (true) {772 tokenStream.consume();773 const current = tokenStream.current().tokenType;774 if (current === parse_css.TokenType.EOF_TOKEN ||775 current === parse_css.TokenType.CLOSE_PAREN) {776 tokenList.push(tokenStream.current());777 return;778 } else {779 consumeAComponentValue(tokenStream, tokenList);780 }781 }782}783/**784 * Returns a function's contents in tokenList, including the leading785 * FunctionToken, but excluding the trailing CloseParen token and786 * appended with an EOFToken instead.787 * @param {!parse_css.TokenStream} tokenStream788 * @return {!Array<!parse_css.Token>}789 */790parse_css.extractAFunction = function(tokenStream) {791 /** @type {!Array<!parse_css.Token>} */792 const consumedTokens = [];793 consumeAFunction(tokenStream, consumedTokens);794 // A function always has a start FunctionToken and795 // either a CloseParenToken or EOFToken.796 goog.asserts.assert(consumedTokens.length >= 2);797 // Convert end token to EOF.798 const end = consumedTokens.length - 1;799 consumedTokens[end] = amp.validator.LIGHT ?800 parse_css.TRIVIAL_EOF_TOKEN :801 consumedTokens[end].copyPosTo(new parse_css.EOFToken());802 return consumedTokens;803};804/**805 * Used by parse_css.ExtractUrls to return urls it has seen. This represents806 * URLs in CSS such as url(http://foo.com/) and url("http://bar.com/").807 * For this token, line() and col() indicate the position information808 * of the left-most CSS token that's part of the URL. E.g., this would be809 * the URLToken instance or the FunctionToken instance.810 */811parse_css.ParsedCssUrl = class extends parse_css.Token {812 constructor() {813 super();814 /** @type {parse_css.TokenType} */815 this.tokenType = parse_css.TokenType.PARSED_CSS_URL;816 /**817 * The decoded URL. This string will not contain CSS string escapes,818 * quotes, or similar. Encoding is utf8.819 * @type {string}820 */821 this.utf8Url = '';822 /**823 * A rule scope, in case the url was encountered within an at-rule.824 * If not within an at-rule, this string is empty.825 * @type {string}826 */827 this.atRuleScope = '';828 }829};830if (!amp.validator.LIGHT) {831 /** @inheritDoc */832 parse_css.ParsedCssUrl.prototype.toJSON = function() {833 const json = parse_css.Token.prototype.toJSON.call(this);834 json['utf8Url'] = this.utf8Url;835 json['atRuleScope'] = this.atRuleScope;836 return json;837 };838}839/**840 * Parses a CSS URL token; typically takes the form "url(http://foo)".841 * Preconditions: tokens[token_idx] is a URL token842 * and token_idx + 1 is in range.843 * @param {!Array<!parse_css.Token>} tokens844 * @param {number} tokenIdx845 * @param {!parse_css.ParsedCssUrl} parsed846 */847function parseUrlToken(tokens, tokenIdx, parsed) {848 goog.asserts.assert(tokenIdx + 1 < tokens.length);849 const token = tokens[tokenIdx];850 goog.asserts.assert(token.tokenType === parse_css.TokenType.URL);851 token.copyPosTo(parsed);852 parsed.utf8Url = /** @type {parse_css.URLToken}*/ (token).value;853}854/**855 * Parses a CSS function token named 'url', including the string and closing856 * paren. Typically takes the form "url('http://foo')".857 * Returns the token_idx past the closing paren, or -1 if parsing fails.858 * Preconditions: tokens[token_idx] is a URL token859 * and tokens[token_idx]->StringValue() == "url"860 * @param {!Array<!parse_css.Token>} tokens861 * @param {number} tokenIdx862 * @param {!parse_css.ParsedCssUrl} parsed863 * @return {number}864 */865function parseUrlFunction(tokens, tokenIdx, parsed) {866 const token = tokens[tokenIdx];867 goog.asserts.assert(token.tokenType == parse_css.TokenType.FUNCTION_TOKEN);868 goog.asserts.assert(869 /** @type {parse_css.FunctionToken} */ (token).value === 'url');870 goog.asserts.assert(871 tokens[tokens.length - 1].tokenType === parse_css.TokenType.EOF_TOKEN);872 token.copyPosTo(parsed);873 ++tokenIdx; // We've digested the function token above.874 // Safe: tokens ends w/ EOF_TOKEN.875 goog.asserts.assert(tokenIdx < tokens.length);876 // Consume optional whitespace.877 while (tokens[tokenIdx].tokenType === parse_css.TokenType.WHITESPACE) {878 ++tokenIdx;879 // Safe: tokens ends w/ EOF_TOKEN.880 goog.asserts.assert(tokenIdx < tokens.length);881 }882 // Consume URL.883 if (tokens[tokenIdx].tokenType !== parse_css.TokenType.STRING) {884 return -1;885 }886 parsed.utf8Url =887 /** @type {parse_css.StringToken} */ (tokens[tokenIdx]).value;888 ++tokenIdx;889 // Safe: tokens ends w/ EOF_TOKEN.890 goog.asserts.assert(tokenIdx < tokens.length);891 // Consume optional whitespace.892 while (tokens[tokenIdx].tokenType === parse_css.TokenType.WHITESPACE) {893 ++tokenIdx;894 // Safe: tokens ends w/ EOF_TOKEN.895 goog.asserts.assert(tokenIdx < tokens.length);896 }897 // Consume ')'898 if (tokens[tokenIdx].tokenType !== parse_css.TokenType.CLOSE_PAREN) {899 return -1;900 }901 return tokenIdx + 1;902}903/**904 * Helper class for implementing parse_css.extractUrls.905 * @private906 */907class UrlFunctionVisitor extends parse_css.RuleVisitor {908 /**909 * @param {!Array<!parse_css.ParsedCssUrl>} parsedUrls910 * @param {!Array<!parse_css.ErrorToken>} errors911 */912 constructor(parsedUrls, errors) {913 super();914 /** @type {!Array<!parse_css.ParsedCssUrl>} */915 this.parsedUrls = parsedUrls;916 /** @type {!Array<!parse_css.ErrorToken>} */917 this.errors = errors;918 /** @type {string} */919 this.atRuleScope = '';920 }921 /** @inheritDoc */922 visitAtRule(atRule) {923 this.atRuleScope = atRule.name;924 }925 /** @inheritDoc */926 leaveAtRule(atRule) {927 this.atRuleScope = '';928 }929 /** @inheritDoc */930 visitQualifiedRule(qualifiedRule) {931 this.atRuleScope = '';932 }933 /** @inheritDoc */934 visitDeclaration(declaration) {935 goog.asserts.assert(declaration.value.length > 0);936 goog.asserts.assert(937 declaration.value[declaration.value.length - 1].tokenType ===938 parse_css.TokenType.EOF_TOKEN);939 if (amp.validator.LIGHT && this.errors.length > 0) {940 return;941 }942 for (let ii = 0; ii < declaration.value.length - 1;) {943 const token = declaration.value[ii];944 if (token.tokenType === parse_css.TokenType.URL) {945 const parsedUrl = new parse_css.ParsedCssUrl();946 parseUrlToken(declaration.value, ii, parsedUrl);947 parsedUrl.atRuleScope = this.atRuleScope;948 this.parsedUrls.push(parsedUrl);949 ++ii;950 continue;951 }952 if (token.tokenType === parse_css.TokenType.FUNCTION_TOKEN &&953 /** @type {!parse_css.FunctionToken} */ (token).value === 'url') {954 const parsedUrl = new parse_css.ParsedCssUrl();955 ii = parseUrlFunction(declaration.value, ii, parsedUrl);956 if (ii === -1) {957 if (amp.validator.LIGHT) {958 this.errors.push(parse_css.TRIVIAL_ERROR_TOKEN);959 } else {960 this.errors.push(token.copyPosTo(new parse_css.ErrorToken(961 amp.validator.ValidationError.Code.CSS_SYNTAX_BAD_URL,962 ['style'])));963 }964 return;965 }966 parsedUrl.atRuleScope = this.atRuleScope;967 this.parsedUrls.push(parsedUrl);968 continue;969 }970 // It's neither a url token nor a function token named url. So, we skip.971 ++ii;972 }973 }974}975/**976 * Extracts the URLs within the provided stylesheet, emitting them into977 * parsedUrls and errors into errors.978 * @param {!parse_css.Stylesheet} stylesheet979 * @param {!Array<!parse_css.ParsedCssUrl>} parsedUrls980 * @param {!Array<!parse_css.ErrorToken>} errors981 */982parse_css.extractUrls = function(stylesheet, parsedUrls, errors) {983 const parsedUrlsOldLength = parsedUrls.length;984 const errorsOldLength = errors.length;985 const visitor = new UrlFunctionVisitor(parsedUrls, errors);986 stylesheet.accept(visitor);987 // If anything went wrong, delete the urls we've already emitted.988 if (errorsOldLength !== errors.length) {989 parsedUrls.splice(parsedUrlsOldLength);990 }991};992/**993 * Helper class for implementing parse_css.parseMediaQueries.994 * @private995 */996class MediaQueryVisitor extends parse_css.RuleVisitor {997 /**998 * @param {!Array<!parse_css.IdentToken>} mediaTypes999 * @param {!Array<!parse_css.IdentToken>} mediaFeatures1000 * @param {!Array<!parse_css.ErrorToken>} errors1001 */1002 constructor(mediaTypes, mediaFeatures, errors) {1003 super();1004 /** @type {!Array<!parse_css.IdentToken>} */1005 this.mediaTypes = mediaTypes;1006 /** @type {!Array<!parse_css.IdentToken>} */1007 this.mediaFeatures = mediaFeatures;1008 /** @type {!Array<!parse_css.ErrorToken>} */1009 this.errors = errors;1010 }1011 /** @inheritDoc */1012 visitAtRule(atRule) {1013 if (atRule.name.toLowerCase() !== 'media') {return;}1014 const tokenStream = new parse_css.TokenStream(atRule.prelude);1015 tokenStream.consume(); // Advance to first token.1016 if (!this.parseAMediaQueryList_(tokenStream)) {1017 if (amp.validator.LIGHT) {1018 this.errors.push(parse_css.TRIVIAL_ERROR_TOKEN);1019 } else {1020 this.errors.push(atRule.copyPosTo(new parse_css.ErrorToken(1021 amp.validator.ValidationError.Code.CSS_SYNTAX_MALFORMED_MEDIA_QUERY,1022 ['style'])));1023 }1024 }1025 }1026 /**1027 * Maybe consume one whitespace token.1028 * @param {!parse_css.TokenStream} tokenStream1029 * @private1030 */1031 maybeConsumeAWhitespaceToken_(tokenStream) {1032 // While the grammar calls for consuming multiple whitespace tokens,1033 // our tokenizer already collapses whitespace so only one token can ever1034 // be present.1035 if (tokenStream.current().tokenType === parse_css.TokenType.WHITESPACE)1036 {tokenStream.consume();}1037 }1038 /**1039 * Parse a media query list1040 * @param {!parse_css.TokenStream} tokenStream1041 * @return {boolean}1042 * @private1043 */1044 parseAMediaQueryList_(tokenStream) {1045 // https://www.w3.org/TR/css3-mediaqueries/#syntax1046 // : S* [media_query [ ',' S* media_query ]* ]?1047 // ;1048 this.maybeConsumeAWhitespaceToken_(tokenStream);1049 if (tokenStream.current().tokenType !== parse_css.TokenType.EOF_TOKEN) {1050 if (!this.parseAMediaQuery_(tokenStream)) {return false;}1051 while (tokenStream.current().tokenType === parse_css.TokenType.COMMA) {1052 tokenStream.consume(); // ','1053 this.maybeConsumeAWhitespaceToken_(tokenStream);1054 if (!this.parseAMediaQuery_(tokenStream)) {return false;}1055 }1056 }1057 return tokenStream.current().tokenType === parse_css.TokenType.EOF_TOKEN;1058 }1059 /**1060 * Parse a media query1061 * @param {!parse_css.TokenStream} tokenStream1062 * @return {boolean}1063 * @private1064 */1065 parseAMediaQuery_(tokenStream) {1066 // : [ONLY | NOT]? S* media_type S* [ AND S* expression ]*1067 // | expression [ AND S* expression ]*1068 // ;1069 //1070 // Below we parse media queries with this equivalent grammar:1071 // : (expression | [ONLY | NOT]? S* media_type S* )1072 // [ AND S* expression ]*1073 // ;1074 //1075 // This is more convenient because we know that expressions must start with1076 // '(', so it's simpler to use as a check to distinguis the expression case1077 // from the media type case.1078 if (tokenStream.current().tokenType === parse_css.TokenType.OPEN_PAREN) {1079 if (!this.parseAMediaExpression_(tokenStream)) {return false;}1080 } else {1081 if (tokenStream.current().tokenType === parse_css.TokenType.IDENT &&1082 (1083 /** @type {parse_css.IdentToken} */1084 (tokenStream.current()).ASCIIMatch('only') ||1085 /** @type {parse_css.IdentToken} */1086 (tokenStream.current()).ASCIIMatch('not'))) {1087 tokenStream.consume(); // 'ONLY' | 'NOT'1088 }1089 this.maybeConsumeAWhitespaceToken_(tokenStream);1090 if (!this.parseAMediaType_(tokenStream)) {return false;}1091 this.maybeConsumeAWhitespaceToken_(tokenStream);1092 }1093 while (tokenStream.current().tokenType === parse_css.TokenType.IDENT &&1094 /** @type {parse_css.IdentToken} */1095 (tokenStream.current()).ASCIIMatch('and')) {1096 tokenStream.consume(); // 'AND'1097 this.maybeConsumeAWhitespaceToken_(tokenStream);1098 if (!this.parseAMediaExpression_(tokenStream)) {return false;}1099 }1100 return true;1101 }1102 /**1103 * Parse a media type1104 * @param {!parse_css.TokenStream} tokenStream1105 * @return {boolean}1106 * @private1107 */1108 parseAMediaType_(tokenStream) {1109 // : IDENT1110 // ;1111 if (tokenStream.current().tokenType === parse_css.TokenType.IDENT) {1112 this.mediaTypes.push(1113 /** @type {!parse_css.IdentToken} */ (tokenStream.current()));1114 tokenStream.consume();1115 return true;1116 }1117 return false;1118 }1119 /**1120 * Parse a media expression1121 * @param {!parse_css.TokenStream} tokenStream1122 * @return {boolean}1123 * @private1124 */1125 parseAMediaExpression_(tokenStream) {1126 // : '(' S* media_feature S* [ ':' S* expr ]? ')' S*1127 // ;1128 if (tokenStream.current().tokenType !== parse_css.TokenType.OPEN_PAREN)1129 {return false;}1130 tokenStream.consume(); // '('1131 this.maybeConsumeAWhitespaceToken_(tokenStream);1132 if (!this.parseAMediaFeature_(tokenStream)) {return false;}1133 this.maybeConsumeAWhitespaceToken_(tokenStream);1134 if (tokenStream.current().tokenType === parse_css.TokenType.COLON) {1135 tokenStream.consume(); // '('1136 this.maybeConsumeAWhitespaceToken_(tokenStream);1137 // The CSS3 grammar at this point just tells us to expect some1138 // expr. Which tokens are accepted here are defined by the media1139 // feature found above. We don't implement media features here, so1140 // we just loop over tokens until we find a CLOSE_PAREN or EOF.1141 // While expr in general may have arbitrary sets of open/close parens,1142 // it seems that https://www.w3.org/TR/css3-mediaqueries/#media11143 // suggests that media features cannot:1144 //1145 // "Media features only accept single values: one keyword, one number,1146 // or a number with a unit identifier. (The only exceptions are the1147 // ‘aspect-ratio’ and ‘device-aspect-ratio’ media features.)1148 while (1149 tokenStream.current().tokenType !== parse_css.TokenType.EOF_TOKEN &&1150 tokenStream.current().tokenType !== parse_css.TokenType.CLOSE_PAREN)1151 {tokenStream.consume();}1152 }1153 if (tokenStream.current().tokenType !== parse_css.TokenType.CLOSE_PAREN)1154 {return false;}1155 tokenStream.consume(); // ')'1156 this.maybeConsumeAWhitespaceToken_(tokenStream);1157 return true;1158 }1159 /**1160 * Parse a media feature1161 * @param {!parse_css.TokenStream} tokenStream1162 * @return {boolean}1163 * @private1164 */1165 parseAMediaFeature_(tokenStream) {1166 // : IDENT1167 // ;1168 if (tokenStream.current().tokenType === parse_css.TokenType.IDENT) {1169 this.mediaFeatures.push(1170 /** @type {!parse_css.IdentToken} */ (tokenStream.current()));1171 tokenStream.consume();1172 return true;1173 }1174 return false;1175 }1176}1177/**1178 * Parses media queries within the provided stylesheet, emitting the set of1179 * discovered media types and media features, as well as errors if parsing1180 * failed.1181 * parsedUrls and errors into errors.1182 * @param {!parse_css.Stylesheet} stylesheet1183 * @param {!Array<!parse_css.IdentToken>} mediaTypes1184 * @param {!Array<!parse_css.IdentToken>} mediaFeatures1185 * @param {!Array<!parse_css.ErrorToken>} errors1186 */1187parse_css.parseMediaQueries = function(1188 stylesheet, mediaTypes, mediaFeatures, errors) {1189 const visitor = new MediaQueryVisitor(mediaTypes, mediaFeatures, errors);1190 stylesheet.accept(visitor);...

Full Screen

Full Screen

css-selectors.js

Source:css-selectors.js Github

copy

Full Screen

1/**2 * @license3 * Copyright 2015 The AMP HTML Authors. All Rights Reserved.4 *5 * Licensed under the Apache License, Version 2.0 (the "License");6 * you may not use this file except in compliance with the License.7 * You may obtain a copy of the License at8 *9 * http://www.apache.org/licenses/LICENSE-2.010 *11 * Unless required by applicable law or agreed to in writing, software12 * distributed under the License is distributed on an "AS-IS" BASIS,13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.14 * See the License for the specific language governing permissions and15 * limitations under the license.16 */17goog.provide('parse_css.AttrSelector');18goog.provide('parse_css.ClassSelector');19goog.provide('parse_css.Combinator');20goog.provide('parse_css.IdSelector');21goog.provide('parse_css.PseudoSelector');22goog.provide('parse_css.Selector');23goog.provide('parse_css.SelectorVisitor');24goog.provide('parse_css.SelectorsGroup');25goog.provide('parse_css.SimpleSelectorSequence');26goog.provide('parse_css.TypeSelector');27goog.provide('parse_css.parseAClassSelector');28goog.provide('parse_css.parseASelector');29goog.provide('parse_css.parseASelectorsGroup');30goog.provide('parse_css.parseASimpleSelectorSequence');31goog.provide('parse_css.parseATypeSelector');32goog.provide('parse_css.parseAnAttrSelector');33goog.provide('parse_css.parseAnIdSelector');34goog.provide('parse_css.parseSelectors');35goog.provide('parse_css.traverseSelectors');36goog.require('goog.asserts');37goog.require('parse_css.EOFToken');38goog.require('parse_css.ErrorToken');39goog.require('parse_css.Token');40goog.require('parse_css.TokenStream');41goog.require('parse_css.extractAFunction');42/**43 * Abstract super class for CSS Selectors. The Token class, which this44 * class inherits from, has line, col, and tokenType fields.45 */46parse_css.Selector = class extends parse_css.Token {47 /** @param {!function(!parse_css.Selector)} lambda */48 forEachChild(lambda) {}49 /** @param {!parse_css.SelectorVisitor} visitor */50 accept(visitor) {}51};52/**53 * A super class for making visitors (by overriding the types of interest).54 * The parse_css.traverseSelectros function can be used to visit nodes in a55 * parsed CSS selector.56 */57parse_css.SelectorVisitor = class {58 constructor() {}59 /** @param {!parse_css.TypeSelector} typeSelector */60 visitTypeSelector(typeSelector) {}61 /** @param {!parse_css.IdSelector} idSelector */62 visitIdSelector(idSelector) {}63 /** @param {!parse_css.AttrSelector} attrSelector */64 visitAttrSelector(attrSelector) {}65 /** @param {!parse_css.PseudoSelector} pseudoSelector */66 visitPseudoSelector(pseudoSelector) {}67 /** @param {!parse_css.ClassSelector} classSelector */68 visitClassSelector(classSelector) {}69 /** @param {!parse_css.SimpleSelectorSequence} sequence */70 visitSimpleSelectorSequence(sequence) {}71 /** @param {!parse_css.Combinator} combinator */72 visitCombinator(combinator) {}73 /** @param {!parse_css.SelectorsGroup} group */74 visitSelectorsGroup(group) {}75};76/**77 * Visits selectorNode and its children, recursively, by calling the78 * appropriate methods on the provided visitor.79 * @param {!parse_css.Selector} selectorNode80 * @param {!parse_css.SelectorVisitor} visitor81 */82parse_css.traverseSelectors = function(selectorNode, visitor) {83 /** @type {!Array<!parse_css.Selector>} */84 const toVisit = [selectorNode];85 while (toVisit.length > 0) {86 /** @type {!parse_css.Selector} */87 const node = toVisit.shift();88 node.accept(visitor);89 node.forEachChild(child => { toVisit.push(child); });90 }91};92/**93 * This node models type selectors and universial selectors.94 * http://www.w3.org/TR/css3-selectors/#type-selectors95 * http://www.w3.org/TR/css3-selectors/#universal-selector96 */97parse_css.TypeSelector = class extends parse_css.Selector {98 /**99 * Choices for namespacePrefix:100 * - 'a specific namespace prefix' means 'just that specific namespace'.101 * - '' means 'without a namespace'102 * - '*' means 'any namespace including without a namespace'103 * - null means the default namespace if one is declared, and '*' otherwise.104 *105 * The universal selector is covered by setting the elementName to '*'.106 *107 * @param {?string} namespacePrefix108 * @param {string} elementName109 */110 constructor(namespacePrefix, elementName) {111 super();112 /** @type {?string} */113 this.namespacePrefix = namespacePrefix;114 /** @type {string} */115 this.elementName = elementName;116 /** @type {parse_css.TokenType} */117 this.tokenType = parse_css.TokenType.TYPE_SELECTOR;118 }119 /**120 * Serializes the selector to a string (in this case CSS syntax that121 * could be used to recreate it).122 * @return {string}123 */124 toString() {125 if (this.namespacePrefix === null) {126 return this.elementName;127 }128 return this.namespacePrefix + '|' + this.elementName;129 }130 /** @inheritDoc */131 toJSON() {132 const json = super.toJSON();133 json['namespacePrefix'] = this.namespacePrefix;134 json['elementName'] = this.elementName;135 return json;136 }137 /** @inheritDoc */138 accept(visitor) {139 visitor.visitTypeSelector(this);140 }141};142/**143 * Helper function for determining whether the provided token is a specific144 * delimiter.145 * @param {!parse_css.Token} token146 * @param {string} delimChar147 * @return {boolean}148 */149function isDelim(token, delimChar) {150 if (!(token instanceof parse_css.DelimToken)) {151 return false;152 }153 const delimToken = goog.asserts.assertInstanceof(token, parse_css.DelimToken);154 return delimToken.value === delimChar;155}156/**157 * tokenStream.current() is the first token of the type selector.158 * @param {!parse_css.TokenStream} tokenStream159 * @return {!parse_css.TypeSelector}160 */161parse_css.parseATypeSelector = function(tokenStream) {162 /** @type {?string} */163 let namespacePrefix = null;164 /** @type {string} */165 let elementName = '*';166 const start = tokenStream.current();167 if (isDelim(tokenStream.current(), '|')) {168 namespacePrefix = '';169 tokenStream.consume();170 } else if (isDelim(tokenStream.current(), '*') &&171 isDelim(tokenStream.next(), '|')) {172 namespacePrefix = '*';173 tokenStream.consume();174 tokenStream.consume();175 } else if (tokenStream.current() instanceof parse_css.IdentToken &&176 isDelim(tokenStream.next(), '|')) {177 const ident = goog.asserts.assertInstanceof(178 tokenStream.current(), parse_css.IdentToken);179 namespacePrefix = ident.value;180 tokenStream.consume();181 tokenStream.consume();182 }183 if (tokenStream.current() instanceof parse_css.DelimToken &&184 isDelim(tokenStream.current(), '*')) {185 elementName = '*';186 tokenStream.consume();187 } else if (tokenStream.current() instanceof parse_css.IdentToken) {188 const ident = goog.asserts.assertInstanceof(189 tokenStream.current(), parse_css.IdentToken);190 elementName = ident.value;191 tokenStream.consume();192 }193 const selector = new parse_css.TypeSelector(194 namespacePrefix, elementName);195 start.copyStartPositionTo(selector);196 return selector;197};198/**199 * An ID selector references some document id.200 * http://www.w3.org/TR/css3-selectors/#id-selectors201 * Typically written as '#foo'.202 */203parse_css.IdSelector = class extends parse_css.Selector {204 /**205 * @param {string} value206 */207 constructor(value) {208 super();209 /** @type {string} */210 this.value = value;211 /** @type {parse_css.TokenType} */212 this.tokenType = parse_css.TokenType.ID_SELECTOR;213 }214 /** @return {string} */215 toString() { return '#' + this.value; }216 /** @inheritDoc */217 toJSON() {218 const json = super.toJSON();219 json['value'] = this.value;220 return json;221 }222 /** @inheritDoc */223 accept(visitor) {224 visitor.visitIdSelector(this);225 }226};227/**228 * tokenStream.current() must be the hash token.229 * @param {!parse_css.TokenStream} tokenStream230 * @return {!parse_css.IdSelector}231 */232parse_css.parseAnIdSelector = function(tokenStream) {233 goog.asserts.assertInstanceof(234 tokenStream.current(), parse_css.HashToken,235 'Precondition violated: must start with HashToken');236 const hash = goog.asserts.assertInstanceof(237 tokenStream.current(), parse_css.HashToken);238 tokenStream.consume();239 const selector = new parse_css.IdSelector(hash.value);240 hash.copyStartPositionTo(selector);241 return selector;242};243/**244 * An attribute selector matches document nodes based on their attributes.245 * http://www.w3.org/TR/css3-selectors/#attribute-selectors246 *247 * Typically written as '[foo=bar]'.248 */249parse_css.AttrSelector = class extends parse_css.Selector {250 /**251 * @param {string?} namespacePrefix252 * @param {!string} attrName253 * @param {!string} matchOperator is either the string254 * representation of the match operator (e.g., '=' or '~=') or '',255 * in which case the attribute selector is a check for the presence256 * of the attribute.257 * @param {!string} value is the value to apply the match operator258 * against, or if matchOperator is '', then this must be empty as259 * well.260 */261 constructor(namespacePrefix, attrName, matchOperator, value) {262 super();263 /** @type {string?} */264 this.namespacePrefix = namespacePrefix;265 /** @type {!string} */266 this.attrName = attrName;267 /** @type {string?} */268 this.matchOperator = matchOperator;269 /** @type {string?} */270 this.value = value;271 /** @type {parse_css.TokenType} */272 this.tokenType = parse_css.TokenType.ATTR_SELECTOR;273 }274 /** @inheritDoc */275 toJSON() {276 const json = super.toJSON();277 json['namespacePrefix'] = this.namespacePrefix;278 json['attrName'] = this.attrName;279 json['matchOperator'] = this.matchOperator;280 json['value'] = this.value;281 return json;282 }283 /** @inheritDoc */284 accept(visitor) {285 visitor.visitAttrSelector(this);286 }287};288/**289 * Helper for parseAnAttrSelector.290 * @private291 * @param {!parse_css.Token} start292 * @return {!parse_css.ErrorToken}293 */294function newInvalidAttrSelectorError(start) {295 const error = new parse_css.ErrorToken(296 amp.validator.ValidationError.Code.CSS_SYNTAX_INVALID_ATTR_SELECTOR,297 ['style']);298 start.copyStartPositionTo(error);299 return error;300}301/**302 * tokenStream.current() must be the open square token.303 * @param {!parse_css.TokenStream} tokenStream304 * @return {!parse_css.AttrSelector|!parse_css.ErrorToken}305 */306parse_css.parseAnAttrSelector = function(tokenStream) {307 goog.asserts.assert(308 tokenStream.current() instanceof parse_css.OpenSquareToken,309 'Precondition violated: must be an OpenSquareToken');310 const start = tokenStream.current();311 tokenStream.consume(); // Consumes '['.312 if (tokenStream.current() instanceof parse_css.WhitespaceToken) {313 tokenStream.consume();314 }315 // This part is defined in https://www.w3.org/TR/css3-selectors/#attrnmsp:316 // Attribute selectors and namespaces. It is similar to parseATypeSelector.317 let namespacePrefix = null;318 if (isDelim(tokenStream.current(), '|')) {319 namespacePrefix = '';320 tokenStream.consume();321 } else if (isDelim(tokenStream.current(), '*') &&322 isDelim(tokenStream.next(), '|')) {323 namespacePrefix = '*';324 tokenStream.consume();325 tokenStream.consume();326 } else if (tokenStream.current() instanceof parse_css.IdentToken &&327 isDelim(tokenStream.next(), '|')) {328 const ident = goog.asserts.assertInstanceof(329 tokenStream.current(), parse_css.IdentToken);330 namespacePrefix = ident.value;331 tokenStream.consume();332 tokenStream.consume();333 }334 // Now parse the attribute name. This part is mandatory.335 if (!(tokenStream.current() instanceof parse_css.IdentToken)) {336 return newInvalidAttrSelectorError(start);337 }338 const ident = goog.asserts.assertInstanceof(339 tokenStream.current(), parse_css.IdentToken);340 const attrName = ident.value;341 tokenStream.consume();342 if (tokenStream.current() instanceof parse_css.WhitespaceToken) {343 tokenStream.consume();344 }345 // After the attribute name, we may see an operator; if we do, then346 // we must see either a string or an identifier. This covers347 // 6.3.1 Attribute presence and value selectors348 // (https://www.w3.org/TR/css3-selectors/#attribute-representation) and349 // 6.3.2 Substring matching attribute selectors350 // (https://www.w3.org/TR/css3-selectors/#attribute-substrings).351 /** @type {string} */352 let matchOperator = '';353 if (isDelim(tokenStream.current(), '=')) {354 matchOperator = '=';355 tokenStream.consume();356 } else if (tokenStream.current() instanceof parse_css.IncludeMatchToken) {357 matchOperator = '~=';358 tokenStream.consume();359 } else if (tokenStream.current() instanceof parse_css.DashMatchToken) {360 matchOperator = '|=';361 tokenStream.consume();362 } else if (tokenStream.current() instanceof parse_css.PrefixMatchToken) {363 matchOperator = '^=';364 tokenStream.consume();365 } else if (tokenStream.current() instanceof parse_css.SuffixMatchToken) {366 matchOperator = '$=';367 tokenStream.consume();368 } else if (tokenStream.current() instanceof parse_css.SubstringMatchToken) {369 matchOperator = '*=';370 tokenStream.consume();371 }372 if (tokenStream.current() instanceof parse_css.WhitespaceToken) {373 tokenStream.consume();374 }375 /** @type {string} */376 let value = '';377 if (matchOperator !== '') { // If we saw an operator, parse the value.378 if (tokenStream.current() instanceof parse_css.IdentToken) {379 const ident = goog.asserts.assertInstanceof(380 tokenStream.current(), parse_css.IdentToken);381 value = ident.value;382 tokenStream.consume();383 } else if (tokenStream.current() instanceof parse_css.StringToken) {384 const str = goog.asserts.assertInstanceof(385 tokenStream.current(), parse_css.StringToken);386 value = str.value;387 tokenStream.consume();388 } else {389 return newInvalidAttrSelectorError(start);390 }391 }392 if (tokenStream.current() instanceof parse_css.WhitespaceToken) {393 tokenStream.consume();394 }395 // The attribute selector must in any case terminate with a close square396 // token.397 if (!(tokenStream.current() instanceof parse_css.CloseSquareToken)) {398 return newInvalidAttrSelectorError(start);399 }400 tokenStream.consume();401 const selector = new parse_css.AttrSelector(402 namespacePrefix, attrName, matchOperator, value);403 start.copyStartPositionTo(selector);404 return selector;405};406/**407 * A pseudo selector can match either pseudo classes or pseudo elements.408 * http://www.w3.org/TR/css3-selectors/#pseudo-classes409 * http://www.w3.org/TR/css3-selectors/#pseudo-elements.410 *411 * Typically written as ':visited', ':lang(fr)', and '::first-line'.412 *413 * isClass: Pseudo selectors with a single colon (e.g., ':visited')414 * are pseudo class selectors. Selectors with two colons (e.g.,415 * '::first-line') are pseudo elements.416 *417 * func: If it's a function style pseudo selector, like lang(fr), then func418 * the function tokens. TODO(powdercloud): parse this in more detail.419 */420parse_css.PseudoSelector = class extends parse_css.Selector {421 /**422 * @param {boolean} isClass423 * @param {string} name424 * @param {!Array<!parse_css.Token>} func425 */426 constructor(isClass, name, func) {427 super();428 /** @type {boolean} */429 this.isClass = isClass;430 /** @type {string} */431 this.name = name;432 /** @type {!Array<!parse_css.Token>} */433 this.func = func;434 /** @type {parse_css.TokenType} */435 this.tokenType = parse_css.TokenType.PSEUDO_SELECTOR;436 }437 /** @inheritDoc */438 toJSON() {439 const json = super.toJSON();440 json['isClass'] = this.isClass;441 json['name'] = this.name;442 if (this.func.length !== 0) {443 json['func'] = recursiveArrayToJSON(this.func);444 }445 return json;446 }447 /** @inheritDoc */448 accept(visitor) {449 visitor.visitPseudoSelector(this);450 }451};452/**453 * tokenStream.current() must be the ColonToken. Returns an error if454 * the pseudo token can't be parsed (e.g., a lone ':').455 * @param {!parse_css.TokenStream} tokenStream456 * @return {!parse_css.PseudoSelector|!parse_css.ErrorToken}457 */458parse_css.parseAPseudoSelector = function(tokenStream) {459 goog.asserts.assert(tokenStream.current() instanceof parse_css.ColonToken,460 'Precondition violated: must be a ":"');461 const firstColon = tokenStream.current();462 tokenStream.consume();463 let isClass = true;464 if (tokenStream.current() instanceof parse_css.ColonToken) {465 // '::' starts a pseudo element, ':' starts a pseudo class.466 isClass = false;467 tokenStream.consume();468 }469 let name = '';470 /** @type {!Array<!parse_css.Token>} */471 let func = [];472 if (tokenStream.current() instanceof parse_css.IdentToken) {473 const ident = goog.asserts.assertInstanceof(474 tokenStream.current(), parse_css.IdentToken);475 name = ident.value;476 tokenStream.consume();477 } else if (tokenStream.current() instanceof parse_css.FunctionToken) {478 const funcToken = goog.asserts.assertInstanceof(479 tokenStream.current(), parse_css.FunctionToken);480 name = funcToken.value;481 func = parse_css.extractAFunction(tokenStream);482 tokenStream.consume();483 } else {484 const error = new parse_css.ErrorToken(485 amp.validator.ValidationError.Code.CSS_SYNTAX_ERROR_IN_PSEUDO_SELECTOR,486 ['style']);487 firstColon.copyStartPositionTo(error);488 return error;489 }490 const selector = new parse_css.PseudoSelector(491 isClass, name, func);492 firstColon.copyStartPositionTo(selector);493 return selector;494};495/**496 * A class selector of the form '.value' is a shorthand notation for497 * an attribute match of the form '[class~=value]'.498 * http://www.w3.org/TR/css3-selectors/#class-html499 */500parse_css.ClassSelector = class extends parse_css.Selector {501 /**502 * @param {string} value the class to match.503 */504 constructor(value) {505 super();506 /** @type {string} */507 this.value = value;508 /** @type {parse_css.TokenType} */509 this.tokenType = parse_css.TokenType.CLASS_SELECTOR;510 }511 /** @return {string} */512 toString() { return '.' + this.value; }513 /** @inheritDoc */514 toJSON() {515 const json = super.toJSON();516 json['value'] = this.value;517 return json;518 }519 /** @inheritDoc */520 accept(visitor) {521 visitor.visitClassSelector(this);522 }523};524/**525 * tokenStream.current() must be the '.' delimiter token.526 * @param {!parse_css.TokenStream} tokenStream527 * @return {!parse_css.ClassSelector}528 */529parse_css.parseAClassSelector = function(tokenStream) {530 goog.asserts.assert(531 isDelim(tokenStream.current(), '.') &&532 tokenStream.next() instanceof parse_css.IdentToken,533 'Precondition violated: must start with "." and follow with ident');534 const dot = tokenStream.current();535 tokenStream.consume();536 const ident = goog.asserts.assertInstanceof(537 tokenStream.current(), parse_css.IdentToken);538 tokenStream.consume();539 const selector = new parse_css.ClassSelector(ident.value);540 selector.line = dot.line;541 selector.col = dot.col;542 return selector;543};544/**545 * @param {!Array<!Object>} array546 * @return {!Array<string>}547 */548function recursiveArrayToJSON(array) {549 const json = [];550 for (const entry of array) {551 json.push(entry.toJSON());552 }553 return json;554}555/**556 * Models a simple selector sequence, e.g. '*|foo#id'.557 */558parse_css.SimpleSelectorSequence = class extends parse_css.Selector {559 /**560 * @param {!parse_css.TypeSelector} typeSelector561 * @param {!Array<!parse_css.Selector>} otherSelectors562 */563 constructor(typeSelector, otherSelectors) {564 super();565 /** @type {!parse_css.TypeSelector} */566 this.typeSelector = typeSelector;567 /** @type {!Array<!parse_css.Selector>} */568 this.otherSelectors = otherSelectors;569 /** @type {parse_css.TokenType} */570 this.tokenType = parse_css.TokenType.SIMPLE_SELECTOR_SEQUENCE;571 }572 /** @inheritDoc */573 toJSON() {574 const json = super.toJSON();575 json['typeSelector'] = this.typeSelector.toJSON();576 json['otherSelectors'] = recursiveArrayToJSON(this.otherSelectors);577 return json;578 }579 /** @inheritDoc */580 forEachChild(lambda) {581 lambda(this.typeSelector);582 for (const other of this.otherSelectors) {583 lambda(other);584 }585 }586 /** @inheritDoc */587 accept(visitor) {588 visitor.visitSimpleSelectorSequence(this);589 }590};591/**592 * tokenStream.current must be the first token of the sequence.593 * This function will return an error if no selector is found.594 * @param {!parse_css.TokenStream} tokenStream595 * @return {!parse_css.SimpleSelectorSequence|!parse_css.ErrorToken}596 */597parse_css.parseASimpleSelectorSequence = function(tokenStream) {598 const line = tokenStream.current().line;599 const col = tokenStream.current().col;600 let typeSelector = null;601 if (isDelim(tokenStream.current(), '*') ||602 isDelim(tokenStream.current(), '|') ||603 tokenStream.current() instanceof parse_css.IdentToken) {604 typeSelector = parse_css.parseATypeSelector(tokenStream);605 }606 /** @type {!Array<!parse_css.Selector>} */607 const otherSelectors = [];608 while (true) {609 if (tokenStream.current() instanceof parse_css.HashToken) {610 otherSelectors.push(parse_css.parseAnIdSelector(tokenStream));611 } else if (isDelim(tokenStream.current(), '.') &&612 tokenStream.next() instanceof parse_css.IdentToken) {613 otherSelectors.push(parse_css.parseAClassSelector(tokenStream));614 } else if (tokenStream.current() instanceof parse_css.OpenSquareToken) {615 const maybeAttrSelector = parse_css.parseAnAttrSelector(tokenStream);616 if (maybeAttrSelector instanceof parse_css.ErrorToken) {617 return maybeAttrSelector;618 }619 otherSelectors.push(maybeAttrSelector);620 } else if (tokenStream.current() instanceof parse_css.ColonToken) {621 const maybePseudo = parse_css.parseAPseudoSelector(tokenStream);622 if (maybePseudo instanceof parse_css.ErrorToken) {623 return maybePseudo;624 }625 otherSelectors.push(maybePseudo);626 // NOTE: If adding more 'else if' clauses here, be sure to udpate627 // isSimpleSelectorSequenceStart accordingly.628 } else {629 if (typeSelector === null) {630 if (otherSelectors.length == 0) {631 const error = new parse_css.ErrorToken(632 amp.validator.ValidationError.Code.CSS_SYNTAX_MISSING_SELECTOR,633 ['style']);634 error.line = tokenStream.current().line;635 error.col = tokenStream.current().col;636 return error;637 }638 // If no type selector is given then the universal selector is implied.639 typeSelector = new parse_css.TypeSelector(640 /*namespacePrefix=*/null, /*elementName=*/'*');641 typeSelector.line = line;642 typeSelector.col = col;643 }644 const sequence = new parse_css.SimpleSelectorSequence(645 typeSelector, otherSelectors);646 sequence.line = line;647 sequence.col = col;648 return sequence;649 }650 }651};652/**653 * @enum {string}654 */655parse_css.CombinatorType = {656 'DESCENDANT': 'DESCENDANT',657 'CHILD': 'CHILD',658 'ADJACENT_SIBLING': 'ADJACENT_SIBLING',659 'GENERAL_SIBLING': 'GENERAL_SIBLING'660};661/**662 * Models a combinator, as described in663 * http://www.w3.org/TR/css3-selectors/#combinators.664 */665parse_css.Combinator = class extends parse_css.Selector {666 /**667 * @param {!parse_css.CombinatorType} combinatorType668 * @param {!parse_css.SimpleSelectorSequence|!parse_css.Combinator} left669 * @param {!parse_css.SimpleSelectorSequence} right670 */671 constructor(combinatorType, left, right) {672 super();673 /** @type {!parse_css.CombinatorType} */674 this.combinatorType = combinatorType;675 /** @type {!parse_css.SimpleSelectorSequence|!parse_css.Combinator} */676 this.left = left;677 /** @type {!parse_css.SimpleSelectorSequence} */678 this.right = right;679 /** @type {parse_css.TokenType} */680 this.tokenType = parse_css.TokenType.COMBINATOR;681 }682 /** @inheritDoc */683 toJSON() {684 const json = super.toJSON();685 json['combinatorType'] = this.combinatorType;686 json['left'] = this.left.toJSON();687 json['right'] = this.right.toJSON();688 return json;689 }690 /** @inheritDoc */691 forEachChild(lambda) {692 lambda(this.left);693 lambda(this.right);694 }695 /** @inheritDoc */696 accept(visitor) {697 visitor.visitCombinator(this);698 }699};700/**701 * The CombinatorType for a given token; helper function used when702 * constructing a Combinator instance.703 * @param {!parse_css.Token} token704 * @return {!parse_css.CombinatorType}705 */706function combinatorTypeForToken(token) {707 if (token instanceof parse_css.WhitespaceToken) {708 return parse_css.CombinatorType.DESCENDANT;709 } else if (isDelim(token, '>')) {710 return parse_css.CombinatorType.CHILD;711 } else if (isDelim(token, '+')) {712 return parse_css.CombinatorType.ADJACENT_SIBLING;713 } else if (isDelim(token, '~')) {714 return parse_css.CombinatorType.GENERAL_SIBLING;715 }716 goog.asserts.fail('Internal Error: not a combinator token');717}718/**719 * Whether or not the provided token could be the start of a simple720 * selector sequence. See the simple_selector_sequence production in721 * http://www.w3.org/TR/css3-selectors/#grammar.722 * @param {!parse_css.Token} token723 * @return {boolean}724 */725function isSimpleSelectorSequenceStart(token) {726 // Type selector start.727 if (isDelim(token, '*') || isDelim(token, '|') ||728 (token instanceof parse_css.IdentToken)) {729 return true;730 }731 // Id selector start.732 if (token instanceof parse_css.HashToken) {733 return true;734 }735 // Class selector start.736 if (isDelim(token, '.')) {737 return true;738 }739 // Attr selector start.740 if (token instanceof parse_css.OpenSquareToken) {741 return true;742 }743 // A pseudo selector.744 if (token instanceof parse_css.ColonToken) {745 return true;746 }747 // TODO(johannes): add the others.748 return false;749}750/**751 * The selector production from752 * http://www.w3.org/TR/css3-selectors/#grammar753 * Returns an ErrorToken if no selector is found.754 * @param {!parse_css.TokenStream} tokenStream755 * @return {!parse_css.SimpleSelectorSequence|756 * !parse_css.Combinator|!parse_css.ErrorToken}757 */758parse_css.parseASelector = function(tokenStream) {759 if (!isSimpleSelectorSequenceStart(tokenStream.current())) {760 const error = new parse_css.ErrorToken(761 amp.validator.ValidationError.Code.CSS_SYNTAX_NOT_A_SELECTOR_START,762 ['style']);763 error.line = tokenStream.current().line;764 error.col = tokenStream.current().col;765 return error;766 }767 let left = parse_css.parseASimpleSelectorSequence(tokenStream);768 if (left instanceof parse_css.ErrorToken) {769 return left;770 }771 while (true) {772 // Consume whitespace in front of combinators, while being careful773 // to not eat away the infamous "whitespace operator" (sigh, haha).774 if ((tokenStream.current() instanceof parse_css.WhitespaceToken) &&775 !isSimpleSelectorSequenceStart(tokenStream.next())) {776 tokenStream.consume();777 }778 // If present, grab the combinator token which we'll use for line779 // / column info.780 if (!(((tokenStream.current() instanceof parse_css.WhitespaceToken) &&781 isSimpleSelectorSequenceStart(tokenStream.next())) ||782 isDelim(tokenStream.current(), '+') ||783 isDelim(tokenStream.current(), '>') ||784 isDelim(tokenStream.current(), '~'))) {785 return left;786 }787 const combinatorToken = tokenStream.current();788 tokenStream.consume();789 if (tokenStream.current() instanceof parse_css.WhitespaceToken) {790 tokenStream.consume();791 }792 const right = parse_css.parseASimpleSelectorSequence(tokenStream);793 if (right instanceof parse_css.ErrorToken) {794 return right; // TODO(johannes): more than one error / partial tree.795 }796 left = new parse_css.Combinator(797 combinatorTypeForToken(combinatorToken), left, right);798 left.line = combinatorToken.line;799 left.col = combinatorToken.col;800 }801};802/**803 * Models a selectors group, as described in804 * http://www.w3.org/TR/css3-selectors/#grouping.805 */806parse_css.SelectorsGroup = class extends parse_css.Selector {807 /**808 * @param {!Array<!parse_css.SimpleSelectorSequence|809 * !parse_css.Combinator>} elements810 */811 constructor(elements) {812 super();813 /** @type {!Array<!parse_css.SimpleSelectorSequence|814 !parse_css.Combinator>} */815 this.elements = elements;816 /** @type {parse_css.TokenType} */817 this.tokenType = parse_css.TokenType.SELECTORS_GROUP;818 }819 /** @inheritDoc */820 toJSON() {821 const json = super.toJSON();822 json['elements'] = recursiveArrayToJSON(this.elements);823 return json;824 }825 /** @inheritDoc */826 forEachChild(lambda) {827 for (const child of this.elements) {828 lambda(child);829 }830 }831 /** @param {!parse_css.SelectorVisitor} visitor */832 accept(visitor) {833 visitor.visitSelectorsGroup(this);834 }835};836/**837 * The selectors_group production from838 * http://www.w3.org/TR/css3-selectors/#grammar.839 * In addition, this parsing routine checks that no input remains,840 * that is, after parsing the production we reached the end of |token_stream|.841 * @param {!parse_css.TokenStream} tokenStream842 * @return {!parse_css.SelectorsGroup|843 * !parse_css.SimpleSelectorSequence|!parse_css.Combinator|844 * !parse_css.ErrorToken}845 */846parse_css.parseASelectorsGroup = function(tokenStream) {847 if (!isSimpleSelectorSequenceStart(tokenStream.current())) {848 const error = new parse_css.ErrorToken(849 amp.validator.ValidationError.Code.CSS_SYNTAX_NOT_A_SELECTOR_START,850 ['style']);851 tokenStream.current().copyStartPositionTo(error);852 return error;853 }854 const start = tokenStream.current();855 const elements = [parse_css.parseASelector(tokenStream)];856 if (elements[0] instanceof parse_css.ErrorToken) {857 return elements[0];858 }859 while (true) {860 if (tokenStream.current() instanceof parse_css.WhitespaceToken) {861 tokenStream.consume();862 }863 if (tokenStream.current() instanceof parse_css.CommaToken) {864 tokenStream.consume();865 if (tokenStream.current() instanceof parse_css.WhitespaceToken) {866 tokenStream.consume();867 }868 elements.push(parse_css.parseASelector(tokenStream));869 if (elements[elements.length - 1] instanceof parse_css.ErrorToken) {870 return elements[elements.length - 1];871 }872 continue;873 }874 // We're about to claim success and return a selector,875 // but before we do, we check that no unparsed input remains.876 if (!(tokenStream.current() instanceof parse_css.EOFToken)) {877 const error = new parse_css.ErrorToken(878 amp.validator.ValidationError.Code879 .CSS_SYNTAX_UNPARSED_INPUT_REMAINS_IN_SELECTOR,880 ['style']);881 tokenStream.current().copyStartPositionTo(error);882 return error;883 }884 if (elements.length == 1) {885 return elements[0];886 }887 const group = new parse_css.SelectorsGroup(elements);888 start.copyStartPositionTo(group);889 return group;890 }...

Full Screen

Full Screen

DestructuringTransformer.js

Source:DestructuringTransformer.js Github

copy

Full Screen

1// Copyright 2012 Traceur Authors.2//3// Licensed under the Apache License, Version 2.0 (the 'License');4// you may not use this file except in compliance with the License.5// You may obtain a copy of the License at6//7// http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// distributed under the License is distributed on an 'AS IS' BASIS,11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12// See the License for the specific language governing permissions and13// limitations under the License.14import { ARRAY, CALL, PROTOTYPE, SLICE } from "../syntax/PredefinedName.js";15import {16 ARRAY_LITERAL_EXPRESSION,17 ARRAY_PATTERN,18 BINDING_ELEMENT,19 BLOCK,20 CALL_EXPRESSION,21 IDENTIFIER_EXPRESSION,22 LITERAL_EXPRESSION,23 MEMBER_EXPRESSION,24 MEMBER_LOOKUP_EXPRESSION,25 OBJECT_LITERAL_EXPRESSION,26 OBJECT_PATTERN,27 OBJECT_PATTERN_FIELD,28 PAREN_EXPRESSION,29 VARIABLE_DECLARATION_LIST,30} from "../syntax/trees/ParseTreeType.js";31import {32 BindingElement,33 Catch,34 ForInStatement,35 ForOfStatement,36 FunctionDeclaration,37 FunctionExpression,38 LiteralExpression,39 SetAccessor,40} from "../syntax/trees/ParseTrees.js";41import { TempVarTransformer } from "./TempVarTransformer.js";42import { EQUAL, IDENTIFIER, IN, LET, VAR } from "../syntax/TokenType.js";43import {44 createArgumentList,45 createAssignmentExpression,46 createBinaryOperator,47 createBindingIdentifier,48 createBlock,49 createCallExpression,50 createCommaExpression,51 createConditionalExpression,52 createExpressionStatement,53 createIdentifierExpression,54 createMemberExpression,55 createMemberLookupExpression,56 createNumberLiteral,57 createOperatorToken,58 createParenExpression,59 createStringLiteral,60 createVariableDeclaration,61 createVariableDeclarationList,62 createVariableStatement,63} from "./ParseTreeFactory.js";64import { options } from "../options.js";65import { prependStatements } from "./PrependStatements.js";66var stack = [];67/**68 * Collects assignments in the desugaring of a pattern.69 */70class Desugaring {71 /**72 * @param {ParseTree} rvalue73 */74 constructor(rvalue) {75 this.rvalue = rvalue;76 }77}78/**79 * Collects assignments as assignment expressions. This is the80 * desugaring for assignment expressions.81 */82class AssignmentExpressionDesugaring extends Desugaring {83 /**84 * @param {ParseTree} rvalue85 */86 constructor(rvalue) {87 super(rvalue);88 this.expressions = [];89 }90 assign(lvalue, rvalue) {91 this.expressions.push(createAssignmentExpression(lvalue, rvalue));92 }93}94/**95 * Collects assignments as variable declarations. This is the96 * desugaring for 'var', 'const' declarations.97 */98class VariableDeclarationDesugaring extends Desugaring {99 /**100 * @param {ParseTree} rvalue101 */102 constructor(rvalue) {103 super(rvalue);104 this.declarations = [];105 }106 assign(lvalue, rvalue) {107 if (lvalue.type === BINDING_ELEMENT) {108 this.declarations.push(createVariableDeclaration(lvalue.binding, rvalue));109 return;110 }111 if (lvalue.type == IDENTIFIER_EXPRESSION)112 lvalue = createBindingIdentifier(lvalue);113 this.declarations.push(createVariableDeclaration(lvalue, rvalue));114 }115}116/**117 * Creates something like "ident" in rvalue ? rvalue.ident : initializer118 */119function createConditionalMemberExpression(rvalue, identToken, initializer) {120 if (identToken.type !== IDENTIFIER) {121 return createConditionalMemberLookupExpression(122 rvalue,123 new LiteralExpression(null, identToken),124 initializer125 );126 }127 if (!initializer) return createMemberExpression(rvalue, identToken);128 return createConditionalExpression(129 createBinaryOperator(130 createStringLiteral(identToken.value),131 createOperatorToken(IN),132 rvalue133 ),134 createMemberExpression(rvalue, identToken),135 initializer136 );137}138/**139 * Creates something like [index] in rvalue ? rvalue[index] : initializer140 */141function createConditionalMemberLookupExpression(rvalue, index, initializer) {142 if (!initializer) return createMemberLookupExpression(rvalue, index);143 return createConditionalExpression(144 createBinaryOperator(index, createOperatorToken(IN), rvalue),145 createMemberLookupExpression(rvalue, index),146 initializer147 );148}149/**150 * Desugars destructuring assignment.151 *152 * @see <a href="http://wiki.ecmascript.org/doku.php?id=harmony:destructuring#assignments">harmony:destructuring</a>153 */154export class DestructuringTransformer extends TempVarTransformer {155 /**156 * @param {ArrayPattern} tree157 * @return {ParseTree}158 */159 transformArrayPattern(tree) {160 // Patterns should be desugared by their parent nodes.161 throw new Error("unreachable");162 }163 /**164 * @param {ObjectPattern} tree165 * @return {ParseTree}166 */167 transformObjectPattern(tree) {168 // Patterns should be desugard by their parent nodes.169 throw new Error("unreachable");170 }171 /**172 * Transforms:173 * [a, [b, c]] = x174 * From an assignment expression into:175 * (function (rvalue) {176 * a = rvalue[0];177 * [b, c] = rvalue[1];178 * }).call(this, x);179 *180 * Nested patterns are desugared by recursive calls to transform.181 *182 * @param {BinaryOperator} tree183 * @return {ParseTree}184 */185 transformBinaryOperator(tree) {186 if (tree.operator.type == EQUAL && tree.left.isPattern()) {187 return this.transformAny(this.desugarAssignment_(tree.left, tree.right));188 } else {189 return super.transformBinaryOperator(tree);190 }191 }192 /**193 * @param {ParseTree} lvalue194 * @param {ParseTree} rvalue195 * @return {ParseTree}196 */197 desugarAssignment_(lvalue, rvalue) {198 var tempIdent = createIdentifierExpression(this.addTempVar());199 var desugaring = new AssignmentExpressionDesugaring(tempIdent);200 this.desugarPattern_(desugaring, lvalue);201 desugaring.expressions.unshift(202 createAssignmentExpression(tempIdent, rvalue)203 );204 desugaring.expressions.push(tempIdent);205 return createParenExpression(createCommaExpression(desugaring.expressions));206 }207 /**208 * Transforms:209 * [a, [b, c]] = x210 * From a variable declaration list into:211 * tmp = x, a = x[0], [b, c] = x[1]212 *213 * We do it this way (as opposed to a block with a declaration and214 * initialization statements) so that we can translate const215 * declarations, which must be initialized at declaration.216 *217 * Nested patterns are desugared by recursive calls to transform.218 *219 * @param {VariableDeclarationList} tree220 * @return {ParseTree}221 */222 transformVariableDeclarationList(tree) {223 if (!this.destructuringInDeclaration_(tree)) {224 // No lvalues to desugar.225 return super.transformVariableDeclarationList(tree);226 }227 this.pushTempVarState();228 // Desugar one level of patterns.229 var desugaredDeclarations = [];230 tree.declarations.forEach((declaration) => {231 if (declaration.lvalue.isPattern()) {232 desugaredDeclarations.push(233 ...this.desugarVariableDeclaration_(declaration)234 );235 } else {236 desugaredDeclarations.push(declaration);237 }238 });239 // Desugar more.240 var transformedTree = this.transformVariableDeclarationList(241 createVariableDeclarationList(tree.declarationType, desugaredDeclarations)242 );243 this.popTempVarState();244 return transformedTree;245 }246 transformForInStatement(tree) {247 return this.transformForInOrOf_(248 tree,249 super.transformForInStatement,250 ForInStatement251 );252 }253 transformForOfStatement(tree) {254 return this.transformForInOrOf_(255 tree,256 super.transformForOfStatement,257 ForOfStatement258 );259 }260 /**261 * Transforms for-in and for-of loops.262 * @param {ForInStatement|ForOfStatement} tree The for-in or for-of loop.263 * @param {Function} superMethod The super method to call if no pattern is264 * present.265 * @param {Function} constr The constructor used to create the transformed266 * tree.267 * @return {ForInStatement|ForOfStatement} The transformed tree.268 * @private269 */270 transformForInOrOf_(tree, superMethod, constr) {271 if (272 !tree.initializer.isPattern() &&273 (tree.initializer.type !== VARIABLE_DECLARATION_LIST ||274 !this.destructuringInDeclaration_(tree.initializer))275 ) {276 return superMethod.call(this, tree);277 }278 this.pushTempVarState();279 var declarationType, lvalue;280 if (tree.initializer.isPattern()) {281 declarationType = null;282 lvalue = tree.initializer;283 } else {284 declarationType = tree.initializer.declarationType;285 lvalue = tree.initializer.declarations[0].lvalue;286 }287 // for (var pattern in coll) {288 //289 // =>290 //291 // for (var $tmp in coll) {292 // var pattern = $tmp;293 //294 // And when the initializer is an assignment expression.295 //296 // for (pattern in coll) {297 //298 // =>299 //300 // for (var $tmp in coll) {301 // pattern = $tmp;302 var statements = [];303 var binding = this.desugarBinding_(lvalue, statements, declarationType);304 var initializer = createVariableDeclarationList(VAR, binding, null);305 var collection = this.transformAny(tree.collection);306 var body = this.transformAny(tree.body);307 if (body.type !== BLOCK) body = createBlock(body);308 statements.push(...body.statements);309 body = createBlock(statements);310 this.popTempVarState();311 return new constr(tree.location, initializer, collection, body);312 }313 transformFunctionDeclaration(tree) {314 return this.transformFunction_(tree, FunctionDeclaration);315 }316 transformFunctionExpression(tree) {317 return this.transformFunction_(tree, FunctionExpression);318 }319 transformFunction_(tree, constructor) {320 stack.push([]);321 var transformedTree =322 constructor === FunctionExpression323 ? super.transformFunctionExpression(tree)324 : super.transformFunctionDeclaration(tree);325 var statements = stack.pop();326 if (!statements.length) return transformedTree;327 // Prepend the var statements to the block.328 statements = prependStatements(329 transformedTree.functionBody.statements,330 ...statements331 );332 return new constructor(333 transformedTree.location,334 transformedTree.name,335 transformedTree.isGenerator,336 transformedTree.formalParameterList,337 createBlock(statements)338 );339 }340 transformSetAccessor(tree) {341 stack.push([]);342 var transformedTree = super.transformSetAccessor(tree);343 var statements = stack.pop();344 if (!statements.length) return transformedTree;345 // Prepend the var statements to the block.346 statements.push(...transformedTree.body.statements);347 return new SetAccessor(348 transformedTree.location,349 transformedTree.isStatic,350 transformedTree.name,351 transformedTree.parameter,352 createBlock(statements)353 );354 }355 transformBindingElement(tree) {356 // If this has an initializer the default parameter transformer moves the357 // pattern into the function body and it will be taken care of by the358 // variable pass.359 if (!tree.binding.isPattern() || tree.initializer) return tree;360 // function f(pattern) { }361 //362 // =>363 //364 // function f($tmp) {365 // var pattern = $tmp;366 // }367 var statements = stack[stack.length - 1];368 var binding = this.desugarBinding_(tree.binding, statements, VAR);369 return new BindingElement(null, binding, null);370 }371 transformCatch(tree) {372 if (!tree.binding.isPattern()) return super.transformCatch(tree);373 // catch(pattern) {374 //375 // =>376 //377 // catch ($tmp) {378 // let pattern = $tmp379 var body = this.transformAny(tree.catchBody);380 var statements = [];381 var kind = options.blockBinding ? LET : VAR;382 var binding = this.desugarBinding_(tree.binding, statements, kind);383 statements.push(...body.statements);384 return new Catch(tree.location, binding, createBlock(statements));385 }386 /**387 * Helper for transformations that transforms a binding to a temp binding388 * as well as a statement added into a block. For example, this is used by389 * function, for-in/of and catch.390 * @param {ParseTree} bindingTree The tree with the binding pattern.391 * @param {Array} statements Array that we add the assignment/variable392 * declaration to.393 * @param {TokenType?} declarationType The kind of variable declaration to394 * generate or null if an assignment expression is to be used.395 * @return {BindingIdentifier} The binding tree.396 */397 desugarBinding_(bindingTree, statements, declarationType) {398 var varName = this.getTempIdentifier();399 var binding = createBindingIdentifier(varName);400 var idExpr = createIdentifierExpression(varName);401 var desugaring;402 if (declarationType === null)403 desugaring = new AssignmentExpressionDesugaring(idExpr);404 else desugaring = new VariableDeclarationDesugaring(idExpr);405 this.desugarPattern_(desugaring, bindingTree);406 if (declarationType === null) {407 statements.push(408 createExpressionStatement(createCommaExpression(desugaring.expressions))409 );410 } else {411 statements.push(412 createVariableStatement(413 // Desugar more.414 this.transformVariableDeclarationList(415 createVariableDeclarationList(416 declarationType,417 desugaring.declarations418 )419 )420 )421 );422 }423 return binding;424 }425 /**426 * @param {VariableDeclarationList} tree427 * @return {boolean}428 */429 destructuringInDeclaration_(tree) {430 return tree.declarations.some((declaration) =>431 declaration.lvalue.isPattern()432 );433 }434 /**435 * @param {VariableDeclaration} tree436 * @return {Array.<VariableDeclaration>}437 */438 desugarVariableDeclaration_(tree) {439 var tempRValueName = this.getTempIdentifier();440 var tempRValueIdent = createIdentifierExpression(tempRValueName);441 var desugaring;442 var initializer;443 // Don't use parens for these cases:444 // - tree.initializer is assigned to a temporary.445 // - tree.initializer normally doesn't need parens for member access.446 // Don't use temporary if:447 // - there is only one value to assign (and no initializer).448 switch (tree.initializer.type) {449 // Paren not necessary.450 case ARRAY_LITERAL_EXPRESSION:451 case CALL_EXPRESSION:452 case IDENTIFIER_EXPRESSION:453 case LITERAL_EXPRESSION:454 case MEMBER_EXPRESSION:455 case MEMBER_LOOKUP_EXPRESSION:456 case OBJECT_LITERAL_EXPRESSION:457 case PAREN_EXPRESSION:458 initializer = tree.initializer;459 // Paren necessary for single value case.460 default:461 // [1] Try first using a temporary (used later as the base rvalue).462 desugaring = new VariableDeclarationDesugaring(tempRValueIdent);463 desugaring.assign(desugaring.rvalue, tree.initializer);464 var initializerFound = this.desugarPattern_(desugaring, tree.lvalue);465 // [2] Was the temporary necessary? Then return.466 if (initializerFound || desugaring.declarations.length > 2)467 return desugaring.declarations;468 initializer = initializer || createParenExpression(tree.initializer);469 // [3] Redo everything without the temporary.470 desugaring = new VariableDeclarationDesugaring(initializer);471 this.desugarPattern_(desugaring, tree.lvalue);472 return desugaring.declarations;473 }474 }475 /**476 * @param {Desugaring} desugaring477 * @param {ParseTree} tree478 * @return {boolean} True if any of the patterns have an initializer.479 */480 desugarPattern_(desugaring, tree) {481 var initializerFound = false;482 switch (tree.type) {483 case ARRAY_PATTERN: {484 var pattern = tree;485 for (var i = 0; i < pattern.elements.length; i++) {486 var lvalue = pattern.elements[i];487 if (lvalue === null) {488 // A skip, for example [a,,c]489 continue;490 } else if (lvalue.isSpreadPatternElement()) {491 // Rest of the array, for example [x, ...y] = [1, 2, 3]492 desugaring.assign(493 lvalue.lvalue,494 createCallExpression(495 createMemberExpression(ARRAY, PROTOTYPE, SLICE, CALL),496 createArgumentList(desugaring.rvalue, createNumberLiteral(i))497 )498 );499 } else {500 if (lvalue.initializer) initializerFound = true;501 desugaring.assign(502 lvalue,503 createConditionalMemberLookupExpression(504 desugaring.rvalue,505 createNumberLiteral(i),506 lvalue.initializer507 )508 );509 }510 }511 break;512 }513 case OBJECT_PATTERN: {514 var pattern = tree;515 pattern.fields.forEach((field) => {516 var lookup;517 switch (field.type) {518 case BINDING_ELEMENT:519 if (field.initializer) initializerFound = true;520 lookup = createConditionalMemberExpression(521 desugaring.rvalue,522 field.binding.identifierToken,523 field.initializer524 );525 desugaring.assign(526 createIdentifierExpression(field.binding),527 lookup528 );529 break;530 case OBJECT_PATTERN_FIELD:531 if (field.element.initializer) initializerFound = true;532 lookup = createConditionalMemberExpression(533 desugaring.rvalue,534 field.identifier,535 field.element.initializer536 );537 desugaring.assign(field.element, lookup);538 break;539 case IDENTIFIER_EXPRESSION:540 lookup = createMemberExpression(541 desugaring.rvalue,542 field.identifierToken543 );544 desugaring.assign(field, lookup);545 break;546 default:547 throw Error("unreachable");548 }549 });550 break;551 }552 case PAREN_EXPRESSION:553 return this.desugarPattern_(desugaring, tree.expression);554 default:555 throw new Error("unreachable");556 }557 return initializerFound;558 }559 /**560 * @param {UniqueIdentifierGenerator} identifierGenerator561 * @param {ParseTree} tree562 * @return {ParseTree}563 */564 static transformTree(identifierGenerator, tree) {565 return new DestructuringTransformer(identifierGenerator).transformAny(tree);566 }...

Full Screen

Full Screen

parse.js

Source:parse.js Github

copy

Full Screen

1import {2 cat,3 oneOf,4 opt,5 match,6 map,7 many,8 not,9 EOFError,10} from './comb';11import {12 NullToken,13 BoolToken,14 NumberToken,15 StringToken,16 IdentToken,17 InfixToken,18 BracketsToken,19 BracesToken,20 ParensToken,21 DelimToken,22 WhitespaceToken,23 SpaceToken,24 BreakToken,25 IndentToken,26} from './lex';27import { OP_PREC } from './shared';28class TokenCursor {29 constructor (tokenStream, ctx) {30 this.tokens = tokenStream;31 this.pos = [0];32 this.proxy = null;33 this.errors = [];34 this.ctx = ctx;35 this.prevTok = null;36 }37 peek () {38 if (this.eof()) throw new EOFError(`unexpected stream end`);39 let t = { contents: this.tokens };40 for (const p of this.pos) {41 t = t.contents[p];42 }43 return t;44 }45 span () {46 if (this.eof()) return this.prevTok.span;47 return this.peek().span;48 }49 next () {50 const t = this.peek();51 this.pos[this.pos.length - 1]++;52 this.errors = [];53 this.prevTok = t;54 return t;55 }56 enter () {57 const t = this.peek();58 if (!Array.isArray(t.contents)) this.throw(`cannot enter token without contents`);59 this.pos.push(0);60 }61 exitAssertingEnd () {62 if (!this.eof()) this.throw(`attempt to exit token without reading all contents`);63 this.pos.pop();64 }65 eof () {66 const pos = [...this.pos];67 const lastPos = pos.pop();68 let t = { contents: this.tokens };69 for (const p of pos) {70 t = t.contents[p];71 }72 return t.contents.length === lastPos;73 }74 topLevelEof () {75 return this.pos.length === 1 && this.eof();76 }77 clone () {78 const tc = new TokenCursor(this.tokens, this.ctx);79 tc.pos = [...this.pos];80 tc.errors = this.errors;81 return tc;82 }83 copyFrom (tc) {84 this.tokens = tc.tokens;85 this.ctx = tc.ctx;86 this.pos = [...tc.pos];87 this.errors = tc.errors;88 }89 addErrorToCurrentPos (err) {90 this.errors.push(err);91 }92 throw (err) {93 if (typeof err === 'string') throw new ParseError(err, this.clone());94 else throw err;95 }96 getCurrentError (fallback = 'unknown error') {97 if (this.errors.length) {98 return new ParseError(this.errors, this.clone());99 }100 return new ParseError(fallback, this.clone());101 }102}103class ParseError {104 constructor (msgOrErrs, state = null) {105 this.contents = msgOrErrs;106 this.state = state;107 }108 get nextFewTokens () {109 const s = this.state.clone();110 const tokens = [];111 for (let i = 0; i < 10; i++) {112 if (s.eof()) break;113 tokens.push(s.next());114 }115 return tokens;116 }117 get _debug__stringified () {118 return this.toString();119 }120 toString () {121 if (typeof this.contents === 'string') {122 return this.contents;123 } else {124 return this.contents.map(x => x.toString()).join('\n');125 }126 }127 valueOf () {128 return `[ParseError ${this.toString()}]`;129 }130 getSpan () {131 if (!this.state) return null;132 return this.state.span();133 }134}135const group = (gclass, inner) => tok => {136 const node = tok.peek();137 if (!(node instanceof gclass)) tok.throw(`unexpected ${node}, expected ${gclass.name}`);138 tok.enter();139 const i = inner(tok);140 tok.exitAssertingEnd();141 tok.next();142 return i;143};144const ctxify = (inner) => tok => {145 const res = inner(tok);146 res.ctx = tok.ctx;147 return res;148};149const nbws = many(match(x => x instanceof SpaceToken, 'non-breaking whitespace'));150const anyws = many(match(x => x instanceof WhitespaceToken, 'whitespace'));151const bws = tok => {152 const r = anyws(tok);153 for (const x of r) if (x instanceof BreakToken) return null;154 tok.throw('expected line break');155};156const tnull = ctxify(map(match(x => x instanceof NullToken, 'null'), () => ({ type: 'u' })));157const tnumber = ctxify(map(match(x => x instanceof NumberToken, 'number'), x => ({ type: 'n', value: parseFloat(x.int + '.' + (x.frac || '0'), 10) })));158const tbool = ctxify(map(match(x => x instanceof BoolToken, 'bool'), x => ({ type: 'b', value: x.value })));159const tstring = ctxify(map(match(x => x instanceof StringToken, 'string'), x => ({ type: 's', value: x.contents })));160const primitive = oneOf(tnull, tbool, tnumber, tstring);161const delim = match(x => x instanceof DelimToken, 'delimiter');162const callArgsInner = map(cat(163 many(map(cat(anyws, expr, anyws, delim), x => x[1])),164 anyws,165 opt(expr),166 anyws,167), ([a,, b]) => a.concat(b));168const callArgs = map(cat(nbws, group(ParensToken, callArgsInner)), a => a[1]);169const callExpr = ctxify(map(cat(170 match(x => x instanceof IdentToken && !(x instanceof InfixToken), 'callee identifier'),171 opt(callArgs),172), ([a, c]) => {173 if (c.length) {174 const ex = { type: 'c', func: { type: 'r', name: a.ident }, args: c[0] };175 ex.func.parent = ex;176 for (const arg of c[0]) arg.parent = ex;177 return ex;178 } else {179 return { type: 'r', name: a.ident };180 }181}));182const IS_INFIX = Symbol();183const IS_INFIX_OP = Symbol();184const groupExpr = map(185 group(ParensToken, cat(anyws, expr, anyws)),186 a => {187 const ex = a[1];188 if (!ex) return ex;189 delete ex[IS_INFIX]; // mark this non-infix so fixPrec doesn't mess it up190 return ex;191 },192);193const matrixInner = map(cat(194 many(map(cat(anyws, expr, anyws, delim), a => a[1])),195 opt(map(cat(anyws, expr), a => a[1])),196 anyws,197), ([a, b]) => a.concat(b));198const matrixExpr = ctxify(map(group(BracketsToken, matrixInner), items => {199 const MATRIX_TYPES = 'ubnsm';200 let isPure = true;201 for (const item of items) {202 if (!MATRIX_TYPES.includes(item.type)) {203 isPure = false;204 }205 }206 if (isPure) return { type: 'm', value: items.map(item => item.value) };207 else {208 const ex = { type: 'l', items };209 for (const item of items) item.parent = ex;210 return ex;211 }212}));213const arrow = match(x => x instanceof InfixToken && x.ident === '->', '->');214const fatArrow = match(x => x instanceof InfixToken && x.ident === '=>', '=>');215const equals = match(x => x instanceof InfixToken && x.ident === '=', '=');216const switchIdent = match(x => x instanceof IdentToken && !x.isRaw && x.ident === 'switch', 'switch keyword');217const wildcardSwitchKey = match(x => x instanceof IdentToken && !x.isRaw && x.ident === 'otherwise', 'otherwise');218const notLastSwitchCase = not(wildcardSwitchKey, expr, 'wildcard case');219const undelimSwitchCase = map(220 cat(anyws, notLastSwitchCase, anyws, fatArrow, anyws, expr),221 ([, a,,,, e]) => ({ cond: a, value: e }),222);223const switchCaseDelim = oneOf(bws, cat(nbws, delim, anyws));224const delimSwitchCase = map(cat(undelimSwitchCase, switchCaseDelim), a => a[0]);225const wildcardSwitchCase = map(226 cat(anyws, wildcardSwitchKey, anyws, expr),227 ([,,, e]) => ({ cond: null, value: e }),228);229const lastSwitchCase = oneOf(230 wildcardSwitchCase,231 undelimSwitchCase,232);233const switchCases = map(cat(234 many(delimSwitchCase),235 opt(lastSwitchCase),236 anyws, opt(delim), anyws,237), ([a, b]) => a.concat(b));238const switchContents = oneOf(239 group(IndentToken, switchCases),240 map(cat(anyws, group(BracesToken, switchCases)), a => a[1]),241 map(cat(nbws, lastSwitchCase), ([, c]) => [c]),242);243const switchExpr = ctxify(map(cat(switchIdent, switchContents), ([, m]) => {244 const ex = { type: 'w', matches: m };245 for (const { cond, value } of m) {246 if (cond) cond.parent = ex;247 value.parent = ex;248 }249 return ex;250}));251const closureArg = map(match(x => x instanceof IdentToken && !(x instanceof InfixToken), 'argument name'), x => x.ident);252const closureArgsInner = map(cat(253 many(map(cat(anyws, closureArg, anyws, delim), ([, a]) => a)),254 opt(map(cat(anyws, closureArg), ([, a]) => a)),255 anyws,256), ([a, b]) => a.concat(b));257const closureArgs = oneOf(map(closureArg, arg => [arg]), group(ParensToken, closureArgsInner));258const closureWhereKey = match(x => x instanceof IdentToken && !x.isRaw && x.ident === 'where', 'where keyword');259const closureWhereInner = oneOf(260 group(IndentToken, program),261 map(cat(anyws, group(BracesToken, program)), a => a[1]),262 ctxify(map(cat(nbws, definition), a => {263 const defs = {264 type: 'd',265 defs: new Set([a[1]]),266 floatingExpr: new Set(),267 };268 a[1].parent = defs;269 return defs;270 })),271);272const closureWhere = map(273 opt(map(cat(anyws, closureWhereKey, closureWhereInner), a => a[2])),274 a => a[0],275);276const closureBody = map(cat(expr, closureWhere), ([e, w], tok) => {277 const body = {278 ctx: tok.ctx,279 type: 'd',280 defs: new Set(),281 floatingExpr: new Set(),282 };283 body.defs.add({284 ctx: tok.ctx,285 type: 'ds',286 name: '=',287 expr: e,288 });289 if (w) for (const d of w.defs) body.defs.add(d);290 for (const d of body.defs) d.parent = body;291 return body;292});293const closureExpr = ctxify(map(cat(closureArgs, nbws, arrow, anyws, closureBody), ([p,,,, b]) => ({294 type: 'f',295 params: p,296 body: b,297})));298const minus = match(x => x instanceof InfixToken && x.ident === '-', 'minus sign');299const unaryMinusExpr = ctxify(map(cat(minus, nbws, nonInfixExpr), ([,, e]) => {300 const ex = {301 type: 'c',302 func: { type: 'r', name: '-' },303 args: [{ type: 'n', value: 0 }, e],304 };305 e.parent = ex;306 ex.func.parent = ex;307 return ex;308}));309const _nonInfixExpr = oneOf(310 unaryMinusExpr,311 primitive,312 matrixExpr,313 switchExpr,314 closureExpr,315 groupExpr,316 callExpr,317);318function nonInfixExpr (tok) { // for hoisting319 return _nonInfixExpr(tok);320}321const isInfixOp = x => x instanceof InfixToken && x.ident !== '=' && x.ident !== '->' && x.ident !== '=>';322const mkInfix = a => {323 Object.defineProperty(a, IS_INFIX, {324 value: true,325 enumerable: false,326 configurable: true,327 });328 return a;329};330const mkInfixOp = a => {331 Object.defineProperty(a, IS_INFIX_OP, {332 value: true,333 enumerable: false,334 configurable: true,335 });336 return a;337};338const infixExpr = ctxify(map(339 cat(nonInfixExpr, anyws, match(isInfixOp, 'infix operator'), anyws, expr),340 ([a,, o,, b]) => {341 const iex = mkInfix({342 type: 'c',343 func: mkInfixOp({ type: 'r', name: o.ident }),344 args: [a, b],345 [IS_INFIX]: true,346 });347 iex.func.parent = iex;348 a.parent = iex;349 b.parent = iex;350 return iex;351 },352));353const KNOWN_PREC_OPS = OP_PREC.flatMap(x => x);354function fixPrec (infixExpr) {355 return tok => {356 const expr = infixExpr(tok);357 const parts = [];358 const additionalOps = [];359 const flatten = e => {360 if (e[IS_INFIX]) {361 flatten(e.args[0]);362 parts.push(e.func);363 if (!KNOWN_PREC_OPS.includes(e.func.name)) additionalOps.push(e.func.name);364 flatten(e.args[1]);365 } else parts.push(e);366 };367 flatten(expr);368 const precLevels = OP_PREC.concat([additionalOps]).reverse();369 for (const ops of precLevels) {370 let i = 0;371 while (i < parts.length) {372 const part = parts[i];373 if (part[IS_INFIX_OP] && ops.includes(part.name)) {374 const pLeft = parts[i - 1];375 const pRight = parts[i + 1];376 if (!pLeft || !pRight) tok.throw(`error during precedence sort: lonely operator`);377 i--;378 const iex = mkInfix({379 ctx: tok.ctx,380 type: 'c',381 func: part,382 args: [pLeft, pRight],383 });384 pLeft.parent = iex;385 pRight.parent = iex;386 parts.splice(i, 3,iex);387 }388 i++;389 }390 }391 if (parts.length !== 1) tok.throw(`error during precedence sort: incomplete reduction`);392 return parts[0];393 };394}395const _expr = oneOf(396 fixPrec(infixExpr),397 nonInfixExpr,398);399function expr (tok) { // for hoisting400 return _expr(tok);401}402const defName = match(x => x instanceof IdentToken, 'definition name');403const _definition = ctxify(map(cat(defName, anyws, equals, anyws, expr), ([n,,,, e]) => {404 const def = {405 type: 'ds',406 name: n.ident,407 expr: e,408 };409 e.parent = def;410 return def;411}));412function definition (tok) {413 return _definition(tok);414}415const _program = map(416 cat(anyws, many(map(cat(definition, bws), ([a]) => a)), opt(definition), anyws),417 ([, a, b], tok) => {418 const defs = new Set();419 const out = { ctx: tok.ctx, type: 'd', defs, floatingExpr: new Set() };420 for (const d of a.concat(b)) {421 defs.add(d);422 d.parent = out;423 }424 return out;425 },426);427function program (tok) { // for hoisting428 return _program(tok);429}430export function parse (tokenStream, ctx) {431 const cursor = new TokenCursor(tokenStream, ctx);432 const defs = program(cursor);433 if (!cursor.topLevelEof()) {434 throw cursor.getCurrentError();435 }436 return defs;...

Full Screen

Full Screen

lex.js

Source:lex.js Github

copy

Full Screen

...216 const contents = takeUntil(tag(closeTag, 'raw ident close tag'))(str);217 for (let i = 0; i < closeTag.length; i++) str.next();218 return contents;219};220const rawIdent = spanned(map(cat(tag('r#', 'raw ident start tag'), rawIdentInner), (a) => new IdentToken(a[1], true)));221const bareIdent = spanned(map(regex(bareIdentRegex, 'bare ident'), match => new IdentToken(match[1])));222const ident = oneOf(rawIdent, bareIdent);223const infixIdent = spanned(map(regex(infixIdentRegex, 'infix ident'), match => new InfixToken(match[1])));224const number = spanned(map(regex(numberRegex, 'number'), match => new NumberToken(match[1], match[2] || '')));225const bool = spanned(map(oneOf(xtag('yes'), xtag('no'), xtag('true'), xtag('false')), token =>226 new BoolToken(token === 'yes' || token === 'true')));227const nul = spanned(map(xtag('null'), () => new NullToken()));228const ws = spanned(map(regex(/^(\s+)/, 'whitespace'), match => match[1].includes('\n') ? new BreakToken() : new SpaceToken()));229const string = spanned((str) => {230 if (str.next() !== '"') throw new LexError('expected " at beginning of string');231 let c;232 let contents = '';233 let escapeNext = false;234 while ((c = str.next())) {235 if (c === '\\') {236 escapeNext = true;237 continue;238 }239 if (!escapeNext && c === '"') {240 break;241 }242 escapeNext = false;243 contents += c;244 }245 return new StringToken(contents);246});247const treeBracket = spanned(map(wrap('[', ']', tokenStream, '[...]'), inner => new BracketsToken(inner)));248const treeBrace = spanned(map(wrap('{', '}', tokenStream, '{...}'), inner => new BracesToken(inner)));249const treeParens = spanned(map(wrap('(', ')', tokenStream, '(...)'), inner => new ParensToken(inner)));250const delim = spanned(map(tag(','), () => new DelimToken()));251const oneValueToken = oneOf(nul, bool, delim, number, string, ident, infixIdent, treeBracket, treeBrace, treeParens);252const nbws = spanned(map(regex(/^[ \t]+/, 'non-breaking whitespace'), () => new SpaceToken()));253const nbToken = oneOf(nbws, oneValueToken);254const treeIndent = spanned((str) => {255 // find next line break256 while (true) {257 const c = str.peek();258 if (c === '\n') break;259 else if (c.match(/\s/)) str.next();260 else throw new Error(`unexpected ${c}, expected breaking whitespace`);261 }262 const getLineIndentation = str => {263 if (str.eof()) return -1;264 let indent;265 outer:266 while (true) {267 indent = 0;268 while (true) {269 if (str.eof()) return -1;270 const c = str.peek();271 if (c === ' ') indent++;272 else if (c === '\t') indent += 4;273 else if (c === '\n') {274 str.next();275 continue outer; // skip empty lines276 }277 else break outer;278 str.next();279 }280 }281 return indent;282 };283 // we're now at the beginning of a line284 // find min indetation level285 const minIndent = getLineIndentation(str);286 const contents = [];287 // we're now at the beginning of the first line's contents288 let atSOL = false;289 while (true) {290 if (str.eof()) break;291 else if (str.peek() === '\n') {292 // line break!293 atSOL = true;294 contents.push(new BreakToken());295 str.next();296 continue;297 } else if (atSOL) {298 atSOL = false;299 // count indentation300 const s = str.clone();301 const currentIndent = getLineIndentation(s);302 if (currentIndent < minIndent) break; // end of block303 str.copyFrom(s); // otherwise continue304 }305 contents.push(...nbTreeToken(str));306 }307 return new IndentToken(contents);308});309const whereClauseKey = spanned(map(xtag('where'), () => new IdentToken('where')));310const switchClauseKey = spanned(map(xtag('switch'), () => new IdentToken('switch')));311// treeIndent will swallow trailing line breaks312const wsWhereClause = map(cat(whereClauseKey, treeIndent), ([a, b]) => [a, b, new BreakToken()]);313const wsSwitchClause = map(cat(switchClauseKey, treeIndent), ([a, b]) => [a, b, new BreakToken()]);314const indentClause = oneOf(315 wsWhereClause,316 wsSwitchClause,317);318const _nbTreeToken = oneOf(319 indentClause,320 map(nbToken, x => [x]),321);322function nbTreeToken (str) { // for hoisting323 return _nbTreeToken(str);324}...

Full Screen

Full Screen

cst_visitor.js

Source:cst_visitor.js Github

copy

Full Screen

1const selectLexer = require("./lexer")2const parser = require("./parser")3const SelectParser = parser.SelectParser4const parserInstance = new SelectParser([], { outputCst: true })5const BaseSQLVisitor = parserInstance.getBaseCstVisitorConstructor()6class SQLtoAstVisitor extends BaseSQLVisitor {7 constructor() {8 super()9 this.validateVisitor()10 }11 selectStatement(ctx) {12 let select = this.visit(ctx.selectClause)13 let from = this.visit(ctx.fromClause)14 let where = this.visit(ctx.whereClause)15 return {16 type: "SELECT_STATEMENT",17 selectClause: select,18 fromClause: from,19 whereCLause: where20 }21 }22 selectClause(ctx) {23 const columns = ctx.Identifier.map(identToken => identToken.image)24 return {25 type: "SELECT_CLAUSE",26 columns: columns27 }28 }29 fromClause(ctx) {30 const tableName = ctx.Identifier[0].image31 return { type: "FROM_CLAUSE", table: tableName }32 }33 whereClause(ctx) {34 const condition = this.visit(ctx.expression)35 return { type: "WHERE_CLAUSE", condition: condition}36 }37 expression(ctx) {38 const lhs = this.visit(ctx.lhs[0])39 const op = this.visit(ctx.relationalOperator)40 const rhs = this.visit(ctx.rhs[0])41 return {42 type: "EXPRESSION",43 lhs: lhs,44 op: op,45 rhs: rhs46 }47 }48 atomicExpression(ctx) {49 if(ctx.Integer)50 return ctx.Integer[0].image51 else52 return ctx.Identifier[0].image53 }54 relationalOperator(ctx) {55 if(ctx.GreaterThan)56 return ctx.GreaterThan[0].image57 else58 return ctx.LessThan[0].image59 }60}61const toAstVisitorInstance = new SQLtoAstVisitor()62module.exports = {63 toAst: function(inputText) {64 const lexingResult = selectLexer.lexer(inputText)65 parserInstance.input = lexingResult.tokens66 const cst = parserInstance.selectStatement()67 console.log(JSON.stringify(parserInstance.errors, null, 2))68 if(parserInstance.errors.length > 0) {69 throw Error("Semantics Parsing errors detected\n" + parserInstance.errors[0].message)70 }71 const ast = toAstVisitorInstance.visit(cst)72 return ast73 }...

Full Screen

Full Screen

step3-visitor.js

Source:step3-visitor.js Github

copy

Full Screen

1'use strict';2// Written Docs for this tutorial step can be found here:3// https://chevrotain.io/docs/tutorial/step3a_adding_actions_visitor.html4// Tutorial Step 3a:5// Adding Actions(semantics) to our grammar using a CST Visitor.6import sumLexer from './step1';7// re-using the parser implemented in step two.8import parser from './step2';9const SumParser = parser.SumParser;10// A new parser instance with CST output (enabled by default).11const parserInstance = new SumParser();12// The base visitor class can be accessed via the a parser instance.13const BaseSQLVisitor = parserInstance.getBaseCstVisitorConstructor();14class SQLToAstVisitor extends BaseSQLVisitor {15 constructor() {16 super();17 this.validateVisitor();18 }19 sumStatement(ctx) {20 // "this.visit" can be used to visit none-terminals and will invoke the correct visit method for the CstNode passed.21 const sum = this.visit(ctx.sumClause);22 return {23 type: 'SUM_STMT',24 sumClause: sum,25 };26 }27 sumClause(ctx) {28 // Each Terminal or Non-Terminal in a grammar rule are collected into29 // an array with the same name(key) in the ctx object.30 const columns = ctx.Identifier.map((identToken) => identToken.image);31 return {32 type: 'SUM_CLAUSE',33 columns: columns,34 };35 }36}37// Our visitor has no state, so a single instance is sufficient.38const toAstVisitorInstance = new SQLToAstVisitor();39const toAst = function (inputText) {40 const lexResult = sumLexer.lex(inputText);41 // ".input" is a setter which will reset the parser's internal's state.42 parserInstance.input = lexResult.tokens;43 // Automatic CST created when parsing44 const cst = parserInstance.sumStatement();45 if (parserInstance.errors.length > 0) {46 throw Error(47 'Sad sad panda, parsing errors detected!\n' +48 parserInstance.errors[0].message49 );50 }51 const ast = toAstVisitorInstance.visit(cst);52 return ast;53};...

Full Screen

Full Screen

cd.js

Source:cd.js Github

copy

Full Screen

1i = require("./iSemanticAction")2module.exports = function(identToken)3{4 let cScope = i.symT.getScope()5 if(!cScope.includes(identToken.Lexeme))6 {7 i.throwError(identToken.linenum, `Constructor ${ident.Lexeme} must match class name ${cScope.split('.').pop()}`, "")8 // <line_number> ": Constructor " <lexeme> " must match class name " <lexeme>9 }...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch();4 const context = await browser.newContext();5 const page = await context.newPage();6 const element = await page.$('input[name="q"]');7 await element.fill('test');8 const token = await element._internalId();9 console.log(token);10 await browser.close();11})();12const { chromium } = require('playwright');13(async () => {14 const browser = await chromium.launch();15 const context = await browser.newContext();16 const page = await context.newPage();17 const element = await page.$('input[name="q"]');18 await element.fill('test');19 const token = await element._internalId();20 console.log(token);21 await browser.close();22})();23const { chromium } = require('playwright');24(async () => {25 const browser = await chromium.launch();26 const context = await browser.newContext();27 const page = await context.newPage();28 const element = await page.$('input[name="q"]');29 await element.fill('test');30 const token = await element._internalId();31 console.log(token);32 await browser.close();33})();34const { chromium } = require('playwright');35(async () => {36 const browser = await chromium.launch();37 const context = await browser.newContext();38 const page = await context.newPage();39 const element = await page.$('input[name="q"]');40 await element.fill('test');41 const token = await element._internalId();42 console.log(token);43 await browser.close();44})();45const { chromium } = require('playwright');46(async () => {47 const browser = await chromium.launch();48 const context = await browser.newContext();49 const page = await context.newPage();50 const element = await page.$('input[name="q"]');51 await element.fill('test');

Full Screen

Using AI Code Generation

copy

Full Screen

1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch({ headless: false });4 const context = await browser.newContext();5 const page = await context.newPage();6 await page.click('text=Sign in');7 await page.waitForSelector('input[type="email"]');8 const emailInput = await page.$('input[type="email"]');9 const emailInputId = await emailInput.evaluateHandle((node) => node.id);10 const emailInputIdToken = await emailInputId.evaluateHandle((node) => node.token);11 console.log(emailInputIdToken);12 await browser.close();13})();14await page.fill('input#'+emailInputIdToken, '

Full Screen

Using AI Code Generation

copy

Full Screen

1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch();4 const context = await browser.newContext();5 const page = await context.newPage();6 await page.fill('[name="q"]', 'Hello World!');7 await page.click('text=Google Search');8 await page.screenshot({ path: `example.png` });9 await browser.close();10})();11const { chromium } = require('playwright');12(async () => {13 const browser = await chromium.launch();14 const context = await browser.newContext();15 const page = await context.newPage();16 await page.fill('[name="q"]', 'Hello World!');17 await page.click('text=Google Search');18 await page.screenshot({ path: `example.png` });19 await browser.close();20})();21const { chromium } = require('playwright');22(async () => {23 const browser = await chromium.launch();24 const context = await browser.newContext();25 const page = await context.newPage();26 await page.fill('[name="q"]', 'Hello World!');27 await page.click('text=Google Search');28 await page.screenshot({ path: `example.png` });29 await browser.close();30})();31const { chromium } = require('playwright');32(async () => {33 const browser = await chromium.launch();34 const context = await browser.newContext();35 const page = await context.newPage();36 await page.fill('[name="q"]', 'Hello World!');37 await page.click('text=Google Search');38 await page.screenshot({ path: `example.png` });39 await browser.close();40})();41const { chromium } = require('playwright');42(async () => {43 const browser = await chromium.launch();44 const context = await browser.newContext();45 const page = await context.newPage();46 await page.fill('[name="q"]', 'Hello World!');47 await page.click('text=Google Search');

Full Screen

Using AI Code Generation

copy

Full Screen

1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch();4 const context = await browser.newContext();5 const page = await context.newPage();6 await page.click('text=Get started');

Full Screen

Using AI Code Generation

copy

Full Screen

1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch();4 const context = await browser.newContext();5 const page = await context.newPage();6 console.log(response.status());7 await browser.close();8})();9const { chromium } = require('playwright');10(async () => {11 const browser = await chromium.launch();12 const context = await browser.newContext();13 const page = await context.newPage();14 console.log(response.status());15 await browser.close();16})();17const { chromium } = require('playwright');18(async () => {19 const browser = await chromium.launch();20 const context = await browser.newContext();21 const page = await context.newPage();22 console.log(response.status());23 await browser.close();24})();25const { chromium } = require('playwright');26(async () => {27 const browser = await chromium.launch();28 const context = await browser.newContext();29 const page = await context.newPage();30 console.log(response.status());31 await browser.close();32})();33const { chromium } = require('playwright');34(async () => {35 const browser = await chromium.launch();36 const context = await browser.newContext();37 const page = await context.newPage();38 console.log(response.status());39 await browser.close();40})();41const { chromium } = require('playwright');42(async () => {43 const browser = await chromium.launch();44 const context = await browser.newContext();45 const page = await context.newPage();46 console.log(response.status());47 await browser.close();48})();49const { chromium } = require('playwright');50(async () => {51 const browser = await chromium.launch();

Full Screen

Using AI Code Generation

copy

Full Screen

1const { chromium, webkit, firefox, devices } = require('playwright');2(async () => {3 const browser = await chromium.launch({ headless: false });4 const context = await browser.newContext();5 const page = await context.newPage();6 const element = await page.$('input[name="q"]');7 await element.identToken('hello');8 await browser.close();9})();

Full Screen

Using AI Code Generation

copy

Full Screen

1const playwright = require('playwright');2(async () => {3 const browser = await playwright.chromium.launch({ headless: false });4 const page = await browser.newPage();5 const token = await page.evaluate(() => {6 return window.playwright._internal.selectors._selectorEngine._identToken;7 });8 console.log(token);9 await browser.close();10})();11const playwright = require('playwright');12(async () => {13 const browser = await playwright.chromium.launch({ headless: false });14 const page = await browser.newPage();15 const token = await page.evaluate(() => {16 return window.playwright._internal.selectors._selectorEngine._identToken;17 });18 console.log(token);19 await browser.close();20})();21const playwright = require('playwright');22(async () => {23 const browser = await playwright.chromium.launch({ headless: false });24 const page = await browser.newPage();25 const token = await page.evaluate(() => {26 return window.playwright._internal.selectors._selectorEngine._identToken;27 });28 console.log(token);29 await browser.close();30})();31const playwright = require('playwright');32(async () => {33 const browser = await playwright.chromium.launch({ headless: false });34 const page = await browser.newPage();35 const token = await page.evaluate(() => {36 return window.playwright._internal.selectors._selectorEngine._identToken;37 });38 console.log(token);39 await browser.close();40})();41const playwright = require('playwright');42(async () => {43 const browser = await playwright.chromium.launch({ headless: false });44 const page = await browser.newPage();45 const token = await page.evaluate(() => {46 return window.playwright._internal.selectors._selectorEngine._identToken;

Full Screen

Using AI Code Generation

copy

Full Screen

1const {IdentToken} = require('playwright/lib/internal/identifiers');2const {IdentToken} = require('playwright/lib/internal/identifiers');3const {IdentToken} = require('playwright/lib/internal/identifiers');4const {IdentToken} = require('playwright/lib/internal/identifiers');5const {IdentToken} = require('playwright/lib/internal/identifiers');6const {IdentToken} = require('playwright/lib/internal/identifiers');7const {IdentToken} = require('playwright/lib/internal/identifiers');8const {IdentToken} = require('playwright/lib/internal/identifiers');9const {IdentToken} = require('playwright/lib/internal/identifiers');10const {IdentToken} = require('playwright/lib/internal/identifiers');11const {IdentToken} = require('playwright/lib/internal/identifiers');

Full Screen

Using AI Code Generation

copy

Full Screen

1const { InternalAPI } = require('playwright');2const token = await InternalAPI.identToken();3console.log(token);4const { InternalAPI } = require('playwright');5const token = await InternalAPI.identToken();6console.log(token);7const { InternalAPI } = require('playwright');8const token = await InternalAPI.identToken();9console.log(token);10const { InternalAPI } = require('playwright');11const token = await InternalAPI.identToken();12console.log(token);13const { InternalAPI } = require('playwright');14const token = await InternalAPI.identToken();15console.log(token);16const { InternalAPI } = require('playwright');17const token = await InternalAPI.identToken();18console.log(token);19const { InternalAPI } = require('playwright');20const token = await InternalAPI.identToken();21console.log(token);22const { InternalAPI } = require('playwright');23const token = await InternalAPI.identToken();24console.log(token);25const { InternalAPI } = require('playwright');26const token = await InternalAPI.identToken();27console.log(token);28const { InternalAPI } = require('playwright');29const token = await InternalAPI.identToken();30console.log(token);31const { InternalAPI } = require('playwright');32const token = await InternalAPI.identToken();33console.log(token);34const { InternalAPI } = require('playwright');35const token = await InternalAPI.identToken();36console.log(token);

Full Screen

Playwright tutorial

LambdaTest’s Playwright tutorial will give you a broader idea about the Playwright automation framework, its unique features, and use cases with examples to exceed your understanding of Playwright testing. This tutorial will give A to Z guidance, from installing the Playwright framework to some best practices and advanced concepts.

Chapters:

  1. What is Playwright : Playwright is comparatively new but has gained good popularity. Get to know some history of the Playwright with some interesting facts connected with it.
  2. How To Install Playwright : Learn in detail about what basic configuration and dependencies are required for installing Playwright and run a test. Get a step-by-step direction for installing the Playwright automation framework.
  3. Playwright Futuristic Features: Launched in 2020, Playwright gained huge popularity quickly because of some obliging features such as Playwright Test Generator and Inspector, Playwright Reporter, Playwright auto-waiting mechanism and etc. Read up on those features to master Playwright testing.
  4. What is Component Testing: Component testing in Playwright is a unique feature that allows a tester to test a single component of a web application without integrating them with other elements. Learn how to perform Component testing on the Playwright automation framework.
  5. Inputs And Buttons In Playwright: Every website has Input boxes and buttons; learn about testing inputs and buttons with different scenarios and examples.
  6. Functions and Selectors in Playwright: Learn how to launch the Chromium browser with Playwright. Also, gain a better understanding of some important functions like “BrowserContext,” which allows you to run multiple browser sessions, and “newPage” which interacts with a page.
  7. Handling Alerts and Dropdowns in Playwright : Playwright interact with different types of alerts and pop-ups, such as simple, confirmation, and prompt, and different types of dropdowns, such as single selector and multi-selector get your hands-on with handling alerts and dropdown in Playright testing.
  8. Playwright vs Puppeteer: Get to know about the difference between two testing frameworks and how they are different than one another, which browsers they support, and what features they provide.
  9. Run Playwright Tests on LambdaTest: Playwright testing with LambdaTest leverages test performance to the utmost. You can run multiple Playwright tests in Parallel with the LammbdaTest test cloud. Get a step-by-step guide to run your Playwright test on the LambdaTest platform.
  10. Playwright Python Tutorial: Playwright automation framework support all major languages such as Python, JavaScript, TypeScript, .NET and etc. However, there are various advantages to Python end-to-end testing with Playwright because of its versatile utility. Get the hang of Playwright python testing with this chapter.
  11. Playwright End To End Testing Tutorial: Get your hands on with Playwright end-to-end testing and learn to use some exciting features such as TraceViewer, Debugging, Networking, Component testing, Visual testing, and many more.
  12. Playwright Video Tutorial: Watch the video tutorials on Playwright testing from experts and get a consecutive in-depth explanation of Playwright automation testing.

Run Playwright Internal automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful