我有一个java程序,必须解析python setup.py文件以从中提取信息。我有些东西在工作,但我撞到了墙上。我首先从一个简单的原始文件开始,一旦我运行,然后我会担心会剥离出我不想让它反映实际文件的噪音。Antlr解析python安装文件
因此,这里是我的语法
grammar SetupPy ;
file_input: (NEWLINE | setupDeclaration)* EOF;
setupDeclaration : 'setup' '(' method ')';
method : setupRequires testRequires;
setupRequires : 'setup_requires' '=' '[' LISTVAL* ']' COMMA;
testRequires : 'tests_require' '=' '[' LISTVAL* ']' COMMA;
WS: [ \t\n\r]+ -> skip ;
COMMA : ',' -> skip ;
LISTVAL : SHORT_STRING ;
UNKNOWN_CHAR
: .
;
fragment SHORT_STRING
: '\'' (STRING_ESCAPE_SEQ | ~[\\\r\n\f'])* '\''
| '"' (STRING_ESCAPE_SEQ | ~[\\\r\n\f"])* '"'
;
/// stringescapeseq ::= "\" <any source character>
fragment STRING_ESCAPE_SEQ
: '\\' .
| '\\' NEWLINE
;
fragment SPACES
: [ \t]+
;
NEWLINE
: ({atStartOfInput()}? SPACES
| ('\r'? '\n' | '\r' | '\f') SPACES?
)
{
String newLine = getText().replaceAll("[^\r\n\f]+", "");
String spaces = getText().replaceAll("[\r\n\f]+", "");
int next = _input.LA(1);
if (opened > 0 || next == '\r' || next == '\n' || next == '\f' || next == '#') {
// If we're inside a list or on a blank line, ignore all indents,
// dedents and line breaks.
skip();
}
else {
emit(commonToken(NEWLINE, newLine));
int indent = getIndentationCount(spaces);
int previous = indents.isEmpty() ? 0 : indents.peek();
if (indent == previous) {
// skip indents of the same size as the present indent-size
skip();
}
else if (indent > previous) {
indents.push(indent);
emit(commonToken(Python3Parser.INDENT, spaces));
}
else {
// Possibly emit more than 1 DEDENT token.
while(!indents.isEmpty() && indents.peek() > indent) {
this.emit(createDedent());
indents.pop();
}
}
}
}
;
和我目前的测试文件(就像我说的,从一个普通的文件剥离噪音下一步)
setup(
setup_requires=['pytest-runner'],
tests_require=['pytest', 'unittest2'],
)
我在哪里卡住是如何告诉antlr setup_requires和tests_requires包含数组。我想要这些数组的值,无论是否有人使用单引号,双引号,不同行上的每个值以及上述所有组合。我不知道如何解决这个问题。我可以得到一些帮助吗?也许是一个例子或两个?
需要注意的事项,
- 不,我不能用Jython和公正运行该文件。
- 正则表达式是不是一种选择,由于在开发样式文件
当然,本次发行后,我还需要弄清楚如何从一个普通的文件剥离噪声和巨大的变化。我尝试使用Python3语法来做到这一点,但我在antlr上是个新手,它把我吹走了。我无法弄清楚如何编写规则来拉取值,所以我决定尝试一个更简单的语法。并迅速撞上另一堵墙。
编辑 这里是一个实际的setup.py文件,它最终必须解析。请记住setup_requires和test_requires可能会或可能不会在那里,并且可能会或可能不会按此顺序。
# -*- coding: utf-8 -*-
from __future__ import with_statement
from setuptools import setup
def get_version(fname='mccabe.py'):
with open(fname) as f:
for line in f:
if line.startswith('__version__'):
return eval(line.split('=')[-1])
def get_long_description():
descr = []
for fname in ('README.rst',):
with open(fname) as f:
descr.append(f.read())
return '\n\n'.join(descr)
setup(
name='mccabe',
version=get_version(),
description="McCabe checker, plugin for flake8",
long_description=get_long_description(),
keywords='flake8 mccabe',
author='Tarek Ziade',
author_email='[email protected]',
maintainer='Ian Cordasco',
maintainer_email='[email protected]',
url='https://github.com/pycqa/mccabe',
license='Expat license',
py_modules=['mccabe'],
zip_safe=False,
setup_requires=['pytest-runner'],
tests_require=['pytest'],
entry_points={
'flake8.extension': [
'C90 = mccabe:McCabeChecker',
],
},
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Console',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: Software Development :: Quality Assurance',
],
)
试图调试和简化和实现我不需要找到方法,只是值。所以我正在玩这个语法
grammar SetupPy ;
file_input: (ignore setupRequires ignore | ignore testRequires ignore)* EOF;
setupRequires : 'setup_requires' '=' '[' dependencyValue* (',' dependencyValue)* ']';
testRequires : 'tests_require' '=' '[' dependencyValue* (',' dependencyValue)* ']';
dependencyValue: LISTVAL;
ignore : UNKNOWN_CHAR? ;
LISTVAL: SHORT_STRING;
UNKNOWN_CHAR: . -> channel(HIDDEN);
fragment SHORT_STRING: '\'' (STRING_ESCAPE_SEQ | ~[\\\r\n\f'])* '\''
| '"' (STRING_ESCAPE_SEQ | ~[\\\r\n\f"])* '"';
fragment STRING_ESCAPE_SEQ
: '\\' .
| '\\'
;
很适合简单的,甚至处理乱序问题。但完整的文件犯规的工作,它被挂在
def get_version(fname='mccabe.py'):
等于在该行的标志。
您有机会评估我的解决方案吗? – TomServo
我终于明白了这一点。不幸的是它打破了一个实际的文件。它拿起进口声明,并且全是古怪的。我确实发布了一个实际需要解析的文件的例子。在我放弃之前,我会继续玩这个游戏,然后用一种不那么优雅的方式来解决这个问题。我没时间了。 – scphantm
是的,解析这个有点多,但是你的UNKNOWN_CHAR符号有问题。几乎所有的东西都不是隐含的词法分析器,它强烈地依赖于这个规则。 – TomServo