Skip to content

Commit 29bbf38

Browse files
committed
fix: three-stage grammar primitives
1 parent ce0c78c commit 29bbf38

24 files changed

+1166
-243
lines changed

packages/pgen/compile.ts

+45-7
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,62 @@
11
import * as t from '@babel/types';
22
import * as g from "./transform";
33

4-
const rtGlobal = '$';
5-
const astGlobal = '$$';
4+
const libGlobal = '$';
5+
const primGlobal = '$';
6+
const astGlobal = '$ast';
67

78
export const compile = (node: g.Grammar): t.File => {
89
const imports: t.Statement[] = [
9-
emitNsImport(rtGlobal, '@langtools/pgen/runtime'),
10+
emitNsImport(libGlobal, '@langtools/runtime'),
1011
];
1112

1213
const definedRules = new Set<string>();
1314
const isDefined = (name: string) => definedRules.has(name);
1415

1516
const stmts: t.Statement[] = [];
1617
const ast: t.Statement[] = [];
18+
const names: string[] = [];
1719
for (const rule of node.rules) {
18-
const { expr, type } = compileRule(rule)({ isDefined });
20+
const { expr, type, name } = compileRule(rule)({ isDefined });
1921
stmts.push(expr);
2022
ast.push(type);
23+
if (name) names.push(name);
2124
definedRules.add(rule.name);
2225
}
2326

27+
// const exports = t.returnStatement(t.objectExpression(
28+
// names.map(name => t.objectProperty(
29+
// t.identifier(name),
30+
// emitCall('compile', [
31+
// t.identifier(name),
32+
// t.arrowFunctionExpression([], t.identifier('space')),
33+
// ]),
34+
// false,
35+
// true,
36+
// ))
37+
// ));
38+
2439
return t.file(t.program([
2540
...imports,
2641
t.exportNamedDeclaration(t.tsModuleDeclaration(
2742
t.identifier(astGlobal),
2843
t.tsModuleBlock(ast),
2944
)),
3045
...stmts,
46+
// t.exportNamedDeclaration(t.variableDeclaration("const", [
47+
// t.variableDeclarator(t.identifier("getParser"), t.arrowFunctionExpression(
48+
// [withType(
49+
// t.identifier(primGlobal),
50+
// t.tsTypeReference(
51+
// t.tsQualifiedName(t.identifier(libGlobal), t.identifier('Algebra')),
52+
// )
53+
// )],
54+
// t.blockStatement([
55+
// ...stmts,
56+
// exports,
57+
// ]),
58+
// ))
59+
// ])),
3160
]));
3261
};
3362

@@ -37,6 +66,7 @@ type Context = {
3766
type Compiler<T> = (ctx: Context) => T;
3867

3968
const compileRule = ({ name, formals, body }: g.Rule): Compiler<{
69+
name: string | undefined,
4070
expr: t.Statement,
4171
type: t.Statement,
4272
}> => ctx => {
@@ -49,6 +79,7 @@ const compileRule = ({ name, formals, body }: g.Rule): Compiler<{
4979
const bodyCode = compileFormals(name, formals, exprCode);
5080

5181
return {
82+
name: formals.length > 0 ? undefined : name,
5283
expr: t.exportNamedDeclaration(t.variableDeclaration('const', [
5384
t.variableDeclarator(nameCode, bodyCode)
5485
])),
@@ -116,6 +147,8 @@ const compileExpr = (node: g.Expr): Compiler<ExprWithType> => ctx => {
116147
return compileStar(node)(ctx);
117148
case 'Stringify':
118149
return compileStringify(node)(ctx);
150+
case 'Lex':
151+
return compileLex(node)(ctx);
119152
case 'Alt':
120153
return compileAlt(node)(ctx);
121154
case 'Class':
@@ -156,7 +189,7 @@ const compileLocated = (node: g.Located): Compiler<ExprWithType> => (ctx) => {
156189
const child = compileExpr(node.child)(ctx);
157190
const body = emitCall('loc', [child.expr]);
158191
const type = t.tsTypeReference(
159-
t.tsQualifiedName(t.identifier(rtGlobal), t.identifier('Located')),
192+
t.tsQualifiedName(t.identifier(libGlobal), t.identifier('Located')),
160193
t.tsTypeParameterInstantiation([child.type]),
161194
);
162195
return ewt(body, type);
@@ -224,6 +257,11 @@ const compileStringify = (node: g.Stringify): Compiler<ExprWithType> => ctx => {
224257
return ewt(emitCall('stry', [expr]), t.tsStringKeyword());
225258
};
226259

260+
const compileLex = (node: g.Lex): Compiler<ExprWithType> => ctx => {
261+
const { expr, type } = compileExpr(node.expr)(ctx);
262+
return ewt(emitCall('lex', [expr]), type);
263+
};
264+
227265
const compilePlus = (node: g.Plus): Compiler<ExprWithType> => ctx => {
228266
const { expr, type } = compileExpr(node.expr)(ctx);
229267
const array = t.tsTypeOperator(t.tsArrayType(type))
@@ -357,7 +395,7 @@ const emitCall = (name: string, args: t.Expression[], params?: t.TSType[]): t.Ex
357395
};
358396

359397
const emitPrim = (name: string): t.Expression => {
360-
return t.memberExpression(t.identifier(rtGlobal), t.identifier(name));
398+
return t.memberExpression(t.identifier(primGlobal), t.identifier(name));
361399
};
362400

363401
const emitParserType = (name: string, formals: readonly string[], notQualified: boolean = false): t.TSType => {
@@ -367,7 +405,7 @@ const emitParserType = (name: string, formals: readonly string[], notQualified:
367405
return t.tsTypeReference(t.identifier(formal));
368406
}));
369407
return t.tsTypeReference(
370-
t.tsQualifiedName(t.identifier(rtGlobal), t.identifier('Parser')),
408+
t.tsQualifiedName(t.identifier(libGlobal), t.identifier('Parser')),
371409
t.tsTypeParameterInstantiation([
372410
t.tsTypeReference(
373411
notQualified

packages/pgen/transform.ts

+17-32
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export type Expr =
2525
| LookNeg
2626
| LookPos
2727
| Stringify
28+
| Lex
2829
| Star
2930
| Plus
3031
| Optional
@@ -53,6 +54,8 @@ export type LookPos = { readonly $: "LookPos", readonly expr: Expr }
5354
export const LookPos = (expr: Expr): LookPos => ({ $: "LookPos", expr });
5455
export type Stringify = { readonly $: 'Stringify', readonly expr: Expr }
5556
export const Stringify = (expr: Expr): Stringify => ({ $: "Stringify", expr });
57+
export type Lex = { readonly $: 'Lex', readonly expr: Expr }
58+
export const Lex = (expr: Expr): Lex => ({ $: "Lex", expr });
5659
export type Star = { readonly $: 'Star', readonly expr: Expr }
5760
export const Star = (expr: Expr): Star => ({ $: "Star", expr });
5861
export type Plus = { readonly $: 'Plus', readonly expr: Expr }
@@ -92,7 +95,6 @@ type SkipType =
9295
| 'keep-space' // has space rule, compile without skipping
9396

9497
type Context = {
95-
skip: SkipType;
9698
formals: Set<string>;
9799
}
98100
type Transform<T> = (ctx: Context) => T
@@ -114,22 +116,15 @@ const renameIfKeyword = (name: string): string => {
114116
};
115117

116118
export const transform = ({ rules }: g.Grammar): Grammar => {
117-
const useSkipper = Boolean(rules.find(({ name }) => name === 'space'));
118-
const skipTypes: SkipType[] = useSkipper ? ['skip-space', 'keep-space'] : ['no-space'];
119-
120119
return Grammar(rules.flatMap((rule) => {
121-
return skipTypes.map(skip => {
122-
return transformRule(skip, rule);
123-
});
120+
return transformRule(rule);
124121
}));
125122
};
126123

127124
const transformRule = (
128-
skip: SkipType,
129125
{ name, formals, body }: g.Rule
130126
) => {
131-
const renamed = renameIfKeyword(name);
132-
const fullName = skip === 'keep-space' ? `${renamed}$noSkip` : renamed;
127+
const fullName = renameIfKeyword(name);
133128

134129
const allFormals = formals ? [formals.head, ...formals.tail] : [];
135130
const formalsSet: Set<string> = new Set(allFormals);
@@ -138,14 +133,12 @@ const transformRule = (
138133

139134
if (isAstRule) {
140135
const expr = transformAstAlt(body)({
141-
skip,
142136
formals: formalsSet,
143137
});
144138

145139
return Rule(fullName, allFormals, Located(Field('$', Pure(name), expr)));
146140
} else {
147141
const expr = transformAlt(body)({
148-
skip,
149142
formals: formalsSet,
150143
});
151144

@@ -207,24 +200,20 @@ const transformExpr = (node: gExpr): Transform<Expr> => (ctx) => {
207200
}
208201
};
209202

210-
const transformAny = (_node: g.Any) => {
211-
return spaced(Any);
203+
const transformAny = (_node: g.Any): Transform<Expr> => () => {
204+
return Any;
212205
};
213206

214-
const transformClass = ({ insensitive, negated, seqs }: g.Class) => {
215-
return spaced(Class(seqs, negated === '^', insensitive === 'i'));
207+
const transformClass = ({ insensitive, negated, seqs }: g.Class): Transform<Expr> => () => {
208+
return Class(seqs, negated === '^', insensitive === 'i');
216209
};
217210

218-
const transformTerminal = ({ value }: g.Terminal) => {
219-
return spaced(Terminal(value));
211+
const transformTerminal = ({ value }: g.Terminal): Transform<Expr> => () => {
212+
return Terminal(value);
220213
};
221214

222215
const transformApply = (node: g.Apply): Transform<Expr> => (ctx) => {
223-
const renamed = renameIfKeyword(node.name);
224-
// FIXME: apply in noskip context should use noskip formals
225-
const newName = ctx.skip === 'keep-space' && !ctx.formals.has(node.name)
226-
? `${renamed}$noSkip`
227-
: renamed;
216+
const newName = renameIfKeyword(node.name);
228217

229218
const allParams = node.params ? [node.params.head, ...node.params.tail] : [];
230219

@@ -240,7 +229,7 @@ const transformAlt = (node: g.Alt): Transform<Expr> => (ctx) => {
240229
};
241230

242231
const transformIter = ({ prefix, expr, suffix }: g.Iter): Transform<Expr> => {
243-
return prefix.reduce(
232+
return prefix.reduceRight(
244233
(acc, prefix) => transformPrefix(prefix, acc),
245234
(ctx: Context) => transformSuffix(suffix, transformExpr(expr)(ctx)),
246235
);
@@ -255,10 +244,7 @@ const transformPrefix = (prefix: "!" | "&" | "#" | "$", expr: Transform<Expr>):
255244
case '$':
256245
return Stringify(expr(ctx));
257246
case '#':
258-
return spaced(expr({
259-
skip: ctx.skip === 'skip-space' ? 'keep-space' : ctx.skip,
260-
formals: ctx.formals,
261-
}))(ctx);
247+
return Lex(expr(ctx));
262248
default:
263249
throw new Error('Unexpected prefix');
264250
}
@@ -292,7 +278,6 @@ const transformSeq = ({ exprs }: g.Seq): Transform<Expr> => (ctx) => {
292278
}
293279

294280
const opts: Context = {
295-
skip: ctx.skip,
296281
formals: ctx.formals,
297282
};
298283

@@ -341,6 +326,6 @@ const transformSeq = ({ exprs }: g.Seq): Transform<Expr> => (ctx) => {
341326
return e.reduceRight((prev, expr) => Ap(expr, prev, 'r'));
342327
};
343328

344-
const spaced = (node: Expr): Transform<Expr> => (ctx) => {
345-
return ctx.skip === 'skip-space' ? Ap(node, Star(Call("space$noSkip", [])), 'l') : node;
346-
};
329+
// const spaced = (node: Expr): Transform<Expr> => (ctx) => {
330+
// return Ap(node, Star(Call("space$noSkip", [])), 'l');
331+
// };

packages/runtime/index.ts

+31-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,31 @@
1-
export * from "./runtime";
1+
import { Context, createContext, getSuccess, isFailure, Parser } from "./runtime";
2+
3+
export { Parser, Located, alt, any, ap, app, compile, debug, eof, eps, field, left, lex, loc, lookNeg, lookPos, opt, plus, pure, ref, regex, right, rule, sat, seq, star, str, stry, terminal, where, withLoc } from './spaced';
4+
export { Loc, LocEmpty, LocRange } from './located';
5+
6+
export type ParseResult<T> = ParseResultSuccess<T> | ParseResultError
7+
export type ParseResultError = {
8+
readonly $: "error";
9+
readonly error: {
10+
readonly position: number;
11+
readonly expected: ReadonlySet<string>;
12+
};
13+
}
14+
export type ParseResultSuccess<T> = {
15+
readonly $: "success";
16+
readonly value: T;
17+
}
18+
19+
export const parse = <T>(
20+
grammar: Parser<T>,
21+
) => (text: string): ParseResult<T> => {
22+
const ctx: Context = createContext(text);
23+
24+
const result = grammar(ctx);
25+
26+
if (isFailure(result)) {
27+
return { $: 'error', error: ctx.getError() };
28+
} else {
29+
return { $: 'success', value: getSuccess(result) };
30+
}
31+
};

packages/runtime/lib/index.d.ts

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,17 @@
1-
export * from "./runtime";
1+
import { Parser } from "./runtime";
2+
export { Parser, Located, alt, any, ap, app, compile, debug, eof, eps, field, left, lex, loc, lookNeg, lookPos, opt, plus, pure, ref, regex, right, rule, sat, seq, star, str, stry, terminal, where, withLoc } from './spaced';
3+
export { Loc, LocEmpty, LocRange } from './located';
4+
export type ParseResult<T> = ParseResultSuccess<T> | ParseResultError;
5+
export type ParseResultError = {
6+
readonly $: "error";
7+
readonly error: {
8+
readonly position: number;
9+
readonly expected: ReadonlySet<string>;
10+
};
11+
};
12+
export type ParseResultSuccess<T> = {
13+
readonly $: "success";
14+
readonly value: T;
15+
};
16+
export declare const parse: <T>(grammar: Parser<T>) => (text: string) => ParseResult<T>;
217
//# sourceMappingURL=index.d.ts.map

packages/runtime/lib/index.d.ts.map

+1-1
Original file line numberDiff line numberDiff line change

packages/runtime/lib/index.js

+42-14
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,45 @@
11
"use strict";
2-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3-
if (k2 === undefined) k2 = k;
4-
var desc = Object.getOwnPropertyDescriptor(m, k);
5-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6-
desc = { enumerable: true, get: function() { return m[k]; } };
2+
Object.defineProperty(exports, "__esModule", { value: true });
3+
exports.parse = exports.withLoc = exports.where = exports.terminal = exports.stry = exports.str = exports.star = exports.seq = exports.sat = exports.rule = exports.right = exports.regex = exports.ref = exports.pure = exports.plus = exports.opt = exports.lookPos = exports.lookNeg = exports.loc = exports.lex = exports.left = exports.field = exports.eps = exports.eof = exports.debug = exports.compile = exports.app = exports.ap = exports.any = exports.alt = void 0;
4+
const runtime_1 = require("./runtime");
5+
var spaced_1 = require("./spaced");
6+
Object.defineProperty(exports, "alt", { enumerable: true, get: function () { return spaced_1.alt; } });
7+
Object.defineProperty(exports, "any", { enumerable: true, get: function () { return spaced_1.any; } });
8+
Object.defineProperty(exports, "ap", { enumerable: true, get: function () { return spaced_1.ap; } });
9+
Object.defineProperty(exports, "app", { enumerable: true, get: function () { return spaced_1.app; } });
10+
Object.defineProperty(exports, "compile", { enumerable: true, get: function () { return spaced_1.compile; } });
11+
Object.defineProperty(exports, "debug", { enumerable: true, get: function () { return spaced_1.debug; } });
12+
Object.defineProperty(exports, "eof", { enumerable: true, get: function () { return spaced_1.eof; } });
13+
Object.defineProperty(exports, "eps", { enumerable: true, get: function () { return spaced_1.eps; } });
14+
Object.defineProperty(exports, "field", { enumerable: true, get: function () { return spaced_1.field; } });
15+
Object.defineProperty(exports, "left", { enumerable: true, get: function () { return spaced_1.left; } });
16+
Object.defineProperty(exports, "lex", { enumerable: true, get: function () { return spaced_1.lex; } });
17+
Object.defineProperty(exports, "loc", { enumerable: true, get: function () { return spaced_1.loc; } });
18+
Object.defineProperty(exports, "lookNeg", { enumerable: true, get: function () { return spaced_1.lookNeg; } });
19+
Object.defineProperty(exports, "lookPos", { enumerable: true, get: function () { return spaced_1.lookPos; } });
20+
Object.defineProperty(exports, "opt", { enumerable: true, get: function () { return spaced_1.opt; } });
21+
Object.defineProperty(exports, "plus", { enumerable: true, get: function () { return spaced_1.plus; } });
22+
Object.defineProperty(exports, "pure", { enumerable: true, get: function () { return spaced_1.pure; } });
23+
Object.defineProperty(exports, "ref", { enumerable: true, get: function () { return spaced_1.ref; } });
24+
Object.defineProperty(exports, "regex", { enumerable: true, get: function () { return spaced_1.regex; } });
25+
Object.defineProperty(exports, "right", { enumerable: true, get: function () { return spaced_1.right; } });
26+
Object.defineProperty(exports, "rule", { enumerable: true, get: function () { return spaced_1.rule; } });
27+
Object.defineProperty(exports, "sat", { enumerable: true, get: function () { return spaced_1.sat; } });
28+
Object.defineProperty(exports, "seq", { enumerable: true, get: function () { return spaced_1.seq; } });
29+
Object.defineProperty(exports, "star", { enumerable: true, get: function () { return spaced_1.star; } });
30+
Object.defineProperty(exports, "str", { enumerable: true, get: function () { return spaced_1.str; } });
31+
Object.defineProperty(exports, "stry", { enumerable: true, get: function () { return spaced_1.stry; } });
32+
Object.defineProperty(exports, "terminal", { enumerable: true, get: function () { return spaced_1.terminal; } });
33+
Object.defineProperty(exports, "where", { enumerable: true, get: function () { return spaced_1.where; } });
34+
Object.defineProperty(exports, "withLoc", { enumerable: true, get: function () { return spaced_1.withLoc; } });
35+
const parse = (grammar) => (text) => {
36+
const ctx = (0, runtime_1.createContext)(text);
37+
const result = grammar(ctx);
38+
if ((0, runtime_1.isFailure)(result)) {
39+
return { $: 'error', error: ctx.getError() };
40+
}
41+
else {
42+
return { $: 'success', value: (0, runtime_1.getSuccess)(result) };
743
}
8-
Object.defineProperty(o, k2, desc);
9-
}) : (function(o, m, k, k2) {
10-
if (k2 === undefined) k2 = k;
11-
o[k2] = m[k];
12-
}));
13-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
14-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
1544
};
16-
Object.defineProperty(exports, "__esModule", { value: true });
17-
__exportStar(require("./runtime"), exports);
45+
exports.parse = parse;

0 commit comments

Comments
 (0)