vlasovskikh / funcparserlib

Recursive descent parsing library for Python based on functional combinators
https://funcparserlib.pirx.ru
MIT License
338 stars 38 forks source link

Parser similar to `maybe()`, but without a return value like `skip()` #62

Closed AndreasMatthias closed 3 years ago

AndreasMatthias commented 3 years ago

I'd like to have a parser similar to maybe(), but one that does not return anything (like skip()) if no match is found.

Here is an example:

def optional(p):
    return p | (pure(None) >> _Ignored)

p = a('x') + optional(a('y')) + a('z')

print(p.parse('xyz')) # --> ('x', 'y', 'z')
print(p.parse('xz')) # --> ('x', 'z')

The issue here is that _Ignored is not in the public interface.

Is it possible to write optional() by using only the public interface of funcparserlib?

AndreasMatthias commented 3 years ago

Another occation where I would like to use _Ignored is this:

many(p) >> (lambda x: x if len(x) > 0 else _Ignored(()))

How about putting _Ignored in the public interface?

vlasovskikh commented 3 years ago

@AndreasMatthias The body of skip() does exactly that: def skip(p): return p >> _Ignored, so you can rewrite your first example as:

class SkipTest(unittest.TestCase):
    def test_optional(self):
        def optional(p):
            return p | skip(pure(None))

        p = a('x') + optional(a('y')) + a('z')

        self.assertEqual(p.parse('xyz'), ('x', 'y', 'z'))
        self.assertEqual(p.parse('xz'), ('x', 'z'))

I'm not sure about your second example though. You may have to deal with the empty list manually here.

I don't want to make _Ignored a part of the public API, since it's one more non-obvious thing to learn.

(It's also one more thing to make performance optimisations harder: see branch declarative-api if you're interested).