Skip to content

Commit

Permalink
Default Arguments (#83)
Browse files Browse the repository at this point in the history
* Update argspec to handle default arguments better
* Store default arguments in `_renderInfo` for each arg
  • Loading branch information
siefkenj committed Feb 20, 2024
1 parent e3a07de commit 898ac25
Show file tree
Hide file tree
Showing 16 changed files with 225 additions and 86 deletions.
2 changes: 1 addition & 1 deletion examples/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"compilerOptions": { "rootDir": "./" },
"include": ["./**/*.ts"],
"extends": "../tsconfig.build.json",
"extends": "../tsconfig.build.json"
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"test:packages-esm": "cd test/esm && npm install && npm run test",
"test:packages-cjs": "cd test/cjs && npm install && npm run test",
"test:packages-install": "cd test && npx vite-node make-packages.ts",
"prettier": "prettier \"**/*.ts\" \"**/*.json\" --write",
"prettier": "prettier \"**/*.ts\" \"**/*.json\" --ignore-path .gitignore --write",
"eslint": "eslint \"**/*.ts\" --ignore-pattern dist"
},
"prettier": {
Expand Down
3 changes: 3 additions & 0 deletions packages/unified-latex-types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ export * from "./libs/ast-types";
export * from "./libs/type-guard";
export * from "./libs/info-specs";

// Export something for importing packages
export default {};

// NOTE: The docstring comment must be the last item in the index.ts file!
/**
* ## What is this?
Expand Down
5 changes: 3 additions & 2 deletions packages/unified-latex-types/libs/ast-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ export interface GenericNode {
// Abstract nodes
interface BaseNode {
type: string;
_renderInfo?: (MacroInfo["renderInfo"] | EnvInfo["renderInfo"]) &
Record<string, unknown>;
_renderInfo?: (MacroInfo["renderInfo"] | EnvInfo["renderInfo"]) & {
defaultArg?: string;
} & Record<string, unknown>;
position?: {
start: { offset: number; line: number; column: number };
end: { offset: number; line: number; column: number };
Expand Down
6 changes: 4 additions & 2 deletions packages/unified-latex-types/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@
"exports": {
".": {
"prebuilt": "./dist/index.js",
"import": "./index.ts"
"import": "./index.ts",
"require": "./dist/index.cjs"
},
"./*js": "./dist/*js",
"./*": {
"prebuilt": "./dist/*.js",
"import": "./*.ts"
"import": "./*.ts",
"require": "./dist/*.cjs"
}
},
"scripts": {
Expand Down
24 changes: 21 additions & 3 deletions packages/unified-latex-util-argspec/libs/argspec-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ export function printRaw(
}

const decorators = getDecorators(node);
const defaultArg = (node as ArgSpec.DefaultArgument).defaultArg
? printRaw((node as ArgSpec.DefaultArgument).defaultArg!)
: "";
const defaultArg = printDefaultArg(
"defaultArg" in node ? node.defaultArg : undefined,
// `embellishment`s are the only spec that can have multiple default args
node.type === "embellishment"
);
let spec = decorators;

const type = node.type;
Expand Down Expand Up @@ -101,3 +103,19 @@ export function parse(str = ""): ArgSpec.Node[] {
parseCache[str] = parseCache[str] || PegParser.parse(str);
return parseCache[str];
}

function printDefaultArg(
args: string | string[] | undefined,
multipleArgs: boolean
): string {
if (!args) {
return "";
}
if (typeof args === "string") {
args = [args];
}
if (!multipleArgs) {
return `{${args.join("")}}`;
}
return `{${args.map((a) => `{${a}}`).join("")}}`;
}
7 changes: 5 additions & 2 deletions packages/unified-latex-util-argspec/libs/argspec-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ export interface LeadingWhitespace {
noLeadingWhitespace: boolean | undefined;
}
export interface DefaultArgument {
defaultArg?: Group;
defaultArg?: string;
}
export interface DefaultArguments {
defaultArg?: string[];
}
interface Verbatim extends Arg {
type: "verbatim";
Expand All @@ -27,7 +30,7 @@ interface OptionalToken extends LeadingWhitespace, AstNode {
type: "optionalToken";
token: string;
}
export interface Embellishment extends DefaultArgument, AstNode {
export interface Embellishment extends DefaultArguments, AstNode {
type: "embellishment";
embellishmentTokens: string[];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,30 +27,7 @@ exports[`unified-latex-util-argspec > parses xparse argument specification strin
[
{
"closeBrace": "]",
"defaultArg": {
"content": [
"n",
"e",
"s",
"t",
"e",
"d",
{
"content": [
"d",
"e",
"f",
"a",
"u",
"l",
"t",
"s",
],
"type": "group",
},
],
"type": "group",
},
"defaultArg": "nested{defaults}",
"openBrace": "[",
"type": "optional",
},
Expand All @@ -61,22 +38,7 @@ exports[`unified-latex-util-argspec > parses xparse argument specification strin
[
{
"closeBrace": "]",
"defaultArg": {
"content": [
"s",
"o",
"m",
"e",
"d",
"e",
"f",
"a",
"u",
"l",
"t",
],
"type": "group",
},
"defaultArg": "somedefault",
"openBrace": "[",
"type": "optional",
},
Expand All @@ -93,6 +55,17 @@ exports[`unified-latex-util-argspec > parses xparse argument specification strin
]
`;

exports[`unified-latex-util-argspec > parses xparse argument specification string "R\\a1{default}" 1`] = `
[
{
"closeBrace": "1",
"defaultArg": "default",
"openBrace": "\\\\a",
"type": "mandatory",
},
]
`;

exports[`unified-latex-util-argspec > parses xparse argument specification string "d++ D--{def}" 1`] = `
[
{
Expand All @@ -102,14 +75,7 @@ exports[`unified-latex-util-argspec > parses xparse argument specification strin
},
{
"closeBrace": "-",
"defaultArg": {
"content": [
"d",
"e",
"f",
],
"type": "group",
},
"defaultArg": "def",
"openBrace": "-",
"type": "optional",
},
Expand Down Expand Up @@ -219,6 +185,16 @@ exports[`unified-latex-util-argspec > parses xparse argument specification strin
]
`;

exports[`unified-latex-util-argspec > parses xparse argument specification string "r\\abc\\d" 1`] = `
[
{
"closeBrace": "\\\\d",
"openBrace": "\\\\abc",
"type": "mandatory",
},
]
`;

exports[`unified-latex-util-argspec > parses xparse argument specification string "s m" 1`] = `
[
{
Expand Down
92 changes: 88 additions & 4 deletions packages/unified-latex-util-argspec/tests/argspec-parser.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, it, expect } from "vitest";
import { VFile } from "unified-lint-rule/lib";
import util from "util";
import * as argspecParser from "..";
import * as argspecParser from "../index";

/* eslint-env jest */

Expand All @@ -18,6 +18,7 @@ function removeWhitespace(x: string) {
describe("unified-latex-util-argspec", () => {
let value: string | undefined;
let file: VFile | undefined;
let ast: ReturnType<typeof argspecParser.parse>;

const SPEC_STRINGS = [
"",
Expand All @@ -35,6 +36,8 @@ describe("unified-latex-util-argspec", () => {
"u{xx;}",
"u;",
"u{ }",
"r\\abc\\d",
"R\\a1{default}",
];

for (const spec of SPEC_STRINGS) {
Expand All @@ -45,17 +48,98 @@ describe("unified-latex-util-argspec", () => {
});
}

it("Default args need not be enclosed in braces", () => {
ast = argspecParser.parse("Ox");
expect(ast).toEqual([
{
closeBrace: "]",
defaultArg: "x",
openBrace: "[",
type: "optional",
},
]);

ast = argspecParser.parse("D(ab");
expect(ast).toEqual([
{
closeBrace: "a",
defaultArg: "b",
openBrace: "(",
type: "optional",
},
]);
});

it("Embellishment tokens can be single characters specified without a group", () => {
ast = argspecParser.parse("e^");
expect(ast).toEqual([
{
type: "embellishment",
embellishmentTokens: ["^"],
},
]);

// Macros count as a single token
ast = argspecParser.parse("e\\foo");
expect(ast).toEqual([
{
type: "embellishment",
embellishmentTokens: ["\\foo"],
},
]);

ast = argspecParser.parse("Ex{}");
expect(ast).toEqual([
{
type: "embellishment",
embellishmentTokens: ["x"],
defaultArg: [],
},
]);
});

it("Embellishment tokens ignore whitespace", () => {
ast = argspecParser.parse("e { ^ }");
expect(ast).toEqual([
{
type: "embellishment",
embellishmentTokens: ["^"],
},
]);
});

it("Embellishment default args can be a mix of tokens and groups", () => {
ast = argspecParser.parse("E{\\token^}{{D1}2}");
expect(ast).toEqual([
{
defaultArg: ["D1", "2"],
embellishmentTokens: ["\\token", "^"],
type: "embellishment",
},
]);
});

it("Embellishments always return a string", () => {
let ast = argspecParser.parse("e{{{x}}y{z}}");
ast = argspecParser.parse("e{{x}y{z}}");
expect(ast).toEqual([
{ type: "embellishment", embellishmentTokens: ["x", "y", "z"] },
]);
ast = argspecParser.parse("E{{{x}}y{z}}{}");
ast = argspecParser.parse("E{{x}y{z}}{}");
expect(ast).toEqual([
{
type: "embellishment",
embellishmentTokens: ["x", "y", "z"],
defaultArg: [],
},
]);
});
it("Embellishments keep default args", () => {
ast = argspecParser.parse("E{{x}y{z}}{{One}{Two}{Three}}");
expect(ast).toEqual([
{
type: "embellishment",
embellishmentTokens: ["x", "y", "z"],
defaultArg: { type: "group", content: [] },
defaultArg: ["One", "Two", "Three"],
},
]);
});
Expand Down
21 changes: 17 additions & 4 deletions packages/unified-latex-util-arguments/libs/gobble-arguments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
parse as parseArgspec,
} from "@unified-latex/unified-latex-util-argspec";
import { gobbleSingleArgument } from "./gobble-single-argument";
import { updateRenderInfo } from "@unified-latex/unified-latex-util-render-info";

/**
* Gobbles an argument of whose type is specified
Expand Down Expand Up @@ -38,7 +39,12 @@ export function gobbleArguments(
// we need to keep gobbling arguments until we've got them all.
const remainingTokens = new Set(spec.embellishmentTokens);
const argForToken = Object.fromEntries(
spec.embellishmentTokens.map((t) => [t, emptyArg()])
spec.embellishmentTokens.map((t, i) => {
// For empty arguments, we also store their default.
const defaultArg =
"defaultArg" in spec ? spec.defaultArg?.[i] : undefined;
return [t, emptyArg(defaultArg)];
})
);

let { argument, nodesRemoved: removed } = gobbleSingleArgument(
Expand Down Expand Up @@ -66,7 +72,10 @@ export function gobbleArguments(
spec,
startPos
);
args.push(argument || emptyArg());
// For empty arguments, we also store their default.
const defaultArg =
"defaultArg" in spec ? spec.defaultArg : undefined;
args.push(argument || emptyArg(defaultArg));
nodesRemoved += removed;
}
}
Expand All @@ -87,6 +96,10 @@ function embellishmentSpec(tokens: Set<string>): ArgSpec.Embellishment {
/**
* Create an empty argument.
*/
function emptyArg(): Ast.Argument {
return arg([], { openMark: "", closeMark: "" });
function emptyArg(defaultArg?: string): Ast.Argument {
const ret = arg([], { openMark: "", closeMark: "" });
if (defaultArg != null) {
updateRenderInfo(ret, { defaultArg });
}
return ret;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { describe, it, expect } from "vitest";
import util from "util";
import * as Ast from "@unified-latex/unified-latex-types";
import { attachMacroArgs } from "../libs/attach-arguments";
Expand Down
Loading

0 comments on commit 898ac25

Please sign in to comment.