Skip to content

Commit be01b46

Browse files
committed
Cleaned up expression rule info passing and logging
1 parent 7f787f7 commit be01b46

File tree

3 files changed

+85
-66
lines changed

3 files changed

+85
-66
lines changed

scripts/bnf.js

Lines changed: 36 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
#!/usr/bin/env node
22
import { readFileSync, writeFileSync } from 'node:fs';
3-
import { inspect, parseArgs } from 'node:util';
3+
import { parseArgs } from 'node:util';
44
import * as bnf from '../dist/bnf.js';
55
import { stringify_node } from '../dist/parser.js';
66
import { is_issue, stringify_issue } from '../dist/issue.js';
7-
import { dirname, resolve } from 'node:path/posix';
7+
import { dirname, parse, resolve } from 'node:path/posix';
88
import { compress as compress_config } from '../dist/config.js';
99

1010
const {
1111
positionals: [input],
1212
values: options,
1313
} = parseArgs({
1414
options: {
15-
format: { short: 'f', type: 'string', default: 'js' },
1615
output: { short: 'o', type: 'string' },
1716
colors: { short: 'c', type: 'boolean' },
1817
tokens: { short: 'T', type: 'string' },
@@ -21,14 +20,14 @@ const {
2120
help: { short: 'h', type: 'boolean', default: false },
2221
compress: { type: 'boolean', default: false },
2322
trace: { type: 'boolean' },
23+
'visual-depth': { type: 'string', default: '0' },
2424
},
2525
allowPositionals: true,
2626
});
2727

2828
if (options.help || !input) {
2929
console.log(`Usage: xcompile-bnf [options] <file>\n
3030
Output options:
31-
--format,-f <format=js> Output format (js, json)
3231
--output,-o <path> Path to an output file
3332
--colors,-c Colorize output messages
3433
--help,-h Display this help message
@@ -37,7 +36,8 @@ Debugging options:
3736
--tokens,-T [only] Show tokenizer output. If 'only', only the tokenizer output will be shown.
3837
--parser,-P [only] Show parser output. If 'only', only the parser output will be shown.
3938
--verbose,-V Show verbose output. If passed multiple times, increases the verbosity level.
40-
--trace Show issue origin trace`);
39+
--visual-depth <size> Display depth visually using indentation, using
40+
--trace Show issue origin`);
4141
process.exit(0);
4242
}
4343

@@ -51,11 +51,24 @@ if ((options.tokens == 'only' || options.parser == 'only') && (options.format ||
5151
process.exit(1);
5252
}
5353

54+
const depth_indentation = parseInt(options['visual-depth']) || 0;
55+
5456
function logger(outputLevel) {
55-
return function ({ level, message, depth }) {
57+
return function __log(entry) {
58+
if (is_issue(entry)) {
59+
console.error(stringify_issue(entry, options));
60+
return;
61+
}
62+
63+
const { level, message, depth } = entry;
64+
5665
if (outputLevel < level) return;
5766

58-
console.log(' '.repeat(4 * depth) + (level > 2 ? '[debug] ' : '') + message);
67+
const debug = level > 2 ? 'debug' : '';
68+
69+
const header = depth_indentation ? `${' '.repeat(depth_indentation)}(${debug})` : `[${depth} ${debug}]`;
70+
71+
console.log(header + ' ' + message);
5972
};
6073
}
6174

@@ -116,30 +129,19 @@ let config = bnf.create_config(ast, { log: logger(verbose), include });
116129

117130
if (options.compress) config = compress_config(config);
118131

119-
const write = data => (options.output ? writeFileSync(options.output, data) : console.log);
120-
121-
switch (options.format) {
122-
case 'json':
123-
write(
124-
JSON.stringify({
125-
...config,
126-
literals: config.literals.map(literal => {
127-
const base = {
128-
name: literal.name,
129-
pattern: literal.pattern.source.slice(1),
130-
};
131-
132-
const { flags } = literal.pattern;
133-
134-
return flags.length ? { ...base, flags } : base;
135-
}),
136-
})
137-
);
138-
break;
139-
case 'js':
140-
write(inspect(config, { colors: options.colors, depth: null }));
141-
break;
142-
default:
143-
console.error('Unsupported output format:', options.format);
144-
process.exit(1);
145-
}
132+
writeFileSync(
133+
options.output || parse(input).name + '.json',
134+
JSON.stringify({
135+
...config,
136+
literals: config.literals.map(literal => {
137+
const base = {
138+
name: literal.name,
139+
pattern: literal.pattern.source.slice(1),
140+
};
141+
142+
const { flags } = literal.pattern;
143+
144+
return flags.length ? { ...base, flags } : base;
145+
}),
146+
})
147+
);

src/bnf.ts

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -50,18 +50,6 @@ interface CreateConfigContext extends CreateConfigOptions {
5050
* The config being created
5151
*/
5252
config: PureConfig;
53-
54-
/**
55-
* The name of the rule currently being parsed.
56-
* Used for group names.
57-
*/
58-
currentRule?: string;
59-
60-
/**
61-
* The number of groups in the current rule.
62-
* Used for group names.
63-
*/
64-
groups: number;
6553
}
6654

6755
/**
@@ -96,7 +84,7 @@ function config_process_directive(text: string, $: CreateConfigContext) {
9684
}
9785
log(1, 'Including: ' + contents);
9886
for (const node of $.include(contents)) {
99-
config_process_node(node, child_context($));
87+
config_process_node(child_context($), node);
10088
}
10189
break;
10290
// ##flags <rule> <flags>
@@ -147,8 +135,31 @@ function config_process_directive(text: string, $: CreateConfigContext) {
147135
}
148136
}
149137

150-
function config_process_expression(expression: Node, $: CreateConfigContext): [DefinitionPart[], boolean] {
151-
const _sub = child_context($);
138+
/**
139+
* Info about a rule that is used when parsing an expression
140+
*/
141+
interface RuleInfo {
142+
/**
143+
* The name of the rule being parsed.
144+
* Used for group names.
145+
*/
146+
name?: string;
147+
148+
/**
149+
* The number of groups in the rule.
150+
* Used for group names.
151+
*/
152+
groups: number;
153+
}
154+
155+
/**
156+
* An expression parsed by `config_process_expression`.
157+
* This might be change to an object later.
158+
*/
159+
type ParsedExpression = [pattern: DefinitionPart[], isAlternation: boolean];
160+
161+
function config_process_expression($: CreateConfigContext, expression: Node, rule: RuleInfo): ParsedExpression {
162+
const sub_context = child_context($);
152163

153164
let isAlternation = false;
154165

@@ -166,7 +177,7 @@ function config_process_expression(expression: Node, $: CreateConfigContext): [D
166177
if (term.kind == 'expression_continue' || term.kind == 'expression#0') {
167178
_log(2, 'Found expression_continue');
168179
let next;
169-
[next, isAlternation] = config_process_expression(term, _sub);
180+
[next, isAlternation] = config_process_expression(sub_context, term, rule);
170181
pattern.push(...next);
171182
continue;
172183
}
@@ -221,7 +232,7 @@ function config_process_expression(expression: Node, $: CreateConfigContext): [D
221232

222233
const type = typeForGroup[node.kind];
223234

224-
const [subPattern, isAlternation] = config_process_expression(inner, _sub);
235+
const [subPattern, isAlternation] = config_process_expression(sub_context, inner, rule);
225236

226237
// Check if subPattern contains another rule name, if so, no need to create a new group
227238
const existing = subPattern.length == 1 && subPattern[0].kind !== 'string' ? subPattern[0].kind : null;
@@ -230,7 +241,7 @@ function config_process_expression(expression: Node, $: CreateConfigContext): [D
230241
break;
231242
}
232243

233-
const subName = `${$.currentRule}#${$.groups++}`;
244+
const subName = `${rule.name}#${rule.groups++}`;
234245

235246
$.config.definitions.push({
236247
name: subName,
@@ -253,22 +264,22 @@ function config_process_expression(expression: Node, $: CreateConfigContext): [D
253264
return [pattern, isAlternation];
254265
}
255266

256-
function config_process_node(node: Node, $: CreateConfigContext) {
257-
const _sub = child_context($);
267+
function config_process_node($: CreateConfigContext, node: Node) {
268+
const sub_context = child_context($);
258269

259270
const log = logger($.log, { kind: node.kind, depth: $.depth });
260271

261272
log(3, `Processing ${node.kind} at ${node.line}:${node.column}`);
262273

263274
switch (node.kind) {
264275
case 'directive':
265-
config_process_directive(node.text, _sub);
276+
config_process_directive(node.text, sub_context);
266277
}
267278

268279
if (node.kind != 'rule') {
269280
// Recursively process child nodes
270281
for (const child of node.children || []) {
271-
config_process_node(child, _sub);
282+
config_process_node(sub_context, child);
272283
}
273284
return;
274285
}
@@ -283,10 +294,7 @@ function config_process_node(node: Node, $: CreateConfigContext) {
283294
return;
284295
}
285296

286-
$.currentRule = name;
287-
$.groups = 0;
288-
289-
const [pattern, isAlternation] = config_process_expression(expression, _sub);
297+
const [pattern, isAlternation] = config_process_expression(sub_context, expression, { name, groups: 0 });
290298

291299
/*
292300
Inline single-use literals
@@ -341,7 +349,7 @@ export function create_config(ast: Node[], options: CreateConfigOptions): Config
341349

342350
// Start processing from the root node
343351
for (const node of ast) {
344-
config_process_node(node, context);
352+
config_process_node(context, node);
345353
}
346354

347355
if (!config.root_nodes) {

src/parser.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,19 @@ export interface LogInfo {
4242
event?: 'attempt' | 'resolve' | 'fail' | 'start';
4343
}
4444

45-
export type Logger = (info: LogInfo) => void;
45+
export type Logger = (info: LogInfo | Issue) => void;
4646

47-
export function logger(log: Logger | undefined, options: Partial<LogInfo> & Pick<LogInfo, 'kind' | 'depth'>): (level: number, message: string, tags?: string[]) => void {
47+
export function logger(log: Logger | undefined, options: Partial<LogInfo> & Pick<LogInfo, 'kind' | 'depth'>) {
4848
if (!log) return () => {};
4949

50-
return function (level, message, tags = []) {
50+
function __log__(level: number, message: string, tags?: string[]): void;
51+
function __log__(issue: Issue): void;
52+
function __log__(level: number | Issue, message: string = '<unknown>', tags: string[] = []): void {
53+
if (typeof level == 'object') {
54+
log!(level);
55+
return;
56+
}
57+
5158
// parse tags into type and stage
5259

5360
let type: DefinitionType | undefined, stage: LogInfo['event'] | undefined;
@@ -67,8 +74,10 @@ export function logger(log: Logger | undefined, options: Partial<LogInfo> & Pick
6774
}
6875
}
6976

70-
log({ ...options, level, message, type, event: stage });
71-
};
77+
log!({ ...options, level, message, type, event: stage });
78+
}
79+
80+
return __log__;
7281
}
7382

7483
export interface ParseOptionsShared extends CommonConfig {

0 commit comments

Comments
 (0)