primary method

List<Node> primary ()

The primary rule is the entry and exit point of the parser. The rules here can appear at any level of the parse tree.

The recursive nature of the grammar is an interplay between the block rule, which represents { ... }, the ruleset rule, and this primary rule, as represented by this simplified grammar:

primary  →  (ruleset | declaration)+
ruleset  →  selector+ block
block    →  '{' primary '}'

Only at one point is the primary rule not called from the block rule: at the root level.

Implementation

List<Node> primary() {
  Node node;
  List<Node> nodeList;
  final root = <Node>[];

  while (true) {
    while (true) {
      node = comment();
      if (node == null) break;
      root.add(node);
    }

    // always process comments before deciding if finished
    if (parserInput.finished || parserInput.isEmpty) break;
    if (parserInput.peekChar('}')) break;

    nodeList = extendRule();
    if (nodeList != null) {
      root.addAll(nodeList);
      continue;
    }

    node = mixin.definition() ??
        declaration() ??
        mixin.call(inValue: false, getLookup: false) ??
        ruleset() ??
        variableCall() ??
        entities.call() ??
        atrule();

    if (node != null) {
      root.add(node);
    } else {
      var foundSemiColon = false;
      while (parserInput.$char(';') != null) {
        foundSemiColon = true;
      }
      if (!foundSemiColon) break;
    }
  }

  return root;

// 3.10.3 20190825
//  primary: function () {
//      const mixin = this.mixin;
//      let root = [];
//      let node;
//
//      while (true) {
//          while (true) {
//              node = this.comment();
//              if (!node) { break; }
//              root.push(node);
//          }
//          // always process comments before deciding if finished
//          if (parserInput.finished) {
//              break;
//          }
//          if (parserInput.peek('}')) {
//              break;
//          }
//
//          node = this.extendRule();
//          if (node) {
//              root = root.concat(node);
//              continue;
//          }
//
//          node = mixin.definition() || this.declaration() || mixin.call(false, false) ||
//              this.ruleset() || this.variableCall() || this.entities.call() || this.atrule();
//          if (node) {
//              root.push(node);
//          } else {
//              let foundSemiColon = false;
//              while (parserInput.$char(';')) {
//                  foundSemiColon = true;
//              }
//              if (!foundSemiColon) {
//                  break;
//              }
//          }
//      }
//
//      return root;
//  },
}