2

正規表現を使用して、任意の関数のパラメーターを文字列として一致させたいと思います。例として、次の文字列を想定します。

predicate(foo(x.bar, predicate(foo(...), bar)), bar)

これは、より長いシーケンスの一部である可能性があります

predicate(foo(x.bar, predicate(foo(...), bar)), bar)predicate(foo(x.bar, predicate(foo(...), bar)), bar)predicate(foo(x.bar, predicate(foo(...), bar)), bar)

ここで、関数/述語とそのパラメーターを表すすべての部分文字列を検索したいと考えています (つまり、最初の例では、文字列全体とネストされたpredicate(foo(...), bar))。問題は、このように単純に一致できないことです

predicate\(.*, bar\)

*貪欲な場合は述語のパラメーターよりも多く一致する可能性があり、怠惰な場合はより少ない可能性があるためです。これは、そのような predicates() をネストできるためです。

と が同量含まれる任意の文字列に一致する文字列predicate(...)を見つける正規表現が必要です(遅延)。...()

問題がある場合: Python の re モジュールで正規表現を使用しています。

4

3 に答える 3

1

正規表現を作成して、コード内のすべての関数呼び出しを見つけることができます。このようなもの:

([_a-zA-Z]+)(?=\()

次に、reモジュールを使用して、コード内の関数呼び出しにインデックスを付けるデータ構造を作成します。

import re

code = 'predicate(foo(x.bar, predicate(foo(...), bar)), bar)predicate(foo(x.bar, predicate(foo(...), bar)), bar)predicate(foo(x.bar, predicate(foo(...), bar)), bar)'
code_cp = code

regex = re.compile(r'([_a-zA-Z]+)(?=\()')
matches = re.findall(regex, code)
structured_matches = []

for m in matches:
    beg = str.index(code, m)
    end = beg + len(m)
    structured_matches.append((m, beg, end))
    code = code[:beg] + '_' * len(m) + code[end:]

これにより、次のようなデータ構造が得られます。

[
  ('predicate', 0, 9),
  ('foo', 10, 13),
  ('predicate', 21, 30),
  ('foo', 31, 34),
  ('predicate', 52, 61),
  ('foo', 62, 65),
  ('predicate', 73, 82),
  ('foo', 83, 86),
  ('predicate', 104, 113),
  ('foo', 114, 117),
  ('predicate', 125, 134),
  ('foo', 135, 138)
]

このデータ構造を関数と組み合わせて使用​​してparse、各関数呼び出しのかっこの内容を引き出すことができます。

def parse(string):
    stack = []
    contents = ''
    opened = False

    for c in string:
        if len(stack) > 0:
            contents += c
        if c == '(':
            opened = True
            stack.append('o')
        elif c == ')':
            stack.pop()
            if opened and len(stack) == 0:
                break

    return contents[:-1]


paren_contents = []

for m in structured_matches:
    fn_name, beg, end = m
    paren_contents.append((fn_name, parse(code_cp[end:])))

最終的には、次のparen_contentsようになります。

[
  ('predicate', 'foo(x.bar, predicate(foo(...), bar)), bar'),
  ('foo', 'x.bar, predicate(foo(...), bar)'),
  ('predicate', 'foo(...), bar'), ('foo', '...'),
  ('predicate', 'foo(x.bar, predicate(foo(...), bar)), bar'),
  ('foo', 'x.bar, predicate(foo(...), bar)'),
  ('predicate', 'foo(...), bar'), ('foo', '...'),
  ('predicate', 'foo(x.bar, predicate(foo(...), bar)), bar'),
  ('foo', 'x.bar, predicate(foo(...), bar)'),
  ('predicate', 'foo(...), bar'),
  ('foo', '...')
]

うまくいけば、これはあなたを正しい方向に向けます。

于 2016-09-26T15:30:29.490 に答える
1
import re

def parse(s):
    pattern = re.compile(r'([^(),]+)|\s*([(),])\s*')
    stack = []
    state = 0 # 0 = before identifier, 1 = after identifier, 2 = after closing paren
    current = None
    args = []
    for match in pattern.finditer(s):
      if match.group(1):
        if state != 0:
          raise SyntaxError("Expected identifier at {0}".format(match.start()))
        current = match.group(1)
        state = 1
      elif match.group(2) == '(':
        if state != 1:
          raise SyntaxError("Unexpected open paren at {0}".format(match.start()))
        stack.append((args, current))
        state = 0
        current = None
        args = []
      elif match.group(2) == ',':
        if state != 0: args.append(current)
        state = 0
        current = None
      elif match.group(2) == ')':
        if state != 0: args.append(current)
        if len(stack) == 0:
          raise SyntaxError("Unmatched paren at {0}".format(match.start()))
        newargs = args
        args, current = stack.pop()
        current = (current, newargs)
        state = 2
    if state != 0: args.append(current)
    if len(stack) > 0: 
      raise SyntaxError("Unclosed paren")
    return args
>>> from pprint import pprint
>>> pprint(parse('predicate(foo(x.bar, predicate(foo(...), bar)), bar)'), width=1)
[('predicate',
  [('foo',
    ['x.bar',
     ('predicate',
      [('foo',
        ['...']),
       'bar'])]),
   'bar'])]

コンマで区切られたすべての最上位の式のリストを返します。関数呼び出しは、名前と引数のタプルになります。

于 2016-09-26T14:47:17.927 に答える
1

@Tim Pietzcker が提案したように、 PyPI パッケージ regexを追加すると、再帰的な正規表現を使用できます。

>>> import regex
>>> s = 'predicate(foo(x.bar, predicate(foo(...), bar)), bar)'
>>> pattern = regex.compile(r'(\w+)(?=\(((?:\w+\((?2)\)|[^()])*)\))')
>>> pattern.findall(s)
[('predicate', 'foo(x.bar, predicate(foo(...), bar)), bar'),
 ('foo', 'x.bar, predicate(foo(...), bar)'),
 ('predicate', 'foo(...), bar'),
 ('foo', '...')]

「述語」だけを探すように制約することもできます。

>>> pattern = regex.compile(r'(predicate)(?=\(((?:\w+\((?2)\)|[^()])*)\))')
>>> pattern.findall(s)
[('predicate', 'foo(x.bar, predicate(foo(...), bar)), bar'),
 ('predicate', 'foo(...), bar')]
于 2016-09-27T18:16:57.893 に答える