{ function filterTerminators(values) { return values.map((value) => { return value[1]; }); } function collapseText(contents) { /* To make the closing-tag parsing work, we interrupt a 'text' block * every time we encounter a brace. Unfortunately, for non-closing-tag * braces, this means things get broken up into consecutive text items. * This function stitches consecutive text items back together. */ let textBuffer = ""; let newContents = []; function completeTextItem() { if (textBuffer.length > 0) { newContents.push({ type: "text", text: textBuffer }); textBuffer = ""; } } contents.forEach((item) => { if (item.type === "text") { textBuffer += item.text; } else { completeTextItem(); newContents.push(item); } }); completeTextItem(); return newContents; } function processContents(contents) { return collapseText(filterTerminators(contents)); } function combineDelimitedItems(firstItem, nextItems, nextItemIndex = 1) { return [firstItem].concat(nextItems.map((item) => { return item[nextItemIndex]; })); } } start = contents:thing* { return collapseText(contents); } text = firstCharacter:. otherCharacters:[^{]* { return {type: "text", text: firstCharacter + otherCharacters.join("")}; } thing = classTag /*/ colorTag / boldTag / italicTag / weightTag / fontTag / sizeTag*/ / alignmentTag / text tagParameter = characters:[^}:]+ { return characters.join(""); } alignmentParameter = "left" / "center" / "right" / "justify" weightParameter = "bold" / "normal" / [0-9]+ classParameter = characters:[^}:,]+ { return characters.join(""); } classTag = "{class:" firstClassName:classParameter nextClassNames:("," classParameter)* "}" contents:(!"{/class}" thing)* "{/class}" { return {type: "class", classNames: combineDelimitedItems(firstClassName, nextClassNames), contents: processContents(contents)}; } /*colorTag = "{color:" color:tagParameter "}" contents:(!"{/color}" thing)* "{/color}" { return {type: "color", color: color, contents: processContents(contents)}; } fontTag = "{font:" font:tagParameter "}" contents:(!"{/font}" thing)* "{/font}" { return {type: "font", font: font, contents: processContents(contents)}; } sizeTag = "{size:" size:tagParameter "}" contents:(!"{/size}" thing)* "{/size}" { return {type: "size", size: size, contents: processContents(contents)}; }*/ alignmentTag = "{align:" alignment:alignmentParameter "}" contents:(!"{/align}" thing)* "{/align}" { return {type: "alignment", alignment: alignment, contents: processContents(contents)}; } /*weightTag = "{weight:" weight:tagParameter "}" contents:(!"{/weight}" thing)* "{/weight}" { return {type: "weight", weight: weight, contents: processContents(contents)}; } boldTag = "{bold}" contents:(!"{/bold}" thing)* "{/bold}" { return {type: "bold", contents: processContents(contents)}; } italicTag = "{italic}" contents:(!"{/italic}" thing)* "{/italic}" { return {type: "italic", contents: processContents(contents)}; }*/