from argparse import Namespace
from lark import logger
-from lark.exceptions import UnexpectedInput
from generators.constant import XdrConstantGenerator
from generators.enum import XdrEnumGenerator
from xdr_ast import _XdrConstant, _XdrEnum, _XdrPointer
from xdr_ast import _XdrTypedef, _XdrStruct, _XdrUnion
from xdr_parse import xdr_parser, set_xdr_annotate
+from xdr_parse import make_error_handler, XdrParseError
logger.setLevel(logging.INFO)
gen.emit_declaration(definition.value)
-def handle_parse_error(e: UnexpectedInput) -> bool:
- """Simple parse error reporting, no recovery attempted"""
- print(e)
- return True
-
-
def subcmd(args: Namespace) -> int:
"""Generate definitions and declarations"""
set_xdr_annotate(args.annotate)
parser = xdr_parser()
with open(args.filename, encoding="utf-8") as f:
- parse_tree = parser.parse(f.read(), on_error=handle_parse_error)
+ source = f.read()
+ try:
+ parse_tree = parser.parse(
+ source, on_error=make_error_handler(source, args.filename)
+ )
+ except XdrParseError:
+ return 1
ast = transform_parse_tree(parse_tree)
gen = XdrHeaderTopGenerator(args.language, args.peer)
from argparse import Namespace
from lark import logger
-from lark.exceptions import UnexpectedInput
from generators.constant import XdrConstantGenerator
from generators.enum import XdrEnumGenerator
from xdr_ast import _RpcProgram, _XdrConstant, _XdrEnum, _XdrPointer
from xdr_ast import _XdrTypedef, _XdrStruct, _XdrUnion
from xdr_parse import xdr_parser, set_xdr_annotate
+from xdr_parse import make_error_handler, XdrParseError
logger.setLevel(logging.INFO)
gen.emit_maxsize(definition.value)
-def handle_parse_error(e: UnexpectedInput) -> bool:
- """Simple parse error reporting, no recovery attempted"""
- print(e)
- return True
-
-
def subcmd(args: Namespace) -> int:
"""Generate definitions"""
set_xdr_annotate(args.annotate)
parser = xdr_parser()
with open(args.filename, encoding="utf-8") as f:
- parse_tree = parser.parse(f.read(), on_error=handle_parse_error)
+ source = f.read()
+ try:
+ parse_tree = parser.parse(
+ source, on_error=make_error_handler(source, args.filename)
+ )
+ except XdrParseError:
+ return 1
ast = transform_parse_tree(parse_tree)
gen = XdrHeaderTopGenerator(args.language, args.peer)
from argparse import Namespace
from lark import logger
-from lark.exceptions import UnexpectedInput
-from xdr_parse import xdr_parser
+from xdr_parse import xdr_parser, make_error_handler, XdrParseError
from xdr_ast import transform_parse_tree
logger.setLevel(logging.DEBUG)
-def handle_parse_error(e: UnexpectedInput) -> bool:
- """Simple parse error reporting, no recovery attempted"""
- print(e)
- return True
-
-
def subcmd(args: Namespace) -> int:
"""Lexical and syntax check of an XDR specification"""
parser = xdr_parser()
with open(args.filename, encoding="utf-8") as f:
- parse_tree = parser.parse(f.read(), on_error=handle_parse_error)
+ source = f.read()
+ try:
+ parse_tree = parser.parse(
+ source, on_error=make_error_handler(source, args.filename)
+ )
+ except XdrParseError:
+ return 1
transform_parse_tree(parse_tree)
return 0
from argparse import Namespace
from lark import logger
-from lark.exceptions import UnexpectedInput
from generators.source_top import XdrSourceTopGenerator
from generators.enum import XdrEnumGenerator
from xdr_ast import _XdrStruct, _XdrTypedef, _XdrUnion
from xdr_parse import xdr_parser, set_xdr_annotate
+from xdr_parse import make_error_handler, XdrParseError
logger.setLevel(logging.INFO)
# cel: todo: client needs PROC macros
-def handle_parse_error(e: UnexpectedInput) -> bool:
- """Simple parse error reporting, no recovery attempted"""
- print(e)
- return True
-
-
def subcmd(args: Namespace) -> int:
"""Generate encoder and decoder functions"""
set_xdr_annotate(args.annotate)
parser = xdr_parser()
with open(args.filename, encoding="utf-8") as f:
- parse_tree = parser.parse(f.read(), on_error=handle_parse_error)
+ source = f.read()
+ try:
+ parse_tree = parser.parse(
+ source, on_error=make_error_handler(source, args.filename)
+ )
+ except XdrParseError:
+ return 1
ast = transform_parse_tree(parse_tree)
match args.peer:
case "server":
"""Common parsing code for xdrgen"""
+import sys
+from typing import Callable
+
from lark import Lark
+from lark.exceptions import UnexpectedInput, UnexpectedToken
# Set to True to emit annotation comments in generated source
annotate = False
+# Map internal Lark token names to human-readable names
+TOKEN_NAMES = {
+ "__ANON_0": "identifier",
+ "__ANON_1": "number",
+ "SEMICOLON": "';'",
+ "LBRACE": "'{'",
+ "RBRACE": "'}'",
+ "LPAR": "'('",
+ "RPAR": "')'",
+ "LSQB": "'['",
+ "RSQB": "']'",
+ "LESSTHAN": "'<'",
+ "MORETHAN": "'>'",
+ "EQUAL": "'='",
+ "COLON": "':'",
+ "COMMA": "','",
+ "STAR": "'*'",
+ "$END": "end of file",
+}
+
+
+class XdrParseError(Exception):
+ """Raised when XDR parsing fails"""
+
def set_xdr_annotate(set_it: bool) -> None:
"""Set 'annotate' if --annotate was specified on the command line"""
return annotate
+def make_error_handler(source: str, filename: str) -> Callable[[UnexpectedInput], bool]:
+ """Create an error handler that reports the first parse error and aborts.
+
+ Args:
+ source: The XDR source text being parsed
+ filename: The name of the file being parsed
+
+ Returns:
+ An error handler function for use with Lark's on_error parameter
+ """
+ lines = source.splitlines()
+
+ def handle_parse_error(e: UnexpectedInput) -> bool:
+ """Report a parse error with context and abort parsing"""
+ line_num = e.line
+ column = e.column
+ line_text = lines[line_num - 1] if 0 < line_num <= len(lines) else ""
+
+ # Build the error message
+ msg_parts = [f"{filename}:{line_num}:{column}: parse error"]
+
+ # Show what was found vs what was expected
+ if isinstance(e, UnexpectedToken):
+ token = e.token
+ if token.type == "__ANON_0":
+ found = f"identifier '{token.value}'"
+ elif token.type == "__ANON_1":
+ found = f"number '{token.value}'"
+ else:
+ found = f"'{token.value}'"
+ msg_parts.append(f"Unexpected {found}")
+
+ # Provide helpful expected tokens list
+ expected = e.expected
+ if expected:
+ readable = [
+ TOKEN_NAMES.get(exp, exp.lower().replace("_", " "))
+ for exp in sorted(expected)
+ ]
+ if len(readable) == 1:
+ msg_parts.append(f"Expected {readable[0]}")
+ elif len(readable) <= 4:
+ msg_parts.append(f"Expected one of: {', '.join(readable)}")
+ else:
+ msg_parts.append(str(e).split("\n")[0])
+
+ # Show the offending line with a caret pointing to the error
+ msg_parts.append("")
+ msg_parts.append(f" {line_text}")
+ prefix = line_text[: column - 1].expandtabs()
+ msg_parts.append(f" {' ' * len(prefix)}^")
+
+ sys.stderr.write("\n".join(msg_parts) + "\n")
+ raise XdrParseError()
+
+ return handle_parse_error
+
+
def xdr_parser() -> Lark:
"""Return a Lark parser instance configured with the XDR language grammar"""
try:
if __name__ == "__main__":
sys.exit(main())
-except SystemExit:
- sys.exit(0)
except (KeyboardInterrupt, BrokenPipeError):
sys.exit(1)