/ lib / syntax.js
syntax.js
  1  /* global process Promise */
  2  
  3  import emptyStack from '@iter-tools/imm-stack';
  4  import * as cstml from '@bablr/language-en-cstml';
  5  import * as verboseOutput from '@bablr/language-en-bablr-cli-verbose-output';
  6  import { streamParse, Context } from 'bablr/enhanceable';
  7  import { Coroutine } from '@bablr/coroutine';
  8  import {
  9    StreamIterable,
 10    generateAllOutput,
 11    getStreamIterator,
 12    writeCSTMLStrategy,
 13    writePrettyCSTMLStrategy,
 14  } from '@bablr/agast-helpers/stream';
 15  import {
 16    buildWriteEffect,
 17    buildAnsiPushEffect,
 18    buildAnsiPopEffect,
 19    buildEmbeddedMatcher,
 20  } from '@bablr/agast-helpers/builders';
 21  import { OpenNodeTag, CloseNodeTag, ReferenceTag, LiteralTag } from '@bablr/agast-helpers/symbols';
 22  import {
 23    buildBasicNodeMatcher,
 24    buildOpenNodeMatcher,
 25    buildPropertyMatcher,
 26  } from '@bablr/helpers/builders';
 27  
 28  function* __higlightStrategy(tags) {
 29    const co = new Coroutine(getStreamIterator(tags));
 30  
 31    let types = emptyStack;
 32  
 33    let currentRef;
 34  
 35    co.advance();
 36  
 37    for (;;) {
 38      if (co.current instanceof Promise) {
 39        co.current = yield co.current;
 40      }
 41  
 42      if (co.done) break;
 43  
 44      const tag = co.value;
 45  
 46      if (tag.type === OpenNodeTag) {
 47        const tagType = tag.value.type;
 48        const currentType = types.value;
 49  
 50        types = types.push(tagType);
 51  
 52        if (
 53          tagType === Symbol.for('LiteralTag') ||
 54          (tagType === Symbol.for('String') && currentRef.name === 'intrinsicValue')
 55        ) {
 56          if (tagType === Symbol.for('LiteralTag') || currentType === Symbol.for('OpenNodeTag')) {
 57            yield buildAnsiPushEffect('bold green');
 58          } else if (currentType === Symbol.for('OpenNodeMatcher')) {
 59            yield buildAnsiPushEffect('bold orange');
 60          } else {
 61            yield buildAnsiPushEffect();
 62          }
 63        } else if (
 64          tagType === Symbol.for('Pattern') &&
 65          tag.value.language !== 'https://bablr.org/languages/core/en/spamex'
 66        ) {
 67          yield buildAnsiPushEffect('bold orange');
 68        } else if (tagType === Symbol.for('EscapeSequence')) {
 69          yield buildAnsiPushEffect('bold cyan');
 70        } else if (tagType === Symbol.for('Identifier')) {
 71          if (currentType === Symbol.for('ReferenceTag')) {
 72            yield buildAnsiPushEffect('bold gray');
 73          } else if (currentType === Symbol.for('Call')) {
 74            yield buildAnsiPushEffect('magenta bold');
 75          } else {
 76            yield buildAnsiPushEffect();
 77          }
 78        } else if (
 79          tagType === Symbol.for('EnterProductionLine') ||
 80          tagType === Symbol.for('LeaveProductionLine')
 81        ) {
 82          yield buildAnsiPushEffect('blue bold');
 83        } else if (
 84          (currentRef?.name === 'sigilToken' &&
 85            (currentType === Symbol.for('ExecSpamexInstructionLine') ||
 86              currentType === Symbol.for('ExecCSTMLInstructionLine'))) ||
 87          (currentType === Symbol.for('Call') &&
 88            (currentRef.name === 'openToken' || currentRef.name === 'closeToken'))
 89        ) {
 90          yield buildAnsiPushEffect('magenta bold');
 91        } else {
 92          yield buildAnsiPushEffect();
 93        }
 94      }
 95  
 96      if (tag.type === ReferenceTag) {
 97        currentRef = tag.value;
 98      }
 99  
100      if (tag.type === CloseNodeTag) {
101        types = types.pop();
102        yield buildAnsiPopEffect();
103      }
104  
105      if (tag.type === LiteralTag) {
106        yield buildWriteEffect(tag.value);
107      } else if (tag.type === OpenNodeTag && tag.value.intrinsicValue) {
108        yield buildWriteEffect(tag.value.intrinsicValue);
109        yield buildAnsiPopEffect();
110      }
111  
112      yield tag;
113  
114      co.advance();
115    }
116  }
117  
118  export const higlightStrategy = (tags) => {
119    return new StreamIterable(__higlightStrategy(tags));
120  };
121  
122  export const generateCSTML = (tags, options = {}) => {
123    const strategyOptions = { ctx: options.ctx };
124    const outputInstructions = options.format
125      ? writePrettyCSTMLStrategy(tags, strategyOptions)
126      : writeCSTMLStrategy(tags, strategyOptions);
127  
128    if (options.color) {
129      const input = generateAllOutput(outputInstructions);
130      const language = options.emitEffects ? verboseOutput : cstml;
131      const type = options.emitEffects ? 'Output' : 'Document';
132  
133      const context = Context.from(language);
134  
135      const tags = streamParse(
136        context,
137        buildEmbeddedMatcher(
138          buildPropertyMatcher(
139            null,
140            buildBasicNodeMatcher(buildOpenNodeMatcher({}, language.canonicalURL, type)),
141          ),
142        ),
143        input,
144      );
145  
146      return higlightStrategy(tags, { ctx: context });
147    } else {
148      return outputInstructions;
149    }
150  };