Files
PlantAI-dev/tools/pymol-env/lib/python3.12/site-packages/pmg_qt/syntax/pmlparser.py
T
2026-08-03 18:42:47 +08:00

140 lines
3.4 KiB
Python

import re
import sys
# unicode can't be memoryviewed, so just slice
buffer = lambda s, i=0: s[i:]
from pymol import cmd, parsing
DEBUG = False
STRICT = [parsing.STRICT, parsing.ABORT, parsing.SECURE, parsing.LEGACY]
LITERAL = [
parsing.LITERAL, parsing.LITERAL1, parsing.LITERAL2, parsing.MOVIE,
parsing.PYTHON
]
MULTILINE = [parsing.PYTHON_BLOCK, parsing.EMBED, parsing.SKIP]
brackopen = {'(': ')', '[': ']', '{': '}'}
def fancysplit(s, delim=',', stop=(';', '\n'), maxsplit=-1):
stack = []
quote = ''
tristart = -1
escaped = False
for i, c in enumerate(s):
if i == maxsplit:
break
if escaped:
escaped = False
elif c == '\\':
escaped = True
elif quote:
if c == quote:
if tristart == -1:
quote = ''
elif i > tristart + 2 and s[i + 1:i + 3] == c + c:
tristart = -1
quote = ''
elif c in ('"', "'"):
quote = c
if s[i + 1:i + 3] == c + c:
tristart = i
elif c in brackopen:
stack.append(brackopen[c])
elif stack:
if c == stack[-1]:
stack.pop()
elif c == delim:
yield i
elif c in stop:
yield i
return
if stack and DEBUG:
print('warning: unmatched quotes')
yield len(s)
def findnewline(s):
m = re.search(r'(?:^|[^\\])$', s, re.M)
if m is None:
return len(s)
return m.end() + 1
def parse_pml(text):
i = 0
L = len(text)
while i < L:
expr_start = i
m = re.match(r'(_ )?[ \t]*($|#|/|@|[^\s;]+)', buffer(text, i), re.M)
if m is None:
break
com = m.group(2)
hit = {
'type': 'python',
'quiet': m.group(1) is not None,
'command': (m.start(2) + i, m.end(2) + i),
'multiline': 0, # 0, 1 (closed), 2 (open)
}
mode = parsing.PYTHON
if com == '':
i += m.end() + 1
continue
elif com == '/':
pass
elif com == '#':
# TODO could be handled as command
hit['type'] = 'comment'
elif com == '@':
# TODO could be handled as command
hit['type'] = 'command'
mode = parsing.STRICT
else:
com = cmd.kwhash.shortcut.get(com, com)
if com in cmd.keyword:
mode = cmd.keyword[com][4]
if mode != parsing.PYTHON:
hit['type'] = 'command'
else:
i += m.start() - m.end()
i += m.end()
arg_i = [i]
if mode in LITERAL:
i += findnewline(buffer(text, i))
arg_i.append(i)
else:
arg_i.extend((i + p) for p in fancysplit(buffer(text, i)))
i = arg_i[-1] + 1
if mode in MULTILINE:
m = re.search(r'^(_ )?[ \t]*' + com + r' end\b',
buffer(text, arg_i[-1]), re.M)
if m is None:
hit['multiline'] = 2
i = len(text)
arg_i.append(i)
else:
hit['multiline'] = 1
arg_i.append(i + m.start())
i += m.end()
hit['args'] = arg_i
hit['expr'] = expr_start, i
yield hit