Skip to content

Commit

Permalink
feat(parse): update grammar DSL, hoist xforms
Browse files Browse the repository at this point in the history
- allow esc sequences in grammar string literals
- expose various preset parser for re-use in grammar DSL
- rename xfHoist => hoistResult
- add new xfHoist to hoist entire child node
- add doc strings
  • Loading branch information
postspectacular committed Apr 21, 2020
1 parent 1d0f4c4 commit 861e7f3
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 21 deletions.
2 changes: 1 addition & 1 deletion packages/parse/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export interface GrammarOpts {
optimize: boolean;
}

export type Rules = IObjectOf<DynamicParser<string>>;
export type Rules = IObjectOf<Parser<string>>;

export type RuleTransforms = IObjectOf<ScopeTransform<string>>;

Expand Down
57 changes: 39 additions & 18 deletions packages/parse/src/grammar.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { DEFAULT, defmulti } from "@thi.ng/defmulti";
import { illegalArgs, unsupported } from "@thi.ng/errors";
import type {
DynamicParser,
GrammarOpts,
Language,
Parser,
Expand All @@ -15,22 +16,24 @@ import { oneOrMore, repeat, zeroOrMore } from "./combinators/repeat";
import { seq } from "./combinators/seq";
import { xform } from "./combinators/xform";
import { defContext } from "./context";
import { ALPHA_NUM } from "./presets/alpha";
import { ALPHA, ALPHA_NUM } from "./presets/alpha";
import { DIGIT } from "./presets/digits";
import { ESC, UNICODE } from "./presets/escape";
import { UINT } from "./presets/numbers";
import { WS0, WS1 } from "./presets/whitespace";
import { HEX_DIGIT } from "./presets/hex";
import { FLOAT, INT, UINT } from "./presets/numbers";
import { WS, WS0, WS1 } from "./presets/whitespace";
import { always } from "./prims/always";
import { lit, litD } from "./prims/lit";
import { noneOf, noneOfP } from "./prims/none-of";
import { noneOf } from "./prims/none-of";
import { oneOf } from "./prims/one-of";
import { range } from "./prims/range";
import { string, stringD, stringOf } from "./prims/string";
import { string, stringD } from "./prims/string";
import { collect, xfCollect } from "./xform/collect";
import { xfDiscard } from "./xform/discard";
import { hoist, xfHoist } from "./xform/hoist";
import { hoistResult, xfHoist, xfHoistResult } from "./xform/hoist";
import { join, xfJoin } from "./xform/join";
import { xfFloat, xfInt } from "./xform/number";
import { print } from "./xform/print";
import { always } from "./prims/always";
import { withID } from "./xform/with-id";

const apos = litD("'");
Expand All @@ -45,7 +48,7 @@ const REPEAT = maybe(
[
litD("{"),
UINT,
maybe(hoist(seq([litD(","), UINT]))),
maybe(hoistResult(seq([litD(","), UINT]))),
litD("}"),
],
"repeatN"
Expand All @@ -68,9 +71,11 @@ const CHAR_SEL = seq(
"charSel"
);

const LIT = hoist(seq([apos, CHAR_OR_ESC, apos], "char"));
const LIT = hoistResult(seq([apos, CHAR_OR_ESC, apos], "char"));

const STRING = hoist(seq([quote, stringOf(noneOfP('"')), quote], "string"));
const STRING = join(
seq([quote, zeroOrMore(alt([UNICODE, ESC, noneOf('"')])), quote], "string")
);

const SYM = join(oneOrMore(alt([ALPHA_NUM, oneOf(".-_$")]), "sym"));

Expand All @@ -91,7 +96,7 @@ const ALT = seq(
"alt"
);

const RULE_XF = hoist(seq([stringD("=>"), WS1, SYM, WS1], "xform"));
const RULE_XF = hoistResult(seq([stringD("=>"), WS1, SYM, WS1], "xform"));

const RULE = seq(
[
Expand Down Expand Up @@ -129,7 +134,7 @@ compile.addAll({
);
for (let r of rules) {
const id = first(r).result;
lang.rules[id].set(compile(r, lang, opts));
(<DynamicParser<string>>lang.rules[id]).set(compile(r, lang, opts));
}
return lang;
},
Expand Down Expand Up @@ -237,19 +242,35 @@ export const defGrammar = (
float: xfFloat,
hex: xfInt(16),
hoist: xfHoist,
hoistR: xfHoistResult,
int: xfInt(10),
join: xfJoin,
...env,
};
const ctx = defContext(rules);
const result = (opts.debug ? print(GRAMMAR) : GRAMMAR)(ctx);
if (result) {
return <Language>(
compile(
ctx.root,
{ env, grammar: ctx, rules: {} },
<GrammarOpts>opts
)
return <Language>compile(
ctx.root,
{
env,
grammar: ctx,
rules: {
ALPHA_NUM,
ALPHA,
DIGIT,
ESC,
FLOAT,
HEX_DIGIT,
INT,
STRING,
UNICODE,
WS,
WS0,
WS1,
},
},
<GrammarOpts>opts
);
}
};
22 changes: 20 additions & 2 deletions packages/parse/src/xform/hoist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,22 @@ import { Parser, ScopeTransform } from "../api";
import { xform } from "../combinators/xform";

/**
* Moves the result of first child node to this node, then discards all
* children. Also see {@link hoist}.
* Replace AST node with its first child node. Also see {@link hoist}.
*
* @param scope
*/
export const xfHoist: ScopeTransform<any> = (scope) => {
Object.assign(scope, scope!.children![0]);
return scope;
};

/**
* Moves the result of first child node to this node, then discards all
* children. Also see {@link hoistR}.
*
* @param scope
*/
export const xfHoistResult: ScopeTransform<any> = (scope) => {
scope!.result = scope!.children![0].result;
scope!.children = null;
return scope;
Expand All @@ -19,3 +29,11 @@ export const xfHoist: ScopeTransform<any> = (scope) => {
* @param parser
*/
export const hoist = <T>(parser: Parser<T>) => xform(parser, xfHoist);

/**
* Syntax sugar for `xform(parser, xfHoistR)`.
*
* @param parser
*/
export const hoistResult = <T>(parser: Parser<T>) =>
xform(parser, xfHoistResult);

0 comments on commit 861e7f3

Please sign in to comment.