var lop = require("lop"); var RegexTokeniser = lop.RegexTokeniser; var documentMatchers = require("./document-matchers"); var htmlPaths = require("./html-paths"); exports.readHtmlPath = readHtmlPath; exports.readDocumentMatcher = readDocumentMatcher; exports.readStyle = readStyle; function readStyle(string) { return parseString(styleRule(), string); } function styleRule() { return lop.rules.sequence( lop.rules.sequence.capture(documentMatcherRule()), lop.rules.tokenOfType("whitespace"), lop.rules.tokenOfType("arrow"), lop.rules.tokenOfType("whitespace"), lop.rules.sequence.capture(htmlPathRule()) ).map(function(documentMatcher, htmlPath) { return { from: documentMatcher, to: htmlPath }; }); } function readDocumentMatcher(string) { return parseString(documentMatcherRule(), string); } function documentMatcherRule() { var sequence = lop.rules.sequence; var paragraphRule = lop.rules.then( lop.rules.token("identifier", "p"), function() { return documentMatchers.paragraph; } ); var runRule = lop.rules.then( lop.rules.token("identifier", "r"), function() { return documentMatchers.run; } ); var elementTypeRule = lop.rules.firstOf("p or r", paragraphRule, runRule ); return sequence( sequence.capture(elementTypeRule), sequence.capture(lop.rules.optional(classRule)) ).map(function(createMatcher, styleName) { return createMatcher(styleName.valueOrElse()); }); } function readHtmlPath(string) { return parseString(htmlPathRule(), string); } function htmlPathRule() { var capture = lop.rules.sequence.capture; var whitespaceRule = lop.rules.tokenOfType("whitespace"); var freshRule = lop.rules.then( lop.rules.optional(lop.rules.sequence( lop.rules.tokenOfType("colon"), lop.rules.token("identifier", "fresh") )), function(option) { return option.map(function() { return true; }).valueOrElse(false); } ); var styleElementRule = lop.rules.sequence( capture(identifierRule), capture(lop.rules.zeroOrMore(classRule)), capture(freshRule) ).map(function(tagName, classNames, fresh) { var attributes = {}; var options = {}; if (classNames.length > 0) { attributes["class"] = classNames.join(" "); } if (fresh) { options["fresh"] = true; } return htmlPaths.element(tagName, attributes, options); }); return lop.rules.then( lop.rules.zeroOrMoreWithSeparator( styleElementRule, lop.rules.sequence( whitespaceRule, lop.rules.tokenOfType("gt"), whitespaceRule ) ), htmlPaths.elements ); } var identifierRule = lop.rules.tokenOfType("identifier"); var classRule = lop.rules.sequence( lop.rules.tokenOfType("dot"), lop.rules.sequence.capture(identifierRule) ).head(); function parseString(rule, string) { var tokens = tokenise(string); var parser = lop.Parser(); var parseResult = parser.parseTokens(rule, tokens); if (!parseResult.isSuccess()) { throw new Error("Failed to parse: " + describeFailure(parseResult)); } return parseResult.value(); } function describeFailure(parseResult) { return parseResult.errors().map(describeError).join("\n"); function describeError(error) { return error.describe(); } } function tokenise(string) { var tokeniser = new RegexTokeniser([ {name: "identifier", regex: /([a-zA-Z][a-zA-Z0-9\-]*)/}, {name: "dot", regex: /\./}, {name: "colon", regex: /:/}, {name: "gt", regex: />/}, {name: "whitespace", regex: /\s+/}, {name: "arrow", regex: /=>/} ]); return tokeniser.tokenise(string); }