Skip to content

Commit 85d345a

Browse files
add permissive flag to parse
1 parent b3a4ae3 commit 85d345a

File tree

1 file changed

+28
-8
lines changed

1 file changed

+28
-8
lines changed

source/openpulse/openpulse/parser.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
from typing import List
2828

2929
try:
30-
from antlr4 import CommonTokenStream, InputStream, ParserRuleContext
30+
from antlr4 import CommonTokenStream, InputStream, ParserRuleContext, RecognitionException
31+
from antlr4.error.Errors import ParseCancellationException
32+
from antlr4.error.ErrorStrategy import BailErrorStrategy
3133
except ImportError as exc:
3234
raise ImportError(
3335
"Parsing is not available unless the [parser] extra is installed,"
@@ -49,24 +51,39 @@
4951
from ._antlr.openpulseParser import openpulseParser
5052
from ._antlr.openpulseParserVisitor import openpulseParserVisitor
5153

54+
class OpenPulseParsingError(Exception):
55+
"""An error raised by the AST visitor during the AST-generation phase. This is raised in cases where the
56+
given program could not be correctly parsed."""
5257

53-
def parse(input_: str) -> ast.Program:
58+
def parse(input_: str, permissive: bool = False) -> ast.Program:
5459
"""
5560
Parse a complete OpenPulse program from a string.
5661
5762
:param input_: A string containing a complete OpenQASM 3 program.
63+
:param permissive: A Boolean controlling whether ANTLR should attempt to
64+
recover from incorrect input or not. Defaults to ``False``; if set to
65+
``True``, the reference AST produced may be invalid if ANTLR emits any
66+
warning messages during its parsing phase.
5867
:return: A complete :obj:`~ast.Program` node.
5968
"""
60-
qasm3_ast = parse_qasm3(input_)
61-
CalParser().visit(qasm3_ast)
69+
qasm3_ast = parse_qasm3(input_, permissive=permissive)
70+
CalParser(permissive=permissive).visit(qasm3_ast)
6271
return qasm3_ast
6372

6473

65-
def parse_openpulse(input_: str, in_defcal: bool) -> openpulse_ast.CalibrationBlock:
74+
def parse_openpulse(input_: str, in_defcal: bool, permissive: bool = True) -> openpulse_ast.CalibrationBlock:
6675
lexer = openpulseLexer(InputStream(input_))
6776
stream = CommonTokenStream(lexer)
6877
parser = openpulseParser(stream)
69-
tree = parser.calibrationBlock()
78+
if not permissive:
79+
# For some reason, the Python 3 runtime for ANTLR 4 is missing the
80+
# setter method `setErrorHandler`, so we have to set the attribute
81+
# directly.
82+
parser._errHandler = BailErrorStrategy()
83+
try:
84+
tree = parser.calibrationBlock()
85+
except (RecognitionException, ParseCancellationException) as exc:
86+
raise OpenPulseParsingError() from exc
7087
result = (
7188
OpenPulseNodeVisitor(in_defcal).visitCalibrationBlock(tree)
7289
if tree.children
@@ -310,14 +327,17 @@ def visitOpenpulseStatement(self, ctx: openpulseParser.OpenpulseStatementContext
310327
class CalParser(QASMVisitor[None]):
311328
"""Visit OpenQASM3 AST and pase calibration"""
312329

330+
def __init__(self, permissive: bool = False):
331+
self.permissive = permissive
332+
313333
def visit_CalibrationDefinition(
314334
self, node: ast.CalibrationDefinition
315335
) -> openpulse_ast.CalibrationDefinition:
316336
node.__class__ = openpulse_ast.CalibrationDefinition
317-
node.body = parse_openpulse(node.body, in_defcal=True).body
337+
node.body = parse_openpulse(node.body, in_defcal=True, permissive=self.permissive).body
318338

319339
def visit_CalibrationStatement(
320340
self, node: ast.CalibrationStatement
321341
) -> openpulse_ast.CalibrationStatement:
322342
node.__class__ = openpulse_ast.CalibrationStatement
323-
node.body = parse_openpulse(node.body, in_defcal=False).body
343+
node.body = parse_openpulse(node.body, in_defcal=False, permissive=self.permissive).body

0 commit comments

Comments
 (0)