def MatchFiles(checkerFile, c1File, targetArch, debuggableMode): for testCase in checkerFile.testCases: if testCase.testArch not in [None, targetArch]: continue if testCase.forDebuggable != debuggableMode: continue # TODO: Currently does not handle multiple occurrences of the same group # name, e.g. when a pass is run multiple times. It will always try to # match a check group against the first output group of the same name. c1Pass = c1File.findPass(testCase.name) if c1Pass is None: Logger.fail( "Test case \"{}\" not found in the CFG file".format( testCase.name), testCase.fileName, testCase.startLineNo) Logger.startTest(testCase.name) try: MatchTestCase(testCase, c1Pass) Logger.testPassed() except MatchFailedException as e: lineNo = c1Pass.startLineNo + e.lineNo if e.assertion.variant == TestAssertion.Variant.Not: Logger.testFailed( "NOT assertion matched line {}".format(lineNo), e.assertion.fileName, e.assertion.lineNo) else: Logger.testFailed( "Assertion could not be matched starting from line {}". format(lineNo), e.assertion.fileName, e.assertion.lineNo)
def MatchFiles(checkerFile, c1File, targetArch, debuggableMode): for testCase in checkerFile.testCases: if testCase.testArch not in [None, targetArch]: continue if testCase.forDebuggable != debuggableMode: continue # TODO: Currently does not handle multiple occurrences of the same group # name, e.g. when a pass is run multiple times. It will always try to # match a check group against the first output group of the same name. c1Pass = c1File.findPass(testCase.name) if c1Pass is None: with file(c1File.fileName) as cfgFile: Logger.log(''.join(cfgFile), Logger.Level.Error) Logger.fail("Test case not found in the CFG file", testCase.fileName, testCase.startLineNo, testCase.name) Logger.startTest(testCase.name) try: MatchTestCase(testCase, c1Pass, c1File.instructionSetFeatures) Logger.testPassed() except MatchFailedException as e: lineNo = c1Pass.startLineNo + e.lineNo if e.statement.variant == TestStatement.Variant.Not: msg = "NOT statement matched line {}" else: msg = "Statement could not be matched starting from line {}" msg = msg.format(lineNo) with file(c1File.fileName) as cfgFile: Logger.log(''.join(cfgFile), Logger.Level.Error) Logger.testFailed(msg, e.statement, e.variables)
def match_files(checker_file, c1_file, target_arch, debuggable_mode, print_cfg): for test_case in checker_file.test_cases: if test_case.test_arch not in [None, target_arch]: continue if test_case.for_debuggable != debuggable_mode: continue # TODO: Currently does not handle multiple occurrences of the same group # name, e.g. when a pass is run multiple times. It will always try to # match a check group against the first output group of the same name. c1_pass = c1_file.find_pass(test_case.name) if c1_pass is None: with open(c1_file.full_file_name) as cfg_file: Logger.log("".join(cfg_file), Logger.Level.ERROR) Logger.fail("Test case not found in the CFG file", c1_file.full_file_name, test_case.start_line_no, test_case.name) Logger.start_test(test_case.name) try: match_test_case(test_case, c1_pass, c1_file.instruction_set_features) Logger.test_passed() except MatchFailedException as e: line_no = c1_pass.start_line_no + e.line_no if e.statement.variant == TestStatement.Variant.NOT: msg = "NOT statement matched line {}" else: msg = "Statement could not be matched starting from line {}" msg = msg.format(line_no) if print_cfg: with open(c1_file.full_file_name) as cfg_file: Logger.log("".join(cfg_file), Logger.Level.ERROR) Logger.test_failed(msg, e.statement, e.variables)
def addExpression(self, new_expression): assert isinstance(new_expression, TestExpression) if self.variant == TestStatement.Variant.Not: if new_expression.variant == TestExpression.Variant.VarDef: Logger.fail("CHECK-NOT lines cannot define variables", self.fileName, self.lineNo) self.expressions.append(new_expression)
def MatchFiles(checkerFile, c1File, targetArch, debuggableMode): for testCase in checkerFile.testCases: if testCase.testArch not in [None, targetArch]: continue if testCase.forDebuggable != debuggableMode: continue # TODO: Currently does not handle multiple occurrences of the same group # name, e.g. when a pass is run multiple times. It will always try to # match a check group against the first output group of the same name. c1Pass = c1File.findPass(testCase.name) if c1Pass is None: Logger.fail("Test case \"{}\" not found in the CFG file".format(testCase.name), testCase.fileName, testCase.startLineNo) Logger.startTest(testCase.name) try: MatchTestCase(testCase, c1Pass) Logger.testPassed() except MatchFailedException as e: lineNo = c1Pass.startLineNo + e.lineNo if e.assertion.variant == TestAssertion.Variant.Not: msg = "NOT assertion matched line {}" else: msg = "Assertion could not be matched starting from line {}" msg = msg.format(lineNo) Logger.testFailed(msg, e.assertion, e.variables)
def addAssertion(self, new_assertion): if new_assertion.variant == TestAssertion.Variant.NextLine: if not self.assertions or \ (self.assertions[-1].variant != TestAssertion.Variant.InOrder and \ self.assertions[-1].variant != TestAssertion.Variant.NextLine): Logger.fail("A next-line assertion can only be placed after an " "in-order assertion or another next-line assertion.", new_assertion.fileName, new_assertion.lineNo) self.assertions.append(new_assertion)
def addStatement(self, new_statement): if new_statement.variant == TestStatement.Variant.NextLine: if not self.statements or \ (self.statements[-1].variant != TestStatement.Variant.InOrder and \ self.statements[-1].variant != TestStatement.Variant.NextLine): Logger.fail( "A next-line statement can only be placed after an " "in-order statement or another next-line statement.", new_statement.fileName, new_statement.lineNo) self.statements.append(new_statement)
def __init__(self, parent, name, body, startLineNo): self.parent = parent self.name = name self.body = body self.startLineNo = startLineNo if not self.name: Logger.fail("C1visualizer pass does not have a name", self.fileName, self.startLineNo) if not self.body: Logger.fail("C1visualizer pass does not have a body", self.fileName, self.startLineNo) self.parent.addPass(self)
def DumpPass(outputFilename, passName): c1File = ParseC1visualizerStream(os.path.basename(outputFilename), open(outputFilename, "r")) compiler_pass = c1File.findPass(passName) if compiler_pass: maxLineNo = compiler_pass.startLineNo + len(compiler_pass.body) lenLineNo = len(str(maxLineNo)) + 2 curLineNo = compiler_pass.startLineNo for line in compiler_pass.body: Logger.log((str(curLineNo) + ":").ljust(lenLineNo) + line) curLineNo += 1 else: Logger.fail("Pass \"" + passName + "\" not found in the output")
def __init__(self, parent, name, startLineNo, testArch = None): assert isinstance(parent, CheckerFile) self.parent = parent self.name = name self.assertions = [] self.startLineNo = startLineNo self.testArch = testArch if not self.name: Logger.fail("Test case does not have a name", self.fileName, self.startLineNo) self.parent.addTestCase(self)
def dump_pass(output_filename, pass_name): c1_file = parse_c1_visualizer_stream(output_filename, open(output_filename, "r")) compiler_pass = c1_file.find_pass(pass_name) if compiler_pass: max_line_no = compiler_pass.start_line_no + len(compiler_pass.body) len_line_no = len(str(max_line_no)) + 2 cur_line_no = compiler_pass.start_line_no for line in compiler_pass.body: Logger.log((str(cur_line_no) + ":").ljust(len_line_no) + line) cur_line_no += 1 else: Logger.fail('Pass "{}" not found in the output'.format(pass_name))
def __init__(self, parent, name, startLineNo, testArch = None, forDebuggable = False): assert isinstance(parent, CheckerFile) self.parent = parent self.name = name self.assertions = [] self.startLineNo = startLineNo self.testArch = testArch self.forDebuggable = forDebuggable if not self.name: Logger.fail("Test case does not have a name", self.fileName, self.startLineNo) self.parent.addTestCase(self)
def __processLine(line, lineNo, prefix, fileName, targetArch): """ This function is invoked on each line of the check file and returns a triplet which instructs the parser how the line should be handled. If the line is to be included in the current check group, it is returned in the first value. If the line starts a new check group, the name of the group is returned in the second value. The third value indicates whether the line contained an architecture-specific suffix. """ if not __isCheckerLine(line): return None, None, None # Lines beginning with 'CHECK-START' start a new test case. # We currently only consider the architecture suffix(es) in "CHECK-START" lines. for debuggable in [True, False]: sline = __preprocessLineForStart(prefix + "-START", line, targetArch) for arch in [None] + archs_list: startLine = __extractLine(prefix + "-START", sline, arch, debuggable) if startLine is not None: return None, startLine, (arch, debuggable) # Lines starting only with 'CHECK' are matched in order. plainLine = __extractLine(prefix, line) if plainLine is not None: return (plainLine, TestAssertion.Variant.InOrder, lineNo), None, None # 'CHECK-NEXT' lines are in-order but must match the very next line. nextLine = __extractLine(prefix + "-NEXT", line) if nextLine is not None: return (nextLine, TestAssertion.Variant.NextLine, lineNo), None, None # 'CHECK-DAG' lines are no-order assertions. dagLine = __extractLine(prefix + "-DAG", line) if dagLine is not None: return (dagLine, TestAssertion.Variant.DAG, lineNo), None, None # 'CHECK-NOT' lines are no-order negative assertions. notLine = __extractLine(prefix + "-NOT", line) if notLine is not None: return (notLine, TestAssertion.Variant.Not, lineNo), None, None # 'CHECK-EVAL' lines evaluate a Python expression. evalLine = __extractLine(prefix + "-EVAL", line) if evalLine is not None: return (evalLine, TestAssertion.Variant.Eval, lineNo), None, None Logger.fail("Checker assertion could not be parsed: '" + line + "'", fileName, lineNo)
def __processLine(line, lineNo, prefix, fileName): """ This function is invoked on each line of the check file and returns a triplet which instructs the parser how the line should be handled. If the line is to be included in the current check group, it is returned in the first value. If the line starts a new check group, the name of the group is returned in the second value. The third value indicates whether the line contained an architecture-specific suffix. """ if not __isCheckerLine(line): return None, None, None # Lines beginning with 'CHECK-START' start a new test case. # We currently only consider the architecture suffix in "CHECK-START" lines. for debuggable in [True, False]: for arch in [None] + archs_list: startLine = __extractLine(prefix + "-START", line, arch, debuggable) if startLine is not None: return None, startLine, (arch, debuggable) # Lines starting only with 'CHECK' are matched in order. plainLine = __extractLine(prefix, line) if plainLine is not None: return (plainLine, TestAssertion.Variant.InOrder, lineNo), None, None # 'CHECK-NEXT' lines are in-order but must match the very next line. nextLine = __extractLine(prefix + "-NEXT", line) if nextLine is not None: return (nextLine, TestAssertion.Variant.NextLine, lineNo), None, None # 'CHECK-DAG' lines are no-order assertions. dagLine = __extractLine(prefix + "-DAG", line) if dagLine is not None: return (dagLine, TestAssertion.Variant.DAG, lineNo), None, None # 'CHECK-NOT' lines are no-order negative assertions. notLine = __extractLine(prefix + "-NOT", line) if notLine is not None: return (notLine, TestAssertion.Variant.Not, lineNo), None, None # 'CHECK-EVAL' lines evaluate a Python expression. evalLine = __extractLine(prefix + "-EVAL", line) if evalLine is not None: return (evalLine, TestAssertion.Variant.Eval, lineNo), None, None Logger.fail("Checker assertion could not be parsed: '" + line + "'", fileName, lineNo)
def __parseC1Line(line, lineNo, state, fileName): """ This function is invoked on each line of the output file and returns a triplet which instructs the parser how the line should be handled. If the line is to be included in the current group, it is returned in the first value. If the line starts a new output group, the name of the group is returned in the second value. The third value is only here to make the function prototype compatible with `SplitStream` and is always set to `None` here. """ if state.currentState == C1ParserState.StartingCfgBlock: # Previous line started a new 'cfg' block which means that this one must # contain the name of the pass (this is enforced by C1visualizer). if re.match("name\s+\"[^\"]+\"", line): # Extract the pass name, prepend it with the name of the method and # return as the beginning of a new group. state.currentState = C1ParserState.InsideCfgBlock return (None, state.lastMethodName + " " + line.split("\"")[1], None) else: Logger.fail("Expected output group name", fileName, lineNo) elif state.currentState == C1ParserState.InsideCfgBlock: if line == "end_cfg": state.currentState = C1ParserState.OutsideBlock return (None, None, None) else: return (line, None, None) elif state.currentState == C1ParserState.InsideCompilationBlock: # Search for the method's name. Format: method "<name>" if re.match("method\s+\"[^\"]*\"", line): methodName = line.split("\"")[1].strip() if not methodName: Logger.fail("Empty method name in output", fileName, lineNo) state.lastMethodName = methodName elif line == "end_compilation": state.currentState = C1ParserState.OutsideBlock return (None, None, None) else: assert state.currentState == C1ParserState.OutsideBlock if line == "begin_cfg": # The line starts a new group but we'll wait until the next line from # which we can extract the name of the pass. if state.lastMethodName is None: Logger.fail("Expected method header", fileName, lineNo) state.currentState = C1ParserState.StartingCfgBlock return (None, None, None) elif line == "begin_compilation": state.currentState = C1ParserState.InsideCompilationBlock return (None, None, None) else: Logger.fail("C1visualizer line not inside a group", fileName, lineNo)
def ParseC1visualizerStream(fileName, stream): c1File = C1visualizerFile(fileName) state = C1ParserState() fnProcessLine = lambda line, lineNo: __parseC1Line(line, lineNo, state, fileName) fnLineOutsideChunk = lambda line, lineNo: \ Logger.fail("C1visualizer line not inside a group", fileName, lineNo) for passName, passLines, startLineNo, testArch in \ SplitStream(stream, fnProcessLine, fnLineOutsideChunk): C1visualizerPass(c1File, passName, passLines, startLineNo + 1) return c1File
def FindCheckerFiles(path): """ Returns a list of files to scan for check annotations in the given path. Path to a file is returned as a single-element list, directories are recursively traversed and all '.java' and '.smali' files returned. """ if not path: Logger.fail("No source path provided") elif os.path.isfile(path): return [ path ] elif os.path.isdir(path): foundFiles = [] for root, dirs, files in os.walk(path): for file in files: extension = os.path.splitext(file)[1] if extension in [".java", ".smali"]: foundFiles.append(os.path.join(root, file)) return foundFiles else: Logger.fail("Source path \"" + path + "\" not found")
def find_checker_files(path): """ Returns a list of files to scan for check annotations in the given path. Path to a file is returned as a single-element list, directories are recursively traversed and all '.java', '.j', and '.smali' files returned. """ if not path: Logger.fail("No source path provided") elif os.path.isfile(path): return [path] elif os.path.isdir(path): found_files = [] for root, dirs, files in os.walk(path): for file in files: extension = os.path.splitext(file)[1] if extension in [".java", ".smali", ".j"]: found_files.append(os.path.join(root, file)) return found_files else: Logger.fail('Source path "{}" not found'.format(path))
def __init__(self, parent, name, start_line_no, test_arch=None, for_debuggable=False): assert isinstance(parent, CheckerFile) self.parent = parent self.name = name self.statements = [] self.start_line_no = start_line_no self.test_arch = test_arch self.for_debuggable = for_debuggable if not self.name: Logger.fail("Test case does not have a name", self.filename, self.start_line_no) self.parent.add_test_case(self)
def ParseCheckerStream(fileName, prefix, stream): checkerFile = CheckerFile(fileName) fnProcessLine = lambda line, lineNo: __processLine(line, lineNo, prefix, fileName) fnLineOutsideChunk = lambda line, lineNo: \ Logger.fail("Checker line not inside a group", fileName, lineNo) for caseName, caseLines, startLineNo, testArch in \ SplitStream(stream, fnProcessLine, fnLineOutsideChunk): testCase = TestCase(checkerFile, caseName, startLineNo, testArch) for caseLine in caseLines: ParseCheckerAssertion(testCase, caseLine[0], caseLine[1], caseLine[2]) return checkerFile
def ParseC1visualizerStream(fileName, stream): c1File = C1visualizerFile(fileName) state = C1ParserState() fnProcessLine = lambda line, lineNo: __parseC1Line(c1File, line, lineNo, state, fileName) fnLineOutsideChunk = lambda line, lineNo: \ Logger.fail("C1visualizer line not inside a group", fileName, lineNo) for passName, passLines, startLineNo, testArch in \ SplitStream(stream, fnProcessLine, fnLineOutsideChunk): C1visualizerPass(c1File, passName, passLines, startLineNo + 1) return c1File
def ParseCheckerStream(fileName, prefix, stream, targetArch=None): checkerFile = CheckerFile(fileName) fnProcessLine = lambda line, lineNo: __processLine(line, lineNo, prefix, fileName, targetArch) fnLineOutsideChunk = lambda line, lineNo: \ Logger.fail("Checker line not inside a group", fileName, lineNo) for caseName, caseLines, startLineNo, testData in \ SplitStream(stream, fnProcessLine, fnLineOutsideChunk): testArch = testData[0] forDebuggable = testData[1] testCase = TestCase(checkerFile, caseName, startLineNo, testArch, forDebuggable) for caseLine in caseLines: ParseCheckerStatement(testCase, caseLine[0], caseLine[1], caseLine[2]) return checkerFile
def ParseCheckerStatement(parent, line, variant, lineNo): """ This method parses the content of a check line stripped of the initial comment symbol and the CHECK-* keyword. """ statement = TestStatement(parent, variant, line, lineNo) if statement.isNoContentStatement() and line: Logger.fail("Expected empty statement: '" + line + "'", statement.fileName, statement.lineNo) # Loop as long as there is something to parse. while line: # Search for the nearest occurrence of the special markers. if statement.isEvalContentStatement(): # The following constructs are not supported in CHECK-EVAL, -IF and -ELIF lines matchWhitespace = None matchPattern = None matchVariableDefinition = None else: matchWhitespace = re.search(r"\s+", line) matchPattern = re.search(TestExpression.Regex.regexPattern, line) matchVariableDefinition = re.search( TestExpression.Regex.regexVariableDefinition, line) matchVariableReference = re.search( TestExpression.Regex.regexVariableReference, line) # If one of the above was identified at the current position, extract them # from the line, parse them and add to the list of line parts. if __isMatchAtStart(matchWhitespace): # A whitespace in the check line creates a new separator of line parts. # This allows for ignored output between the previous and next parts. line = line[matchWhitespace.end():] statement.addExpression(TestExpression.createSeparator()) elif __isMatchAtStart(matchPattern): pattern = line[0:matchPattern.end()] pattern = pattern[2:-2] line = line[matchPattern.end():] statement.addExpression(TestExpression.createPattern(pattern)) elif __isMatchAtStart(matchVariableReference): var = line[0:matchVariableReference.end()] line = line[matchVariableReference.end():] name = var[2:-2] statement.addExpression( TestExpression.createVariableReference(name)) elif __isMatchAtStart(matchVariableDefinition): var = line[0:matchVariableDefinition.end()] line = line[matchVariableDefinition.end():] colonPos = var.find(":") name = var[2:colonPos] body = var[colonPos + 1:-2] statement.addExpression( TestExpression.createVariableDefinition(name, body)) else: # If we're not currently looking at a special marker, this is a plain # text match all the way until the first special marker (or the end # of the line). firstMatch = __firstMatch([ matchWhitespace, matchPattern, matchVariableReference, matchVariableDefinition ], line) text = line[0:firstMatch] line = line[firstMatch:] if statement.isEvalContentStatement(): statement.addExpression(TestExpression.createPlainText(text)) else: statement.addExpression( TestExpression.createPatternFromPlainText(text)) return statement
def addExpression(self, new_expression): assert isinstance(new_expression, TestExpression) if self.variant == TestAssertion.Variant.Not: if new_expression.variant == TestExpression.Variant.VarDef: Logger.fail("CHECK-NOT lines cannot define variables", self.fileName, self.lineNo) self.expressions.append(new_expression)
def _process_line(line, line_no, prefix, filename, target_arch): """ This function is invoked on each line of the check file and returns a triplet which instructs the parser how the line should be handled. If the line is to be included in the current check group, it is returned in the first value. If the line starts a new check group, the name of the group is returned in the second value. The third value indicates whether the line contained an architecture-specific suffix. """ if not _is_checker_line(line): return None, None, None # Lines beginning with 'CHECK-START' start a new test case. # We currently only consider the architecture suffix(es) in "CHECK-START" lines. for debuggable in [True, False]: sline = _preprocess_line_for_start(prefix + "-START", line, target_arch) for arch in [None] + archs_list: start_line = _extract_line(prefix + "-START", sline, arch, debuggable) if start_line is not None: return None, start_line, (arch, debuggable) # Lines starting only with 'CHECK' are matched in order. plain_line = _extract_line(prefix, line) if plain_line is not None: return (plain_line, TestStatement.Variant.IN_ORDER, line_no), None, None # 'CHECK-NEXT' lines are in-order but must match the very next line. next_line = _extract_line(prefix + "-NEXT", line) if next_line is not None: return (next_line, TestStatement.Variant.NEXT_LINE, line_no), None, None # 'CHECK-DAG' lines are no-order statements. dag_line = _extract_line(prefix + "-DAG", line) if dag_line is not None: return (dag_line, TestStatement.Variant.DAG, line_no), None, None # 'CHECK-NOT' lines are no-order negative statements. not_line = _extract_line(prefix + "-NOT", line) if not_line is not None: return (not_line, TestStatement.Variant.NOT, line_no), None, None # 'CHECK-EVAL' lines evaluate a Python expression. eval_line = _extract_line(prefix + "-EVAL", line) if eval_line is not None: return (eval_line, TestStatement.Variant.EVAL, line_no), None, None # 'CHECK-IF' lines mark the beginning of a block that will be executed # only if the Python expression that follows evaluates to true. if_line = _extract_line(prefix + "-IF", line) if if_line is not None: return (if_line, TestStatement.Variant.IF, line_no), None, None # 'CHECK-ELIF' lines mark the beginning of an `else if` branch of a CHECK-IF block. elif_line = _extract_line(prefix + "-ELIF", line) if elif_line is not None: return (elif_line, TestStatement.Variant.ELIF, line_no), None, None # 'CHECK-ELSE' lines mark the beginning of the `else` branch of a CHECK-IF block. else_line = _extract_line(prefix + "-ELSE", line) if else_line is not None: return (else_line, TestStatement.Variant.ELSE, line_no), None, None # 'CHECK-FI' lines mark the end of a CHECK-IF block. fi_line = _extract_line(prefix + "-FI", line) if fi_line is not None: return (fi_line, TestStatement.Variant.FI, line_no), None, None Logger.fail("Checker statement could not be parsed: '" + line + "'", filename, line_no)
def fn_line_outside_chunk(line, line_no): Logger.fail("Checker line not inside a group", file_name, line_no)
def parse_checker_statement(parent, line, variant, line_no): """ This method parses the content of a check line stripped of the initial comment symbol and the CHECK-* keyword. """ statement = TestStatement(parent, variant, line, line_no) if statement.is_no_content_statement() and line: Logger.fail("Expected empty statement: '{}'".format(line), statement.filename, statement.line_no) # Loop as long as there is something to parse. while line: # Search for the nearest occurrence of the special markers. if statement.is_eval_content_statement(): # The following constructs are not supported in CHECK-EVAL, -IF and -ELIF lines match_whitespace = None match_pattern = None match_variable_definition = None else: match_whitespace = re.search(r"\s+", line) match_pattern = re.search(TestExpression.Regex.REGEX_PATTERN, line) match_variable_definition = re.search( TestExpression.Regex.REGEX_VARIABLE_DEFINITION, line) match_variable_reference = re.search( TestExpression.Regex.REGEX_VARIABLE_REFERENCE, line) # If one of the above was identified at the current position, extract them # from the line, parse them and add to the list of line parts. if _is_match_at_start(match_whitespace): # A whitespace in the check line creates a new separator of line parts. # This allows for ignored output between the previous and next parts. line = line[match_whitespace.end():] statement.add_expression(TestExpression.create_separator()) elif _is_match_at_start(match_pattern): pattern = line[0:match_pattern.end()] pattern = pattern[2:-2] line = line[match_pattern.end():] statement.add_expression(TestExpression.create_pattern(pattern)) elif _is_match_at_start(match_variable_reference): var = line[0:match_variable_reference.end()] line = line[match_variable_reference.end():] name = var[2:-2] statement.add_expression( TestExpression.create_variable_reference(name)) elif _is_match_at_start(match_variable_definition): var = line[0:match_variable_definition.end()] line = line[match_variable_definition.end():] colon_pos = var.find(":") name = var[2:colon_pos] body = var[colon_pos + 1:-2] statement.add_expression( TestExpression.create_variable_definition(name, body)) else: # If we're not currently looking at a special marker, this is a plain # text match all the way until the first special marker (or the end # of the line). first_match = _first_match([ match_whitespace, match_pattern, match_variable_reference, match_variable_definition ], line) text = line[0:first_match] line = line[first_match:] if statement.is_eval_content_statement(): statement.add_expression( TestExpression.create_plain_text(text)) else: statement.add_expression( TestExpression.create_pattern_from_plain_text(text)) return statement
def fn_line_outside_chunk(line, line_no): Logger.fail("C1visualizer line not inside a group", c1_file.base_file_name, line_no)
def _parse_c1_line(c1_file, line, line_no, state, filename): """ This function is invoked on each line of the output file and returns a triplet which instructs the parser how the line should be handled. If the line is to be included in the current group, it is returned in the first value. If the line starts a new output group, the name of the group is returned in the second value. The third value is only here to make the function prototype compatible with `SplitStream` and is always set to `None` here. """ if state.current_state == C1ParserState.STARTING_CFG_BLOCK: # Previous line started a new 'cfg' block which means that this one must # contain the name of the pass (this is enforced by C1visualizer). if re.match(r'name\s+"[^"]+"', line): # Extract the pass name, prepend it with the name of the method and # return as the beginning of a new group. state.current_state = C1ParserState.INSIDE_CFG_BLOCK return None, state.last_method_name + " " + line.split( '"')[1], None else: Logger.fail("Expected output group name", filename, line_no) elif state.current_state == C1ParserState.INSIDE_CFG_BLOCK: if line == "end_cfg": state.current_state = C1ParserState.OUTSIDE_BLOCK return None, None, None else: return line, None, None elif state.current_state == C1ParserState.INSIDE_COMPILATION_BLOCK: # Search for the method's name. Format: method "<name>" if re.match(r'method\s+"[^"]*"', line): method_name = line.split('"')[1].strip() if not method_name: Logger.fail("Empty method name in output", filename, line_no) match = re.search(r"isa_features:([\w,-]+)", method_name) if match: raw_features = match.group(1).split(",") # Create a map of features in the form {feature_name: is_enabled}. features = {} for rf in raw_features: feature_name = rf is_enabled = True # A '-' in front of the feature name indicates that the feature wasn't enabled at compile # time. if rf[0] == "-": feature_name = rf[1:] is_enabled = False features[feature_name] = is_enabled c1_file.set_isa_features(features) else: state.last_method_name = method_name elif line == "end_compilation": state.current_state = C1ParserState.OUTSIDE_BLOCK return None, None, None else: assert state.current_state == C1ParserState.OUTSIDE_BLOCK if line == "begin_cfg": # The line starts a new group but we'll wait until the next line from # which we can extract the name of the pass. if state.last_method_name is None: Logger.fail("Expected method header", filename, line_no) state.current_state = C1ParserState.STARTING_CFG_BLOCK return None, None, None elif line == "begin_compilation": state.current_state = C1ParserState.INSIDE_COMPILATION_BLOCK return None, None, None else: Logger.fail("C1visualizer line not inside a group", filename, line_no)