我一直在研究Python的替代编译器前端,其中所有语法都是通过宏解析的。我终于到了开发阶段,我可以开始研究Python语言的超集,其中宏是一个不可或缺的组件。
我的问题是我无法想出pythonic宏定义语法。我在下面的答案中用两种不同的语法发布了几个例子。任何人都可以提出更好的语法吗?它不必以任何方式构建我提出的语法 - 我在这里完全开放。任何评论,建议等都会有所帮助,就像显示我发布的示例的替代语法一样。
关于宏结构的注释,如我发布的示例中所示:使用MultiLine / MLMacro和Partial / PartialMacro告诉解析器如何应用宏。如果是多行,则宏将匹配多个行列表;通常用于构造。如果它是部分的,宏将匹配列表中间的代码;通常用于运营商。
答案 0 :(得分:10)
几天前考虑了一段时间,并且没有任何值得发布的东西,我现在回过头来想出一些我喜欢的语法,因为它几乎看起来像python:
macro PrintMacro:
syntax:
"print", OneOrMore(Var(), name='vars')
return Printnl(vars, None)
Var()
而不是简单Var
)将元素名称作为“关键字参数”传递给我们想要名称的项目。 在解析器中找到所有名称仍然很容易,因为无论如何需要以某种方式解释此语法定义以填充宏类语法变量。
需要转换以填充生成的宏类的语法变量。
内部语法表示看起来也一样:
class PrintMacro(Macro):
syntax = 'print', OneOrMore(Var(), name='vars')
...
像OneOrMore
这样的内部语法类将遵循此模式以允许子项和可选名称:
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)
我将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)
)
在内部代表中:
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)
)
我认为这会给出一个非常灵活的系统。主要优点:
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, ')']]
答案 1 :(得分:3)
您可以考虑一下Boo(一种基于.NET的语言,其语法主要受Python的启发)如何实现宏,如 http://boo.codehaus.org/Syntactic+Macros所述。
答案 2 :(得分:3)
你应该看看MetaPython,看看它是否能完成你想要的东西。
答案 3 :(得分: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
)
答案 4 :(得分: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;
答案 5 :(得分: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 = ...因为这可能与要匹配的宏模式冲突。有什么想法吗?
答案 6 :(得分: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);
功能相同。
我实现这一点的方法是使用预处理器(la C):
a
转换本地var __macro_second_local_a
。毫无疑问会有一些需要处理的事情(比如元组或多个返回点)但是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
答案 7 :(得分:0)
这是使用标准Python类定义语法的当前机制。
打印宏:
class PrintMacro(Macro):
syntax = 'print', Var
def handle(self, stmts):
if not isinstance(stmts, list):
stmts = [stmts]
return Printnl(stmts, None)
如果是/ 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。此外,缺乏报价能力使得复杂的宏难以实现。
答案 8 :(得分: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'行。我也不是使用装饰器来区分宏类型的忠实粉丝。