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