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