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 };