在我们公司,我们喜欢编写django驱动的应用程序,我们也喜欢使用react。最近,我们考虑过为python创建一个基于组件的模板引擎,其中可以使用JSX将模板编写为类似React的组件。
理想情况下,应该可以将JSX嵌入到python代码中,以便您可以编写如下组件:
在header.pyx
中:
import PyReact
from my_awsome_project.components import Logo, Link
def Header(context):
page_title = context.get('page_title')
links = context.get('links')
return (
<div>
<Logo />
{page_title}
<ul>
{[<Link href={link.url}>{link.title}</Link> for link in links]}
</ul>
</div>
)
这当然需要先转换文件以获得有效的python代码。它会转换成类似以下内容的东西:
import PyReact
from my_awsome_project.components import Logo, Link
def Header(context):
page_title = context.get('page_title')
links = context.get('links')
return (
PyReact.createComponent('div', context, [
PyReact.createComponent(Logo),
page_title,
PyReact.createComponent('ul', context, [
[
PyReact.createComponent(Link, {'href': link.url}, link.title)
for link in links
]
]),
])
)
问题是:我将如何编写这样的编译器?
我们还考虑过,而不是直接将JSX直接嵌入到python代码中,我们可以返回一个包含独立解析的JSX的字符串。那会是更好/更容易的方法吗?
答案 0 :(得分:1)
我认为对于SO来说,这基本上是一个太宽泛的问题,任何答案都将在SO准则关于观点的边缘溜冰。您本质上是在寻求有关复杂问题的设计建议,因此,SO并非真正用于此目的。
还是,这是一个有趣的问题。我将尝试解决这些问题,而不必冒险深入研究过自以为是的设计(因为我对此事有意见)。
至少从理论上讲,转换是实用的,如果可以进行转换,它将为您提供合理的性能。
重复地解析模板字符串使我感到效率低下且复杂;复杂性与评估嵌入式Python代码有关,您将希望在定义字符串文字的范围内执行此操作,而该范围可能不是在其解析范围内。
JSX风格的词法分析和解析并不是特别复杂,但是假设的编译器还需要了解Python词法和语法分析。 Python的标准库包括用于lexing和解析Python的模块,但是afaik它们是不可扩展的,这可能使得难以利用它们与嵌入式语言一起使用。您可以使用自己选择的代码生成器来编写自己的词法分析器和解析器,也可以将词法分析器和解析器基于某些开源Python实现。在这两种情况下,您的可维护性挑战都是使自定义代码与将来的Python版本保持同步。
将伪HTML嵌入到其他语言中的主要问题是检测<
何时是比较运算符以及何时启动模板。最简单的解决方案是仅在将<
作为完整标记进行词法分析时才允许模板(这样<=
始终是运算符),后跟标识符,并且在语法环境中遇到
上面的最后一项要求是确保(例如)3 < count
不会使编译器误以为它将要看到<count...>
组件。我很确定在Python中您可以使用基于前面标记的简单词法规则,但是需要完整的语法分析来验证
一旦启动模板,它将继续直到您到达匹配的结束标签;如果必须匹配标签,这非常简单。但这比自下而上的解析更适合自上而下的解析,因为结束标记匹配是上下文相关的。如果您在词法分析和句法分析之间有着紧密的合作,那么这样做很容易,但是有时这种合作有时会被皱眉:-)
由于嵌入在模板中的Python代码本身可以包含一个嵌入的模板,该模板又可以嵌入更多的Python代码,依此类推,因此您的分析将需要递归。预期的递归深度不是很大,因此递归本身没问题,但是许多解析器生成器不能很好地处理这种递归。我建议使用(或实现)与缓冲区处理程序分开的“推式解析器”和词法分析器框架,以便您可以轻松地在缓冲区中间更改扫描程序。
缓冲区处理非常简单;最低要求只是一个字符串和该字符串的索引。如果在缓冲区处理程序中隔离实现细节,则以后应该可以更改为其他实现,例如,在开始解析之前不需要全部可用的实现。您可能实际上并不需要该功能,但是最好保持独立的组件,以防万一
您的编译器的另一个挑战是将其与Python的模块系统集成。 Python集成可能建议在导入模块时执行编译。另一方面,您可能希望能够分发预编译的捆绑软件,该捆绑软件无需安装Transpiler,也无需依赖于Transpiler的特定版本即可使用。如果您花一些时间对此进行仔细考虑,则可以避免以后出现问题。 (例如,Ply问题使不可能将Ply项目捆绑到一个文件分发系统中。)
希望能有所帮助。
答案 1 :(得分:0)
实际上,这是可能的。有一个库:packed [1]。但似乎该项目已被放弃,最近一次提交是在5年前。引用其自述文件:
@packed
def tag(self):
share = get_share_link()
return <a href={share}>Share on internet</a>
# to:
@packed
def tag(self):
share = get_share_link()
return Elem(
'a',
{
'href': share,
},
'Share on internet',
)