Skip to content

Commit

Permalink
Switch to new macro format
Browse files Browse the repository at this point in the history
  • Loading branch information
siefkenj committed Nov 30, 2023
2 parents 9876b1e + 645a04f commit 34bcdeb
Show file tree
Hide file tree
Showing 8 changed files with 456 additions and 571 deletions.
32 changes: 11 additions & 21 deletions packages/parser/src/dast-to-xml/dast-util-to-xml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ export function nodesToXml(
return start + macro + end;
}
case "function": {
const macro = unwrappedMacroToString(node.macro, options);
const macro = unwrappedMacroToString(node, options);

let start = "$$";
let end = "";
Expand Down Expand Up @@ -187,26 +187,26 @@ export function quote(value: string) {
* Convert a macro to a string, but do not wrap it in parens or add a `$` prefix.
*/
function unwrappedMacroToString(
nodes: DastMacro,
nodes: DastMacro | DastFunctionMacro,
options: PrintOptions,
): string {
const path = macroPathToString(nodes.path, options);
const attrs = (nodes.attributes || [])
.map((a) => attrToString(a, options))
.join(" ");
let attrsStr = attrs.length > 0 ? `{${attrs}}` : "";
let propAccess = "";
if (nodes.accessedProp) {
propAccess = "." + unwrappedMacroToString(nodes.accessedProp, options);
let attrsStr = "";
if (nodes.type === "macro") {
const attrs = Object.values(nodes.attributes || {})
.map((a) => attrToString(a, options))
.join(" ");
attrsStr = attrs.length > 0 ? `{${attrs}}` : "";
}
let propAccess = "";
return path + attrsStr + propAccess;
}

function macroPathToString(
path: DastMacroFullPath,
options: PrintOptions,
): string {
return path.map((part) => macroPathPartToString(part, options)).join("/");
return path.map((part) => macroPathPartToString(part, options)).join(".");
}

function macroPathPartToString(
Expand All @@ -233,16 +233,6 @@ function attrToString(attr: DastAttribute, options: PrintOptions): string {
}

function macroNeedsParens(macro: DastMacro | DastFunctionMacro): boolean {
if (macro.type === "function") {
return macroNeedsParens(macro.macro);
}
// Paths are separated by slashes. They always need wrapping.
if (macro.path.length > 1) {
return true;
}
// We also might need wrapping if the path contains a `-` character
return (
macro.path.some((part) => part.name.includes("-")) ||
(macro.accessedProp != null && macroNeedsParens(macro.accessedProp))
);
return macro.path.some((part) => part.name.includes("-"));
}
44 changes: 13 additions & 31 deletions packages/parser/src/macros/macro-to-string.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
import { quote, toXml } from "../dast-to-xml/dast-util-to-xml";
import {
Attr,
FullPath,
FunctionMacro,
Macro,
PropAccess,
ScopedPathPart,
Text,
} from "./types";
import { Attr, Path, FunctionMacro, Macro, Text, PathPart } from "./types";

type Node = Macro | FunctionMacro | Text | PropAccess;
type Node = Macro | FunctionMacro | Text;

/**
* Convert a "pure" macro to a string. I.e., a macro that was parsed directly from the peggy grammar.
Expand All @@ -33,7 +25,7 @@ export function macroToString(node: Node | Node[]): string {
return start + macro + end;
}
case "function": {
const macro = unwrappedMacroToString(node.macro);
const macro = macroPathToString(node.path);

let start = "$$";
let end = "";
Expand All @@ -59,22 +51,21 @@ export function macroToString(node: Node | Node[]): string {
/**
* Convert a macro to a string, but do not wrap it in parens or add a `$` prefix.
*/
function unwrappedMacroToString(nodes: Macro): string {
const path = macroPathToString(nodes.path);
const attrs = (nodes.attributes || []).map(attrToString).join(" ");
function unwrappedMacroToString(macro: Macro): string {
const path = macroPathToString(macro.path);
const attrs = Object.values(macro.attributes || {})
.map(attrToString)
.join(" ");
let attrsStr = attrs.length > 0 ? `{${attrs}}` : "";
let propAccess = "";
if (nodes.accessedProp) {
propAccess = "." + unwrappedMacroToString(nodes.accessedProp);
}
return path + attrsStr + propAccess;
}

function macroPathToString(path: FullPath): string {
function macroPathToString(path: Path): string {
return path.map(macroPathPartToString).join("/");
}

function macroPathPartToString(pathPart: ScopedPathPart): string {
function macroPathPartToString(pathPart: PathPart): string {
return (
pathPart.name +
pathPart.index.map((part) => `[${macroToString(part.value)}]`).join("")
Expand All @@ -90,17 +81,8 @@ function attrToString(attr: Attr): string {
return `${name}=${quote(value)}`;
}

function macroNeedsParens(macro: Macro | PropAccess | FunctionMacro): boolean {
if (macro.type === "function") {
return macroNeedsParens(macro.macro);
}
// Paths are separated by slashes. They always need wrapping.
if (macro.path.length > 1) {
return true;
}
function macroNeedsParens(macro: Macro | FunctionMacro): boolean {
const path = macro.path;
// We also might need wrapping if the path contains a `-` character
return (
macro.path.some((part) => part.name.includes("-")) ||
(macro.accessedProp != null && macroNeedsParens(macro.accessedProp))
);
return path.some((part) => part.name.includes("-"));
}
82 changes: 31 additions & 51 deletions packages/parser/src/macros/macros.peggy
Original file line number Diff line number Diff line change
Expand Up @@ -8,78 +8,55 @@
top = (Macro / FunctionMacro / Text)*

// Identifiers. Scoped identifiers can only be used inside of `$(..)` notation.
Ident = $[a-zA-Z0-9_]+
SimpleIdent = $[a-zA-Z0-9_]+

ScopedIdent = $[a-zA-Z0-9_-]+
Ident = $[a-zA-Z0-9_-]+

// A PathPart cannot have slashes or `..` in it
PathPart
= name:Ident index:PropIndex* {
// A PathPart is an identifier followed by some amount of indexing
SimplePathPart
= name:SimpleIdent index:PropIndex* {
return withPosition({ type: "pathPart", name, index });
}

ScopedPathPart
= name:ScopedIdent index:PropIndex* {
PathPart
= name:Ident index:PropIndex* {
return withPosition({ type: "pathPart", name, index });
}

// A FullPath can have slashes and `..` in it. It may also start with a slash.
EmptyPathPart
= "" { return withPosition({ type: "pathPart", name: "", index: [] }); }

FullPath
= start:EmptyPathPart rest:("/" @ScopedPathPartOrDD)+ {
// A full path looks like `foo.bar[1][3].baz`. Attributes are not allowed to be assigned to a FullPath
SimplePath
= start:SimplePathPart rest:("." @SimplePathPart)* {
return [start, ...rest];
}
/ start:ScopedPathPartOrDD rest:("/" @ScopedPathPartOrDD)* {

Path
= start:PathPart rest:("." @PathPart)* {
return [start, ...rest];
}

ScopedPathPartOrDD
= ScopedPathPart
/ ".." { return withPosition({ type: "pathPart", name: "..", index: [] }); }

// Pops can be accessed with a `.`
PropAccess = "." @PartialPathMacro

ScopedPropAccess = "." @ScopedPartialPathMacro

//
// Macros
//
Macro
= "$" "(" macro:FullPathMacro ")" { return withPosition(macro) }
/ "$" macro:PartialPathMacro { return withPosition(macro) }
= "$" "(" macro:FullAddressMacro ")" { return withPosition(macro) }
/ "$" macro:SimpleAddressMacro { return withPosition(macro) }

// A macro where the path cannot have slashes or `..` in it.
PartialPathMacro
= path:PathPart attrs:PropAttrs? accessedProp:PropAccess? {
return withPosition({
type: "macro",
path: [path],
attributes: attrs || [],
accessedProp,
});
}

ScopedPartialPathMacro
= path:ScopedPathPart attrs:PropAttrs? accessedProp:ScopedPropAccess? {
// A macro where the path can have slashes and `..` in it.
SimpleAddressMacro
= path:SimplePath attrs:PropAttrs? {
return withPosition({
type: "macro",
path: [path],
attributes: attrs || [],
accessedProp,
path,
attributes: attrs || {},
});
}

// A macro where the path can have slashes and `..` in it.
FullPathMacro
= path:FullPath attrs:PropAttrs? accessedProp:ScopedPropAccess? {
FullAddressMacro
= path:Path attrs:PropAttrs? {
return withPosition({
type: "macro",
path,
attributes: attrs || [],
accessedProp,
attributes: attrs || {},
});
}

Expand All @@ -90,17 +67,17 @@ FullPathMacro
// Functions are very similar to macros except they cannot have attrs or accessedProps
// but they do take comma-separated arguments.
FunctionMacro
= "$$" "(" macro:FullPathMacro ")" input:FunctionInput? {
= "$$" "(" path:Path ")" input:FunctionInput? {
return withPosition({
type: "function",
macro,
path,
input,
});
}
/ "$$" macro:PartialPathMacro input:FunctionInput? {
/ "$$" path:SimplePath input:FunctionInput? {
return withPosition({
type: "function",
macro,
path,
input,
});
}
Expand Down Expand Up @@ -138,7 +115,10 @@ BalancedParenText
)* { return x.flat(); }
/ x:EmptyString { return [x]; }

PropAttrs = "{" _? @(@Attr _?)* "}"
PropAttrs
= "{" _? attrs:(@Attr _?)* "}" {
return Object.fromEntries(attrs.map((a) => [a.name, a]));
}

PropIndex
= "[" _? value:(@(FunctionMacro / Macro / TextWithoutClosingSquareBrace) _?)* "]" {
Expand Down
Loading

0 comments on commit 34bcdeb

Please sign in to comment.