index.js
1 #! /usr/bin/env node 2 3 /* global process */ 4 5 import { program } from 'commander'; 6 import { streamParse, Context } from 'bablr'; 7 import { embeddedSourceFrom, readFromStream, stripTrailingNewline } from '@bablr/helpers/source'; 8 import { debugEnhancers } from '@bablr/helpers/enhancers'; 9 import colorSupport from 'color-support'; 10 import { evaluateIO } from '@bablr/io-vm-node'; 11 import { generateCSTML } from '../lib/syntax.js'; 12 import { 13 buildBasicNodeMatcher, 14 buildOpenNodeMatcher, 15 buildPropertyMatcher, 16 } from '@bablr/helpers/builders'; 17 import { buildEmbeddedMatcher, evaluateReturnAsync } from '@bablr/agast-helpers/tree'; 18 19 program 20 .name('bablr') 21 .option('-l, --language [URL]', 'The URL of the top BABLR language') 22 .option('-p, --production [type]', 'The name of the top production type') 23 .option('-f, --format', 'Pretty-format CSTML output', true) 24 .option('-g, --gaps', 'The source and resulting tree may contain gaps') 25 .option('-F, --no-format') 26 .option('-v, --verbose', 'Prints debugging information to stderr') 27 .option( 28 '-c, --color [WHEN]', 29 'When to use ANSI escape colors \n WHEN: "auto" | "always" | "never"', 30 'auto', 31 ) 32 .option('-e, --embedded', 'Requires quoted input but enables gap parsing') 33 .parse(process.argv); 34 35 const programOpts = program.opts(); 36 37 if (programOpts.color && !['auto', 'always', 'never'].includes(programOpts.color.toLowerCase())) { 38 throw new Error('invalid value for --color'); 39 } 40 41 const options = { 42 ...programOpts, 43 color: 44 (programOpts.color.toLowerCase() === 'auto' && colorSupport.hasBasic) || 45 programOpts.color.toLowerCase() === 'always', 46 }; 47 48 const language = await import(options.language); 49 50 const matcher = buildEmbeddedMatcher( 51 buildPropertyMatcher( 52 null, 53 buildBasicNodeMatcher( 54 buildOpenNodeMatcher({ hasGap: options.gaps }, language.canonicalURL, options.production), 55 ), 56 ), 57 ); 58 59 const logStderr = (...args) => { 60 process.stderr.write(args.join(' ') + '\n'); 61 }; 62 63 const enhancers = options.verbose ? { ...debugEnhancers, agast: null } : {}; 64 65 const ctx = Context.from(language, enhancers.bablrProduction); 66 67 const rawStream = process.stdin.setEncoding('utf-8'); 68 69 const output = evaluateIO(() => 70 generateCSTML( 71 streamParse( 72 ctx, 73 matcher, 74 options.embedded 75 ? embeddedSourceFrom(readFromStream(rawStream)) 76 : stripTrailingNewline(readFromStream(rawStream)), 77 {}, 78 { enhancers, emitEffects: true }, 79 ), 80 { 81 ctx, 82 color: options.color, 83 format: options.format, 84 emitEffects: true, 85 }, 86 ), 87 ); 88 89 await evaluateReturnAsync(output);