diff --git a/montepy/input_parser/input_syntax_reader.py b/montepy/input_parser/input_syntax_reader.py index e6cf1b9e..ac202831 100644 --- a/montepy/input_parser/input_syntax_reader.py +++ b/montepy/input_parser/input_syntax_reader.py @@ -9,6 +9,7 @@ from montepy.input_parser.input_file import MCNP_InputFile from montepy.input_parser.mcnp_input import Input, Message, ReadInput, Title from montepy.input_parser.read_parser import ReadParser +from montepy.utilities import is_comment import os import warnings @@ -88,20 +89,6 @@ def read_front_matters(fh, mcnp_version): break -def is_comment(line): - """""" - upper_start = line[0 : BLANK_SPACE_CONTINUE + 1].upper() - non_blank_comment = upper_start and line.lstrip().upper().startswith("C ") - if non_blank_comment: - return True - blank_comment = ( - "C\n" == upper_start.lstrip() - or "C\r\n" == upper_start.lstrip() - or ("C" == upper_start and "\n" not in line) - ) - return blank_comment or non_blank_comment - - def read_data(fh, mcnp_version, block_type=None, recursion=False): """ Reads the bulk of an MCNP file for all of the MCNP data. diff --git a/montepy/input_parser/mcnp_input.py b/montepy/input_parser/mcnp_input.py index fd533892..700c034e 100644 --- a/montepy/input_parser/mcnp_input.py +++ b/montepy/input_parser/mcnp_input.py @@ -295,16 +295,28 @@ class ReadInput(Input): def __init__(self, input_lines, block_type, input_file=None, lineno=None): super().__init__(input_lines, block_type, input_file, lineno) + if not self.is_read_input(input_lines): + raise ValueError("Not a valid Read Input") parse_result = self._parser.parse(self.tokenize(), self) - first_word = input_lines[0].split()[0].lower() if not parse_result: - if first_word != "read": - raise ValueError("Not a valid Read Input") - else: - raise ParsingError(self, "", self._parser.log.clear_queue()) + raise ParsingError(self, "", self._parser.log.clear_queue()) self._tree = parse_result self._parameters = self._tree["parameters"] + @staticmethod + def is_read_input(input_lines): + first_non_comment = "" + for line in input_lines: + if not is_comment(line): + first_non_comment = line + break + words = first_non_comment.split() + if len(words) > 0: + first_word = words[0].lower() + return first_word == "read" + # this is a fall through catch that only happens for a blank input + return False # pragma: no cover + @property def file_name(self): """ diff --git a/montepy/input_parser/parser_base.py b/montepy/input_parser/parser_base.py index 900062f6..dcd844ce 100644 --- a/montepy/input_parser/parser_base.py +++ b/montepy/input_parser/parser_base.py @@ -374,6 +374,7 @@ def file_name(self, p): "SURFACE_TYPE", "THERMAL_LAW", "ZAID", + "NUMBER_WORD", ) def file_atom(self, p): return p[0] diff --git a/montepy/utilities.py b/montepy/utilities.py index feb1a3db..e167dfef 100644 --- a/montepy/utilities.py +++ b/montepy/utilities.py @@ -1,4 +1,5 @@ # Copyright 2024, Battelle Energy Alliance, LLC All Rights Reserved. +from montepy.constants import BLANK_SPACE_CONTINUE import functools import re @@ -31,6 +32,25 @@ def fortran_float(number_string): raise ValueError(f"Value Not parsable as float: {number_string}") from e +def is_comment(line): + """ + Determines if the line is a ``C comment`` style comment. + + :param line: the line to analyze + :type line: str + :returns: True if the line is a comment + :rtype: bool + """ + upper_start = line[0 : BLANK_SPACE_CONTINUE + 1].upper() + non_blank_comment = upper_start and line.lstrip().upper().startswith("C ") + if non_blank_comment: + return True + blank_comment = ("C" == upper_start.strip() and "\n" in line) or ( + "C" == upper_start and "\n" not in line + ) + return blank_comment + + def make_prop_val_node( hidden_param, types=None, base_type=None, validator=None, deletable=False ): diff --git a/tests/constants.py b/tests/constants.py index 7398f7e4..87ec8663 100644 --- a/tests/constants.py +++ b/tests/constants.py @@ -23,6 +23,7 @@ "testReadTarget.imcnp", "bad_encoding.imcnp", "unicode.imcnp", + "file2read.imcnp", } BAD_ENCODING_FILES = { diff --git a/tests/inputs/file2read.imcnp b/tests/inputs/file2read.imcnp new file mode 100644 index 00000000..8a2c2b33 --- /dev/null +++ b/tests/inputs/file2read.imcnp @@ -0,0 +1,2 @@ +1 0 -1 +c diff --git a/tests/inputs/readEdgeCase.imcnp b/tests/inputs/readEdgeCase.imcnp new file mode 100644 index 00000000..f06c3224 --- /dev/null +++ b/tests/inputs/readEdgeCase.imcnp @@ -0,0 +1,7 @@ +Testing read card +C this is a comment +read file=file2read.imcnp + +1 so 0.5 + +mode n diff --git a/tests/test_edge_cases.py b/tests/test_edge_cases.py index fabcf147..ef6ac566 100644 --- a/tests/test_edge_cases.py +++ b/tests/test_edge_cases.py @@ -177,3 +177,8 @@ def test_geometry_comments(self): # this step caused an error for #163 cell.comments cell._tree.format() + + def test_bad_read(self): + problem = montepy.read_input( + os.path.join("tests", "inputs", "readEdgeCase.imcnp") + ) diff --git a/tests/test_syntax_parsing.py b/tests/test_syntax_parsing.py index 73b6f34c..a59a4e05 100644 --- a/tests/test_syntax_parsing.py +++ b/tests/test_syntax_parsing.py @@ -1101,6 +1101,10 @@ def testReadCardConfusions(self): input = ReadInput([f"Read FILE={file}"], BlockType.CELL) self.assertEqual(input.file_name, file) + def testReadCardBadSyntax(self): + with self.assertRaises(ParsingError): + card = ReadInput(["Read 1"], BlockType.CELL) + def testTitleFinder(self): test_title = "Richard Stallman writes GNU" test_string = f"""{test_title}