【发布时间】:2019-06-13 01:36:10
【问题描述】:
最近有人问是否有一种简单的方法来转换自定义标记,如下所示,包括嵌套标记。示例包括...
- 对于
\k[hello],输出将为<b>hello</b> - 对于
\i[world],输出将为<em>world</em> - 对于
hello \k[dear \i[world]],输出将为hello <b>dear <em>world</em></b> - 对于
\b[some text](url),输出将为<a href=”url”>some text</a> - 对于
\r[some text](url),输出将为<img alt=”some text” src=”url” />
有趣的是,将上述内容转换为 javascript(包括考虑嵌套)非常简单,尤其是在标记语法一致的情况下。
//
// Define the syntax and translation to javascript.
//
const grammar = {
syntax: {
k: {markUp: `\k[`, javascript: `"+grammar.oneArg("k","`, pre: `<b>`, post: `</b>`},
i: {markUp: `\i[`, javascript: `"+grammar.oneArg("i","`, pre: `<em>`, post: `</em>`},
b: {markUp: `\b[`, javascript: `"+grammar.twoArgs("b","`, pattern: `<a href="$2">$1</a>`},
r: {markUp: `\r[`, javascript: `"+grammar.twoArgs("r","`, pattern: `<img alt="$1" src="$2"/>`},
close0: {markUp: `](`, javascript: `","`},
close1: {markUp: `)`, javascript: `")+"`},
close2: {markUp: `]`, javascript: `")+"`}
},
oneArg: function( command, arg1 ) {
return grammar.syntax[ command ].pre + arg1 + grammar.syntax[ command ].post;
},
twoArgs: function( command, arg1, arg2 ) {
return grammar.syntax[ command ].pattern.split( `$1` ).join( arg1 ).split( `$2` ).join( arg2 );
}
}
function transpileAndExecute( markUpString ) {
// Convert the markUp to javascript.
for ( command in grammar.syntax ) {
markUpString = markUpString.split( grammar.syntax[ command ].markUp ).join( grammar.syntax[ command ].javascript );
}
// With the markUp now converted to javascript, let's execute it!
return new Function( `return "${markUpString}"` )();
}
var markUpTest = `Hello \k[dear \i[world!]] \b[\i[Search:] \k[Engine 1]](http://www.google.com) \r[\i[Search:] \k[Engine 2]](http://www.yahoo.com)`;
console.log( transpileAndExecute( markUpTest ) );
请注意,显然还必须解决预处理问题,例如如何处理在普通文本中包含标记。例如,在文本字符串中包含一个 ']' 会给编译器带来一个曲线球,因此执行一个规则,例如使用 '\]' 来表示一个 ']',然后替换所有出现的 '\]'在转译之前使用无害的文本,然后重新替换可以简单地解决这个问题......
在转译方面,使用上面定义的语法,下面的标记...
Hello \k[dear \i[world!]] \b[\i[Search:] \k[Engine 1]](http://www.google.com) \r[\i[Search:] \k[Engine 2]](http://www.yahoo.com)
...被转译成...
"Hello world! "+grammar.oneArg("k","dear "+grammar.oneArg("i","world")+"")+" "+grammar.twoArgs("b",""+grammar.oneArg("i","Search:")+" "+grammar.oneArg("k","Engine 1")+"","http://www.google.com")+" "+grammar.twoArgs("r",""+grammar.oneArg("i","Search:")+" "+grammar.oneArg("k","Engine 2")+"","http://www.yahoo.com")+""
...一旦作为 javascript 函数执行,结果...
Hello <b>dear <em>world!</em></b> <a href="http://www.google.com"><em>Search:</em> <b>Engine 1</b></a> <img alt="<em>Search:</em> <b>Engine 2</b>" src="http://www.yahoo.com"/>
真正的挑战是语法错误的处理,尤其是在有大量标记要转译的情况下。由CertainPerformance(见Find details of SyntaxError thrown by javascript new Function() constructor)提供的清晰答案提供了一种从动态编译的javascript函数中捕获语法错误的行号和字符号的方法,但我不太确定映射语法错误的最佳方法将代码转译回原始标记。
例如,如果多余的 ']' 不合适(在“再见”之后)...
Hello World! \b[\i[Goodbye]]] \k[World!]]
...这转换为...
"Hello World! "+grammar.twoArgs("b",""+grammar.oneArg("i","Goodbye")+"")+"")+" "+grammar.oneArg("k","World!")+"")+""
^
...CertainPerformance 的 checkSyntax 函数按预期返回“Error throw at: 1:76”,上面标有“^”。
问题是,如何将其映射回原始标记以帮助缩小标记中的错误? (显然在这种情况下,很容易看到标记中的错误,但如果有页面被转译,那么帮助缩小语法错误是必须的。)维护标记和转译代码之间的映射似乎棘手,因为转译器在遍历语法转换矩阵时逐步将标记更改为 javascript 代码。我的直觉告诉我有一个更简单的方法...感谢您的关注。
【问题讨论】:
-
这与 Markdown 无关。 Markdown 不是“任意标记”。它是一种具有一些常见变体的特定语言。我已经删除了错误的标签。请花点时间相应地编辑您的问题。
-
它给出了参考错误。 ReferenceError: 语法未定义
-
@Chris,感谢您的澄清。没有意识到降价是对特定语言的引用。已相应修改问题。验证更改后也会调整代码。
-
@mirkancal,请确保“const grammar =”在全局范围内,因为编译转译函数需要访问“grammar”i。现在想来,我想语法 const 可以包含在编译中,但是需要一些修补。
-
我突然意识到我可以将 cmets(即 /* */ )注入到转译的代码中,其想法是包含必要的信息以引用回标记中的相应位置。将在接下来的一天左右进行更多修改,看看这个概念是否与CertainPerformance 的 checkSyntax 函数结合使用。
标签: javascript algorithm transpiler