20

我一直在为 Python 开发一个替代编译器前端,其中所有语法都通过宏进行解析。我终于明白了它的发展,我可以开始研究 Python 语言的超集,其中宏是一个不可或缺的组件。

我的问题是我无法提出 pythonic 宏定义语法。我在下面的答案中以两种不同的语法发布了几个示例。谁能想出更好的语法?它不必以任何方式构建我提出的语法——我在这里完全开放。任何评论、建议等都会有所帮助,显示我发布的示例的替代语法也会有所帮助。

关于宏结构的注释,如我发布的示例所示: MultiLine/MLMacro 和 Partial/PartialMacro 的使用告诉解析器如何应用宏。如果是多行,宏会匹配多行列表;一般用于构造。如果它是部分的,宏将匹配列表中间的代码;一般用于操作员。

4

9 回答 9

11

几天前考虑了一下,没有什么值得发的东西,我现在回到它,想出了一些我比较喜欢的语法,因为它几乎看起来像 python:

macro PrintMacro:
  syntax:
    "print", OneOrMore(Var(), name='vars')

  return Printnl(vars, None)
  • 使所有宏“关键字”看起来像创建 python 对象(Var()而不是简单Var
  • 将元素的名称作为“关键字参数”传递给我们想要命名的项目。在解析器中找到所有名称应该仍然很容易,因为无论如何这个语法定义都需要以某种方式解释以填充宏类语法变量。

    需要转换以填充生成的宏类的语法变量。

内部语法表示也可能看起来相同:

class PrintMacro(Macro):
  syntax = 'print', OneOrMore(Var(), name='vars')
  ...

内部语法类 likeOneOrMore将遵循此模式以允许子项和可选名称:

class MacroSyntaxElement(object):
  def __init__(self, *p, name=None):
    self.subelements = p
    self.name = name

当宏匹配时,您只需收集所有具有名称的项目并将它们作为关键字参数传递给处理函数:

class Macro():
   ...
   def parse(self, ...):
     syntaxtree = []
     nameditems = {}
     # parse, however this is done
     # store all elements that have a name as
     #   nameditems[name] = parsed_element
     self.handle(syntaxtree, **nameditems)

然后处理函数将被定义如下:

class PrintMacro(Macro):
  ...
  def handle(self, syntaxtree, vars):
    return Printnl(vars, None)

我将语法树添加为始终传递的第一个参数,因此如果您只想在语法树上执行非常基本的操作,则不需要任何命名项。

此外,如果您不喜欢装饰器,为什么不添加宏类型,如“基类”?IfMacro然后看起来像这样:

macro IfMacro(MultiLine):
  syntax:
    Group("if", Var(), ":", Var(), name='if_')
    ZeroOrMore("elif", Var(), ":", Var(), name='elifs')
    Optional("else", Var(name='elseBody'))

  return If(
      [(cond, Stmt(body)) for keyword, cond, colon, body in [if_] + elifs],
      None if elseBody is None else Stmt(elseBody)
    )

在内部表示中:

class IfMacro(MultiLineMacro):
  syntax = (
      Group("if", Var(), ":", Var(), name='if_'),
      ZeroOrMore("elif", Var(), ":", Var(), name='elifs'),
      Optional("else", Var(name='elseBody'))
    )

  def handle(self, syntaxtree, if_=None, elifs=None, elseBody=None):
    # Default parameters in case there is no such named item.
    # In this case this can only happen for 'elseBody'.
    return If(
        [(cond, Stmt(body)) for keyword, cond, body in [if_] + elifs],
        None if elseNody is None else Stmt(elseBody)
      )

我认为这将提供一个非常灵活的系统。主要优点:

  • 易于学习(看起来像标准 python)
  • 易于解析(像标准 python 一样解析)
  • 可以轻松处理可选项目,因为您可以None在处理程序中有一个默认参数
  • 灵活使用命名项目:
    • 如果你不想,你不需要命名任何项目,因为语法树总是被传入。
    • 您可以在大宏定义中命名任何子表达式,因此很容易挑选出您感兴趣的特定内容
  • 如果您想向宏结构添加更多功能,则可以轻松扩展。例如Several("abc", min=3, max=5, name="a"). 我认为这也可以用于将默认值添加到可选元素,如Optional("step", Var(), name="step", default=1).

我不确定带有“quote:”和“$”的引用/取消引用语法,但是需要一些语法,因为如果您不必手动编写语法树,它会使生活变得更容易。要求(或只是允许?)“$”的括号可能是个好主意,这样您就可以根据需要插入更复杂的语法部分。喜欢$(Stmt(a, b, c))

ToMacro 看起来像这样:

# macro definition
macro ToMacro(Partial):
  syntax:
    Var(name='start'), "to", Var(name='end'), Optional("inclusive", name='inc'), Optional("step", Var(name='step'))

  if step == None:
    step = quote(1)
  if inclusive:
    return quote:
      xrange($(start), $(end)+1, $(step))
  else:
    return quote:
      xrange($(start), $(end), $(step))

# resulting macro class
class ToMacro(PartialMacro):
  syntax = Var(name='start'), "to", Var(name='end'), Optional("inclusive", name='inc'), Optional("step", Var(name='step'))

  def handle(syntaxtree, start=None, end=None, inc=None, step=None):
    if step is None:
      step = Number(1)
    if inclusive:
      return ['xrange', ['(', start, [end, '+', Number(1)], step, ')']]
    return ['xrange', ['(', start, end, step, ')']]
于 2009-01-27T13:48:47.700 回答
3

您可能会考虑查看 Boo(一种基于 .NET 的语言,其语法很大程度上受 Python 启发)如何实现宏,如在http://boo.codehaus.org/Syntactic+Macros

于 2009-01-30T04:55:21.500 回答
3

你应该看看MetaPython看看它是否完成了你正在寻找的东西。

于 2009-04-18T23:30:26.880 回答
2

结合BNF

class IfMacro(Macro):
    syntax: "if" expression ":" suite ("elif" expression ":" suite )* ["else" ":" suite] 

    def handle(self, if_, elifs, elseBody):
        return If(
            [(expression, Stmt(suite)) for expression, suite in [if_] + elifs],
            elseBody != None and Stmt(elseBody) or None
            )
于 2009-01-27T19:33:49.847 回答
1

我正在发布一些浮动的想法,看看它是否能激发灵感。我不太了解python,而且我没有使用真正的python语法,但它什么都比不上:p

macro PrintMacro:
  syntax:  
          print $a
  rules:  
        a: list(String),  as vars
  handle:
       # do something with 'vars' 

macro IfMacro:
  syntax: 
      if $a :
          $b
      $c 
   rules: 
        a: 1 boolean  as if_cond 
        b: 1 coderef     as if_code 
        c: optional macro(ElseIf) as else_if_block 

    if( if_cond ):
          if_code();
    elsif( defined else_if_block ): 
          else_if_block(); 

更多想法:

实现 Perl 引用样式,但在 Python 中!(这是一个非常糟糕的实现,请注意:空格在规则中很重要)

macro stringQuote:
  syntax:  
          q$open$content$close
  rules:  
        open:  anyOf('[{(/_') or anyRange('a','z') or anyRange('0','9');
        content: string
        close:  anyOf(']})/_') or anyRange('a','z') or anyRange('0','9');
  detect: 
      return 1 if open == '[' and close == ']' 
      return 1 if open == '{' and close == '}'
      return 1 if open == '(' and close  == ')'
      return 1 if open == close 
      return 0
  handle: 
      return content;
于 2009-01-20T10:05:14.093 回答
1

这是我根据 Kent Fredric 的想法提出的一种新的宏语法。就像解析代码一样,它将语法解析为列表。

打印宏:

macro PrintMacro:
    syntax:
      print $stmts

  if not isinstance(stmts, list):
    stmts = [stmts]
  return Printnl(stmts, None)

如果是宏:

@MultiLine
macro IfMacro:
  syntax:
    @if_ = if $cond: $body
    @elifs = ZeroOrMore(elif $cond: $body)
    Optional(else: $elseBody)

  return If(
      [(cond, Stmt(body)) for cond, body in [if_] + elifs],
      elseBody != None and Stmt(elseBody) or None
    )

X 到 Y [含] [步骤 Z] 宏:

@Partial
macro ToMacro:
  syntax:
    $start to $end Optional(inclusive) Optional(step $step)

  if step == None:
    step = quote 1
  if inclusive:
    return quote:
      xrange($start, $end+1, $step)
  else:
    return quote:
      xrange($start, $end, $step)

除了使用装饰器来识别宏类型的小问题之外,我唯一真正的问题是您可以命名组的方式,例如在 if 情况下。我正在使用@name = ...,但这只是 Perl 的味道。我不想只使用 name = ... 因为这可能与要匹配的宏模式冲突。有任何想法吗?

于 2009-01-20T10:24:32.137 回答
1

如果您只是询问 Python 中宏的语法(而不是实现),那么我相信答案是显而易见的。语法应该与 Python 已有的语法非常匹配(即“ def”关键字)。

您是否将其实现为以下之一取决于您:

def macro largest(lst):
defmac largest(lst):
macro largest(lst):

但我认为它应该与其他功能的正常功能完全相同,以便:

def twice_second(a,b):
    glob_i = glob_i + 1
    return b * 2
x = twice_second (1,7);

defmac twice_second(a,b):
    glob_i = glob_i + 1
    return b * 2
x = twice_second (1,7);

在功能上是等效的。

我实现这一点的方法是使用预处理器(a la C),它将:

  • 用输入文件中的 defs 替换所有 defmac。
  • 通过 Python 传递它以检查语法(偷偷摸摸的位,this)。
  • 把defmac的放回去。
  • 找到每个宏的所有用途并“内联”它们,使用您自己的保留变量,例如将本地 var 转换a__macro_second_local_a.
  • 返回值也应该是一个特殊变量(macro_second_retval)。
  • 全局变量将保留其真实名称。
  • 参数可以被赋予 _macro_second_param_XXX 名称。
  • 完成所有内联后,完全删除 defmac 'functions'。
  • 通过 Python 传递结果文件。

毫无疑问,会有一些琐事需要处理(比如元组或多个返回点),但在我看来,Python 足够强大来处理这些问题。

所以:

x = twice_second (1,7);

变成:

# These lines are the input params.
__macro_second_param_a = 1
__macro_second_param_b = 7

# These lines are the inlined macro.
glob_i = glob_i + 1
__macro_second_retval = __macro_second_param_b * 2

# Modified call to macro.
x = __macro_second_retval
于 2009-01-30T04:18:19.360 回答
0

这是当前使用标准 Python 类定义语法的机制。

打印宏:

class PrintMacro(Macro):
  syntax = 'print', Var
  def handle(self, stmts):
    if not isinstance(stmts, list):
      stmts = [stmts]
    return Printnl(stmts, None)

if/elif/else 宏类:

class IfMacro(MLMacro):
  syntax = (
      ('if', Var, Var),
      ZeroOrMore('elif', Var, Var),
      Optional('else', Var)
    )
  def handle(self, if_, elifs, elseBody):
    return If(
        [(cond, Stmt(body)) for cond, body in [if_] + elifs],
        elseBody != None and Stmt(elseBody) or None
      )

X到Y【含】【步骤Z】宏类:

class ToMacro(PartialMacro):
  syntax = Var, 'to', Var, Optional('inclusive'), Optional('step', Var)
  def handle(self, start, end, inclusive, step):
    if inclusive:
      end = ['(', end, '+', Number(1), ')']
    if step == None: step = Number(1)
    return ['xrange', ['(', start, end, step, ')']]

我对这个设计的问题是事情非常冗长,至少感觉不到pythonic。此外,报价能力的缺乏使得复杂的宏变得困难。

于 2009-01-20T09:37:13.587 回答
0

这是我为我的 Python 超集提出的宏语法。

打印宏:

macro PrintMacro:
    syntax:
        stmts = 'print', Var

  if not isinstance(stmts, list):
    stmts = [stmts]
  return Printnl(stmts, None)

如果是宏:

@MultiLine
macro IfMacro:
  syntax:
    if_ = 'if', Var, Var
    elifs = ZeroOrMore('elif', Var, Var)
    else_ = Optional('else', Var)

  return If(
      [(cond, Stmt(body)) for cond, body in [if_] + elifs],
      elseBody != None and Stmt(elseBody) or None
    )

X 到 Y [含] [步骤 Z] 宏:

@Partial
macro ToMacro:
  syntax:
    start = Var
    'to'
    end = Var
    inclusive = Optional('inclusive')
    step = Optional('step', Var)

  if step == None:
    step = quote 1
  if inclusive:
    return quote:
      xrange($start, $end+1, $step)
  else:
    return quote:
      xrange($start, $end, $step)

我的主要问题是语法块不清楚,尤其是最后一个示例中的“to”行。我也不喜欢使用装饰器来区分宏类型。

于 2009-01-20T09:38:34.117 回答