パーサを書くことをテーマに、私は自分の使用してステートマシンを作成するには、この機会を利用:
import sys
ASTERISK = '*'
DEFAULT = 'default'
EOL = '\n'
ESCAPE = '\\'
QUOTE = '"'
SLASH = '/'
class ExtractStrings:
def __init__(self, multiline_string):
self.buffer = multiline_string
self.chars_collected = ''
self.strings = None
def noop(self, ch):
pass
def collect_char(self, ch):
self.chars_collected += ch
def return_string(self, ch):
self.strings.append(self.chars_collected)
self.chars_collected = ''
def parse(self):
self.strings = []
state = {
'start': {
QUOTE: (self.noop, 'in_string'),
SLASH: (self.noop, 'first_slash'),
DEFAULT: (self.noop, 'start'),
},
'in_string': {
QUOTE: (self.return_string, 'start'),
ESCAPE: (self.collect_char, 'escaping'),
DEFAULT: (self.collect_char, 'in_string'),
},
'escaping': {
DEFAULT: (self.collect_char, 'in_string'),
},
'first_slash': {
SLASH: (self.noop, 'line_comment'),
ASTERISK: (self.noop, 'block_comment'),
DEFAULT: (self.noop, 'start'),
},
'line_comment': {
EOL: (self.noop, 'start'),
DEFAULT: (self.noop, 'line_comment'),
},
'block_comment': {
ASTERISK: (self.noop, 'near_comment_block_end'),
DEFAULT: (self.noop, 'block_comment'),
},
'near_comment_block_end': {
SLASH: (self.noop, 'start'),
ASTERISK: (self.noop, 'near_comment_block_end'),
DEFAULT: (self.noop, 'block_comment'),
}
}
current = 'start'
for ch in self.buffer:
default = state[current][DEFAULT]
action, next_state = state[current].get(ch, default)
action(ch)
current = next_state
def __iter__(self):
if self.strings is None:
self.parse()
return iter(self.strings)
if __name__ == '__main__':
with open(sys.argv[1]) as f:
code = f.read()
for string_literal in ExtractStrings(code):
print('"%s"' % string_literal)
どのように動作しますか?
ステートマシンは、さまざまなステート、各ステート(図には示されていません)で行うこと、および次のステートへの遷移を定義します。 ステートマシンが(ネストされた辞書として)定義されると、状態のアクションを実行し、次のcharを読み込み、ステートマシンを参照してどのステートに遷移すべきかを調べるだけです。
ステートマシンはネストされた辞書です。外部辞書の場合、キーは状態名であり、値は内部辞書である。内側の辞書の場合、キーは次の文字で、値は(アクション、次の状態)のタプルです。
正規表現は、非正規言語を解析するためのひどい考えです。実際のレクサー/パーサーを使用することを検討しましたか? yacc、lex、flex、bison?はい、それを学ぶのはより高いオーバーヘッドですが、エッジケースを離れることなく、適度に複雑な文法を正しく解析する唯一の方法です。 – ShadowRanger
'lookahead'または' lookbehind'アサーションを使う必要があるかもしれません。正規表現とのヒントについてはhttps://pythex.org/をご覧ください。 – Olexandr
@ShadowRanger:コメントも文字列定数も入れ子になっていないので、言語は規則的です。私はそれを扱うために簡単なレクサー+明示的なFSMを使うことは、特にメモリ内のファイル全体を文字列としてはしたくない場合は、正規表現を使うよりもずっと簡単だということに同意します。 – 9000