Skip to content

Commit

Permalink
Generate tree files from YAML with comment support
Browse files Browse the repository at this point in the history
  • Loading branch information
brickpop committed Oct 23, 2024
1 parent a926e49 commit ad4a26a
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 79 deletions.
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
.DEFAULT_TARGET: help

SOLIDITY_VERSION=0.8.17
SOURCE_FILES=$(wildcard test/*.htree test/integration/*.htree)
TREE_FILES = $(SOURCE_FILES:.htree=.tree)
SOURCE_FILES=$(wildcard test/*.t.yaml test/integration/*.t.yaml)
TREE_FILES = $(SOURCE_FILES:.t.yaml=.tree)
TARGET_TEST_FILES = $(SOURCE_FILES:.tree=.t.sol)
MOUNTED_PATH=/data
MAKE_TEST_TREE=deno run ./test/script/make-test-tree.ts
Expand Down Expand Up @@ -48,10 +48,10 @@ check-tree: $(TREE_FILES)

$(TREE_FILES): $(SOURCE_FILES)

%.tree:%.htree
%.tree:%.t.yaml
@for file in $^; do \
echo "[Convert] $$file" ; \
cat $$file | $(MAKE_TEST_TREE) > $${file%.htree}.tree ; \
cat $$file | $(MAKE_TEST_TREE) > $${file%.t.yaml}.tree ; \
done

.PHONY: clean
Expand Down
137 changes: 62 additions & 75 deletions test/script/make-test-tree.ts
Original file line number Diff line number Diff line change
@@ -1,118 +1,105 @@
// Run this program with:
// $ cat file | deno run test/script/make-test-tree.ts
// $ cat file.t.yaml | deno run test/script/make-test-tree.ts

type Line = { content: string; indentation: number };
type TreeItem = { content: string; children: Array<TreeItem> };
import { parse } from "jsr:@std/yaml";

async function main() {
const content = await readStdinText();
Deno.stdout.write(new TextEncoder().encode(transformContent(content)));
}
type Rule = {
comment?: string;
given?: string;
when?: string;
and?: Array<Rule>;
it?: string;
};

function transformContent(content: string) {
const lines = parseLines(content);
if (!lines.length) return;
type TreeItem = {
content: string;
children: Array<TreeItem>;
comment?: string;
};

const root: TreeItem = {
content: lines[0].content,
children: parseChildrenLines(lines.slice(1)),
};
return formatTree(root);
async function main() {
const content = await readStdinText();
const tree = processTree(content);
Deno.stdout.write(new TextEncoder().encode(tree));
}

function parseLines(input: string): Array<Line> {
const lines = input
.split("\n")
.map((line) => {
const match = line.match(/^\s*(#*)\s*(.*)/);
if (!match || !match[2].trim()) return null;

let result: Line = {
indentation: match[1].length,
content: match[2].trim(),
};
return result;
})
.filter((line) => !!line);

if (!lines.length) return [];

// Sanity checks
let count = lines.filter((line) => line.indentation === 0).length;
if (count > 1) {
throw new Error("There can be only one root element at the begining");
} else if (lines[0].indentation != 0) {
throw new Error("The first element should have no indentation");
function processTree(content: string) {
const data: { [k: string]: Array<Rule> } = parse(content);
if (!data || typeof data !== "object") {
throw new Error("The file format is not a valid yaml object");
}

for (let i = 1; i < lines.length; i++) {
if (lines[i].indentation > lines[i - 1].indentation + 1) {
throw new Error(
"Incorrect indentation: Was " +
lines[i - 1].indentation +
" and now is " +
lines[i].indentation
);
}
const rootKeys = Object.keys(data);
if (rootKeys.length > 1) {
throw new Error("The test definition must have only one root node");
}
const [rootKey] = rootKeys;
if (!rootKey || !data[rootKey]) {
throw new Error("A root node needs to be defined");
} else if (!data[rootKey].length) {
throw new Error("The root node needs to include at least one element");
}

return lines;
const root: TreeItem = {
content: rootKey,
children: parseRuleChildren(data[rootKey]),
};
return renderTree(root);
}

function parseChildrenLines(
lines: Array<Line>,
parentIndentation = 0
): Array<TreeItem> {
function parseRuleChildren(lines: Array<Rule>): Array<TreeItem> {
if (!lines.length) return [];

const result: Array<TreeItem> = [];
const result: Array<TreeItem> = lines.map((rule) => {
if (!rule.when && !rule.given && !rule.it)
throw new Error("All rules should have a 'given', 'when' or 'it' rule");

for (let i = 0; i < lines.length; i++) {
const item = lines[i];
// Iterate over the direct children
if (item.indentation != parentIndentation + 1) continue;
let result: TreeItem = {
content: "",
children: rule.and?.length ? parseRuleChildren(rule.and) : [],
};
if (rule.given) result.content = "Given " + rule.given;
else if (rule.when) result.content = "When " + rule.when;

// Determine the boundaries of `item`'s children
let j = i + 1;
for (; j < lines.length; j++) {
if (lines[j].indentation <= item.indentation) break;
if (rule.it) {
result.children.push({ content: "It " + rule.it, children: [] });
}

// Process our subsegment only
const children = lines.slice(i, j);
result.push({
content: item.content,
children: parseChildrenLines(children, item.indentation),
});
}
if (rule.comment) result.comment = rule.comment;
return result;
});

return result;
}

function formatTree(root: TreeItem): string {
function renderTree(root: TreeItem): string {
let result = root.content + "\n";

for (let i = 0; i < root.children.length; i++) {
const item = root.children[i];
const newLines = formatTreeItem(item, i === root.children.length - 1);
const newLines = renderTreeItem(item, i === root.children.length - 1);
result += newLines.join("\n") + "\n";
}

return result;
}

function formatTreeItem(
function renderTreeItem(
root: TreeItem,
lastChildren: boolean,
prefix = ""
): Array<string> {
const result: string[] = [];

// Add ourselves
const content = root.comment
? `${root.content} // ${root.comment}`
: root.content;

if (lastChildren) {
result.push(prefix + "└── " + root.content);
result.push(prefix + "└── " + content);
} else {
result.push(prefix + "├── " + root.content);
result.push(prefix + "├── " + content);
}

// Add any children
Expand All @@ -122,14 +109,14 @@ function formatTreeItem(
// Last child
if (i === root.children.length - 1) {
const newPrefix = lastChildren ? prefix + " " : prefix + "│ ";
const lines = formatTreeItem(item, true, newPrefix);
const lines = renderTreeItem(item, true, newPrefix);
lines.forEach((line) => result.push(line));
continue;
}

// The rest of children
const newPrefix = lastChildren ? prefix + " " : prefix + "│ ";
const lines = formatTreeItem(item, false, newPrefix);
const lines = renderTreeItem(item, false, newPrefix);
lines.forEach((line) => result.push(line));
}

Expand Down

0 comments on commit ad4a26a

Please sign in to comment.