• Jump To … +
    api.js attributes.js node-exports.js parser.js rule-attributes.js rule-dependencies.js sabnf-grammar.js scanner-callbacks.js scanner-grammar.js scanner.js semantic-callbacks.js show-rules.js syntax-callbacks.js web-exports.js converter.js node-exports.js transformers.js web-exports.js apg-conv.js help.js apg-exp.js exec.js flags.js parse-replacement.js replace-grammar.js replace.js result.js sabnf-generator.js split.js web-exports.js ast.js circular-buffer.js emitcss.js identifiers.js node-exports.js parser.js stats.js style.js trace.js utilities.js web-exports.js apg.js command-line.js LICENSE.md README.md index.md
  • rule-dependencies.js

  • §
    /*  *************************************************************************************
     *   copyright: Copyright (c) 2021 Lowell D. Thomas, all rights reserved
     *     license: BSD-2-Clause (https://opensource.org/licenses/BSD-2-Clause)
     *   ********************************************************************************* */
  • §

    Determine rule dependencies and types. For each rule, determine which other rules it refers to and which of the other rules refer back to it.

    Rule types are:

    • non-recursive - the rule never refers to itself, even indirectly
    • recursive - the rule refers to itself, possibly indirectly
    • mutually-recursive - belongs to a group of two or more rules, each of which refers to every other rule in the group, including itself.
    module.exports = (() => {
      const id = require('../apg-lib/identifiers');
      let state = null; /* keep a global reference to the state for the show functions */
    
      /* scan the opcodes of the indexed rule and discover which rules it references and which rule refer back to it */
      const scan = (ruleCount, ruleDeps, index, isScanned) => {
        let i = 0;
        let j = 0;
        const rdi = ruleDeps[index];
        isScanned[index] = true;
        const op = rdi.rule.opcodes;
        for (i = 0; i < op.length; i += 1) {
          const opi = op[i];
          if (opi.type === id.RNM) {
            rdi.refersTo[opi.index] = true;
            if (!isScanned[opi.index]) {
              scan(ruleCount, ruleDeps, opi.index, isScanned);
            }
            for (j = 0; j < ruleCount; j += 1) {
              if (ruleDeps[opi.index].refersTo[j]) {
                rdi.refersTo[j] = true;
              }
            }
          } else if (opi.type === id.UDT) {
            rdi.refersToUdt[opi.index] = true;
          } else if (opi.type === id.BKR) {
            if (opi.index < ruleCount) {
              rdi.refersTo[opi.index] = true;
              if (!isScanned[opi.index]) {
                scan(ruleCount, ruleDeps, opi.index, isScanned);
              }
            } else {
              rdi.refersToUdt[ruleCount - opi.index] = true;
            }
          }
        }
      };
  • §

    Determine the rule dependencies, types and mutually recursive groups.

      const ruleDependencies = (stateArg) => {
        state = stateArg; /* make it global */
        let i = 0;
        let j = 0;
        let groupCount = 0;
        let rdi = null;
        let rdj = null;
        let newGroup = false;
        state.dependenciesComplete = false;
    
        /* make a working array of rule scanned markers */
        const isScanned = state.falseArray(state.ruleCount);
    
        /* discover the rule dependencies */
        for (i = 0; i < state.ruleCount; i += 1) {
          state.falsifyArray(isScanned);
          scan(state.ruleCount, state.ruleDeps, i, isScanned);
        }
        /* discover all rules referencing each rule */
        for (i = 0; i < state.ruleCount; i += 1) {
          for (j = 0; j < state.ruleCount; j += 1) {
            if (i !== j) {
              if (state.ruleDeps[j].refersTo[i]) {
                state.ruleDeps[i].referencedBy[j] = true;
              }
            }
          }
        }
        /* find the non-recursive and recursive types */
        for (i = 0; i < state.ruleCount; i += 1) {
          state.ruleDeps[i].recursiveType = id.ATTR_N;
          if (state.ruleDeps[i].refersTo[i]) {
            state.ruleDeps[i].recursiveType = id.ATTR_R;
          }
        }
    
        /* find the mutually-recursive groups, if any */
        groupCount = -1;
        for (i = 0; i < state.ruleCount; i += 1) {
          rdi = state.ruleDeps[i];
          if (rdi.recursiveType === id.ATTR_R) {
            newGroup = true;
            for (j = 0; j < state.ruleCount; j += 1) {
              if (i !== j) {
                rdj = state.ruleDeps[j];
                if (rdj.recursiveType === id.ATTR_R) {
                  if (rdi.refersTo[j] && rdj.refersTo[i]) {
                    if (newGroup) {
                      groupCount += 1;
                      rdi.recursiveType = id.ATTR_MR;
                      rdi.groupNumber = groupCount;
                      newGroup = false;
                    }
                    rdj.recursiveType = id.ATTR_MR;
                    rdj.groupNumber = groupCount;
                  }
                }
              }
            }
          }
        }
        state.isMutuallyRecursive = groupCount > -1;
    
        /* sort the rules/UDTS */
        state.ruleAlphaIndexes.sort(state.compRulesAlpha);
        state.ruleTypeIndexes.sort(state.compRulesAlpha);
        state.ruleTypeIndexes.sort(state.compRulesType);
        if (state.isMutuallyRecursive) {
          state.ruleTypeIndexes.sort(state.compRulesGroup);
        }
        if (state.udtCount) {
          state.udtAlphaIndexes.sort(state.compUdtsAlpha);
        }
    
        state.dependenciesComplete = true;
      };
      const show = (type = null) => {
        let i = 0;
        let j = 0;
        let count = 0;
        let startSeg = 0;
        const maxRule = state.ruleCount - 1;
        const maxUdt = state.udtCount - 1;
        const lineLength = 100;
        let str = '';
        let pre = '';
        const toArrow = '=> ';
        const byArrow = '<= ';
        let first = false;
        let rdi = null;
        let { ruleIndexes } = state;
        let { udtIndexes } = state;
        if (type === 97) {
          ruleIndexes = state.ruleAlphaIndexes;
          udtIndexes = state.udtAlphaIndexes;
        } else if (type === 116) {
          ruleIndexes = state.ruleTypeIndexes;
          udtIndexes = state.udtAlphaIndexes;
        }
        for (i = 0; i < state.ruleCount; i += 1) {
          rdi = state.ruleDeps[ruleIndexes[i]];
          pre = `${ruleIndexes[i]}:${state.typeToString(rdi.recursiveType)}:`;
          if (state.isMutuallyRecursive) {
            pre += rdi.groupNumber > -1 ? rdi.groupNumber : '-';
            pre += ':';
          }
          pre += ' ';
          str += `${pre + state.rules[ruleIndexes[i]].name}\n`;
          first = true;
          count = 0;
          startSeg = str.length;
          str += pre;
          for (j = 0; j < state.ruleCount; j += 1) {
            if (rdi.refersTo[ruleIndexes[j]]) {
              if (first) {
                str += toArrow;
                first = false;
                str += state.ruleDeps[ruleIndexes[j]].rule.name;
              } else {
                str += `, ${state.ruleDeps[ruleIndexes[j]].rule.name}`;
              }
              count += 1;
            }
            if (str.length - startSeg > lineLength && j !== maxRule) {
              str += `\n${pre}${toArrow}`;
              startSeg = str.length;
            }
          }
          if (state.udtCount) {
            for (j = 0; j < state.udtCount; j += 1) {
              if (rdi.refersToUdt[udtIndexes[j]]) {
                if (first) {
                  str += toArrow;
                  first = false;
                  str += state.udts[udtIndexes[j]].name;
                } else {
                  str += `, ${state.udts[udtIndexes[j]].name}`;
                }
                count += 1;
              }
              if (str.length - startSeg > lineLength && j !== maxUdt) {
                str += `\n${pre}${toArrow}`;
                startSeg = str.length;
              }
            }
          }
          if (count === 0) {
            str += '=> <none>\n';
          }
          if (first === false) {
            str += '\n';
          }
          first = true;
          count = 0;
          startSeg = str.length;
          str += pre;
          for (j = 0; j < state.ruleCount; j += 1) {
            if (rdi.referencedBy[ruleIndexes[j]]) {
              if (first) {
                str += byArrow;
                first = false;
                str += state.ruleDeps[ruleIndexes[j]].rule.name;
              } else {
                str += `, ${state.ruleDeps[ruleIndexes[j]].rule.name}`;
              }
              count += 1;
            }
            if (str.length - startSeg > lineLength && j !== maxRule) {
              str += `\n${pre}${toArrow}`;
              startSeg = str.length;
            }
          }
          if (count === 0) {
            str += '<= <none>\n';
          }
          if (first === false) {
            str += '\n';
          }
          str += '\n';
        }
        return str;
      };
  • §

    Display the rule dependencies.

    • order
      • “index” or “i”, index order (default)
      • “alpha” or “a”, alphabetical order
      • “type” or “t”, ordered by type (alphabetical within each type/group)
      • none of above, index order (default)
      const showRuleDependencies = (order = 'index') => {
        let str = 'RULE DEPENDENCIES(index:type:[group number:])\n';
        str += '=> refers to rule names\n';
        str += '<= referenced by rule names\n';
        if (!state.dependenciesComplete) {
          return str;
        }
    
        if (order.charCodeAt(0) === 97) {
          str += 'alphabetical by rule name\n';
          str += show(97);
        } else if (order.charCodeAt(0) === 116) {
          str += 'ordered by rule type\n';
          str += show(116);
        } else {
          str += 'ordered by rule index\n';
          str += show(null);
        }
        return str;
      };
    
      /* Destructuring assignment - see MDN Web Docs */
      return { ruleDependencies, showRuleDependencies };
    })();