개념적 BNF를 사용하여 다른 내부에 있음을 표시 할 수 있습니다 단계를위한 단계 :
다음 부분은 pyparsing의 infixNotation
메서드 (이전의 operatorPrecedence
)를 사용하여 가장 쉽게 구현됩니다. infixNotation
을 사용하면 작업 계층을 정의 할 수 있으며 계층 구조에 정의 된 우선 순위에 따라 구문 분석 된 출력을 그룹화합니다. 나는 귀하의 '^'
"내부"연산자는 이진수 '->'
, 등 연산자 전에 평가되어야한다고 가정합니다. infixNotation
은 또한 괄호 안에 중첩을 허용하지만 절대적으로 필요한 것으로 보여주는 예제는 없습니다. infixNotation
은 기본 피연산자 유형을 지정하고 그 뒤에 연산자를 표시하는 3 개 튜플 목록, 단항 연산자, 2 진 또는 3 진 연산자에 대한 값 1, 2 또는 3 및 왼쪽 또는 오른쪽에 대한 상수 opAssoc.LEFT
또는 RIGHT
을 지정하여 정의합니다 연산자의 연관성 :
nodeExpr = infixNotation(nodeRef,
[
('^', 2, opAssoc.LEFT),
(binop, 2, opAssoc.LEFT),
])
마지막으로 우리는 일종의 명령으로 해석 한 전체 표현식을 정의합니다. 쉼표로 구분 된 노드 표현식 목록은 nodeExpr + ZeroOrMore(Suppress(',') + nodeExpr)
으로 직접 구현 될 수 있습니다 (구문 분석 된 출력에서 쉼표를 표시하지 않습니다. 구문 분석시 유용하지만 이후에는 건너 뛰어도됩니다).
이
nodeCommand = verb('verb') + delimitedList(nodeExpr)('nodes')
이름 '동사'와 각각의 표현에 분석 결과가 그 이름에 관련 지을 수 '노드'원인, 그것을 만들 것입니다 :하지만이 그렇게 자주, 대한 파싱이 방법 delimitedList
을 제공 온다 파싱이 완료된 후에 파싱 된 데이터로 작업하는 것이 더 쉽습니다.지금
파서 테스트하려면 중첩리스트로 해석 토큰 아웃
tests = """\
GO node1,node2
TURN node1->node2->node3
GO node1,node2^node3,node4
FOLLOW node1->node2<->node3
GO node5,node1->node2^node4<->node3,node6
""".splitlines()
for test in tests:
test = test.strip()
if not test:
continue
print (test)
try:
result = nodeCommand.parseString(test, parseAll=True)
print (result.dump())
except ParseException as pe:
print ("Failed:", test)
print (pe)
dump()
있어서 인쇄를 각 결과 이름을 나열하고 그 부착 값 :
GO node1,node2
['GO', 'node1', 'node2']
- nodes: ['node1', 'node2']
- verb: GO
TURN node1->node2->node3
['TURN', ['node1', '->', 'node2', '->', 'node3']]
- nodes: [['node1', '->', 'node2', '->', 'node3']]
- verb: TURN
GO node1,node2^node3,node4
['GO', 'node1', ['node2', '^', 'node3'], 'node4']
- nodes: ['node1', ['node2', '^', 'node3'], 'node4']
- verb: GO
FOLLOW node1->node2<->node3
['FOLLOW', ['node1', '->', 'node2', '<->', 'node3']]
- nodes: [['node1', '->', 'node2', '<->', 'node3']]
- verb: FOLLOW
GO node5,node1->node2^node4<->node3,node6
['GO', 'node5', ['node1', '->', ['node2', '^', 'node4'], '<->', 'node3'], 'node6']
- nodes: ['node5', ['node1', '->', ['node2', '^', 'node4'], '<->', 'node3'], 'node6']
- verb: GO
이때 명령을 구문 분석 한 다음 verb
을 기반으로 해당 동사를 수행하는 적절한 방법으로 발송하십시오.
그러나이 논리를 파이썬 개체를 사용하여 캡처하는 것이 도움이된다는 것을 알았습니다. 추상적 인 방법 doCommand
의 다양한 동사 기능을 구현 명령의 간단한 클래스 계층 구조를 정의
# base class
class Command(object):
def __init__(self, tokens):
self.cmd = tokens.verb
self.nodeExprs = tokens.nodes
def doCommand(self):
"""
Execute command logic, using self.cmd and self.nodeExprs.
To be overridden in sub classes.
"""
print (self.cmd, '::', self.nodeExprs.asList())
# these should implement doCommand, but not needed for this example
class GoCommand(Command): pass
class TurnCommand(Command): pass
class FollowCommand(Command): pass
적절한 명령 클래스의 인스턴스로 구문 분석 결과를 변환합니다이 방식 :
verbClassMap = {
'GO' : GoCommand,
'TURN' : TurnCommand,
'FOLLOW' : FollowCommand,
}
def tokensToCommand(tokens):
cls = verbClassMap[tokens.verb]
return cls(tokens)
그러나 파싱이 완료되면 구문 분석기의 구문 분석기 콜백으로이를 빌드하여 구문 분석이 완료되면 문자열과 하위 목록뿐만 아니라 doCommand
메서드를 호출하여 "실행"할 준비가 된 개체를 얻을 수 있습니다. 이렇게하려면, 단지 전체 nodeCommand
식에 구문 분석 작업으로 tokensToCommand
첨부 : 우리가 실제로 서브 클래스에 doCommand
을 구현하지 않았기 때문에
for test in tests:
test = test.strip()
if not test:
continue
try:
result = nodeCommand.parseString(test, parseAll=True)
result[0].doCommand()
except ParseException as pe:
print ("Failed:", test)
print (pe)
:
nodeCommand.setParseAction(tokensToCommand)
이제 우리는 약간 우리의 테스트 코드를 수정
GO :: ['node1', 'node2']
TURN :: [['node1', '->', 'node2', '->', 'node3']]
GO :: ['node1', ['node2', '^', 'node3'], 'node4']
FOLLOW :: [['node1', '->', 'node2', '<->', 'node3']]
GO :: ['node5', ['node1', '->', ['node2', '^', 'node4'], '<->', 'node3'], 'node6']
: 우리가 얻을 수있는 모든 단지 구문 분석 동사와 노드 목록을 반향하는 기본 기본 클래스의 행동입니다 (이 코드는 Python 3에서 실행되었고, 2.0.0으로 짂행되었다. 또한 1.5.7을 대한 파싱, 파이썬이 실행됩니다.)
편집
에서 [A, 연산, B, 연산 구조 조정 구문 분석 작업을 사용하여 체인 표현 a op b op c
[a,op,b], [b, op, c]
로 얻으려면, c]는 pairwise 표현식으로 분석됩니다. infixNotation
메서드를 사용하면 연산자 계층 구조의 수준에 첨부 할 구문 분석 동작을 정의 할 수 있습니다.
def expandChainedExpr(tokens):
ret = ParseResults([])
tokeniter = iter(tokens[0])
lastexpr = next(tokeniter)
for op,nextexpr in zip(tokeniter,tokeniter):
ret += ParseResults([[lastexpr, op, nextexpr]])
lastexpr = nextexpr
return ret
이 원래 체인 결과를 대체하기 위해 완전히 새로운 ParseResults를 구축 : 같은
체인화 된 표현의 결과를 구조 조정하는 방법이 보인다. 각 lastexpr op nextexpr
이 자신의 하위 그룹으로 저장되는 방식에 유의하십시오. nextexpr
은 lastexpr
으로 복사 된 다음 다음 op-nextexpr 쌍을 얻기 위해 반복됩니다.
는, 파서에이 포매터를 부착
infixNotation
에 해당 계층의 수준의 제 4 요소로 추가하려면 : 이제
nodeExpr = infixNotation(nodeRef,
[
('^', 2, opAssoc.LEFT),
(binop, 2, opAssoc.LEFT, expandChainedExpr),
])
의 출력 :
FOLLOW node1->node2<->node3
로 확장됩니다
('FOLLOW', '::', [['node1', '->', 'node2'], ['node2', '<->', 'node3']])
이미 시도한 내용을 확인하는 것이 도움이됩니다. "원시 파서"를 최소한의 형태로 보여 주시면 거기에서 도울 수 있습니다. – Hooked
verb + oneormore (node + optional (.... – Marinus
)와 같은 표준 문법 구조를 사용하여 첫 번째 구문 분석 문제를 해결했습니다. 두 번째 문제는 실제로 쌍을 추출하기 때문에 매우 흥미 롭습니다. a-> b-> c가 구문 분석 할 때 그룹에 이전에 파싱 된 노드를 포함하려고합니다. – Marinus