处理 EBNF / Lark 中是否有 token

问题描述 投票:0回答:1

我正在使用 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,)

但我觉得这不是正确的方法 - 它之所以有效,是因为例如半分钟标记是最终标记。

非常感谢收到的任何其他指示 - 我觉得今晚我取得了快速进步,但仍有很多东西我会错过!

python ebnf lark-parser
1个回答
0
投票

我找到了部分答案。这会将时间表分钟转换为秒:

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

我想我可以在喝完咖啡后从这里延伸……

© www.soinside.com 2019 - 2024. All rights reserved.