Skip to content

Commit 9aa7b28

Browse files
committed
feat: 🎸 add codegen for a rule
1 parent e3002d9 commit 9aa7b28

File tree

5 files changed

+149
-1
lines changed

5 files changed

+149
-1
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// import {Codegen} from '@jsonjoy.com/util/lib/codegen'
2+
// import {dynamicFunction} from '@jsonjoy.com/util/lib/codegen/dynamicFunction'
3+
// import {emitStringMatch} from '@jsonjoy.com/util/lib/codegen/util/helpers';
4+
// import {RegExpTerminalMatch, StringTerminalMatch} from '../matches';
5+
// import {scrub} from '../util';
6+
// import type {Grammar, Rule, MatchParser} from '../types';
7+
8+
// export class CodegenGrammar {
9+
// public static readonly compile = (grammar: Grammar): MatchParser => {
10+
// const codegen = new CodegenGrammar(grammar);
11+
// codegen.generate();
12+
// return codegen.compile();
13+
// };
14+
15+
// public readonly codegen: Codegen<MatchParser>;
16+
// protected readonly rules = new Map<string, MatchParser>();
17+
18+
// constructor(public readonly grammar: Grammar) {
19+
// this.codegen = new Codegen({
20+
// args: ['str', 'pos'],
21+
// });
22+
// }
23+
24+
// protected compileRule(name: string, rule: Rule): MatchParser {
25+
// const {codegen} = this;
26+
// const {match} = rule;
27+
// const codegenProduction = dynamicFunction<MatchParser>(codegen);
28+
// codegenProduction(`
29+
// const results = [];
30+
// ${match.map((m, i) => {
31+
// const dep = codegen.linkDependency(m);
32+
// return `
33+
// const r${i} = ${dep}(str, pos);
34+
// if (!r${i}) return;
35+
// pos = r${i}.end;
36+
// results.push(r${i});
37+
// `;
38+
// }).join('\n')}
39+
// return {kind: '${name}', pos, end: pos, children: results};
40+
// `);
41+
// return codegenProduction.compile();
42+
// }
43+
44+
// public generate() {
45+
// const {codegen, grammar} = this;
46+
// const {start, rules} = grammar;
47+
// // for (const [name, rule] of Object.entries(rules)) {
48+
// // const dep = codegen.linkDependency(this.compileRule(name, rule));
49+
// // this.rules.set(name, dep);
50+
// // }
51+
// // const start = this.rules.get(grammar.start);
52+
// // if (!start) throw new Error('INVALID_START_SYMBOL');
53+
// // codegen.return(`${start}(str, pos)`);
54+
// }
55+
56+
// public compile(): MatchParser {
57+
// return this.codegen.compile();
58+
// }
59+
// }

‎src/codegen/CodegenRule.ts‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import {Codegen} from '@jsonjoy.com/util/lib/codegen'
2+
import {RuleMatch} from '../matches';
3+
import type {MatchParser, Rule, RuleParser} from '../types';
4+
import {scrub} from '../util';
5+
6+
export class CodegenRule {
7+
public static readonly compile = (kind: string, rule: Rule, alternatives: MatchParser[]): RuleParser => {
8+
const codegen = new CodegenRule(kind, rule, alternatives);
9+
codegen.generate();
10+
return codegen.compile();
11+
};
12+
13+
public readonly codegen: Codegen<RuleParser>;
14+
15+
constructor(
16+
public readonly kind: string,
17+
public readonly rule: Rule,
18+
public readonly alternatives: MatchParser[],
19+
) {
20+
this.codegen = new Codegen({
21+
args: ['str', 'pos'],
22+
});
23+
}
24+
25+
public generate() {
26+
const {codegen, alternatives} = this;
27+
const deps: string[] = [];
28+
const dRM = codegen.linkDependency(RuleMatch);
29+
const kind = scrub(this.kind);
30+
for (const matcher of alternatives)
31+
deps.push(codegen.linkDependency(matcher));
32+
const rMatch = codegen.var(`${deps.join('(str, pos) || ')}(str, pos)`);
33+
codegen.if(`!${rMatch}`, () => {
34+
codegen.return('');
35+
});
36+
const rEnd = codegen.var();
37+
const rChildren = codegen.var();
38+
codegen.if(`${rMatch} instanceof Array`, () => {
39+
codegen.js(`${rEnd} = ${rMatch}[${rMatch}.length - 1].end;`);
40+
codegen.js(`${rChildren} = ${rMatch};`);
41+
}, () => {
42+
codegen.js(`${rEnd} = ${rMatch}.end;`);
43+
codegen.js(`${rChildren} = [${rMatch}];`);
44+
});
45+
codegen.return(`new ${dRM}('${kind}', pos, ${rEnd}, ${rChildren})`);
46+
}
47+
48+
public compile(): RuleParser {
49+
return this.codegen.compile();
50+
}
51+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import {Rule} from '../../types';
2+
import {CodegenRule} from '../CodegenRule';
3+
import {CodegenTerminal} from '../CodegenTerminal';
4+
5+
describe('CodegenRule', () => {
6+
test('can parse a simple rule', () => {
7+
const rule: Rule = {
8+
match: ['foo', 'bar'],
9+
};
10+
const foo = CodegenTerminal.compile('foo');
11+
const bar = CodegenTerminal.compile('bar');
12+
const parse = CodegenRule.compile('FooOrBar', rule, [foo, bar]);
13+
expect(parse('.foo', 1)).toEqual({
14+
kind: 'FooOrBar',
15+
pos: 1,
16+
end: 4,
17+
children: [{end: 4, kind: 'Text', pos: 1, text: 'foo'}],
18+
});
19+
expect(parse('bar', 0)).toEqual({
20+
kind: 'FooOrBar',
21+
pos: 0,
22+
end: 3,
23+
children: [{end: 3, kind: 'Text', pos: 0, text: 'bar'}],
24+
});
25+
});
26+
});

‎src/matches.ts‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,14 @@ export class StringTerminalMatch<Kind extends string = string> extends BaseMatch
2424

2525
export class RegExpTerminalMatch<Kind extends string = string> extends BaseMatch<Kind> {
2626
}
27+
28+
export class RuleMatch<Kind extends string = string> extends BaseMatch<Kind> {
29+
constructor (
30+
kind: Kind,
31+
pos: number,
32+
end: number,
33+
public readonly children: BaseMatch[],
34+
) {
35+
super(kind, pos, end);
36+
}
37+
}

‎src/types.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {Expr} from 'json-joy/lib/json-expression';
2+
import {RuleMatch} from './matches';
23

34
export type MaybeArray<T> = T | T[];
45

@@ -53,5 +54,5 @@ export interface MatchResult {
5354
export type ProductionResult = MatchResult[];
5455

5556
export type MatchParser = (str: string, pos: number) => MatchResult | undefined;
56-
5757
export type ProductionParser = (str: string, pos: number) => ProductionResult | undefined;
58+
export type RuleParser = (str: string, pos: number) => RuleMatch | undefined;

0 commit comments

Comments
 (0)