Rename enchanted words to bare words
This commit is contained in:
parent
8863290725
commit
981ddd7a6d
7 changed files with 74 additions and 98 deletions
2
TODO
2
TODO
|
@ -17,8 +17,6 @@
|
|||
|
||||
- rename peg.ts
|
||||
|
||||
- rename enchanted words -> bare words
|
||||
|
||||
- impl backslash escapes
|
||||
- eval words
|
||||
- error handling
|
||||
|
|
|
@ -6,7 +6,7 @@ exports[`Parsing Notcl Misc Big mess of markup 1`] = `
|
|||
[
|
||||
[
|
||||
{
|
||||
"enchanted": "h1",
|
||||
"bare": "h1",
|
||||
},
|
||||
{
|
||||
"text": "Hello, World!",
|
||||
|
@ -14,7 +14,7 @@ exports[`Parsing Notcl Misc Big mess of markup 1`] = `
|
|||
],
|
||||
[
|
||||
{
|
||||
"enchanted": "para",
|
||||
"bare": "para",
|
||||
},
|
||||
{
|
||||
"pieces": [
|
||||
|
@ -22,13 +22,13 @@ exports[`Parsing Notcl Misc Big mess of markup 1`] = `
|
|||
"script": [
|
||||
[
|
||||
{
|
||||
"enchanted": "2",
|
||||
"bare": "2",
|
||||
},
|
||||
{
|
||||
"enchanted": "+",
|
||||
"bare": "+",
|
||||
},
|
||||
{
|
||||
"enchanted": "2",
|
||||
"bare": "2",
|
||||
},
|
||||
],
|
||||
],
|
||||
|
@ -38,7 +38,7 @@ exports[`Parsing Notcl Misc Big mess of markup 1`] = `
|
|||
],
|
||||
[
|
||||
{
|
||||
"enchanted": "block",
|
||||
"bare": "block",
|
||||
},
|
||||
{
|
||||
"text": "
|
||||
|
@ -48,10 +48,10 @@ exports[`Parsing Notcl Misc Big mess of markup 1`] = `
|
|||
],
|
||||
[
|
||||
{
|
||||
"enchanted": "block",
|
||||
"bare": "block",
|
||||
},
|
||||
{
|
||||
"enchanted": "-red",
|
||||
"bare": "-red",
|
||||
},
|
||||
{
|
||||
"text": "Beware!",
|
||||
|
@ -59,7 +59,7 @@ exports[`Parsing Notcl Misc Big mess of markup 1`] = `
|
|||
],
|
||||
[
|
||||
{
|
||||
"enchanted": "para",
|
||||
"bare": "para",
|
||||
},
|
||||
{
|
||||
"text": "All text should be quoted, it's clearer that way. & blockquotes already should contain paragraphs. (maybe normalize nested paragraphs)",
|
||||
|
@ -67,7 +67,7 @@ exports[`Parsing Notcl Misc Big mess of markup 1`] = `
|
|||
],
|
||||
[
|
||||
{
|
||||
"enchanted": "block",
|
||||
"bare": "block",
|
||||
},
|
||||
{
|
||||
"text": "
|
||||
|
@ -91,7 +91,7 @@ exports[`Parsing Notcl Misc Big mess of markup 1`] = `
|
|||
],
|
||||
[
|
||||
{
|
||||
"enchanted": "para",
|
||||
"bare": "para",
|
||||
},
|
||||
{
|
||||
"text": "
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { parse } from "./parser";
|
||||
import { parse } from './parser';
|
||||
|
||||
describe("Parsing Notcl", () => {
|
||||
describe("Commands", () => {
|
||||
|
@ -8,62 +8,56 @@ describe("Parsing Notcl", () => {
|
|||
it("can parse a multi-word command", () =>
|
||||
expect(parse("a b c")).toEqual([
|
||||
true,
|
||||
[[{ enchanted: "a" }, { enchanted: "b" }, { enchanted: "c" }]],
|
||||
[[{ bare: "a" }, { bare: "b" }, { bare: "c" }]],
|
||||
]));
|
||||
|
||||
it("accepts newlines as command separators", () =>
|
||||
expect(parse("a\nb")).toEqual([
|
||||
true,
|
||||
[[{ enchanted: "a" }], [{ enchanted: "b" }]],
|
||||
[[{ bare: "a" }], [{ bare: "b" }]],
|
||||
]));
|
||||
it("does not split commands on folded newlines", () =>
|
||||
expect(
|
||||
parse(String.raw`a\
|
||||
b`)
|
||||
).toEqual([true, [[{ enchanted: "a" }, { enchanted: "b" }]]]));
|
||||
).toEqual([true, [[{ bare: "a" }, { bare: "b" }]]]));
|
||||
it("does split words on folded newlines", () =>
|
||||
expect(
|
||||
parse(String.raw`a\
|
||||
b`)
|
||||
).toEqual([true, [[{ enchanted: "a" }, { enchanted: "b" }]]]));
|
||||
).toEqual([true, [[{ bare: "a" }, { bare: "b" }]]]));
|
||||
it("does split commands on newlines with escaped backslashes", () =>
|
||||
expect(
|
||||
parse(String.raw`a\\
|
||||
b`)
|
||||
).toEqual([true, [[{ text: "a\\" }], [{ enchanted: "b" }]]]));
|
||||
).toEqual([true, [[{ text: "a\\" }], [{ bare: "b" }]]]));
|
||||
it("does not split commands on folded newlines with escaped backslashes", () =>
|
||||
expect(
|
||||
parse(String.raw`a\\\
|
||||
b`)
|
||||
).toEqual([true, [[{ text: "a\\" }, { enchanted: "b" }]]]));
|
||||
).toEqual([true, [[{ text: "a\\" }, { bare: "b" }]]]));
|
||||
|
||||
it("accepts semicolons as command separators", () =>
|
||||
expect(parse("a;b")).toEqual([
|
||||
true,
|
||||
[[{ enchanted: "a" }], [{ enchanted: "b" }]],
|
||||
]));
|
||||
expect(parse("a;b")).toEqual([true, [[{ bare: "a" }], [{ bare: "b" }]]]));
|
||||
|
||||
it("tolerates, and ignores, empty commands", () =>
|
||||
expect(parse("a;;b\n\nc")).toEqual([
|
||||
true,
|
||||
[[{ enchanted: "a" }], [{ enchanted: "b" }], [{ enchanted: "c" }]],
|
||||
[[{ bare: "a" }], [{ bare: "b" }], [{ bare: "c" }]],
|
||||
]));
|
||||
|
||||
test.each([[" a"], ["a "], ["a ;"], ["; a"]])(
|
||||
"tolerates whitespace before and after commands {%s}",
|
||||
(text) => expect(parse(text)).toEqual([true, [[{ enchanted: "a" }]]])
|
||||
(text) => expect(parse(text)).toEqual([true, [[{ bare: "a" }]]])
|
||||
);
|
||||
});
|
||||
|
||||
describe("Comments", () => {
|
||||
it("ignores comments", () => expect(parse("#comment")).toEqual([true, []]));
|
||||
it("does not treat # in argument position as a comment", () =>
|
||||
expect(parse("a #1")).toEqual([
|
||||
true,
|
||||
[[{ enchanted: "a" }, { enchanted: "#1" }]],
|
||||
]));
|
||||
expect(parse("a #1")).toEqual([true, [[{ bare: "a" }, { bare: "#1" }]]]));
|
||||
it("can have commands before a comment", () =>
|
||||
expect(parse("a ;#comment")).toEqual([true, [[{ enchanted: "a" }]]]));
|
||||
expect(parse("a ;#comment")).toEqual([true, [[{ bare: "a" }]]]));
|
||||
|
||||
it("ignores the whole line after a comment", () =>
|
||||
expect(parse("# comment ; not a command")).toEqual([true, []]));
|
||||
|
@ -77,7 +71,7 @@ b`)
|
|||
expect(
|
||||
parse(String.raw`#a\\
|
||||
b`)
|
||||
).toEqual([true, [[{ enchanted: "b" }]]]));
|
||||
).toEqual([true, [[{ bare: "b" }]]]));
|
||||
it("continues the comment through a folded newline with escaped backslashes", () =>
|
||||
expect(
|
||||
parse(String.raw`#a\\\
|
||||
|
@ -87,9 +81,9 @@ b`)
|
|||
|
||||
describe("interpolated words", () => {
|
||||
it("can parse a simple word", () =>
|
||||
expect(parse("a")).toEqual([true, [[{ enchanted: "a" }]]]));
|
||||
expect(parse("a")).toEqual([true, [[{ bare: "a" }]]]));
|
||||
it("can parse a word with non-special punctuation", () =>
|
||||
expect(parse("-switch")).toEqual([true, [[{ enchanted: "-switch" }]]]));
|
||||
expect(parse("-switch")).toEqual([true, [[{ bare: "-switch" }]]]));
|
||||
|
||||
it("accepts empty quotes", () =>
|
||||
expect(parse('""')).toEqual([true, [[{ text: "" }]]]));
|
||||
|
@ -110,9 +104,9 @@ b`)
|
|||
expect(parse("a\\ b")).toEqual([true, [[{ text: "a b" }]]]));
|
||||
|
||||
it("treats a non-leading quote as a plain character", () =>
|
||||
expect(parse('a"')).toEqual([true, [[{ enchanted: 'a"' }]]]));
|
||||
expect(parse('a"')).toEqual([true, [[{ bare: 'a"' }]]]));
|
||||
it("treats a non-leading brace as a plain character", () =>
|
||||
expect(parse("a{")).toEqual([true, [[{ enchanted: "a{" }]]]));
|
||||
expect(parse("a{")).toEqual([true, [[{ bare: "a{" }]]]));
|
||||
it("treats an escaped quote as a plain character", () =>
|
||||
expect(parse('\\"')).toEqual([true, [[{ text: '"' }]]]));
|
||||
it("treats an escaped brace as a plain character", () =>
|
||||
|
@ -133,7 +127,7 @@ b`)
|
|||
it("can parse one-word command interpolations", () =>
|
||||
expect(parse("[a]")).toEqual([
|
||||
true,
|
||||
[[{ pieces: [{ script: [[{ enchanted: "a" }]] }] }]],
|
||||
[[{ pieces: [{ script: [[{ bare: "a" }]] }] }]],
|
||||
]));
|
||||
it("can parse multi-word command interpolations", () =>
|
||||
expect(parse("[a b c]")).toEqual([
|
||||
|
@ -143,13 +137,7 @@ b`)
|
|||
{
|
||||
pieces: [
|
||||
{
|
||||
script: [
|
||||
[
|
||||
{ enchanted: "a" },
|
||||
{ enchanted: "b" },
|
||||
{ enchanted: "c" },
|
||||
],
|
||||
],
|
||||
script: [[{ bare: "a" }, { bare: "b" }, { bare: "c" }]],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -164,10 +152,7 @@ b`)
|
|||
{
|
||||
pieces: [
|
||||
{
|
||||
script: [
|
||||
[{ enchanted: "a" }],
|
||||
[{ enchanted: "b" }, { enchanted: "c" }],
|
||||
],
|
||||
script: [[{ bare: "a" }], [{ bare: "b" }, { bare: "c" }]],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -182,7 +167,7 @@ b`)
|
|||
{
|
||||
pieces: [
|
||||
{
|
||||
script: [[{ pieces: [{ script: [[{ enchanted: "a" }]] }] }]],
|
||||
script: [[{ pieces: [{ script: [[{ bare: "a" }]] }] }]],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -196,7 +181,7 @@ b`)
|
|||
[
|
||||
[
|
||||
{
|
||||
pieces: [{ script: [[{ enchanted: "a" }]] }, { enchanted: "b" }],
|
||||
pieces: [{ script: [[{ bare: "a" }]] }, { bare: "b" }],
|
||||
},
|
||||
],
|
||||
],
|
||||
|
@ -208,9 +193,9 @@ b`)
|
|||
[
|
||||
{
|
||||
pieces: [
|
||||
{ enchanted: "a" },
|
||||
{ script: [[{ enchanted: "b" }]] },
|
||||
{ enchanted: "c" },
|
||||
{ bare: "a" },
|
||||
{ script: [[{ bare: "b" }]] },
|
||||
{ bare: "c" },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
@ -222,7 +207,7 @@ b`)
|
|||
[
|
||||
[
|
||||
{
|
||||
pieces: [{ enchanted: "a" }, { script: [[{ enchanted: "b" }]] }],
|
||||
pieces: [{ bare: "a" }, { script: [[{ bare: "b" }]] }],
|
||||
},
|
||||
],
|
||||
],
|
||||
|
@ -234,8 +219,8 @@ b`)
|
|||
[
|
||||
{
|
||||
pieces: [
|
||||
{ script: [[{ enchanted: "a" }]] },
|
||||
{ script: [[{ enchanted: "b" }]] },
|
||||
{ script: [[{ bare: "a" }]] },
|
||||
{ script: [[{ bare: "b" }]] },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
|
@ -36,7 +36,7 @@ function bareWordTmpl(charRegex: RegExp) {
|
|||
Bracket,
|
||||
Regex(charRegex)
|
||||
.expects("CHAR")
|
||||
.map(([text]) => ({ enchanted: text }))
|
||||
.map(([text]) => ({ bare: text }))
|
||||
)
|
||||
)
|
||||
).map(([, pieces]) => SimplifyWord(pieces));
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import {
|
||||
AsHtml, AsText, Concat, EnchantedWord, HtmlWord, InterpolatedPiece, Script, TextPiece, TextWord,
|
||||
Word
|
||||
AsHtml, AsText, BareWord, Concat, HtmlWord, InterpolatedPiece, Script, TextPiece, TextWord, Word
|
||||
} from './words';
|
||||
|
||||
/**
|
||||
|
@ -29,8 +28,8 @@ export type Vm = {
|
|||
function evaluateWord(
|
||||
state: Vm,
|
||||
word: Word | InterpolatedPiece
|
||||
): TextWord | EnchantedWord | HtmlWord {
|
||||
if ("enchanted" in word || "text" in word || "html" in word) {
|
||||
): TextWord | BareWord | HtmlWord {
|
||||
if ("bare" in word || "text" in word || "html" in word) {
|
||||
return word;
|
||||
} else if ("variable" in word) {
|
||||
return { text: "" };
|
||||
|
|
|
@ -1,41 +1,37 @@
|
|||
import { AsHtml, AsText, Concat } from "./words";
|
||||
import { AsHtml, AsText, Concat } from './words';
|
||||
|
||||
describe("Text Words", () => {
|
||||
test.each([
|
||||
[{ enchanted: "apple" }, "apple"],
|
||||
[{ enchanted: "<pie>" }, "<pie>"],
|
||||
[{ bare: "apple" }, "apple"],
|
||||
[{ bare: "<pie>" }, "<pie>"],
|
||||
[{ text: "banana" }, "banana"],
|
||||
[{ text: "<b>kiwi" }, "<b>kiwi"],
|
||||
[{ html: "<b>cherry</b>" }, "<b>cherry</b>"],
|
||||
])("AsText(%s)", (word, expected) => expect(AsText(word)).toEqual(expected));
|
||||
|
||||
test.each([
|
||||
[{ enchanted: "apple" }, "apple"],
|
||||
[{ enchanted: "<pie>" }, "<pie>"],
|
||||
[{ bare: "apple" }, "apple"],
|
||||
[{ bare: "<pie>" }, "<pie>"],
|
||||
[{ text: "banana" }, "banana"],
|
||||
[{ text: "<b>kiwi" }, "<b>kiwi"],
|
||||
[{ html: "<b>cherry</b>" }, "<b>cherry</b>"],
|
||||
])("AsHtml(%s)", (word, expected) => expect(AsHtml(word)).toEqual(expected));
|
||||
|
||||
test.each([
|
||||
[null, { enchanted: "1" }, { text: "1" }],
|
||||
[null, { enchanted: ">1" }, { text: ">1" }],
|
||||
[null, { bare: "1" }, { text: "1" }],
|
||||
[null, { bare: ">1" }, { text: ">1" }],
|
||||
[null, { text: "2" }, { text: "2" }],
|
||||
[null, { text: "<b>3</b>" }, { text: "<b>3</b>" }],
|
||||
[null, { html: "2" }, { html: "2" }],
|
||||
[null, { html: "<b>3</b>" }, { html: "<b>3</b>" }],
|
||||
[{ enchanted: "&pple" }, { enchanted: "1" }, { text: "&pple1" }],
|
||||
[{ enchanted: "&pple" }, { enchanted: ">1" }, { text: "&pple>1" }],
|
||||
[{ enchanted: "&pple" }, { text: "2" }, { text: "&pple2" }],
|
||||
[{ enchanted: "&pple" }, { text: "<b>3</b>" }, { text: "&pple<b>3</b>" }],
|
||||
[{ enchanted: "&pple" }, { html: "2" }, { html: "&pple2" }],
|
||||
[
|
||||
{ enchanted: "&pple" },
|
||||
{ html: "<b>3</b>" },
|
||||
{ html: "&pple<b>3</b>" },
|
||||
],
|
||||
[{ text: "<b>anana" }, { enchanted: "1" }, { text: "<b>anana1" }],
|
||||
[{ text: "<b>anana" }, { enchanted: ">1" }, { text: "<b>anana>1" }],
|
||||
[{ bare: "&pple" }, { bare: "1" }, { text: "&pple1" }],
|
||||
[{ bare: "&pple" }, { bare: ">1" }, { text: "&pple>1" }],
|
||||
[{ bare: "&pple" }, { text: "2" }, { text: "&pple2" }],
|
||||
[{ bare: "&pple" }, { text: "<b>3</b>" }, { text: "&pple<b>3</b>" }],
|
||||
[{ bare: "&pple" }, { html: "2" }, { html: "&pple2" }],
|
||||
[{ bare: "&pple" }, { html: "<b>3</b>" }, { html: "&pple<b>3</b>" }],
|
||||
[{ text: "<b>anana" }, { bare: "1" }, { text: "<b>anana1" }],
|
||||
[{ text: "<b>anana" }, { bare: ">1" }, { text: "<b>anana>1" }],
|
||||
[{ text: "<b>anana" }, { text: "2" }, { text: "<b>anana2" }],
|
||||
[{ text: "<b>anana" }, { text: "<b>3</b>" }, { text: "<b>anana<b>3</b>" }],
|
||||
[{ text: "<b>anana" }, { html: "2" }, { html: "<b>anana2" }],
|
||||
|
@ -44,8 +40,8 @@ describe("Text Words", () => {
|
|||
{ html: "<b>3</b>" },
|
||||
{ html: "<b>anana<b>3</b>" },
|
||||
],
|
||||
[{ html: "<img />" }, { enchanted: "1" }, { html: "<img />1" }],
|
||||
[{ html: "<img />" }, { enchanted: ">1" }, { html: "<img />>1" }],
|
||||
[{ html: "<img />" }, { bare: "1" }, { html: "<img />1" }],
|
||||
[{ html: "<img />" }, { bare: ">1" }, { html: "<img />>1" }],
|
||||
[{ html: "<img />" }, { text: "2" }, { html: "<img />2" }],
|
||||
[
|
||||
{ html: "<img />" },
|
||||
|
|
30
src/words.ts
30
src/words.ts
|
@ -1,8 +1,8 @@
|
|||
import { escapeHtml } from "./helpers";
|
||||
import { escapeHtml } from './helpers';
|
||||
|
||||
/**
|
||||
* A word whose value is text with provenance- this literal value appeared in the source
|
||||
* code, and was not the result of any backslash, variable, or command substitutions.
|
||||
* A word whose value is text with provenance- this literal value appeared in the source code,
|
||||
* and was neither quoted nor the result of any backslash, variable, or command substitutions.
|
||||
*
|
||||
* This provides a level of intentionality that commands can use to distinguish switches:
|
||||
*
|
||||
|
@ -13,8 +13,8 @@ import { escapeHtml } from "./helpers";
|
|||
* puts $var text ;# The value of $var is part of the message to print, even if the value happens to be "-stderr"
|
||||
* ```
|
||||
*/
|
||||
export type EnchantedWord = {
|
||||
enchanted: string;
|
||||
export type BareWord = {
|
||||
bare: string;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -32,7 +32,7 @@ export type HtmlWord = {
|
|||
html: string;
|
||||
};
|
||||
|
||||
export type TextPiece = EnchantedWord | TextWord | HtmlWord;
|
||||
export type TextPiece = BareWord | TextWord | HtmlWord;
|
||||
export type VariablePiece = { variable: string };
|
||||
export type ScriptPiece = { script: Script };
|
||||
export type InterpolatedPiece = TextPiece | VariablePiece | ScriptPiece;
|
||||
|
@ -46,8 +46,8 @@ export type InterpolatedWord = {
|
|||
};
|
||||
|
||||
export function AsText(word: TextPiece): string {
|
||||
if ("enchanted" in word) {
|
||||
return word.enchanted;
|
||||
if ("bare" in word) {
|
||||
return word.bare;
|
||||
} else if ("text" in word) {
|
||||
return word.text;
|
||||
} else if ("html" in word) {
|
||||
|
@ -57,8 +57,8 @@ export function AsText(word: TextPiece): string {
|
|||
}
|
||||
}
|
||||
export function AsHtml(word: TextPiece): string {
|
||||
if ("enchanted" in word) {
|
||||
return escapeHtml(word.enchanted);
|
||||
if ("bare" in word) {
|
||||
return escapeHtml(word.bare);
|
||||
} else if ("text" in word) {
|
||||
return escapeHtml(word.text);
|
||||
} else if ("html" in word) {
|
||||
|
@ -71,7 +71,7 @@ export function AsHtml(word: TextPiece): string {
|
|||
// safely concatenate text pieces, converting as needed
|
||||
export function Concat(left: TextPiece | null, right: TextPiece) {
|
||||
if (left === null) {
|
||||
return "enchanted" in right ? { text: right.enchanted } : right;
|
||||
return "bare" in right ? { text: right.bare } : right;
|
||||
}
|
||||
if ("html" in left || "html" in right) {
|
||||
return { html: AsHtml(left) + AsHtml(right) };
|
||||
|
@ -81,14 +81,12 @@ export function Concat(left: TextPiece | null, right: TextPiece) {
|
|||
}
|
||||
|
||||
function IsTextPiece(piece: InterpolatedPiece | undefined): piece is TextPiece {
|
||||
return piece
|
||||
? "text" in piece || "enchanted" in piece || "html" in piece
|
||||
: false;
|
||||
return piece ? "text" in piece || "bare" in piece || "html" in piece : false;
|
||||
}
|
||||
|
||||
export function SimplifyWord(
|
||||
pieces: InterpolatedPiece[]
|
||||
): InterpolatedWord | EnchantedWord | TextWord | HtmlWord {
|
||||
): InterpolatedWord | BareWord | TextWord | HtmlWord {
|
||||
const consolidated: InterpolatedPiece[] = [];
|
||||
for (const piece of pieces) {
|
||||
const top = consolidated[consolidated.length - 1];
|
||||
|
@ -108,6 +106,6 @@ export function SimplifyWord(
|
|||
}
|
||||
}
|
||||
|
||||
export type Word = EnchantedWord | TextWord | HtmlWord | InterpolatedWord;
|
||||
export type Word = BareWord | TextWord | HtmlWord | InterpolatedWord;
|
||||
export type Command = Word[];
|
||||
export type Script = Command[];
|
||||
|
|
Loading…
Reference in a new issue