1- import assert from 'assert' ;
21import { readFileSync } from 'fs' ;
2+ import assert from 'node:assert/strict' ;
3+ import process from 'node:process' ;
4+ import { parseArgs } from 'node:util' ;
35import * as ohm from 'ohm-js' ;
4- import { exit } from 'process' ;
56
67const { raw} = String ;
78
9+ // A simple logging facade.
10+ /* eslint-disable no-console */
11+ let loglevel = 1 ;
12+ const log = {
13+ error ( msg , ...args ) {
14+ if ( loglevel >= 0 ) console . error ( msg , ...args ) ;
15+ } ,
16+ warn ( msg , ...args ) {
17+ if ( loglevel >= 1 ) console . error ( msg , ...args ) ;
18+ } ,
19+ info ( msg , ...args ) {
20+ if ( loglevel >= 2 ) console . error ( msg , ...args ) ;
21+ } ,
22+ debug ( msg , ...args ) {
23+ if ( loglevel >= 3 ) console . error ( msg , ...args ) ;
24+ }
25+ } ;
26+ /* eslint-enable no-console */
27+
828// Maps from a Unicode character literal as used in the ECMAScript spec to
929// an equivalent Ohm parsing expression.
1030const literalToOhm = {
@@ -22,14 +42,14 @@ const literalToOhm = {
2242} ;
2343
2444function safelyReplace ( str , pattern , replacement ) {
25- if ( ! str . includes ( pattern ) ) console . error ( str ) ;
45+ if ( ! str . includes ( pattern ) ) log . error ( str ) ;
2646 assert ( str . includes ( pattern ) , `not found: ${ JSON . stringify ( pattern ) } ` ) ;
2747 return str . replace ( pattern , replacement ) ;
2848}
2949
3050const lexicalRuleName = str => str [ 0 ] . toLowerCase ( ) + str . slice ( 1 ) ;
3151const syntacticRuleName = str => {
32- assert ( str [ 0 ] === str [ 0 ] . toUpperCase ( ) ) ;
52+ assert . equal ( str [ 0 ] , str [ 0 ] . toUpperCase ( ) ) ;
3353 return str ;
3454} ;
3555
@@ -47,6 +67,7 @@ function mkRuleOverrides(substitutions) {
4767 const keys = Object . keys ( substitution ) ;
4868 if ( keys . includes ( 'override' ) ) {
4969 overrides [ substitution . name ] = substitution . override ;
70+ log . debug ( `Added substitution: ${ substitution . name } => '${ substitution . override } '` ) ;
5071 continue ;
5172 }
5273 if ( keys . includes ( 'pattern' ) && keys . includes ( 'replacement' ) ) {
@@ -98,8 +119,10 @@ const PRELUDE = raw`
98119function overrideRuleBodyOrElse ( ruleName , rhs , noOverrideValue ) {
99120 const override = ruleOverrides [ ruleName ] ;
100121 if ( typeof override === 'string' ) {
122+ log . debug ( ` Using string override for '${ ruleName } '` ) ;
101123 return override ;
102124 } else if ( typeof override === 'function' ) {
125+ log . debug ( ` Using function override for '${ ruleName } '` ) ;
103126 return override ( rhs , noOverrideValue ) ;
104127 } else {
105128 return noOverrideValue ;
@@ -115,12 +138,18 @@ let grammarName; // Initialized in main(), below.
115138semantics . addOperation (
116139 'toOhm()' ,
117140 ( ( ) => {
141+ // This is the main handler for converting a production from
142+ // the source spec to a rule in the destination Ohm grammar.
118143 function handleProduction (
119144 nonterminal ,
120145 rhs ,
121146 parameterListOpt = undefined ,
122147 kind = 'LEXICAL'
123148 ) {
149+ log . debug ( `handleProduction: ${ nonterminal . sourceString } ` , {
150+ rhs : rhs . sourceString ,
151+ kind
152+ } ) ;
124153 // const isLexical = parameterListOpt === undefined;
125154 const isLexical = kind === 'LEXICAL' ;
126155 const strippedNonTerminal = nonterminal . sourceString . replaceAll ( '|' , '' ) ;
@@ -599,10 +628,21 @@ function addContext(semantics, getActions) {
599628 for details of the grammar format.
600629 */
601630( function main ( ) {
602- grammarName = process . argv [ 2 ] ;
603- const inputFilename = process . argv [ 3 ] ;
604- if ( process . argv . length > 4 ) {
605- const overrideConfig = JSON . parse ( readFileSync ( process . argv [ 4 ] , { encoding : 'utf-8' } ) ) ;
631+ const { values, positionals} = parseArgs ( {
632+ options : {
633+ debug : { type : 'boolean' } ,
634+ name : { type : 'string' } ,
635+ overrides : { type : 'string' } ,
636+ verbose : { type : 'boolean' }
637+ } ,
638+ allowPositionals : true
639+ } ) ;
640+ if ( values . verbose ) loglevel = Math . max ( loglevel , 2 ) ;
641+ if ( values . debug ) loglevel = Math . max ( loglevel , 3 ) ;
642+ grammarName = values . name ;
643+ const inputFilename = positionals [ 0 ] ;
644+ if ( values . overrides ) {
645+ const overrideConfig = JSON . parse ( readFileSync ( values . overrides , { encoding : 'utf-8' } ) ) ;
606646 if ( overrideConfig . substitutions ) {
607647 ruleOverrides = mkRuleOverrides ( overrideConfig . substitutions ) ;
608648 }
@@ -616,9 +656,10 @@ function addContext(semantics, getActions) {
616656 }
617657 }
618658
659+ log . info ( `Reading grammarkdown from ${ inputFilename } ` ) ;
619660 const result = grammarkdown . match ( readFileSync ( inputFilename , 'utf-8' ) ) ;
620661 if ( ! result . succeeded ( ) ) {
621- console . error ( result . message ) ;
662+ log . error ( result . message ) ;
622663 }
623664
624665 const root = semantics ( result ) ;
@@ -629,8 +670,9 @@ function addContext(semantics, getActions) {
629670 try {
630671 ohm . grammar ( outGrammar ) ;
631672 } catch ( e ) {
632- console . error ( e . message ) ;
633- exit ( 1 ) ;
673+ log . error ( e . message ) ;
674+ process . exit ( 1 ) ;
634675 }
676+ // eslint-disable-next-line no-console
635677 console . log ( outGrammar ) ;
636678} ) ( ) ;
0 commit comments