Pythonic宏语法

Cod*_*ous 20 python syntax macros

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

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

关于宏结构的注释,如我发布的示例中所示:使用MultiLine/MLMacro和Partial/PartialMacro告诉解析器如何应用宏.如果是多行,则宏将匹配多个行列表; 通常用于构造.如果它是部分的,宏将匹配列表中间的代码; 通常用于运营商.

sth*_*sth 10

在几天前考虑了一段时间,并且没有任何值得发布的内容之后,我现在回过头来想出一些我喜欢的语法,因为它几乎看起来像python:

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

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

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

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

class PrintMacro(Macro):
  syntax = 'print', OneOrMore(Var(), name='vars')
  ...
Run Code Online (Sandbox Code Playgroud)

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

class MacroSyntaxElement(object):
  def __init__(self, *p, name=None):
    self.subelements = p
    self.name = name
Run Code Online (Sandbox Code Playgroud)

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

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)
Run Code Online (Sandbox Code Playgroud)

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

class PrintMacro(Macro):
  ...
  def handle(self, syntaxtree, vars):
    return Printnl(vars, None)
Run Code Online (Sandbox Code Playgroud)

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

另外,如果您不喜欢装饰器,为什么不像"基类"那样添加宏类型?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)
    )
Run Code Online (Sandbox Code Playgroud)

并在内部表示:

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)
      )
Run Code Online (Sandbox Code Playgroud)

我认为这会给出一个非常灵活的系统.主要优点:

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

我不确定带有"quote:"和"$"的quote/unquote语法,但是需要一些语法,因为如果你不必手动编写语法树,它会让生活变得更加容易.可能需要(或只允许?)括号"$"是个好主意,这样你就可以插入更复杂的语法部分,如果你愿意的话.喜欢$(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, ')']]
Run Code Online (Sandbox Code Playgroud)