我正在使用 Python 中的 Lark 库进行早期步骤,并且真的很期待用 EBNF 解析器替换许多糟糕的
if
语句..!
这里的任务是解释铁路时刻表上写的时间。
12:34
表示火车将停下来,12/34
表示火车预计不会停下来,12d34
表示火车可能会停下来仅供乘客下车......并且至少还有四种类似的变化。时间精确到半分钟:12:34h
的意思是 12:34:30
,这就是 h
我遇到了麻烦。
我有一个数据类和枚举来存储解析的值:
from dataclasses import dataclass
from enum import Enum, auto
@dataclass(frozen=True)
class TTime:
class StopMode(Enum):
STOPPING = auto()
PASSING = auto()
SET_DOWN = auto() # etc...
hour: int
minute: int
second: int = 0
stopmode: StopMode = StopMode.PASSING
from lark import Lark, Transformer
class TreeToTTime(Transformer):
def hour(self, n):
return int("".join(n))
def minute(self, n):
return int("".join(n))
def stopmode(self, n):
(n,) = n
return n
def stopping(self, _):
return TTime.StopMode.STOPPING
def passing(self, _):
return TTime.StopMode.PASSING
def setdown(self, _):
return TTime.StopMode.SET_DOWN
def halfminute(self, _):
return True
def ttime(self, args):
hour, stopmode, minute, halfminute = args
second = 30 if halfminute else 0
return TTime(hour, minute, second, stopmode)
ttime_parser = Lark(
r"""
ttime : hour stopmode minute halfminute?
hour : ("0".."2")? DIGIT
minute : ("0".."5") DIGIT
stopmode : stopping | passing | setdown
stopping : ":"
passing : "/"
setdown : "d"
halfminute : "h" | "H"
%import common.DIGIT
%import common.WS
%ignore WS
""",
start="ttime",
)
def parse_ttime(text):
tree = ttime_parser.parse(text)
return TreeToTTime().transform(tree)
import pytest
@pytest.mark.parametrize(
"text,expected",
[
(" 0:00", TTime(0, 0, 0, TTime.StopMode.STOPPING)),
("13:20", TTime(13, 20, 0, TTime.StopMode.STOPPING)),
("00/00", TTime(0, 0, 0, TTime.StopMode.PASSING)),
("12d34", TTime(12, 34, 0, TTime.StopMode.SET_DOWN)),
# ("01:23h", TTime(1, 23, 30, TTime.StopMode.STOPPING)),
],
)
def test_parse_times(text, expected):
assert expected == parse_time(text)
问题是
args
传递给了 ttime
。如果 halfminute
不存在,即 12:34
,则 args
具有三个值:(12, StopMode.STOPPING, 34)
。如果存在 halfminute
,即 12:34h
,则它有四个值:(12, StopMode.STOPPING, 34, True)
。
如何在此处配置转换器以包含令牌的缺席?例如,对于
12:34
,我希望 args
为 [12, StopMode.STOPPING, 34, False]
。
我可以写:
hour, stopmode, minute, halfminute, *_ = args + (False,)
但我觉得这不是正确的方法 - 它之所以有效,是因为例如半分钟标记是最终标记。
非常感谢收到的任何其他指示 - 我觉得今晚我取得了快速进步,但仍有很多东西我会错过!
我找到了部分答案。这会将时间表分钟转换为秒:
from lark import Lark, Transformer
ebnf = """
tmin : minute h?
minute : "0".."5"? DIGIT
h : "h"
%import common.DIGIT
%import common.WS
%ignore WS
"""
class TreeToTmin(Transformer):
def __init__(self):
self.seconds = 0
def minute(self, n):
seconds = 60 * int("".join(n))
self.seconds += seconds
def h(self, _):
self.halfmin = False
self.seconds += 30
def tmin(self, _):
return self.seconds
parser = Lark(ebnf,start="tmin")
def parse(text):
tree = parser.parse(text)
return TreeToTmin().transform(tree)
assert parse("4") == 240
assert parse("4h") == 270
assert parse("59h") == 3570
我想我可以在喝完咖啡后从这里延伸……