@@ -198,6 +198,7 @@ def __init__(self, filepath: Path):
198198 self .encoding : str = result .encoding if result else "utf-8"
199199 with open (filepath , "rb" ) as f :
200200 self ._content : bytes = f .read ()
201+ self ._node : Node | None = None
201202
202203 @property
203204 def content (self ):
@@ -226,26 +227,38 @@ def parse(self, **kwargs: Any) -> MatlabMixin:
226227 Raises:
227228 ValueError: If the file could not be parsed.
228229 """
229- tree = PARSER . parse ( self . _content )
230- cursor = tree . walk ( )
231-
232- if cursor . node is None :
233- raise ValueError ( f"The file { self . filepath } could not be parsed." )
234- captures = FILE_QUERY . captures ( cursor . node )
235-
236- if "function" in captures :
237- model = self ._parse_function (captures ["function" ][0 ], ** kwargs )
238- elif "class" in captures :
239- model = self ._parse_class (captures ["class" ][0 ], ** kwargs )
240- else :
241- model = Script (self .filepath .stem , filepath = self .filepath , ** kwargs )
230+ try :
231+ tree = PARSER . parse ( self . _content )
232+ cursor = tree . walk ()
233+
234+ if cursor . node is None :
235+ raise ValueError ( f"The file { self . filepath } could not be parsed." )
236+ captures = FILE_QUERY . captures ( cursor . node )
237+ if "function" in captures :
238+ model = self ._parse_function (captures ["function" ][0 ], ** kwargs )
239+ elif "class" in captures :
240+ model = self ._parse_class (captures ["class" ][0 ], ** kwargs )
241+ else :
242+ model = Script (self .filepath .stem , filepath = self .filepath , ** kwargs )
242243
243- if not model .docstring :
244- model .docstring = self ._comment_docstring (
245- captures .get ("header" , None ), parent = model
246- )
244+ if not model .docstring :
245+ model .docstring = self ._comment_docstring (
246+ captures .get ("header" , None ), parent = model
247+ )
247248
248- return model
249+ return model
250+ except Exception as ex :
251+ syntax_error = SyntaxError (f"Error parsing Matlab file" )
252+ syntax_error .filename = str (self .filepath )
253+ if self ._node is not None :
254+ if self ._node .text is not None :
255+ indentation = ' ' * self ._node .start_point .column
256+ syntax_error .text = indentation + self ._node .text .decode (self .encoding )
257+ syntax_error .lineno = self ._node .start_point .row + 1
258+ syntax_error .offset = self ._node .start_point .column + 1
259+ syntax_error .end_lineno = self ._node .end_point .row + 1
260+ syntax_error .end_offset = self ._node .end_point .column + 1
261+ raise syntax_error from ex
249262
250263 def _parse_class (self , node : Node , ** kwargs : Any ) -> Class :
251264 """
@@ -262,6 +275,7 @@ def _parse_class(self, node: Node, **kwargs: Any) -> Class:
262275 Returns:
263276 Class: The parsed Class or Classfolder model.
264277 """
278+ self ._node = node
265279 saved_kwargs = {key : value for key , value in kwargs .items ()}
266280 captures = CLASS_QUERY .captures (node )
267281
@@ -401,6 +415,7 @@ def _parse_attribute(self, node: Node) -> tuple[str, Any]:
401415 The value is `True` if no value is specified,
402416 otherwise it is the parsed value which can be a boolean or a string.
403417 """
418+ self ._node = node
404419 captures = ATTRIBUTE_QUERY .captures (node )
405420
406421 key = self ._first_from_capture (captures , "name" )
@@ -429,6 +444,7 @@ def _parse_function(self, node: Node, method: bool = False, **kwargs: Any) -> Fu
429444 KeyError: If required captures are missing from the node.
430445
431446 """
447+ self ._node = node
432448 captures : dict = FUNCTION_QUERY .matches (node )[0 ][1 ]
433449
434450 input_names = self ._decode_from_capture (captures , "input" )
@@ -529,6 +545,7 @@ def _decode(self, node: Node) -> str:
529545 Returns:
530546 str: The decoded text of the node. If the node or its text is None, returns an empty string.
531547 """
548+ self ._node = node
532549 return (
533550 node .text .decode (self .encoding )
534551 if node is not None and node .text is not None
0 commit comments