2009-01-23 98 views
3

我正在研究Python的宏观系统(as discussed here),我一直在考虑的其中一项是度量单位。虽然度量单位可以在没有宏或通过静态宏实现的情况下实现(例如,提前定义所有单位),但我正在考虑允许在运行时动态扩展语法。解析的部分评估

为此,我正在考虑在编译时对代码进行一种部分评估。如果给定表达式的解析失败,由于宏的语法不可用,编译器将暂停对函数/块的评估,并生成已存在的未知表达式存根的代码。在运行时命中该存根时,该函数将针对当前宏集重新编译。如果此编译失败,则会抛出解析错误,因为执行无法继续。如果编译成功,则新函数会替换旧函数并继续执行。

我看到的最大问题是,在受影响的代码运行之前,您无法找到分析错误。但是,这不会影响很多情况,例如像[],{},()和``这样的组运算符仍然需要配对(我的tokenizer/list分析器的要求),而类和函数等顶级语法不会受到影响,因为它们的“运行时”真正加载时间,语法的评估和对象的生成。

除了上面描述的实施难度和问题,这个想法有什么问题吗?

回答

3

下面是一些可能出现的问题:

  • 您可能会发现很难在出现问题时提供有用的错误消息的用户。这似乎很可能,因为任何编译时语法错误可能只是语法扩展。
  • 表现受到影响。

我试图找到一些关于Perl 6中动态解析的优缺点和实现的讨论,但我找不到任何适合的东西。但是,您可能会发现从尼克劳斯·沃思(Pascal和其他语言的设计者)这句话有意思:

计算机科学家的幻想 在20世纪60年代就知道没有界限。通过自动语法 分析和解析器生成的成功,剔除了 ,一些 提出了灵活的思想,或至少可扩展的语言。 的概念是程序在 之前是句法规则,然后 将在解析后续程序时指导一般语法分析器 。 更进一步:语法规则 不仅在程序之前,但它们 可以散布在整个文本的任何地方 。例如,如果有人 希望使用的for语句特别 看中私人形式, 他可以这样做优雅,甚至 指定不同的变体中的 同一程序的不同部分中的 同一个概念。 语言用于沟通 人类的概念已完全混合 出来,因为显然现在每个人现在可以 在飞行中定义他自己的语言。 然而,寄予厚望的是 因试图指定遇到的困难 而受到阻碍,这些私人构造应该是什么意思。由于 的一个后果,可扩展语言的积极想法迅速消失而不是 。

编辑:这是Perl 6的的Synopsis 6: Subroutines,可惜在标记形式,因为我无法找到一个更新的,格式化的版本;搜索“宏”。不幸的是,这并不是也很有趣,但是您可能会发现一些相关的东西,比如Perl 6的单通解析规则或其抽象语法树的语法。 Perl 6采用的方法是,宏是一个函数,在解析其参数后立即执行,并返回AST或字符串; Perl 6继续解析,就好像源实际包含返回值一样。提到了错误消息的产生,但是它们使得宏看起来像是返回AST,你可以做得很好。

+0

感谢您的输入。赏金可能是所发生的最好的事情。给它多一点时间来看看还有什么进来,但在赏金期结束之前,我会标记一个可接受的答案。再次感谢:) – 2009-01-27 13:45:43

+0

我希望我可以帮助更多。我不认为这应该是你的赏金=)但是赏金显然将我的注意力引向了你的帖子。 – 2009-01-27 14:06:06

1

我考虑过的另一件事是使这个默认行为全面,但允许语言(意思是一组宏)来在编译时抛出一个分析错误。例如,我的系统中的Python 2.5会执行此操作。

除了存根思想外,只需重新编译那些在执行时不能在编译时完全处理的函数。这也会使自修改代码更容易,因为您可以修改代码并在运行时重新编译它。

0

您可能需要使用未知语法对输入文本的位进行分隔,以便除了稍后将展开的某些字符序列节点之外,可以解析语法树的其余部分。根据您的顶级语法,这可能没问题。

您可能会发现解析算法和词法分析器以及它们之间的接口都需要更新,这可能会排除大多数编译器创建工具。

(更常见的方法是为此目的使用字符串常量,可以在运行时解析为一个小解释器)。

2

进一步推进这一步,您可以做“懒惰”的解析,并且始终只进行足够的分析以评估下一个语句。就像某种即时分析器一样。然后,语法错误可能成为只是提出一个正常的异常,可以通过周围的代码来处理正常运行时错误:

def fun(): 
    not implemented yet 

try: 
    fun() 
except: 
    pass 

这将是一个有趣的效果,但如果它是可用或需要的是一个不同的问题。即使您目前不调用代码,通常很高兴知道错误。

直到控制达到它们,宏才会被评估,解析器自然会知道所有以前的定义。此外,宏定义可能甚至可能使用程序迄今为止计算出的变量和数据(例如为先前计算的列表中的所有元素添加一些语法)。但是,对于通常可以直接在语言中完成的事情开始编写自修改程序,这可能是一个糟糕的主意。这可能会令人困惑...

在任何情况下,您都应该确保只解析一次代码,并且如果第二次执行它,请使用已解析的表达式,这样不会导致性能问题。

0

我不认为你的方法会工作得很好。让我们写的伪代码一个简单的例子:

define some syntax M1 with definition D1 
if _whatever_: 
    define M1 to do D2 
else: 
    define M1 to do D3 
code that uses M1 

所以存在这样的情况,如果允许在运行时语法重新定义,你有问题(因为通过你的方法使用M1代码将被编译一个例子根据定义D1)。注意验证语法重定义是否是不可判定的。过度近似可以通过某种类型的键入系统或某种其他类型的静态分析来计算,但Python并不为人所熟知:D。

困扰我的另一件事是您的解决方案不'感觉'正确。我发现存储你无法解析的源代码是邪恶的,因为你可能能够在运行时解析它。

跳转至心灵另一个例子是这样的:

...function definition fun1 that calls fun2... 
define M1 (at runtime) 
use M1 
...function definition for fun2 

从技术上讲,当你使用M1,你不能分析它,所以你需要保持程序的其余部分(包括FUN2的函数定义)在源代码中。当你运行整个程序时,你会看到一个你不能调用的fun2调用,即使它已经被定义。

1

以下是我的硕士论文的一些想法,这些想法可能有用也可能没有帮助。该论文是关于自然语言的健壮分析。 主要思想:给定一种语言的上下文无关语法,尝试解析给定的 文本(或者,在你的情况下,一个Python程序)。如果解析失败,您将有一个部分生成的分析树。使用树结构来建议新的语法规则,以更好地覆盖分析的文本。 我可以寄给你我的论文,但除非你阅读希伯来文,否则这可能不会有用。

简而言之: 我使用了bottom-upchart parser。这种类型的解析器从语法中为生产生成边缘。每条边都用消耗的树的一部分标记。每条边根据它多么接近全覆盖,例如得到一个分数:

S -> NP . VP 

拥有得分的一半(我们成功地覆盖了NP,但不是VP)。 得分最高的边缘提示了一条新规则(如X-> NP)。 一般来说,图表解析器的效率低于常见的LALR或LL解析器(通常用于编程语言的类型) - O(n^3)而不是O(n)的复杂性,但是您又在尝试更复杂的事情而不仅仅是解析现有的语言。 如果你可以对这个想法做些什么,我可以给你发送更多细节。 我相信看自然语言解析器可能会给你一些其他的想法。