Skip to content

Instantly share code, notes, and snippets.

@faithandbrave
Created April 27, 2017 06:49
Show Gist options
  • Select an option

  • Save faithandbrave/e8c67e2b2ae0bb5c58aa6f46ad9ff79b to your computer and use it in GitHub Desktop.

Select an option

Save faithandbrave/e8c67e2b2ae0bb5c58aa6f46ad9ff79b to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#import re
import regex as re
text = """
```cpp
# abs
## 例
```cpp
#include <iostream>
#include <valarray>
int main()
{
const std::valarray<float> va = {1.0f, -2.0f, 3.0f, -4.0f};
std::valarray<float> result = std::abs(va);
for (float x : result) {
std::cout << x << std::endl;
}
}
```
* std::abs[color ff0000]
### 出力
```
1
2
3
4
```
"""
QUALIFIED_FENCED_BLOCK_RE = re.compile(r'(?P<fence>`{3,})[ ]*(?P<lang>[a-zA-Z0-9_+-]*)[ ]*\n(?P<code>.*?)(?<=\n)(?P<indent>[ \t]*)(?P=fence)[ ]*\n(?:(?=\n)|(?P<qualifies>.*?\n(?=\s*\n)))', re.MULTILINE | re.DOTALL)
QUALIFY_COMMAND_RE = re.compile(r'\[(.*?)\]')
INDENT_RE = re.compile(r'^[ \t]+', re.MULTILINE)
def _removeIndent(code, indent):
if len(indent) == 0:
return code
l = len(indent.expandtabs(4))
return INDENT_RE.sub(lambda m: m.group().expandtabs(4)[l:], code)
def _make_random_string():
"""アルファベットから成るランダムな文字列を作る"""
from random import randrange
import string
alphabets = string.ascii_letters
return ''.join(alphabets[randrange(len(alphabets))] for i in xrange(32))
class QualifyDictionary(object):
def __init__(self):
# 各コマンドに対する実際の処理
def _qualify_italic(*xs):
return '<i>{0}</i>'.format(*xs)
def _qualify_color(*xs):
return '<span style="color:#{1}">{0}</span>'.format(*xs)
def _qualify_link(*xs):
return '<a href="{1}">{0}</a>'.format(*xs)
self.qualify_dic = {
'italic': _qualify_italic,
'color': _qualify_color,
'link': _qualify_link,
}
class Qualifier(object):
"""修飾1個分のデータを保持するクラス"""
def __init__(self, line, qdic):
command_res = [r'(\[{cmd}(\]|.*?\]))'.format(cmd=cmd) for cmd in qdic.qualify_dic]
qualify_re_str = r'^[ \t]*\*[ \t]+(?P<target>.*?)(?P<commands>({commands})+)$'.format(
commands='|'.join(command_res))
qualify_re = re.compile(qualify_re_str)
# parsing
m = qualify_re.search(line)
if not m:
raise ValueError('Failed parse')
self.target = m.group('target')
self.commands = []
def f(match):
self.commands.append(match.group(1))
cmds = m.group('commands')
print cmds
print QUALIFY_COMMAND_RE
try:
QUALIFY_COMMAND_RE.sub(f, cmds)
except TypeError:
pass # workaround for regex library
# TypeError: expected string instance, NoneType found
class QualifierList(object):
def __init__(self, lines):
self._qdic = QualifyDictionary()
# Qualifier を作るが、エラーになったデータは取り除く
def ignore(f, *args, **kwargs):
try:
return f(*args, **kwargs)
except Exception:
return None
def unique(xs):
seen = set()
results = []
for x in xs:
if x not in seen:
seen.add(x)
results.append(x)
return results
self._qs = filter(None, [ignore(Qualifier, v, self._qdic) for v in unique(lines)])
print self._qs
def mark(self, code):
"""置換対象になる単語にマーキングを施す
対象文字列が 'sort' だとすれば、文字列中にある全ての 'sort' を
'{ランダムな文字列}'
という文字列に置換する。
"""
if len(self._qs) == 0:
self._code_re = re.compile("")
return code
# 置換対象になる単語を正規表現で表す
def get_target_re(target):
return '((?<=[^a-zA-Z_])|(?:^)){target}((?=[^a-zA-Z_])|(?:$))'.format(
target=re.escape(target)
)
def find_match(target):
pattern = re.compile(target)
return pattern.search(code) is not None
pre_target_re_text_list = ['(?:{})'.format(get_target_re(q.target)) for q in self._qs]
pre_target_re_text_list = filter(find_match, pre_target_re_text_list)
if len(pre_target_re_text_list) == 0:
self._code_re = re.compile("")
return code
target_re_text = '|'.join(pre_target_re_text_list)
# 対象となる単語を置換し、その置換された文字列を後で辿るための正規表現(text_re_list)と、
# 置換された文字列に対してどのような修飾を行えばいいかという辞書(match_qualifier)を作る。
text_re_list = []
match_qualifier = {}
def mark_command(match):
# 各置換毎に一意な文字列を用意する
match_name = _make_random_string()
# 対象となる単語がどの修飾のデータなのかを調べる
text = match.group(0)
q = (q for q in self._qs if q.target == text).next()
match_qualifier[match_name] = q
# text をこの文字列に置換する
text = '{match_name}'.format(
match_name=match_name,
)
# 置換された text だけを確実に検索するための正規表現
text_re = '(?P<{match_name}>{match_name})'.format(
match_name=match_name
)
text_re_list.append(text_re)
return text
# 対象になる単語を一括置換
code = re.sub(target_re_text, mark_command, code)
# マークされた文字列を見つけるための正規表現を作る
self._code_re = re.compile('|'.join(r for r in text_re_list))
self._match_qualifier = match_qualifier
return code
m = QUALIFIED_FENCED_BLOCK_RE.search(text)
if m:
qualifies = m.group('qualifies') or ''
code = _removeIndent(*m.group('code', 'indent'))
qualifies = filter(None, qualifies.split('\n'))
qualifier_list = QualifierList(qualifies)
code = qualifier_list.mark(code)
print code
else:
print "else"
@faithandbrave

Copy link
Copy Markdown
Author

cpprefjpのsite_generatorで、正規表現エンジンをreからregexから切り替えた際に動かなかったコードのワークアラウンド。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment